2 min read

You should probably use 3-way conflict style for Git

3-way diff styles might just improve your Git workflow when working with large sets of patches
You should probably use 3-way conflict style for Git

Preface

I'd like to preface that this takes references from the excellent tutorial from Ted Felix called Git Conflict Resolution; I highly recommend reading through the tutorial if you want to go even deeper.

This article is more of a condensed version that helped me address problems I encountered when working with a huge codebase.

Since Git is constantly evolving and improving, the commands in this article require at least version 2.17.1

$ git --version
git version 2.17.1

The problem

Cherry-picking a set of 10-plus commits, unfortunately for me, the files that those commits were touching also were being worked on by me, so downloading a patch and doing git apply <patch_file> would error out due to the limited conflict resolution abilities of this command. Also, for some reason, my muscle memory likes to default to this command, even though this is only recommended when working with files that do not belong to the same Git repository you're working with; in other words, I had skill issues. From the manual, we have one particular line that just doesn't seem to stick to my brain:

This command applies the patch but does not create a commit. Use git-am(1) to create commits from patches generated by git-format-patch(1) and/or received by email.

The solution

git-am creates commits, allowing me to resolve conflicts when applying a patch file. And since I wanted to experience the 3-way merge before committing to use the diff3 conflict style in my git config file, I can run git am <patch_file> --3way and start resolving conflicts should they appear; also, git am will generate non-merge commits if the patch uses the mailbox UNIX form from git-format-patch; from the docs, we have

Splits mail messages in a mailbox into commit log messages, authorship information, and patches and applies them to the current branch. You could think of it as a reverse operation of git-format-patch(1) run on a branch with a straight history without merges.

Platforms like GitLab and GitHub provide these patches from their Merge Requests/Pull Requests.

Additional notes

What does a diff3 look like? Again, thanks to Ted Felix for the tutorial and the examples of 3-way diffs

diff --cc hello.txt
index 5eb9649,379bd44..0000000
--- a/hello.txt
+++ b/hello.txt
@@@ -1,1 -1,1 +1,7 @@@
++<<<<<<< HEAD
 +Hello, main change.
++||||||| merged common ancestors
++Hello, Original.
++=======
+ Hello, branch b1 change.
++>>>>>>> b1

You have a common ancestor followed by the main branch and the branch that wants to apply the patches. The process is similar to the 2-way diff but with additional context for easier "grokking" with visual tools.

git mergetool can help visualize these diffs in more detail. Tools like P4Merge are among my favorites; Ted recommends kdiff3, but I haven't tried it yet.

git-am is similar to git rebase, meaning that to resolve conflicts, we have to issue --continue to keep applying the additional commits that come from the patch file after a resolution has been completed, unlike git-merge where to continue we have issue git commit to finish the conflict resolution process.

Similar commands in behavior to git-am

  • git rebase
  • git cherry-pick
  • git stash pop