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 !