From 5445df741c6c7fdc8fa1e0073ac2a5dc670c530a Mon Sep 17 00:00:00 2001 From: Benjamin Tan Date: Thu, 2 May 2024 16:29:31 +0800 Subject: [PATCH] cli: add `jj operation show` command --- CHANGELOG.md | 4 +- cli/src/commands/operation/mod.rs | 4 + cli/src/commands/operation/show.rs | 119 +++++++++++++++++++++++++++++ cli/tests/cli-reference@.md.snap | 35 +++++++++ 4 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 cli/src/commands/operation/show.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index e4da46520f..e5005f42bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,9 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). * New command `jj operation diff` that can compare changes made between two operations. +* New command `jj operation show` that can show the changes made in a single + operation. + ### Fixed bugs ## [0.19.0] - 2024-07-03 @@ -175,7 +178,6 @@ Thanks to the people who made this release happen! were global flags and specifying them once would insert the new commit before/ after all the specified commits. - ### Deprecations * Attempting to alias a built-in command now gives a warning, rather than being diff --git a/cli/src/commands/operation/mod.rs b/cli/src/commands/operation/mod.rs index 8b45df520e..aef1925c7f 100644 --- a/cli/src/commands/operation/mod.rs +++ b/cli/src/commands/operation/mod.rs @@ -16,6 +16,7 @@ mod abandon; mod diff; mod log; mod restore; +mod show; pub mod undo; use abandon::{cmd_op_abandon, OperationAbandonArgs}; @@ -23,6 +24,7 @@ use clap::Subcommand; use diff::{cmd_op_diff, OperationDiffArgs}; use log::{cmd_op_log, OperationLogArgs}; use restore::{cmd_op_restore, OperationRestoreArgs}; +use show::{cmd_op_show, OperationShowArgs}; use undo::{cmd_op_undo, OperationUndoArgs}; use crate::cli_util::CommandHelper; @@ -39,6 +41,7 @@ pub enum OperationCommand { Diff(OperationDiffArgs), Log(OperationLogArgs), Restore(OperationRestoreArgs), + Show(OperationShowArgs), Undo(OperationUndoArgs), } @@ -52,6 +55,7 @@ pub fn cmd_operation( OperationCommand::Diff(args) => cmd_op_diff(ui, command, args), OperationCommand::Log(args) => cmd_op_log(ui, command, args), OperationCommand::Restore(args) => cmd_op_restore(ui, command, args), + OperationCommand::Show(args) => cmd_op_show(ui, command, args), OperationCommand::Undo(args) => cmd_op_undo(ui, command, args), } } diff --git a/cli/src/commands/operation/show.rs b/cli/src/commands/operation/show.rs new file mode 100644 index 0000000000..bea49a81eb --- /dev/null +++ b/cli/src/commands/operation/show.rs @@ -0,0 +1,119 @@ +// Copyright 2020-2024 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 std::io::Write; + +use itertools::Itertools; +use jj_lib::op_walk; + +use super::diff::show_op_diff; +use crate::cli_util::{CommandHelper, LogContentFormat}; +use crate::command_error::{user_error, CommandError}; +use crate::diff_util::DiffFormatArgs; +use crate::operation_templater::OperationTemplateLanguage; +use crate::ui::Ui; + +/// Show changes to the repository in an operation +#[derive(clap::Args, Clone, Debug)] +pub struct OperationShowArgs { + /// Show repository changes in this operation, compared to its parent(s) + #[arg(visible_alias = "op", default_value = "@")] + operation: String, + /// Don't show the graph, show a flat list of modified changes + #[arg(long)] + no_graph: bool, + /// Show patch of modifications to changes + /// + /// If the previous version has different parents, it will be temporarily + /// rebased to the parents of the new version, so the diff is not + /// contaminated by unrelated changes. + #[arg(long, short = 'p')] + patch: bool, + #[command(flatten)] + diff_format: DiffFormatArgs, +} + +pub fn cmd_op_show( + ui: &mut Ui, + command: &CommandHelper, + args: &OperationShowArgs, +) -> Result<(), CommandError> { + let workspace = command.load_workspace()?; + let repo_loader = workspace.repo_loader(); + let head_op_str = &command.global_args().at_operation; + let head_ops = if head_op_str == "@" { + // If multiple head ops can't be resolved without merging, let the + // current op be empty. Beware that resolve_op_for_load() will eliminate + // redundant heads whereas get_current_head_ops() won't. + let current_op = op_walk::resolve_op_for_load(repo_loader, head_op_str).ok(); + if let Some(op) = current_op { + vec![op] + } else { + op_walk::get_current_head_ops( + repo_loader.op_store(), + repo_loader.op_heads_store().as_ref(), + )? + } + } else { + vec![op_walk::resolve_op_for_load(repo_loader, head_op_str)?] + }; + let current_op_id = match &*head_ops { + [op] => Some(op.id()), + _ => None, + }; + let op = op_walk::resolve_op_for_load(repo_loader, &args.operation)?; + let parents: Vec<_> = op.parents().try_collect()?; + if parents.is_empty() { + return Err(user_error("Cannot show the root operation")); + } + let parent_op = repo_loader.merge_operations(command.settings(), parents, None)?; + let with_content_format = LogContentFormat::new(ui, command.settings())?; + + // TODO: Should we make this customizable via clap arg? + let template; + { + let language = OperationTemplateLanguage::new( + repo_loader.op_store().root_operation_id(), + current_op_id, + command.operation_template_extensions(), + ); + let text = command.settings().config().get_string("templates.op_log")?; + template = command + .parse_template( + ui, + &language, + &text, + OperationTemplateLanguage::wrap_operation, + )? + .labeled("op_log"); + } + + let parent_repo = repo_loader.load_at(&parent_op)?; + let repo = repo_loader.load_at(&op)?; + + ui.request_pager(); + template.format(&op, ui.stdout_formatter().as_mut())?; + writeln!(ui.stdout())?; + + show_op_diff( + ui, + command, + &parent_repo, + &repo, + !args.no_graph, + &with_content_format, + &args.diff_format, + args.patch, + ) +} diff --git a/cli/tests/cli-reference@.md.snap b/cli/tests/cli-reference@.md.snap index 2363d3e152..a4a6d684bd 100644 --- a/cli/tests/cli-reference@.md.snap +++ b/cli/tests/cli-reference@.md.snap @@ -64,6 +64,7 @@ This document contains the help content for the `jj` command-line program. * [`jj operation diff`↴](#jj-operation-diff) * [`jj operation log`↴](#jj-operation-log) * [`jj operation restore`↴](#jj-operation-restore) +* [`jj operation show`↴](#jj-operation-show) * [`jj operation undo`↴](#jj-operation-undo) * [`jj parallelize`↴](#jj-parallelize) * [`jj prev`↴](#jj-prev) @@ -1242,6 +1243,7 @@ For information about the operation log, see https://github.com/martinvonz/jj/bl * `diff` — Compare changes to the repository between two operations * `log` — Show the operation log * `restore` — Create a new operation that restores the repo to an earlier state +* `show` — Show changes to the repository in an operation * `undo` — Create a new operation that undoes an earlier operation @@ -1341,6 +1343,39 @@ This restores the repo to the state at the specified operation, effectively undo +## `jj operation show` + +Show changes to the repository in an operation + +**Usage:** `jj operation show [OPTIONS] [OPERATION]` + +###### **Arguments:** + +* `` — Show repository changes in this operation, compared to its parent(s) + + Default value: `@` + +###### **Options:** + +* `--no-graph` — Don't show the graph, show a flat list of modified changes +* `-p`, `--patch` — Show patch of modifications to changes + + If the previous version has different parents, it will be temporarily rebased to the parents of the new version, so the diff is not contaminated by unrelated changes. +* `-s`, `--summary` — For each path, show only whether it was modified, added, or deleted +* `--stat` — Show a histogram of the changes +* `--types` — For each path, show only its type before and after + + The diff is shown as two letters. The first letter indicates the type before and the second letter indicates the type after. '-' indicates that the path was not present, 'F' represents a regular file, `L' represents a symlink, 'C' represents a conflict, and 'G' represents a Git submodule. +* `--name-only` — For each path, show only its path + + Typically useful for shell commands like: `jj diff -r @- --name_only | xargs perl -pi -e's/OLD/NEW/g` +* `--git` — Show a Git-format diff +* `--color-words` — Show a word-level diff with changes indicated only by color +* `--tool ` — Generate diff by external command +* `--context ` — Number of lines of context to show + + + ## `jj operation undo` Create a new operation that undoes an earlier operation