Repository navigation
fix(strategy): compare redo content in the commit index - #2330
Open
MuskanPaliwal wants to merge 9 commits into
Open
MuskanPaliwal wants to merge 9 commits into
MuskanPaliwal wants to merge 9 commits into
Conversation
Read staged filenames and blob IDs together from Git's selected commit index. During partial commits and commit -a, the previous content check read the ordinary worktree index while filenames came from Git's temporary index, omitting valid checkpoint links or adding unwanted trailers. Pass one read-only snapshot through fresh and carry-forward content checks, preserving GIT_INDEX_FILE and the existing non-blocking hook behavior. Document why commit-index queries retain the hook environment. Add a 48-case real-Git regression matrix covering both checkpoint backends, ordinary and linked worktrees, staged/partial/-a commits, and kept/replaced agent content. The original implementation fails 32 cases; the fix passes all 48. Add native Git and raw-format parser coverage. Validation: mise run check passed, including race-enabled unit/integration tests and 60 deterministic canaries. The focused 48-case regression also passed on a subsequent rerun. No paid real-agent E2E tests ran. Entire-Checkpoint: 01M213A52SASJDN4ZRT42KMY2J
MuskanPaliwal
marked this pull request as ready for review
September 9, 2026 09:23
Entire-Checkpoint: 01M2SPQXWPH644HG70SPNSVKXA
Entire-Checkpoint: 01M4X750V4GKDGAH7E08FDCQ1R
Entire-Checkpoint: 01M4X75DA57CMZJQZQDPV4M6R2
Contributor
Author
|
The two red checks (
Their fixtures (from 6142ebe) use fixed session timestamps on 2026-10-07. #2769 fixes the three |
Redo detection read staged changes with git's rename detection on, so a staged deletion that git paired with a similar addition reported only the destination. A dropped commit that changed the deleted file then looked recommitted, and its trailer was inherited for work the commit no longer contains. The result also depended on the user's diff.renames setting. The redo query now passes --no-renames; getStagedFiles keeps reporting a rename's new path. The real-git test helper now passes the arguments the installed hooks pass, so amends work through it, and writes its hook scripts under ForkLock to avoid ETXTBSY. TestRedo_ComparesTheCommitIndex adds amend, deletion and renamed-deletion cases. git-safety.md gets its own section for git subprocesses in hooks, and CLAUDE.md covers hook queries of the hook's own refs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Entire-Checkpoint: 01M4ZJJPGR2CSMQS435CEY4CB7
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
git commit -aand path-limited commits prepare the commit in a temporaryGIT_INDEX_FILE. Redo detection (inheritReplacedCommitsTrailers) read staged paths from that index but blob IDs from go-git's ordinary index, so it compared the dropped commit with content that was not being committed. It also let git's rename detection hide staged deletions. The inheritedEntire-Checkpointtrailer was wrong in both directions.Problem
After
git reset HEAD~1:git commit -a,git commit -- <path>orgit commit --amend -aloses its trailer.-aor-- <path>inherits the trailer for work the commit no longer contains.git addandgit commit. Git reports the pair as a rename, so the deletion is never compared.A plain
git addfollowed bygit commitwas otherwise correct, because both indexes agree.Cause
stagedBlobs(manual_commit_redo.go, added in 6170bc1) listed paths withgetStagedFiles, which runsgit diff --cachedand so honorsGIT_INDEX_FILE. It then looked up each blob inrepo.Storer.Index(), which reads.git/index. During-a, path-limited and--amend -acommits, those are different indexes.git diff --cachedalso detects renames, following the user'sdiff.renames(on by default). A rename's raw record carries only its destination path, so the source's staged deletion never reached the comparison.The existing redo tests call
PrepareCommitMsgdirectly, where no temporary index exists, so they could not see the difference.Fix
getStagedChangesreads paths and full blob IDs in onegit diff --cached --raw -z --no-abbrevquery, through the existingparseStagedRaw(which already skips submodule gitlinks and reports rename destinations).getStagedFilesandstagedBlobsboth build on it, and the second index read is removed. The query deliberately keeps the hook environment:GIT_INDEX_FILEidentifies the commit Git is preparing.stagedBlobsadds--no-renames, so a rename's source is compared as the staged deletion it is.getStagedFileskeeps reporting a rename's new path, so its squash and session-overlap callers do not change.git-safety.mdmoves the hook-environment rules into their own section and adds the--no-renamesrule.CLAUDE.mdnow also covers hook queries of the hook's own refs, which the redo path runs.How this PR changed
This PR originally fixed the same mismatch in session linking (
stagedFilesOverlapWithContent). Upstream 389e855 then made new-file linking match by name, and prepare-commit-msg stopped reading the index there, so that change no longer applies and was dropped while syncing withmain. The same mismatch had meanwhile appeared in redo detection, which this PR now fixes.I started this from the side finding in #2303 about
getStagedFiles, which suggests stripping the hook environment. Doing that would read both paths and blob IDs from.git/index. They'd agree with each other and still miss whatgit commit -aor a path-limited commit records. This fix keepsGIT_INDEX_FILE, so the comparison reads the index Git is committing from.Related: #2111. This does not fix its empty-tree corruption.
Tests
TestRedo_ComparesTheCommitIndexruns realgit committhrough Entire's hooks, with the arguments the installed hooks pass. The dropped commit editsa.txtandb.txtand deletesd.txt.Verified RED against
main:With the commit-index change alone, only
renamed-deletionstill fails;--no-renamesfixes it. Thestagedcase passes throughout, as a control. All seven pass with the fix.TestParseStagedRawalso asserts each path's blob ID and deletions.Test plan
TestRedo_ComparesTheCommitIndex(7 cases), plus the existing redo and adversarial redo testsmise run lint: 0 issuesmise run check: passes exceptTestExternalCommand_SigintReachesPlugin, which failed once under-raceload (its plugin never reached the ready state) and passes 3/3 when run alone馃 Generated with Claude Code