Skip to content

Instantly share code, notes, and snippets.

@timj
Last active August 29, 2015 14:01
Show Gist options
  • Save timj/92bd4523f8e4db4b0b4f to your computer and use it in GitHub Desktop.
Save timj/92bd4523f8e4db4b0b4f to your computer and use it in GitHub Desktop.
Feature branches primer for Starlink

For very simple patches that are known to be distinct from patches worked on by other people, git-pull --rebase may be sufficient to overcome the problem with diamond commits messing with the history. For any type of meaningful development the recommendation for Starlink is to always use a feature branch and handle merging/rebasing in the branch before fixing up master. Git is designed to make branching trivial so we should make use of the facility. It's almost no additional work to work on a development/feature branch (it's known as a "feature" branch because the concept is that each feature you add is kept conceptually distinct in the history).

% git branch dev
% git checkout dev
< edits and commits>

Meanwhile origin/master has had some changes so bring them into your repository

% git checkout master
% git pull
% git checkout dev

If you have some uncommitted changes you may want to stash them first, especially if you are worried that the pull will conflict with a local change (changing branches preserves local changes but can cause conflict if that file is changed in both branches), so in long hand that becomes

% git stash
% git checkout master
% git pull
% git checkout dev
% git stash apply

Now you have an up to date master and some changes on your dev branch

 A - B - C - D - E            master
          \ - F - G            dev

Since no-one has seen your patches you are free to tweak the SHA1s at this point. Rather than merge dev into master (resulting in a merge commit) you can linearize the history by rebasing

% git rebase master

will reapply F and G on your branch as if they happened after D and E. This may result in a conflict (but it would have resulted in a conflict when merging) so you can resolve the conflict and use the --continue option to continue the rebasing. You now have

 A - B - C - D - E             master
                 \ - F' - G'   dev

and if you want to push those back to the repository

% git checkout master
% git merge --no-ff dev
% git push

As written you will get a merge commit because of the use of the --no-ff option. Your history will now look like:

  A - B - C - D - E - -  - -  - M
                  \ - F' - G' /

The merge commit indicates to a reader of the history that F' and G' were done as part of a single feature. Indeed, it is common for the merge commit to include the Github issue number along with some text to indicating that it is close:

Completes #4

when the commits are pushed the ticket #4 will automatically be closed. Without this merge commit there would be no way of knowing that a feature was finished or which commits were part of it. The only solution would be to squash F' and G' into a single commit and include the information in that commit.

In some cases you don't need a merge commit. For example you were working on a few small unrelated things on a development branch and you just want them to be added to master, or your feature was conceptually a single self-contained commit. In that case the rebase and merge will result in a "fast forward" merge with no special merge commit. Your history without the --no-ff would look like

  A - B - C - D - E - F' - G'  master+dev

HEAD is simply moved to G' and dev and master will share the same HEAD.

The push will work so long as no-one has committed since you pulled. You can now delete dev since it has no additional information.

 % git branch -d dev

If you keep dev alive it will branch from G'

  A - B - C - D - E - F' - G'
                            \ - H
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment