How to Merge Branches in Git Without Auto-Resolving Conflicts

3 min read

By default, Git tries to automatically combine changes when you merge branches. Most of the time that works fine. But when you need to review every change before it lands — like merging a long-lived branch or pulling in changes from multiple contributors — you want to merge without auto-resolving conflicts so you stay in control.

The examples in this guide are run on WSL2 Ubuntu in Windows, but they work the same on any system with Git installed.

When Would You Use This?

  • You’re merging a feature branch that has weeks of changes and you want to inspect everything before committing
  • You need to cherry-pick which changes to keep and which to modify
  • You’re integrating main into a release branch and want to test before finalizing the merge
  • Your team requires merge reviews that go beyond a pull request diff

The Two Flags You Need

The key to a controlled merge is combining two Git flags: --no-commit and --no-ff.

Flag What It Does
--no-commit Performs the merge but stops before creating the commit, letting you review and modify the result
--no-ff Forces a merge commit even when Git could fast-forward, preserving branch history

Without --no-ff, Git may fast-forward your branch pointer instead of creating a merge commit — which means the merge won’t show up as a distinct event in your history.

Step-by-Step: Controlled Git Merge

1. Switch to the target branch

Check out the branch you want to merge into. If you’re pulling main into your feature branch:

git checkout feature-branch

Or using the newer switch command:

git switch feature-branch

2. Start the merge without committing

git merge --no-commit --no-ff main

Git will apply the changes from main into your working tree but won’t create a commit. Your terminal will show something like:

Automatic merge went well; stopped before committing as requested

If there are conflicts, Git will tell you which files need manual resolution.

3. Review the merged changes

Now you can inspect exactly what the merge will include before committing anything.

git diff --cached

This shows the staged changes — everything that will go into the merge commit. You can also check individual files:

git diff --cached -- path/to/file.js

4. Resolve conflicts (if any)

If Git reports conflicts, open the affected files and look for conflict markers:

<<<<<<< HEAD
your current branch changes
=======
incoming changes from main
>>>>>>> main

Edit the file to keep the version you want, then remove the conflict markers. To see which files still have conflicts:

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

5. Stage and commit

Once you’re satisfied with the result, stage your changes and commit:

git add .
git commit -m "Merge main into feature-branch"

If you want to be more selective about what gets staged, add files individually instead of using git add ..

6. Abort if something goes wrong

Changed your mind? You can cancel the entire merge and go back to where you started:

git merge --abort

This only works before you commit. Once committed, you’d need to revert.

Quick Reference

Command Purpose
git merge --no-commit --no-ff main Merge without auto-committing
git diff --cached Review staged merge changes
git diff --name-only --diff-filter=U List files with unresolved conflicts
git merge --abort Cancel the merge entirely
git add . && git commit Finalize the merge

Conclusion

Using --no-commit --no-ff gives you full control over what goes into a merge commit. It’s a small change to your workflow that prevents surprises when integrating branches with significant changes.

If you’re working with uncommitted changes and need to switch branches first, check out How to Switch to a Different Branch in Git Including the Uncommitted Changes. And if your merge results in too many commits, you can clean them up with GitLens Interactive Rebase in VSCode.