Skip to content

Commit

Permalink
op log: add --op-diff option to embed operation diffs
Browse files Browse the repository at this point in the history
This is basically "log -p" for "op log". The flag name has "op" because --diff
and --patch mean a similar thing in this context. Since -p implies --op-diff,
user can just do "op log -p" if he's okay with verbose op + content diffs.
Note that --no-graph affects both "op log" and "op diff" parts.

We might want to do some style changes later, such as inserting/deleting blank
lines, highlighting headers, etc.
  • Loading branch information
yuja committed Sep 11, 2024
1 parent 050cde9 commit 78edc6a
Show file tree
Hide file tree
Showing 5 changed files with 340 additions and 12 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 4 additions & 0 deletions cli/src/cli_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand Down
81 changes: 76 additions & 5 deletions cli/src/commands/operation/log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -56,6 +64,18 @@ pub struct OperationLogArgs {
/// For the syntax, see https://martinvonz.github.io/jj/latest/templates/
#[arg(long, short = 'T')]
template: Option<String>,
/// 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(
Expand All @@ -66,35 +86,36 @@ 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, &current_op, args)
do_op_log(ui, &workspace_env, repo_loader, &current_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)?;

let template;
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(),
);
Expand All @@ -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();
Expand Down Expand Up @@ -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(),
Expand All @@ -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)?;
}
}
}

Expand Down
16 changes: 16 additions & 0 deletions cli/tests/[email protected]
Original file line number Diff line number Diff line change
Expand Up @@ -1390,6 +1390,22 @@ Like other commands, `jj op log` snapshots the current working-copy changes and
* `-T`, `--template <TEMPLATE>` — Render each operation using the given template
For the syntax, see https://martinvonz.github.io/jj/latest/templates/
* `--op-diff` — Show changes to the repository at each operation
* `-p`, `--patch` — 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.
* `-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 <TOOL>` — Generate diff by external command
* `--context <CONTEXT>` — Number of lines of context to show
Expand Down
Loading

0 comments on commit 78edc6a

Please sign in to comment.