Using Git Conflict Style Diff3 to Easily Resolve Merge Conflicts
There is a git setting that is off by default, but when turned on, it makes
resolving merge conflicts trivial. That setting is merge.conflictstyle =
diff3
. This setting is a merge conflict resolving superpower. Even if you had
nothing to do with the changes in the two trees you are merging, with it on, you
are able to infer the correct resolution.
To explain what it really does, I want to start by presenting a merge conflict of ECMAScript imports without this setting turned on:
<<<<<<< HEAD import { documentService, userService, operationalTransformService } from './src/services' ======= import { documentService, documentHistoryService } from './src/services' import { sanitizeDocumentTitle } from './src/view-helpers' import { formatRelative, differenceInMinutes } from 'date-fns' >>>>>>> hotfix-abc
Now suppose you are a tech lead, who had nothing to do with the implementation of either of these branches, and you need to do this merge to run the tests against a hotfix before deploying. What do you do? Go back and read each patch to figure out what changes each one did? Scan the rest of the file for references to these imports? Do you run a linter a bunch of times to figure out which imports to keep and which to remove? What if it's not a set of imports but actual business logic that changed?
The problem is that you have no context without looking at the actual patches
themselves to know what happened. Did hotfix-abc
add documentHistoryService
or did the branch you have checked out remove it?
What you really need is context of what this portion of the file looked like
before each branch made their incompatible changes. This is what
merge.conflictstyle = diff3
gives you. With each merge conflict, git will find
the closest common ancestor to that piece of code and git will insert it between
the two sides of your conflict like so:
<<<<<<< HEAD import { documentService, userService, operationalTransformService } from './src/services' ||||||| f6e4a54 import { documentService, userService } from './src/services' import { formatRelative } from 'date-fns' ======= import { documentService, documentHistoryService } from './src/services' import { sanitizeDocumentTitle } from './src/view-helpers' import { formatRelative, differenceInMinutes } from 'date-fns' >>>>>>> hotfix-abc
Instead of just the final results of the two changes you are merging, git gives you the information you need to easily figure out what happened in each branch. Even though you, the reader, had nothing to do with either change, you should still be able to deduce the following:
In HEAD:
operationalTransformService
from./src/services
was addedformatRelative
fromdate-fns
was removed
And in hotfix-abc
:
userService
from./src/services
was removeddocumentHistoryService
from./src/services
was addedsanitizeDocumentTitle
from./src/view-helpers
was addeddifferenceInMinutes
fromdate-fns
was added
Given this information, you can find the correct conflict resolution:
import { documentService, documentHistoryService, operationalTransformService } from './src/services' import { sanitizeDocumentTitle } from './src/view-helpers' import { differenceInMinutes } from 'date-fns'