From baa7c85ff3db2366d67ac875fca48ad6dcabf212 Mon Sep 17 00:00:00 2001 From: Joel Klinghed Date: Mon, 9 Jun 2025 23:03:30 +0200 Subject: git: Make fetch return the new head for the fetched branch --- server/common/src/git.rs | 39 +++++++++++++++++++++++++++++++++++---- server/common/src/tests.rs | 10 +++++----- server/src/git_root.rs | 2 +- 3 files changed, 41 insertions(+), 10 deletions(-) (limited to 'server') diff --git a/server/common/src/git.rs b/server/common/src/git.rs index 053ff3f..8fe7863 100644 --- a/server/common/src/git.rs +++ b/server/common/src/git.rs @@ -122,6 +122,22 @@ fn parse_tree_entries(output: String) -> Vec { ret } +fn branch_eq(a: impl AsRef, b: impl AsRef) -> bool { + let a = a.as_ref(); + let b = b.as_ref(); + if a.starts_with("refs/heads/") { + if b.starts_with("refs/heads/") { + return a[11..] == b[11..]; + } + return a == format!("refs/heads/{b}"); + } else { + if b.starts_with("refs/heads/") { + return format!("refs/heads/{a}") == b; + } + return a == b; + } +} + impl RepoData { fn new() -> Self { Self { @@ -130,7 +146,7 @@ impl RepoData { } } - async fn fetch(&self, repo: &Repository, branch: String) -> Result<(), Error> { + async fn fetch(&self, repo: &Repository, branch: String) -> Result { if repo.remote.is_none() { return Err(Error::new("No remote set")); } @@ -149,9 +165,24 @@ impl RepoData { // <+ force update>: cmd.arg(format!("+{branch}:{branch}")); - self.output(&mut cmd).await?; + let output = self.output(&mut cmd).await?; + + // git fetch porcelain format: + // + for line in output.lines() { + let mut parts = line.split(' '); + parts.next(); // flag + parts.next(); // old-object-id + if let Some(new_object_id) = parts.next() { + let local_reference = parts.collect::>().join(" "); + if branch_eq(local_reference, &branch) { + return Ok(new_object_id.to_string()); + } + } + } - Ok(()) + assert!(false); + Err(Error::new("Fetch succeeded but branch not found")) } async fn init(&mut self, repo: &Repository) -> Result<(), Error> { @@ -590,7 +621,7 @@ impl Repository { Ok(()) } - pub async fn fetch(&self, branch: impl Into) -> Result<(), Error> { + pub async fn fetch(&self, branch: impl Into) -> Result { let branch = branch.into(); let data = self.lock.read().await; diff --git a/server/common/src/tests.rs b/server/common/src/tests.rs index 34cc315..c8553f7 100644 --- a/server/common/src/tests.rs +++ b/server/common/src/tests.rs @@ -272,15 +272,15 @@ async fn git_fetch(bare: bool) -> git::Repository { repo.fetch("branch"), repo.fetch("other"), ); - main.unwrap(); - branch.unwrap(); - other.unwrap(); + assert_eq!(main.unwrap(), "d7c502b9c6b833060576a0c4da0287933d603011"); + assert_eq!(branch.unwrap(), "2d05d489d42d4f36dd0ebf52502f243991e010eb"); + assert_eq!(other.unwrap(), "2cecdec660a30bf3964cee645d9cee03640ef8dc"); } else { // Not bare repo will complain when you try to fetch into the currently checked // out branch. So just try fetching other branches. let (branch, other) = tokio::join!(repo.fetch("branch"), repo.fetch("other")); - branch.unwrap(); - other.unwrap(); + assert_eq!(branch.unwrap(), "2d05d489d42d4f36dd0ebf52502f243991e010eb"); + assert_eq!(other.unwrap(), "2cecdec660a30bf3964cee645d9cee03640ef8dc"); } repo } diff --git a/server/src/git_root.rs b/server/src/git_root.rs index 01cc2b5..71ad96f 100644 --- a/server/src/git_root.rs +++ b/server/src/git_root.rs @@ -535,7 +535,7 @@ async fn setup_project_root( let bg_project_id = project_id.clone(); tokio::spawn(async move { match bg_repo.fetch(&main_branch).await { - Ok(()) => {} + Ok(_) => {} Err(e) => { error!("{bg_project_id}: fetch {main_branch} returned {e:?}"); } -- cgit v1.2.3-70-g09d2