diff --git a/CHANGELOG.md b/CHANGELOG.md index 61c69b60c..77103452d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,8 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). (inherit from parent; default), `full` (full working copy), or `empty` (the empty working copy). +* `jj op log` gained an option to include operation diffs. + ### Fixed bugs * Fixed panic when parsing invalid conflict markers of a particular form. diff --git a/cli/src/cli_util.rs b/cli/src/cli_util.rs index 670c4e9e7..237ae1f36 100644 --- a/cli/src/cli_util.rs +++ b/cli/src/cli_util.rs @@ -902,6 +902,10 @@ impl WorkspaceCommandHelper { self.workspace.repo_path() } + pub fn workspace(&self) -> &Workspace { + &self.workspace + } + pub fn working_copy(&self) -> &dyn WorkingCopy { self.workspace.working_copy() } diff --git a/cli/src/commands/operation/log.rs b/cli/src/commands/operation/log.rs index 6050d44e0..aabba7087 100644 --- a/cli/src/commands/operation/log.rs +++ b/cli/src/commands/operation/log.rs @@ -14,16 +14,24 @@ use std::slice; +use itertools::Itertools as _; use jj_lib::op_walk; use jj_lib::operation::Operation; +use jj_lib::repo::RepoLoader; use jj_lib::settings::ConfigResultExt as _; use jj_lib::settings::UserSettings; +use super::diff::show_op_diff; use crate::cli_util::format_template; use crate::cli_util::CommandHelper; use crate::cli_util::LogContentFormat; use crate::cli_util::WorkspaceCommandEnvironment; use crate::command_error::CommandError; +use crate::commit_templater::CommitTemplateLanguage; +use crate::diff_util::diff_formats_for_log; +use crate::diff_util::DiffFormatArgs; +use crate::diff_util::DiffRenderer; +use crate::formatter::Formatter; use crate::graphlog::get_graphlog; use crate::graphlog::Edge; use crate::graphlog::GraphStyle; @@ -56,6 +64,18 @@ pub struct OperationLogArgs { /// For the syntax, see https://martinvonz.github.io/jj/latest/templates/ #[arg(long, short = 'T')] template: Option, + /// Show changes to the repository at each operation + #[arg(long)] + op_diff: bool, + /// Show patch of modifications to changes (implies --op-diff) + /// + /// 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_log( @@ -66,27 +86,28 @@ pub fn cmd_op_log( if command.is_working_copy_writable() { let workspace_command = command.workspace_helper(ui)?; let current_op = workspace_command.repo().operation(); - do_op_log(ui, workspace_command.env(), current_op, args) + let repo_loader = workspace_command.workspace().repo_loader(); + do_op_log(ui, workspace_command.env(), repo_loader, current_op, args) } else { // Don't load the repo so that the operation history can be inspected // even with a corrupted repo state. For example, you can find the first // bad operation id to be abandoned. let workspace = command.load_workspace()?; let workspace_env = command.workspace_environment(ui, &workspace)?; + let repo_loader = workspace.repo_loader(); let current_op = command.resolve_operation(ui, workspace.repo_loader())?; - do_op_log(ui, &workspace_env, ¤t_op, args) + do_op_log(ui, &workspace_env, repo_loader, ¤t_op, args) } } fn do_op_log( ui: &mut Ui, workspace_env: &WorkspaceCommandEnvironment, + repo_loader: &RepoLoader, current_op: &Operation, args: &OperationLogArgs, ) -> Result<(), CommandError> { let settings = workspace_env.settings(); - let op_store = current_op.op_store(); - let graph_style = GraphStyle::from_settings(settings)?; let with_content_format = LogContentFormat::new(ui, settings)?; @@ -94,7 +115,7 @@ fn do_op_log( let op_node_template; { let language = OperationTemplateLanguage::new( - op_store.root_operation_id(), + repo_loader.op_store().root_operation_id(), Some(current_op.id()), workspace_env.operation_template_extensions(), ); @@ -114,6 +135,49 @@ fn do_op_log( .labeled("node"); } + let diff_formats = diff_formats_for_log(settings, &args.diff_format, args.patch)?; + let maybe_show_op_diff = if args.op_diff || !diff_formats.is_empty() { + let template_text = settings.config().get_string("templates.commit_summary")?; + let show = move |ui: &Ui, + formatter: &mut dyn Formatter, + op: &Operation, + with_content_format: &LogContentFormat| { + let parents: Vec<_> = op.parents().try_collect()?; + let parent_op = repo_loader.merge_operations(settings, parents, None)?; + let parent_repo = repo_loader.load_at(&parent_op)?; + let repo = repo_loader.load_at(op)?; + + let id_prefix_context = workspace_env.new_id_prefix_context(); + let commit_summary_template = { + let language = + workspace_env.commit_template_language(repo.as_ref(), &id_prefix_context); + workspace_env.parse_template( + &language, + &template_text, + CommitTemplateLanguage::wrap_commit, + )? + }; + let path_converter = workspace_env.path_converter(); + let diff_renderer = (!diff_formats.is_empty()) + .then(|| DiffRenderer::new(repo.as_ref(), path_converter, diff_formats.clone())); + + show_op_diff( + ui, + formatter, + repo.as_ref(), + &parent_repo, + &repo, + &commit_summary_template, + (!args.no_graph).then_some(graph_style), + with_content_format, + diff_renderer.as_ref(), + ) + }; + Some(show) + } else { + None + }; + ui.request_pager(); let mut formatter = ui.stdout_formatter(); let formatter = formatter.as_mut(); @@ -141,6 +205,10 @@ fn do_op_log( if !buffer.ends_with(b"\n") { buffer.push(b'\n'); } + if let Some(show) = &maybe_show_op_diff { + let mut formatter = ui.new_formatter(&mut buffer); + show(ui, formatter.as_mut(), &op, &within_graph)?; + } let node_symbol = format_template(ui, &op, &op_node_template); graph.add_node( op.id(), @@ -153,6 +221,9 @@ fn do_op_log( for op in iter { let op = op?; with_content_format.write(formatter, |formatter| template.format(&op, formatter))?; + if let Some(show) = &maybe_show_op_diff { + show(ui, formatter, &op, &with_content_format)?; + } } } diff --git a/cli/tests/cli-reference@.md.snap b/cli/tests/cli-reference@.md.snap index 4bcf14a7d..080444e9e 100644 --- a/cli/tests/cli-reference@.md.snap +++ b/cli/tests/cli-reference@.md.snap @@ -1390,6 +1390,22 @@ Like other commands, `jj op log` snapshots the current working-copy changes and * `-T`, `--template