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. By the end of this read 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 onlyedit
: Alter the commitdrop
: 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
andupdate-ref
, which can be used to control various aspects during the rebasing. For the scope of this article, 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.
The rebase
Now the terminal will take you through each of the commits selected for rebasing, starting from the base commit. In this section, we will go through an example for each of the commands specified before the rebase.
The pick
command does not do anything and just includes the commit in the final history and it will be done automatically, and the next non-pick
commit will be shown. So let's move on to the rewording of Commit 0.
Rewording a commit
Once the rebase starts, the editor would display the current commit message for Commit 0, you can edit it to alter the commit message or leave it as it is.
Commit Message 0
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
Hit I
to enter the INSERT
mode. Modify the commit message to say "Altered Commit Message 0".
Altered Commit Message 0
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
--INSERT--
Hit Esc
and type :wq
to save the changes. The commit is saved successfully and the updated commit message is shown, note that the commit date would be updated to the current date-time. (It would obviously!)
$ [detached HEAD edbs23s] Altered Commit Message 0
Author: <Author>
Date: <Date>
You can amend the commit now, with
git commit --amend '-S'
Once you are satisfied with your changes, run
git rebase --continue
Enter the git rebase
command with the --continue
flag to go to the next commit and continue with the rebase.
$ git rebase --continue
Editing a commit
Next comes Commit 1, for which we have passed the edit
command. This allows us to modify all aspects of the commit i.e. change the files, dates, authors and even the commit message. It is as good as making the same commit once again but with its properties modified.
Here is an entire git commit
command that modifies every aspect of the commit:
$ GIT_COMMITTER_DATE="yyyy-mm-ddThh:mm:ss" git commit --amend --date="yyyy-mm-ddThh:mm:ss" --reset-author -m "Altered Commit Message 1"
The breakdown of the above command is as follows:
--date
: Modify the date when the commit has been authored.--reset-author
: Reset the author of the commit to the current author (as set in thegit config
of the system)-m
or--message
: Modify the commit messageGIT_COMMITTER_DATE
: Modify the date when the commit has been committed. (Refer to the box above on the difference between the commit author and committer)
One can also pass the --no-edit
flag instead of the commit message to keep it unaltered.
$ GIT_COMMITTER_DATE="yyyy-mm-ddThh:mm:ss" git commit --amend --date="yyyy-mm-ddThh:mm:ss" --reset-author --no-edit
If you wish to modify the files in a commit, just normally edit the files before executing the above commands (this action may cause unwanted effects in the showing of the diffs between the commits prior to and next to the current commit in the history).
Note that, all the above flags are optional and alter a particular property of the commit. You may use only the ones you require depending on the property /properties you wish to modify.
Continue to the next commit using the --continue
flag.
Deleting a commit
The final commit in this rebase is Commit 2, which has to be deleted as we have passed the drop
command. No action has to be taken at this step, the commit will be automatically removed from the commit history. Use the --continue
flag one last time to complete rebasing.
Tip: You can abort the rebasing and discard all changes anytime during the rebase by using the
--abort
flag.$ git rebase --abort
Finalizing the changes
While this is not recommended on public repositories, sometimes rebasing is useful to make some critical modifications to the commit history. However, extreme care must be taken while pushing the rebased branch onto the base branch, as this process overwrites any of the old commits which are not a part of the rebased branch.
To integrate the changes, we would have to do a force push onto the base branch:
$ git push -f <remote> <branch>
Conclusion
In this read, we learnt about rebasing in a brief and how we can actually use rebasing to alter previous commits and modify the entire commit history of a project. Rebasing is very useful in getting a linear commit history, and sometimes we can even use it for our own gains (as we did in this article :)), but one should keep extreme caution during rebasing else might end up messing up the commit history.
Rebasing just doesn't end here and there's a lot more to explore. This is just the start and I hope that I was able to answer the questions that were put forth at the beginning of this article!
Top comments (0)