Working with master and project branches in Git

Bringing project work into the main branch
$ git checkout master
$ git merge projectbranchfrommaster

If there are merge conflicts, you will need to resolve them and commit the changes.
    x     <- merge commit
    |  \
    x   o <- commit on main branch and conflicting commit on project branch
    |  /
If there aren’t any conflicts, you'll get a 'fast-forward' merge which just brings in the new commits on top of the branch
But it’s nice to create merge commits when a project branch is merged into the main branch, to show how the commits relate to each other. This can be achieved using
$ git merge --no-ff projectbranchfrommaster

Bringing a project branch up to date
$ git checkout projectbranchfrommaster
$ git rebase master

Given a project branch taken from a main branch which has since moved on, this makes your history look as if the project branch was taken from the most recent point of the main branch. If you want to rebase onto an earlier point of your main branch, to change
   x2    o
    |   /
you can use
$ git rebase --onto x1 master [projectbranchfrommaster]
where ‘x1’ is the commit ID of the commit you want to branch from. You’re specifying the new base, the parent of the first of the commits you want to move, and, optionally, the last of the commits to be moved.

If you encounter conflicts during a rebase and your merge tool talks about ‘theirs’ and ‘mine’, remember that ‘mine’ refers to the commit you are rebasing onto and ‘theirs’ refers to the commits that are being rebased onto it. (The terminology confused me initially, since from my point of view the rebased commits were ‘mine’ and the new base was ‘theirs’.) Unlike merge conflicts, you don’t need to commit your conflict resolution when rebasing – Git will include the changes in the reapplied commit when you do
$ git rebase --continue

Coping with upstream rebases
The difference between rebasing and merging is that rebasing reapplies your commits. When the rebase starts you will see a message
First, rewinding head to replay your work on top of it...
and once you have rebased your commits will all have new commit IDs. So if you have any branches which have been taken from the rebased branch you will need to rebase them onto the new version of the branch. (Hey, I’m not saying rebasing upstream branches is a good idea. I’m just making a note of how to cope if it happens…)

If nothing changed in the rebased commits, then Git is apparently clever enough that you can just do
$ git checkout projectsubbranch
$ git rebase projectbranchfrommaster

(Disclaimer: I have never tried applying this version and so can’t vouch for whether Git really is that clever.)

However, if there were any changes during the rebase – such as a conflict resolution – you’ll need to use --onto. As before, you specify the new base and the parent of the first of the commits you want to move. If you have 3 commits in the branch you want to move, you can refer to the parent of the first commit in it as projectsubbranch~3, so you can use:
$ git rebase --onto projectbranchfrommaster projectsubbranch~3

And finally…
And if everything goes horribly wrong, remember that you can undo a rebase using the instructions here: look up the head commit of the branch prior to the rebase in the reflog and do a hard reset to it (see my post on git workflow solutions on how to do this).

git-rebase(1) Manual Page
Recovering from an upstream rebase
Examples of getting rid of unwanted commits with a rebase

About Jennifer Phillips Campbell

Software Developer and Medieval Historian
This entry was posted in Version Control. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s