From 1d21312345811654fa8afc22c3be686fa44e3a14 Mon Sep 17 00:00:00 2001 From: Kriday Dave Date: Tue, 6 Oct 2026 01:26:38 +0530 Subject: [PATCH] Name the commits' real authors in git dockets GitAdapter::authors passed the revision range after `--`, so git read it as a pathspec rather than a range. That matched no path and exited 0, so the call returned an empty list instead of failing. adjudicate_3way treats an empty list as "unknown author" and substitutes the literal @git-author, so every git docket recorded a fabricated provenance rather than the commits' actual authors. The error branch at git.rs:115 never fired either, because git exited successfully. Move the range before `--`. Adds test_git_adapter_3way_reports_real_commit_authors, which commits as a named author and asserts the docket names them. src/adapter/git.rs | 5 +++- tests/git_adapter_test.rs | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) --- src/adapter/git.rs | 5 +++- tests/git_adapter_test.rs | 62 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/src/adapter/git.rs b/src/adapter/git.rs index 1fb6d8f..6c4ae88 100644 --- a/src/adapter/git.rs +++ b/src/adapter/git.rs @@ -106,8 +106,11 @@ impl GitAdapter { /// Extract commit authors across a revision or range (e.g. `base..head`). pub fn authors(&self, rev_range: &str) -> Result> { + // The range goes before `--`. After it git reads the range as a + // pathspec, which matches nothing and exits 0, so authors came back + // empty and every git docket fell through to the @git-author fallback. let output = Command::new("git") - .args(["log", "--format=%an", "--", rev_range]) + .args(["log", "--format=%an", rev_range, "--"]) .current_dir(&self.repo_root) .output() .with_context(|| format!("Failed to query authors for range '{rev_range}'"))?; diff --git a/tests/git_adapter_test.rs b/tests/git_adapter_test.rs index a5b1f24..fb5d98f 100644 --- a/tests/git_adapter_test.rs +++ b/tests/git_adapter_test.rs @@ -94,6 +94,36 @@ impl TempGitRepo { .expect("git checkout failed"); assert!(status.success()); } + + /// Commit under a specific author identity, so docket provenance is real. + fn commit_as(&self, msg: &str, name: &str, email: &str) -> String { + let prev_name = self.git_config("user.name"); + let prev_email = self.git_config("user.email"); + self.set_git_config("user.name", name); + self.set_git_config("user.email", email); + let sha = self.commit(msg); + self.set_git_config("user.name", &prev_name); + self.set_git_config("user.email", &prev_email); + sha + } + + fn git_config(&self, key: &str) -> String { + let out = Command::new("git") + .args(["config", "--get", key]) + .current_dir(&self.path) + .output() + .expect("git config read failed"); + String::from_utf8_lossy(&out.stdout).trim().to_string() + } + + fn set_git_config(&self, key: &str, value: &str) { + let status = Command::new("git") + .args(["config", key, value]) + .current_dir(&self.path) + .status() + .expect("git config write failed"); + assert!(status.success()); + } } impl Drop for TempGitRepo { @@ -275,6 +305,38 @@ fn test_git_adapter_visibility_violation_cloaked() { assert!(docket.disputes[0].detail.contains("private path")); } +#[test] +fn test_git_adapter_3way_reports_real_commit_authors() { + let repo = TempGitRepo::new("3way_authors"); + + repo.write_file("src/lib.rs", "pub fn common() -> i32 { 0 }\n"); + repo.commit_as("base version", "Base Author", "base@example.com"); + + repo.create_and_checkout_branch("feature/auth"); + repo.write_file("src/lib.rs", "pub fn common() -> i32 { 1 }\n"); + repo.commit_as("feature commit", "Dana Feature", "dana@example.com"); + + let adapter = GitAdapter::new(&repo.path).expect("valid git repo"); + let engine = Engine::new().expect("valid engine"); + + let docket = adapter + .adjudicate_3way( + "main", + "feature/auth", + &engine, + &MeaningPolicy::default(), + &VisibilityPolicy::default(), + &GitAdjudicateOptions::default(), + ) + .expect("adjudicate 3way"); + + assert_eq!( + docket.authors, + vec!["Dana Feature".to_string()], + "docket must name the commits' real author, not the @git-author fallback" + ); +} + #[test] fn test_cli_git_adjudication_flags() { let repo = TempGitRepo::new("cli_git");