A piggy bank of commands, fixes, succinct reviews, some mini articles and technical opinions from a (mostly) Perl developer.

Git workflow

See also "A successful Git branching model"

Warning: This practice only works if everyone on a team does the same thing. If there is even one developer who doesn't rebase then the whole system breaks down. A much simpler way to do things is to stick with the defaults of pulling via merge (not via rebase) and endure the extra commits and mangled history. Then if you're using Bitbucket, the UI provides special links to 'sync' out-of-date branches before merging; Exclusively using those links is much safer and easier than manually rebasing. /Warning

Best practice

  1. branch from master
  2. make changes
  3. when you're ready to merge back, first rebase onto master
  4. then merge to master in your working directory
  5. squash all your commits into a single commit
  6. push the single commit to master
UPDATE: If you delete your feature branches after merging (perhaps for audit reasons), it seems better not to squash but to have one additional commit (--no-ff) which represents the merge for ease of backing out, viewing the diff of a single feature, etc. while simultaneously keeping full commit history in master.
Branch names are of the format: xxx-123-foo-bar (where xxx is our JIRA project ID, 123 is the Jira story number, and 'foo bar' describes the story)
If anyone has cut a branch off your branch, be nice and tell them before you rebase.

Detailed instructions

Setup
git fetch
git checkout master
git pull origin master
Branch
git checkout -t -b my-branch
# make changes to code
# run tests
git commit
git push origin my-branch
Rebase
git fetch
git checkout master
git pull origin master
git checkout my-branch
git rebase master
# run tests
git push --force origin my-branch
Merge
git fetch
git checkout master
git pull origin master
    # should already be up-to-date, otherwise go back and rebase again
git merge my-branch
git push origin master
NOTE: Rebasing before merging to master is required to keeping all your commits together in a bunch,
and in the right place at the top of the commit log.
NOTE: push --force will change the commits on your branch that you had pushed previously.
Never push --force to master!

Rebasing onto master before merging

If you don't rebase before merging to master, the master commit log could look like this after merging (commits are shown in reverse chronological order):
  • 4pm commit, in master
  • [lots of other commits in master]
  • 3pm commit, from feature branch                   <-- a="" feature="" li="" new="" of="" part="">
  • 2pm commit, in master
  • [lots of other commits in master]
  • 1pm commit, from feature branch                   <-- a="" another="" feature="" li="" new="" of="" part="">
Someone looking at the top of the log would not realise that a feature has just been merged (and probably deployed).
And even if they did find out, they would not be able to easily pick out the commits belonging to the feature branch.
If you do rebase then you end up with this output, where the feature commits appear all together at the top:
  • 3pm commit, from feature branch                   <-- a="" feature="" li="" new="" of="" part="">
  • 1pm commit, from feature branch                   <-- a="" another="" feature="" li="" new="" of="" part="">
  • 4pm commit, in master
  • [lots of other commits in master]
  • 2pm commit, in master
  • [lots of other commits in master]
NOTE: The same useful log view is possible by passing the --topo-order option to git log, which displays the first example (not rebased) in the same order as the second example.

Squashing

You can optionally squash all your commits into a single commit or several large commits for ease of reading. This is quite controversial and there are good arguments for both sides.
The commit history showing that you tried several different approaches before settling on one, or made and fixed typos may be unnecessary noise. On the other hand, it may be useful to see the broad strokes that were taken to solve the problem. Another view is that all commits are valuable and don't need to be changed.

Consider squashing some of your commits which weren't intended for anyone to see, while keeping others to maintain the general workflow in the individual commit history.

To squash some or all of your commits:
git rebase -i $(git merge-base my-branch master)
  • Change the word 'pick' to 'squash' in order to combine that commit into the previous one
    • you may also change the order of the commits if it makes sense
  • save and exit the file

Project branch

AKA Testing branch/QA branch
After making changes and pushing them to a feature branch, we then merge that feature branch to a project/team branch. After the feature has been tested, the feature branch is merged to master.
A major benefit of this approach is that there's no big "final merge" at the end of an iteration where conflicts from all feature branches must be resolved at the same time. As soon as a feature is complete and tested, it is merged to master.
One drawback of this is that conflicts between features will need to be resolved once when merging to the project/testing branch, and then possibly resolved again when merging to master. We consider the benefits to outweigh this drawback.
There is no branching strategy that can completely prevent conflicts if two people are working on the same code simultaneously. Our solution is: Try to avoid conflicts as much as possible simply by not working on the same area of code at the same time as someone else.
If two people are working on the same code, they should resolve conflicts with each other as frequently as possible (e.g. every day or every couple of days), perhaps by using a merged feature branch. A side effect of this is that all their code must be released together.

Multiple feature branches

Where several stories need to be tested together before being finished. First create a feature branch for an umbrella story, and branch all features off there. Merge back to test them together, and finally merge the umbrella branch to master.
Do not cut feature branches from master as usual and merge them to the umbrella branch, as it then gets contaminated with unrelated commits.
If you some changes from master, first rebase the umbrella branch on master, let everyone know, and then rebase each feature branch on the umbrella branch.

Do not merge after a rebase

We've discovered that actually git doesn't hate us, we've just been using it wrong.
Say two people are working on a feature branch (that's been cut from master), they are happily pushing to and pulling from the branch. Then one of them rebases that branch back onto master. The others all pull the new version, and everything looks fine (except the number of commits might look like it's doubled). But everything isn't fine.
When you come to rebase that branch on master again, (perhaps just before you make the final merge back), you will encounter some weird conflicts - the branch will appear to conflict with its own commits.
The solution is: If the feature branch gets rebased, do not use 'git pull' (as that does a merge by default, and merges don't play nicely with rebases). Instead do 'git pull --rebase', and after that, everything will be groovy. It's best to make 'git pull' always do a rebase instead of a merge:
# make 'git pull' on master always use rebase 
git config branch.master.rebase true

# setup rebase for every tracking branch
git config --global branch.autosetuprebase always
Another way to avoid this problem is to never 'pull': If you find you can't push your feature branch because someone else has made commits to it, then make a temporary branch to store your commits, then re-checkout the latest feature branch from origin (or do 'git reset --hard origin/branchname'), and then cherry-pick your commits back into it from the temporary branch.

General advice

Merge frequently

Merge your code upstream as often as possible, in order to resolve any conflicts early, while the code is still fresh in everyone's mind.

Rebase cautiously

If you're the only developer on a branch, rebase frequently.
If others are working on the same branch, or are working downstream, then you must agree a good time to rebase, i.e. after everyone has committed and is prepared to pull (if on same branch) or rebase (if downstream) and resolve any conflicts.

Document your policy

Write down the rules you plan to follow in order to minimise the pain of merge conflicts and other confusion. Make sure everyone understands the rules.

Communicate

Ensure developers working on the same branch, or downstream of others in the team are communicating enough to get advance warning of possible merge conflicts, so these can be resolved as early as possible, and prevent potential conflicts from all building up and causing pain/confusion at the end of the development cycle.

Avoid dependencies

The best way to avoid conflicts is to organise the work to reduce dependencies in the first place.

  • Horizontal slicing is one way to achieve this (e.g. one person works on the model, one on the view, and one on the controller).
  • Making small changes and deploying them quickly is another way.

There's more than one way to do it

If a set of stories/tasks requires distinct sections of code to be written for the same feature, consider cutting a main feature branch, and having each developer cut a sub-branch from that for their individual tasks. Individual developers could also use a sub-branch for wide-ranging experimental changes. Or if developers feel more comfortable committing to a single large feature branch and co-ordinating with each other, or if that just makes more sense for a particular feature, then do that instead.