The power of Git Rebase

Introduction

Git, is the Version control system used by almost all the open-source (and many closed source) projects today. However, not everyone who uses Git, understands it's true potential. In fact, many people miss out on some of the core features due to which Git is such a popular choice among giant tech companies and nerdy-hackers alike.

To wit, one such feature would be the interactive rebase that Git provides.

Rebasing with Git

There may have been times when you thought, oh, I wish I could just move some fixes (such as typos) into the previous commit, maybe reordering the commits or deleting them all together. However, up until now, you just were in the habit of making the fixes and giving them a special commit instead. Rebase can help with that.

Let's look at some of the options that rebase provides us.

Firstly, rebasing is essentially a form of rewriting history within Git.

So, you would have a bunch of commits that you want to work upon and they can be referenced starting from the HEAD of the branch (i.e. the latest commit) till the commits you want to work upon.

To start an interactive rebase on the first 3 commits from HEAD you would type in your terminal

Here, you will see a menu with a host of options.

Let's go over some of them.

To reorder the commits

You can reorder them in the interactive window. It will refect when you save and quit (:wq).

Note that there might be times when you have merge conflicts, resolve them as you normally would and commit it back.

Finally, force push so the rebase overrides the previous commits, the history is rewriten and the commits are exactly as you wanted them to be.

Rewriting commit message

In case you want to rewrite the commit message for the most recent commit, using amend

This will show a window with your most recent commit, and you can rewrite the message as needed.

If the default editor is vim, you would need to enter the Insert mode by hitting i and then make changes as required. Once you are satisfied with the message, then use the esc key to move back to normal mode so that you can save and quit using :wq That's it, your commit message is rewritten

In case you want to change the commit message for a commit that was made before the most recent one, the HEAD is no longer attached to that commit. In that case, the easiest way to rewrite the commit message using the reword option in the Interactive Rebase. Assuming that the commit message that you want to rewrite is 3 commits behind HEAD (HEAD->most-recent-commit(#1)->#2->#3) First, enter rebase including the most recent 3 commits,

You will see the commits in the reverse order.

The top most commit will be the #3 commit, or in other words, the commit that you want to operate upon. Change the pick keyword with the reword keyword. You can use the shorthand version, i.e, r as well.

Note: You will have to use vim keybindings explained above to make the changes, save and quit.

Now, you will be shown a screen with the commit message on top

Change this to whatever is required, save and quit.

You would see a message such as Successfully rebased and updated refs/head/master

Why can't I push right after rebase?

Delete commit from history

In case you want to delete a commit (make it so it was never commited)

  1. Find out (approximately) how far back is the commit from HEAD. Let's assume that number to be 10.
  2. Open interactive rebase:

You will see the last 10 commits in the reverse order.

Now, let's say that you want to remove the commits:

  • 2d8466da Uploaded to Already Uploaded For Months
  • a60b3592 feat(Robusta): use email/pass from config Change the pick keyword with the drop (or d) keyword.
  • Note: You can also comment out or delete the commits

Once required changes have been made, save and quit (:wq) You would see a message such as Successfully rebased and updated refs/head/robusta-dev

  • Note: you might have to fix merge conflicts if new commits depended on the deleted ones in some way.

Common Note

If you do git status or git push after a rebase, you might see something like this:

Not to worry, all you need to do here is force push to branch (or whichever branch you are operting on) This is happening essentially because we have rewritten history (as can be seen from the commit hashes before and after rebase). However, since we know that the current changes are what we want to show in Git, we will push these changes and override the original commits.

Splitting commit into smaller commits

There could be a situation where you committed a lot of changes as a signle commit, and later realized that they should be separate commits instead. To fix this, we can use interactive rebase. Suppose that you want to break the last commit into smaller commits. Start interactive rebase on HEAD~

Interative rebase window will show:

Change the pick keyword to edit:

At this point, your rebase will be in halted state. Here you can unstage the changes using reset

git status will show something of this sort:

Now, you can commit the changes as per your liking maybe using the patch option

Once you made the commits as per your original requirement, you can continue the rebase opertaion

Combining Commits together

You can combine commits together using the interactive rebase. Suppose you run interactive rebase on 4 commits,

The interactive rebase window will show:

Now if I want to combine commit 123, and 789 together

  • First I will reorder the commmits
  • Then I will change the pick keyword for 789 commit to squash

Saving this state will open a window such as this:

Here, we can decide on what commit message we want to keep. Once the commit message is finalized,

just save and quit

You will see a message "Successfully rebased and updated refs" Finally, force push to master (if required), and you're done!

Change Commit Date of an Earlier Commit

Open interactive rebase:

Change the pick keyword with edit (or e)

Now when I save this, the rebase prompt will close. Here, you would currently be in the commit "789" (i.e. the commit on which you wrote edit). Now, in your terminal you will type:

This will set the commit date to "Febuaray 8th, 2021"

Finally, type the following in your terminal:

For the changes to reflect on remote branch, force push to master.

Do note that this might cause other commit's to have the date of your rebase, since you are essentially rewriting history, so maybe you want to use the "edit" keyword on all the commits. You can also try to use the --committer-date-is-author-date flag, but I haven't tried this myself.

You can also try the method mentioned in this answer for changing the commit date.

Thank the author. Fork this blog.


Tagged in git