29

When I met a conflict, I tried to use git-mergetool to solve it. I typed:

>git mergetool -t vimdiff

It opened vimdiff in 4-way, not 3-way. My split windows in vimdiff look like:

:ls
  1 #a   "Gemfile.lock"                 line 1
  2 %a   "Gemfile.lock.LOCAL.4828.lock" line 1
  3  a   "Gemfile.lock.BASE.4828.lock"  line 0
  4  a   "Gemfile.lock.REMOTE.4828.lock" line 0

What are they? I want a 3-way diff: target file, merge file and working file. How should I configure my git or vimdiff?

Rob Bednark
  • 25,981
  • 23
  • 80
  • 125
Lai Yu-Hsuan
  • 27,509
  • 28
  • 97
  • 164
  • 9
    No one seems to have pointed this out to you: you need four windows to perform a 3-way diff if you also want the merge-result window to show. In other words, you get a diff between BASE, LOCAL and REMOTE (3 windows), and the possibility to edit the resulting merge (the 4th window). – Magnus Mar 28 '13 at 16:15
  • @Magnus with fugitive and meld it is absolutely possible to resolve a 3-way conflict with 3 windows - the local and merge result is in the middle see this [vimcast for fugitive](http://vimcasts.org/episodes/fugitive-vim-resolving-merge-conflicts-with-vimdiff/) and this [blog post for meld](https://blog.wuwon.id.au/painless-merge-conflict-resolution-in-git/) – icc97 Jan 30 '22 at 10:59
  • Actually the [fugitive issue #1306](https://github.com/tpope/vim-fugitive/issues/1306) explains this perfectly. You have `LOCAL ║ BASE/MERGED ║ REMOTE` – icc97 Jan 30 '22 at 11:04

5 Answers5

37

As an alternative, have you thought about using fugitive?

I'm not going to lie to you; fugitive.vim may very well be the best Git wrapper of all time.

There is a an excellent vimcast, Fugitive.vim - resolving merge conflicts with vimdiff, by Drew Neil. This is part of a series on fugitive.

The Vimcasts website is a good place to learn more about vim.

To use fugitive as you mergetool you can use the following.

git config --global mergetool.fugitive.cmd 'vim -f -c "Gvdiffsplit!" "$MERGED"'
git config --global merge.tool fugitive

Note: you may want to change vim to mvim or gvim.

Fugitive has a lot more to offer than just being a merge tool script so make sure you read the documentation and/or check out the vimcasts.

nbari
  • 25,603
  • 10
  • 76
  • 131
Peter Rincker
  • 43,539
  • 9
  • 74
  • 101
  • Can I use it with `git-mergetool`?? It tells me open the conflict file and call `:Gdiff`. – Lai Yu-Hsuan Sep 06 '11 at 17:48
  • @Lai Yu-Hsuan: I have edited my post to include some mergetool config options – Peter Rincker Sep 06 '11 at 18:28
  • Great. Solved my question prefectly. – Lai Yu-Hsuan Sep 09 '11 at 10:46
  • Maybe this eliminates the need to BeyondCompare for me, thanks – dashesy Sep 11 '13 at 20:21
  • I'm hoping this answer becomes the de-facto standard for all git mergetool questions. fugitive + vim + vimcasts + git mergetool is a truly awesome combination of tools and people. – icc97 Jan 30 '22 at 10:51
  • This answer is referred to in [vim-fugitive issue #1306](https://github.com/tpope/vim-fugitive/issues/1306) – icc97 Jan 30 '22 at 10:52
  • 1
    The [issue #1306](https://github.com/tpope/vim-fugitive/issues/1306) also has an alternative cmd `git config --global mergetool.fugitive.cmd 'nvim -f -c "Gvdiffsplit!" "$MERGED"'` – icc97 Jan 30 '22 at 11:11
10

After lots of research for issuing mergetool with vimdiff and only 3 windows, I came up with this configuration, that allows me to chose when I want 3 windows or the default 4 windows:

git config --global merge.tool vimdiff
git config --global alias.mt mergetool

git config --global mergetool.merge3.cmd 'vim -d -c "wincmd l" "$LOCAL" "$MERGED" "$REMOTE"'
git config --global alias.m3 'mergetool -t merge3'

Now you can start 3 windows just typing:

git m3

And the default (4 windows) will still works as expected with:

git mt

Also, you probably would like to add this lines to the end of your ~/.vimrc or /etc/vim/vimrc

 " shortcuts to vimdiff
 let mapleader=','
 let g:mapleader=','

 if &diff
    map <leader>1 :diffget LOCAL<CR>
    map <leader>2 :diffget BASE<CR>
    map <leader>3 :diffget REMOTE<CR>
 endif

This will create shortcuts like ,1 to grab from left, ,3 to grab from right (in both modes) and also ,2 to grab from base (center window) in the 4 windows mode.

That helps a lot!

My ~/.gitconfig file looks like this:

[user]
        name = Dr Beco
        email = my@email
[merge]
        tool = vimdiff
[mergetool "merge3"]
        cmd = vim -d -c \"wincmd l\" \"$LOCAL\" \"$MERGED\" \"$REMOTE\"
[alias]
        lo = log --pretty=format:\"%h %ce %cd %s\" --graph
        co = checkout
        ci = commit
        cm = commit -a -m
        st = status
        br = branch
        m3 = mergetool -t merge3
        mt = mergetool
[diff]
        tool = vimdiff

I hope this helps you (and those who get to here).

DrBeco
  • 11,237
  • 9
  • 59
  • 76
7

Note: while you can use only 3 Windows as described in Dr Beco's answer
(vim -d -c "wincmd l" "$LOCAL" "$MERGED" "$REMOTE"'),
the 4-windows mode is enhanced with git 2.8 (March 2016)

See commit 2300328 (29 Jan 2016) by Dickson Wong (diwo).
(Merged by Junio C Hamano -- gitster -- in commit 82c17b7, 17 Feb 2016)

The vimdiff backend for "git mergetool" has been tweaked to arrange and number buffers in the order that would match the expectation of majority of people who read left to right, then top down and assign buffers 1 2 3 4 "mentally" to local base remote merge windows based on that order.

Internally, git will now use:

"$merge_tool_path" -f -d -c '4wincmd w | wincmd J' \
            "$LOCAL" "$BASE" "$REMOTE" "$MERGED"

instead of:

"$merge_tool_path" -f -d -c 'wincmd J' \
        "$MERGED" "$LOCAL" "$BASE" "$REMOTE"

mergetool: reorder vim/gvim buffers in three-way diffs

When invoking default (g)vimdiff three-way merge, the merged file is loaded as the first buffer but moved to the bottom as the fourth window.
This causes a disconnect between vim commands that operate on window positions (e.g. CTRL-W_w) and those that operate on buffer index (e.g. do/dp).

This change reorders the buffers to have the same index as windows while keeping the cursor default to the merged result as the bottom window.


With Git 2.31 (Q1 2021), mergetools help considering only 3 panels, not 4.

See commit 30bb808 (13 Feb 2021) by Seth House (whiteinge).
(Merged by Junio C Hamano -- gitster -- in commit cadae71, 25 Feb 2021)

mergetools/vimdiff: add vimdiff1 merge tool variant

Signed-off-by: Seth House
Tested-by: David Aguilar

This adds yet another vimdiff/gvimdiff variant and presents conflicts as a two-way diff between 'LOCAL' and 'REMOTE'.
'MERGED' is not opened which deviates from the norm so usage text is echoed as a Vim message on startup that instructs the user with how to proceed and how to abort.

Vimdiff is well-suited to two-way diffs so this is an option for a more simple, more streamlined conflict resolution.
For example: it is difficult to communicate differences across more than two files using only syntax highlighting; default vimdiff commands to get and put changes between buffers do not need the user to manually specify a source or destination buffer when only using two buffers.

Like other merge tools that directly compare 'LOCAL' with 'REMOTE', this tool will benefit when paired with the new mergetool.hideResolved setting.


Arthur Bowers reports in the comments having used:

git config --global mergetool.vimdiff.cmd \
'$merge_tool_path -f -d -c "4wincmd w | \
 wincmd J" "$LOCAL" "$BASE" "$REMOTE" "$MERGED"'
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • 1
    THANK YOU! This comment saved my sanity. Somehow, I had gotten git to not have the merged pane as default, and googling for how to get it back just leads to lots of pages and guides on how to do 3 way merges, even how to REMOVE the MERGED pane, but it was this comment pointing out the internal usage that allowed me to get my beloved MERGED pane back. Once again, thank you! Anyone else having this problem, I can post the command that solved it for me if needed. – Arthur Bowers Mar 10 '22 at 11:02
  • @ArthurBowers I would be interested in the command you used indeed. – VonC Mar 10 '22 at 11:04
  • 1
    here you go `git config --global mergetool.vimdiff.cmd '$merge_tool_path -f -d -c "4wincmd w | wincmd J" "$LOCAL" "$BASE" "$REMOTE" "$MERGED"'` – Arthur Bowers Mar 23 '22 at 17:17
  • @ArthurBowers Thank you. I have included your comment in the answer for more visibility. – VonC Mar 23 '22 at 18:39
5

Modifying a bit the commands from this page:

git config --global mergetool.vimdiff3.cmd 'vim -f -d "$LOCAL" "$MERGED" "$REMOTE"'
git config --global merge.tool vimdiff3
  • 'Merged' would be your working copy.
  • 'Local' the file that is in the branch you are trying to make the changes
  • 'Remote' the file from the branch you are trying to merge with.

And then you execute the command: git mergetool.

Note: I use fugitive also and highly recommend it.

e3matheus
  • 2,112
  • 1
  • 20
  • 28
4

I'll second the fugitive recommendation.

You could also try out splice.vim. It's a Vim plugin designed to act as a git or mercurial mergetool drop-in replacement. It allows you to easily shuffle various views of the conflict. It's also very quick, straightforward and does a good job at making merging more intuitive. Here's a screencast.

The files you've listed are:

  1. The local file containing the conflict.
  2. The file in the branch you're merging into.
  3. The file in the branch you're merging from.
  4. The file as it was in both branch ancesestor node. This file is very useful for figuring out what's going on!

Hope this helps.

Greg Sexton
  • 9,189
  • 7
  • 31
  • 37