Skip to content
Fran Gonzalez
← Back to blog
(updated Jun 26, 2026)·Clanker·4 min read

git rebase warns about local changes that git diff can't see

git rebase detached HEAD and already applied 8 of my feature commits. git diff showed nothing because the 'local changes' were the rebase itself, not uncommitted edits.

Some matmuls wrote this slop, sorry. My goal with this content is to document some work I (a real human bean) do while poking the Clanker, and try to learn something along the way.

I ran git rebase main on a branch with a fair number of commits. Eight commits applied cleanly, then it stopped:

error: Your local changes to the following files would be overwritten by merge:
        deploy/mobile-sync/sync-config.yaml

git diff was empty. git diff --cached was empty. The file looked clean.

The Problem

The file had no content changes. Running byte-level verification confirmed zero differences:

$ git hash-object deploy/mobile-sync/sync-config.yaml
1e4d1247e2b53857d27a7c4db03a495e51b585ba

$ git rev-parse HEAD:deploy/mobile-sync/sync-config.yaml
1e4d1247e2b53857d27a7c4db03a495e51b585ba

Identical hashes. No filemode change. No line-ending conversion. No .gitattributes in play. The file was byte-for-byte identical to HEAD.

Git was warning about modifications that did not exist in any normal sense.

What Changed

The git-rebase documentation describes the mechanics precisely. A rebase happens in four steps 1:

  1. List all commits to replay.
  2. Detach HEAD at <upstream>: git checkout --detach <upstream>.
  3. Cherry-pick each commit in order onto the detached HEAD.
  4. Update the branch pointer to the final commit.

The Pro Git book explains the same process as “resetting the current branch to the same commit as the branch you are rebasing onto, and finally applying each change in turn.” 2

Why I saw the warning

After each successful cherry-pick, HEAD advances. I had a fair number of commits on my feature branch. After 8 applied, HEAD was at <upstream> + commit1' + ... + commit8'.

The ASCII representation:

Before rebase:
  main:       A---B---C  (upstream)
                   \
  feature:          D---E---F---...---(bunch of commits, HEAD)

After 8 commits applied:
  main:       A---B---C---D'---E'---...---H'  (HEAD, detached)
                   \
  feature:          D---E---... (original tip)

git diff <file>  →  empty (working tree matches new HEAD)
rebase next: apply commit I'  →  file already modified by H'  →  warning

git diff compares the working tree against the index (HEAD). Since HEAD already holds the accumulated result of 8 successful cherry-picks, the working tree looks clean. But the next commit being applied (I') modifies the same file that a previous commit (H') already touched. Git sees a conflict between the patch it is trying to apply (“theirs”) and the current HEAD state (“ours”).

This is the “ours”/“theirs” swap specific to rebase: in a merge, ours is the current branch. In a rebase, ours is the upstream plus already-applied patches: the new history being built. theirs is the original topic branch. Git sees the next patch (theirs) clobbering what is already in HEAD (ours).

The merge conflicts section also notes that the apply backend writes “Your files would be overwritten…” to stdout, while the merge backend uses stderr. Same problem, different pipe.

What I did

The file was clean. The “local changes” were the rebase’s own accumulated state. Continuing worked:

git rebase --continue

The rescheduled commit re-attempted and applied cleanly on the second pass, the conflict was between two commits that modified the same file in non-overlapping ways.

If the conflict were real, I would have gotten merge markers to resolve. If it were persistent, git rebase --skip would have skipped that commit. And git rebase --abort would have returned me to the original branch tip before the entire rebase started 3.

References

Footnotes

  1. git-rebase(1) Description: the four explicit steps: list commits, detach HEAD at upstream, replay each as cherry-pick, update branch pointer.

  2. Pro Git Rebasing: “resetting the current branch to the same commit as the branch you are rebasing onto.”

  3. git-rebase(1) Mode Options: --continue, --skip, --abort, and --quit behavior.

This post was written with AI assistance.