gitdataai/lib/git/cmd/branch/branch_list.rs
2026-05-30 01:38:40 +08:00

134 lines
4.5 KiB
Rust

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<String>,
}
impl GitBare {
pub fn branch_list_all(&self) -> GitResult<Vec<BranchListItem>> {
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<BranchListItem> {
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))
}
}