~/ When to Rebase

I've come to appreciate that scary git command - git rebase. I've found two incredible uses for it:

  1. Updating feature branches with progress made on master.
    • Avoids those nasty merge commits.
  2. Cleaning up feature branches before merging with master

Updating feature branches

Say you've branched from master to work on a new topic. Since you've started making your edits, your team members have pushed a commit or two to master. A git log will demonstrate the scenario:

* a1caf92 (master) Some updates your team members have made.
| * c4b61f8 (HEAD, feature-branch) Started working on a feature. 
|/
| * 7884fb6 Common ancestor commit. 

If you want to pull in the changes made on master, while in the feature-branch branch:

$ git rebase master

I like to think of this rebase as peeling off your changes from the history, moving up the master branch, and then laying your changes back down on top of master.

By updating your feature branch with the progress made on master, you can avoid merge commits that clutter the git history and contribute (most often) meaningless commit messages.

After the rebase, your git tree will show the feature branch on top of master, which means the common ancestor commit is the latest update on master.

Running git log will give the following graph:

* c4b61f8 (HEAD, feature-branch) Started working on a feature. 
* a1caf92 (master) Some updates your team members have made.
* 7884fb6 Common ancestor commit. 

Since the changes you're pulling in from master are all wrapped up into one commit, you could have just as easily used git cherry-pick to merge the changes from that commit into your feature branch.

Cleaning up feature branches

Quite often, my feature branches consist of several commits. They don't have to be pretty. I commit as often as possible, and try to leave a note about what a change does as often as I make a significant change, or have encountered a state I might want to return to in the future.

Imagine the following tree:

* f3e1236 (HEAD, feature-branch) Remove unused files from repo
* 8be8878 Cleanup CSS
* 0a16b8c Complete testing for feature-branch 
* b42147c Add syntax theming and type size changes
* 08e20d1 Add some more code related to feature 
* 9715882 Start working on feature.
* 8ec539e (master) added indenting with vim post

feature-branch is complete and ready for merge into master, but it's littered with some rather embarassing commits. Knowing that I cleaned up css or removed left over files in a repo inspires a curiosity to view the project just one commit before.

But as savvy git users, we know better. We can remove the commits related to cleaning up, and the end result will be a git history that looks like we just code perfectly without any mistakes along the way.

Given the previous git tree, we can clean up the feature branch into only 1 commit. While in the feature-branch branch, run:

$ git rebase -i master

This will open up a Vim window unless you've configured another editor:

pick f3e1236 Remove unused files from repo
squash 8be8878 Cleanup CSS
squash 0a16b8c Complete testing for feature-branch
squash b42147c Add syntax theming and type size changes
squash 08e20d1 Add some more code related to feature
squash 9715882 Start working on feature.
squash 8ec539e added indenting with vim post

Change every 'pick' to 'squash' for each commit that you want to squash into the previous one.

When prompted to add a commit message for this new super clean and short rebased branch, be sure to write something meaningful. Something like "Implement Feature One" will suffice.

Running git log now will show our clean and readable history:

* f3e1236 (HEAD, feature-branch) Implement Feature One
* 8ec539e (master) added indenting with vim post

Merging feature-branch into master will update master without leaving a merge commit.

* f3e1236 (HEAD, master, feature-branch) Implement Feature One
* 8ec539e added indenting with vim post

Be sure not to push any branches that you plan on rebasing in the future. Rewriting the history will surely raise complications for anyone else who might be working with the history you've pushed. If you want to push them so you can access the changes from a machine while out of the office, be sure to communicate that those branches are unstable and should not be used for new branches.

A note about merge conflicts while in a rebase

It can be totally scary. When manipulating the history, it feels like you could easily mess up your entire project. Fortunately, git helps us out during a merge conflict.

If you encounter a merge conflict, git will prompt you to:

  1. Fix the changes
  2. git add the files to which you made changes
  3. git rebase --continue

That's it! If for some reason you've got merge conflicts out the wozz, you can always abort the rebase entirely with git rebase --abort.


~/ Posted by Jesse Shawl on 2013-12-03