From 841455c974e4e5ab40218fd0d476800f762b0ec4 Mon Sep 17 00:00:00 2001 From: Seongmin Lee Date: Thu, 8 Oct 2026 19:53:06 +0900 Subject: [PATCH] gitmirror-sync: sha256 support Signed-off-by: Seongmin Lee --- gitmirror/crates/gitmirror-git/src/layout.rs | 10 +- gitmirror/crates/gitmirror-sync/src/task.rs | 107 ++++++++++++++----- 2 files changed, 87 insertions(+), 30 deletions(-) diff --git a/gitmirror/crates/gitmirror-git/src/layout.rs b/gitmirror/crates/gitmirror-git/src/layout.rs index 33109b875..1d06a295c 100644 --- a/gitmirror/crates/gitmirror-git/src/layout.rs +++ b/gitmirror/crates/gitmirror-git/src/layout.rs @@ -53,8 +53,16 @@ impl Layout { }) .collect::>()?; + let object_hash = self.open(dids[0])?.object_hash(); let scratch = tempfile::tempdir()?; - gix::init_bare(scratch.path())?; + gix::ThreadSafeRepository::init( + scratch.path(), + gix::create::Kind::Bare, + gix::create::Options { + object_hash: Some(object_hash), + ..Default::default() + }, + )?; let info_dir = scratch.path().join("objects").join("info"); std::fs::create_dir_all(&info_dir)?; diff --git a/gitmirror/crates/gitmirror-sync/src/task.rs b/gitmirror/crates/gitmirror-sync/src/task.rs index 48c250752..c2bd49410 100644 --- a/gitmirror/crates/gitmirror-sync/src/task.rs +++ b/gitmirror/crates/gitmirror-sync/src/task.rs @@ -20,7 +20,9 @@ const AGENT: &str = concat!("gitmirror/", env!("CARGO_PKG_VERSION")); #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum TaskDisposition { - Finished { default_branch_commit: Option }, + Finished { + default_branch_commit: Option, + }, Deferred, } @@ -52,6 +54,13 @@ pub enum BackfillGitError { Fetch(#[from] gix_protocol::fetch::Error), #[error("edit references: {0}")] RefsEdit(#[from] gix::reference::edit::Error), + #[error("unsupported object format: {0}")] + ObjectFormat(String), + #[error("object format mismatch: local {local}, knot {remote}")] + ObjectFormatMismatch { + local: gix_hash::Kind, + remote: gix_hash::Kind, + }, } /// syncs git repository @@ -80,21 +89,16 @@ pub async fn did_task( return Ok(TaskDisposition::Deferred); }; - // TODO: add sha256 support. - let refs = parse_refs(&repo.refs.refs, gix_hash::Kind::Sha1)?; - let head = FullName::try_from(format!("refs/heads/{}", repo.refs.default_branch)) + let default_branch = FullName::try_from(format!("refs/heads/{}", repo.refs.default_branch)) .map_err(|e| BackfillError::MalformedRefs(e.to_string()))?; - let default_branch_commit = refs - .iter() - .find(|(_, name)| *name == head) - .map(|(oid, _)| *oid); - let url = format!("{knot}{}", repo.did.as_str()); let path = layout.repo_path(&repo.did); - tokio::task::spawn_blocking(move || fetch_into(&url, &path, &refs, head)) - .await - .map_err(|_| BackfillError::Panicked)??; + let refs = repo.refs.refs.clone(); + let default_branch_commit = + tokio::task::spawn_blocking(move || fetch_into(&url, &path, &refs, default_branch)) + .await + .map_err(|_| BackfillError::Panicked)??; Ok(TaskDisposition::Finished { default_branch_commit, @@ -145,15 +149,9 @@ fn parse_refs( fn fetch_into( url: &str, path: &Path, - refs: &[(ObjectId, FullName)], - head: FullName, -) -> Result<(), BackfillGitError> { - let repo = if path.join("HEAD").exists() { - gix::open(path).map_err(gix::init::Error::Open) - } else { - gix::init_bare(path) - }?; - + refs: &[u8], + default_branch: FullName, +) -> Result, BackfillError> { let mut transport = gix_transport::client::blocking_io::connect::connect( url, gix_transport::client::blocking_io::connect::Options { @@ -161,7 +159,8 @@ fn fetch_into( ssh: Default::default(), trace: false, }, - )?; + ) + .map_err(BackfillGitError::from)?; let mut progress = gix::progress::Discard; let should_interrupt = AtomicBool::new(false); @@ -174,7 +173,48 @@ fn fetch_into( |_| Ok(None), Default::default(), &mut progress, - )?; + ) + .map_err(BackfillGitError::from)?; + + let object_hash = match handshake + .capabilities + .capability("object-format") + .and_then(|c| c.value()) + { + Some(format) => format + .to_string() + .parse() + .map_err(BackfillGitError::ObjectFormat)?, + None => gix_hash::Kind::Sha1, + }; + + let refs = parse_refs(refs, object_hash)?; + let default_branch_commit = refs + .iter() + .find(|(_, name)| *name == default_branch) + .map(|(oid, _)| *oid); + + let repo = if path.join("HEAD").exists() { + gix::open(path).map_err(gix::init::Error::Open) + } else { + gix::ThreadSafeRepository::init( + path, + gix::create::Kind::Bare, + gix::create::Options { + object_hash: Some(object_hash), + ..Default::default() + }, + ) + .map(Into::into) + } + .map_err(BackfillGitError::from)?; + if repo.object_hash() != object_hash { + return Err(BackfillGitError::ObjectFormatMismatch { + local: repo.object_hash(), + remote: object_hash, + } + .into()); + } let pack_dir = repo.path().join("objects").join("pack"); let odb = repo.objects.clone(); @@ -182,7 +222,7 @@ fn fetch_into( let cache = repo.commit_graph_if_enabled().ok().flatten(); let mut negotiate = StoredRefs { repo: &repo, - wants: refs, + wants: &refs, negotiator: gix_negotiate::Algorithm::Consecutive.into_negotiator(), graph: repo.revision_graph(cache.as_ref()), }; @@ -196,7 +236,10 @@ fn fetch_into( progress, should_interrupt, Some(odb.clone()), - Default::default(), + gix_pack::bundle::write::Options { + object_hash, + ..Default::default() + }, ) .map(|_| true) }, @@ -214,8 +257,10 @@ fn fetch_into( tags: gix_protocol::fetch::Tags::None, reject_shallow_remote: true, }, - )?; + ) + .map_err(BackfillGitError::from)?; + // TODO(boltless): dump packed-refs instead let ref_edit = |name: FullName, new: Target| RefEdit { change: Change::Update { log: LogChange { @@ -232,11 +277,15 @@ fn fetch_into( let edits = refs .iter() .map(|(oid, name)| ref_edit(name.clone(), Target::Object(*oid))) - .chain([ref_edit("HEAD".try_into().expect("valid ref name"), Target::Symbolic(head))]); - repo.edit_references(edits)?; + .chain([ref_edit( + "HEAD".try_into().expect("valid ref name"), + Target::Symbolic(default_branch), + )]); + repo.edit_references(edits) + .map_err(BackfillGitError::from)?; debug!(refs = refs.len(), "fetched and updated refs"); - Ok(()) + Ok(default_branch_commit) } struct StoredRefs<'a, 'cache> { -- 2.54.0