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
main
branch 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
M5
on 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
feature
branch 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
F3
comes now first resulting inF3*
andF2
has been squased intoF1
resulting inF1-2*
This will now help in rebasing again onto
main
while 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
merge
is 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
rebase
takes 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.