Merge Conflicts
This stackoverflow question is a great starting point for understanding how merge conflict resolution works with the builtin diffing tool.
One thing I noticed right away is that by default 4 splits appear. I'm not really a fan of this kind of resolution so I would propose either a traditional 2 or 3 way split approach. We could also try to replicate VSCode's way of doing this as it is very space efficient and based on my experience it is not too confusing.
We would also need to think about how we could reshape the API to support both normal diffing and merge conflict resolution.
@sindrets have you thought about how you want to implement this before?
@TimUntersberger I don't have anything planned out yet. But like you say, the first thing we need to figure out is the layout. How many splits do we need? I agree with you that we should reduce the number of splits needed if we can. How do we want the workflow of selecting one of the conflict hunks to work? (I think I want to be able to use :diffget to do this).
In terms of implementation, I think we should create a new subclass of View that handles the mergeview layout. The file panel should work just fine for listing the conflicting files. And we probably need to refactor FileEntry to be able to accommodate both types of views.
@ur4ltz Thanks, I've actually already seen it, but it's a good reference for how it can be done!
Just as a reference, here's another really good video on how to resolve merge conflicts with (neo)vim, http://vimcasts.org/episodes/fugitive-vim-resolving-merge-conflicts-with-vimdiff/
As @sindrets mentioned, diffget is used along with Gdiff from fugitive, the thing is that fugitive uses a specific naming convention for the buffers when you use Gdiff, this makes it easier to identify buffers when doing diffget
Hello! Any updates on this enhancement? It would be great to not depend on fugitive just for this one feature, which is the only plugin I've found that provides a 3-way split etc. Thanks!
@serhez No, unfortunately not much. Nothing substantial at least, but I can dump some of my thoughts and ideas regarding what needs to be done in order to get this implemented, as well as some design and UX ideas.
I need to do some major refactoring before I can really start working on this. Currently, the internal representation of a file is heavily tied to the 2-way split layout. My plan is currently to refactor the file entries such that they are only tied to a single git commit / stage number, and a single buffer. Then I'm going to need a more capable layout module that can manage a list of predefined layouts.
I think the layout module is going to be very important, because "the best way to resolve a merge conflict" is a topic people are very opinionated about, and people have very different preferences in that regard. I believe I have settled on the 3-way merge as the main supported approach, as it gives good context during a merge. There are a number of ways one might want to arrange three windows, and I would like for this to be somewhat configurable, by letting the user choose between a number of predefined supported layouts. Here are some common layouts that should be supported:
A = Base (Common ancestor)
B = Ours (Head)
C = Theirs (Remote)
Horizontal
┌───────┬────────┬───────┐
│ │ │ │
│ │ │ │
│ │ │ │
│ B │ A │ C │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
└───────┴────────┴───────┘
Vertical
┌────────────────────────┐
│ B │
│ │
├────────────────────────┤
│ A │
│ │
├────────────────────────┤
│ C │
│ │
└────────────────────────┘
Mixed
┌───────────┬────────────┐
│ │ │
│ B │ C │
│ │ │
│ │ │
├───────────┴────────────┤
│ │
│ A │
│ │
└────────────────────────┘
In terms of design, I don't think we need a new type of view for the merge tool. I think it would be nice if the conflicted files just showed up the normal Diffview, under their own heading. Something like this:
…/some/example/path
Conflicts (2)
eiusmod/tempor
U incididunt.lua
U ut.lua
Changes (2)
lorem/ipsum
M foo.lua 7, 3
M bar.lua 3, 3
Staged changes (4)
eiusmod/tempor
U incididunt.lua
U ut.lua
M labore.lua 5, 1
M dolore.md 6, 0
And then when the user selects a conflicted file, we can just rearrange the layout to show the 3-way diff. Here the buffer in the "Base" window (A in the diagrams above) would be an editable version of the file from the common ancestor (without conflict markers). Similarly to Fugitive, we can here set buffer local mappings d2o and d3o to obtain hunks from B or C respectively. We can also remap dp in B and C, such that it always targets window A. The changes in A would only be applied when staging the conflicted file from the file panel.
That's as far as I've gotten. If anyone has thoughts / ideas / wishes regarding design, UX or implementation; please feel free to share.
@sindrets thank you for those ideas and for your work on this! ❤️
If I may add, some people may also like the single-file approach, where you can have both set of changes being compared on a single file and, using some commands (or manually deleting lines), you can choose a set of changes for each conflict, or both, or none. You can have a look at the git-conflict plugin, which is following this approach.
I am really on board with your ideas, but I also think they represent the ideal final state of the plugin and perhaps one of the four layouts we've mentioned would suffice at first, just to have the key functionality.
Hi @sindrets I love your plugin <3
I am also looking for some good implementation for three way merge in (n)vim. As you said, there are several layouts of how to do 3Way Merge (i am a fan of mixed). But as I was searching for the plugin, I have found that tere is also possibility of doing 2 way merge (see this plugin https://github.com/whiteinge/diffconflicts), and I think this approach would fit your solution beautifully in case of adding merge functionality without changing layout.
I just opened a PR with a working version of the merge-tool! I would greatly appreciate if anyone subscribed to this thread would have time to test it, and let me know if you encounter any problems.
Works very well so far, thank you so much!