Resolving whitespace conflicts with strategy options Jump to heading

Introducing a formatter is a good decision that produces one bad week. The sweep commit touches every file, every open branch now conflicts with it on every line it touched, and the conflicts are not real — both sides agree about the code and disagree about the whitespace. Git has options for exactly this, and there is a better technique still: replay the branch and re-run the formatter, so the conflict never arises. This recipe covers both, within merge strategies and gitattributes.

When to use this approach Jump to heading

  • A formatting sweep has landed on the default branch and your branch predates it.
  • Conflicts appear in files where neither side changed the logic.
  • An indentation or line-ending change is producing whole-file conflicts.
  • You are planning a sweep and want to know what it will cost others.
  • If the conflict involves actual content changes on both sides, these options will hide a real decision — resolve it properly instead.

Step 1 — Confirm the conflict is genuinely whitespace Jump to heading

Do not reach for a whitespace option until you have proved that is what it is.

# Does the conflict disappear when whitespace is ignored?
git diff --ignore-all-space HEAD other-branch -- src/payments/refund.ts | head -20
# Across the whole branch: how many files differ only by whitespace?
git diff --name-only HEAD other-branch > /tmp/all.txt
git diff --ignore-all-space --name-only HEAD other-branch > /tmp/real.txt
comm -23 /tmp/all.txt /tmp/real.txt | wc -l
# Verification: the files in the difference are the whitespace-only ones
comm -23 /tmp/all.txt /tmp/real.txt | head
Is this conflict real?If ignoring whitespace makes the difference vanish, the conflict is an artefact of formatting and can be resolved mechanically. If content remains, the two sides genuinely disagree and a whitespace option would hide the decision rather than make it.Does the difference vanish under --ignore-all-space?yes, nothing leftFormatting artefactsafe to resolve mechanicallyno, content remainsGenuine conflictresolve it properlypartlySplit the filehandle each hunk on meritreaching for a whitespace option before this check is how a real change gets discarded

Step 2 — Merge with whitespace ignored Jump to heading

git merge -X ignore-all-space origin/main
# Narrower variants, if the sweep changed only one kind of whitespace
git merge -X ignore-space-change origin/main     # runs of whitespace treated as equal
git merge -X ignore-space-at-eol origin/main     # trailing whitespace only
# Verification: the merged file matches the formatter's output
npx prettier --check src/ && echo "merged result is correctly formatted"

The narrower options are worth preferring when they suffice: ignore-all-space will also ignore an indentation change that was deliberate, and in a language where indentation is syntax that is a genuine risk.

Step 3 — Prefer replaying and reformatting Jump to heading

The merge option resolves the symptom. Rebasing across the sweep and re-running the formatter removes the cause, and leaves a history where every commit is correctly formatted.

# Replay the branch onto the formatted default branch, reformatting each commit
git rebase --exec 'npx prettier --write . && git add -A && git commit --amend --no-edit' origin/main
# Or, for a branch with many commits, squash first and reformat once
git rebase -i --autosquash origin/main
npx prettier --write . && git add -A && git commit --amend --no-edit
# Verification: every commit on the branch is formatted
git rebase --exec 'npx prettier --check .' origin/main && echo "all commits clean"
Merging with an option against replaying the branchThe merge option resolves this merge and leaves the branch's own commits unformatted, so the next merge conflicts again. Rebasing with the formatter applied produces commits that are already correct, and the problem does not recur.-X ignore-all-spaceRebase and reformatthis mergeresolvedresolvednext mergeconflicts againcleanbranch commitsunformattedformattedeffortone flagone rebasethe option is right for a one-off; the rebase is right for a branch you will merge more than once

Step 4 — Plan a sweep so it costs others less Jump to heading

If you are the one introducing the formatter, three things reduce the damage.

# 1. Do it when open branches are minimal, and say so in advance
git branch -r --no-merged origin/main | wc -l
# 2. Make the sweep a single commit containing nothing else
npx prettier --write . && git add -A
git commit -m 'style: apply the formatter across the repository

No behavioural change. Use --ignore-all-space or git blame -w to see through
this commit; it is listed in .git-blame-ignore-revs.'
# 3. Record it so blame skips it
echo "$(git rev-parse HEAD)  # formatter sweep" >> .git-blame-ignore-revs
git add .git-blame-ignore-revs && git commit -m 'chore: ignore the formatter sweep in blame'
git config --local blame.ignoreRevsFile .git-blame-ignore-revs
# Verification: blame attributes lines to their real authors
git blame -- src/payments/refund.ts | head -3

SAFETY WARNING — a formatting sweep and a functional change in the same commit is unreviewable: the diff is thousands of lines and the three that matter are invisible inside it. Keep the sweep isolated, and if a formatter reveals a genuine bug while running, fix it in a separate commit before or after. Reviewers should be able to verify the sweep by re-running the formatter and getting an empty diff.

Step 5 — Prevent the recurrence Jump to heading

The sweep is a one-off; unformatted commits arriving afterwards are not.

# Format staged files only, at commit time
npx lint-staged
{
  "lint-staged": {
    "*.{ts,tsx,js,json,css,md}": "prettier --write"
  }
}
# Verification: an unformatted change cannot be committed
printf 'const x   =    1\n' >> src/scratch.ts && git add src/scratch.ts && git commit -m test
git show --stat HEAD && git reset --hard HEAD~1
The order that makes a sweep survivableAnnounce, sweep in one isolated commit, record it for blame, and install a hook so no unformatted commit can follow. Branches in flight rebase across the sweep with the formatter applied, so they never conflict on whitespace again.Announcequiet periodSweepone commitnothing elseRecordblame-ignore-revsHookformat on commitwithout the last box the sweep has to be repeated within a year

The staged-file approach is covered in running Prettier and ESLint only on staged files, and the variant for codebases too large to reformat at once is in formatting only changed lines in a legacy codebase.

Validation checklist Jump to heading

Frequently Asked Questions Jump to heading

Is -X ignore-all-space safe in Python or YAML? Jump to heading

Less so, because indentation carries meaning. A merge that ignores all whitespace can accept a hunk whose indentation places a statement in a different block. Prefer ignore-space-at-eol or ignore-space-change there, and verify the merged result by running the tests rather than by reading the diff.

What about a sweep that changes line endings as well? Jump to heading

Do them as two separate commits, because they are two different operations and conflate badly. The line-ending half is covered in standardising line endings with gitattributes, and it has its own blame and renormalisation considerations.

Can the formatter be run automatically during a rebase? Jump to heading

Yes — git rebase --exec runs a command after each commit is applied, which is exactly the mechanism in Step 3. It makes a long branch take noticeably longer to replay, which is the main reason to squash first when the branch has many small commits.