git merge squash and recurring conflicts

19,340

Solution 1

By squashing the merge, you've created a commit which has the effect of, but is not really, a merge.

That is, the working tree has the modifications you'd expect, but the metadata doesn't: crucially, the commit doesn't have two parents (one on master and one on alt) and therefore subsequent merges can't figure out the last common ancestor.


Useful uses of squash

  1. merging a completely finished feature branch onto master. I'll accumulate any useful information into the squashed commit, but specifically don't want this feature's incremental development history polluting the master commit timeline.
  2. merging several independent features (or contributions from different developers) onto the same integration branch, again without preserving their incremental histories. I could rebase them all together, and then use rebase -i to squash their commits, but this is easier

Useless uses of squash

Any merge where you want to keep the history and ancestry metadata intact, such as any time you want repeated recursive merges to work correctly, specifically what the OP is trying to do.

squashjust isn't really a good default, which is why it isn't the default.

Solution 2

To add to the "Useless uses of squash merge": Git 2.29 (Q4 2020) illustrates the traps with merge --squash:

See commit 087c616, commit 409f066, commit 5065ce4 (20 Sep 2020) by brian m. carlson (bk2204).
(Merged by Junio C Hamano -- gitster -- in commit c5a8f1e, 29 Sep 2020)

docs: explain why squash merges are broken with long-running branches

Signed-off-by: brian m. carlson

In many projects, squash merges are commonly used, primarily to keep a tidy history in the face of developers who do not use logically independent, bisectable commits.

As common as this is, this tends to cause significant problems when squash merges are used to merge long-running branches due to the lack of any new merge bases.

Even very experienced developers may make this mistake, so let's add a FAQ entry explaining why this is problematic and explaining that regular merge commits should be used to merge two long-running branches.

gitfaq now includes in its man page:

Merging and Rebasing


What kinds of problems can occur when merging long-lived branches with squash merges?

In general, there are a variety of problems that can occur when using squash merges to merge two branches multiple times.
These can include seeing extra commits in git log output, with a GUI, or when using the ... notation to express a range, as well as the possibility of needing to re-resolve conflicts again and again.

When Git does a normal merge between two branches, it considers exactly three points: the two branches and a third commit, called the merge base, which is usually the common ancestor of the commits.
The result of the merge is the sum of the changes between the merge base and each head.

When you merge two branches with a regular merge commit, this results in a new commit which will end up as a merge base when they're merged again, because there is now a new common ancestor.
Git doesn't have to consider changes that occurred before the merge base, so you don't have to re-resolve any conflicts you resolved before.

When you perform a squash merge, a merge commit isn't created; instead, the changes from one side are applied as a regular commit to the other side.
This means that the merge base for these branches won't have changed, and so when Git goes to perform its next merge, it considers all of the changes that it considered the last time plus the new changes.
That means any conflicts may need to be re-resolved.
Similarly, anything using the ... notation in git diff, git log, or a GUI will result in showing all of the changes since the original merge base.

As a consequence, if you want to merge two long-lived branches repeatedly, it's best to always use a regular merge commit.

Share:
19,340
draganHR
Author by

draganHR

Updated on June 26, 2022

Comments

  • draganHR
    draganHR about 2 years

    I have a git repository with master and alt branches. alt branch contains modified version of master code, and i am trying to merge changes from master to alt like this:

    git merge --squash master
    

    Merge results in conflict:

    Auto-merging myproject/foo/bar
    CONFLICT (content): Merge conflict in myproject/foo/bar
    Squash commit -- not updating HEAD
    Automatic merge failed; fix conflicts and then commit the result.
    

    After I resolve conflicts and commit changes everything seems fine, but when i run git merge --squash master again (without doing any changes on any branches) i will get same conflict error.

    Why is that? What did i miss?

  • Shahbaz
    Shahbaz almost 12 years
    the --squash option seems pretty useless then.
  • Shahbaz
    Shahbaz almost 12 years
    But seriously, what's the point of --squash? If you get the changes from master, but not knowing it, a later merge would get the same changes again, duplicating whatever was taken from master the first time merge was called, wouldn't it?
  • Useless
    Useless almost 12 years
    Just because squash doesn't do what the OP wants, doesn't mean it has no use. It's great if you're doing a one-off merge from a completed feature branch back to master and never want to use that feature branch again (and aren't interested in keeping the incremental development history of that feature)
  • Shahbaz
    Shahbaz almost 12 years
    I'm not so sure. You would think the feature branch is complete, but there's always a little bug somewhere or a small fix needed. What I don't understand is, why wouldn't you want the history? Is the merge node, carrying the history, hurting anyone in any way?
  • Useless
    Useless almost 12 years
    If you don't like it, don't use it. I've seen plenty of feature branches where I wish there was less noise obscuring the real changes; rebasing is one approach (to squash tens of tiny commits into one worth reading). Note that I don't care if someone else wants to keep their old feature branch for archaeological purposes, but I don't get any benefit from them sucking it into the shared master timeline.
  • Shahbaz
    Shahbaz almost 12 years
    What I'm trying to say is, it's best to keep the history of git the same as the history of your actions. If you merged, let git also know it, otherwise in the future either you or git would get confused. I also don't like git rebase for the same reason (rewriting history).
  • Shahbaz
    Shahbaz almost 12 years
    Yeah I don't think I would every use it. Anyway, your answer was interesting and I learned something :)
  • Felipe Pereira
    Felipe Pereira over 4 years
    once you got into this state (already squshed to master), what would be the correct approach for going back to normal?
  • Useless
    Useless over 4 years
    Ideally, stop working on the branch whose ancestry/merge metadata you destroyed, and work on a new branch based at (or after) the squashed commit.
  • Woozar
    Woozar about 4 years
    Whoever asks why one would not like to keep the full history of commits for every single feature branch, has probably never worked on a repo with 20+ active devs. If you try to find something in that commit history, it can take ages. If you have one commit for every feature with the ticket-number-prefix it is much easier to understand changes.
  • Useless
    Useless about 4 years
    Why on earth would any number of devs push every local change they make on every feature branch? You can squash each complete write/test/review/refactor complex into a single commit before pushing in the first place. They can just live locally (and in your review/CI/pull request systems) until they're ready to share.