use anyhow::{Result, bail};
use clap::ArgAction;
use crate::cli::PushMode;
use crate::commands::Run;
use crate::commands::sync::sync;
use crate::prompt::confirm;
use crate::providers::{ReviewState, detect_provider, review_provider};
use crate::settings;
use crate::stack;
#[derive(Debug, clap::Args)]
pub struct Merge {
#[arg(long, action = ArgAction::SetTrue)]
dry_run: bool,
#[arg(long, short = 'y', action = ArgAction::SetTrue)]
yes: bool,
}
impl Run for Merge {
fn run(self) -> Result<()> {
merge(self.dry_run, self.yes)
}
}
fn merge(dry_run: bool, yes: bool) -> Result<()> {
let current = crate::git::current_branch()?;
let root = stack::stack_root(¤t)?;
let trunk = stack::trunk_branch(&crate::git::local_branches()?);
let Some(bottom) = stack::branch_and_descendants(&root)?
.into_iter()
.find(|branch| Some(branch) != trunk.as_ref())
else {
bail!("no stacked branches to merge");
};
let provider = detect_provider()?;
let review_provider = review_provider(provider.kind);
let Some(review) = review_provider.review_for_branch(&bottom)? else {
bail!(
"no {} review found for {bottom}; submit the stack first",
provider.kind
);
};
if review.state != ReviewState::Open {
bail!(
"review {} for {bottom} is {}, not open",
review.id,
review.state
);
}
let expected_base = stack::parent_for_branch(&bottom)?;
if let Some(expected) = &expected_base
&& *expected != review.base
{
bail!(
"review {} targets {}, but {bottom}'s stack parent is {expected}; \
run `git stk submit` first",
review.id,
review.base
);
}
let strategy = settings::merge_strategy()?;
let label = if review.title.is_empty() {
review.id.clone()
} else {
format!("{} ({})", review.title, review.id)
};
if dry_run {
println!("would merge {label} into {} ({strategy})", review.base);
println!("would sync afterwards");
return Ok(());
}
if !yes
&& !confirm(&format!(
"merge {label} into {} ({strategy})? [y/N] ",
review.base
))?
{
println!("merge cancelled");
return Ok(());
}
let output = review_provider.merge_review(&review, &strategy)?;
if !output.is_empty() {
println!("{output}");
}
println!("merged {label}");
sync(false, PushMode::Config)
}