Altering commits with Git Rebase: The setup

Part 1 of using rebase unconventionally to alter previous commits in the project history

Rebasing is the process of moving or combining a sequence of commits to a new base commit.

The above line may sound scary at first, but rebasing is simply altering all the commits on top of a particular commit in a base branch. For simplicity, the series has been divided into two parts and by the end of it, you'll have the following questions answered for you:

  • How can I alter the commit message of a previous commit?

  • How can I alter the date of a previous commit?

  • How can I alter the author of a previous commit?

  • How can I discard a previous commit?

Rebasing vs Merging

While rebasing might sound similar to merging, as both are used to solve the same problem of integrating changes from one branch to another, there is one key difference between them:

  • Merging creates a new commit with all your changes from the source branch to the target branch. It is the simplest way to integrate your changes, the only downfall being that the process becomes very tedious in case of conflicts between the two branches.

  • Rebasing on the other hand moves the entire change history from the source branch to the top of the target branch and re-writes new commits for all the changes instead of a single merge commit. It is a more complex process, the downfall being that it re-writes the project history which is not so desirable.

Interactive Rebasing

Git offers rebasing in two modes - standard and interactive. In the standard mode, the commits are taken from your source branch and automatically applied to the top of the target branch. Interactive mode on the other hand allows you to alter commits on the way before applying them to the target branch, thus allowing you to edit, remove or split commits before merging them.

We would be exploring Interactive Rebasing, which is the answer to all those questions at the beginning of this article. Although rebasing is used to merge changes from different branches, we would be rebasing from and into our base branch itself as we want to alter the commit history of the base branch. Let's fire up the Terminal and begin with our rebase without any further ado.

Note that, rebasing commits that have been pushed to a public repository is not recommended, as it could replace the old commits with new ones, looking as if a part of the project just vanished, thereby also altering the committer of those commits and tampering with the project commit history in an undesirable way. (Just for clarity, a commit author is the one who made the changes and the commit initially, whereas a committer is the person who modified the commit and rebased it)

Blast off!

Enter the interactive mode

Begin rebasing the current branch using the git rebase command with the i flag which stands for interactive.

$ git rebase -i <base>

Here <base> is the commit on top of which you'd like to rebase. You can specify it as --root to start rebasing from the very beginning of the commit tree else specify a commit SHA or HEAD position.

$ git rebase -i --root
#OR
$ git rebase -i HEAD~4
#OR
$ git rebase -i 42ca410

Make your choice

Once you enter the interactive mode, you'll be able to see all the commits made from the base you specified in the above command to the latest commit in ascending order (the base commit comes on the top), along with a command written before each of them. It is actually a file opened in the Vim editor, something like this:

pick acdb98c Initial commit
pick edbs23s Commit Message 0 #Commit 0
pick 4sah32b Commit Message 1 #Commit 1
pick 1ab343e Commit Message 2 #Commit 2

# Rebase <Source Branch SHA> into <Target Branch SHA>
# ...

Press I on the keyboard to enter into the INSERT mode of the editor. Now against each commit, you wish to modify, enter one of the following basic commands depending on what action you would like to perform. If you want to leave the commit unaltered then let the command be pick.

  • pick: Include the commit in the rebasing (i.e. do nothing)

  • reword: Edit the commit message only

  • edit: Alter the commit

  • drop: Drop the commit from the history (i.e. delete)

Some other commands offered during the rebasing apart from the above are squash, fixup, exec, break, label, reset, merge and update-ref, which can be used to control various aspects during the rebasing. For the scope of this series, we will constrain ourselves to the above 4 commands.

After making the choices, we end up with something like this:

pick acdb98c Initial commit
reword edbs23s Commit Message 0 #Commit 0
edit 4sah32b Commit Message 1 #Commit 1
drop 1ab343e Commit Message 2 #Commit 2

--INSERT--

This means we want to edit the commit message of Commit 0, edit Commit 1 and delete Commit 2 from the commit history. Hit Esc and to save the file and begin rebasing, type :wq else to quit without saving type :qa.

Note that, the lines of the commits in the above file can be re-ordered and they will be executed from top to bottom. Beware that if you remove the line of a commit from this file, then the commit will be lost forever. Removing all the lines will cause the rebase to abort.

Conclusion

In this read, we learnt about rebasing in a brief, the difference between merging and rebasing and began the process of interactive rebasing for the project. Rebasing is very useful in getting a linear commit history, and sometimes we can even use it for our own gains (as we are going to do in the next part of the series :)). The next part will continue from where left off and dive deep into the specifics of the rebase itself. Let's jump on to part 2: The rebase!