Arezgitfield notes / engineering
Git workflowsUPDATED JUL 16, 2026

How to Resolve Git Merge Conflicts Without Losing Intent

A safe process for understanding, resolving, testing, and documenting Git merge conflicts while preserving the intent of both sides of a change.

AREZGIT / FIELD NOTEGIT WORKFLOWS
A merge conflict is not a request to choose one side. It is evidence that Git cannot infer the combined intent. The correct result may be the current version, the incoming version, both, or
READ / VERIFY / APPLYTECHNICALLY REVIEWED

A merge conflict is not a request to choose one side. It is evidence that Git cannot infer the combined intent. The correct result may be the current version, the incoming version, both, or code that appears in neither.

Treat conflict resolution as a small integration task. Understand the operation, reconstruct both intentions, produce a coherent result, and verify the behavior that crosses the conflict.

Identify the operation in progress

Start with repository state:

git status
git diff --name-only --diff-filter=U

Determine whether you are resolving a merge, rebase, cherry-pick, or revert. The meaning of labels such as "ours" and "theirs" changes with the operation, especially during a rebase.

During a normal merge, ours is the currently checked-out branch and theirs is the branch being merged. During a rebase, Git replays commits onto a new base, and the labels can feel reversed from the feature author's perspective. Trust the commit identifiers and inspect content rather than relying on the words.

Use git status to get the correct continue or abort command. Do not start another history operation until the current one is complete or aborted.

Preserve a recovery point

If the worktree contains valuable uncommitted work that existed before the operation, confirm it is recorded or backed up before experimenting. Git can usually abort cleanly, but recovery becomes harder when unrelated edits and conflict resolution are mixed.

Useful checks include:

git reflog --date=iso
git rev-parse HEAD
git diff > conflict-worktree.patch

The patch is a secondary recovery aid, not a replacement for understanding repository state. Store it outside the repository if its location could be cleaned.

Avoid hard reset, checkout of every conflicted path, or blanket acceptance commands until you have identified what would be discarded.

Reconstruct intent from history

Conflict markers show nearby text, not the reason for each change. Inspect the commits that introduced both sides.

git log --oneline --decorate --all -- path/to/file
git show <commit> -- path/to/file
git blame -L 40,90 path/to/file

Read tests, issue context, and adjacent call sites. A renamed function may represent a new contract. An altered condition may fix a production bug. Combining both lines mechanically can restore an older defect or call an API that no longer exists.

For a rebase conflict, inspect the commit being replayed:

git rebase --show-current-patch

State the two intentions in plain language before editing. For example:

  • The base branch made refresh tokens single-use.
  • The feature branch added device labels to session creation.

The desired resolution must preserve both rotation and device labeling.

Resolve the smallest coherent unit

Conflict markers look like this:

<<<<<<< HEAD
current content
=======
incoming content
>>>>>>> feature

Remove every marker and produce valid code. Search the repository afterward because markers can appear in multiple files:

git grep -n -E '^(<<<<<<<|=======|>>>>>>>)'

Resolve related files together. A conflict in an interface and its implementation is one logical unit. A migration and the code that reads the new column are another.

When generated files conflict, resolve the source manifest first and regenerate with the supported tool. Hand-editing a lockfile or generated client is error-prone unless the project explicitly documents that process.

Treat delete and rename conflicts explicitly

Text conflicts are only one category. Git can report modify/delete, rename/rename, add/add, file/directory, and mode conflicts.

For a deleted file, determine whether deletion was intentional and whether the other side's behavior moved elsewhere. Restoring the file may duplicate functionality. Accepting deletion may discard a required fix.

For renames, locate the canonical destination and apply the semantic change there. Verify imports, case-only filename differences, and platform behavior. A rename that works on a case-sensitive Linux filesystem may behave differently on a default Windows filesystem.

Check executable bits and symlink targets as part of the resolution.

Stage only after inspecting the result

Staging marks a path as resolved. It does not prove correctness.

Before staging, inspect the file and its diff. After staging, inspect the combined patch:

git add path/to/file
git diff --cached --check
git diff --cached

git diff --cached --check finds whitespace errors and remaining conflict markers recognized by Git. A full marker search remains useful for formats that contain marker-like content.

Stage logical groups, then re-run git status. This makes it easier to notice an unresolved path or accidentally staged unrelated edit.

Test the integration seam

Run tests for both original intentions and the behavior where they meet. If two branches changed authentication and session display, test login, rotation, reuse handling, and device presentation. Passing each branch's old unit tests may not exercise the combination.

At minimum:

  • Run the focused tests for changed modules.
  • Run static type checks.
  • Build in production mode.
  • Exercise one successful and one failure path.
  • Inspect data or API side effects when the conflict crosses a boundary.

If the resolution changes public behavior, add a regression test that would fail for each incorrect one-sided resolution.

Continue or abort deliberately

When the index contains the intended resolution and tests pass, use the operation-specific command:

git merge --continue
git rebase --continue
git cherry-pick --continue
git revert --continue

If the integration direction is wrong, abort instead of forcing progress:

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

After continuing, inspect the resulting history and final diff against the target branch. A multi-commit rebase can produce additional conflicts or semantic changes in later replayed commits.

Document non-obvious decisions

The final commit message or review note should explain any resolution that cannot be inferred from the code. Name the competing intentions and the chosen combined behavior. This helps reviewers distinguish a deliberate integration from an accidental mix.

Conflict resolution is complete when the repository is clean, the history operation has ended, the integrated behavior is verified, and a future reader can understand why the result is correct. Removing the markers is only the editing step.