6

I'm attempting to get something setup that invokes the vimdiff git merge tool externally, but because of the way the setup works, I need to invoke vimdiff manually (without git mergetool). Does anyone know the exact command git runs with vimdiff to get the proper layout (three windows split horizontally and one vertically)?

Usually, git mergetool results in this layout:

-------------------------
| LOCAL | BASE | REMOTE |
-------------------------
|        MERGED         |
-------------------------

However, I can only get either an all horizontal or all vertical layout using the vimdiff ... command.

-----------------     -----------------
| L | B | R | M |     |       L       |
-----------------     -----------------
                      |       B       |
                      -----------------
                      |       R       |
                      -----------------
                      |       M       |
                      -----------------

I'd like to know the proper sequence of arguments to either the vim -d command or vimdiff command.

8

I used the trick from this SO question to see what the command line was, and got this:

:exe '!tr "\0" " " </proc/' . getpid() . '/cmdline'
:!tr "\0" " " </proc/23932/cmdline
gvim -f -d -c wincmd J foo ./foo_LOCAL_23800 ./foo_BASE_23800 ./foo_REMOTE_23800

So the trick is using Ctrl-WJ (via :wincmd):

                                                :winc :wincmd
:[count]winc[md] {arg}
                Like executing CTRL-W [count] {arg}.  Example: 
                        :wincmd j
               Moves to the window below the current one.
                This command is useful when a Normal mode cannot be used (for
                the CursorHold autocommand event).  Or when a Normal mode
                command is inconvenient.
                                                CTRL-W_J
CTRL-W J        Move the current window to be at the very bottom, using the
                full width of the screen.  This works like closing the current
                window and then creating another one with ":botright split",
                except that the current window contents is used for the new
                window.

@lcd047 has a better way of getting the arguments: use a (wrapper) script that dumps its arguments before proceeding to exec them. For example:

$ cat args-echo
#! /bin/sh
echo "$0" "$@" > args 
# since I don't really need to merge
# exec vim "$@"
$ git config mergetool.gvimdiff.path "${PWD}/args-echo"
$ yes n | git mergetool
Merging:
foo

Normal merge conflict for 'foo':
  {local}: modified file
  {remote}: modified file
Hit return to start merge resolution tool (gvimdiff): 
foo seems unchanged.
Was the merge successful? [y/n] merge of foo failed
Continue merging other unresolved paths (y/n) ? 
$ cat args 
/tmp/bar/args-echo -f -d -c wincmd J foo ./foo_LOCAL_18649 ./foo_BASE_18649 ./foo_REMOTE_18649
$ git config --unset mergetool.gvimdiff.path
  • Awesome! I just figured it out too. The getting command line options is something I hadn't figured out too. I ended up having to remap a single string command to the "wincmd J" as well. – Cameron Little Jul 19 '15 at 3:20
  • 1
    Looking at /proc is specific to Linux, some systems don't even have a /proc. A more portable way to find out what git mergetool does is to use a wrapper script that writes its arguments to a file and execs Vim, and point mergetool to the script. – lcd047 Jul 19 '15 at 12:58
  • @lcd047 nice idea. Updated. – muru Jul 19 '15 at 13:21
1

For completeness I use the following git config for mergetool. It uses the vim-fugitive plugin:

[merge]
  conflictstyle = merge
  tool = fugitive

[mergetool "fugitive"]
  cmd = vim "+Gdiff" $MERGED

I also dabbled with my own version that handled the three way merge with mutliple tabs for different perspectives. However, I found that the above fugitive version suited my day to day needs better.

[merge]
  conflictstyle = diff3
  tool = diffconflicts

[mergetool "diffconflicts"]
  cmd = diffconflicts vim $BASE $LOCAL $REMOTE $MERGED
  trustExitCode = false
  keepBackup = false

From my diffconflict script:

#!/bin/bash
# Use *real* vimdiff to edit merge conflicts in Git
#
# Instead of editing a file with  <<<< ==== >>> conflict markers, this opens
# each "side" of the conflict markers in a two-way vimdiff window.
#
# (I'm not clear why this isn't the default behavior for Git's vimdiff
# mergetool script. What purpose does the 'result' window with the conflict
# markers serve that vimdiff can't do better on its own?)
#
# Layout:
#
#   Tab1 is a two-way diff of the conflicts.
#       +--------------------------------+
#       |    LCONFL     |    RCONFL      |
#       +--------------------------------+
#   Tab2 is a three-way diff of the original files and the merge base.
#       +--------------------------------+
#       |  LOCAL   |   BASE   |  REMOTE  |
#       +--------------------------------+
#   Tab3 is the MERGED or 'result' file that contains the conflict markers.
#       +---------------------------------------+
#       |       <<<<<<< HEAD                    |
#       |        LCONFL                         |
#       |       ||||||| merged common ancestors |
#       |        BASE                           |
#       |       =======                         |
#       |        RCONFL                         |
#       |       >>>>>>> someref                 |
#       +---------------------------------------+
#
# Workflow:
#
# 1.    Save your changes to the LCONFL temporary file (the left window on the
#       first tab; also the only file that isn't read-only).
# 2.    The LOCAL, BASE, and REMOTE versions of the file are available in the
#       second tabpage if you want to look at them.
# 3.    When vimdiff exits cleanly, the file containing the conflict markers
#       will be updated with the contents of your LCONFL file edits.
# 4.    Sometimes it helps to edit the MERGED file manually. Making changes in
#       Tab3 will overwrite / destroy changes in Tab1. In other words only
#       edit one or the other.
#
# NOTE: Use :cq to abort the merge and exit Vim with an error code.

The full script is on github.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.