refactor: extract operation commands into commands/operation module

This commit is contained in:
Samuel Tardieu 2023-01-17 21:43:28 +01:00
parent 3d09f675bd
commit d3789cb28d
2 changed files with 204 additions and 194 deletions

View file

@ -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<RevisionArg>,
}
/// 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<Operation> 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),

199
src/commands/operation.rs Normal file
View file

@ -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<Operation> 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),
}
}