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:


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 than origin):
    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