Git Guides - Jacob Herrington (9 Part Series)
Recently (not that recently), I wrote an article explaining the oft-misunderstood git command:
There I tried to ensure that anyone could glean the purpose and basic application of
In this article, I'm going to explain a more intimidating application of
git rebase. Choosing to rebase in this way allows me to convey the purpose and reasoning behind commits with clarity.
I'm talking using
git rebase in the optional interactive mode. It is my preferred method of organizing code changes; good organization makes git a more powerful tool.
If you're unfamiliar with rebase, I'd give the previous article a look before reading this one. This article will provide a glimpse into more powerful applications of the command.
git rebase -i invokes a rebase in interactive mode.
Why is it called the interactive mode? Glad you asked. Interactive mode allows the programmer to influence how rebase executes. In the previous article, we used rebase to bring upstream changes into our branch, but we didn't manipulate any commits in that process.
In many ways, the process we previously followed is ideal; we didn't have to do any work, git handled everything for us!
Unfortunately, the real world is rarely ideal.
When it comes to git, we frequently make incomplete commits, forget to include important changes, or make bad commit messages.
In our scenario, we can imagine that we've got a branch called
some-feature which looks something like the following:
47a0a7008b Add a rea322ally cool feature 6dbc838bae WIP forgot something from that feature f2ee0378f9 WIP fixing a bug f0978af68d Fixed the very annoying bug 61b6f8ea3e Fix a typo from my doc changes 8340051649 Update documentation f8a81956e2 Fix documentation formatting
There are a few things that we might not like about this git history. For example, we have a couple of WIP (work-in-progress) commits that could be removed, a typo in one of our commits, and a commit that fixes a typo that we introduced in a previous change on this branch.
We are planning to merge this branch into another branch called
To fix the obnoxious git mistakes in this branch, we can use
git rebase -i master.
When you run
git rebase -i with another branch you'll be dropped into a text editor with the following prompt:
pick 47a0a7008b Add a rea322ally cool feature pick 6dbc838bae WIP forgot something from that feature pick f2ee0378f9 WIP fixing a bug pick f0978af68d Fixed the very annoying bug pick 61b6f8ea3e Fix a typo from my doc changes pick 8340051649 Update documentation pick f8a81956e2 Fix documentation formatting # Rebase a6305fd0a..4a5782397 onto a6305fd0a (1 command) # # Commands: # p, pick <commit> = use commit # r, reword <commit> = use commit, but edit the commit message # e, edit <commit> = use commit, but stop for amending # s, squash <commit> = use commit, but meld into previous commit # f, fixup <commit> = like "squash", but discard this commit's log message # x, exec <command> = run command (the rest of the line) using shell # b, break = stop here (continue rebase later with 'git rebase --continue') # d, drop <commit> = remove commit # l, label <label> = label current HEAD with a name # t, reset <label> = reset HEAD to a label # m, merge [-C <commit> | -c <commit>] <label> [# <oneline>] # . create a merge commit using the original merge commit's # . message (or the oneline, if no original merge commit was # . specified). Use -c <commit> to reword the commit message. # # These lines can be re-ordered; they are executed from top to bottom. # # If you remove a line here THAT COMMIT WILL BE LOST. # # However, if you remove everything, the rebase will be aborted. # # Note that empty commits are commented out
The prompt is extremely helpful, but sometimes a wall of text like this one can be intimidating.
In this view, git is showing you a list of the commits that exist in your branch, but not in
master. They will be replayed on (or added on top of) the history in
master. Each commit has a "command" alongside it.
pick 47a0a7008b Add a rea322ally cool feature shows the command
pick on the commit
47a0a7008b followed by its commit message.
Each command does something unique, you can read through the commands listed in the prompt if you like, but my most used commands are:
To progress in the rebase you should replace the
pick command with your preferred commands, then save and close the file.
I'd fix our example history like this:
reword 47a0a7008b Add a rea322ally cool feature fixup 6dbc838bae WIP forgot something from that feature pick f2ee0378f9 WIP fixing a bug squash f0978af68d Fixed the very annoying bug pick 8340051649 Update documentation fixup 61b6f8ea3e Fix a typo from my doc changes squash f8a81956e2 Fix documentation formatting
A few things happened.
First, I grouped the documentation commits together. This makes it easier to squash and follow the git history.
Secondly, I chose to
fixup several of the commits that were work in progress commits or fixes for issues I might have created while working on this branch (minor typos, formatting, work-in-progress commits).
Finally, I chose to reword the first commit message because it contains a typo.
Upon saving and closing this file, git will go to work. Each time a commit is squashed or reworded, I will have an opportunity to write a new commit message.
For example, when I hit the first commit I can reword it to "Add a really cool feature." When I hit one of the squash commits, I'll have the option to merge both commit messages or rewrite it entirely. Those commits with the
fixup command are treated exactly like the ones with
squash except that the commit message is completely dropped.
Assuming we don't have any merge conflicts, once the rebase has completed, the git history might read something like this:
47a0a7008b Add a really cool feature f2ee0378f9 Fix a bug 8340051649 Update documentation
Those commits are much easier to follow and should make for a cleaner PR.
You can also follow me on Twitter, where I make silly memes and talk about being a developer.