Git merge conflicts are easier to resolve safely when you identify the file type before choosing an action. This checklist explains how to resolve merge conflicts in Git without silently discarding work, with separate guidance for source code, lockfiles, environment configuration, generated files, and documentation.
Overview
A merge conflict occurs when Git cannot combine changes automatically. Usually, two branches changed overlapping lines, renamed or deleted the same file, or introduced incompatible directory changes. Git pauses the merge, marks the affected files, and waits for you to decide what the final version should contain.
The conflict markers in a text file show the competing versions:
<<<<<<< HEAD
version from the current branch
=======
version from the branch being merged
>>>>>>> feature-branchDo not treat every conflict as a request to choose “ours” or “theirs.” The correct result may combine both changes, preserve one side, or require a new implementation that neither branch contains. The safest workflow is to inspect the context, understand the intent of each change, edit the file deliberately, validate the result, and only then stage it.
Start by recording the state of the operation:
git status
git diff
git diff --name-only --diff-filter=UThe last command lists files that remain unmerged. Keep this list visible while working. If the merge involves several unrelated files, resolve them one category at a time rather than making broad automated replacements.
For larger tasks, a separate worktree can keep the conflicted branch isolated from other work. See the Git worktrees guide for a workflow that avoids repeated checkouts and unnecessary stashing.
Checklist by scenario
| File type | Safe first step | What to verify |
|---|---|---|
| Source code | Read both versions in surrounding context and combine the intended behavior | Builds, tests, error handling, interfaces, and changed control flow |
| Package lockfile | Use the package manager to regenerate it from the intended manifest | Dependency graph, lockfile format, package-manager version, and reproducibility |
| Environment configuration | Separate shared defaults from machine-specific or secret values | Required variables, precedence, secrets, and deployment behavior |
| Generated files | Resolve the source inputs, then regenerate the output when possible | Deterministic output and absence of unintended manual edits |
| Documentation | Combine accurate instructions and remove stale alternatives | Commands, paths, version assumptions, and links |
Source code
For application code, first read the complete function, class, or configuration block around the markers. A line-by-line choice can compile while changing behavior. Ask three questions: What problem did each branch solve? Are both changes required? Did one branch change an interface that the other branch still calls?
After editing, search for remaining markers:
git grep -n -E '^(<<<<<<<|=======|>>>>>>>)'Then run the narrowest useful checks, followed by the normal project test or build command. If both branches changed tests, review the assertions rather than accepting whichever test version happens to run.
Package lockfiles
Lockfiles deserve different treatment from ordinary source files. They are generated descriptions of a dependency graph, so manually merging large sections can produce a file that looks plausible but does not match the manifest or package manager's resolution rules.
Inspect the related manifest first, such as package.json, pyproject.toml, requirements files, or another project-specific dependency definition. Decide which dependency changes should remain. Then use the project's documented package-manager command to regenerate the lockfile. Do not blindly accept “ours” or “theirs” if both branches intentionally upgraded different dependencies.
Review the resulting lockfile diff. Confirm that unrelated packages did not change unexpectedly, the expected versions or ranges are represented, and the lockfile is compatible with the package-manager version used by the project. If the repository pins a package-manager version, use that version during resolution.
Environment configuration
Configuration conflicts often contain values that should not be committed at all. Separate shared, non-sensitive defaults from local overrides and secrets. A committed example file can document required variables without exposing credentials. For deployment-specific files, confirm which environment actually consumes the file and whether another source has higher precedence.
Never resolve a conflict by pasting a secret into a tracked file merely because it appears on one side of the merge. Check the project's secret-management process and validate configuration with a safe, non-secret test. The Docker Compose environment variables and secrets checklist provides a useful model for checking precedence and profiles.
Generated files and documentation
For generated assets, resolve the source templates, schemas, migrations, or code-generation inputs first. Regenerate the output using the repository's normal command. A manually edited generated file may be overwritten later or conceal a missing source change.
Documentation conflicts still require technical review. Merge the instructions that remain accurate, remove obsolete alternatives, and test commands or paths that readers are expected to copy. Pay particular attention to version-specific flags and setup steps. Documentation is part of the interface of a project, not merely a text file.
What to double-check
- Confirm the operation.
git statusshows whether you are merging, rebasing, cherry-picking, or completing another operation. The commands to continue or abort depend on that state. - Review both sides before staging. Use
git difffor unstaged edits andgit diff --cachedafter staging. For a merge, compare the result against each parent when the conflict is subtle. - Inspect renames and deletions. A conflict may be reported because one branch deleted or renamed a file while the other edited it. Decide whether the file should remain, move, or disappear.
- Use a mergetool when it improves visibility. Run
git mergetoolonly after understanding the tool's layout and output. Treat its result as an edit to review, not an automatic answer. - Stage only resolved files.
git add path/to/filetells Git that you have resolved that path. It does not prove that the content is correct. Check the staged diff immediately afterward. - Run validation appropriate to the change. Use formatting, linting, unit tests, integration tests, type checks, builds, or a focused manual test. A successful commit does not establish behavioral correctness.
- Inspect the final commit. Before committing, run
git diff --cached --checkand review the complete staged diff. Confirm that no debug output, temporary files, secrets, or unrelated edits slipped in.
A compact resolution sequence is:
git status
git diff
# edit each conflicted file
git grep -n -E '^(<<<<<<<|=======|>>>>>>>)'
git add path/to/resolved-file
git diff --cached
git diff --cached --check
# run project tests or build
git commitIf you are resolving a rebase, use git rebase --continue after staging the intended files. For a merge, use git commit or the project's normal merge-completion command. Read the status output rather than assuming which operation is active.
Common mistakes
Accepting “theirs” everywhere. This can remove a security fix, test update, or bug fix from the current branch. Bulk acceptance is appropriate only when you have verified that the entire file should come from one side.
Editing lockfiles by hand without regenerating them. This may leave inconsistent metadata or omit transitive dependencies. Resolve the manifest intent and let the package manager produce the lockfile where practical.
Staging without reviewing. Git considers a staged file resolved even if its logic is wrong. Always inspect the staged diff and run relevant checks.
Testing only the conflict file. A conflict often involves an interface shared by other files. Run tests that exercise callers, configuration loading, build steps, and deployment packaging when those areas changed.
Continuing from the wrong branch or operation. Check git status before using git merge --continue, git rebase --continue, or a cherry-pick command. If the result is clearly wrong and you have not created useful new work, stop and reassess.
To abandon an in-progress merge, use:
git merge --abortFor an in-progress rebase, use:
git rebase --abortThese commands are intended to return the worktree to its pre-operation state, but inspect your status and protect any unrelated uncommitted work first. If a bad resolution has already been committed, do not immediately reset a shared branch. Save the commit reference, discuss the branch history with collaborators, and choose between a corrective commit and a carefully planned reset. A reflog can help locate earlier local states:
git reflogWhen uncertain, create a safety branch or tag before attempting recovery. Recovery is easier when you preserve the current state instead of repeatedly rewriting it.
When to revisit
Keep this checklist available before dependency upgrades, release backports, long-lived feature-branch merges, and onboarding exercises. Revisit the lockfile section when the project changes package managers, changes its package-manager version, or adopts a new lockfile format. Revisit generated-file guidance when code-generation commands or build pipelines change.
Teams should also review their merge and conflict-resolution conventions when they change editor integrations, merge drivers, formatting tools, or continuous-integration checks. A custom merge driver may be appropriate for a deliberately generated or structured file, but it should be documented and tested before becoming the default. Never add an automated driver merely to make conflicts disappear; verify that it preserves the intended data.
Before completing your next difficult merge, copy this short action list:
- Run
git statusand identify the active operation. - List every unmerged path and classify each file by type.
- Resolve source code by intent, lockfiles through the package manager, and generated files from their sources.
- Search for leftover conflict markers.
- Inspect both unstaged and staged diffs.
- Run focused checks and the broader test or build workflow.
- Commit only after the result is understandable and reproducible.
That process turns a merge conflict from a rushed text-editing task into a controlled review of competing changes.