From b24a85f87914eefa79dda46bad40a3c909b73400 Mon Sep 17 00:00:00 2001 From: Antoine Cezar Date: Sun, 29 Oct 2023 22:42:56 +0100 Subject: [PATCH] commands: move prev code to prev.rs --- cli/src/commands/mod.rs | 105 +-------------------------------- cli/src/commands/prev.rs | 124 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+), 102 deletions(-) create mode 100644 cli/src/commands/prev.rs diff --git a/cli/src/commands/mod.rs b/cli/src/commands/mod.rs index 3a9c21714..d0a6cbcfc 100644 --- a/cli/src/commands/mod.rs +++ b/cli/src/commands/mod.rs @@ -38,6 +38,7 @@ mod r#move; mod new; mod next; mod operation; +mod prev; mod rebase; mod resolve; @@ -125,7 +126,7 @@ enum Commands { #[command(subcommand)] #[command(visible_alias = "op")] Operation(operation::OperationCommands), - Prev(PrevArgs), + Prev(prev::PrevArgs), Rebase(rebase::RebaseArgs), Resolve(resolve::ResolveArgs), Restore(RestoreArgs), @@ -215,42 +216,6 @@ struct ObslogArgs { diff_format: DiffFormatArgs, } -/// Move the working copy commit to the parent of the current revision. -/// -/// -/// The command moves you to the parent in a linear fashion. -/// -/// -/// D @ D -/// |/ | -/// A => A @ -/// | | / -/// B B -/// -/// -/// If `--edit` is passed, it will move the working copy commit -/// directly to the parent. -/// -/// -/// D @ D -/// |/ | -/// C => @ -/// | | -/// B B -/// | | -/// A A -// TODO(#2126): Handle multiple parents, e.g merges. -#[derive(clap::Args, Clone, Debug)] -#[command(verbatim_doc_comment)] -struct PrevArgs { - /// How many revisions to move backward. By default moves to the parent. - #[arg(default_value = "1")] - amount: u64, - /// Edit the parent directly, instead of moving the working-copy commit. - #[arg(long)] - edit: bool, -} - /// Move changes from a revision into its parent /// /// After moving the changes into the parent, the child revision will have the @@ -979,70 +944,6 @@ fn edit_sparse( .try_collect() } -fn cmd_prev(ui: &mut Ui, command: &CommandHelper, args: &PrevArgs) -> Result<(), CommandError> { - let mut workspace_command = command.workspace_helper(ui)?; - let edit = args.edit; - let amount = args.amount; - let current_wc_id = workspace_command - .get_wc_commit_id() - .ok_or_else(|| user_error("This command requires a working copy"))?; - let current_wc = workspace_command.repo().store().get_commit(current_wc_id)?; - let current_short = short_commit_hash(current_wc.id()); - let start_id = if edit { - current_wc_id - } else { - match current_wc.parent_ids() { - [parent_id] => parent_id, - _ => return Err(user_error("Cannot run `jj prev` on a merge commit")), - } - }; - let ancestor_expression = RevsetExpression::commit(start_id.clone()).ancestors_at(amount); - let target_revset = if edit { - ancestor_expression - } else { - // Jujutsu will always create a new commit for prev, even where Mercurial cannot - // and fails. The decision and all discussion around it are available - // here: https://github.com/martinvonz/jj/pull/1200#discussion_r1298623933 - // - // If users ever request erroring out, add `.ancestors()` to the revset below. - ancestor_expression.minus(&RevsetExpression::commit(current_wc_id.clone())) - }; - let targets: Vec<_> = target_revset - .resolve(workspace_command.repo().as_ref())? - .evaluate(workspace_command.repo().as_ref())? - .iter() - .commits(workspace_command.repo().store()) - .take(2) - .try_collect()?; - let target = match targets.as_slice() { - [target] => target, - [] => { - return Err(user_error(format!( - "No ancestor found {amount} commit{} back", - if amount > 1 { "s" } else { "" } - ))) - } - _ => return Err(user_error("Ambiguous target commit")), - }; - // Generate a short commit hash, to make it readable in the op log. - let target_short = short_commit_hash(target.id()); - // If we're editing, just move to the revision directly. - if edit { - // The target must be rewritable if we're editing. - workspace_command.check_rewritable([target])?; - let mut tx = workspace_command - .start_transaction(&format!("prev: {current_short} -> editing {target_short}")); - tx.edit(target)?; - tx.finish(ui)?; - return Ok(()); - } - let mut tx = - workspace_command.start_transaction(&format!("prev: {current_short} -> {target_short}")); - tx.check_out(target)?; - tx.finish(ui)?; - Ok(()) -} - fn combine_messages( repo: &ReadonlyRepo, source: &Commit, @@ -1874,8 +1775,8 @@ pub fn run_command(ui: &mut Ui, command_helper: &CommandHelper) -> Result<(), Co Commands::Duplicate(sub_args) => duplicate::cmd_duplicate(ui, command_helper, sub_args), Commands::Abandon(sub_args) => abandon::cmd_abandon(ui, command_helper, sub_args), Commands::Edit(sub_args) => edit::cmd_edit(ui, command_helper, sub_args), - Commands::Prev(sub_args) => cmd_prev(ui, command_helper, sub_args), Commands::Next(sub_args) => next::cmd_next(ui, command_helper, sub_args), + Commands::Prev(sub_args) => prev::cmd_prev(ui, command_helper, sub_args), Commands::New(sub_args) => new::cmd_new(ui, command_helper, sub_args), Commands::Move(sub_args) => r#move::cmd_move(ui, command_helper, sub_args), Commands::Squash(sub_args) => cmd_squash(ui, command_helper, sub_args), diff --git a/cli/src/commands/prev.rs b/cli/src/commands/prev.rs new file mode 100644 index 000000000..bed00d10b --- /dev/null +++ b/cli/src/commands/prev.rs @@ -0,0 +1,124 @@ +// Copyright 2020 The Jujutsu Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use itertools::Itertools; +use jj_lib::repo::Repo; +use jj_lib::revset::{RevsetExpression, RevsetIteratorExt}; + +use crate::cli_util::{short_commit_hash, user_error, CommandError, CommandHelper}; +use crate::ui::Ui; + +/// Move the working copy commit to the parent of the current revision. +/// +/// +/// The command moves you to the parent in a linear fashion. +/// +/// +/// D @ D +/// |/ | +/// A => A @ +/// | | / +/// B B +/// +/// +/// If `--edit` is passed, it will move the working copy commit +/// directly to the parent. +/// +/// +/// D @ D +/// |/ | +/// C => @ +/// | | +/// B B +/// | | +/// A A +// TODO(#2126): Handle multiple parents, e.g merges. +#[derive(clap::Args, Clone, Debug)] +#[command(verbatim_doc_comment)] +pub(crate) struct PrevArgs { + /// How many revisions to move backward. By default moves to the parent. + #[arg(default_value = "1")] + amount: u64, + /// Edit the parent directly, instead of moving the working-copy commit. + #[arg(long)] + edit: bool, +} + +pub(crate) fn cmd_prev( + ui: &mut Ui, + command: &CommandHelper, + args: &PrevArgs, +) -> Result<(), CommandError> { + let mut workspace_command = command.workspace_helper(ui)?; + let edit = args.edit; + let amount = args.amount; + let current_wc_id = workspace_command + .get_wc_commit_id() + .ok_or_else(|| user_error("This command requires a working copy"))?; + let current_wc = workspace_command.repo().store().get_commit(current_wc_id)?; + let current_short = short_commit_hash(current_wc.id()); + let start_id = if edit { + current_wc_id + } else { + match current_wc.parent_ids() { + [parent_id] => parent_id, + _ => return Err(user_error("Cannot run `jj prev` on a merge commit")), + } + }; + let ancestor_expression = RevsetExpression::commit(start_id.clone()).ancestors_at(amount); + let target_revset = if edit { + ancestor_expression + } else { + // Jujutsu will always create a new commit for prev, even where Mercurial cannot + // and fails. The decision and all discussion around it are available + // here: https://github.com/martinvonz/jj/pull/1200#discussion_r1298623933 + // + // If users ever request erroring out, add `.ancestors()` to the revset below. + ancestor_expression.minus(&RevsetExpression::commit(current_wc_id.clone())) + }; + let targets: Vec<_> = target_revset + .resolve(workspace_command.repo().as_ref())? + .evaluate(workspace_command.repo().as_ref())? + .iter() + .commits(workspace_command.repo().store()) + .take(2) + .try_collect()?; + let target = match targets.as_slice() { + [target] => target, + [] => { + return Err(user_error(format!( + "No ancestor found {amount} commit{} back", + if amount > 1 { "s" } else { "" } + ))) + } + _ => return Err(user_error("Ambiguous target commit")), + }; + // Generate a short commit hash, to make it readable in the op log. + let target_short = short_commit_hash(target.id()); + // If we're editing, just move to the revision directly. + if edit { + // The target must be rewritable if we're editing. + workspace_command.check_rewritable([target])?; + let mut tx = workspace_command + .start_transaction(&format!("prev: {current_short} -> editing {target_short}")); + tx.edit(target)?; + tx.finish(ui)?; + return Ok(()); + } + let mut tx = + workspace_command.start_transaction(&format!("prev: {current_short} -> {target_short}")); + tx.check_out(target)?; + tx.finish(ui)?; + Ok(()) +}