It can often happen that you did something wrong by using GIT: you gave the wrong commit message, created too few or too many commits, you have commits in the wrong order or with the wrong content. To change such things you have to change history in GIT. Interactive rebase in GIT is an efficient and powerful tool for that.
Git rebase can change the commits between the current HEAD and a dedicated commit. You can define the commit either by its hash or by its index relative to the current HEAD by giving HEAD~n, where n is the index. So HEAD~10 means the 10.th commit before the current HEAD.
To change the last 3 commits before the current HEAD type git rebase --interactive HEAD~3 or git rebase --interactive hash_of_the_third_commit_after_head. Pay attention, the third commit after HEAD is practically the fourth commit in git log, since the first is the HEAD itself. Instead of --interactive you can also type -i. This command will open your default editor and list your last three commits. The format is something like:
pick hash_id commit_message for each commit.
The very first thing you can do is to change the order of the commits in this file. It is pretty straight-forward, just change to order of the lines
Other than that you have the following options with each of the commits:
- Pick (p): You would like to keep that commit as it is, this is the default action.
- Reword (r): You would like to change the commit message of the commit.
- Edit (e): You would like to change the content of the commit
- Squash (s): It merges the commit with the previous one, keeping both the commit messages
- Fixup (f): Same as squash, but it keeps the commit message of the previous commit
- Exec (x): Executes a custom shell command
- Break (b): It stops the rebasing at that commit, you can continue later with git rebase --continue
- Drop (d): It drops that commit together with its content. That’s the best way of getting rid of a commit
- Label (l): It attaches a label to the given which is the actual HEAD: Pay attention! The parameter here is not a commit id.
- Reset (t): It resets the label of the current HEAD commit. It is also not expecting a commit id.
- Merge (m): It creates a merge commit.
You should just write the right keyword or its abbreviation (in brackets) before he commit id.
Here is a small collection of real life scenarios which can be resolved by interactive rebase.
As already mentioned, you can change the order of the commits. If you want to change the order of the last 10 commits:
- Type git rebase --interactive HEAD~10
- You see now the list of commits is your default editor. Change their order as you wish.
- Save this file, resolve all conflicts
There’s a huge chance that you have to resolve some rebase conflicts, in this case change them, add the changed file with calling git add and type git rebase --continue at the end. Since the rebasing is commit based it can be that you have to fix similar conflicts in the same file multiple times.
It can happen, that you would like to remove a commit with its content, do the following:
- Type git rebase --interactive HEAD~10 (to remove some commits from the last 10)
- Change the “pick” to “drop” in the lines of commits to be removed
- Save the file and resolve the conflicts
That’s it, you removed the unnecessary commits with all their content.
Sometimes you created too many simple commits and you would like to make your history more clean with less commits. You can simply merge the content of multiple commits into one.
- Type git rebase --interactive HEAD~10 (to merge some commits from the last 10)
- Change the “pick” to “fixup” in the lines of commits to be merged into the previous ones
- Save the file
What to do if you want to change the commit message of an older commit?
- Type git rebase --interactive HEAD~10 (to change the message of some commits from the last 10)
- Change the “pick” to “reword” in the lines of commits to be renamed
- Save the file
- The current commit message will appear in your editor, change it and save it
How to change the content of an older commit?
- Type git rebase --interactive HEAD~10 (to change some commits from the last 10)
- Change the “pick” to “edit” in the lines of commits to be edited
- Save the file
- Do the changes you want, add them by git add and commit them by git commit --amend
- Type git rebase --continue
- Resolve all the conflicts
How to split an already existing commit?
- Type git rebase --interactive HEAD~10 (to split a commit from the last 10)
- Change the “pick” to “edit” in the lines of the commit to be splitted
- Save the file
- Type git reset
- Add the changes for the first commit and commit it
- Add the changes for the second commit and commit it
- git rebase --continue
In my view interactive rebasing in git is a very powerful option which can make several scenarios easier and faster. But in order to use it in an efficient way you have to really know how it works.