...or How I Learned To Stop Worrying And Love The Rebase.
During the inception of our new project I've been working on a proposal for how people can work on features within the organisation. This proposal takes into account branch staleness and preparing a pull request to be merged back into master.
NB: We use Github flow so create short-lived feature branches which are merged into master.
NB: This document is not exhaustive and a working knowledge of git is assumed.
Stage 0: I want to do something!
Our issue tracking is done through JIRA so every item of work in the repo is linked to a JIRA ticket and must be added to master through a branch and a pull request. We don't commit directly to master, ever.
Stage 1: I have a ticket, I want to do work!
First you need a copy of the code.
> git clone <repository url>
Next you'll be needing a branch. Our branches are named with the JIRA ticket number, so if the feature you're working on has the ID PROJECT-01 then your branch should be called PROJECT-01. This can be created using:
> git checkout -b PROJECT-01
...then to add it to remote (to Github etc) use:
> git push -u origin PROJECT-1
If you don't have a ticket, then you shouldn't be working on a feature. :) Now you're in your branch, you can code away! Each commit to git should follow our standard commit message format and make sure the tests pass (and that you /have/ tests).
Stage 2: I want to keep my branch fresh!
We follow continuous integration principles so it's important for us all to make sure we're integrating often and early. By performing regular integrations with what's happening on master, we not only benefit from new features that may be available but we also deal with conflicts as they happen (and they will happen) rather than right at the end when we're trying to merge the feature back in.
Merging into master (when a feature is peer-reviewed and signed-off) should be an 'easy' task of just performing a non fast-forward merge (when we perform a merge that creates another commit on master of "Merge feature X". If it's not easy it's probably because the branch got all crufty and stale...
As often as possible (at least once per day is our team approach) we should be running 'git up' which is a custom command to update master (and then your branch). To do this add into your ~/.gitconfig file:
[alias]
up = !git pull --rebase --prune $@ && git submodule update --init --recursive
This command pulls and rebases from origin and also uses prune which removes branches in your local repo which have been removed from remote (for example if the branch was deleted in Github after being merged).
So with this custom command, you can do:
> git checkout master
> git up
> git rebase master PROJECT-01
(or obviously whatever your branch is called) You may get conflicts, in which case you can resolve them and push these to origin.
This looks more confusing than just pulling from master, but this approach has the effect that a branch is wound forward in time to appear that it was created from a later commit, rather than having 'merge commits' from master in the branch which at some point would require a merge back into master, which is confusing and can make rebasing your branch much more problematic.
Stage 3: I'm finished so I want to submit my branch for review
Woah, that was quick! :)
You'll need to create a pull request (PR) on Github then ask at least two of the team to review this PR. You may have to go through some cycles of updates before it is signed off, but you'll need at least two separate members of the team to sign off a feature; they will do this by awarding a :shipit: squirrel to the PR.
If your PR has more than one commit, you will be asked to squash these commits using rebase. The process is quite straight-forward in most cases.
Firstly, determine the 'merge base' of your branch by running:
> git merge-base <yourbranch> master
...(where <yourbranch> is the name of your branch). This will return a hash such as:
b0dea256444af445676e00dd362e3aab9862bc25
Use this hash to 'rebase' your commits by using:
> git rebase –i <thehashyougotpreviously>
This should open an editor showing something like:
pick b0dea25 = PROJECT-01: add library for foo feature
pick 248cd4f + PROJECT-01: add feature to do foo
pick 7dae0c4 = PROJECT-01: add extra fixture for foo
For this branch we have three commits, two having no effect on the public API and one having an effect. We don't want all of the feature commits in master as they may not be appropriate in that context so we're going to squash our commits into one. Edit the file to look something like:
pick b0dea25 = PROJECT-01: add library for foo feature
squash 248cd4f + PROJECT-01: add feature to do foo
squash 7dae0c4 = PROJECT-01: add extra fixture for foo
Exiting your editor will run the rebase then present you with a load of stuff to use as the commit message. Delete all of this stuff and replace it with something like:
+ PROJECT-01: add new foo feature
..then you'll need to push these changes up to Github using
> git push -f
You should now see your PR with one lovely commit which describes the feature. Note that if you have any commits containing either an additive (+) or breaking (!) change, your single commit should reflect this by having this as its prefix.
So our commit would now look something like:
commit 3c48d7a844dd1e504eef12bbab57d5585b615979
Author: Beth Anderson <beth.anderson@example.com>
Date: Thu May 21 17:48:41 2015 +0100
+ PROJECT-01: add foo feature
Stage 4: My branch is reviewed, I want to merge into master
Once you have at least two :shipit: squirrels (we call this the Meatloaf Protocol) then the PR can be merged in Github, although if it can't be merged automatically then you may have to go through another round of sync-ing, which could mean code changes, which could mean re-review...hey, nobody said working in teams was easy! ;) As we follow the Github Flow process, when the PR has been merged successfully, the branch should be deleted as it's now done its job and come to the end of its life.
You've done it! Now time to bask in the warm glory of submitting a feature. A hearty "boat drinks" to you! :)