From fd2f38c394359af9ee7a8fcb5f2ffb18c6bbb50c Mon Sep 17 00:00:00 2001 From: Essien Ita Essien <34972+essiene@users.noreply.github.com> Date: Wed, 18 Oct 2023 21:00:10 +0100 Subject: [PATCH] Implement a `rename` subcommand for the `branch` command. This is really a simple change that does the following in a transaction: * Set the new branch name to point to the same commit as the old branch name. * Set the old branch name to point to no commit (hence deleting the old name). Before it starts, it confirms that the new branch name is not already in use. --- CHANGELOG.md | 2 ++ cli/src/commands/branch.rs | 51 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b48d6c3787..04c5bf7af16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### New features * Information about new and resolved conflicts is now printed by every command. +* `jj branch` has gained a new `rename` subcommand that allows changing a branch + name atomically. `jj branch help rename` for details. ### Fixed bugs diff --git a/cli/src/commands/branch.rs b/cli/src/commands/branch.rs index 35a5a914482..9469ec1a9b1 100644 --- a/cli/src/commands/branch.rs +++ b/cli/src/commands/branch.rs @@ -34,6 +34,8 @@ pub enum BranchSubcommand { Forget(BranchForgetArgs), #[command(visible_alias("l"))] List(BranchListArgs), + #[command(visible_alias("r"))] + Rename(BranchRenameArgs), #[command(visible_alias("s"))] Set(BranchSetArgs), Track(BranchTrackArgs), @@ -122,6 +124,19 @@ pub struct BranchForgetArgs { pub glob: Vec, } +/// Rename `old` branch name to `new` branch name. +/// +/// The new branch name points at the same commit as the old +/// branch name. +#[derive(clap::Args, Clone, Debug)] +pub struct BranchRenameArgs { + /// The old name of the branch. + pub old: String, + + /// The new name of the branch. + pub new: String, +} + /// Update an existing branch to point to a certain commit. #[derive(clap::Args, Clone, Debug)] pub struct BranchSetArgs { @@ -248,6 +263,7 @@ pub fn cmd_branch( ) -> Result<(), CommandError> { match subcommand { BranchSubcommand::Create(sub_args) => cmd_branch_create(ui, command, sub_args), + BranchSubcommand::Rename(sub_args) => cmd_branch_rename(ui, command, sub_args), BranchSubcommand::Set(sub_args) => cmd_branch_set(ui, command, sub_args), BranchSubcommand::Delete(sub_args) => cmd_branch_delete(ui, command, sub_args), BranchSubcommand::Forget(sub_args) => cmd_branch_forget(ui, command, sub_args), @@ -301,6 +317,41 @@ fn cmd_branch_create( Ok(()) } +fn cmd_branch_rename( + ui: &mut Ui, + command: &CommandHelper, + args: &BranchRenameArgs, +) -> Result<(), CommandError> { + let mut workspace_command = command.workspace_helper(ui)?; + let repo = workspace_command.repo().as_ref(); + let old_branch = &args.old; + // call clone because .set_local_branch_target takes ownership + // of the target_commit, so we need to create a copy that is no + // longer owned by the workspace. + let target_commit = repo.view().get_local_branch(old_branch).clone(); + if target_commit.is_absent() { + return Err(user_error_with_hint(format!( + "No such branch: {old_branch}" + ))); + } + + let new_branch = &args.new; + let mut tx = workspace_command.start_transaction(); + tx.mut_repo() + .set_local_branch_target(new_branch, target_commit); + tx.mut_repo() + .set_local_branch_target(old_branch, RefTarget::absent()); + tx.finish( + ui, + format!( + "rename {} to {}", + make_branch_term(&[old_branch]), + make_branch_term(&[new_branch]), + ), + )?; + Ok(()) +} + fn cmd_branch_set( ui: &mut Ui, command: &CommandHelper,