use anyhow::{Context, Result, bail};
use git2::Repository;
use crate::core::msg;
use crate::core::repo;
use crate::core::weave::{self, Weave};
use crate::git;
use super::{should_weave, warn_if_hidden};
pub fn run(name: Option<String>, target: Option<String>) -> Result<()> {
let repo = repo::open_repo()?;
let workdir = repo::require_workdir(&repo, "create branch")?;
let name = match name {
Some(n) => n,
None => msg::input("Branch name", |s| {
if s.trim().is_empty() {
Err("Branch name cannot be empty")
} else {
Ok(())
}
})?,
};
let name = name.trim().to_string();
if name.is_empty() {
bail!("Branch name cannot be empty");
}
git::branch_validate_name(&name)?;
repo::ensure_branch_not_exists(&repo, &name)?;
let info = repo::gather_repo_info(&repo, false, 1).ok();
let commit_hash = resolve_commit(&repo, &info, target.as_deref())?;
git::branch_create(workdir, &name, &commit_hash)?;
warn_if_hidden(&repo, &name);
msg::success(&format!(
"Created branch `{}` at `{}`",
name,
git::short_hash(&commit_hash)
));
if let Some(ref info) = info
&& should_weave(info, &repo, &commit_hash)?
{
let mut graph = Weave::from_repo(&repo)?;
graph.weave_branch(&name);
let todo = graph.to_todo();
if let Err(e) =
weave::run_rebase_or_abort(workdir, Some(&graph.base_oid.to_string()), &todo)
{
let _ = git::branch_delete(workdir, &name);
return Err(e);
}
msg::success(&format!("Woven `{}` into integration branch", name));
}
Ok(())
}
fn resolve_commit(
repo: &Repository,
info: &Option<repo::RepoInfo>,
target: Option<&str>,
) -> Result<String> {
match target {
None => {
let info = info
.as_ref()
.ok_or_else(|| anyhow::anyhow!("No upstream tracking branch — cannot determine merge-base\nSpecify an explicit target commit"))?;
Ok(info.upstream.merge_base_oid.to_string())
}
Some(t) => {
let resolved = repo::resolve_arg(
repo,
t,
&[repo::TargetKind::Commit, repo::TargetKind::Branch],
)?;
match resolved {
repo::Target::Commit(hash) => Ok(hash),
repo::Target::Branch(name) => {
let branch = repo.find_branch(&name, git2::BranchType::Local)?;
let oid = branch
.get()
.target()
.context("Branch does not point to a commit")?;
Ok(oid.to_string())
}
_ => unreachable!(),
}
}
}
}