Git Collaboration Strategies
Git Merge Versus Rebase
In this article we are looking on strategies on how to collaborate on a shared
git repository: we want to compare the two strategies: git merge and
git rebase. We make some simple assumptions on the branching strategy, but the
discussion can easily be adapted to other branching strategies.
Assumptions
In order to make the discussion concrete we assume the following situation:
- there is a shared remote code repository with a
mainbranch of production level code - each collaborator works on a feature branch with a custom name
- there are code reviews before feature branches are merged into
main
Situation
A developer has worked on feature branch feature, that was split off from
main at a commit M2, but now the main branch has expanded to commit M4. So
we want to compare strategies to integrate the work that has been done on the
feature branch into main.
Beginner's Strategy with Merge
During development of the feature many developers merge main into the feature
branch several times to keep up with the latest changes from other
collaborators.
Advantages
Merge Conflicts
With merge you only need to solve any merge conflict once at each Merge
commit.
Forward Thinking - "safe"
The strategy is "safe": merge is always forward thinking and cannot mess up your code base in any way as it never touches the history.
Good for Squash-Merging a Pull-Request
When you squash merge your pull-request into main you do not take the
history onto the main branch and the Sync* merge commits on branch feature
are not reachable from main, which make the history linear and clean.
Disadvantages
Bad for Normal-Merging a Pull-Request
When the feature branch is merged, the history is not linear anymore:
Remedy: Squash merge as shown here.
Advanced Strategy with Rebase
Rebase is an advanced strategy as it changes the history of what happened.
It has the potential to disrupt other people's work and should always be used
with care:
Rebase Explained
Let's consider the same situation: the feature branched off at M2 and both
feature and main have moved on since.
Now the developer wants to get ready for review or just integrate the latest changes from main.
With
git checkout feature
git rebase main
you can achieve the following:
This looks elegant and it is. It moves the feature branch forward on main as
if it has not been checked out at M2 but instead at the most recent commit
M5on main. Almost like wishful thinking, but it comes at a price. The
commits F1*, F2*, F3* are not the original ones F1, F2, F3. Those
original commits are now detached (git will garbage collect them in approx. 2
weeks).
You can always rebase your feature branches (like feature) since by
best-practice you should be the only person to work on it and rewriting
history only has impact for you.
git checkout feature
git rebase -i main
-
Never rebase any long-living branches, like
main(orrelease/*) since you rewrite history and thats forbidden on these branches.Don't do this:
git checkout main
git rebase -i <other-commit-ref> -
Be careful when rebasing a feature branch where you are not the only one working on since you rewrite history. If you still need to do a rebase on a shared feature branch branch with others, communicate the rebase before hand such that your collaborators know that a rebase happened.
Advantages
Good for Normal-Merging a Pull-Request
If you want your clean commits to be reachable (for the history) on the
main branch, then a rebase with normal merge to main will result in a clean
history.
Preserving feature history:
But you can also decide to just squash merge to main and
forget about the history..
Disadvantages
Collaboration
If a collaborater C uses your feature branch for syncing in your work on its
own branch:
After the rebase of the feature branch, a collaborator cannot update their
local feature branch anymore due to changed history. The commits are
rewritten and now have a different SHA1 hash.
The collaborator can however reset its local feature branch (if its needed,
you can always use origin/feature)
git checkout feature # Collaborate C should not work on that branch!
git reset --hard origin/feature
Merge Conflicts
With rebase merge conflicts may need to be resolved several times: a rebase
happens like this:
During a rebase commits are transferred one by one: starting at F1
- resolve merge conflicts for
F1, thengit rebase --continue
- resolve merge conflicts for
F2, thengit rebase --continue
- resolve merge conflicts for
F3, thengit rebase --continue
In case you did not plan for this, it can be a tedious process. Compare this with a merge where you resolve all conflicts only once.
Remedies:
-
First clean up all your commits on the
featurebranch by doing a rebase on its merge base. Starting fromwith
git rebase -i $(git merge-base origin/main HEAD)you can reorder/edit/squash commits (fromM2) which might result inwhere
F3comes now first resulting inF3*andF2has been squased intoF1resulting inF1-2*This will now help in rebasing again onto
mainwhile having less merge conflicts.
Conclusion
Leave the syncing strategy on the feature branch to the developer, to chose what they feel most comfortable with:
-
A sync with
mergeis easier and safe for beginners but you should- Only squash merge your changes to the base branch, e.g.
main. Do not do normal merges due to convoluted history.
- Only squash merge your changes to the base branch, e.g.
-
A sync with
rebasetakes a lot more experience and planning, but has certain advantages in regards to the history.- You can normal merge to the base branch, e.g.
main, if you want the history on your branch to be reachable (in that case you should have properly formatted commits). Squash merging is of course also possible.
- You can normal merge to the base branch, e.g.