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
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.
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 creates the new branch silently when you do this, as shown when I created a geoMeasurementRefactoring branch here:
If you don't specify a branch name, the branch command will show the current list of branches:
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:
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
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:
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!
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:
- Identify the change that needs to be made
- Find the JIRA ticket and (if needed) feature specification for the change
- Clarify any ambiguities so that work can start
- Create a branch for the work and check it out:
$ git checkout -b MyNewFeature
- Write and update code as needed to implement the change†
- Test the code updates
- Stage any additional file updates:
$ git add *
- Commit the remaining changes to the branch:
$ git commit -m "Final set of changes for the branch"
- Change to the master branch:
$ git checkout master
- Pull all updates from gs-mesajade:
$ git pull
- Merge in the local branch:
$ git merge MyNewFeature
- Test the new feature
- Build GMAT with the new feature
- Test the new feature
- Run smoke tests
- 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:
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:
Checking the branch status shows that the new branch has been created and is set as the working branch:
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:
- Remove the intermediate class from GeometricRange
- Fix the code
- Stage the change
Commit the change
- Remove the intermediate class from GeometricRangeRate
- Fix the code
Stage and commit the changes in one step
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.
- Remove the intermediate class from GeometricAzEl
- Fix the code
- Stage and commit the change. (For the rest of this section, I'm omitting the console display for the changes.)
- Remove the intermediate class from GeometricRADec
- Fix the code
- Stage the change
- Commit all staged changes
- Remove the intermediate class from the branch
- Fix the code
- 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:
As you can see, the master branch contains 10 source files that start with the string "Geometric", while the refactoring branch has eight.
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:
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:
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:
then that final set of changes gets staged:
And the code is ready for committing.
6 Commit the Branch Changes
Any code staged in the previous step is now committed:
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:
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.
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:
The merge message recommended that I run Git Prune, so I ran it next:
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:
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
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:
This completes a full developer branching cycle.