DEV Community

Mahiro Hirakawa
Mahiro Hirakawa

Posted on

git commit -m with a path argument doesn't commit what you staged

git commit -m "msg" -- doesn't commit what you staged

I'd staged a handful of files, run a narrow git commit -m "fix: ..." -- path/to/file.rs, and assumed the diff that landed matched what I'd git added. It didn't. It matched whatever was sitting on disk in that file at the moment the commit ran, which happened to include a few uncommitted lines from a different working session sharing the same checkout.

This is documented git behavior, not a bug, and that's the part that makes it dangerous. When you give git commit a pathspec after --, git switches into what's effectively --only mode for those paths: it commits the current working-tree content of the matched files, not the index content you staged for them. The index still matters for every other path in the repo, but for the ones you named, git reads straight from disk. Running git add --cached beforehand to narrow the index first doesn't change this, because the pathspec form on the commit itself overrides that narrowing.

In a private checkout, this distinction almost never surfaces, because your working tree and your staged content for a given file are usually the same thing by the time you commit. You edited it, you staged it, nobody else touched it in between. The gap only opens up in a shared worktree, multiple sessions or people or agents pointed at the same directory, where the working tree can hold somebody else's in-progress, unstaged edits at the exact moment you run a pathspec-scoped commit against a file they'd also touched. Git isn't confused. It's doing exactly what the pathspec form says: here's the current state of this path, commit that. It just isn't what most people mean when they type -- path/to/file.rs at the end of a commit command, which reads more like "restrict this commit to this file" than "ignore the index for this file and use the working tree instead."

I've now hit this twice on a shared checkout, both times recovered with git reset --soft before anything got pushed, both times because I ran git diff -- <path> right after and noticed hunks I didn't write. That check is now the actual safeguard, not a nice-to-have: before a pathspec-scoped commit, diff each named path and confirm every hunk in it is yours. A plain git status after the fact isn't enough, because by then the commit already happened and the damage, if any, is baked into a commit object.

The safer default, when it's available, is simpler than the check: stage exactly what you want, then commit with no pathspec at all. git commit with nothing after the message commits the index as it stands, which is the behavior most people already believe pathspec-scoped commits have. The pathspec form is for a narrower, less common case, commit only the current disk state of these specific paths regardless of what's staged, and it's worth knowing that's what it means before it grabs someone else's unfinished edit and hands it to you as your own commit.

Top comments (0)