Skip to content

Commit c42f442

Browse files
committed
Add 'but branch unapply' command
Introduce a new ‘unapply' branch subcommand and implement unapply_branch to unapply a virtual branch (or branch by CLI ID/full name). This changeadds the CLI variant, wires the handler into main, and implements logic to resolve CLI IDs, locate the associated stack, call unapply_stack, and print user-facing messages.
1 parent d95023d commit c42f442

File tree

4 files changed

+173
-45
lines changed

4 files changed

+173
-45
lines changed

crates/but/src/args.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,12 @@ pub enum BranchSubcommands {
135135
/// Works with both virtual branches and regular Git branches.
136136
id: Option<String>,
137137
},
138+
/// Unapply a virtual branch.
139+
Unapply {
140+
/// Branch ID or branch name to unapply.
141+
/// Can be a 2-character CLI ID (e.g., "ab") or a full branch name.
142+
branch_id: String,
143+
},
138144
}
139145

140146
#[derive(Debug, Clone, Copy, clap::ValueEnum, Default)]

crates/but/src/branch/mod.rs

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use but_settings::AppSettings;
22
use colored::Colorize;
33
use gitbutler_branch::BranchCreateRequest;
4-
use gitbutler_branch_actions::{create_virtual_branch, create_virtual_branch_from_branch};
4+
use gitbutler_branch_actions::{create_virtual_branch, create_virtual_branch_from_branch, unapply_stack};
55
use gitbutler_command_context::CommandContext;
66
use gitbutler_project::Project;
77
use gitbutler_reference::Refname;
@@ -115,3 +115,86 @@ pub(crate) fn create_branch(
115115

116116
Ok(())
117117
}
118+
119+
pub(crate) fn unapply_branch(
120+
repo_path: &Path,
121+
_json: bool,
122+
branch_id: &str,
123+
) -> anyhow::Result<()> {
124+
let project = Project::from_path(repo_path)?;
125+
let mut ctx = CommandContext::open(&project, AppSettings::load_from_default_path_creating()?)?;
126+
127+
// Try to resolve the branch ID
128+
let cli_ids = CliId::from_str(&mut ctx, branch_id)?;
129+
130+
if cli_ids.is_empty() {
131+
return Err(anyhow::anyhow!(
132+
"Branch '{}' not found. Try using a branch CLI ID or full branch name.",
133+
branch_id
134+
));
135+
}
136+
137+
if cli_ids.len() > 1 {
138+
let matches: Vec<String> = cli_ids.iter().map(|id| {
139+
match id {
140+
CliId::Branch { name } => format!("{} (branch '{}')", id.to_string(), name),
141+
_ => format!("{} ({})", id.to_string(), id.kind())
142+
}
143+
}).collect();
144+
return Err(anyhow::anyhow!(
145+
"Branch '{}' is ambiguous. Matches: {}. Try using more characters or the full branch name.",
146+
branch_id,
147+
matches.join(", ")
148+
));
149+
}
150+
151+
let cli_id = &cli_ids[0];
152+
let stack_id = match cli_id {
153+
CliId::Branch { .. } => {
154+
// Find the stack ID for this branch
155+
let stacks = crate::log::stacks(&ctx)?;
156+
let stack = stacks.iter().find(|s| {
157+
s.heads.iter().any(|head| {
158+
if let CliId::Branch { name } = cli_id {
159+
head.name.to_string() == *name
160+
} else {
161+
false
162+
}
163+
})
164+
});
165+
166+
match stack {
167+
Some(s) => s.id.ok_or_else(|| anyhow::anyhow!("Stack has no ID"))?,
168+
None => return Err(anyhow::anyhow!("No stack found for branch '{}'", branch_id)),
169+
}
170+
}
171+
_ => {
172+
return Err(anyhow::anyhow!(
173+
"ID '{}' does not refer to a branch (it's {})",
174+
branch_id,
175+
cli_id.kind()
176+
));
177+
}
178+
};
179+
180+
let branch_name = match cli_id {
181+
CliId::Branch { name } => name,
182+
_ => unreachable!(),
183+
};
184+
185+
println!(
186+
"Unapplying branch '{}' ({})",
187+
branch_name.yellow().bold(),
188+
branch_id.blue().underline()
189+
);
190+
191+
unapply_stack(&ctx, stack_id, Vec::new())?;
192+
193+
println!(
194+
"{} Branch '{}' unapplied successfully!",
195+
"✓".green().bold(),
196+
branch_name.yellow().bold()
197+
);
198+
199+
Ok(())
200+
}

crates/but/src/main.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,9 @@ async fn main() -> Result<()> {
154154
BranchSubcommands::New { branch_name, id } => {
155155
branch::create_branch(&args.current_dir, args.json, branch_name, id.as_deref())
156156
}
157+
BranchSubcommands::Unapply { branch_id } => {
158+
branch::unapply_branch(&args.current_dir, args.json, branch_id)
159+
}
157160
},
158161
Subcommands::Rub { source, target } => {
159162
let result = rub::handle(&args.current_dir, args.json, source, target)

0 commit comments

Comments
 (0)