From d3789cb28d2775c7ef9f5cd95657c80810c2d19a Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Tue, 17 Jan 2023 21:43:28 +0100 Subject: [PATCH] refactor: extract operation commands into commands/operation module --- src/commands/mod.rs | 199 +------------------------------------- src/commands/operation.rs | 199 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 204 insertions(+), 194 deletions(-) create mode 100644 src/commands/operation.rs diff --git a/src/commands/mod.rs b/src/commands/mod.rs index af23aea77..c468a35fb 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -14,6 +14,7 @@ mod branch; mod git; +mod operation; use std::collections::{BTreeMap, HashSet}; use std::fmt::Debug; @@ -32,7 +33,6 @@ use jujutsu_lib::dag_walk::topo_order_reverse; use jujutsu_lib::index::IndexEntry; use jujutsu_lib::matchers::EverythingMatcher; use jujutsu_lib::op_store::WorkspaceId; -use jujutsu_lib::operation::Operation; use jujutsu_lib::repo::ReadonlyRepo; use jujutsu_lib::repo_path::RepoPath; use jujutsu_lib::revset::{RevsetAliasesMap, RevsetExpression}; @@ -55,10 +55,6 @@ use crate::diff_util::{self, DiffFormat, DiffFormatArgs}; use crate::formatter::{Formatter, PlainTextFormatter}; use crate::graphlog::{get_graphlog, Edge}; use crate::template_parser::TemplateParser; -use crate::templater::Template; -use crate::time_util::{ - format_absolute_timestamp, format_duration, format_timestamp_relative_to_now, -}; use crate::ui::Ui; #[derive(clap::Parser, Clone, Debug)] @@ -106,10 +102,10 @@ enum Commands { #[command(subcommand)] Branch(branch::BranchSubcommand), /// Undo an operation (shortcut for `jj op undo`) - Undo(OperationUndoArgs), + Undo(operation::OperationUndoArgs), #[command(subcommand)] #[command(visible_alias = "op")] - Operation(OperationCommands), + Operation(operation::OperationCommands), #[command(subcommand)] Workspace(WorkspaceCommands), Sparse(SparseArgs), @@ -741,36 +737,6 @@ struct BackoutArgs { destination: Vec, } -/// Commands for working with the operation log -/// -/// Commands for working with the operation log. For information about the -/// operation log, see https://github.com/martinvonz/jj/blob/main/docs/operation-log.md. -#[derive(Subcommand, Clone, Debug)] -enum OperationCommands { - Log(OperationLogArgs), - Undo(OperationUndoArgs), - Restore(OperationRestoreArgs), -} - -/// Show the operation log -#[derive(clap::Args, Clone, Debug)] -struct OperationLogArgs {} - -/// Restore to the state at an operation -#[derive(clap::Args, Clone, Debug)] -struct OperationRestoreArgs { - /// The operation to restore to - operation: String, -} - -/// Undo an operation -#[derive(clap::Args, Clone, Debug)] -struct OperationUndoArgs { - /// The operation to undo - #[arg(default_value = "@")] - operation: String, -} - /// Commands for working with workspaces #[derive(Subcommand, Clone, Debug)] enum WorkspaceCommands { @@ -3035,161 +3001,6 @@ fn cmd_debug( Ok(()) } -fn cmd_op_log( - ui: &mut Ui, - command: &CommandHelper, - _args: &OperationLogArgs, -) -> Result<(), CommandError> { - let workspace_command = command.workspace_helper(ui)?; - let repo = workspace_command.repo(); - let head_op = repo.operation().clone(); - let head_op_id = head_op.id().clone(); - ui.request_pager(); - let mut formatter = ui.stdout_formatter(); - let mut formatter = formatter.as_mut(); - struct OpTemplate { - relative_timestamps: bool, - } - impl Template for OpTemplate { - fn format(&self, op: &Operation, formatter: &mut dyn Formatter) -> io::Result<()> { - // TODO: Make this templated - write!(formatter.labeled("id"), "{}", &op.id().hex()[0..12])?; - formatter.write_str(" ")?; - let metadata = &op.store_operation().metadata; - write!( - formatter.labeled("user"), - "{}@{}", - metadata.username, - metadata.hostname - )?; - formatter.write_str(" ")?; - formatter.with_label("time", |formatter| { - formatter.write_str( - &(if self.relative_timestamps { - let mut f = timeago::Formatter::new(); - f.min_unit(timeago::TimeUnit::Microseconds).ago(""); - let mut duration = - format_duration(&metadata.start_time, &metadata.end_time, &f); - if duration == "now" { - duration = "less than a microsecond".to_string() - } - let start = format_timestamp_relative_to_now(&metadata.start_time); - format!("{start}, lasted {duration}",) - } else { - format!( - "{} - {}", - format_absolute_timestamp(&metadata.start_time), - format_absolute_timestamp(&metadata.end_time) - ) - }), - ) - })?; - formatter.write_str("\n")?; - write!( - formatter.labeled("description"), - "{}", - &metadata.description - )?; - for (key, value) in &metadata.tags { - write!(formatter.labeled("tags"), "\n{key}: {value}")?; - } - Ok(()) - } - } - let template = OpTemplate { - relative_timestamps: command.settings().relative_timestamps(), - }; - - let mut graph = get_graphlog(command.settings(), &mut formatter); - for op in topo_order_reverse( - vec![head_op], - Box::new(|op: &Operation| op.id().clone()), - Box::new(|op: &Operation| op.parents()), - ) { - let mut edges = vec![]; - for parent in op.parents() { - edges.push(Edge::direct(parent.id().clone())); - } - let is_head_op = op.id() == &head_op_id; - let mut buffer = vec![]; - { - let mut formatter = ui.new_formatter(&mut buffer); - formatter.with_label("op-log", |formatter| { - if is_head_op { - formatter.with_label("head", |formatter| template.format(&op, formatter)) - } else { - template.format(&op, formatter) - } - })?; - } - if !buffer.ends_with(b"\n") { - buffer.push(b'\n'); - } - let node_symbol = if is_head_op { "@" } else { "o" }; - graph.add_node( - op.id(), - &edges, - node_symbol, - &String::from_utf8_lossy(&buffer), - )?; - } - - Ok(()) -} - -fn cmd_op_undo( - ui: &mut Ui, - command: &CommandHelper, - args: &OperationUndoArgs, -) -> Result<(), CommandError> { - let mut workspace_command = command.workspace_helper(ui)?; - let bad_op = workspace_command.resolve_single_op(&args.operation)?; - let parent_ops = bad_op.parents(); - if parent_ops.len() > 1 { - return Err(user_error("Cannot undo a merge operation")); - } - if parent_ops.is_empty() { - return Err(user_error("Cannot undo repo initialization")); - } - - let mut tx = - workspace_command.start_transaction(&format!("undo operation {}", bad_op.id().hex())); - let repo_loader = tx.base_repo().loader(); - let bad_repo = repo_loader.load_at(&bad_op); - let parent_repo = repo_loader.load_at(&parent_ops[0]); - tx.mut_repo().merge(&bad_repo, &parent_repo); - workspace_command.finish_transaction(ui, tx)?; - - Ok(()) -} - -fn cmd_op_restore( - ui: &mut Ui, - command: &CommandHelper, - args: &OperationRestoreArgs, -) -> Result<(), CommandError> { - let mut workspace_command = command.workspace_helper(ui)?; - let target_op = workspace_command.resolve_single_op(&args.operation)?; - let mut tx = workspace_command - .start_transaction(&format!("restore to operation {}", target_op.id().hex())); - tx.mut_repo().set_view(target_op.view().take_store_view()); - workspace_command.finish_transaction(ui, tx)?; - - Ok(()) -} - -fn cmd_operation( - ui: &mut Ui, - command: &CommandHelper, - subcommand: &OperationCommands, -) -> Result<(), CommandError> { - match subcommand { - OperationCommands::Log(command_matches) => cmd_op_log(ui, command, command_matches), - OperationCommands::Restore(command_matches) => cmd_op_restore(ui, command, command_matches), - OperationCommands::Undo(command_matches) => cmd_op_undo(ui, command, command_matches), - } -} - fn cmd_workspace( ui: &mut Ui, command: &CommandHelper, @@ -3456,8 +3267,8 @@ pub fn run_command( Commands::Backout(sub_args) => cmd_backout(ui, command_helper, sub_args), Commands::Resolve(sub_args) => cmd_resolve(ui, command_helper, sub_args), Commands::Branch(sub_args) => branch::cmd_branch(ui, command_helper, sub_args), - Commands::Undo(sub_args) => cmd_op_undo(ui, command_helper, sub_args), - Commands::Operation(sub_args) => cmd_operation(ui, command_helper, sub_args), + Commands::Undo(sub_args) => operation::cmd_op_undo(ui, command_helper, sub_args), + Commands::Operation(sub_args) => operation::cmd_operation(ui, command_helper, sub_args), Commands::Workspace(sub_args) => cmd_workspace(ui, command_helper, sub_args), Commands::Sparse(sub_args) => cmd_sparse(ui, command_helper, sub_args), Commands::Git(sub_args) => git::cmd_git(ui, command_helper, sub_args), diff --git a/src/commands/operation.rs b/src/commands/operation.rs new file mode 100644 index 000000000..9511c1e1e --- /dev/null +++ b/src/commands/operation.rs @@ -0,0 +1,199 @@ +use std::io; + +use clap::Subcommand; +use jujutsu_lib::dag_walk::topo_order_reverse; +use jujutsu_lib::operation::Operation; + +use crate::cli_util::{user_error, CommandError, CommandHelper}; +use crate::formatter::Formatter; +use crate::graphlog::{get_graphlog, Edge}; +use crate::templater::Template; +use crate::time_util::{ + format_absolute_timestamp, format_duration, format_timestamp_relative_to_now, +}; +use crate::ui::Ui; + +/// Commands for working with the operation log +/// +/// Commands for working with the operation log. For information about the +/// operation log, see https://github.com/martinvonz/jj/blob/main/docs/operation-log.md. +#[derive(Subcommand, Clone, Debug)] +pub enum OperationCommands { + Log(OperationLogArgs), + Undo(OperationUndoArgs), + Restore(OperationRestoreArgs), +} + +/// Show the operation log +#[derive(clap::Args, Clone, Debug)] +pub struct OperationLogArgs {} + +/// Restore to the state at an operation +#[derive(clap::Args, Clone, Debug)] +pub struct OperationRestoreArgs { + /// The operation to restore to + operation: String, +} + +/// Undo an operation +#[derive(clap::Args, Clone, Debug)] +pub struct OperationUndoArgs { + /// The operation to undo + #[arg(default_value = "@")] + operation: String, +} + +fn cmd_op_log( + ui: &mut Ui, + command: &CommandHelper, + _args: &OperationLogArgs, +) -> Result<(), CommandError> { + let workspace_command = command.workspace_helper(ui)?; + let repo = workspace_command.repo(); + let head_op = repo.operation().clone(); + let head_op_id = head_op.id().clone(); + ui.request_pager(); + let mut formatter = ui.stdout_formatter(); + let mut formatter = formatter.as_mut(); + struct OpTemplate { + relative_timestamps: bool, + } + impl Template for OpTemplate { + fn format(&self, op: &Operation, formatter: &mut dyn Formatter) -> io::Result<()> { + // TODO: Make this templated + write!(formatter.labeled("id"), "{}", &op.id().hex()[0..12])?; + formatter.write_str(" ")?; + let metadata = &op.store_operation().metadata; + write!( + formatter.labeled("user"), + "{}@{}", + metadata.username, + metadata.hostname + )?; + formatter.write_str(" ")?; + formatter.with_label("time", |formatter| { + formatter.write_str( + &(if self.relative_timestamps { + let mut f = timeago::Formatter::new(); + f.min_unit(timeago::TimeUnit::Microseconds).ago(""); + let mut duration = + format_duration(&metadata.start_time, &metadata.end_time, &f); + if duration == "now" { + duration = "less than a microsecond".to_string() + } + let start = format_timestamp_relative_to_now(&metadata.start_time); + format!("{start}, lasted {duration}",) + } else { + format!( + "{} - {}", + format_absolute_timestamp(&metadata.start_time), + format_absolute_timestamp(&metadata.end_time) + ) + }), + ) + })?; + formatter.write_str("\n")?; + write!( + formatter.labeled("description"), + "{}", + &metadata.description + )?; + for (key, value) in &metadata.tags { + write!(formatter.labeled("tags"), "\n{key}: {value}")?; + } + Ok(()) + } + } + let template = OpTemplate { + relative_timestamps: command.settings().relative_timestamps(), + }; + + let mut graph = get_graphlog(command.settings(), &mut formatter); + for op in topo_order_reverse( + vec![head_op], + Box::new(|op: &Operation| op.id().clone()), + Box::new(|op: &Operation| op.parents()), + ) { + let mut edges = vec![]; + for parent in op.parents() { + edges.push(Edge::direct(parent.id().clone())); + } + let is_head_op = op.id() == &head_op_id; + let mut buffer = vec![]; + { + let mut formatter = ui.new_formatter(&mut buffer); + formatter.with_label("op-log", |formatter| { + if is_head_op { + formatter.with_label("head", |formatter| template.format(&op, formatter)) + } else { + template.format(&op, formatter) + } + })?; + } + if !buffer.ends_with(b"\n") { + buffer.push(b'\n'); + } + let node_symbol = if is_head_op { "@" } else { "o" }; + graph.add_node( + op.id(), + &edges, + node_symbol, + &String::from_utf8_lossy(&buffer), + )?; + } + + Ok(()) +} + +pub fn cmd_op_undo( + ui: &mut Ui, + command: &CommandHelper, + args: &OperationUndoArgs, +) -> Result<(), CommandError> { + let mut workspace_command = command.workspace_helper(ui)?; + let bad_op = workspace_command.resolve_single_op(&args.operation)?; + let parent_ops = bad_op.parents(); + if parent_ops.len() > 1 { + return Err(user_error("Cannot undo a merge operation")); + } + if parent_ops.is_empty() { + return Err(user_error("Cannot undo repo initialization")); + } + + let mut tx = + workspace_command.start_transaction(&format!("undo operation {}", bad_op.id().hex())); + let repo_loader = tx.base_repo().loader(); + let bad_repo = repo_loader.load_at(&bad_op); + let parent_repo = repo_loader.load_at(&parent_ops[0]); + tx.mut_repo().merge(&bad_repo, &parent_repo); + workspace_command.finish_transaction(ui, tx)?; + + Ok(()) +} + +fn cmd_op_restore( + ui: &mut Ui, + command: &CommandHelper, + args: &OperationRestoreArgs, +) -> Result<(), CommandError> { + let mut workspace_command = command.workspace_helper(ui)?; + let target_op = workspace_command.resolve_single_op(&args.operation)?; + let mut tx = workspace_command + .start_transaction(&format!("restore to operation {}", target_op.id().hex())); + tx.mut_repo().set_view(target_op.view().take_store_view()); + workspace_command.finish_transaction(ui, tx)?; + + Ok(()) +} + +pub fn cmd_operation( + ui: &mut Ui, + command: &CommandHelper, + subcommand: &OperationCommands, +) -> Result<(), CommandError> { + match subcommand { + OperationCommands::Log(command_matches) => cmd_op_log(ui, command, command_matches), + OperationCommands::Restore(command_matches) => cmd_op_restore(ui, command, command_matches), + OperationCommands::Undo(command_matches) => cmd_op_undo(ui, command, command_matches), + } +}