Skip to content

but: switch command#14278

Merged
estib-vega merged 1 commit into
masterfrom
but-switch-command
Jun 22, 2026
Merged

but: switch command#14278
estib-vega merged 1 commit into
masterfrom
but-switch-command

Conversation

@estib-vega

@estib-vega estib-vega commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Usage

Switch to a local branch, workspace branch ID, or the GitButler workspace.

Examples

Switch to a branch:
but switch my-feature

Switch back to the GitButler workspace:
but switch --workspace

Create a new branch at the target and switch to it (canned name if no name is specified):
but switch --new my-feature
but switch --new

@github-actions github-actions Bot added rust Pull requests that update Rust code CLI The command-line program `but` labels Jun 17, 2026
@estib-vega estib-vega force-pushed the but-switch-command branch from 9371b86 to 96871ac Compare June 17, 2026 15:11
@estib-vega

estib-vega commented Jun 17, 2026

Copy link
Copy Markdown
Contributor Author

Waiting on this other PR #14277 to be merged to fix the integration test issues.

Good to go

@estib-vega estib-vega requested review from davidpdrsn and slarse June 17, 2026 15:12
@estib-vega estib-vega force-pushed the but-switch-command branch from 96871ac to 8ca6d40 Compare June 17, 2026 15:15
@estib-vega estib-vega marked this pull request as ready for review June 17, 2026 15:15
Copilot AI review requested due to automatic review settings June 17, 2026 15:15

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new but switch subcommand to the but CLI, enabling users to switch to an existing local branch (by name, full ref, or workspace CLI ID), switch back to the GitButler workspace ref, or create-and-switch to a new branch. This fits into the CLI’s branching workflow alongside existing branch/workspace operations, and wires the command into help text and metrics.

Changes:

  • Introduces but switch command implementation and clap argument wiring (--workspace, --new, optional target).
  • Extends but-api branch checkout helpers with a workspace checkout entrypoint (ref-based checkout refactor).
  • Adds integration tests covering the main switch scenarios and several input validation cases.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
crates/but/tests/but/command/switch.rs New integration tests for but switch behavior (existing branch, workspace, new branch, invalid inputs).
crates/but/tests/but/command/mod.rs Registers the new switch test module.
crates/but/src/utils/metrics.rs Maps Subcommands::Switch to the Switch metrics command name.
crates/but/src/lib.rs Dispatches the Switch subcommand to command::switch::handle.
crates/but/src/command/switch.rs Implements but switch behavior (workspace checkout, new branch checkout, existing local branch resolution).
crates/but/src/command/mod.rs Exposes the new switch command module.
crates/but/src/command/help.rs Adds switch into grouped help output.
crates/but/src/args/mod.rs Defines the Switch clap subcommand, flags, argument grouping, and help text.
crates/but/src/args/metrics.rs Adds CommandName::Switch for telemetry identifiers.
crates/but/src/args/atoms/branch_arg.rs Gates try_resolve_stack behind legacy since atoms are now used outside legacy.
crates/but-api/src/branch.rs Adds workspace_checkout_with_perm and factors checkout into a shared ref-based helper.

Comment thread crates/but/tests/but/command/switch.rs Outdated
Comment thread crates/but/src/args/mod.rs Outdated
@estib-vega estib-vega force-pushed the but-switch-command branch from 8ca6d40 to 7baca4e Compare June 18, 2026 07:28
Copilot AI review requested due to automatic review settings June 18, 2026 07:46
@estib-vega estib-vega force-pushed the but-switch-command branch from 7baca4e to be4fea3 Compare June 18, 2026 07:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.

Comment thread crates/but/src/args/mod.rs Outdated
Comment on lines +1114 to +1115
/// Branch name, full local branch ref, or workspace CLI branch ID
target: Option<CliIdArg>,
Comment thread crates/but/src/args/mod.rs Outdated
Comment on lines +1119 to +1121
/// Create a new branch at the target and switch to it
#[clap(long = "new", short = 'n')]
new: bool,
Comment thread crates/but/tests/but/command/switch.rs Outdated
Comment on lines +182 to +185
.stderr_eq(str![[r#"
Error: Invalid branch. '94' is a commit

"#]]);
@estib-vega estib-vega force-pushed the but-switch-command branch from be4fea3 to 0b84953 Compare June 18, 2026 08:02
Copilot AI review requested due to automatic review settings June 18, 2026 08:31
@estib-vega estib-vega force-pushed the but-switch-command branch from 0b84953 to 95f8f4c Compare June 18, 2026 08:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.

Comment thread crates/but/src/command/switch.rs Outdated
Comment on lines +81 to +87
if matches.len() > 1 {
return Err(anyhow::anyhow!(
"Branch '{}' is ambiguous. Try using more characters to disambiguate.",
target.0
)
.into());
}
Comment thread crates/but/src/command/switch.rs Outdated
Comment on lines +62 to +74
if target.0.starts_with("refs/remotes/") || looks_like_remote_branch(&repo, &target.0) {
return Err(bad_input(format!(
"Can only switch to local branches, got '{}'",
target.0
))
.into());
}

if let Ok(short_name) = Category::LocalBranch.to_full_name(target.0.as_str())
&& repo.try_find_reference(short_name.as_ref())?.is_some()
{
return Ok(short_name);
}
@estib-vega

Copy link
Copy Markdown
Contributor Author

Notes for reviewer: This command should be hidden for now, even if we add it. Since switching away from the workspace and then running other actions will trigger an error until something like: #14276 lands

@slarse slarse left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks fine to add as a hidden command, but argument parsing should be consolidated with CLI atoms.

Note that hidden commands are added to auto completion, and users are likely to find switch very quickly. If we want it to be less discoverable, consider prepending an unusual character (e.g. _) to the command name until we're ready to release it to the masses.

Comment thread crates/but/src/command/switch.rs Outdated
Comment on lines +48 to +108
fn resolve_existing_local_branch(
ctx: &but_ctx::Context,
perm: &but_core::sync::RepoShared,
target: &CliIdArg,
) -> CliResult<FullName> {
let repo = ctx.repo.get()?;

if target.0.starts_with("refs/heads/") {
let full_name = FullName::try_from(target.0.as_str())
.map_err(|_| bad_input(format!("Invalid branch ref '{}'", target.0)))?;
ensure_existing_local_branch(&repo, &full_name)?;
return Ok(full_name);
}

if target.0.starts_with("refs/remotes/") || looks_like_remote_branch(&repo, &target.0) {
return Err(bad_input(format!(
"Can only switch to local branches, got '{}'",
target.0
))
.into());
}

if let Ok(short_name) = Category::LocalBranch.to_full_name(target.0.as_str())
&& repo.try_find_reference(short_name.as_ref())?.is_some()
{
return Ok(short_name);
}

let id_map = IdMap::new_from_context(ctx, None, perm)?;
let matches = id_map.parse_using_context(&target.0, ctx)?;
if matches.is_empty() {
return Err(bad_input(format!("Could not find branch: '{}'", target.0)).into());
}
if matches.len() > 1 {
return Err(anyhow::anyhow!(
"Branch '{}' is ambiguous. Try using more characters to disambiguate.",
target.0
)
.into());
}

match &matches[0] {
CliId::Branch { name, .. } => {
let branch = Category::LocalBranch.to_full_name(name.as_str())?;
ensure_existing_local_branch(&repo, &branch)?;
Ok(branch)
}
other => {
let kind = match other {
CliId::Branch { .. } => unreachable!("handled above"),
CliId::Commit { .. } => "a commit",
CliId::Uncommitted(..) => "an uncommitted file",
CliId::PathPrefix { .. } => "a path",
CliId::CommittedFile { .. } => "a committed file",
CliId::Unassigned { .. } => "unassigned changes",
CliId::Stack { .. } => "a stack",
};
Err(bad_input(format!("Invalid branch. '{}' is {kind}", target.0)).into())
}
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We try to contain this kind of resolution to CLI atoms, this duplicates lot of that code. I think BranchArg::resolve_branch() should do the trick, see if you can use that instead.

I don't know if we have any existing code path from CliIdArg all the way to an unapplied branch, but you should be able to cobble it together from CLI atoms, perhaps with some minimal extension.

@estib-vega

Copy link
Copy Markdown
Contributor Author

Looks fine to add as a hidden command, but argument parsing should be consolidated with CLI atoms.

Thanks! I'll address the changes and hide the command.

Add a command to switch to an new or existing ref. And back to the workspace.
@estib-vega estib-vega force-pushed the but-switch-command branch from 95f8f4c to 142012e Compare June 22, 2026 10:54
@estib-vega estib-vega enabled auto-merge June 22, 2026 10:55
@estib-vega

Copy link
Copy Markdown
Contributor Author

Addressed the feedback

@estib-vega estib-vega merged commit 6454170 into master Jun 22, 2026
39 checks passed
@estib-vega estib-vega deleted the but-switch-command branch June 22, 2026 11:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLI The command-line program `but` rust Pull requests that update Rust code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants