use crate::bare::GitBare; use crate::error::{GitError, GitResult}; use crate::pb::{AdvertiseRefsRequest, AdvertiseRefsResponse, ReferenceAdvertisement}; impl GitBare { pub fn advertise_refs( &self, request: AdvertiseRefsRequest, ) -> GitResult { if request.raw { return self.advertise_refs_raw(&request); } let repo = self.gix_repo()?; let mut references = Vec::new(); for r in repo.references()?.all()? { let mut r = match r { Ok(r) => r, Err(_) => continue, }; let name = r.name().to_string(); let target_oid = r.peel_to_id().ok().map(|id| self.oid_to_pb(id.to_string())); let is_symbolic = r.target().try_id().is_none(); let symbolic_target = if is_symbolic { match r.target() { gix::refs::TargetRef::Symbolic(name) => name.to_string(), _ => String::new(), } } else { String::new() }; // Peel past tags to get the commit OID if this is a tag ref let peeled_oid = if name.starts_with("refs/tags/") { r.peel_to_id().ok().map(|id| self.oid_to_pb(id.to_string())) } else { None }; references.push(ReferenceAdvertisement { name, target_oid, peeled_oid, symbolic: is_symbolic, symbolic_target, }); } // Sort by name for deterministic output references.sort_by(|a, b| a.name.cmp(&b.name)); Ok(AdvertiseRefsResponse { references, capabilities: vec![ "report-status".into(), "delete-refs".into(), "side-band-64k".into(), "ofs-delta".into(), "multi_ack_detailed".into(), "multi_ack".into(), "symref=HEAD".into(), ], raw_data: Vec::new(), }) } /// Return raw pkt-line output from git upload-pack/receive-pack --advertise-refs. /// Used by transparent proxies (gitshell) that forward bytes verbatim to git clients. fn advertise_refs_raw( &self, request: &AdvertiseRefsRequest, ) -> GitResult { let bare_dir_str = self.bare_dir.to_string_lossy().into_owned(); let stateless = request.protocol.as_ref().is_some_and(|p| p.stateless); // Default to upload-pack if service is unspecified let subcommand = if request.service == "git-receive-pack" { "receive-pack" } else { "upload-pack" }; let mut args: Vec = vec![ "--git-dir".into(), bare_dir_str, subcommand.into(), "--advertise-refs".into(), ]; if stateless { args.push("--stateless-rpc".into()); } let result = duct::cmd("git", &args) .stdout_capture() .stderr_capture() .unchecked() .run()?; if !result.status.success() { return Err(GitError::CommandFailed { status_code: result.status.code(), stderr: String::from_utf8_lossy(&result.stderr).into_owned(), }); } tracing::debug!( raw_len = result.stdout.len(), service = %request.service, stateless = stateless, "advertise_refs raw output" ); Ok(AdvertiseRefsResponse { references: Vec::new(), capabilities: Vec::new(), raw_data: result.stdout, }) } }