Let’s say you’re working on a project with a main branch and a feature branch. While you are making changes to feature branch, the main branch has received some new commits from other developers.
You want to incorporate these new commits into your feature branch, but not sure whether you should do it by git rebase or git merge.
▶ Why not just ignore changes in the main branch for now?
Ignoring updates on the main branch for now is sometimes feasible, but there are a few important disadvantages to consider.
Delaying sync increases potential conflicts
The longer your feature branch diverges from main, the more changes accumulate. When you finally integrate with main, you’re more likely to encounter a large, complex set of merge conflicts. Resolving conflicts with many changes can be time-consuming and prone to errors.
Working in an outdated context
If main includes changes that affect the overall project (e.g., updates to libraries, modifications to shared components, or security fixes), ignoring them means you’re working in an increasingly outdated context. Your feature might develop incompatibilities that aren’t apparent until the final merge.
Plus, Continuous Integration testing or Continuous Deployment workflows typically run against the latest main code. By not keeping up with main, you risk your branch passing tests locally but failing in CI/CD because it lacks compatibility with newer changes.
🍵 Might Be Okay to Ignore main Changes
There are situations where you can safely ignore main temporarily:
Isolated feature
Short-lived feature branch
Experimental branches
Solution
There is no one-size-fits-all approach and it all comes down to what you value most.
▶ Pros and Cons
Command
Pros
Cons
rebase
Clean, linear history; ideal for local branches
It’s possible that a “commit that was working fine” could turn into a “commit that doesn’t work.”
merge
Maintains full history; safe for shared branches
Creates new merge commits, making history less linear
Generally speaking,
git merge pull the latest changes from main into the feature branch, creating a new merge commit
git rebase changes the base of the feature branch to the latest commit and then replays the changes in the feature branch from there
Incorporate the changes by git rebase
At the branch development of Figure 1, you can rebase the feature branch with the following steps:
# Step 1: Checkout the feature branchgit switch feature# Step 2: rebase onto main:git rebase main
These commands tell Git to
Temporarily remove B1, B2
Fast-forward the branch to main’s latest commit (A3, A4)
If there are changes in main that modify the same parts of code as your commits, Git won’t know how to reconcile those differences automatically. These overlapping changes are what cause conflicts.
For example:
Let’s say you edited file_A.py in your feature branch to add a new function.
Meanwhile, another developer made a conflicting change to the same section of file_A.py in main.
When rebase tries to apply your changes on top of main, Git encounters a conflict because it doesn’t know which version to keep. Instead, Git will list files with conflicts. You’ll see a message like
CONFLICT(content): Merge conflict in file_A.py
Open each conflicted file. Git will add conflict markers to show where the differences are:
<<<<<<< HEAD// Code from main branch=======// Code from your feature branch>>>>>>> your-commit-hash
You are expected to decide which parts of the code to keep and remove the conflict markers (<<<<<<<, =======, >>>>>>>) after resolving. Then,
# git add the modified filesgit add file_A.py# Continue the rebasegit rebase --continue
If you want to start over or quit rebasing, you can abort the rebase with
git rebase --abort
▶ “commit that was working fine” could turn into a “not working”
When you rebase a branch, you’re reapplying commits onto a new base, which can potentially break previously functional code. So to minimize this risk, better to do the followings:
Test after rebasing: After a rebase, test your feature branch to ensure that everything still works as expected.
Check each commit after conflicts: If you resolved conflicts during the rebase, double-check those areas to ensure the changes align with your intended functionality.
In summary, rebasing changes the context in which your commits operate, so it’s important to verify that they still work as intended in the new context.
Undo git rebase
Let’s say you’re working on a feature branch. You rebased it onto the main branch to incorporate recent changes, but after the rebase, you realize that:
You made a mistake resolving a conflict. or
Some tests are failing because of unexpected interactions with the latest changes from main.
In this case, You wants to undo the rebase and return the branch to its original state.
Sadly, you have successfully rebased the feature branch onto commit-id D of the main. But no worried, run the git reflog command to see recent actions on your branch:
% git reflog14b3c5d(HEAD -> feature) HEAD@{0}: rebase (finish): returning to refs/heads/feature14b3c5d(HEAD -> feature) HEAD@{1}: rebase (pick): Y110878e HEAD@{2}: rebase (pick): X1625fb5(main) HEAD@{3}: rebase (start): checkout maind4ac550 HEAD@{4}: checkout: moving from feature to featured4ac550 HEAD@{5}: checkout: moving from main to feature1625fb5(main) HEAD@{6}: commit: Dcd439d1 HEAD@{7}: commit: C963f1a1 HEAD@{8}: checkout: moving from feature to maind4ac550 HEAD@{9}: commit: Y4fbd292 HEAD@{10}: commit: X963f1a1 HEAD@{11}: checkout: moving from main to feature963f1a1 HEAD@{12}: commit: Bfeadb03 HEAD@{13}: commit (initial): A
The line 1625fb5 (main) HEAD@{3}: rebase (start): checkout main indicates when Git started the rebase process. So, if you want to undo the rebase, just reset to the entry d4ac550 HEAD@{4}: to go back to your previous state before the rebase.
Use git reset to move your branch pointer back to the commit just before the rebase:
As explined above, if your goal is to maintain a clean and linear commit history and you’re working primarily with your own branches, git rebase is often the best choice. On the other hand, if you’re collaborating closely with others and want to ensure that history is preserved, or if you want to avoid rewriting shared commits, git merge is likely the better option.
▶ General Recommendation
If your feature branch is not shared yet, go with git rebase for a cleaner, linear history.
If your feature branch is already shared or part of a collaborative workflow, stick with git merge to avoid potential conflicts for collaborators.
Versioning and git rebase strategy
Let’s say you are working on a repository with the following versioning strategy:
Version class
explained
Major Version (x)
Changes in the major version indicate breaking changes or significant new features.
Minor Version (y)
Changes in the minor version often introduce new features that are backward-compatible.
Patch Version (z)
Changes in the patch version generally include bug fixes and minor improvements.
Then, better to adopt the following git rebase strategy:
▶ Changes in x (Major Version):
Recommendation: Always rebase.
Reason: Major changes may have significant impacts and require integration with the latest code. Rebasing helps ensure that the new major features align correctly with the current state of the codebase, avoiding integration issues.
▶ Changes in y (Minor Version):
Recommendation: Rebase as a precaution.
Reason: While minor changes are generally backward-compatible, they can still introduce complexities. Rebasing hels ensuring the new minor features do not conflict with other updates.
▶ Changes in z (Patch Version):
Recommendation: Rebase not required.
Reason: Patch changes are typically small fixes. If the changes in the main branch are minimal, there may be no need to rebase.