How to use submodules in the HarmonieCSC repository
The information on this page is not relevant for CY46 yet, but is intended as pointer to upcoming changes in CY49 and might be useful when porting changes from CY46 to CY49
Introduction
Up to CY49 the Hirlam/Harmonie repository contained all scripts and code needed for Harmonie and then some. Since CY49 a new Hirlam/HarmonieCSC repository is used. The src
directory has been externalized so it can follow the ACCORD IAL repository, simplifying code development within ACCORD. In addition to the src
directory, several other components have been moved into separate repositories outside the Harmonie repository as well. To create a consistent set of scripts, code and tools, git submodules are used. Git submodules are essentially git repositories contained within the main repository, a.k.a. the superproject, with commit hashes of the submodules tied to a specific commit of the superproject. The Hirlam/HarmonieCSC superproject contains the following submodules:
const
–> Hirlam/HarmonieConstsrc
–> Hirlam/IALutil/auxlibs
–> Hirlam/Auxlibsutil/gl
–> Hirlam/GL
Preparation
To be able to access github using ssh, add your public ssh key to https://github.com/settings/keys
For users of Harmonie (no development)
Clone the Harmonie superproject + the submodules contained therein from the Hirlam organization:
git clone --recurse-submodules git@github.com:Hirlam/HarmonieCSC.git
This will create a structure very similar to the original Harmonie repository and usage is as before.
If you forget the --recurse-submodules
, you can use:
git submodule init
To initialise your submodules after cloning followed by a recursive pull described below.
Pull changes to your local repository
If a submodule has changed remotely, the reference in the superproject will also have changed and you can get the change with a pull. After pulling, you can update your submodules with:
git submodule update
This can be done with one command with:
git pull --recurse-submodules
This can be set to the default behaviour by setting:
git config --global submodule.recurse true
However, this will check out the commit set in the superproject in all your submodules, so if you are working on your own branch, you will have to check it out again afterwards manually. If you have uncommited changes in your branch, you will get an error and the checkout will be aborted.
To avoid this, you can use:
git submodule update path/to/submodule
to update a specific submodule.
For developers
Contributing code has become somewhat more complex, as the submodules and the superproject need to be treated separately. The steps that need to be taken are described below for 3 scenarios.
Creating user forks
To be able to create pull requests a user fork of the superproject is needed. This can be done via https://github.com/Hirlam/HarmonieCSC/fork. To contribute to one or more of the submodules, forks must be created for these as well in a similar fashion, e.g. from src
via https://github.com/Hirlam/IAL/fork. User forks need to be kept up-to-date manually by the users themselves (see GitDeveloperDocumentation). Alternatively, you can clone the superproject from Hirlam
and add your user fork as a second remote:
- Clone the superproject + the submodules contained therein from the Hirlam organization, and move into it (Note that the remote will be called
Hirlam
, rather thanorigin
):git clone -o Hirlam --recurse-submodules git@github.com:Hirlam/HarmonieCSC.git cd HarmonieCSC
- Then add your user fork as new remote and update all remotes
git remote add <YOUR_GITHUB_USERNAME> git@github.com:<YOUR_GITHUB_USERNAME>/HarmonieCSC git fetch --all
- After making changes, push to your fork, not to
Hirlam
.
Scenario 1: Modify the superproject only
If only modifications to the superproject are needed, the workflow is very similar to before, as there is no need to update the submodules and their hashes.
Create a new feature or bugfix branch for the new development, e.g.:
git checkout -b <YOUR_BRANCH> [branch_to_branch_from]
This will create a new branch named <YOUR_BRANCH>
based on the branch that was checked out, or from the branch specified as [branch_to_branch_from]
. to create and check out the branch in a different location without copying the full repository, worktrees can be used, e.g.:
git worktree add -b <YOUR_BRANCH> ../some_new_dev [branch_to_branch_from]
Which will create new branch <YOUR_BRANCH>
and check it out in ../some_new_dev
Make the changes as needed, then stage and commit them:
git add <changed_files> # stage
git commit -m "Useful message" # and commit
Then push the changes in your branch to your fork:
git push -u <YOUR_GITHUB_USERNAME> <YOUR_BRANCH>
Once you are happy, create a pull request in github.
Scenario 2: Modify a submodule only
If only a change in the submodules is needed, first the change in the submodule needs to be made and merged, and then the submodule's hash in the superproject needs to be updated so that it points to the new commit. Basic steps are:
- Make the desired changes to the submodule:
- Clone your user fork
- Go to the submodule directory and add your fork of the submodule as remote
- Create a new branch for the submodule
- Make and test your changes to the submodule
- Commit the changes to the submodule
- Push the submodule changes to your user fork of the submodule on github
- Create a pull request for the submodule changes and wait until they are approved and merged in
- Update the submodule's hash in the superproject:
- Go to the superproject clone directory
- Create a new branch
- Update the submodule's hash and commit the change
- Push the new branch to your user fork of the superproject
- Create a pull request to update the submodule's hash
See next section for details from a real case.
Steps taken in a real case, modifying src/surfex/SURFEX/interpol_npts.F90
Clone your up-to-date fork
> cd <SOMEPATH> > git clone --recurse-submodules git@github.com:<YOUR_GITHUB_USERNAME>/HarmonieCSC.git Cloning into 'HarmonieCSC'... remote: Enumerating objects: 25230, done. remote: Counting objects: 100% (277/277), done. remote: Compressing objects: 100% (154/154), done. remote: Total 25230 (delta 156), reused 198 (delta 120), pack-reused 24953 Receiving objects: 100% (25230/25230), 8.28 MiB | 2.91 MiB/s, done. Resolving deltas: 100% (18952/18952), done. Updating files: 100% (1395/1395), done. Submodule 'const' (git@github.com:Hirlam/HarmonieConst) registered for path 'const' Submodule 'src' (git@github.com:Hirlam/IAL.git) registered for path 'src' Submodule 'util/auxlibs' (git@github.com:Hirlam/Auxlibs.git) registered for path 'util/auxlibs' Submodule 'util/gl' (git@github.com:Hirlam/GL.git) registered for path 'util/gl' Cloning into '<SOMEPATH>/HarmonieCSC/const'... remote: Enumerating objects: 535, done. remote: Total 535 (delta 0), reused 0 (delta 0), pack-reused 535 Receiving objects: 100% (535/535), 158.98 MiB | 16.17 MiB/s, done. Resolving deltas: 100% (189/189), done. Cloning into '<SOMEPATH>/HarmonieCSC/src'... remote: Enumerating objects: 472517, done. remote: Counting objects: 100% (7780/7780), done. remote: Compressing objects: 100% (2433/2433), done. remote: Total 472517 (delta 5550), reused 6930 (delta 5329), pack-reused 464737 Receiving objects: 100% (472517/472517), 560.70 MiB | 11.45 MiB/s, done. Resolving deltas: 100% (381932/381932), done. Cloning into '<SOMEPATH>/HarmonieCSC/util/auxlibs'... remote: Enumerating objects: 2880, done. remote: Counting objects: 100% (2880/2880), done. remote: Compressing objects: 100% (1102/1102), done. remote: Total 2880 (delta 1614), reused 2880 (delta 1614), pack-reused 0 Receiving objects: 100% (2880/2880), 20.29 MiB | 12.55 MiB/s, done. Resolving deltas: 100% (1614/1614), done. Cloning into '<SOMEPATH>/HarmonieCSC/util/gl'... remote: Enumerating objects: 9414, done. remote: Counting objects: 100% (9414/9414), done. remote: Compressing objects: 100% (1839/1839), done. remote: Total 9414 (delta 5595), reused 9408 (delta 5593), pack-reused 0 Receiving objects: 100% (9414/9414), 2.82 MiB | 4.56 MiB/s, done. Resolving deltas: 100% (5595/5595), done. Submodule path 'const': checked out '6ff54331ba1e9253ffde7a0c5a003f6258f78fff' Submodule path 'src': checked out '61847aa01cf880252d5bd35e57a417ed31d1cd08' Submodule path 'util/auxlibs': checked out '626140284a8ec8eef5974cc7fb38b7d08105ea91' Submodule path 'util/gl': checked out '9285e2fcdb76c5afb9444e227fb03a338b68ff6a'
all submodules in the user fork point to repositories in the Hirlam organisation, e.g.:
> cd <SOMEPATH>/HarmonieCSC/src > git remote -v origin git@github.com:Hirlam/IAL.git (fetch) origin git@github.com:Hirlam/IAL.git (push)
Add my fork of the IAL repository as remote, in the
src
directory, and fetch it:> cd <SOMEPATH>/HarmonieCSC/src > git remote add <YOUR_GITHUB_USERNAME> git@github.com:<YOUR_GITHUB_USERNAME>/IAL.git > git fetch <YOUR_GITHUB_USERNAME> remote: Enumerating objects: 363, done. remote: Counting objects: 100% (340/340), done. remote: Compressing objects: 100% (77/77), done. remote: Total 363 (delta 266), reused 328 (delta 263), pack-reused 23 Receiving objects: 100% (363/363), 315.12 KiB | 1.12 MiB/s, done. Resolving deltas: 100% (268/268), completed with 56 local objects. From github.com:<YOUR_GITHUB_USERNAME>/IAL * [new branch] accord_CY49T0_bf -> <YOUR_GITHUB_USERNAME>/accord_CY49T0_bf * [new branch] accord_CY49T0_to_T1 -> <YOUR_GITHUB_USERNAME>/accord_CY49T0_to_T1 * [new branch] feature/markdown_docs -> * <YOUR_GITHUB_USERNAME>/feature/markdown_docs * [new branch] gco_CY46T1_bf -> <YOUR_GITHUB_USERNAME>/gco_CY46T1_bf * [new branch] gco_CY46T1_op1 -> <YOUR_GITHUB_USERNAME>/gco_CY46T1_op1 * [new branch] mary_CY48T1_preT2 -> <YOUR_GITHUB_USERNAME>/mary_CY48T1_preT2 * [new branch] master -> <YOUR_GITHUB_USERNAME>/master * [new tag] CY49T0_T1rc.01 -> CY49T0_T1rc.01 * [new tag] CY49T0_bf.00 -> CY49T0_bf.00 * [new tag] CY49T0_bf.01 -> CY49T0_bf.01 * [new tag] CY49T0_bf.02 -> CY49T0_bf.02 * [new tag] CY49T0_op0.00 -> CY49T0_op0.00 * [new tag] CY49T0_to_T1.01 -> CY49T0_to_T1.01 * [new tag] CY49T0_to_T1.03 -> CY49T0_to_T1.03 * [new tag] CY49T0_to_T1.04 -> CY49T0_to_T1.04 * [new tag] CY49T0_to_T1.05 -> CY49T0_to_T1.05 * [new tag] CY49T0_to_T1.06 -> CY49T0_to_T1.06 * [new tag] CY49T0_to_T1.07 -> CY49T0_to_T1.07 * [new tag] CY49T0_to_T1.08 -> CY49T0_to_T1.08 * [new tag] CY49T0_to_T1.09 -> CY49T0_to_T1.09 * [new tag] CY49T1 -> CY49T1 * [new tag] CY49T1_toT2.02 -> CY49T1_toT2.02
Create a new branch in the
src
directory and check it out:> cd <SOMEPATH>/HarmonieCSC/src > git checkout -b <YOUR_BRANCH> Switched to a new branch '<YOUR_BRANCH>'
Make your changes to the submodule (and test everything carefully of course):
git status
will show modified files:> cd <SOMEPATH>/HarmonieCSC/src > git status On branch <YOUR_BRANCH> Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: surfex/SURFEX/interpol_npts.F90 no changes added to commit (use "git add" and/or "git commit -a")
Stage and commit the changes, e.g.:
> cd <SOMEPATH>/HarmonieCSC/src git add surfex/SURFEX/interpol_npts.F90 git commit -m '<a good descriptive message>'
Push the submodule to your fork of the submodule's repository:
> cd <SOMEPATH>/HarmonieCSC/src > git push -u <YOUR_GITHUB_USERNAME> <YOUR_BRANCH> Enumerating objects: 442, done. Counting objects: 100% (374/374), done. Delta compression using up to 256 threads Compressing objects: 100% (123/123), done. Writing objects: 100% (227/227), 84.20 KiB | 1.24 MiB/s, done. Total 227 (delta 183), reused 144 (delta 103), pack-reused 0 remote: Resolving deltas: 100% (183/183), completed with 120 local objects. remote: remote: Create a pull request for '<YOUR_BRANCH>' on GitHub by visiting: remote: https://github.com/<YOUR_GITHUB_USERNAME>/IAL/pull/new/<> remote: To github.com:<YOUR_GITHUB_USERNAME>/IAL.git * [new branch] <YOUR_BRANCH> -> <YOUR_BRANCH> Branch '<YOUR_BRANCH>' set up to track remote branch '<YOUR_BRANCH>' from '<YOUR_GITHUB_USERNAME>'.
Go to github and create a pull request for the submodule. Make sure the correct base repository and branch are used in the PR!
After the PR for the submodule has been merged in, the hash of the submodule in the superproject needs to be updated
Get the latest changes of the submodule into your local copy and check out the newest commit:
> cd <SOMEPATH>/HarmonieCSC/src > git fetch origin remote: Enumerating objects: 1, done. remote: Counting objects: 100% (1/1), done. remote: Total 1 (delta 0), reused 1 (delta 0), pack-reused 0 Unpacking objects: 100% (1/1), 904 bytes | 14.00 KiB/s, done. From github.com:Hirlam/IAL 61847aa01c..f8d3d93767 dev-CY49T2h -> origin/dev-CY49T2h > git checkout origin Previous HEAD position was 61847aa01c Updates for CMake compilation of CY49T2h (Hirlam/IAL#7) HEAD is now at f8d3d93767 Fix a FPE (NaN) in PGD, instead of ZDIST, use X, which is ZDIST passed to internal submodule ORDERI (#10)
Ensure that this is indeed the commit of the submodule you want to use in the superproject.
- Go to the superproject's directory and create and check out a new branch:
> cd <SOMEPATH>/HarmonieCSC > git checkout -b <YOUR_BRANCH> Switched to a new branch '<YOUR_BRANCH>'
- Update the submodule's hash,
src
in the example, and commit the change:> cd <SOMEPATH>/HarmonieCSC > git add src > git commit -m '<another good log message>'
- Push the new branch to your user fork (remote:
origin
) of the superproject:> git push -u origin <YOUR_BRANCH> Enumerating objects: 3, done. Counting objects: 100% (3/3), done. Delta compression using up to 256 threads Compressing objects: 100% (2/2), done. Writing objects: 100% (2/2), 297 bytes | 148.00 KiB/s, done. Total 2 (delta 1), reused 0 (delta 0), pack-reused 0 remote: Resolving deltas: 100% (1/1), completed with 1 local object. remote: remote: Create a pull request for '<YOUR_BRANCH>' on GitHub by visiting: remote: https://github.com/<YOUR_GITHUB_USERNAME>/HarmonieCSC/pull/new/<YOUR_BRANCH> remote: To github.com:<YOUR_GITHUB_USERNAME>/HarmonieCSC.git * [new branch] <YOUR_BRANCH> -> <YOUR_BRANCH> Branch '<YOUR_BRANCH>' set up to track remote branch '<YOUR_BRANCH>' from 'origin'.
- Finally create a pull request to update the submodule's hash, as suggested by git above. Once this PR has been approved and merged in, you're done!
Scenario 3: Changes in superproject and a submodule
If changes are needed in both the superproject (e.g. namelist) and submodules (e.g. new namelist variable) the steps are very similar to the scenario in which only a submodule needs to be modified. In the step where the submodule's hash is updated in the superproject, additional commits to the superproject's own files can be pushed and included in the same pull request.
For developers++
This section describes some git submodule actions that regular users probably never need to worry about.
Merge from dev-CY46h1 into dev-CY49T2h
ToDo
Adding a submodule
To add a new submodule to the superproject, got to the cloned superproject's directory, create a new branch and then add the submodule:
git submodule add <submodule_url> [<new_submodule_path>]
The path to the submodule and updated .gitmodules
file will be staged automatically, e.g.:
> git status
On branch main
Your branch is up to date with 'origin/main'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: .gitmodules
new file: <new_submodule_path>
Then simply commit (git commit
) and push (git push
).
More info
- https://git-scm.com/book/en/Git-Tools-Submodules
- https://www.atlassian.com/git/tutorials/git-submodule