View already-committed Git merge in external 3-way diff tool

空扰寡人 提交于 2019-12-03 05:35:32

问题


Is there any way to view a merge that has already been committed in a 3-way diff?

If a huge merge between branches was committed 3 weeks ago, is there any way I can see a 3-way diff of it in an external diff-tool like BeyondCompare3? I'm looking for just the files changed in the merge commit. Bonus if I could get it to only show me the conflicts and anything manually changed, as opposed to seeing the entire difference of a file between the two branches.

I wouldn't mind settling for a 2-way diff if the left side had the <<<<< ===== >>>>> conflict markers and the right side was the committed result.

I tried looking at diff-tree, diff-files, diff, difftool, show, and others and couldn't figure it out. I know gitk will show the changes just in the merge commit but I do not like the over-under diff view and it is very hard to understand when there are tons of changes.

If only I could do something like git difftool --cc firstparent..secondparent..result


回答1:


Updated answer: My original version of the script below was flawed in the sense that $conflicting_files in fact did not contain only the files that really had conflicts, but all files that were changed in both parent branches (but not necessarily had conflicts). Also, it was not using "the configured merge tool" as advertized in the rationale, but diffuse. I've addressed both issues in the current version of the script.

Original answer: Let's say we have a "master" branch with the main development going on, and a "topic" branch which adds some feature on top of some (older) state of master. By saying that you're looking for just the files changed in the merge commit I assume you're only interested in the changes "topic" introduced to "master" in the merge commit (including any conflict resolution), not in the non-conflicting changes that were done in "master" since "topic" was branched. Further assuming that "master" is the first parent of your merge commit and "topic" is the second, this can be achieved with

git difftool <merge commit>^1 <merge commit>

Note that it does not make sense to use a 3-way diff here as we are looking at the state that includes any conflict resolution. This is also what GitHub is showing for merge commits, by the way, see e.g. this merge commit which I have used for testing.

To see only the conflicting files and their resolutions in a 3-way diff tool I came up with this script

#!/bin/sh

if [ $# -ne 1 ]; then
    echo "Rationale : Show the conflict resolution of a given merge commit in the configured merge tool."
    echo "Usage : $(basename $0) <merge commit>"
    exit -1
fi

# Test e.g. with https://github.com/git/git/commit/8cde60210dd01f23d89d9eb8b6f08fb9ef3a11b8
our=$1^1
their=$1^2
base=$(git merge-base $our $their)

conflicting_files=$(git merge-tree $base $our $their | grep -A 3 "changed in both" | grep "base" | grep -Po "[^\s]+$")
for f in $conflicting_files; do
    diffuse -r $our -r $base -r $their $f
done

I'm using Diffuse instead of Beyond Compare because the first can work directly on Git commits as opposed to local files; change the order of arguments to your liking. To use BC, you probably would need to do temporary checkouts; I was also thinking about redoing the merge, applying the known resolution, and run what ever git mergetool is configured, but both of these ideas would require more work to not clutter your working tree and to do the clean up properly.




回答2:


I don't know how to do a three-way diff in git without some hackery, but for a two way diff I'd use meld. meld is capable of doing a three-way diff if you checkout the three different versions of your project, do a new diff by directory and select the "Three Way Compare" option.

First install meld

sudo apt-get install meld

Then set meld as the difftool

git config --global diff.tool meld

Find the commits

git log | more

Open the commits

git difftool <old-version>..HEAD




回答3:


Like sschuberth, I wrote a script that helped me in finding a change in a merge commit. It works on a single file at a time using vimdiff to show the differences between parents and merge commit.

#! /usr/bin/env ruby

require 'pp'
require 'tmpdir'

merge = ARGV[0] || abort("I need a merge commit as the first argument")
file = ARGV[1] || abort("I need a path as the second argument")
cmd = "vimdiff"

commits = `git log -n 1 #{merge} --format="%H %P"`.split(' ')
abort "expected three commits" unless commits.size == 3
commits[0], commits[1] = commits[1], commits[0]
tmpdir = Dir.mktmpdir
commits.each do |commit|
  tfile = "#{tmpdir}/#{commit[0..10]}"

  puts "git show #{commit}:./#{file} > #{tfile}"
  `git show #{commit}:./#{file} > #{tfile}`
  cmd += " #{tfile}"
end
puts cmd
exec(cmd)

Its a bit hacky but I posted it in the off chance it helps someone.



来源:https://stackoverflow.com/questions/10998728/view-already-committed-git-merge-in-external-3-way-diff-tool

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!