Skip to content

Add FourDiff - a new way to resolve merge conflicts

Noam Raphael requested to merge noamraph/meld:fourdiff into main

Hi!

This PR adds another diff view, called FourDiff. I have been using it for resolving merge conflicts since 2018 (see branch fourdiff-old), and it helped me a lot - before I made it, resolving merge conflicts was a chore that I never fully understood how to do, and now it's usually pretty easy, and I feel confidence when resolving conflicts.

The key idea is that when merging/rebasing/cherry-picking/reverting, you want the diff between LOCAL and MERGED to be logically the same as the diff between BASE and REMOTE. The FourDiff view displays those two diffs side-by-side, which allows to visually verify that they are logically the same diff.

This is a new feature, so I'll be happy to get any feedback on it. I hope it can be added to Meld, since I think it can help others like it helped me.

To see it in action, I created a simple merge conflict example. You can see it like this:

cd /tmp
git clone https://github.com/noamraph/conflict-example.git
cd conflict-example
git merge origin/remove-greetings
# Shows: Automatic merge failed; fix conflicts and then commit the result.
git mergetool -t meld

It shows something like this: image For me it is very confusing. In truth, there's no way to know what the result should be just from the two side panes.

To see the FourDiff view, checkout the fourdiff branch, add this to ~/.gitconfig:

[mergetool "fourdiff"]
    cmd = /PATH/TO/meld/bin/meld "$REMOTE" "$BASE" "$LOCAL" "$MERGED"

And run:

git mergetool -t fourdiff

You get this (without the red arrows, of course): image The panes are, from left to right: REMOTE, BASE, LOCAL, MERGED. Resolving the merge means making the diff from LOCAL to MERGED be the same as the diff from BASE to REMOTE.

We can see that the diff between BASE and REMOTE removed the name arguments and the print() calls. It's easy to edit the right pane to do the same: image What I really like about this view is that since the diffs appear side-by-side, it's immediately visible that the two diffs are logically the same.

Another feature that helps with understanding and resolving the merge conflict is the ability to switch the view to show the difference between BASE and LOCAL. If you press Ctrl-T (or click View->Toggle FourDiff View), you see this: image This shows the difference between BASE and LOCAL, which caused the merge conflict. We can see that the difference is adding the c argument to multiply(). This change needs to be preserved when resolving the conflict.

The implementation of FourDiff is quite simple - the FourDiff widget holds three FileDiff widgets:

  1. REMOTE - BASE
  2. BASE - LOCAL
  3. LOCAL - MERGED

There are two views: In the first view, FileDiffs 1 and 3 are visible, and in the second view, FileDiff 2 is visible.

When one of the two BASE panes is scrolled, the other is scrolled to the same position, and so with the two LOCAL panes. This causes all panes to be scrolled together, since the two panes in all three FileDiffs are already scrolled together.

Note: I made only the rightmost pane (MERGED) editable. It is required that the shared panes will not be editable, so their text will remain in sync. It was possible to be more symmetric, and treat the leftmost and rightmost panes in the same way, allowing both to be editable. However, I preferred to break the symmetry and only make the rightmost pane editable. My main reason was that I made the "next conflict" and "previous conflict" actions go the the next and previous <<<<<<< in the right pane, so it already broke the symmetry. But this is open for debate.

What do you think? Can this be added, even as an experimental feature?

Thanks! Noam

Merge request reports

Loading