use serde::{Deserialize, Serialize}; use crate::{ bare::GitBare, cmd::oid::ObjectId, errors::{GitError, GitResult}, }; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct BranchListItem { pub name: String, pub oid: ObjectId, pub is_head: bool, pub is_remote: bool, pub is_current: bool, pub upstream: Option, } impl GitBare { pub fn branch_list_all(&self) -> GitResult> { let repo = self.gix_repo()?; let head_name = repo.head_name()?.map(|n| n.shorten().to_string()); let mut items = Vec::new(); let platform = repo.references()?; let local_iter = platform.local_branches()?; for ref_result in local_iter { let reference = ref_result?; let name = reference.name().shorten().to_string(); let is_head = head_name.as_ref() == Some(&name); let is_current = is_head; let target = reference.target(); let target_id = target.try_id().ok_or_else(|| { GitError::Internal( "local branch has no direct target".to_string(), ) })?; let oid = ObjectId::new(target_id.to_hex().to_string()); let upstream = reference .remote_tracking_ref_name(gix::remote::Direction::Fetch) .and_then(|r| r.ok()) .map(|n| n.shorten().to_string()); items.push(BranchListItem { name, oid, is_head, is_remote: false, is_current, upstream, }); } let remote_iter = platform.remote_branches()?; for ref_result in remote_iter { let reference = ref_result?; let name = reference.name().shorten().to_string(); let is_head = head_name.as_ref() == Some(&name); let is_current = is_head; let target = reference.target(); let target_id = target.try_id().ok_or_else(|| { GitError::Internal( "remote branch has no direct target".to_string(), ) })?; let oid = ObjectId::new(target_id.to_hex().to_string()); items.push(BranchListItem { name, oid, is_head, is_remote: true, is_current, upstream: None, }); } Ok(items) } pub fn branch_info(&self, branch: String) -> GitResult { let repo = self.gix_repo()?; let head_name = repo.head_name()?.map(|n| n.shorten().to_string()); let local_ref_str = format!("refs/heads/{branch}"); let local_ref = repo.try_find_reference(local_ref_str.as_str())?; if let Some(reference) = local_ref { let name = reference.name().shorten().to_string(); let is_head = head_name.as_ref() == Some(&name); let target = reference.target(); let target_id = target.try_id().ok_or_else(|| { GitError::Internal("branch has no direct target".to_string()) })?; let oid = ObjectId::new(target_id.to_hex().to_string()); let upstream = reference .remote_tracking_ref_name(gix::remote::Direction::Fetch) .and_then(|r| r.ok()) .map(|n| n.shorten().to_string()); return Ok(BranchListItem { name, oid, is_head, is_remote: false, is_current: is_head, upstream, }); } let remote_ref_str = format!("refs/remotes/{branch}"); let remote_ref = repo.try_find_reference(remote_ref_str.as_str())?; if let Some(reference) = remote_ref { let name = reference.name().shorten().to_string(); let is_head = head_name.as_ref() == Some(&name); let target = reference.target(); let target_id = target.try_id().ok_or_else(|| { GitError::Internal("branch has no direct target".to_string()) })?; let oid = ObjectId::new(target_id.to_hex().to_string()); return Ok(BranchListItem { name, oid, is_head, is_remote: true, is_current: is_head, upstream: None, }); } Err(GitError::RefNotFound(branch)) } }