I want to perform a three-way diff between two git branches with a common merge base, and view it with kdiff3.
I've found lots of guidance on SO (and a few very similar questions (1, 2, 3) ) but I haven't found a direct answer. Notably, a comment on this answer implies that what I want is possible, but it didn't work for me. Hopefully that user might chime in here :)
For background, when I perform merges I use a "diff3" conflict style:
git config --global merge.conflictstyle diff3
And I have git mergetool configured to use kdiff3.
When resolving merge conflicts this shows me four files:
- The current branch's file ($LOCAL)
- The other branch's file ($REMOTE)
- The file which is the common ancestor of the two branches ($BASE)
- The merged output file ($MERGED)
However, git difftool only will pull up the two branch tips.  I want to see the base file, too.  To be clear, I want to be able to perform this diff before merging, including on files without merge conflicts. (git mergetool only shows the three-way diffs if there are conflicts).
Partial Solution #1:
With an individual file, I can export the three versions and manually call the diff:
git show local_branch:filename > localfile
git show remote_branch:filename > remotefile
git show `git merge-base local_branch remote_branch`:filename > basefile
{kdiff3_path}/kdiff3.exe --L1 "Base" --L2 "Local" --L3 "Remote" -o "outputfile" basefile localfile remotefile &
There are two problems with this:
- I want it to work for the whole project, not just a specific file.
- This is ugly! I can script it, but I hope there's a much cleaner way using standard git processes.
Partial Solution #2:
Thanks to this answer and comment for the inspiration.
Create a custom merge driver that always returns "false", which creates a conflicted merge state without actually doing any auto-merging.  Then perform the diff using git mergetool.  Then abort the merge when you're finished.
- Add to - .git/config:- [merge "assert_conflict_states"] name = assert_conflict_states driver = false
- Create (or append to) - .git/info/attributesto cause all merges to use the new driver:- * merge=assert_conflict_states
- Perform the merge, which now doesn't do any automerging. 
- Do the diff. In my case: - git mergetoolwhich brings up the kdiff3 three-way merge.
- When done, abort the merge: - git merge --abort.
- Undo step #2. 
This would (sorta) work except that kdiff3 performs an automerge when called, so I still can't see the pre-merged diffs.  I can fix this, though, by changing Git's stock kdiff3 driver file (.../git-core/mergetools/kdiff3 by removing the --auto switch.
Even so, this has the following show-stopping problems:
- This only works when both files have changed! In the case where only one file changed, the updated file replaces the older file and the merge is never called.
- I have to modify the Git kdiff3 driver, which isn't portable at all.
- I have to modify attributesbefore and after doing the diff.
- And, of course, I was hoping to do this without merging :)
Information for the bounty:
According to answers given, this isn't possible with standard Git. So now I'm looking for a more out-of-the-box solution: How can I tweak Git to make this happen?
Here's one lead: Apparently, if only one of the three files has changed, this newer file is used in the merge result without actually calling the merge driver. This means that my custom "conflict-creating" merge driver is never called in this case. If it was, then my "Partial Solution #2" would actually function.
Could this behavior be changed by tweaking files or configurations? Or perhaps there's a way to create a custom diff driver? I'm not ready to start playing with the Git source code...
Any clever ideas?
 
     
     
     
     
     
    