TL;DR: after the squash merge, you should merge main into feature1, then feature1 into feature2, to establish the common ancestors to avoid conflicts.
These squash merge policies exist to make sure the commits of development branches are garbage collected and removed from the repo when the development is finished, if you do a merge commit to main, then the commits of the development branch is retained forever. Your local policy can determine if this is desired or not.
When you merge in Git, it looks at the two commits and goes back in the commit graph until it finds the nearest common ancestor and then diffs both branches against that common ancestor when doing a merge.
If you do a squash merge, the merge commit does not have the source branch as it's parent, so Git can't select the appropriate common ancestor.
Consider the following case, where you developed feature1, and you made a pull request, then you started working on feature2. In the meantime main got new commits and you also did some code-review changes from feature1. So the commit graph looks like this:
o--o--o feature2
/
o--B--o feature1
/
-o--o--A--o--M main
When you merge feature1 into main, the common ancestor will be commit A.
In the diff between A and main there are unrelated developments.
In the diff between A and feature1, there is the development of the feature1.
I you squash merge, you just create a new commit on main:
o--o--o feature2
/
o--B--o feature 1
/
-o--o--A--o--M--F1 main
So when you merge feature2 onto main, Git again finds only A as a common ancestor. Then
In the diff between A and main, you have the unrelated developments on main, plus the feature1's changes. In the diff between A and feature2, you have diff of feature1 until B, and the diff of feature2.
Since both diffs contain changes from feature1, you may see conflicts.
To avoid this you need to help Git to find the appropriate common ancestor.
In order to do that first merge main to feature1 before doing the squash merge, to create the merge commit C:
o--o--o feature2
/
B--o--o--C feature 1
/ /
-o--o--A--o--M--. main
This updates feature1 with changes from main.
It's generally a good practice to update your branch before merging and run your tests to see if you new code integrates with the existing codebase well.
Then do the squash merge of feature1 on main, to create the commit D:
o--o--o feature2
/
B--o--o--C feature 1
/ /
-o--o--A--o--M--.--D main
Then merge main to feature1 again. In this case the common ancestor of commits C and D is M, the diff between M and C and M and D is identical, so this should be an empty merge commit E:
o--o--o feature2
/
B--o--o--C--E feature 1
/ / /
-o--o--A--o--M--.--D main
And finally merge feature1 onto feature2. The common ancestor between E and feature2 is B. So the diff from B and E is the changes from main and the changes of feature1. The diff from B and feature2 is the changes that belong to feature2 so the commits are different so no conflict is expected. This creates commit F and now you can delete feature1:
o--o--o--F feature2
/ /
B--o--o--C--E
/ / /
-o--o--A--o--M--.--D main
And now feature2 is up-to-date with main. The common ancestor between D and feature2 is D, so the squash merge of feature2 should be straightforward.
Then you can delete feature2 branch too, and the garbage collection should clean up both branches completely then.
feature1
onto master, then want to mergefeature2
later. In that case, wouldn't the first approach result in conflicts as git tries to re-apply thefeature1
commits on top of the squashed commit, whereas the second would allow git to determine that those commits don't need to be merged? – Jez Feb 09 '18 at 17:11git clone
ing the repo and copying my changed files over!) because I branched from a branch and then the maintainer squashed the first branch. At my job they also do squash merges. This means I can't work on a featureb
that depends on featurea
until featurea
is merged. – Throw Away Account Feb 09 '18 at 18:35