use std::path::Path;
use worktrunk::HookType;
use worktrunk::config::UserConfig;
use worktrunk::git::{BranchDeletionMode, Repository};
use worktrunk::styling::{eprintln, info_message};
use super::resolve::path_mismatch;
use super::types::RemoveResult;
use crate::commands::command_executor::CommandContext;
use crate::commands::context::CommandEnv;
use crate::commands::hook_plan::{ApprovedHookPlan, register_planned};
use crate::commands::hooks::HookAnnouncer;
use crate::commands::repository_ext::{check_not_default_branch, is_primary_worktree};
use crate::commands::template_vars::TemplateVars;
use crate::output::{
BackgroundFallbackMode, handle_remove_output, post_hook_display_path, pre_hook_display_path,
};
pub struct FinishAfterMergeArgs<'a> {
pub current_branch: &'a str,
pub target_branch: &'a str,
pub target_worktree_path: Option<&'a Path>,
pub remove: bool,
pub verify: bool,
pub yes: bool,
pub plan: &'a ApprovedHookPlan,
}
pub fn finish_after_merge(
repo: &Repository,
config: &UserConfig,
env: &CommandEnv,
announcer: &mut HookAnnouncer<'_>,
args: FinishAfterMergeArgs<'_>,
) -> anyhow::Result<bool> {
let FinishAfterMergeArgs {
current_branch,
target_branch,
target_worktree_path,
remove,
verify,
yes,
plan,
} = args;
let on_target = current_branch == target_branch;
let destination_path = match target_worktree_path {
Some(path) => path.to_path_buf(),
None => repo.home_path()?,
};
let mut feature_vars = TemplateVars::new().with_active_worktree(&env.worktree_path);
let feature_commit = if verify || remove {
repo.current_worktree()
.run_command(&["rev-parse", "HEAD"])
.ok()
.map(|s| s.trim().to_string())
} else {
None
};
if let Some(commit) = feature_commit.as_deref() {
let short = repo
.short_sha(commit)
.unwrap_or_else(|_| commit.to_string());
feature_vars = feature_vars.with_active_commit(commit, &short);
}
let removed = if !remove {
eprintln!("{}", info_message("Worktree preserved (--no-remove)"));
false
} else if on_target {
eprintln!(
"{}",
info_message("Worktree preserved (already on target branch)")
);
false
} else if is_primary_worktree(repo)? {
eprintln!("{}", info_message("Worktree preserved (primary worktree)"));
false
} else {
check_not_default_branch(repo, current_branch, &BranchDeletionMode::SafeDelete)?;
let current_wt = repo.current_worktree();
current_wt.ensure_clean("remove worktree after merge", Some(current_branch), false)?;
let worktree_root = current_wt.root()?;
let expected_path = path_mismatch(repo, current_branch, &worktree_root, config);
let remove_result = RemoveResult::RemovedWorktree {
main_path: destination_path.clone(),
worktree_path: worktree_root,
changed_directory: true,
branch_name: Some(current_branch.to_string()),
deletion_mode: BranchDeletionMode::SafeDelete,
target_branch: Some(target_branch.to_string()),
force_worktree: false,
expected_path,
removed_commit: feature_commit.clone(),
};
handle_remove_output(
&remove_result,
false,
plan,
false,
false,
announcer,
BackgroundFallbackMode::Detached,
)?;
true
};
if verify {
let dest_repo = Repository::at(&destination_path)?;
let ctx = CommandContext::new(
&dest_repo,
config,
Some(current_branch),
&destination_path,
yes,
);
let display_path = if removed {
post_hook_display_path(&destination_path)
} else {
pre_hook_display_path(&destination_path)
};
let mut vars = feature_vars.with_target(target_branch);
if let Some(p) = target_worktree_path {
vars = vars.with_target_worktree_path(p);
}
register_planned(
announcer,
plan,
&destination_path,
&ctx,
HookType::PostMerge,
&vars.as_extra_vars(),
display_path,
)?;
}
Ok(removed)
}