Doing a three-way compare with Git and KDiff3
Asked Answered
P

2

15

Is it possible to set Git up so that I can use the three-way compare in KDiff3?

I have two branches that are far too different to auto-merge them, I simply have to check each merge point and I think the best way would be to check out the branch I want the changes from the other branch and say

git difftool HEAD_OF_OTHER_BRANCH -- .

And then select Merge File in KDiff3. After having gone through the files I'd just commit.

I have set up merge.conflictstyle and diff.conflictstyle to diff3 but KDiff3 still starts with a two-way diff. Is this possible? I guess if Git also sends the common ancestor's hash as a parameter, this is possible, but does it?

There is discussion about how to do this with SVN and BC3, but I couldn't find anything for Git and KDiff3.

Piper answered 3/9, 2010 at 12:39 Comment(10)
If you are trying to do a three way merge, why aren't you using mergetool (instead of difftool) ?Revitalize
I don't want Git to automerge anything. I did found out that maybe I could use .gitattributes to tell Git to not automerge. Still, merge will merge the whole massive system on one go, I'd rather handle this on a by-directory basis. Yes, this is a mess, but my intention is to clean it. For that I unfortunately have to do some handiwork.Piper
@Makis: It would seem sensible to do a proper merge but use a strategy that doesn't automerge or force the 'binary' merge driver via .gitattributes to always leave conflicts for the user to resolve. At least this way you can use mergetool to kick off kdiff3 in the logical way.Revitalize
I tried that already, problem is the tree has quite a few binary files as well. And there is a ton of conflicting files, I'm a bit hesitant to go through all of them on one go. I'd rather work in smaller batches.Piper
Why are binary files a problem? Effectively you're just trying to treat all files as binary anyway. The problem with a true merge is that you need (at some point) to make a merge commit anyway so trying to do things in stages and getting a true history are conflicting objectives.Revitalize
@Charles, that's sounds interesting, how do you do that ?Wrist
@mb14: Which approach are you referring to?Revitalize
@Charles: a way to disable the automerge (with you binary driver ???)Wrist
@mb14: IIRC, you can just put a * merge = binary line in .gitattributes and conflicts won't be autoresolved at all. I haven't tried it recently, though, so I might have remembered incorrectly.Revitalize
@Charles: I'm not actually that worried about the history at this point. It's going to be a mess anyway. Problem is, we are talking about a large code base which will take several people days to sort out. Doing it all in one go is just too complex, my plan was to go through the a component at a time, where possible, and run tests in-between. As for .gitignore, I tried the unset value for merge.Piper
W
3

It seems that git diff do only a 2-way diff (which make sense to generate patch etc) except in a merging state , you have to do a merge for that. I was in a similar situation the other day and I ended up mergin using the ours strategy. That worked but wasn't ideal. Maybe we need a 'nonresolve' merging strategy which doesn't try to resolve any conflicts. You might be able to emulate that by tweaking the .git/MERGE_* files and set all the files as conflicted.
Otherwise the obvious solution is to checkout 3 different directory and run kdiff3 , but I guess you are looking for a more elegant solution

Wrist answered 3/9, 2010 at 13:40 Comment(4)
Checking to different directories would be ok, except how do you create the third dir that holds the common ancestors? As long as I work on a single repo, Git figures the common ancestors out for me.Piper
@makis you can use the 'git merge-base' command to find the common ancestor of two commits, handy.Wrist
Hm, so the common ancestor is the same for all files? I thought this could differ depending on when a file is edited. If it's the same for each file, then this would definitely be a workable solution.Piper
git merge-base will give you the commit from where you fork. This commit is the same for every files indeed, but as long as you haven't cherry-picked any files between branches, that should be a good enough approximation.Wrist
S
10

Run this on the command line:

git config --global mergetool.kdiff3.path /path/for/your/kdiff3/binary  

Then, when solving conflicts you just have to do:

git mergetool --tool=kdiff3
Scoop answered 5/3, 2012 at 18:20 Comment(0)
W
3

It seems that git diff do only a 2-way diff (which make sense to generate patch etc) except in a merging state , you have to do a merge for that. I was in a similar situation the other day and I ended up mergin using the ours strategy. That worked but wasn't ideal. Maybe we need a 'nonresolve' merging strategy which doesn't try to resolve any conflicts. You might be able to emulate that by tweaking the .git/MERGE_* files and set all the files as conflicted.
Otherwise the obvious solution is to checkout 3 different directory and run kdiff3 , but I guess you are looking for a more elegant solution

Wrist answered 3/9, 2010 at 13:40 Comment(4)
Checking to different directories would be ok, except how do you create the third dir that holds the common ancestors? As long as I work on a single repo, Git figures the common ancestors out for me.Piper
@makis you can use the 'git merge-base' command to find the common ancestor of two commits, handy.Wrist
Hm, so the common ancestor is the same for all files? I thought this could differ depending on when a file is edited. If it's the same for each file, then this would definitely be a workable solution.Piper
git merge-base will give you the commit from where you fork. This commit is the same for every files indeed, but as long as you haven't cherry-picked any files between branches, that should be a good enough approximation.Wrist

© 2022 - 2024 — McMap. All rights reserved.