From: Michael Spiegel <hidden> Date: 2017-01-27 17:28:07
Hi folks,
I'm trying to determine whether a merge required a conflict to resolve
after the merge has occurred. The git book has some advice
(https://git-scm.com/book/en/v2/Git-Tools-Advanced-Merging) to use
`git show` on the merge commit or use `git log --cc -p -1`. These
strategies work when the merge conflict was resolved with a change
that is different from either parent. When the conflict is resolved
with a change that is the same as one of the parents, then these
commands are indistinguishable from a merge that did not conflict. Is
it possible to distinguish between a conflict-free merge and a merge
conflict that is resolved by with the changes from one the parents?
Thanks,
--Michael
From: Jeff King <hidden> Date: 2017-01-27 17:54:26
On Fri, Jan 27, 2017 at 11:56:08AM -0500, Michael Spiegel wrote:
I'm trying to determine whether a merge required a conflict to resolve
after the merge has occurred. The git book has some advice
(https://git-scm.com/book/en/v2/Git-Tools-Advanced-Merging) to use
`git show` on the merge commit or use `git log --cc -p -1`. These
strategies work when the merge conflict was resolved with a change
that is different from either parent. When the conflict is resolved
with a change that is the same as one of the parents, then these
commands are indistinguishable from a merge that did not conflict. Is
it possible to distinguish between a conflict-free merge and a merge
conflict that is resolved by with the changes from one the parents?
No. You'd have to replay the merge to know if it would have had
conflicts.
There was a patch series a few years ago that added a new diff-mode to
do exactly that, and show the diff against what was resolved. It had a
few issues (I don't remember exactly what) and never got merged.
Certainly one complication is that you don't know exactly _how_ the
merge was done in the first place (e.g., which merge strategy, which
custom merge drivers were in effect, etc). But in general, replaying
with a standard merge-recursive would get you most of what you want to
know.
I've done this manually sometimes when digging into erroneous merges
(e.g., somebody accidentally runs "git reset -- <paths>" in the middle
of a merge and throws away some changes.
You should be able to do:
git checkout $merge^1
git merge $merge^2
git diff -R $merge
to see what the original resolution did.
-Peff
From: G. Sylvie Davies <hidden> Date: 2017-01-28 05:44:05
On Fri, Jan 27, 2017 at 9:51 AM, Jeff King [off-list ref] wrote:
On Fri, Jan 27, 2017 at 11:56:08AM -0500, Michael Spiegel wrote:
quoted
I'm trying to determine whether a merge required a conflict to resolve
after the merge has occurred. The git book has some advice
(https://git-scm.com/book/en/v2/Git-Tools-Advanced-Merging) to use
`git show` on the merge commit or use `git log --cc -p -1`. These
strategies work when the merge conflict was resolved with a change
that is different from either parent. When the conflict is resolved
with a change that is the same as one of the parents, then these
commands are indistinguishable from a merge that did not conflict. Is
it possible to distinguish between a conflict-free merge and a merge
conflict that is resolved by with the changes from one the parents?
No. You'd have to replay the merge to know if it would have had
conflicts.
Aside from the usual "git log -cc", I think this should work (replace
HEAD with whichever commit you are analyzing):
git diff --name-only HEAD^2...HEAD^1 > m1
git diff --name-only HEAD^1...HEAD^2 > b1
git diff --name-only HEAD^1..HEAD > m2
git diff --name-only HEAD^2..HEAD > b2
If files listed between m1 and b2 differ, then the merge is dirty.
Similarly for m2 and b1.
More information here:
http://stackoverflow.com/questions/27683077/how-do-you-detect-an-evil-merge-in-git/41356308#41356308
- Sylvie
There was a patch series a few years ago that added a new diff-mode to
do exactly that, and show the diff against what was resolved. It had a
few issues (I don't remember exactly what) and never got merged.
Certainly one complication is that you don't know exactly _how_ the
merge was done in the first place (e.g., which merge strategy, which
custom merge drivers were in effect, etc). But in general, replaying
with a standard merge-recursive would get you most of what you want to
know.
I've done this manually sometimes when digging into erroneous merges
(e.g., somebody accidentally runs "git reset -- <paths>" in the middle
of a merge and throws away some changes.
You should be able to do:
git checkout $merge^1
git merge $merge^2
git diff -R $merge
to see what the original resolution did.
-Peff
From: Philip Oakley <hidden> Date: 2017-01-28 13:48:00
From: "G. Sylvie Davies" <redacted>
On Fri, Jan 27, 2017 at 9:51 AM, Jeff King [off-list ref] wrote:
quoted
On Fri, Jan 27, 2017 at 11:56:08AM -0500, Michael Spiegel wrote:
quoted
I'm trying to determine whether a merge required a conflict to resolve
after the merge has occurred. The git book has some advice
(https://git-scm.com/book/en/v2/Git-Tools-Advanced-Merging) to use
`git show` on the merge commit or use `git log --cc -p -1`. These
strategies work when the merge conflict was resolved with a change
that is different from either parent. When the conflict is resolved
with a change that is the same as one of the parents, then these
commands are indistinguishable from a merge that did not conflict. Is
it possible to distinguish between a conflict-free merge and a merge
conflict that is resolved by with the changes from one the parents?
No. You'd have to replay the merge to know if it would have had
conflicts.
Aside from the usual "git log -cc", I think this should work (replace
HEAD with whichever commit you are analyzing):
git diff --name-only HEAD^2...HEAD^1 > m1
git diff --name-only HEAD^1...HEAD^2 > b1
git diff --name-only HEAD^1..HEAD > m2
git diff --name-only HEAD^2..HEAD > b2
If files listed between m1 and b2 differ, then the merge is dirty.
Similarly for m2 and b1.
More information here:
http://stackoverflow.com/questions/27683077/how-do-you-detect-an-evil-merge-in-git/41356308#41356308
- Sylvie
This feels as though there ought to be some sort of --left-right option to
get an indication of which side various changes came from
quoted
There was a patch series a few years ago that added a new diff-mode to
do exactly that, and show the diff against what was resolved. It had a
few issues (I don't remember exactly what) and never got merged.
Certainly one complication is that you don't know exactly _how_ the
merge was done in the first place (e.g., which merge strategy, which
custom merge drivers were in effect, etc). But in general, replaying
with a standard merge-recursive would get you most of what you want to
know.
I've done this manually sometimes when digging into erroneous merges
(e.g., somebody accidentally runs "git reset -- <paths>" in the middle
of a merge and throws away some changes.
You should be able to do:
git checkout $merge^1
git merge $merge^2
git diff -R $merge
to see what the original resolution did.
-Peff
From: Jeff King <hidden> Date: 2017-01-28 14:28:35
On Fri, Jan 27, 2017 at 09:42:41PM -0800, G. Sylvie Davies wrote:
Aside from the usual "git log -cc", I think this should work (replace
HEAD with whichever commit you are analyzing):
git diff --name-only HEAD^2...HEAD^1 > m1
git diff --name-only HEAD^1...HEAD^2 > b1
git diff --name-only HEAD^1..HEAD > m2
git diff --name-only HEAD^2..HEAD > b2
If files listed between m1 and b2 differ, then the merge is dirty.
Similarly for m2 and b1.
More information here:
http://stackoverflow.com/questions/27683077/how-do-you-detect-an-evil-merge-in-git/41356308#41356308
I don't think that can reliably find evil merges, since it looks at the
file level. If you had one hunk resolved for "theirs" and one hunk for
"ours" in a given file, then the file will be listed in each diff,
whether it has evil hunks or not.
I don't think this is just about evil merges, though. For instance,
try:
seq 1 10 >file
git add file
git commit -m base
sed s/4/master/ <file >tmp && mv tmp file
git commit -am master
git checkout -b other HEAD^
sed s/4/other/ <file >tmp && mv tmp file
git commit -am other
git merge master
git checkout --ours file
git commit -am merged
merge=$(git rev-parse HEAD)
The question is: were there conflicts in $merge, and how were they
resolved?
That isn't an evil merge, but there's still something interesting to
show that "git log --cc" won't display.
Replaying the merge like:
git checkout $merge^1
git merge $merge^2
git diff -R $merge
shows you the patch to go from the conflict state to the final one.
-Peff
From: G. Sylvie Davies <hidden> Date: 2017-01-29 18:59:54
On Sat, Jan 28, 2017 at 6:28 AM, Jeff King [off-list ref] wrote:
On Fri, Jan 27, 2017 at 09:42:41PM -0800, G. Sylvie Davies wrote:
quoted
Aside from the usual "git log -cc", I think this should work (replace
HEAD with whichever commit you are analyzing):
git diff --name-only HEAD^2...HEAD^1 > m1
git diff --name-only HEAD^1...HEAD^2 > b1
git diff --name-only HEAD^1..HEAD > m2
git diff --name-only HEAD^2..HEAD > b2
If files listed between m1 and b2 differ, then the merge is dirty.
Similarly for m2 and b1.
More information here:
http://stackoverflow.com/questions/27683077/how-do-you-detect-an-evil-merge-in-git/41356308#41356308
I don't think that can reliably find evil merges, since it looks at the
file level. If you had one hunk resolved for "theirs" and one hunk for
"ours" in a given file, then the file will be listed in each diff,
whether it has evil hunks or not.
Well, you have to do both. Do "git show -c" to catch that one
("theirs" for one hunk, "ours" for the other, same file).
And then do that sequence of the 4 "git diff" commands to identify
dirty merges where "theirs" or "ours" was applied to entire files, and
thus not showing up in the "git show -c".
I don't think this is just about evil merges, though. For instance,
try:
seq 1 10 >file
git add file
git commit -m base
sed s/4/master/ <file >tmp && mv tmp file
git commit -am master
git checkout -b other HEAD^
sed s/4/other/ <file >tmp && mv tmp file
git commit -am other
git merge master
git checkout --ours file
git commit -am merged
merge=$(git rev-parse HEAD)
The question is: were there conflicts in $merge, and how were they
resolved?
That isn't an evil merge, but there's still something interesting to
show that "git log --cc" won't display.
Replaying the merge like:
git checkout $merge^1
git merge $merge^2
git diff -R $merge
shows you the patch to go from the conflict state to the final one.
I know the stackoverflow question asks "how to detect evil merges",
and I go along with that in my answer. But honestly I prefer to call
them dirty rather than evil, and by "dirty" I just mean merges that
did not resolve cleanly via "git merge", and had some form of user
intervention, be it conflict resolution, or other strange things.
The trick I propose with the sequence of 4 "git diff" commands
identifies that merge from your example as dirty:
$ cat b1 m2
file
$ cat b2 m1
file
file
The trick doesn't really tell you much except that the merge is dirty.
If you notice that the "m2" file is empty, I think that's one way to
realize that master's edit was dropped, and therefore "other" won.
Maybe it even merged cleanly but someone did a "git commit --amend" to
make it the merge dirty after the fact.
I do like your approach, it's very simple and reliable. But in my
situation I'm writing pre-receive hooks for bare repos, so I don't
think I can actually do "git merge"!
I think my suggestion would work for OP, as long as they also run "git
show -c" alongside it. (And your suggestion would work, too, of
course).
- Sylvie
From: Michael J Gruber <hidden> Date: 2017-02-27 14:28:40
G. Sylvie Davies venit, vidit, dixit 29.01.2017 07:45:
On Sat, Jan 28, 2017 at 6:28 AM, Jeff King [off-list ref] wrote:
quoted
On Fri, Jan 27, 2017 at 09:42:41PM -0800, G. Sylvie Davies wrote:
quoted
Aside from the usual "git log -cc", I think this should work (replace
HEAD with whichever commit you are analyzing):
git diff --name-only HEAD^2...HEAD^1 > m1
git diff --name-only HEAD^1...HEAD^2 > b1
git diff --name-only HEAD^1..HEAD > m2
git diff --name-only HEAD^2..HEAD > b2
If files listed between m1 and b2 differ, then the merge is dirty.
Similarly for m2 and b1.
More information here:
http://stackoverflow.com/questions/27683077/how-do-you-detect-an-evil-merge-in-git/41356308#41356308
I don't think that can reliably find evil merges, since it looks at the
file level. If you had one hunk resolved for "theirs" and one hunk for
"ours" in a given file, then the file will be listed in each diff,
whether it has evil hunks or not.
Well, you have to do both. Do "git show -c" to catch that one
("theirs" for one hunk, "ours" for the other, same file).
And then do that sequence of the 4 "git diff" commands to identify
dirty merges where "theirs" or "ours" was applied to entire files, and
thus not showing up in the "git show -c".
quoted
I don't think this is just about evil merges, though. For instance,
try:
seq 1 10 >file
git add file
git commit -m base
sed s/4/master/ <file >tmp && mv tmp file
git commit -am master
git checkout -b other HEAD^
sed s/4/other/ <file >tmp && mv tmp file
git commit -am other
git merge master
git checkout --ours file
git commit -am merged
merge=$(git rev-parse HEAD)
The question is: were there conflicts in $merge, and how were they
resolved?
That isn't an evil merge, but there's still something interesting to
show that "git log --cc" won't display.
Replaying the merge like:
git checkout $merge^1
git merge $merge^2
git diff -R $merge
shows you the patch to go from the conflict state to the final one.
I know the stackoverflow question asks "how to detect evil merges",
and I go along with that in my answer. But honestly I prefer to call
them dirty rather than evil, and by "dirty" I just mean merges that
did not resolve cleanly via "git merge", and had some form of user
intervention, be it conflict resolution, or other strange things.
The trick I propose with the sequence of 4 "git diff" commands
identifies that merge from your example as dirty:
$ cat b1 m2
file
$ cat b2 m1
file
file
The trick doesn't really tell you much except that the merge is dirty.
If you notice that the "m2" file is empty, I think that's one way to
realize that master's edit was dropped, and therefore "other" won.
Maybe it even merged cleanly but someone did a "git commit --amend" to
make it the merge dirty after the fact.
I do like your approach, it's very simple and reliable. But in my
situation I'm writing pre-receive hooks for bare repos, so I don't
think I can actually do "git merge"!
I think my suggestion would work for OP, as long as they also run "git
show -c" alongside it. (And your suggestion would work, too, of
course).
If you're curious, I kept rebasing Thomas' remerge-diff (on top of our
next) so far. You can find it at
https://github.com/mjg/git/tree/remerge-diff
if you're interested. I don't know what problems were found back then,
or what it would take to get this in-tree now.
Michael