Git Branching Strategy

Branches created in Git are very lightweight configurations, so you are encouraged to use them often.  The Git branching and merging features are a key strength of the tool (see branching-and-merging), and unlike in Subversion, should be used for any task that involves feature development that could affect other team members.  The big picture of the GMAT branching strategy is shown in Figure 1.  On this page I'll describe how branching is performed on both sides of the master branch shown in the diagram, present the commands used to perform branching in Git, present the development workflow that implements the developer branching strategy, and then illustrate development branching with a refactoring example in the estimation plugin.


Figure 1: GMAT branching, the big picture

Release Branching

Figure 2 shows the release side of the GMAT branching strategy.  Small changes and feature development feeds code into the mater branch.  Each code pushed into that branch is represented by a yellow node on the master branch line.  Code from the master branch is then fed into production, sometimes in bulk and sometimes on a feature by feature basis.  The production branch is then used to create a release package.

Figure 2: The release branch structure

The main GMAT development branch is the branch named "master."  This branch is analogous in practice to Subversion's trunk.  Git branches act more as peers than do branches in Subversion (and in CVS): a branch in Git does not replicate files, but instead tracks branches using internal revision references and user identified branch names.

The master branch runs in parallel with a second branch, named "production," which was started with GMAT R2013b.  Features that are complete and ready to be distributed to users are merged from the the master branch into the production branch.  Code in the production branch should be ready to be built into a GMAT release with minimal extra effort.  The R2013b branch in Git is one such branch that was built off of the production branch. 

To summarize: there are three main branch sets that are used on the server side of GMAT development:

  • master is the development source code branch that contains the current working code
  • production is a branch containing release ready code that can be built on short notice for distribution to users
  • branches off of production, like the R2013b branch, contain released versions of the source code.

During normal development periods, the test system pulls code from the master branch when creating a nightly build.  When a release is needed, the test system switches to the production branch, and builds release candidates from that branch for testing.  Once the code passes testing for release, a release specific branch is built from the production branch node that passes testing, and that build is packaged for release.

Development Branching

The GMAT development team follows a branching strategy similar to the one employed by the ODTBX developers.  Simple updates and bug fixes can be performed in the master branch and pushed to the gs-mesajade repository after the developer has tested a build incorporating the code updates.  When a new feature or a non-trivial update or bug fix is needed, the developer creates a local branch that is used to work on the task.  When the task is completed, the developer merges the local branch into the master branch, tests the new feature, checks for side effects of the code updates, and then pushes the feature to the gs-mesajade repository so the rest of the team can access the new code, and so that it can be run through nightly testing and, eventually, be released.  Figure 3 sketches out the branching employed in GMAT development.

Figure 3: The development branch structure

Git Branching Commands

There are three branching commands that are used on a regular basis.  A new branch is created using the "branch" command.  You change the current branch using the "checkout" command.  Code in a branch is merged into teh current branch using the "merge" command.  Each of these commands is illustrated from the command line, in TortoiseGit, and in SmartGit below.

The Branch Command

From the command line you create a new branch from the current working repository using the syntax

git branch newBranchName

Git creates the new branch silently when you do this, as shown when I created a geoMeasurementRefactoring branch here:

$ cd gsfcgit/GmatDevelopment/
$ git branch geoMeasurementRefactoring
$ 

If you don't specify a branch name, the branch command will show the current list of branches:

$ git branch 
  R2013b
  geoMeasurementRefactoring
* master
  production
$

The current branch setting for your working directories is marked with an asterisk in this list.  If you want to delete a branch – for example, because you have finished a task and have merged the code updates into the master branch -- you use the -D option:

$ git branch -D geoMeasurementRefactoring
Deleted branch geoMeasurementRefactoring (was e935c7e).
$ git branch 
  R2013b
* master
  production
$ 

In TortoiseGit, you create a new branch by selecting TortoiseGit > Create Branch... from the popup window that appears when you right-click in a folder of your Git repository.  When you make this selection, a panel opens that you use to fill in the name of the new branch and select the branch used as the starting point for the new branch.  Figure 4 shows this panel, set to create a geoMeasurementRefactoring branch based on the current (HEAD) branch.  In this case, the current branch is set to the master 

Figure 4: Creating a branch with TortoiseGit

Branches are created in SmartGit/Hg using the "Branch | Add Branch..." menu option.  This selection opens a popup window that lets you name the new branch that will be created based on the current working branch.  SmartGit/Hg's popup window (on Windows) is shown in Figure 5.

Figure 5: Creating a branch in SmartGit/Hg

The Checkout Command

You change your working branch using the checkout command.  From the command line, the command to change to the new geoMeasurementRefactoring branch is

$ git checkout geoMeasurementRefactoring
Switched to branch 'geoMeasurementRefactoring'
$ 

The checkout command provides an option to create a new branch and switch to it in a single step.  You do this with the -b option:

$ git checkout -b geoMeasurementRefactoring
Switched to a new branch 'geoMeasurementRefactoring'
$

In TortoiseGit, you checkout a branch using the panel that appears then you right click in an explorer window showing your repository and selecting TortoiseGit > Switch/Checkout.  The Switch/Checkout panel provides options to change to an existing branch in your repository, or to create a new branch and switch to it.  You can also switch branches by selecting the branch you want from teh list that appears when you select "Git Branch >" option from the Explorer popup menu.

The SmartGit/Hg GUI has a panel (by default in the lower left corner) showing all of the existing branches in your local repository.  You change branches by double clicking on the branch you would like to checkout, , and accepting the checkout from the resulting "Do you want to switch" popup panel.

The Merge Command 

To be written – I have to build something to merge first!

Developer Workflow

When a non-trivial feature or defect correction is being coded, the developer working on this feature creates a new branch, works on the feature, merges the new code into the local master branch, and then pushes the code to gs-mesajade.  The actual workflow can be slightly more complicated, but generally follows this work flow:

  1. Identify the change that needs to be made 
    1. Find the JIRA ticket and (if needed) feature specification for the change
    2. Clarify any ambiguities so that work can start
  2. Create a branch for the work and check it out:
        $ git checkout -b MyNewFeature
  3. Write and update code as needed to implement the change†
  4. Test the code updates
  5. Stage any additional file updates:
        $ git add * 
  6. Commit the remaining changes to the branch:
        $ git commit -m "Final set of changes for the branch"
  7. Change to the master branch:
        $ git checkout master 
  8.  Pull all updates from gs-mesajade:
        $ git pull
  9. Merge in the local branch:
        $ git merge MyNewFeature
  10. Test the new feature
    1. Build GMAT with the new feature
    2. Test the new feature
    3. Run smoke tests
  11. Pull and then (if there were no additional code changes that affect the new feature) push the code to gs-mesajade:
        $ git pull
        $ git push 

Example: Refactoring the Geometric Measurement Models

In this section I'll walk through the branching process while refactoring the Geometric measurement code to remove the intermediate GeometricMeasurement class.  I'll show the layout of the code when appropriate, the repository log as reported in SmartGit, and try to be verbose as I proceed.  Each step in the developer workflow will be numbered and placed in a subsection so that you can skip around in the descriptions as needed, and so that you can skip my tortured text at will.

For simplicity, all Git interactions will be performed using the command line.  Git logging will be shown using the SmartGit Log tool.

1 Identify the change

The change needed here is identified on JIRA ticket GMT-4150.  At the start of this work, the class layout for the geometric measurements looks like Figure 6.  

Figure 6: Geometric Measurement Classes at the Start

The goal of this work is to change the layout to match Figure 7.

Figure 7: Geometric Measurement Classes at the End

2 Create Branch

At the start of this task SmartGit shows three branches in my local repository:

Astrolabe:GmatDevelopment djc$ git branch
  R2013b
* master
  production
Astrolabe:GmatDevelopment djc$ 

The SmartGit Log tool shows these branches in its Branches panel, displayed here as the upper left panel:

Figure 8: The SmartGit Log Panel at the start of the work

The new working branch is built and made the local HEAD node by executing the checkout -b command:

Astrolabe:GmatDevelopment djc$ git checkout -b geoMeasurementRefactoring
Switched to a new branch 'geoMeasurementRefactoring'
Astrolabe:GmatDevelopment djc$ 

Checking the branch status shows that the new branch has been created and is set as the working branch:

Astrolabe:GmatDevelopment djc$ git branch
  R2013b
* geoMeasurementRefactoring
  master
  production
Astrolabe:GmatDevelopment djc$ 

You can see this in the SmartGit Log as well:

Figure 9: The Log After Creating the New Branch

3 Write / Update the Code

A developer then makes the code changes needed to address the JIRA ticket,  I won't go into the code change details here.  I do tend to follow the "commit often" rule, so as you'll see in teh steps described here, I stage and commit files each time a small chunk is ready.  The process I followed was the following:

  1. Remove the intermediate class from GeometricRange
    1. Fix the code
    2. Compile
    3. Test
    4. Stage the change
    5. Commit the change

      Astrolabe:GmatDevelopment djc$ cd plugins/EstimationPlugin/src/base/measurement
      Astrolabe:measurement djc$ git add .
      Astrolabe:measurement djc$ git commit -m "Removed GeometricMeasurement intermedaiate class from GeometricRange"
      [geoMeasurementRefactoring 5e9a36e] Removed GeometricMeasurement intermedaiate class from GeometricRange
       2 files changed, 6 insertions(+), 6 deletions(-)
  2. Remove the intermediate class from GeometricRangeRate
    1. Fix the code
    2. Compile
    3. Test
    4. Stage and commit the changes in one step

      Astrolabe:measurement djc$ git commit -a -m "Removed GeometricMeasurement intermedaiate class from GeometricRangeRate"
      [geoMeasurementRefactoring ee3a620] Removed GeometricMeasurement intermedaiate class from GeometricRangeRate
       5 files changed, 34 insertions(+), 29 deletions(-)
       rewrite application/data/gui_config/MyGmat.ini (92%)

      NOTE: The one step commit writes changes to ALL managed files, so use it carefully.  In the case shown here, 5 files were committed: the 2 I intended to commit, plus the startup file,  MyGmat.ini, and one of the Makefiles.  I reverted that change and just committed the 2 files that were of interest for this task.

  3. Remove the intermediate class from GeometricAzEl
    1. Fix the code
    2. Compile
    3. Test
    4. Stage and commit the change.  (For the rest of this section, I'm omitting the console display for the changes.)
  4. Remove the intermediate class from GeometricRADec
    1. Fix the code
    2. Compile
    3. Test
    4. Stage the change
    5. Commit all staged changes
  5. Remove the intermediate class from the branch
    1. Fix the code
    2. Compile
    3. Test
    4. Stage and commit the change

At this point, the code has been refactored successfully, and the files GeometricMeasurement.cpp and GeometricMeasurement.hpp have been removed from the geoMeasurementRefactoring branch.  We can see the differences between the files in the refactoring branch and the master branch by listing the contents for one, switching branches, and listing them for the other:

Astrolabe:measurement djc$ git checkout geoMeasurementRefactoring
Switched to branch 'geoMeasurementRefactoring'
Astrolabe:measurement djc$ ls Geometric*.?pp
GeometricAzEl.cpp	GeometricRADec.cpp	GeometricRange.cpp	GeometricRangeRate.cpp
GeometricAzEl.hpp	GeometricRADec.hpp	GeometricRange.hpp	GeometricRangeRate.hpp


Astrolabe:measurement djc$ git checkout master
Switched to branch 'master'
Astrolabe:measurement djc$ ls Geometric*.?pp
GeometricAzEl.cpp		GeometricRADec.cpp		GeometricRangeRate.cpp
GeometricAzEl.hpp		GeometricRADec.hpp		GeometricRangeRate.hpp
GeometricMeasurement.cpp	GeometricRange.cpp
GeometricMeasurement.hpp	GeometricRange.hpp

  As you can see, the master branch contains 10 source files that start with the string "Geometric", while the refactoring branch has eight.  

Notes:

Once in a while you will inadvertently make a commit with the wrong message.   When I committed the AxEl model, I had a message saying I was updating the range rate model.  I cleaned up the message using the commit --amend option:

Astrolabe:measurement djc$ git add .
Astrolabe:measurement djc$ git commit -m "Removed GeometricMeasurement intermedaiate class from GeometricRangeRate"
[geoMeasurementRefactoring 3b97238] Removed GeometricMeasurement intermedaiate class from GeometricRangeRate
 2 files changed, 6 insertions(+), 6 deletions(-)
Astrolabe:measurement djc$ git commit --amend -m "Removed GeometricMeasurement intermediate class from GeometricAzEl"
[geoMeasurementRefactoring 090420a] Removed GeometricMeasurement intermedaiate class from GeometricAzEl
 2 files changed, 6 insertions(+), 6 deletions(-)
Astrolabe:measurement djc$ 

4 Test the Change

Before code changes are merged back into the master branch, GMAT is tested with the new code in place on the branch based build.  Once all defects uncovered in this process have been resolved, the final branch maintenance is performed.

5 Stage the Files

I tend to stage and commit files as I work, so this step is minimal,  In order to determine if there are files that I need to commit, I check the file status either on my GUI based tool or at the command line, like this:

Astrolabe:measurement djc$ git status
# On branch geoMeasurementRefactoring
nothing to commit (working directory clean)
Astrolabe:measurement djc$ 

If there are no files identified as needing to be updated in the repository (as in this case), I skip to step 7.  If, on the other hand, I do see a file that needs to be managed:

Astrolabe:measurement djc$ git status
# On branch geoMeasurementRefactoring
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#	modified:   GeometricRADec.cpp
#
no changes added to commit (use "git add" and/or "git commit -a")
Astrolabe:measurement djc$ 

then that final set of changes gets staged:

Astrolabe:measurement djc$ git add GeometricRADec.cpp 
Astrolabe:measurement djc$ git status
# On branch geoMeasurementRefactoring
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#	modified:   GeometricRADec.cpp
#
Astrolabe:measurement djc$ 

And the code is ready for committing.

6 Commit the Branch Changes

Any code staged in the previous step is now committed:

Astrolabe:measurement djc$ git commit -m "Began adding comments to bring the code in line with the GMAT coding guidelines"
[geoMeasurementRefactoring fc0b1d0] Began adding comments to bring the code in line with the GMAT coding guidelines
 1 file changed, 9 insertions(+)
Astrolabe:measurement djc$ git status
# On branch geoMeasurementRefactoring
nothing to commit (working directory clean)
Astrolabe:measurement djc$ 

7 Change to the Master Branch

At this point we can merge the updates into the master branch, preparing to push it to the gs-mesajade repository.  Since the chanes are going to be made to the master branch, we need to check out that branch:

Astrolabe:measurement djc$ git checkout master
Switched to branch 'master'
Astrolabe:measurement djc$ 

8 Pull Updates

Other team members may have committed changes to the gs-mesajade repository while the changes made to the measurement model were being coded.  The next step is to pull those changes from mesajade into the local master branch.

Astrolabe:measurement djc$ git pull
               !!!!!!!!!!!!!!!WARNING!!!!!!!!!!!!!!!
      This U.S. Government resource is for authorized use only.
      If not authorized to access this resource, disconnect now.
   Unauthorized use of, or access to this resource may subject you to
            disciplinary action or criminal prosecution.
      By accessing and using this resource, you are consenting to
            monitoring, keystroke recording or auditing
               !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
djconway@gs-mesajade.gsfc.nasa.gov's password: 
remote: Counting objects: 63, done.
remote: Compressing objects: 100% (36/36), done.
remote: Total 37 (delta 28), reused 0 (delta 0)
Unpacking objects: 100% (37/37), done.
From ssh://gs-mesajade.gsfc.nasa.gov/home/GMAT/git/gmat
   e935c7e..76ba0f7  master     -> origin/master
First, rewinding head to replay your work on top of it...
Fast-forwarded master to 76ba0f75d8da88f13a62586d96b546de179829b3.
Astrolabe:measurement djc$

The pull here indicates that there were changes made to the remote repository.  These changes can be viewed in the SmartGit Log window:

Figure 10: The SmartGit log showing the geoMeasurementRefactoring branch and the master branch

The red line in Figure 5 shows the master branch in my local repository.  The blue line shows the changes made for the geoMeasurementRefactoring branch.  The code for these two branches need to be merged together, so that the code in the refactoring branch is incorporated into the master branch.

9 Merge the Changes

The merge process is straightforward for this example.  All that is needed is a call to the merge command, pulling code from the refactoring branch into the current working branch, which is set to the local master branch:

Astrolabe:measurement djc$ git merge geoMeasurementRefactoring
Removing plugins/EstimationPlugin/src/base/measurement/GeometricMeasurement.hpp
Removing plugins/EstimationPlugin/src/base/measurement/GeometricMeasurement.cpp
Merge made by the 'recursive' strategy.
Auto packing the repository for optimum performance. You may also
run "git gc" manually. See "git help gc" for more information.
Counting objects: 278, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (255/255), done.
Writing objects: 100% (278/278), done.
Total 278 (delta 122), reused 0 (delta 0)
Removing duplicate objects: 100% (256/256), done.
warning: There are too many unreachable loose objects; run 'git prune' to remove them.
 application/output/.gitignore                      |    1 +
 plugins/EstimationPlugin/src/base/Makefile         |    1 -
 .../src/base/measurement/GeometricAzEl.cpp         |    8 +-
 .../src/base/measurement/GeometricAzEl.hpp         |    4 +-
 .../src/base/measurement/GeometricMeasurement.cpp  |  252 --------------------
 .../src/base/measurement/GeometricMeasurement.hpp  |  141 -----------
 .../src/base/measurement/GeometricRADec.cpp        |   19 +-
 .../src/base/measurement/GeometricRADec.hpp        |    4 +-
 .../src/base/measurement/GeometricRange.cpp        |    8 +-
 .../src/base/measurement/GeometricRange.hpp        |    4 +-
 .../src/base/measurement/GeometricRangeRate.cpp    |    8 +-
 .../src/base/measurement/GeometricRangeRate.hpp    |    4 +-
 12 files changed, 35 insertions(+), 419 deletions(-)
 delete mode 100644 plugins/EstimationPlugin/src/base/measurement/GeometricMeasurement.cpp
 delete mode 100644 plugins/EstimationPlugin/src/base/measurement/GeometricMeasurement.hpp

The merge message recommended that I run Git Prune, so I ran it next:

Astrolabe:measurement djc$ git prune
Astrolabe:measurement djc$ 

The SmartGit Log shows that the branches have now been merged:

Figure 11: The SmartGit log after the merge


If I planned to continue working in the geoMeasurementRefactoring branch, I would proceed to step 10 at this point.  However, sSince I am finished working with the refactoring branch, I'll now delete it from my local repository:  

Astrolabe:measurement djc$ git branch -d geoMeasurementRefactoring
Deleted branch geoMeasurementRefactoring (was fc0b1d0).
Astrolabe:measurement djc$

When this command is executed, the local branch is removed from my repository.  The log still contains the full history of the work performed on that branch:

Figure 12: The SmartGit Log After Removing the Refactoring Branch 

10 Test

Once the code is merged in, the developer builds the system and runs tests to make sure that the code is ready to be committed to mesajade.  Once everything is ready, the new code is pushed to the remote repository (step 11), completing the code update.

11 Pull and Push

As with all Git remote commits, the recommended practice is to pull from the remote repository, and then push the changes to the repository:

Astrolabe:measurement djc$ git pull
               !!!!!!!!!!!!!!!WARNING!!!!!!!!!!!!!!!
      This U.S. Government resource is for authorized use only.
      If not authorized to access this resource, disconnect now.
   Unauthorized use of, or access to this resource may subject you to
            disciplinary action or criminal prosecution.
      By accessing and using this resource, you are consenting to
            monitoring, keystroke recording or auditing
               !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
djconway@gs-mesajade.gsfc.nasa.gov's password: 
First, rewinding head to replay your work on top of it...
Applying: Removed GeometricMeasurement intermedaiate class from GeometricRange
Applying: Removed GeometricMeasurement intermedaiate class from GeometricRangeRate
Applying: Revert "Removed GeometricMeasurement intermedaiate class from GeometricRangeRate"
Applying: Removed GeometricMeasurement intermediate class from GeometricRangeRate
Applying: Removed GeometricMeasurement intermediate class from GeometricAzEl
Applying: Removed GeometricMeasurement intermediate class from GeometricRADec
Applying: Added an ignore seting for GMAT neasurement data files (*.gmd).
Applying: Removed GeometricMeasurement from the project
Applying: Began adding comments to bring the code in line with the GMAT coding guidelines
Astrolabe:measurement djc$ git push
               !!!!!!!!!!!!!!!WARNING!!!!!!!!!!!!!!!
      This U.S. Government resource is for authorized use only.
      If not authorized to access this resource, disconnect now.
   Unauthorized use of, or access to this resource may subject you to
            disciplinary action or criminal prosecution.
      By accessing and using this resource, you are consenting to
            monitoring, keystroke recording or auditing
               !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
djconway@gs-mesajade.gsfc.nasa.gov's password: 
Counting objects: 92, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (39/39), done.
Writing objects: 100% (68/68), 10.42 KiB, done.
Total 68 (delta 57), reused 30 (delta 28)
To ssh://djconway@gs-mesajade.gsfc.nasa.gov/home/GMAT/git/gmat.git
   76ba0f7..c2d934f  HEAD -> master
Astrolabe:measurement djc$

This completes a full developer branching cycle.