Rebasing in Git is a very useful feature which helps to maintain a readable commit history. Interactive mode of the rebasing is one of the most powerful tools which Git provides. In this post I am going to present interactive rebase and show how you can leverage it in daily work with your repository.
Git rebase in a nutshell
Let’s start with a very quick overview of Git rebase in its most-common, non-interactive mode.
A typical scenario for rebasing is that we have a feature branch (call it feature1) and we need the most recent changes from master on it. It is illustrated in the following picture:
With rebase we can take all of our commits from feature1 and put them on top of commits from master. The command to do it is just git rebase master (assuming that feature1 is the current branch). This process consists of a few steps. They are presented in the following slides:
Why and when to use it
Of course, our goal could be accomplished with a merge too. Rebase is nice though, because it keeps our commit history clean — we do not have too many merge commits. In my opinion, a good practice is to:
- use merge when we want to move changes from a feature branch to the master branch
- use rebase when we want to move changes from the master branch to a feature branch
Of course, rebase rewrites the branch history, so we have to be careful if the branch is used by other team members. Such changes must be coordinated with everyone who works on the branch.
What is more, sometimes there is an additional big challenge related with Git rebase. It comes from the fact that merge conflicts may occur multiple times, as we move changes one by one. Sometimes it might lead to a serious effort. In such cases I would recommend to fallback to a merge.
Interactive rebase basics
Now, once we covered the regular rebase, comes the time for the advanced approach.
Rebasing over the same branch
What would happen if we selected an older commit on the same branch as our rebase point?
Actually, such operation would not do anything to our branch history. The changes would be put aside for a moment and then just reapplied. It is presented in the following slides:
Nothing interesting yet. If we add -i option for our rebase command, though, we can tell Git to do something more than just reapplying these commits. Let’s take a closer look at the available options.
What can we do while reapplying commits?
In the interactive mode Git gives us multiple options for each commit. They include but are not limited to:
- pick — just apply it on top of the current state of the branch (this is the default option which is done in the non-interactive mode)
- reword — apply it, but change the commit message
- edit — use commit, but amend it; in this case Git will put the changes in the working area and pause the rebase; you can make additional changes, stage them and then continue the rebase with git rebase –continue
- squash — use the changes but meld them into the previous commit
- drop — don’t use the commit at all
These options are very powerful but in most cases they change the history of the branch. This is not a big deal if the branch was not pushed to any remote repository yet. If it was, though, then we can push it only with –force flag, as it might affect the work of other team members who use the same branch. Be careful!
Let’s take a look at interactive rebase in action. The starting point is the following state of the repository:
What we want to achieve is:
- meld commits dd77d, ee35a and ff44c into a single one
- remove commit aa11d
We start with git rebase -i 44882 command. Git presents a text file with the rebase plan (and some comments reminding the available options, which I will skip for brevity). The actions are executed from top to bottom:
pick aa11d typo fix pick 55c24 changes in readme pick dd77d added logs in controller pick ee35a changed log level pick ff44c improved logs
We edit the file to achieve the goals described above:
pick 55c24 changes in readme pick dd77d added logs in controller squash ee25a changed log level squash ff44c improved logs
We save the file and git starts rebasing. The process is presented in the following slides:
As we can see, with interactive rebase it is possible to revert changes and clean up the commit history. In my opinion it is a good approach to make commits as often as possible while working on a feature or during a refactoring. It gives us multiple checkpoints in the history, which is useful when we are in the middle of big changes. Then, before pushing to a remote repository, we can use interactive rebase to review the commit history and improve it if needed. This might often include squashing multiple commits into a single one to make each commit more meaningful and complete.
In this article I presented Git rebase, including its interactive mode. Interactive rebase is one of the most powerful ways of managing your commit history. You can remove arbitrary commits from the branch history, amend them or meld multiple commits together. This allows you to keep your Git history clean and maintainable, which is very important for every project. I hope you found this post useful. Good luck with rebasing!
More articles about Git
If you liked this post, then you might also like my Undoing In Git article, which is a summary of different ways of reverting changes in Git. I highly recommend it!