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)
- Find out (approximately) how far back is the commit from HEAD. Let's assume that number to be 10.
- 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 Monthsa60b3592
feat(Robusta): use email/pass from config Change thepick
keyword with thedrop
(ord
) 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.