GitGit

error: Your local changes to the following files would be overwritten by merge

Git is protecting uncommitted work that a pull would destroy. The four ways out, and which one to pick depending on whether you want to keep the changes.

easy fix6 min read

the git error
error: Your local changes to the following files would be overwritten by merge:
        src/config.ts
Please commit your changes or stash them before you merge.
Aborting

error: Your local changes to the following files would be overwritten by checkout:
        package.json
Please commit your changes or stash them before you switch branches.

Do this first3 steps

Run these in order. Each one tells you what its output means before you change anything.

  1. 1

    See exactly what is uncommitted before deciding

    git status --short && git diff --stat

    The M column is modified and unstaged, and the files listed in the error are a subset of these. Read the diff before choosing: the difference between keeping and discarding this work is not recoverable in one direction.

  2. 2

    Keep the changes and get the remote commits

    git stash push -m "wip before pull" && git pull && git stash pop

    If stash pop reports a conflict, your work and the incoming commits touched the same lines. The stash is kept until the conflict is resolved, so nothing is lost.

  3. 3

    Or discard them, if the changes are genuinely disposable

    git restore src/config.ts

    This overwrites the file from the index with no confirmation and no reflog entry. Uncommitted changes discarded this way cannot be recovered.

All 9 sections

Git is refusing to overwrite work you have not committed. The merge would replace those files with the incoming versions, and your edits would be gone with no way back, so it stops first.

This is Git being careful. The fix is to decide what happens to those changes.

Look before you choose

git status --short
 M src/config.ts
 M package.json
?? notes.txt

The first column is the index, the second is the working tree. M means modified and not staged. ?? is untracked, and untracked files are not what triggered this error.

Then read the actual diff:

git diff src/config.ts

Often this is a local port number or a debug line you do not need. Sometimes it is an afternoon of work. The four options below differ in whether that survives, and only you can tell them apart.

Option 1: Stash, pull, restore

The usual answer when the changes matter and are not ready to commit:

git stash push -m "wip before pull"
git pull
git stash pop

git stash pop applies the stash and drops it. If applying produces a conflict, the stash is not dropped and stays in the list until you resolve things, so nothing is lost.

git stash list
# stash@{0}: On main: wip before pull

Two things worth knowing:

  • git stash without -u leaves untracked files in place. With -u they go into the stash too, which is what you want before switching branches.
  • Named stashes are much easier to identify later than WIP on main: a1b2c3d.

Option 2: Commit first

If the work is coherent, committing is cleaner than stashing:

git add -A
git commit -m "Add retry to the payment client"
git pull --rebase

--rebase replays your commit on top of the incoming ones rather than creating a merge commit, which keeps the history readable for a small change.

If the commit is not finished, commit it anyway and amend later:

git commit -m "wip"
git pull --rebase
# carry on working
git commit --amend -m "Add retry to the payment client"

Only amend commits you have not pushed.

Option 3: Discard the changes

When the diff is genuinely disposable:

git restore src/config.ts        # one file
git restore .                    # everything tracked

git restore replaced git checkout -- <file> in Git 2.23 and is worth using because it cannot be confused with switching branches.

There is no undo. Uncommitted changes are not in the object database, so the reflog cannot help. Read the diff first.

To clear untracked files as well:

git clean -nd     # dry run, always do this first
git clean -fd     # actually delete

git clean -fd deletes untracked files permanently, including ones you meant to keep and never added. The -n dry run is not optional in practice.

Option 4: Take theirs for specific files

A middle path when you want the remote version of one file and your own for everything else:

git checkout origin/main -- src/config.ts
git pull

That overwrites just src/config.ts from the remote, leaving your other changes intact.

The one that keeps coming back

If the same file is modified every time you pull and you never edited it, it is a tracked file that should not be tracked. Adding it to .gitignore does nothing, because .gitignore only affects untracked files.

git rm --cached .env
echo ".env" >> .gitignore
git commit -m "Stop tracking .env"

--cached removes it from Git and leaves it on disk. Without it, you delete the file.

Common culprits: .env, .vscode/settings.json, *.log, and build output someone committed once.

Where a file must be tracked but is locally modified on purpose, such as a sample config, the honest pattern is to track config.example.json and gitignore the real one. git update-index --skip-worktree also exists and is a recurring source of confusion, since the change becomes invisible to git status and reappears in inexplicable ways later.

The checkout variant

Switching branches produces the same error:

error: Your local changes to the following files would be overwritten by checkout:
        package.json

Same causes, same options. Git allows the switch when the changed files do not differ between the two branches, which is why it sometimes works and sometimes does not.

git switch is the modern command for this and gives clearer messages than git checkout:

git switch feature/payments

Since Git 2.27 you can also carry your changes across with a merge:

git switch -m feature/payments

A checklist

  1. git status --short and git diff first. Decide with the diff in front of you.
  2. Keep and not ready to commit → git stash push -m "...", pull, git stash pop.
  3. Keep and coherent → commit, then git pull --rebase.
  4. Discard → git restore <file>. No undo, so be sure.
  5. Untracked files too → git clean -nd before git clean -fd.
  6. Just one file should follow the remote → git checkout origin/main -- <file>.
  7. Same file every time → git rm --cached <file> and add it to .gitignore.
  8. Conflict during stash pop → the stash is kept, so you have not lost anything.

Frequently Asked Questions

Why does Git block a pull instead of just merging?

Because the merge would overwrite files you have modified and not committed, and uncommitted changes exist nowhere else. Committed work can always be recovered through the reflog even after a bad merge or reset, but a working-tree edit that gets overwritten is simply gone. Git stops and asks you to commit or stash precisely because it cannot offer you an undo for this one. Untracked files do not trigger it, since there is nothing of yours to overwrite.

What is the difference between git stash and committing?

A commit is a permanent, named point in history that you can push, share and recover through the reflog. A stash is a local side-stack, invisible to everyone else, intended for work in progress you do not want in the history yet. For anything you might want back in a week, commit; you can always rewrite the message later with git commit --amend or squash it during a rebase. Stash is best for the five-minute interruption, and stashes are easy to accumulate and forget, so give them messages with -m.

Can I recover changes I discarded with git restore?

Generally no. Uncommitted changes were never written to Git's object database, so there is no reflog entry and no dangling object to recover. The realistic chances are your editor's local history, which VS Code and the JetBrains IDEs both keep, or a filesystem snapshot such as Time Machine. This is the reason to read git diff before discarding anything, and the reason git stash is worth the extra few seconds when you are not certain.

Why does the same file keep appearing as modified when I never edit it?

It is tracked by Git and being changed by something else: a tool rewriting .vscode/settings.json, a build step touching a committed artefact, or an .env file that was committed once. Adding it to .gitignore has no effect, because .gitignore only applies to files Git is not already tracking. Stop tracking it with git rm --cached <file>, which removes it from the repository while leaving it on disk, then add it to .gitignore and commit both changes together.

What happens if git stash pop hits a conflict?

The conflicting files are left with conflict markers for you to resolve, and, importantly, the stash entry is not dropped. You can see it still in git stash list, so nothing is lost and you can start again with git checkout -- . followed by another git stash pop if the resolution goes wrong. Once you have resolved and staged the files, remove the entry yourself with git stash drop. Using git stash apply instead of pop makes this explicit, since it never drops the stash automatically.

Reference and practice

Learn the underlying concept

Other Git errors