git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
From: Edward Anderson <nilbus@nilbus.com>
To: git@vger.kernel.org
Subject: No one understands diff3 "Temporary merge branch" conflict markers
Date: Tue, 7 Jul 2015 10:55:05 -0400	[thread overview]
Message-ID: <CAOMsSXQVJsd0h1fnNMEJ5+cKpxbeF9mHraXva-wr6Y2zBCADbg@mail.gmail.com> (raw)

I have the diff3 conflictstyle enabled and want to be able to
understand how to understand its output when there are criss-cross
merges requiring temporary merge branches.  Eg:

    <<<<<<< HEAD
      print(A);
    ||||||| merged common ancestors
    <<<<<<< Temporary merge branch 1
      print(B);
    =======
      print(C);
    >>>>>>> feature

I have combed Google but have been able to find no good explanation.
People are hitting these and asking for help, but the suggestions
are weak and fail to explain anything.


Background
----------

Our workflow often causes criss-cross merges. In this more extreme
(but real) case, I have 16 ambiguously "best" merge bases:

    $ git merge-base --all HEAD feature | wc -l
    16

The 'resolve' strategy isn't an option for this many merge-bases:

    $ git merge -s resolve feature
    fatal: I cannot read more than 8 trees
    Merge with strategy resolve failed.

That brings me back to the recursive strategy.

https://lwn.net/Articles/210045/ "Branching and merging with git"
describes what a criss-cross merge is and how the recursive strategy
deals with them by merging the multiple merge-bases to form a single
temporary merge base.

> The classic confusing case is called a "criss-cross merge", and
> looks like this:
>
>          o--b-o-o--B
>         /    \ /
>  o--o--o      X
>         \    / \
>          o--a-o-o--A
>
> There are two common ancestors of A and B, marked a and b in the
> graph above.  And they're not the same.  You could use either one
> and get reasonable results, but how to choose?
>
> The details are too advanced for this discussion, but the default
> "recursive" merge strategy that git uses solves the answer by
> merging a and b into a temporary commit and using *that* as the
> merge base.
>
> Of course, a and b could have the same problem, so merging them
> could require another merge of still-older commits.  This is why the
> algorithm is called "recursive."

With the diff3 conflictstyle showing the merged common ancestors and
the recursive merge strategy, the merged common ancestors may have a
conflict

Conflict #1
-----------

     1 <<<<<<< HEAD
     2     unless admin
     3       fail Unauthorized.new("Admin only")
     4     end
     5 ||||||| merged common ancestors
     6 <<<<<<< Temporary merge branch 1
     7     unless admin
     8       fail Unauthorized.new("Admin only")
     9     end
    10 ||||||| merged common ancestors
    11     unless admin
    12       fail Unauthorized.new
    13     end
    14 =======
    15     fail Unauthorized.new unless admin
    16 >>>>>>> Temporary merge branch 2
    17 =======
    18     unless admin
    19         fail Unauthorized.new("Admin only")
    20       fail Unauthorized.new
    21     end
    22 >>>>>>> feature

It seems lines 6-16 are a conflict that occurred when merging the
merge-bases.  That conflict could be resolved by merging the change in
Temporary merge branch 1 (add "Admin only") with Temporary merge
branch 2 (convert multi-line unless to single-line) as this:

           fail Unauthorized.new("Admin only") unless admin

Thus, you might thing that you could refactor the above conflict to be
this:

     1 <<<<<<< HEAD
     2     unless admin
     3       fail Unauthorized.new("Admin only")
     4     end
     5 ||||||| merged common ancestors
     6     fail Unauthorized.new("Admin only") unless admin
     7 =======
     8     unless admin
     9         fail Unauthorized.new("Admin only")
    10       fail Unauthorized.new
    11     end
    12 >>>>>>> feature

However this would be resolved by merging HEAD's apparent change
(break single-line unless onto multiple lines) and feature's change,
which appears to be a botched up conflict resolution.

Obviously I'm reading this wrong. What is the correct way?

Conflict #2
------------

     1 <<<<<<< HEAD
     2     unless feature.enabled_for_user?(UserIdLookup.new(params).user_id)
     3       fail Unauthorized.new("Requires setting #{label}.")
     4 ||||||| merged common ancestors
     5 <<<<<<< Temporary merge branch 1
     6     unless feature.enabled_for_user?(params[:user_id])
     7       fail Unauthorized.new("Requires setting #{label}.")
     8 =======
     9     unless feature.enabled_for_user?(params[:user_id])
    10       fail Unauthorized.new("Requires setting #{label}.")
    11 >>>>>>> feature

This is the full conflict, and it doesn't seem to balance. In this
case, there's only one Temporary merge branch. Can the line marker on
line 5 just be ignored?  If so, then this is easy to resolve, but I am
left puzzled as to why the sections in lines 6-7 and 9-10 are
identical.  Is the Temporary merge branch 1 marker here simply because
other conflicts had multiple Temporary merge branches that had to be
labeled?  Or is the 0 lines between lines 4 and 5 a significant
deletion?

---

The git community needs some light and understanding shed on this
topic, on how these conflicts are to be read and understood. I'm
looking for something that can be applied methodically. If better
understanding the recursive merge strategy is necessary, that is fine.
However suggestions like "just resolve it to how you think it should
be" or "talk to the authors" will not be appreciated, as they do not
lend understanding regarding these conflict markers nor where the code
they delineate comes from.

Does anyone out there understand this stuff?

Thanks,
Edward

             reply	other threads:[~2015-07-07 14:57 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-07-07 14:55 Edward Anderson [this message]
2015-07-07 16:16 ` No one understands diff3 "Temporary merge branch" conflict markers Matthieu Moy
2015-07-07 17:44   ` Junio C Hamano
2015-07-07 21:44     ` Matthieu Moy

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: http://vger.kernel.org/majordomo-info.html

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CAOMsSXQVJsd0h1fnNMEJ5+cKpxbeF9mHraXva-wr6Y2zBCADbg@mail.gmail.com \
    --to=nilbus@nilbus.com \
    --cc=git@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).