DevOps & Workflow5 min read

    How to resolve merge conflicts in git

    Share

    A merge conflict in git happens when two branches changed the same lines of the same file in ways git can't reconcile automatically. Git stops the merge, marks the conflicting hunks in your working tree, and waits for you to resolve them.

    The same machinery surfaces during git merge, git rebase, git cherry-pick, git stash pop, and git revert. The recovery flow is mostly the same.


    What a conflict looks like

    A conflicting file has hunks like this:

    <<<<<<< HEAD
    const timeout = 30;
    =======
    const timeout = 60;
    >>>>>>> feature/longer-timeout
    
    • Everything between <<<<<<< and ======= is your side (HEAD, often called ours).
    • Everything between ======= and >>>>>>> is the incoming side (the branch you're merging in, often called theirs).
    • The label after <<<<<<< and >>>>>>> tells you which ref is which.

    A resolved hunk has neither markers nor leftover code — just the version you want to keep:

    const timeout = 60;
    

    The conflict workflow

    git merge feature/x
    # CONFLICT (content): Merge conflict in src/config.ts
    # Automatic merge failed; fix conflicts and then commit the result.
    
    1. See what's conflicted.

      git status
      

      Files under "Unmerged paths" are the ones you need to fix.

    2. Open each file and resolve the markers. Pick ours, theirs, both, or a hybrid. Delete every <<<<<<<, =======, and >>>>>>> line.

    3. Stage the resolved files.

      git add src/config.ts
      
    4. Finish the merge.

      git commit         # for merge
      git rebase --continue
      git cherry-pick --continue
      

      Git pre-fills the commit message for merge commits — usually accept it.

    5. Or bail out.

      git merge --abort
      git rebase --abort
      git cherry-pick --abort
      

      This restores your branch to the state before the operation.


    Useful inspection commands

    git status                       # which files are conflicted
    git diff                         # unresolved conflicts in the working tree
    git diff --name-only --diff-filter=U   # just the names of conflicted files
    git log --merge --oneline        # commits that touched the conflicted regions on either side
    

    The git log --merge output is gold for deciding why the conflict is there. It shows the commits from both sides that affected the conflicting lines.


    Choosing one side wholesale

    Sometimes you know one side is the right answer for a whole file or hunk:

    git checkout --ours path/to/file     # keep your version
    git checkout --theirs path/to/file   # take the incoming version
    git add path/to/file
    

    During a rebase, the meaning of ours and theirs is reversed compared to a merge — ours refers to the commit being replayed, theirs refers to the branch you're rebasing onto. Read the message git prints; don't guess.


    Use a three-way merge tool

    For non-trivial conflicts, looking at three versions side-by-side (your side, their side, and the original common ancestor) makes the right resolution obvious. Configure once:

    git config --global merge.tool meld
    # or vimdiff, kdiff3, opendiff, code (VS Code)
    

    Then on a conflict:

    git mergetool
    

    Most editors and IDEs (VS Code, JetBrains, etc.) ship a built-in three-way merge UI that handles this without mergetool.


    Show the common ancestor in conflict markers

    By default git shows two sides. You can ask for three (the merge base too):

    git config --global merge.conflictStyle diff3
    

    Conflicts now look like:

    <<<<<<< HEAD
    const timeout = 30;
    ||||||| merged common ancestors
    const timeout = 15;
    =======
    const timeout = 60;
    >>>>>>> feature/longer-timeout
    

    Knowing the common ancestor (15) tells you that both sides changed the value, which is usually a stronger signal than just seeing the two endpoints. The newer zdiff3 style is even cleaner if your git is recent enough.


    Conflicts during rebase

    Rebases can produce one conflict per replayed commit. The rhythm is:

    git rebase main
    # conflict
    # resolve, then:
    git add path/to/file
    git rebase --continue
    # next conflict, repeat...
    

    Useful escape hatches:

    git rebase --skip       # drop the current commit and move on
    git rebase --abort      # give up and return to the original branch state
    

    Tip: turn on git rerere (git config --global rerere.enabled true). Git remembers conflict resolutions and replays them automatically when the same conflict shows up again — invaluable on long rebases.


    Conflicts during cherry-pick, stash, revert

    Same flow:

    Operation Continue Abort
    git cherry-pick --continue --abort
    git revert --continue --abort
    git stash pop resolve, git add, the stash stays on the stack until you drop it n/a — just git reset --hard if you want to bail

    Practical recipes

    "I'm halfway through a merge — what was I doing?"

    git status                       # shows merge in progress and conflicted files
    cat .git/MERGE_MSG               # the prepared merge commit message
    git diff --name-only --diff-filter=U
    

    "Take all of my changes, throw away theirs"

    git merge -X ours feature/x
    

    -X ours is a strategy option: in conflicts, prefer our side. Note this is different from git checkout --ours, which works on already-conflicted files.

    "Delete vs modify" conflict

    If one side deleted a file and the other modified it, git asks you to choose:

    git rm path/to/file       # accept the deletion
    git add path/to/file      # accept your modification
    

    Binary file conflicts

    Git can't merge binaries. Pick a side:

    git checkout --ours path/to/image.png
    git add path/to/image.png
    

    Avoiding painful conflicts

    • Pull / rebase often. A 1-commit-behind branch is cheap to resolve. A 200-commit-behind branch isn't.
    • Keep branches small. A focused feature branch conflicts in fewer places.
    • Use git rerere. It pays for itself the first time you repeat a rebase.
    • Format / lint changes separately. A "ran prettier on the whole repo" commit is the worst possible thing to merge across — every other branch will conflict with it.

    Summary

    • A conflict is git asking you to pick a winner between two changes to the same lines.
    • Open the file, resolve the markers, git add, then --continue or commit.
    • git checkout --ours / --theirs chooses one side wholesale.
    • Turn on merge.conflictStyle = diff3 and rerere.enabled = true — both make conflicts dramatically easier.
    • When in doubt, --abort and try a smaller, less divergent merge.