Re: confused about a conflict during octopus merging

Previous thread: [PATCH] mergetool: add support for ECMerge by Steffen Prohaska on Monday, October 8, 2007 - 2:22 pm. (15 messages)

Next thread: bug? - git-checkout treeish paths ignores deleted files by Mark Levedahl on Monday, October 8, 2007 - 5:58 pm. (4 messages)
From: martin f krafft
Date: Monday, October 8, 2007 - 5:31 pm

Dear list,

I am a little confused why the following yields a conflict:

$ git init
$ touch a; git add a; git commit -ma
Created initial commit 0ba90cc: a
 0 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 a

  # create merger branch for later
$ git branch merger

  # create a file
$ date > d; git add d; git commit -md1
Created commit 89d22eb: d
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 d

  # branch off a second branch and populate it
$ git checkout -b second
Switched to a new branch "second"
$ touch b; git add b; git commit -mb
Created commit 7370737: b
 0 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 b

  # modify file changed in previous commit
$ git checkout master
Switched to branch "master"
$ date >> d; git add d; git commit -md2
Created commit bb5f1b4: d2
 1 files changed, 1 insertions(+), 1 deletions(-)



Now the tree structure is

  x -- master: d2
  | x -- second: b
  |/
  x d1
  |
  x -- merger: a

  # now octopus-merge master+second into merger
$ git checkout merger
Switched to branch "merger"

$ git merge master second
Trying simple merge with bb5f1b4f3a8cbfcbc9dbc97cc823f3ca05db2db1
Trying simple merge with 7370737ff3aa6540a425db015e194766d2cbda2e
Simple merge did not work, trying automatic merge.
Added d in both, but differently.
error: Could not read .merge_file_mCwa4W
ERROR: Merge conflict in d
fatal: merge program failed
Automatic merge failed; fix conflicts and then commit the result.


I don't understand why these conflict. I mean, sure, it makes sense:
after merging master, file d is at d2. Now when merging second, it
tries to replace d by its ancestor, d1.

What I don't understand is why Git does not understand that commit
d is part of the path of arriving at d2, so when 'second' is merged,
Git *should* really see that the change to file d in 'second' (d1)
is an ancestor of the already present d2 commit, and *not* conflict.

What am I misunderstanding ...
From: Linus Torvalds
Date: Monday, October 8, 2007 - 6:21 pm

Octopus merges are special. Don't use them unless you really know what 

Right. It makes total sense, because an octopus merge is simply always 
going to create a whole new merge commit that goes through the sources one 
by one, and merges them in - and if you do that, then you get a conflict 

Octopus merges do *not* fast-forward. Maybe they should. But they don't.

So when the octopus merge starts merging in "master" into the "merger" 
branch, it will *not* just fast-forward the branch to "master". Instead, 
if will generate a new tree that is basically the merged state: but that 
new tree is *not* the same as the master commit, it's literally a merge of 
the two branches - which in practice means that it has the same *content* 
as master, but it's not at that commit.

So that first stage of the octopus merge has gone fine, but it didn't 
really do what you expected.

And so now we have a half-way done octopus merge, with the first branch 
added. Now it merges in the second branch ("second"), and it *still* has 
the merge base being the original merge base, namely "merger".

And from that standpoint, it really *is* a conflict. 

I do think that 
 - octopus merges should probably refuse to happen when a branch isn't a 
   "merge" at all, but a fast-forward.
*or*
 - we should make octopus-merges smarter, and for each branch that is a 
   fast-forward, it really just fast-forwards.

but I don't think the current behaviour is "wrong", it's just not what you 
expected.

If you think of octopus merges as a really stupid thing where git will 
mindlessly do a three-way merge based on the *current* state with all the 
branches you name, then you get the current octopus merge. You just 
expected it to be smarter than it is, probably because you compare it to 
the *real* merge.

		Linus
-

From: martin f krafft
Date: Tuesday, October 9, 2007 - 2:47 am

Thanks, Linus, for taking your time to answer me. I truly appreciate
it.

also sprach Linus Torvalds <torvalds@linux-foundation.org> [2007.10.09.0221=


I would have to agree with you: this is pretty exactly what's going
on.

Now, I think Git could do better though. Fast-forwarding
octopus merges, as you suggest, is one possible enhancement, but is
that the solution to my problem? Yes, but could we do better?

Couldn't Git just ignore any commit it has already seen in this
octopus merge? I think this is perfectly okay in terms of the
resulting ancestry, it's really all about applying a commit to the
worktree or not.

Recall the original tree:

  x -- master: d2
  | x -- second: b
  |/
  x  d1
  |
  x -- merger: a

Now after merging master, the tree is at the same state as it is at
the tip of master. The asterisk denotes that the commit is half-way
done:

  x  c* (a+d1+d2)
  |\
  | x -- master: d2
  | | x -- second: b
  | |/
  | x  d1
  |/
  x  a

Next, we merge second to create c2

  x_ c2*  ((a+d1+d2)+(d1+b))
  |\ \
  | | |
  | x-|- master: d2
  | | |
  | | x -- second: b
  | |/
  | x  d1
  |/
  x  a

(yay ASCII art!)

At this point, the conflict happens, when Git tries to re-apply d1
to the work tree. But since d1 is already in the ancestry of the
node into which we are merging, couldn't it just skip applying the
commit to the worktree?

  x_ c  (a+d1+d2+b)
  |\ \
  | | |
  | x-|- master: d2
  | | |
  | | x -- second: b
  | |/
  | x  d1
  |/
  x  a

If it does, then I think ordering of merges for an octopus becomes
relevant, but I'd say that's already the case.

And I guess this is identical to fast-forwarding the branches...

No, Git just raised the bar for expectations half-way up to the
moon In other words: you spoiled me so far; now I won't settle for
less than perfection. :)

--=20
martin;              (greetings from the heart of the sun.)
  \____ echo mailto: !#^."<*>"|tr "<*> mailto:" net@madduck
=20
"however ...
Previous thread: [PATCH] mergetool: add support for ECMerge by Steffen Prohaska on Monday, October 8, 2007 - 2:22 pm. (15 messages)

Next thread: bug? - git-checkout treeish paths ignores deleted files by Mark Levedahl on Monday, October 8, 2007 - 5:58 pm. (4 messages)