You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Unstaged changes in unrelated files are discarded by git checkout . when restoring hidden changes fails #1480
With a job that rewrites files — a formatter or linter run with --write / --fix — the hidden unstaged changes of a partially staged file can no longer be re-applied afterwards. When that happens, lefthook reverts the whole worktree with git checkout . — RevertAllUnstagedChanges() in guard.go.
Only partially staged files are covered by the patch that gets re-applied afterwards, so unstaged changes in every other file are silently discarded. The lefthook auto backup stash is dropped in the same run, so nothing is left to recover from.
Introduced in #1417. The abort-and-restore behaviour itself is a clear improvement — only the revert is wider than the backup. 2.1.6 and earlier don't touch the worktree here, but have #1369 instead.
Stage a hunk in a.txt containing foo, then edit that same line again without staging it. Leave an unstaged edit in an unrelated b.txt — already tracked, not a new file, since git checkout . leaves untracked files alone. Commit: the job rewrites foo, re-applying the saved patch fails, and the hook aborts as designed — a.txt is restored correctly, but b.txt is reverted too.
Lefthook version
2.1.10 (also reproduces on 2.1.7)
Possible solution
I assume the wide revert is deliberate: jobs can rewrite files outside the partially staged set, and those changes have to be undone too, so narrowing it to the hidden files alone would leave them behind. That's the tension — the revert has to cover everything the jobs touched, while the patch replayed right after only restores the partially staged ones.
Two things in scope at that point look relevant: withHiddenUnstagedChanges still holds the partiallyStagedFiles list it passed to RevertUnstagedChanges when hiding, and the lefthook auto backup stash created a few lines earlier is a full pre-hook snapshot that is currently only ever dropped, never read back. Happy to test a fix against the case above.
Got bitten by this today on 2.1.12, so here's a data point from a real repo.
I was splitting a repo-wide formatting pass into several commits: 128 files partially staged, ~2000 more with unstaged-only changes, and a pre-commit job running our formatter on {staged_files}. Ran it again afterwards with LEFTHOOK_VERBOSE=1 in a throwaway clone of the same state, and it's exactly what the issue describes:
stash create/store, patch the unstaged half of the 128 files, checkout --force them
formatter rewrites those 128 files
git apply --check on the patch fails (fair enough)
git checkout . -> all ~2000 unrelated files reverted
patch re-applies fine, so the 128 partially staged files look untouched, which is what makes it so confusing
git stash drop of the backup, so nothing left in stash list
We only got the work back because the backup stash was still a dangling commit (git fsck --unreachable). Would be nice if the backup survived on that failure path and its hash was printed, even once the revert is scoped to the hidden files.
#1483 looks like it covers it. Happy to run it against the same state if that helps.
Activity
kucharzyk-sebastian commentedon Aug 14, 2026
We've been hit by this issue quite severely. I'd love to see the fix being merged.
rujorgensen commentedon Sep 8, 2026
Got bitten by this today on 2.1.12, so here's a data point from a real repo.
I was splitting a repo-wide formatting pass into several commits: 128 files partially staged, ~2000 more with unstaged-only changes, and a pre-commit job running our formatter on
{staged_files}. Ran it again afterwards withLEFTHOOK_VERBOSE=1in a throwaway clone of the same state, and it's exactly what the issue describes:checkout --forcethemgit apply --checkon the patch fails (fair enough)git checkout .-> all ~2000 unrelated files revertedgit stash dropof the backup, so nothing left instash listWe only got the work back because the backup stash was still a dangling commit (
git fsck --unreachable). Would be nice if the backup survived on that failure path and its hash was printed, even once the revert is scoped to the hidden files.#1483 looks like it covers it. Happy to run it against the same state if that helps.
rujorgensen commentedon Oct 1, 2026
Bump