Skip to content

Commit 326ba57

Browse files
authored
Merge pull request #754 from git-ai-project/fix/reduce-git-network-usage
2 parents b51acb5 + 7513612 commit 326ba57

3 files changed

Lines changed: 58 additions & 59 deletions

File tree

src/commands/git_handlers.rs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -359,10 +359,6 @@ fn run_pre_command_hooks(
359359
command_hooks_context.push_authorship_handle =
360360
push_hooks::push_pre_command_hook(parsed_args, repository);
361361
}
362-
Some("fetch") => {
363-
command_hooks_context.fetch_authorship_handle =
364-
fetch_hooks::fetch_pull_pre_command_hook(parsed_args, repository);
365-
}
366362
Some("pull") => {
367363
fetch_hooks::pull_pre_command_hook(parsed_args, repository, command_hooks_context);
368364
}
@@ -420,12 +416,6 @@ fn run_post_command_hooks(
420416
repository,
421417
command_hooks_context,
422418
),
423-
Some("fetch") => fetch_hooks::fetch_pull_post_command_hook(
424-
repository,
425-
parsed_args,
426-
exit_status,
427-
command_hooks_context,
428-
),
429419
Some("pull") => fetch_hooks::pull_post_command_hook(
430420
repository,
431421
parsed_args,

src/commands/git_hook_handlers.rs

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1569,12 +1569,19 @@ fn load_pull_hook_state(repo: &Repository) -> Option<PullHookState> {
15691569
serde_json::from_slice(&data).ok()
15701570
}
15711571

1572-
fn fetch_notes_from_all_remotes(repo: &Repository) {
1573-
if let Ok(remotes) = repo.remotes() {
1574-
for remote in remotes {
1575-
let _ = fetch_authorship_notes(repo, &remote);
1576-
}
1577-
}
1572+
fn fetch_notes_from_pull_remote(repo: &Repository) {
1573+
let remote = repo
1574+
.upstream_remote()
1575+
.ok()
1576+
.flatten()
1577+
.or_else(|| repo.get_default_remote().ok().flatten());
1578+
1579+
let Some(remote) = remote else {
1580+
debug_log("pull hooks: could not resolve remote for authorship fetch; skipping");
1581+
return;
1582+
};
1583+
1584+
let _ = fetch_authorship_notes(repo, &remote);
15781585
}
15791586

15801587
fn was_fast_forward_pull(repository: &Repository, expected_new_head: &str) -> bool {
@@ -1860,7 +1867,7 @@ fn maybe_handle_pull_post_merge(repo: &mut Repository) {
18601867
return;
18611868
}
18621869

1863-
fetch_notes_from_all_remotes(repo);
1870+
fetch_notes_from_pull_remote(repo);
18641871

18651872
let Ok(new_head) = repo.head().and_then(|head| head.target()) else {
18661873
return;
@@ -1886,7 +1893,7 @@ fn maybe_handle_pull_post_rewrite(repo: &mut Repository) {
18861893
return;
18871894
}
18881895

1889-
fetch_notes_from_all_remotes(repo);
1896+
fetch_notes_from_pull_remote(repo);
18901897

18911898
let Ok(new_head) = repo.head().and_then(|head| head.target()) else {
18921899
clear_pull_hook_state(repo);

src/git/sync_authorship.rs

Lines changed: 43 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -83,47 +83,8 @@ pub fn fetch_authorship_notes(
8383
remote_name, tracking_ref
8484
));
8585

86-
// First, check if the remote has refs/notes/ai using ls-remote
87-
// This is important for bare repos where the refmap might not be configured
88-
let mut ls_remote_args = repository.global_args_for_exec();
89-
ls_remote_args.push("ls-remote".to_string());
90-
ls_remote_args.push(remote_name.to_string());
91-
ls_remote_args.push("refs/notes/ai".to_string());
92-
93-
debug_log(&format!("ls-remote command: {:?}", ls_remote_args));
94-
95-
match exec_git(&ls_remote_args) {
96-
Ok(output) => {
97-
let result = String::from_utf8_lossy(&output.stdout).to_string();
98-
debug_log(&format!("ls-remote stdout: '{}'", result));
99-
debug_log(&format!(
100-
"ls-remote stderr: '{}'",
101-
String::from_utf8_lossy(&output.stderr)
102-
));
103-
104-
if result.trim().is_empty() {
105-
debug_log(&format!(
106-
"no authorship notes found on remote '{}', nothing to sync",
107-
remote_name
108-
));
109-
return Ok(NotesExistence::NotFound);
110-
}
111-
debug_log(&format!(
112-
"found authorship notes on remote '{}'",
113-
remote_name
114-
));
115-
}
116-
Err(e) => {
117-
debug_log(&format!(
118-
"failed to check for authorship notes on remote '{}': {}",
119-
remote_name, e
120-
));
121-
// Return error instead of assuming no notes - we don't know the state
122-
return Err(e);
123-
}
124-
}
125-
126-
// Now fetch the notes to the tracking ref with explicit refspec
86+
// Fetch notes to tracking ref with explicit refspec.
87+
// If the remote does not have refs/notes/ai yet, treat that as NotFound.
12788
let fetch_refspec = format!("+refs/notes/ai:{}", tracking_ref);
12889

12990
// Build the internal authorship fetch with explicit flags and disabled hooks.
@@ -148,6 +109,13 @@ pub fn fetch_authorship_notes(
148109
));
149110
}
150111
Err(e) => {
112+
if is_missing_remote_notes_ref_error(&e) {
113+
debug_log(&format!(
114+
"no authorship notes found on remote '{}', nothing to sync",
115+
remote_name
116+
));
117+
return Ok(NotesExistence::NotFound);
118+
}
151119
debug_log(&format!("authorship fetch failed: {}", e));
152120
return Err(e);
153121
}
@@ -187,6 +155,19 @@ pub fn fetch_authorship_notes(
187155

188156
Ok(NotesExistence::Found)
189157
}
158+
159+
fn is_missing_remote_notes_ref_error(error: &GitAiError) -> bool {
160+
let GitAiError::GitCliError { stderr, .. } = error else {
161+
return false;
162+
};
163+
164+
let stderr_lower = stderr.to_ascii_lowercase();
165+
stderr_lower.contains("refs/notes/ai")
166+
&& (stderr_lower.contains("couldn't find remote ref")
167+
|| stderr_lower.contains("could not find remote ref")
168+
|| stderr_lower.contains("remote ref does not exist")
169+
|| stderr_lower.contains("not our ref"))
170+
}
190171
// for use with post-push hook
191172
pub fn push_authorship_notes(repository: &Repository, remote_name: &str) -> Result<(), GitAiError> {
192173
// STEP 1: Fetch remote notes into tracking ref and merge before pushing
@@ -368,4 +349,25 @@ mod tests {
368349
);
369350
assert!(args.contains(&"push".to_string()));
370351
}
352+
353+
#[test]
354+
fn missing_remote_notes_ref_error_is_detected() {
355+
let err = GitAiError::GitCliError {
356+
code: Some(128),
357+
stderr: "fatal: couldn't find remote ref refs/notes/ai".to_string(),
358+
args: vec!["fetch".to_string(), "origin".to_string()],
359+
};
360+
assert!(is_missing_remote_notes_ref_error(&err));
361+
}
362+
363+
#[test]
364+
fn missing_remote_notes_ref_error_ignores_unrelated_git_errors() {
365+
let err = GitAiError::GitCliError {
366+
code: Some(128),
367+
stderr: "fatal: Authentication failed for 'https://github.com/org/repo.git/'"
368+
.to_string(),
369+
args: vec!["fetch".to_string(), "origin".to_string()],
370+
};
371+
assert!(!is_missing_remote_notes_ref_error(&err));
372+
}
371373
}

0 commit comments

Comments
 (0)