1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
//! Branch deletion logic for worktree operations.
//!
//! This module handles the decision-making around whether a branch can be safely
//! deleted after its worktree is removed. It checks if the branch's content has
//! been integrated into the target branch.
use worktrunk::git::{IntegrationReason, Repository};
/// Outcome of a branch deletion attempt.
pub enum BranchDeletionOutcome {
/// Branch was not deleted (not integrated and not forced)
NotDeleted,
/// Branch was force-deleted without integration check
ForceDeleted,
/// Branch was deleted because it was integrated
Integrated(IntegrationReason),
}
/// Result of a branch deletion attempt.
pub struct BranchDeletionResult {
pub outcome: BranchDeletionOutcome,
/// The target that was actually checked against (may be upstream if ahead of local)
pub integration_target: String,
}
/// Attempt to delete a branch if it's integrated or force_delete is set.
///
/// Returns `BranchDeletionResult` with:
/// - `outcome`: Whether/why deletion occurred
/// - `integration_target`: The ref checked against (may be upstream if ahead of local)
pub fn delete_branch_if_safe(
repo: &Repository,
branch_name: &str,
target: &str,
force_delete: bool,
) -> anyhow::Result<BranchDeletionResult> {
// Force-delete: skip integration check entirely (matches compute_integration_reason
// behavior for the Worktree path). The user explicitly chose -D.
if force_delete {
repo.run_command(&["branch", "-D", branch_name])?;
return Ok(BranchDeletionResult {
outcome: BranchDeletionOutcome::ForceDeleted,
integration_target: target.to_string(),
});
}
let (effective_target, reason) = repo.integration_reason(branch_name, target)?;
let outcome = match reason {
Some(r) => {
repo.run_command(&["branch", "-D", branch_name])?;
BranchDeletionOutcome::Integrated(r)
}
None => BranchDeletionOutcome::NotDeleted,
};
Ok(BranchDeletionResult {
outcome,
integration_target: effective_target,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_branch_deletion_outcome_matching() {
// Ensure the match patterns work correctly
let outcomes = [
(BranchDeletionOutcome::NotDeleted, false),
(BranchDeletionOutcome::ForceDeleted, true),
(
BranchDeletionOutcome::Integrated(IntegrationReason::SameCommit),
true,
),
];
for (outcome, expected_deleted) in outcomes {
let deleted = matches!(
outcome,
BranchDeletionOutcome::ForceDeleted | BranchDeletionOutcome::Integrated(_)
);
assert_eq!(deleted, expected_deleted);
}
}
}