Understanding Git Rebase Basics
Git rebasing is the process of rewriting your git history by changing the point of origin of the branch you’re working on. It’s helps you avoid complicated merge conflicts which can wreak havoc on your git log history.
Check out the official git docs for a deeper technical insight into how it works: https://git-scm.com/docs/git-rebase
Take this scenario for example: You’re developing a feature branch, and you’re missing some essential updates that have been recently added to the master branch. Your feature works, but you’re missing some commits from master that you want to test your feature on top of…
Git rebase can help you! While not screwing up your git history!!
What about git merge
?
Git merge is arguably the easiest way to get the latest changes from your master branch into your feature branch, and is a valid way of getting the updates you need.
You’re checked out to your feature branch, and run git merge master
— and Voilà! Your branch now has the latest updates from master.
But depending on how often you do this, your git history could be plagued with merge pull requests—rendering your git history unreliable and a chore to read…
- Merge pull request #123 from project/master
- Merge pull request #124 from project/master
- feat(Component): create new component
- Merge pull request #125 from project/master
While running a git merge is easier, it will be at the expense of the quality of your git history.
Rebase can be more complicated in some ways, but when done correctly, it keeps your logs clean and organised.
Some Examples
Before we start…
With my feature development process, I try to focus on continuous integration. I always start a new feature branch forking from master, and merge it right back in when I’m done.
If you prefer to use a different approach to organising your branches. For example, git-flow where you are using a develop branch for your features instead of master, these steps will still help you with what you need. You’ll just need to swap master with develop (or whatever branch you want to use).
I’m also using npm
commands in these examples for test scripts. Maybe you are using a different package manager? Maybe you don’t have a test suite? In which case, you can leave these steps out.
Assuming we have a master
branch, a feature
branch, and maybe an other-feature
branch:
SCENARIO 1 :: My feature is several commits behind master
. I want to update my feature branch with the latest changes on master.
feature
incorporates both A and B commits, but not C and D. Commits C and D were created at some point after feature
branch was created.
I want feature
branch to incorporate commits C and D before I merge my branch into master, so I can ensure the feature works with the current state of master
:
SCENARIO 2 :: My feature was branched from another feature branch. I want to change this so my feature starts from master branch instead.
feature
incorporates both A, B, E, and F commits, but not C and D. Commits C and D were created at some point after other-feature
branch was created, and probably long before feature
was created.
I want feature
to be based directly on master
, not other-feature
. I don’t want commits E and F on my branch, and instead want to base feature
on master
, and encorporate commits C and D:
The goal of both scenarios are the same—We want feature to have all of the latest updates from master
, and master
alone, with a clean feature
git history.
Rebase Preparation (master
)
- run
git checkout master
- run
git pull
to get the latest changes from remote - run
npm test
to ensure tests are passing. - If yours tests are failing here on
master
, it’s advised to resolve these issues before you rebasefeature
ontomaster
.
Rebase Preparation (feature
)
- run
git checkout feature
- run
git pull
to get the latest changes from remote
Find the common ancestor with master
using git-merge-base
- run
git checkout feature
- run
git merge-base master feature
to find the most recent commit both branches have in common. - This will generate a long commit ID string looking something like this:
abcdef1234567890abcdef
.
Run git-rebase
- NOTE: If you run into any issues, you can run
git rebase --abort
to cancel the entire process. Rebasing can be instantaneous when no conflicts are found, but if you’re rebasing a feature branch that is missing tens/hundreds or commits from master, expect a long process of fixing conflicts. - run
git rebase --onto master abcdef1234567890abcdef
. Replace the long ID string with the ID string generated on your end. - If conflicts are found, resolve any files and run
git add .
when done to stage the changes. Then rungit rebase --continue
to continue the process. - Eventually the process will complete, and your
feature
branch will now contain a clean record with all of the latest frommaster
. - If your pre-rebased branch was pushed to upstream, you’ll need to run
git push --force
to overwrite the pre-rebased branch with your new clean rebased branch.
Conclusion
I’ve worked on a lot of projects in the past were git usage was used for little more than stage->commit->push
and merge
, with no regard for the quality of the project’s git history. Git becomes a useless tool for debugging, it’s tells you nothing meaningful about the project’s history, and results in gradual development slowdown over time…
You should ask yourself how important your git history is to your project. Having the ability to go back through a clean history where every commit is readable and means something is something that is easy to take for granted.
References
Git rebase: https://git-scm.com/docs/git-rebase
Git merge-base: https://git-scm.com/docs/git-merge-base
Attlasian: https://www.atlassian.com/git/tutorials/merging-vs-rebasing