use crate::error::GitResult; use crate::pb::*; /// Discover remote refs via `git ls-remote`. pub fn find_remote_repository( request: FindRemoteRepositoryRequest, ) -> GitResult { if request.remote_url.is_empty() { return Ok(FindRemoteRepositoryResponse { refs: vec![], exists: false, }); } let output = std::process::Command::new("git") .args(["ls-remote", "--symref", &request.remote_url]) .stdout(std::process::Stdio::piped()) .stderr(std::process::Stdio::piped()) .output() .map_err(|e| crate::error::GitError::CommandFailed { status_code: None, stderr: e.to_string(), })?; if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr); if stderr.contains("Could not resolve host") || stderr.contains("Repository not found") { return Ok(FindRemoteRepositoryResponse { refs: vec![], exists: false, }); } return Ok(FindRemoteRepositoryResponse { refs: vec![], exists: false, }); } let stdout = String::from_utf8_lossy(&output.stdout); let mut refs = Vec::new(); for line in stdout.lines() { let line = line.trim(); if line.is_empty() { continue; } // Lines can be: // SHArefname (direct ref) // ref: refs/heads/mainHEAD (symbolic ref via --symref) if line.starts_with("ref:") { if let Some((target, name)) = line.split_once('\t') { refs.push(RemoteHead { ref_name: name.to_string(), target_oid: String::new(), symbolic: true, symbolic_target: target .strip_prefix("ref:") .unwrap_or(target) .trim() .to_string(), }); } } else if let Some((oid, name)) = line.split_once('\t') { refs.push(RemoteHead { ref_name: name.to_string(), target_oid: oid.to_string(), symbolic: false, symbolic_target: String::new(), }); } } Ok(FindRemoteRepositoryResponse { refs, exists: true }) } /// Find the root ref (HEAD) of a remote repository. pub fn find_remote_root_ref( request: FindRemoteRootRefRequest, ) -> GitResult { let output = std::process::Command::new("git") .args(["ls-remote", "--symref", &request.remote_url, "HEAD"]) .stdout(std::process::Stdio::piped()) .stderr(std::process::Stdio::piped()) .output() .map_err(|e| crate::error::GitError::CommandFailed { status_code: None, stderr: e.to_string(), })?; let stdout = String::from_utf8_lossy(&output.stdout); for line in stdout.lines() { let line = line.trim(); if line.starts_with("ref:") { if let Some((target, _name)) = line.split_once('\t') { let ref_name = target .strip_prefix("ref:") .unwrap_or(target) .trim() .to_string(); return Ok(FindRemoteRootRefResponse { ref_name, target_oid: String::new(), }); } } else if let Some((oid, name)) = line.split_once('\t') { return Ok(FindRemoteRootRefResponse { ref_name: name.to_string(), target_oid: oid.to_string(), }); } } Ok(FindRemoteRootRefResponse::default()) }