The other day, I had an invaluable opportunity to learn about git rebase
. It took me some time to fully understand the command's behaviour and its benefits. This article is kind of my learning note, but I hope this will also help fellow developers out there deepen their knowledge! Also, if you find anything incorrect or have something to say, please feel free to leave comments or reach out to me on any platform!
What is Git Rebase?
Git rebase is a git command that lets you integrate a history in one branch to another, without creating additional merge commits. In other words, when using it, all the commit history appears on one branch linearly without additional merge commits, resulting in looking as if all the changes were made on the branch upfront and there was no other working branch in the first place.
Visualized Example
The initial state: A feature branch is created from the commit A. Then, three new commits are created on the main branch (B, C, D), and two commits are created on the feature branch (X, Y).
main: A -- B -- C -- D
feature: A -- X -- Y
Before merging the feature branch to the main branch, we have to reflect the new commits on the main branch (B, C, D) to the feature branch. This is the case where git rebase comes in handy. We can run git rebase on the feature branch. The histories will look like below:
main: A -- B -- C -- D
feature: A -- B -- C -- D -- X -- Y
As you can see, the three commits on the main branch (B, C, D) are inserted into the feature branch before the commits X and Y. (Potentially, we might see some conflicts here, in which case we should solve them manually.)
Now, we can peacefully merge the feature branch to the main. The histories after the merge will look like below:
main: A -- B -- C -- D -- X -- Y
feature: A -- B -- C -- D -- X -- Y
π‘ Technically, the commits X and Y after rebasing are not the same as the ones before rebasing. In this example case, the rebase command performed on the feature branch rewinds the commit history to the root commit (A), places the commits to be integrated (B, C, D), and re-creates the commits rewinded (X, Y) again (we can call them X' and Y'). So, the commit X' and Y' are different commits from X and Y that contain the same information as X and Y.
main: A -- B -- C -- D feature: A -- X -- Y β rebase main to feature main: A -- B -- C -- D feature: A -- B -- C -- D -- X' -- Y' β merge feature to main main: A -- B -- C -- D -- X' -- Y' feature: A -- B -- C -- D -- X' -- Y'
Why Git Rebase?
One thing to notice from the example above is there's no merge commit on either the main or feature branches. This is the main reason why we want to use this command.
To appreciate this more clearly, let me take a look at the case where we use git pull and merge on the example above.
main: A -- B -- C -- D
feature: A -- X -- Y
β pull and merge main to feature
main: A -- B -- C -- D
feature: A -- X -- Y -- M(B--C--D)
β merge feature to main
main: A -- B -- C -- D -- M(X--Y--M(B--C--D))
feature: A -- X -- Y -- M(B--C--D)
We can obviously see that the histories are getting complex because of the merge commits. Git rebase is a powerful way to avoid this.
π‘ Why do we want to keep the histories simple? There could be a lot of answers to this question, but the main idea is to make it easier to understand who did what and when. This would be beneficial for code reviewers, project managers, and newly joined team members who are trying to catch up with the team.
When to Git Rebase?
We should use git rebase when...
- we want to integrate some commits from one branch into another, keeping the histories simple.
- Basically, when we want to perform git pull + merge, rebase could be an alternative.
When NOT to?
We should not use git rebase when...
- the branch whose history we want to add changes to is shared with others.
- Since git rebase changes the commit history (as seen on the feature branch from the example case above,) if we perform git rebase on a shared branch, it will cause confusion and conflicts with the branch in the others' environment.
- the commits are already pushed to GitHub.
- This is actually prohibited. If you perform git rebase on such commits and try to push them to GitHub again, GitHub doesn't accept this push.
- we can expect a lot of conflicts.
- Because the command checks every commit to find whether there are conflicts and stops the process if found till the conflict is resolved, it would be troublesome to use this when too many conflicts are expected.
- we want to keep merge and branch integration histories.
- If we intentionally want to keep them, of course we should not use rebase. Git pull + merge would be the way.
How to Git Rebase?
Let me re-follow the example case with the actual commands we should run.
Initial state:
main: A -- B -- C -- D
feature: A -- X -- Y
1.Checkout to the feature branch (git checkout feature
)
2.Perform git rebase (git rebase main
)
Result of step #2:
main: A -- B -- C -- D
feature: A -- B -- C -- D -- X' -- Y'
3.Checkout to the main branch and merge feature to main (git checkout main
then git merge feature
)
Result of step #3:
main: A -- B -- C -- D -- X' -- Y'
feature: A -- B -- C -- D -- X' -- Y'
π‘ In the real-world scenario, step #3 is most likely to be replaced by pushing the feature branch to GitHub and creating a PR to merge it to the main.
Top comments (0)