mirror of
https://github.com/martinvonz/jj.git
synced 2025-01-02 18:01:05 +00:00
refactor: extract operation commands into commands/operation module
This commit is contained in:
parent
3d09f675bd
commit
d3789cb28d
2 changed files with 204 additions and 194 deletions
|
@ -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
199
src/commands/operation.rs
Normal 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),
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue