Skip to main content

A repackaged version of Meld, including the Fourdiff feature.

Project description

Fourdiff: A new way to resolve merge conflicts with ease and confidence

A few years ago I was trying to resolve a particularly horrible merge conflict, and as a way to escape the horror, I started thinking: could there be a better way? A way that would let me really understand what I'm doing, instead of trying to guess my way out of the mess? Thanks God, I found such a way, and I've been using it ever since. I think it can help others as well. Would you like to give it a try?

To try it on Ubuntu (including WSL), install uv, some packages required to build pygobject, and meld-fourdiff:

curl -LsSf https://astral.sh/uv/install.sh | sh
sudo apt update && sudo apt install -y --no-install-recommends libgirepository1.0-dev gcc libcairo2-dev pkg-config gir1.2-gtk-3.0 gir1.2-gtksource-4
uv tool install --python 3.10 meld-fourdiff

Make sure that meld-fourdiff works properly, by running meld-fourdiff and making sure you see a window, and add this section to your ~/.gitconfig:

[mergetool "fourdiff"]
    cmd = meld-fourdiff "$BASE" "$REMOTE" "$LOCAL" "$MERGED"

I want to share with you a hack I made about two years ago, which I'm using to resolve merge conflicts (and cherry-pick conflicts, and revert conflicts) in my job. Before I discovered this idea, I have hated merges, I never understood what was going on. Now I resolve conflicts with confidence. It's actually really hard for me to use any other tool.

It looks like this. After running git merge and getting merge conflicts, I run git mergetool and see this (except for the arrows): In every merge, the goal is to take the difference between two revisions (the "base revision" and the "remote revision") and apply it to the current revision (the "local revision"). From left to right we see: 1. the remote revision 2. the base revision 3. the local revision (without any changes applied) and 4. the merged revision, which is what we have in our working directory.

The first lines show a successful merge. We can see that the change between BASE and REMOTE is the same change between LOCAL and merged. Namely, the change is to remove the name argument and remove the print() line. I can visually verify that the changes along the left arrow look the same as the changes along the right arrow.

Then, we have a merge conflict. Git doesn't know how to apply the change, and leaves the job to us. The right pane shows the git merge conflict format. To find the conflicts, click the right pane, press Ctrl-F, and search for <<<.

To better understand what's going on, I can press Ctrl-T, and the view switches to show the difference between BASE and LOCAL. This difference is the cause of the merge conflict:

We see that the change between BASE and LOCAL is that we added another argument to the function, so it now adds 3 numbers instead of 2 (Please excuse my stupid example). Now that I understand what causes the merge conflict, I can apply the change with confidence, by pressing again Ctrl-T to switch back to the original view, and editing the file on the right. I get this:

Again, I can visually verify that the change applied from LOCAL to MERGED looks the same as the change from BASE to REMOTE.

Perhaps it would help to show how the conflict looks with existing tools. If I run git mergetool -t meld, I get this:

On one side we have the arguments name, a, b, c and a print. On the other side we have only a, b and no print. What should I take? The truth is that It's actually impossible to resolve the conflict using only LOCAL and REMOTE[^1].

Installation

This is known to work on Ubuntu 20.04. You just need to clone my fork of meld, the excellent diff viewer, and configure git to use it to resolve merge conflicts.

Run this:

cd ~/
git clone -b fourdiff https://github.com/noamraph/meld.git
sudo apt install meld  # to install dependencies

And add this to ~/.gitconfig:

[mergetool "fourdiff"]
    cmd = ~/meld/bin/meld "$REMOTE" "$BASE" "$LOCAL" "$MERGED"
[merge]
    tool = fourdiff

If you want to test it with the example I showed above, run this:

cd /tmp
git clone https://github.com/noamraph/conflict-example.git
cd conflict-example/
git merge origin/remove-greetings

You should get a merge conflict. Run this and the fourdiff should appear:

git mergetool

Final thoughts

I find this to be a simple and very effective concept. I hope the meld developers would agree to add this feature, and I hope others will add it as well.

My initial thought was to arrange the panes in this order: BASE, REMOTE, LOCAL, MERGED. This would have made both the original change and the new change apply from left to right. However, this would have made the second view, showing the difference between BASE and LOCAL, confusing.

If you're a developer of a diff viewer and you want to add this to your app, the actual implementation is quite simple: there are always three 2-way diff widgets behind the scenes: BASE-REMOTE, REMOTE-LOCAL, LOCAL-MERGED. When one REMOTE is scrolled the other REMOTE is scrolled, and the same goes with LOCAL and LOCAL. This keeps all the panes in sync. The user can switch between showing BASE-REMOTE and LOCAL-MERGED, and showing only REMOTE-LOCAL.

[^1]: I could in theory compare the middle column to both sides, understand how it changed relative to both sides, and figure out how to apply both changes. But the visual diff doesn't help this at all, and after editing the middle there's no way to check this, and all this wouldn't work at all if you do a revert or a cherry-pick.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

meld_fourdiff-0.0.5.tar.gz (1.6 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

meld_fourdiff-0.0.5-py3-none-any.whl (1.1 MB view details)

Uploaded Python 3

File details

Details for the file meld_fourdiff-0.0.5.tar.gz.

File metadata

  • Download URL: meld_fourdiff-0.0.5.tar.gz
  • Upload date:
  • Size: 1.6 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for meld_fourdiff-0.0.5.tar.gz
Algorithm Hash digest
SHA256 f49dc00ef2711dcd8acb64f175a32635678ccc424536ad30923de74e82e50f36
MD5 8fab44a5b13f352a99cd7a607f0c5ab9
BLAKE2b-256 56df8fc013e4b166d3f86a41d87189e72b2b23bddde2a669ac0fe4da10a25390

See more details on using hashes here.

File details

Details for the file meld_fourdiff-0.0.5-py3-none-any.whl.

File metadata

  • Download URL: meld_fourdiff-0.0.5-py3-none-any.whl
  • Upload date:
  • Size: 1.1 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for meld_fourdiff-0.0.5-py3-none-any.whl
Algorithm Hash digest
SHA256 00fbbba30dcfebd26fe530fb1dccd8b9b423a4e88cff3aa1d9c956f9227862c7
MD5 1c1e80c8730f10784b94ce3f0ba4b649
BLAKE2b-256 5298122a34a6890fa362373eebb20feaa888227f2d15b534ca78864de2c55955

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page