List merge commits affecting a file

2019-03-09 14:14发布

问题:

I want to find all the merge commits which affect or involve a given file.

For background, someone mis-resolved a conflict when merging, and it wasn't noticed by the team for a few days. At that point, a lot of other unrelated merges had been committed (some of us have been preferring to not use rebase, or things would be simpler). I need to locate the "bad" merge commit, so it can be checked to identify what else might have been reverted (and, of course, to identify and punish the guilty).

The scenario is like this:

$ echo First > a.txt && git add a.txt && git commit -m 'First commit'
$ git branch branch1
$ echo "Second: main-branch" >> a.txt && git commit -a -m 'Commit on master'
$ git tag a1
$ echo "Third: main" >> a.txt && git commit -a -m 'Other commit on master'
$ git checkout branch1
$ echo "Second: on branch1" >> a.txt && git commit -a -m 'Commit on branch'
$ git tag b1

...So now there are conflicting changes to a.txt in master and branch1.

$ git checkout master
$ git merge branch1 
Auto-merging a.txt
CONFLICT (content): Merge conflict in a.txt
Automatic merge failed; fix conflicts and then commit the result.
$ cat a.txt 
First
<<<<<<< HEAD:a.txt
Second: main-branch
Third: main
=======
Second: on branch1
>>>>>>> branch1:a.txt
$ vi a.txt
# ...
$ cat a.txt 
First
Second: on branch1
$ git add a.txt
$ git commit -m 'Merge commit'

...In other words, the resolution is "take theirs". The graph now looks like this:

$ git log --graph --branches --format='%h %s %d' 
*   fefb623 Merge commit  (refs/heads/master)
|\  
| * 93e065e Commit on branch  (refs/tags/b1, refs/heads/branch1)
* | cf4d12b Other commit on master 
* | 6fade67 Commit on master  (refs/tags/a1)
|/  
* 07545ea First commit 

At this point wrong version of a.txt is on master. The version in a1 is what we want, but the b1 version was committed. So far I've tried:

$ git log --decorate --oneline a.txt
93e065e (refs/tags/b1, refs/heads/branch1) Commit on branch
07545ea First commit

Ok, so neither a1 nor the merge commit appear.

$ git log --decorate --oneline --follow a.txt
...

This shows me everything on both branches, but still omits the merge commits.

$ git log --oneline --graph --decorate a1..master
...

This is of course "everything in master not in a1", which works in this toy example but in my real case gives me all the merges done recently (and no indication of which one touched a.txt).

I can rationalize the disappearance of a1 from the file's history, since the merge chose to omit that change (at least in the sense that git cares about, I think). But how do I locate all the merge commits affecting a.txt? Is this even possible without manually inspecting all the candiate merge commits?

回答1:

The problem in your case is that the merge commits are empty, because you take stuff only from one branch, so git simplifies history by removing those "empty" commits.

So to have your merge commits shown in the log, you need to tell git to change history simplification, by telling --simplify-merges. If you want to understand why you have to use this, go ahead in the git log man page, but I'm out of it :) Anyway in this case the merge commit is correctly shown with a.txt filtering, so that's what you want.

Finally, you want to show diff two times, with each parent, with -m.

So this gives

git log -U -m --simplify-merges --merges -- a.txt

with output

commit a7340d6e91deedff6f52c8ec8da932245f73e5f6 (from d577e6c4dcbff5485ded666c89f38
Merge: d577e6c fe3c4d2

    merge commit

diff --git a/a.txt b/a.txt
index 319137b..1aa6dc4 100644
--- a/a.txt
+++ b/a.txt
@@ -1,3 +1,2 @@
 First
-Second: main-branch
-Third: main
+Second: on branch1


回答2:

You can add the --merges to the log command. This will only list merge commits. Further, you can specify --first-parent so that you only follow the branch history and not consider the history of branches merged into it.