fix(goal-classifier): git diff headers keep a/ b/ prefixes under any host gitconfig
`extract_changed_files` parses `diff --git a/<old> b/<new>` headers. A host `diff.mnemonicprefix=true` or `diff.noprefix=true` rewrote them (`c/ w/`, or none), so every tracked change vanished from `changed_files` and the two evidence git-diff tests failed on such hosts. Both capture paths now pass `--src-prefix=a/ --dst-prefix=b/`, which override both config keys. Regression test sets each key in the temp repo. Claude-Session: https://claude.ai/code/session_01RUqV8HVGXcAETyYbZ2tPQ4
This commit is contained in:
1 parent
09867915ef
commit
d331b0b2a3
1 file changed
+44
-3
@@ -519,6 +519,9 @@ pub(crate) async fn capture_plan_changes(
|
||||
}
|
||||
}
|
||||
|
||||
/// Pin the `a/` `b/` header prefixes [`extract_changed_files`] parses.
|
||||
const DIFF_PREFIX_ARGS: [&str; 2] = ["--src-prefix=a/", "--dst-prefix=b/"];
|
||||
|
||||
/// Run `git diff <baseline>` and return the FULL (untruncated) stdout;
|
||||
/// the caller extracts the changed-file list before truncating.
|
||||
async fn run_git_diff_against_baseline(
|
||||
@@ -526,7 +529,7 @@ async fn run_git_diff_against_baseline(
|
||||
workspace_root: &Path,
|
||||
) -> Result<String, ChangesCaptureError> {
|
||||
let mut cmd = git_command(workspace_root);
|
||||
cmd.arg("diff").arg(baseline);
|
||||
cmd.arg("diff").args(DIFF_PREFIX_ARGS).arg(baseline);
|
||||
let output = match tokio::time::timeout(DIFF_COMMAND_TIMEOUT, cmd.output()).await {
|
||||
Ok(Ok(output)) => output,
|
||||
Ok(Err(err)) => return Err(ChangesCaptureError::DiffCommandFailed(err.to_string())),
|
||||
@@ -579,10 +582,14 @@ async fn lazy_git_baseline_diff(
|
||||
// support SHA-256 repos.
|
||||
let mut cmd = git_command(workspace_root);
|
||||
if git_has_parent(workspace_root, &oldest).await {
|
||||
cmd.arg("diff").arg(format!("{oldest}^..{head}"));
|
||||
cmd.arg("diff")
|
||||
.args(DIFF_PREFIX_ARGS)
|
||||
.arg(format!("{oldest}^..{head}"));
|
||||
} else {
|
||||
let empty_tree = derive_empty_tree_sha(workspace_root).await;
|
||||
cmd.arg("diff").arg(format!("{empty_tree}..{head}"));
|
||||
cmd.arg("diff")
|
||||
.args(DIFF_PREFIX_ARGS)
|
||||
.arg(format!("{empty_tree}..{head}"));
|
||||
}
|
||||
|
||||
let output = match tokio::time::timeout(DIFF_COMMAND_TIMEOUT, cmd.output()).await {
|
||||
@@ -2192,4 +2199,38 @@ mod tests {
|
||||
"patch must carry the untracked-files note",
|
||||
);
|
||||
}
|
||||
#[tokio::test]
|
||||
async fn changed_files_ignore_the_host_diff_prefix_config() {
|
||||
// Both keys rewrite the `diff --git a/<old> b/<new>` header the parser keys on.
|
||||
for key in ["diff.mnemonicprefix", "diff.noprefix"] {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
init_repo(tmp.path());
|
||||
git(tmp.path(), &["config", key, "true"]);
|
||||
tokio::fs::write(tmp.path().join("tracked.txt"), b"seed\n")
|
||||
.await
|
||||
.unwrap();
|
||||
let goal_created_at: i64 = 1_700_000_100;
|
||||
git_at(tmp.path(), &["add", "."], goal_created_at + 100);
|
||||
git_at(
|
||||
tmp.path(),
|
||||
&["commit", "-q", "-m", "post-goal"],
|
||||
goal_created_at + 100,
|
||||
);
|
||||
let lazy = capture_changes_diff(None, tmp.path(), goal_created_at)
|
||||
.await
|
||||
.expect("lazy baseline must succeed");
|
||||
assert_eq!(lazy.changed_files, ["tracked.txt"], "{key}: lazy baseline");
|
||||
tokio::fs::write(tmp.path().join("tracked.txt"), b"changed\n")
|
||||
.await
|
||||
.unwrap();
|
||||
let explicit = capture_changes_diff(Some("HEAD"), tmp.path(), goal_created_at)
|
||||
.await
|
||||
.expect("layer-1 git diff must succeed");
|
||||
assert_eq!(
|
||||
explicit.changed_files,
|
||||
["tracked.txt"],
|
||||
"{key}: explicit baseline"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user