Interactive Rebase

Git rebase has an interactive mode which helps you in rough times while working with git. You might come across situations where you have to alter what you’ve already committed. Interactive rebase provides us with tools and functions which helps us to do such things. Let’s do a quick recap of what rebasing is. By now we know that rebasing re-base our local commits on the top of commits done in the base branch.  And due to this the commit hash value changes and it act as a totally new commit with same changes as in earlier one. Hence, we shouldn’t rebase any public branch. That’s just to get a rough idea about rebasing. For more detailed insight you can refer here.

Rewriting the commit history

Interactive rebasing helps you to rewrite your commit history in case you find that you committed something wrong or some previous commit required more work to be done before committing. To tackle and get out of these problems, we have this magic wand in our hand. So, let’s know how can we use it.

Let’s create few files in our git repo(You know how to do it, right?). First create a file with name first.txt with the following content in it

This is the first file in this repository.

and then we add the changes to staging area. And finally commit it as

git commit -m "Add first.txt"

After this create another file with name second.txt with the following content in it

This is the second file in this repo.
This content needs to be deleted.
We'll do it later

And commit it as

git commit -m "Add third.txt"

Yeah, I know the file name is second.txt but I have done it intentionally. We’ll correct it later. Just keep reading. 🙂

Finally create the actual third.txt with the following content in it:

Yeah! this one is the last file.

and commit it as

git commit -m "Add actual third.txt"

At last, enough preparing. Now is the time to use our magic wand. As of now, our commit history looks like this

d6f128b Add actual third.txt
b2d48ff Add third.txt
e57559a Add first.txt

What? You also want to see your commit history like this instead of that long output of git log! Ok let me tell you then, I just used few options which git log supports

git log --pretty=format:"%h %s"

To get to know more about such options, do give a read to this.

Now we want to correct the content of our second file and then commit it with correct commit message. For that we can use git rebase as

git rebase -i HEAD~2

Ok. Let’s first understand what does this command mean?  For interactive mode we used -i option, short for –interactive. And then we give the number of commits we want to consider. As we know, HEAD always refers to the latest commit of current branch by default. So, to take 2 commits from HEAD(since the second commit from HEAD needs to be corrected), we used ~ symbol. When you’ll hit enter, a text editor would open with following content

pick b2d48ff Add third.txt
pick d6f128b Add actual third.txt

# Rebase e57559a..d6f128b onto e57559a (2 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# 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 last two commits would be shown in older to newer fashion. Git is smart enough to explain you the various options which can be applied to each commit. By default each commit is marked with pick command. If you leave a commit with pick command, it just applies that commit as it is.

In our case we want to edit our first commit in the list, if you see the list of options available, can you guess which option should we use? Yup, you are correct, we would need to use the edit option. We can also use it’s short form ie ‘e‘ as

e b2d48ff Add third.txt
pick d6f128b Add actual third.txt
...

Now, as you’ll save the file, you would see a message as

Stopped at b2d48ff...  Add third.txt
You can amend the commit now, with

  git commit --amend 

Once you are satisfied with your changes, run

  git rebase --continue

So what exactly happened? Let’s understand it, rebase starts to take each commit one by one in the list and performs the command defined against them. In our case, the very first commit in the list comes up with the ‘e‘ option, which says rebase to stop at this commit until user tells it to continue. And as expected it stopped at the commit. Now if you do git log as

git log --pretty=oneline

and it would give you the output as

b2d48ff1ba706f9751bd950e17355a9fb9a3fd99 (HEAD) Add third.txt
e57559a96cd0c5b9f2ef4e2dccbcf6f20b3b11d4 Add first.txt

You can see that now our HEAD is at the commit at which rebase stopped. Now we can edit this file. So open second.txt and delete the last two lines. It would be now

This is the second file in this repo.

and then add the changes. Since its commit message was also incorrect, we can correct it now using the –amend option. With this option we can amend the commit refer by HEAD. Since our HEAD is at the second commit, we can now amend it. For that write the following command

git commit --amend

And hit enter, it would open a text editor with the previous incorrect commit message. So we can correct it now and save that file. Now our commit message has been amended. You can check that using git log command. Now do git status

interactive rebase in progress; onto e57559a
Last command done (1 command done):
edit b2d48ff Add third.txt
Next command to do (1 remaining command):
pick d6f128b Add actual third.txt
(use "git rebase --edit-todo" to view and edit)
You are currently editing a commit while rebasing branch 'master' on 'e57559a'.
(use "git commit --amend" to amend the current commit)
(use "git rebase --continue" once you are satisfied with your changes)

nothing to commit, working tree clean

git tells us that interactive rebase is in progress. It means that there are other commits yet to be processed and it also tells you the next command which is going to get execute. So simply do git rebase –continue to continue the interactive rebasing and then it would show you the message like

Successfully rebased and updated refs/heads/master.

Now again look at the git log, in my case it is

9259ca9 Add actual third.txt
7092e07 Add second.txt
e57559a Add first.txt

You might have noticed that the hash value of first two commits now has been changed. You know it, right? Yes, it’s because rebasing replays each commit and gives it a new hash value.

Now you know the procedure it follows. Let’s look at other available options rebase provides.

  • Squash

Indeed, squashing is one of the most used technique used by many contributors and anyone who works with git.  In simple words, squashing squashes more than one commit into a single commit. Let’s try it. Suppose we want to squash the first two commits in one in our above example. We’ll again give the same command for interactive mode. Can you write it without scrolling above? Nice. Now the text editor would open with following content

pick 7092e07 Add second.txt
pick 9259ca9 Add actual third.txt
...

You just need to write ‘s‘ option. write it as

pick 7092e07 Add second.txt
s 9259ca9 Add actual third.txt

And then save the file. As soon as you’ll save the file, another text editor would open. It would contain the commit messages of both the commits as git doesn’t know which commit message to take. Either you can keep any one of them or you can rewrite a new commit message. After writing the commit message, just save the file and it’s done. You just squashed your commits in one. Now my commit history looks like this.

b805b0a Add second.txt and third.txt
e57559a Add first.txt
  • fixup

If you see the options in rebase text editor, you’ll find this also. fixup is also used to squash the commits together. Then what’s the difference between the two? Well, it’s simple. fixup discards the commit message of the commit on which it is applied. Just this. You just have to use ‘f‘ option instead of ‘s‘ for it.

  • reword

Well, if you want to correct the commit message of the latest commit,  you can use –amend option. But if  you need to correct the commit message of previous commits, just use ‘r‘ option for whichever commit’s  message you want to correct. As one by one it would take commit and as soon as it reaches at the commit with option ‘r’, you would land into another text editor where you can correct it and save the file. And rebase would continue by itself.

  • drop

If you want to remove a commit from your commit history, then use ‘d‘ option short for drop and it would be removed. Do notice that the changes will remain there, just the commit would be removed from your history.

  • reorder

If you want to re-order your commits, then just change their position in the rebase text editor. And it’ll be done. So easy, isn’t it?

  • edit

We just used edit option in the initial example. It is used in case you want to stop at some particular commit and perform any action on that commit specifically.

These are the some of the most helpful functions while using git. You’ll most often come across using these. However, there are some facts which I’ve experienced and can confuse you if you go with default options.

  1.  git rebase -i ignores merge commits until and unless you use the flag ‘-p‘ abbreviated for –preserve-merges.  For more details, you can refer to this answer on stackoverflow.
  2. You can’t rebase the initial commit of your repository. So, in above example if you would try replacing HEAD~2 with HEAD~3 in order to get all the three commits in rebase it would give you the error as
fatal: Needed a single revision
invalid upstream 'HEAD~3'

Since the 3rd commit is the root commit of our repository. If you want to change the root commit of your repository and want to know about it then you can refer to this answer on stackoverflow.

And yes, I again want to focus on the part that rebasing changes your commit history. So, you should never rebase a public branch. That’s all from my side.

References:

I hope this would be helpful. I am still a newbie with it. So, if you find any correction or doubt then don’t hesitate to write in the comment sections below. Meet you soon.

Till then, be curious and keep learning!

Advertisements

2 thoughts on “Interactive Rebase

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s