🛠️ Understanding zealous diff3 style in Git conflicts

6 min read

gitvcsclidiff... and 2 more

When collaborating on code with others or merging branches in Git, conflicts are inevitable. A common tool to resolve these conflicts is the conflict markers built-in mechanisms. Although merge style of conflict markers became well-known and industry standard there is still place to improve the experience of using the version control system. This article aims to explain what zdiff3 is and how it can be applied to conflict markers and highlight the algorithm significance.

Branching, merging and why conflicts even occur

In Git, development most of the times happens in branches. A branch in Git is essentially a movable pointer to one of the commits. The default branch in Git is usually called main, trunk or master, but developers can create new branches for features, bug fixes, or experiments. These branches allow developers to work on changes independently from the main line of development.

When the work on a branch is complete, it is merged back e.g. into the main branch. (it can albo be merge of feature branch into other feature branch, it depends on the approach and specific scenario). Git attempts to automatically merge the changes by combining the snapshots pointed to by the branch pointers. If the changes in the two branches do not overlap, Git can usually merge them automatically. In other cases Git will raise a conflict.

Common scenarios for merge conflicts

  • Editing the same line: Two branches have made different changes to the same line of a file.
  • One branch edits a file while another deletes it: One branch makes changes to a file, while another branch deletes that same file.
  • Concurrent file renaming: Two branches rename the same file differently or make changes to a file that one of the branches has renamed.

What is Git's zealous diff3 merge conflicts style?

The zdiff3 conflict marker is an option for a merge conflict markers in Git that provides way more context than default merge style and is enhanced version of diff3. It shows the common ancestor's version in addition to the two conflicting changes, offering a three-way comparison - just like diff3.

However unlike the diff3, zealous one tries to resolve as many conflicts as possible by itself. It is a bit more aggressive in its approach to the whole process which results in less manual work for the developer.

The zdiff3 conflict marker is activated by setting the merge conflict style to diff3 in Git configuration via the CLI:

git config --global merge.conflictstyle zdiff3

... or by setting it directly in the .gitconfig file:

...
[merge]
  conflictstyle = zdiff3
...

Structure of zdiff3 and diff3 conflict markers

A zdiff3 and diff3 conflict marker styles divide the conflicting area into three sections:

  1. Local Changes (HEAD): Changes that are present in the current branch.
  2. Base Changes (Ancestor): The common ancestor's version of this part of the file.
  3. Incoming Changes (MERGE_HEAD): Changes that are present in the branch being merged into the current one.

The sections are separated by different markers:

  • <<<<<<< HEAD marks the beginning of the local changes.
  • ||||||| merged common ancestors marks the beginning of the base changes.
  • ======= separates the local changes from the incoming changes.
  • >>>>>>> MERGE_HEAD marks the end of the incoming changes.

Understanding the difference between conflict styles

Let's start from the default merge style of conflict markers. It is the most common and widely used style. It is simple and easy to understand. It shows only the local changes and the incoming changes. It is a two-way comparison.


Reference drawing explaining how does the merge conflicts style look like

Frankly speaking - it is not enough to resolve the conflict in a complex scenario. It is not enough to understand the context of the changes and the evolution of the code. This is where diff3 and zdiff3 come into play.

Reference example of a diff3 style conflict


Reference drawing explaining how does the diff3 conflicts style look like

Unlike previous style and apart from the common ancestor section, the diff3 does not try to aggresively resolve the conflicts by itself, which is easy spottable on the embedded drawing. The developer gets full set of information it is up to him to decide which changes to keep and which to discard.

Reference example of a zdiff3 style conflict


Reference drawing explaining how does the zealous diff (aka zdiff3) conflicts style look like

And here comes the zdiff3 style. As the name says - it is zealous version of a diff3. The behavior can be summed up as an merge + diff3 - there are both aggressive resolving and common ancestor features present.

Personally I find it the most useful, straightforward and productive style of conflict markers with being the most informative but not overwhelming information-wise, since changes that are the same between local and incoming are moved out of conflict markers leaving clean ground for analyzing it.

Explore the differences in practice

If you want to play with the examples from the drawings, create a new directory and use the following commands to create a conflict and see how it looks in different styles:

git init \
  && git branch -m main \
  && echo -e "Foo\nHello" > file.txt \
  && git add file.txt \
  && git commit -m "a" \
  && git branch new-branch \
  && git switch new-branch \
  && echo -e "Bar\nFarwell" > file.txt \
  && git add file.txt \
  && git commit -m "b" \
  && git switch main \
  && echo -e "Bar\nHello" > file.txt \
  && git add file.txt \
  && git commit -m "c" \
  && git merge new-branch

NOTE: Assuming, you've started exploring from zdiff3 you can create another directory and change the merge.conflictstyle to merge and diff3 in the meantime and compare the outcome

Conclusion

The zdiff3 conflict marker is a powerful tool for resolving merge conflicts in Git.

  • By providing a three-way comparison (outgoing, common ancestor, incoming), it offers developers additional context, helping them understand the evolution of changes and make informed decisions.

  • By being more aggressive in its approach to resolving conflicts, it can save developers time and effort by automatically resolving as many conflicts as possible. The more time you spend on the repetative manual work the more error-prone it gets. zdiff3 is a marvellous way to avoid it and minimize the hassle and frustration.

As collaboration and branch management are integral parts of modern software development, mastering tools like zdiff3 is essential for maintaining a smooth workflow.

Did you find this article helpful?

Share it with the world!