GithubHelp home page GithubHelp logo

whiteinge / diffconflicts Goto Github PK

View Code? Open in Web Editor NEW
382.0 13.0 20.0 746 KB

A better Vimdiff Git mergetool

License: BSD 3-Clause "New" or "Revised" License

Shell 37.31% Vim Script 62.69%
vim vim-plugin git mergetool vimdiff

diffconflicts's Introduction

vim-diffconflicts

A better Vimdiff mergetool.

tl;dr:

  • Call :DiffConflicts to convert a file containing conflict markers into a two-way diff.
  • Install as a Git or Mercurial mergetool to do that automatically. (See Installation below.)

Why?

Watch a demonstration version of this README on YouTube:

diffconflicts Video Demonstration

Contents:

Three-Way Diffs are Hard

When Git cannot automatically resolve conflicts it writes a file with conflict markers surrounding the conflicting areas. These conflicts must be resolved manually. This is often done via a three-way comparison.

Vim supports three-way diffs however syntax highlighting alone is not sufficient to showcase the differences between that many versions. In addition, the default keybindings are not well suited to moving individual changes between that many windows.

The screenshot below is an example of Vimdiff as a Git mergetool using default settings. None of the conflicts have an obvious resolution:

Editing Conflict Markers is Hard

When human intervention is needed it is rarely as simple as choosing the "left" change or the "right" change. The correct resolution often involves a mix of both changes. It is difficult to manually edit a file containing Git conflict markers because the human eye isn't well suited to spotting subtle differences, particularly when the differences are not adjacent:

Two-Way Diffs are Eas(ier)

A two-way diff more simply highlights just the relevant differences which makes the resolution more clear. The merge base and history of each version of the conflict is a useful reference to learn the intent of each conflicting change, however those are not as useful to see in the diff.

Vimdiff is well suited to two-way diffs:

Conflict-Markers are a Two-Way Diff

Git does an admirable job of automatically resolving conflicts. We want to retain all the work and resolve only the things that Git could not. That work is reflected in the files containing conflict markers, but it is not reflected in a two-way diff between LOCAL and REMOTE.

Rather than editing the conflict markers directly, it is better to perform a two-way diff on only the "left" and "right" sides of the conflict markers by splitting them apart.

Installation

  1. Install this plugin using your favorite Vim plugin manager, or just clone the repo into your packages directory (see :help packages).

  2. Configure Git to use this plugin as a mergetool:

    git config --global merge.tool diffconflicts
    git config --global mergetool.diffconflicts.cmd 'vim -c DiffConflicts "$MERGED" "$BASE" "$LOCAL" "$REMOTE"'
    git config --global mergetool.diffconflicts.trustExitCode true
    git config --global mergetool.keepBackup false
    

    Or, if you'd prefer to always open both the diff view and the history view call DiffConflictsWithHistory instead:

    git config --global mergetool.diffconflicts.cmd 'vim -c DiffConflictsWithHistory "$MERGED" "$BASE" "$LOCAL" "$REMOTE"'
    
  3. During a merge you can call :DiffConflictsShowHistory to open a new tab containing the merge BASE and full copies of the LOCAL and REMOTE versions of the conflicted file. This can help to understand the history or intent behind the conflicting changes to help you decide how best to combine the changes.

    This tab is not opened by default so that Vim starts more quickly.

Mercurial

Configure Mercurial to use diffconflicts as a mergetool by adding:

[merge-tools]
diffconflicts.executable=vim
diffconflicts.args=-c 'let g:diffconflicts_vcs="hg"' -c DiffConflicts "$output" $base $local $other
diffconflicts.premerge=keep
diffconflicts.check=conflicts
diffconflicts.priority=99

to your .hgrc file. Or, if you prefer to always open both the diff view and the history view use

diffconflicts.args=-c 'let g:diffconflicts_vcs="hg"' -c DiffConflictsWithHistory "$output" $base $local $other

as the args setting to call DiffConflictsWithHistory.

diffconflicts's People

Contributors

alfunx avatar sudo-nice avatar tmc avatar utahdave avatar whiteinge avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

diffconflicts's Issues

Is it possible to use default two-pane view for viewing `$ git diff` output?

Is it possible to use diffconflicts's excellent two-pane presentation to view differences between files? Vim's default terminal output of cramming everything into one giant single-column display can prove quite cumbersome when needing to identify and update a file's contents.

I've set up my .gitconfig with:
[difftool "diffconflicts"]

When I launch a diff mode $ git diff <branch1> <branch2> I am given the following prompt:
"Launch 'diffconflicts' [Y/n]"

And then only a single file opens into view.

Thank you again for your great plugin! And if this question is outside of the scope of this plugin, I apologize. Just tell me so and I'll gladly search for a solution elsewhere :)

All the best,
Blake

Compatibility with folding

Thank you for this really useful plugin. However, I am getting confusing results when folding is enabled. I think that the :g commands don't look inside the folds, so they may delete unintended lines when the diff markers somehow end up inside a fold. I have solved the issue by adding silent execute "normal! zR" and silent execute "normal! zM" around the delete commands, but I am no vimscript expert by any means and I guess there should be a better way.

vim-diffconflict workflow

Hello. Sorry may I ask a stupid question.
When I run :DiffConflict, it open a new tab with 2 windows for Left and Right. How could I choose which one to use? I put the cursor at the change I want to pick, ran :diffget but vim show an error that modifiable is off.

Excellent viewmode, thank you!

The viewmode you've made is EXCELLENT! Also, your walkthrough video is a great resource to better understand how to work through merge issues. Thank you very much for all the work and effort everyone involved has put into this project, it's something I use on a regular basis!

3way

Support for 3way conflicts should be trivial.

Help when editing multiple diffs with git mergetool

Thanks for a great project.

When I have multiple conflicted files, and I run git mergetool, diffconflicts successfully edits the first conflicted file, but does not edit any of the subsequent files.
If I try to run git mergetool a second time, I get a message that No files need merging

After I fixed up the first file I ran :xa or :wqa (as I see in the Youtube video in the README). Is this the correct command?

The relevant config in my ~/.gitconfig is

[merge]
        tool = diffconflicts
[mergetool "diffconflicts"]
        cmd = vim -c DiffConflicts \"$MERGED\" \"$BASE\" \"$LOCAL\" \"$REMOTE\"
        trustExitCode = true
[mergetool]
        keepBackup = false

DiffConflictsShowHistory broke?

when I run the DiffConflictsShowHistory, I get errors:

Error detected while processing function <SNR>17_checkThenShowHistory:
line    1:
E117: Unknown function: getbufinfo
E116: Invalid arguments for function copy(getbufinfo()), {i, x -> x.name =~# 'BASE' || x.name =~# 'LOCAL' || x.name =~# 'REMOTE'})
E116: Invalid arguments for function filter(copy(getbufinfo()), {i, x -> x.name =~# 'BASE' || x.name =~# 'LOCAL' || x.name =~# 'REMOTE'})
E15: Invalid expression: filter(copy(getbufinfo()), {i, x -> x.name =~# 'BASE' || x.name =~# 'LOCAL' || x.name =~# 'REMOTE'})
line    4:
E121: Undefined variable: l:xs
E116: Invalid arguments for function len(l:xs) < 3)
E15: Invalid expression: (len(l:xs) < 3)
Press ENTER or type command to continue

How to start in "legacy" Two-Pane view, without 3-Way shown?

Hello!
Now that 3-way diffs have been added, how do we set it up so that it starts in the "legacy" viewmode with only two panes showing?

Here's what I have in my .gitconfig
[merge] tool = diffconflicts

[mergetool "diffconflicts"] cmd = vim -c DiffConflicts \"$MERGED\" \"$BASE\" \"$LOCAL\" \"$REMOTE\" trustExitCode = true

[mergetool] keepBackup = false

Here's how it initially looks upon start:
https://imgur.com/a/s4h8812

And here's how it looks after entering :DiffConflicts:
https://imgur.com/a/Bs2Wd3W

Is there any way to start the mergetool with the upper "3-way" portion hidden and just show the side-by-side view as outlined in your youtube video?

Thanks again for such a great diff viewmode!
-Blake

Feedback :)

This plugin is golden โค๏ธ
It's kept simple and does the job very well ๐Ÿ‘

Then I was reading git config options, I saw mergetool.hideResolved (added somewhere in 2021):

During a merge Git will automatically resolve as many conflicts as possible and write the MERGED file containing conflict markers around any conflicts that it cannot
resolve; LOCAL and REMOTE normally represent the versions of the file from before Git's conflict resolution. This flag causes LOCAL and REMOTE to be overwriten so that only
the unresolved conflicts are presented to the merge tool. Can be configured per-tool via the mergetool..hideResolved configuration variable. Defaults to false.

Which basically implements what you did with this plugin. Thought you'd want to know ๐Ÿคทโ€โ™‚๏ธ

The plugin still helps with the UI setup, where we can easily access local, base & remote with the ...WithHistory variant

Local and Remote windows swapped??

Sorry don't mean to spam you with multiple issues, but there's no place for general questions. When i run the example:

<<<<<<< HEAD
twas brillig, and the slithy toves
Did gyre and gimble in the wabe:
all mimsy were the borogoves,
And the mome raths outgrabe.
=======
'Twas brillig, and the slithy toves
Did gyre and gimble in the wabe:
All mimsy were the borogroves
And the mome raths outgabe.
>>>>>>> branchA

"Beware the Jabberwock, my son!
The jaws that bite, the claws that catch!
Beware the Jubjub bird, and shun
The frumious Bandersnatch!"

I would expect the HEAD section to be the LOCAL (LEFT WINDOW) and branchA (the branch being merged into LOCAL) to be the REMOTE (Right window)

But for some reason they are swapped. Is this by design?

I feel it's more natural to be "Left (local) is what i have locally" and "Right (remote) is what I'm merging In"

Using diff-specific configuration in .vimrc

Thanks for this ๐Ÿ˜Š

I'd like to set up some specific configuration options (eg changing the colour scheme, remapping :diffget) when resolving git conflicts with diffconflicts.

For regular vimdiff, I have if &diff. I only half know what I'm doing in vim, so I tried changing that to if &diff || exists("g:loaded_diffconflicts"), but it didn't work.

Regarding remapping, the vimdiff configuration I have (which I must have copy-pasted from somewhere) has

nnoremap <buffer> <leader>< :diffget RE<cr>]c

(etc), but when I run that while using this plugin, I get an error that there's no such buffer (if I call :diffget without a buffer specified, that works, but it seems like that would be less robust). I can't work out from the plugin code what the buffer should be called (it looks like REMOTE, but that didn't work either).

Can you advise me on how to set this up? Ideally I'd like to have one configuration block for both vimdiff and diffconflicts, if that's possible (but if it isn't, I'd still like to set up diffconflicts automatically when running git mergetool).

Add command to Open history tab automatically on startup

This adds new command DiffConflictsWithHistory that opens the 2-way diff and the history at the same time. This saves me from having to manually open the history view each time (i usually always take a quick peek before saving).

With this you can launch from git with either command "DiffConflicts" (original) or "DiffConflictsWithHistory"

Thanks again for the great plugin! This is so much better than the standard vimdiff merge.

--- a/plugin/diffconflicts.vim
+++ b/plugin/diffconflicts.vim
@@ -92,19 +92,25 @@ function! s:checkThenShowHistory()
     endif
 endfunction

-function! s:checkThenDiff()
+function! s:checkThenDiff(showHist)
     if (s:hasConflicts())
-        redraw
-        echohl WarningMsg
-            \ | echon "Resolve conflicts leftward then save. Use :cq to abort."
-            \ | echohl None
-        return s:diffconfl()
+      call s:diffconfl()
+      if (a:showHist)
+        call s:checkThenShowHistory()
+        tabfirst
+      endif
+      redraw
+      echohl WarningMsg
+         \ | echon "Resolve conflicts leftward then save. Use :cq to abort."
+         \ | echohl None
     else
         echohl WarningMsg | echo "No conflict markers found." | echohl None
     endif
 endfunction

-command! DiffConflicts call s:checkThenDiff()
+
+command! DiffConflicts call s:checkThenDiff(0)
+command! DiffConflictsWithHistory call s:checkThenDiff(1)
 command! DiffConflictsShowHistory call s:checkThenShowHistory()

 let &cpo = s:save_cpo

Get rid of initial prompt (Press Enter to continue)

Great work on this plugin. Only small issue is i don't like the extra prompt before editing the files (too slow when editing lots of conflicts) Here a simple fix that seems to work. Sorry to lazy to do a pull request.

--- plugin/diffconflicts.vim	2018-11-29 20:26:13.000000000 -0500
+++ /Users/jmandawg/.vim/plugin/diffconflicts.vim	2018-11-30 09:46:18.000000000 -0500
@@ -94,8 +94,9 @@

 function! s:checkThenDiff()
     if (s:hasConflicts())
+        redraw
         echohl WarningMsg
-            \ | echo "Resolve conflicts leftward then save. Use :cq to abort."
+            \ | echon "Resolve conflicts leftward then save. Use :cq to abort."
             \ | echohl None
         return s:diffconfl()
     else

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.