gitgit-mergemergetool

git, merge with a tool, instead of resolve with a tool


I come from mercurial, and there is this thing I don't know how to do with git.

Under mercurial, during a merge, each versions of a conflicting file (base, other and local) are presented side by side on my favorite merging tool (kdiff3 or meld). I merge them, save the result and everybody is happy.

With git, I git merge and in case of conflicts, I git mergetool ... and I get files full of horrors like this cluttered mess:

first line
<<<<<<< HEAD
local line
=======
other line
>>>>>>> other-branch
last line

How can I configure git to open my favorite merge tool before the conflict happens ? I would like to merge manually instead of resolving manually :)


Solution

  • (Preliminary note: I don't use any of these tools. Instead, I set merge.conflictStyle to diff3, which uses a slightly different markup format in the work-tree file. Most of the time I can easily resolve this directly in vim. If it's too messy, I will sometimes use git show to extract the three inputs directly, e.g., git show :1:path. But everyone is different, so see Git: How configure KDiff3 as merge tool and diff tool.)

    How can I configure git to open my favorite merge tool before the conflict happens?

    You can't. Well, you could if you wrote your own merge strategy—the equivalent of git merge-recursive—but nobody has done this sort of thing in many years because it's just too hard. Fortunately, you don't need to:

    I would like to merge manually instead of resolving manually :)

    If there is a conflict, Git writes its own attempt at merging into the work-tree file.1 This is what you are seeing in your editor here. But Git has left the three input files in what Git calls, variously, the index, the staging area, and (rarely these days) the cache. These are three names for the same thing: staging area refers to the way you use it, while cache refers to various internal aspects of it and index is a sort of meaningless term. In this case, the word "index" is probably the best one to use, and I'll use it here.

    When you run git mergetool, Git should fire up your chosen merge tool on all three input files, plus the fourth (Git's-attempt-at-merge with conflict markers) work-tree file. This tool can then show you some or all of these various inputs. You can then use the tool, in whatever way the tool itself works, to do the resolving manually, completely ignoring Git's attempt at merging, if you like.

    Note that the three input files—merge base, left or local our --ours, and right or remote or --theirs—that git mergetool provides to your merge tool are just temporary files. They will not be used as the merge result. The file that will be used as the merge result, when your merge tool indicates back to Git that it is done merging, is the main work-tree file: the one with the mess in it. So your tool must overwrite this file with your merge result.

    Unlike Mercurial, the core Git does not have built in merge tools (well, other than its own default—which it always runs anyway, as part of the merge strategy you choose with -s). Hence you, or someone who sets up a Git distribution for you, must tell Git how to run any particular external merge tool. Each Git installation will in general have some set of pre-configured external merge tools, which may or may not include the one you want. If you need to configure your own external merge tool, see Git on Windows: How do you set up a mergetool? (Despite the title, some of its answers are good on non-Windows systems. See CB Bailey's answer in particular.)

    The four names that your merge tool must deal with are supplied as environment variables: $BASE names the merge base file (from staging slot #1 in the index), $LOCAL names the --ours file (staging slot #2 in the index), $REMOTE names the --theirs file (staging slot #3), and $MERGED is the name of the file that git mergetool expects your merge tool to update before your merge tool signals success or failure to the git mergetool script.2


    1Remember that Git stores usable copies of your files in your work-tree, but actually builds commits from the special Git-ized format files that are stored (indirectly) in the index. During a conflicted merge, Git simply leaves all three input files in the index, using nonzero index slot numbers. A file that Git thinks it resolved on its own goes into the normal slot-zero index entry, wiping out the nonzero slots, so the three input files are not easily available here. (This is something of a defect, in my opinion, but Git doesn't ask my opinion. Technically Git doesn't even write the files' entries into the nonzero index slots if it can merge them trivially as it processes files. This is probably for speed and/or laziness.)

    2Your tool should signal success or failure via its OS-level exit code. If it does so, and you configure this tool with trustExitCode set to true, git mergetool will know whether to automatically git add the $MERGED file. If you configure trustExitCode to false, git mergetool will use some heuristics to guess whether to believe that the tool successfully merged the files.