Skip to content

Commit abfd4d0

Browse files
lucatescariclaude
andcommitted
fix(status): use git add --renormalize so -f actually re-encrypts
Root cause of the intermittent Windows failure, confirmed by evidence from a 30x stress run: git add skipped the file entirely. index entry: 100644 dfbd3cbb... bad.secret HEAD blob: dfbd3cbb... <- identical index bytes: "should-be-" <- plaintext check-attr: filter: git-crypt <- attribute in effect filter.git-crypt.clean: configured The filter was configured and the attribute applied, yet git add exited 0 having done nothing. Only .gitattributes had changed; the working file was byte-identical to what was committed, so git's stat cache said there was no work and the clean filter was never invoked. It surfaced only intermittently because git re-hashes 'racily clean' files (mtime not strictly older than the index) as a safeguard, which usually masked it. This was never merely a flaky test. status -f would report 're-staged N of M file(s)' while leaving the plaintext secret staged — a false assurance about the exact thing the command exists to fix. --renormalize re-applies the clean process to tracked files unconditionally. It does not stage untracked files, so -f still refuses to auto-add them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TYMP8eH52L6rNdjbukfuyZ
1 parent e68f1a5 commit abfd4d0

2 files changed

Lines changed: 15 additions & 4 deletions

File tree

‎.github/workflows/stress-windows.yml‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ jobs:
2020
- name: Build test binary once
2121
run: cargo test --test integration --no-run
2222

23-
- name: Run the flaky test until it fails (max 30 attempts)
23+
- name: Run the flaky test until it fails (max 50 attempts)
2424
shell: bash
2525
run: |
26-
for i in $(seq 1 30); do
26+
for i in $(seq 1 50); do
2727
echo "::group::attempt $i"
2828
if ! cargo test --test integration test_status_fix_restages_only_warning_files -- --exact --nocapture; then
2929
echo "::endgroup::"
@@ -32,4 +32,4 @@ jobs:
3232
fi
3333
echo "::endgroup::"
3434
done
35-
echo "30/30 attempts passed — no reproduction this run"
35+
echo "50/50 attempts passed — no reproduction this run"

‎src/commands/status.rs‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,19 @@ pub fn status(
172172
);
173173
continue;
174174
}
175+
// `--renormalize` is required, not cosmetic. A plain
176+
// `git add` consults the stat cache first: the working file
177+
// is byte-identical to what was committed, only
178+
// `.gitattributes` changed, and git cannot see that from
179+
// stat data. So it decides there is nothing to do, exits 0,
180+
// and never invokes the clean filter — leaving the plaintext
181+
// blob staged while we report success. It only appeared
182+
// intermittently because git re-hashes "racily clean" files
183+
// (mtime not strictly older than the index) as a safeguard.
184+
// `--renormalize` re-applies the clean process to tracked
185+
// files unconditionally, which is exactly the intent here.
175186
let st = Command::new("git")
176-
.args(["add", "--"])
187+
.args(["add", "--renormalize", "--"])
177188
.arg(file)
178189
.status()
179190
.map_err(|e| GitVeilError::Git(format!("failed to stage {}: {}", file, e)))?;

0 commit comments

Comments
 (0)