Merge changes using vimdiff

VimDiff

Vim Problem Overview


In my case, I have two files file1 and file2. Using vimdiff, I want to merge the changes as follows:

  1. In first difference, place line from file1 above line from file2. It means difference such as Listing 2 in file2 and List 2 should be List 2 followed by Listing 2 in the merged file.
  2. Reverse case in another change.

Snapshot is shown below.

enter image description here

How can we achieve this using vimdiff?

Vim Solutions


Solution 1 - Vim

You can use the following basic commands to merge:

  • do - Get changes from other window into the current window.

  • dp - Put the changes from current window into the other window.

  • ]c - Jump to the next change.

  • [c - Jump to the previous change.

  • zo - Open folded lines.

  • zc - Close folded lines.

  • zr - Unfold both files completely.

  • zm - Fold both files completely.

  • Ctrlww - change window.

  • :only | wq - quit other windows, write and quit.

Quirks to watch for

  • Both do and dp work if you are on a block of change (or just one line under a single line of change) in Normal mode, but not in Visual mode.

  • The undo command will only work in the buffer that was changed, so if you use dp and change your mind, you need to switch to the other buffer to undo.

  • :diffupdate will re-scan the files for changes (Vim can get confused, and show bogus stuff).

Visual mode and finer grained control

When selecting lines of text in Visual mode, you must use the normal commands:

  • :'<,'>diffget and
  • :'<,'>diffput.

For example:

  1. Enter Visual mode and mark some text/lines.
  2. Then type :diffput to push the selected lines to the other file or :diffget to get the selected lines from the other file.

To belabor the point: This means that if there is a block of changes consisting of multiple lines, then selecting a subset of lines and issueing :diffput will only apply those changes in the other buffer.

(:diffget and :diffput also accept ranges, see :h copy-diffs for more.)

Compare two buffers inside Vim

If you load up two files in splits (:vs or :sp), you can do :diffthis on each window and achieve a diff of files that were already loaded in buffers.

:diffoff can be used to turn off the diff mode.

This Vimcasts post and video show this in practice.

How to apply all changes between buffers

  1. Make sure that all participating buffers are in diff mode (see :h start-vimdiff)

  2. a. Get changes from a buffer to the current one: :%diffget <buffer-number>

  3. b. Put all changes from current buffer into another: :%diffput <buffer-number>

(:% is a range to select the entire file; see :h :%. :ls will show currently opened buffers.)

Solution 2 - Vim

You can switch back and forth between the two windows with Ctrlww. You can copy from one window do a Ctrlww, and then paste into the other. As you resolve differences, the highlights will change, and disappear.

Take a look at this video.

Solution 3 - Vim

You can just switch between the windows and copy and paste to resolve the differences, as @David W. suggests in his answer, but Vim also has dedicated :diffput and :diffget commands to simplify this. With these (or the corresponding normal mode do and dp commands), you don't have to switch between windows, and the range defaults to the current change.

If you need to add instead of overwrite with the other buffer's differences (which is a rather unusual case in a classic two-way diff), you still have to yank the original lines and put them after the :diffget.

After you're done in one place, you can use the ]c, [c commands to jump to the next difference.

Solution 4 - Vim

I am using the following mappings to deal with three-way merges (when conflictstyle=diff3)

  nnoremap g1 :<C-U>call MergeKeepLeft()<CR>
  nnoremap g2 :<C-U>call MergeKeepBoth()<CR>
  nnoremap g3 :<C-U>call MergeKeepRight()<CR>

  function! MergeKeepLeft()
    let lastsearch = @/
    let @/ = '<<<<<<<'
    execute "normal! ?\<cr>dd"

    let @/ = '|||||||'
    execute "normal! /\<cr>V"

    let @/ = '>>>>>>>'
    execute "normal! /\<cr>d"

    let @/ = lastsearch
  endfunction

  function! MergeKeepBoth()
    let lastsearch = @/
    let @/ = '<<<<<<<'
    execute "normal! ?\<cr>dd"

    let @/ = '|||||||'
    execute "normal! /\<cr>V"

    let @/ = '======='
    execute "normal! /\<cr>d"

    let @/ = '>>>>>>>'
    execute "normal! /\<cr>dd"

    let @/ = lastsearch
  endfunction

  function! MergeKeepRight()
    let lastsearch = @/
    let @/ = '<<<<<<<'
    execute "normal! ?\<cr>V"

    let @/ = '======='
    execute "normal! /\<cr>d"

    let @/ = '>>>>>>>'
    execute "normal! /\<cr>dd"

    let @/ = lastsearch
  endfunction

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiondoptimusprimeView Question on Stackoverflow
Solution 1 - VimF YaqoobView Answer on Stackoverflow
Solution 2 - VimDavid W.View Answer on Stackoverflow
Solution 3 - VimIngo KarkatView Answer on Stackoverflow
Solution 4 - VimPetur SubevView Answer on Stackoverflow