Stu
Stu

Reputation: 324

Does Git keep a record of past merge conflicts?

I'd like to look at the repository history and see what types of merge conflicts have occurred in the past.

I've tried using git log --merges but that just seems to show successful merges.

Upvotes: 7

Views: 3388

Answers (4)

A.H.
A.H.

Reputation: 66263

For a first quick overview you can use:

git log --merges --cc --all

The --cc option is (from git help log):

-c
   With this option, diff output for a merge commit shows the
   differences from each of the parents to the merge result
   simultaneously instead of showing pairwise diff between a parent
   and the result one at a time. Furthermore, it lists only files
   which were modified from all parents.

--cc
   This flag implies the -c option and further compresses the patch
   output by omitting uninteresting hunks whose contents in the
   parents have only two variants and the merge result picks one of
   them without modification.

This will show show the conflicts but - AFAICT - a little bit more on "near conflicts".

Upvotes: 1

code_fodder
code_fodder

Reputation: 16341

Not easily.. but you could knock up a script if you need to...

You could use git log to find all the merge-commits - assuming you left the automatic message in place, or use a sensible message that is (hopefully) always the same. For example:

git log --all --oneline --graph --decorate produces a decent tree-view of the repo - eg:

*   a1a9bde (HEAD -> TESTER) Merge branch 'test' into TESTER
|\
| * 2ff3965 (test) updated
| * 12af0b0 s
* | eb9ab80 updated
* |   bec65ad (master) Merge branch 'master' of d:\software\sandpit\git-test
|\ \
| * \   595bff6 Merge branch 'master' of d:\software\gitRepos\git-test
| |\ \
| | * | 77d69c7 new file
| | |/
   :

But for scripting use just git log --oneline --all:

a1a9bde Merge branch 'test' into TESTER      <----- LETS LOOK AT THIS MERGE...
eb9ab80 updated
2ff3965 updated
bec65ad Merge branch 'master' of d:\software\sandpit\git-test
8a73cd1 updated test1
58080f2 new file
819226c new file
e122cc6 file for merge back to master
49acb0b file added in branch
a262470 yet another file
12af0b0 s

Or just get the hash list: git log --oneline --all | grep "Merge branch" | awk '{print $1}':

a1a9bde
bec65ad

update 1

or as Dietrich Epp (almost) suggested use git rev-list --min-parents=2 a1a9bde - this gives the full hash's of any commit with 2 parents - nice Dietrich Epp!

update 1 - end

Now loop through these hash's, e.g. from here using the first one: a1a9bde

You can get the parent hash's like: git show --format="%P" 595bff6 - this yeilds:

eb9ab8029e6951f68a9c1008bb8611444d31528d 2ff3965b9aa1e6c49c82127e5f08199796a40780

So now you can run a mock-merge by:

  • Checkout the first hash: git checkout eb9ab8029e6951f68a9c1008bb8611444d31528d -B merge_test - this checks out the first hash onto a branch called merge_test (and replace any previous branch of the same name)

  • merge the second hash (dry run): git merge --no-commit --no-ff 2ff3965b9aa1e6c49c82127e5f08199796a40780

  • Use git status | grep "both modified" to see conflicts - gives:
     both modified:   testfile1.txt     <---- THIS FILE IS A CONFLICT
  • Now do what you need to do, then finally tidy up with git merge --abort

So anyway - its a bit painful, but this can all be scripted up quite easily, I have provided the commands to do it but I don't have the incentive to write the script out... :o

Upvotes: 1

Dietrich Epp
Dietrich Epp

Reputation: 213338

Git does not record the merge conflicts, unless you've enabled rerere, but it does record most of the information necessary to recreate them. You can write a script to recreate the merge conflicts, with some caveats... Git doesn't record the merge strategy you used to resolve a merge, so you will have to hope that the default strategies are sensible choices, or do some tweaking to get this to work on your repo.

How It Works

You can enumerate merges with rev-list:

git rev-list --parents --min-parents=2 --all

This is preferable to git log, because the output is suitable for parsing by a script. This will produce a list of merge commits, one per line. The child commit is the first hash and the remaining hashes are parents. For example,

28171e725cc93e8cb85194931e7138c31f980a43 20af81cf6388026ecf0de4eec8783e7a38905ccd 2d77896f5fcc1a9b09f4f099aa6e945e8da86410

Here, the merge is between 20af and 2d77.

Check out the first parent, in detached head:

git checkout -q --detach 20af81cf6388026ecf0de4eec8783e7a38905ccd

Then merge the remaining parents:

git merge -q --no-commit --no-ff 2d77896f5fcc1a9b09f4f099aa6e945e8da86410

The status code of merge will be 1 if there are conflicts, which you can examine. If there is a merge conflict, you will see files with UU in the porcelain status:

git status --porcelain

You can then abort the merge to reset to a state where you can work with the repo:

git merge --abort

Script

I have turned this into a script as a demo. The script must be run on a pristine repository, since it will run a bunch of git checkout and git merge commands.

https://gist.github.com/depp/f1838cf4559f9cde74b9d3052b8abbb0

The script will retry every merge in history using the default strategy, and report the conflicting files.

If you wish, you can easily extend the script to copy out the conflicting files so you can look at the conflicts.

Upvotes: 8

mhb
mhb

Reputation: 773

I think you could benefit from git-rerere in some combination with previous questions on training it:

I think the actual storage location is in .git/rr-cache, but I've never looked and don't know what's in there.

Upvotes: 2

Related Questions