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
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.
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
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