When you work on a codebase with other people, you need to manage your local branches: you need to ensure that when you push some code and create a merge/pull request on the remote branch, your changes will be easily integrated with the main codebase. And by “easily” I mean preferably without merge conflicts or the dreaded message “your branch is xxx commits behind the target branch”. ?
There are many ways to accomplish this but I personally like to use git rebase.
what is git rebase
Git rebase is the tool used to combine changes from one branch into another branch.
when to use git rebase
You want to use git rebase when have a local branch with changes (for a bug fix or new feature, for example) and the main branch (the one you created your branch from) received updates and you want those updates in your local branch too, along with your changes.
In other words, you want to use git rebase to make sure your work is up to date with all the changes from the main branch and ready to be reviewed and potentially merged.
when NOT to use git rebase
You should not use git rebase on a shared branch (say a main or dev branch). Only rebase your local, non-shared branches. Treat rebase as a clean up tool for when you are getting ready to open a merge/pull request.
The reason for this recommendation is because of how git rebase works – which I will not get into here but you can watch this short video.
how to use git rebase:
I often run into 2 different cases that will determine how I use git rebase:
1- my local branch was created from the main branch (a “child” branch)
2- my local branch was created a from a branch that was branched from main (a “grandchild” branch)
In both cases the “main” branch received updates and I need those updates in my local branch.
This is how it works:
git rebase – branch out of main
I created a local branch directly out of the main branch so I can add my super-cool feature. While I’m still working on it OR if I’m ready to send it to code review, I find out that there’s one important bug fix in the main branch and I need it on my local branch too.
This is the most simple case and all I need to do is to 1. get those latest changes from main and 2. add them to my branch:
git fetch && git rebase origin/main
That’s it. You can continue working on your super-cool feature or just push your local to the remote (for code review, I hope! ?)
git rebase – branch of branch of main
This is another case I run into often:
I create a local branch “super-cool” from the main branch and add my changes to it. I send this change for code review and I want to start working right-away on adding another feature that depends on the super-cool feature that have not been merged to the main branch yet.
If I create a branch from main, the required code won’t be there. There are other ways to solve this (like cherry-picking, for example) but what I usually do is to create a branch from the “super-cool” branch:
git checkout -b "super-cool-premium"
Now I have all the super-cool changes and can add the premium changes I want.
Meanwhile my colleagues approved my pull/merge request from “super-cool” and those changes are now merged to the main branch.
Now my local “super-cool-premium” branch needs to reflect that change also: I want to make it look like as if I had branched directly from latest main (without getting into much detail here, this is good because it makes the history look cleaner).
In this case I use the –onto variant of the rebase command in my “super-cool-premium” branch:
git rebase --onto origin/main @~1
This is telling git to update my local “super-cool-premium” branch by bringing the latest changes from main and applying my changes on top of it. When my super-cool-premium gets merged to main later on, the main branch history will look like this:
Merge branch 'super-cool-premium' into 'main'
"add super-cool-premium feature"
Merge branch 'super-cool' into 'main'
"add super-cool feature"
Merge branch 'bug-fix-34' into 'main'
"main bug fix #34"
Merge branch 'bug-fix-34' into 'main'
"main bug fix #33"
when git rebase has conflicts
There are times when you try to rebase and you will get a warning that there are files with conflicts.
At this point you will notice that your branch is in “detached” mode: it’s waiting for instructions on what to do next and you must pick one of the two options: resolve the conflicts and continue the rebase with –continue or abort the operation with the –abort flag.
If you decide to keep going, it’s a matter of resolving the conflicts in your code editor, adding all changes (with git add; no need to create a new commit!) and telling git to continue with the rebase process:
git rebase --continue
managing your commits – the case for single commits
??? strong personal opinion disclaimer ???
One habit that I have that I find that helps a lot with minimizing confusion and conflicts when dealing with remote public branches is that I always only submit one single commit with my pull/merge request. Even if I need to address feedback from code review and update my code, I will still keep one single commit (I usually amend the commit, which is a *safe* thing to do only before the branch has been merged!!)
I can have multiple commits while I work on my local branch but before I push it to remote I will always squash the commits. No need to add “fix bug“, “fix bug again“, “fix for reals“, “are you kidding me” and so on the the main history, right? ? I will squash them all and give my commit a clear message like: “add super cool feature”.
That way, in the main branch history you will see only one commit per feature or bug fix added (plus the actual merge commit). ?
This helps a lot when merging multiple branches, doing rebases, managing conflicts, reverting changes, etc. Plus the history will look soooooo tidy. I treat each bug fix/new feature as a one distinct package to be added to the codebase.
What’s wrong with using git merge?
Nothing! Git merge adds a merge commit so if you try to keep your local branch up to date with main by merging main into your branch, be aware that you will have those extra commits. And there is nothing wrong with that. Personally I don’t like it and prefer one single commit per feature or bug fix I submit and that’s why I use rebase a lot. And squash my commits (which I will write about some other time but – spoiler alert – it’s just git rebase in interactive mode ?)
there’s more to git rebase
These are two examples of git rebase that I use on a regular basis but there’s a lot more to it. This tutorial goes into more depth on how git rebase works and brings up some considerations and warnings regarding this helpful command. One warning worth mentioning again is that you should never rebase a remote common branch (in our example, the main branch). Rebase should always be done in a local branch before it gets merged to the remote.
If you found this helpful, please share this article!