Better Git Conflicts with zdiff3

Better Git Conflicts with zdiff3

diff3 is great for resolving conflicts in git. I love it and I've used it ever since I laid eyes on the linked article years ago. However, diff3 has a frustrating user experience flaw in it that the merge conflict version does not have. Let's take a look at this list as our initial commit:

A
B
C
# Add More Letters

Let's say in the next commit on the main branch we change it to look like this:

A
B
C
D
E
F
G

In an alternate branch based off the first commit we change it to look like this:

A
B
C
D
E
X
Y
Z

In both cases they add D and E and get rid of the comment, but then they each add different letters (F, G and X,Y,Z respectively). If we do a merge between the second and third list we get a conflict. Here is what the git conflict looks like using the merge conflict style:

A
B
C
D
E
<<<<<<< ours
F
G
=======
X
Y
Z
>>>>>>> theirs

The algorithm correctly identifies the differences between the lists. However, we don't have the key benefit of using diff3 and that's seeing what came before this change. If we look at the same conflict with diff3 we get this:

A
B
C
<<<<<<< ours
D
E
F
G
||||||| base
# Add More Letters
=======
D
E
X
Y
Z
>>>>>>> theirs

diff3 has shown us that in the base commit there was a deleted comment, giving us some contextual clues, but in this case the conflict is bigger. We have D and E in both conflict blocks. This is the user experience flaw mentioned earlier. We would probably just want D and E to stay and would not consider them conflicting. However, we now need to move D and E out of one conflict area manually and delete the other lines. This problem gets more confusing for larger conflicts in far more complex code where there might be multiple shared lines at the start or end of the conflict markers. What we want is a best of both worlds approach where we can shrink the conflict area between branches as much as possible automatically while still having the context of the common ancestral commit between the branches.

That's where zdiff3, a new merge conflict algorithm added in git 2.35 in January 2022, comes in. Let's take a look at the conflict one last time using that algorithm:

A
B
C
D
E
<<<<<<< ours
F
G
||||||| base
# Add More Letters
=======
X
Y
Z
>>>>>>> theirs

Now we're getting the real benefits of the merge and diff3 conflict styles! D and E have rightfully been moved outside of the conflict zone automatically, the conflict markers cover a much smaller area, and we have the base section to see what preceded the conflict. zdiff3 is just the diff3 we know and love but it zealously moves any common lines at the beginning or end outside of the conflict area.

Conclusion

Like many things in git, zdiff3 is one of those hidden features that I wish was set as the default option. It has made my day to day development much easier when it comes to resolving conflicts and it's a nice little improvement over diff3. If you want to enable zdiff3 by default on versions of git >= 2.35, you can run git config --global merge.conflictStyle zdiff3. If you just want to give it a test run next time without setting that option to see if you like it you can also run git checkout --conflict zdiff3 ./conflicted/file/path to checkout just the one conflicted file again with the zdiff3 algorithm. I might be a bit zealous about it, but I think it's worth switching over to it.