use git2::{BranchType, Repository};
use crate::error::{Error, Result};
#[derive(Debug)]
pub struct MergedBranch {
pub name: String,
}
#[derive(Debug)]
pub struct BranchReport {
pub merged_branches: Vec<MergedBranch>,
}
pub fn find_merged_branches(repo: &Repository) -> Result<BranchReport> {
let repo_path = repo.workdir().unwrap_or_else(|| repo.path()).to_path_buf();
let main_oid = match resolve_main_oid(repo) {
Some(oid) => oid,
None => {
return Ok(BranchReport {
merged_branches: Vec::new(),
});
}
};
let head_name = repo
.head()
.ok()
.and_then(|h| h.shorthand().map(|s| s.to_string()));
let branches = repo
.branches(Some(BranchType::Local))
.map_err(|e| Error::Git {
path: repo_path.clone(),
source: e,
})?;
let mut merged = Vec::new();
for branch_result in branches {
let (branch, _) = match branch_result {
Ok(b) => b,
Err(_) => continue,
};
let name = match branch.name() {
Ok(Some(n)) => n.to_string(),
_ => continue,
};
if name == "main" || name == "master" {
continue;
}
if Some(&name) == head_name.as_ref() {
continue;
}
let branch_oid = match branch.get().target() {
Some(oid) => oid,
None => continue,
};
if is_ancestor_of(repo, branch_oid, main_oid) {
merged.push(MergedBranch { name });
}
}
merged.sort_by(|a, b| a.name.cmp(&b.name));
Ok(BranchReport {
merged_branches: merged,
})
}
pub fn delete_branch(repo: &Repository, branch_name: &str) -> Result<()> {
let repo_path = repo.workdir().unwrap_or_else(|| repo.path()).to_path_buf();
let mut branch = repo
.find_branch(branch_name, BranchType::Local)
.map_err(|e| Error::Git {
path: repo_path.clone(),
source: e,
})?;
branch.delete().map_err(|e| Error::Git {
path: repo_path,
source: e,
})?;
Ok(())
}
fn resolve_main_oid(repo: &Repository) -> Option<git2::Oid> {
for name in &["main", "master"] {
if let Ok(branch) = repo.find_branch(name, BranchType::Local) {
if let Some(oid) = branch.get().target() {
return Some(oid);
}
}
}
None
}
fn is_ancestor_of(repo: &Repository, maybe_ancestor: git2::Oid, descendant: git2::Oid) -> bool {
if maybe_ancestor == descendant {
return true;
}
repo.merge_base(maybe_ancestor, descendant)
.map(|base| base == maybe_ancestor)
.unwrap_or(false)
}