I recently added display helper functions that allow you to toggle the
diff
option in selected windows. That in turn makes it possible to see 2-way diffs at different points of the conflict resolution process. I am going to show you how such a procedure could look like.There is more documentation on the Conflict Slides frontpage and the functions are documented in more detail in the plugin file.
The tutorial part focuses on Git somewhat, but as long as you have the same conflict markers, this should work with any VCS.
Setup
I would recommend to turn the folding off in the post-lock callback to mitigate jolting when changing content. You can turn the folding back on with the argument
'on'
instead of 'off'
, and the folding can be toggled with the argument 'toggle'
.
fun! g:conflict_slides_post_lock_callback() call CS_DiffChangeFolding('off') endfun
If your VCS offers to insert the base content into the markers, you should consider to turn that on. Otherwise the base slide is not available. In Git you can add the base content with the following command:
git config merge.conflictstyle diff3
Generally, I am using three diff windows with
ours, dest, theirs
content. That is the setup provided by vim-fugitive. You should really check out that plugin, if you haven't done so already. It is tremendously easy to find the commit for a line in a conflict (among many other things).Example Mappings
Here are some mappings that extend the default mappings from Conflict Slides (CS) a bit. You can use this as a template for your own mappings. The
^[1-3]
mappings do essentially turn the diff on in all 3 windows, except in the given window where the diff is toggled. This allows you to see a two-way diff for any combination of the three windows, as well as return to a three-way diff.nnoremap <silent> ^l :call CS_LockNextConflict('lock-current')<CR>zz nnoremap <silent> ^n :call CS_LockNextConflict()<CR>zz nnoremap <silent> ^N :call CS_LockNextConflict('backward')<CR>zz nnoremap <silent> ^u :call CS_ReleaseLockedConflict()<Bar> \ call CS_DiffChangeFolding('on')<CR> nnoremap <silent> ^f :call CS_DiffChangeFolding('toggle')<CR>zz nnoremap <silent> ^1 :call CS_DiffSwitch3Toggle(1)<CR> nnoremap <silent> ^2 :call CS_DiffSwitch3Toggle(2)<CR> nnoremap <silent> ^3 :call CS_DiffSwitch3Toggle(3)<CR>
You can simply define a
g:conflictslides_locked_mappings
dictionary in your Vim configuration to override the one from CS. The top section in the following snippet is the same as in the plugin. Below that are a few mappings that allow you to insert a slide while turning diff on/off in selected windows. The first two of those mappings are useful immediately after you locked to a conflict to explore the changes in each parent.
let g:conflictslides_locked_mappings = { \ 'b' : ":call CS_ModifyConflictContent('base')<CR>", \ 'B' : ":call CS_ModifyConflictContent('base', 'append')<CR>", \ 'o' : ":call CS_ModifyConflictContent('ours')<CR>", \ 'O' : ":call CS_ModifyConflictContent('ours', 'append')<CR>", \ 't' : ":call CS_ModifyConflictContent('theirs')<CR>", \ 'T' : ":call CS_ModifyConflictContent('theirs', 'append')<CR>", \ 'a' : ":call CS_ModifyConflictContent('ours theirs')<CR>", \ 'A' : ":call CS_ModifyConflictContent('theirs ours')<CR>", \ 'f' : ":call CS_ModifyConflictContent('forward')<CR>", \ 'r' : ":call CS_ModifyConflictContent('reverse')<CR>", \ 'F' : ":call CS_ModifyConflictContent('forward-nobase')<CR>", \ 'R' : ":call CS_ModifyConflictContent('reverse-nobase')<CR>", \ \ 'e' : ":call CS_ReleaseLockedConflict()<CR>", \ 'q' : ":call CS_ModifyConflictContent('forward')<Bar>" \ . "call CS_ReleaseLockedConflict()<CR>", \ '<CR>' : ":call CS_LockNextConflict()<CR>", \ '<BS>' : ":call CS_LockNextConflict('restore-conflict')<CR>", \ \ 'V' : ":call CS_SelectCurrentConflictRange(0)<CR>", \ 'v' : ":call CS_SelectCurrentConflictRange(500)<CR>", \ \ \ \ '<space>b' : ":call CS_ModifyConflictContent('base')<Bar>" \ . "call CS_DiffSwitch3Off(3)<CR>", \ '<space><space>b' : ":call CS_ModifyConflictContent('base')<Bar>" \ . "call CS_DiffSwitch3Off(1)<CR>", \ '<space>o' : ":call CS_ModifyConflictContent('ours')<Bar>" \ . "call CS_DiffSwitch3Off(1)<CR>", \ '<space>t' : ":call CS_ModifyConflictContent('theirs')<Bar>" \ . "call CS_DiffSwitch3Off(3)<CR>", \ '<space>a' : ":call CS_DiffSwitch3AllOn()<CR>", \ \ '<space>1' : ":call CS_DiffSwitch3Toggle(1)<CR>", \ '<space>2' : ":call CS_DiffSwitch3Toggle(2)<CR>", \ '<space>3' : ":call CS_DiffSwitch3Toggle(3)<CR>", \ \ '<space>f' : ":call CS_DiffChangeFolding('on')<CR>zz", \ '<space>F' : ":call CS_DiffChangeFolding('off')<CR>zz", \ }
There is nothing in there that cannot be done with the global mappings from above. So, it's just a template in case you want to create more convenient mappings with the mechanism provided by CS. You may need to adapt the window number in the calls if you use a different setup.
Prepare for Conflict
There is this awesome merge resolution boot camp with solutions and I've picked one merge to demonstrate how to resolve a conflict. You can follow if you like. This is the commit:
736a2dd Merge tag 'virtio-next-for-linus' of git://...
First, prepare two tags and a branch.
git tag ExpDest 736a2dd2571ac56b11ed95a7814d838d5311be04 git tag ExpTheirs ExpDest^2 git checkout -b ExpMaster ExpDest^1
Inspect the result with this command:
git log --graph --oneline --decorate --simplify-by-decoration -3 ExpDest
It should look similar to this:
Now, let's try to merge.
git merge --log ExpTheirs
This should result in a conflict with several unmerged files.
Resolution
If you use Git without Fugitive, you can start the setup described above by adding the following section to your
~/.gitconfig
file,[mergetool "gv3"] cmd = gvim -f -d \"$LOCAL\" \"$MERGED\" \"$REMOTE\" \"+2wincmd w\"
and start that mergetool for
drivers/char/virtio_console.c
like so:
git mergetool -t gv3 drivers/char/virtio_console.c
By pressing
^l
you can lock to the one and only conflict in this file. This will produce a beautiful rainbow of colors.
The initial 3-way diff of the conflict |
Now you can use the
<space>b
and <space><space>b
mappings to see two-way diffs between the base slide and ours/theirs respectively. (The same can be accomplished with b
and then ^1
or ^3
)Difference ours/base |
Difference base/theirs |
Aha! The conditional branch has been wrapped in a spinlock on our side and a very important call has been replaced by an even more important call on their side. If you are using Fugitive you can very conveniently open a blame view for their commit and try to find out more about that change.
Fugitive's blame view started from their modification |
To resolve it you can take our side with
<space>o
, and append their side with T
.
Spotting the surplus line in the difference dest/theirs |
Because this is a two-way diff with theirs, the wrong call can be easily spotted. Let's unlock with
e
(for edit) and delete the wrong line. You can visualize the changes on each side by using the mappings ^1
and ^3
.Resolved difference ours/dest |
Resolved difference dest/theirs |
Resolve more
If you would like to resolve some more conflicts yourself, search the repository with the following command:
git log --merges --grep Conflicts master
No comments:
Post a Comment