Cleaning Up Commit History With Git Rebase

Git rebase is a powerful command with multiple use cases. In this post I want to focus on a specific use case that makes it a very valuable and easy to use tool :

Cleaning up my commit history.

But why ?

It often appends to me, while working with Heroku/Gitlab CI or other tools, that I fix a bug, commit the changes, push them and realize then only that these changes are uneffective. It often results in a commit history that looks like this

20:31 | er0scc0 | quick fix final
20:27 | oesf800 | quick fix 2
20:21 | bf6544a | fixing ccs compile error

These commit messages aren’t helpul at all (they don’t help understanding the dev process of the app) and they can become a real problem on a shared repo. However, they are also easy to fix with git rebase.

How ?

We will use the interractive option of the command : -i.

git rebase -i HEAD~3 will allow us to modify the last 3 commits - If we want to get back to a specific commit, we can use a commit hash instead of HEAD~3.

It opens up an editor (most likely Vim) with the following content.

   1 pick er0scc0 quick fix final
   2 pick oesf800 quick fix 2
   3 pick bf6544a fixing ccs compile error
   4
   5 # Rebase 3254b46..8bd4511 onto 3254b46 (3 commands)
   6 #
   7 # Commands:
   8 # p, pick <commit> = use commit
   9 # r, reword <commit> = use commit, but edit the commit message
  10 # e, edit <commit> = use commit, but stop for amending
  11 # s, squash <commit> = use commit, but meld into previous commit
  12 # f, fixup <commit> = like "squash", but discard this commit's log message
  13 # x, exec <command> = run command (the rest of the line) using shell
  14 # b, break = stop here (continue rebase later with 'git rebase --continue')
  15 # d, drop <commit> = remove commit
  16 # l, label <label> = label current HEAD with a name
  17 # t, reset <label> = reset HEAD to a label
  18 # m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
  19 # .       create a merge commit using the original merge commit's
  20 # .       message (or the oneline, if no original merge commit was
  21 # .       specified). Use -c <commit> to reword the commit message.
  22 #
  23 # These lines can be re-ordered; they are executed from top to bottom.
  24 #
  25 # If you remove a line here THAT COMMIT WILL BE LOST.
  26 #
  27 # However, if you remove everything, the rebase will be aborted.
  28 #
  29 # Note that empty commits are commented out

The most useful commands for our use case are going to be pick, reword, edit, squash and fixup. We want to keep the modifications we made but change the overall commit message. So we are going to change the file accordingly.

   1 f er0scc0 quick fix final
   2 f oesf800 quick fix 2
   3 r bf6544a fixing ccs compile error
   ...

With these changes, we meld the commits from lines 1 and 2 into line 3 commit. And we ask to edit the commit message of line 3. We then save and close the file.

Another file opens and we are asked to promped to enter the new commit message for commit bf6544a. (e.g. “Setting up for Heroku”)

Once we close the file, the terminal reads

Successfully rebased and updated refs/heads/master.

And a git log shows a simple

bf6544a Setting up for Heroku

We got rid of the unhelpful commits, rewrote our commit history, and left a more informative commit message. Job done !

In case anything goes wrong, the commits we changed or deleted aren’t gone forever, we can use git reflog and recover our commit history. It is going to show us the hidden/deleted commits and we can then come back to a stable commit with :

git reset --hard <commit-hash>

This is only one use case and for further reading on git rebase there is the Git-rebase Documentation. Furthermore, I also found this article interesting Merging vs. Rebasing.

Thank you for reading !