cli: op log: add --reversed option

Closes #4190.
This commit is contained in:
Benjamin Tan 2025-01-10 20:34:48 +08:00
parent 73d38b5cdc
commit d7f064c311
4 changed files with 100 additions and 8 deletions

View file

@ -58,7 +58,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
### New features
* `jj evolog` now accepts `--reversed`.
* `jj evolog` and `jj op log` now accept `--reversed`.
* `jj restore` now supports `-i`/`--interactive` selection.

View file

@ -17,7 +17,9 @@ use std::slice;
use itertools::Itertools as _;
use jj_lib::config::ConfigGetError;
use jj_lib::config::ConfigGetResultExt as _;
use jj_lib::graph::reverse_graph;
use jj_lib::graph::GraphEdge;
use jj_lib::op_store::OpStoreError;
use jj_lib::op_walk;
use jj_lib::operation::Operation;
use jj_lib::repo::RepoLoader;
@ -47,8 +49,13 @@ use crate::ui::Ui;
#[derive(clap::Args, Clone, Debug)]
pub struct OperationLogArgs {
/// Limit number of operations to show
///
/// Applied after operations are reordered.
#[arg(long, short = 'n')]
limit: Option<usize>,
/// Show operations in the opposite order (older operations first)
#[arg(long)]
reversed: bool,
/// Don't show the graph, show a flat list of operations
#[arg(long)]
no_graph: bool,
@ -190,16 +197,27 @@ fn do_op_log(
let mut formatter = ui.stdout_formatter();
let formatter = formatter.as_mut();
let limit = args.limit.unwrap_or(usize::MAX);
let iter = op_walk::walk_ancestors(slice::from_ref(current_op)).take(limit);
let iter = op_walk::walk_ancestors(slice::from_ref(current_op));
if !args.no_graph {
let mut raw_output = formatter.raw()?;
let mut graph = get_graphlog(graph_style, raw_output.as_mut());
for op in iter {
let iter = iter.map(|op| -> Result<_, OpStoreError> {
let op = op?;
let mut edges = vec![];
for id in op.parent_ids() {
edges.push(GraphEdge::direct(id.clone()));
}
let edges = op.parents().map_ok(GraphEdge::direct).try_collect()?;
Ok((op, edges))
});
let iter_nodes: Box<dyn Iterator<Item = _>> = if args.reversed {
Box::new(reverse_graph(iter)?.into_iter().map(Ok))
} else {
Box::new(iter)
};
for node in iter_nodes.take(limit) {
let (op, edges) = node?;
let edges = edges
.into_iter()
.map(|e| e.map(|e| e.id().clone()))
.collect_vec();
let mut buffer = vec![];
let within_graph = with_content_format.sub_width(graph.width(op.id(), &edges));
within_graph.write(ui.new_formatter(&mut buffer).as_mut(), |formatter| {
@ -221,7 +239,12 @@ fn do_op_log(
)?;
}
} else {
for op in iter {
let iter: Box<dyn Iterator<Item = _>> = if args.reversed {
Box::new(iter.collect_vec().into_iter().rev())
} else {
Box::new(iter)
};
for op in iter.take(limit) {
let op = op?;
with_content_format.write(formatter, |formatter| template.format(&op, formatter))?;
if let Some(show) = &maybe_show_op_diff {

View file

@ -1550,6 +1550,9 @@ Like other commands, `jj op log` snapshots the current working-copy changes and
###### **Options:**
* `-n`, `--limit <LIMIT>` — Limit number of operations to show
Applied after operations are reordered.
* `--reversed` — Show operations in the opposite order (older operations first)
* `--no-graph` — Don't show the graph, show a flat list of operations
* `-T`, `--template <TEMPLATE>` — Render each operation using the given template

View file

@ -192,6 +192,72 @@ fn test_op_log_no_graph() {
"#);
}
#[test]
fn test_op_log_reversed() {
let test_env = TestEnvironment::default();
test_env.jj_cmd_ok(test_env.env_root(), &["git", "init", "repo"]);
let repo_path = test_env.env_root().join("repo");
test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "description 0"]);
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "log", "--reversed"]);
insta::assert_snapshot!(&stdout, @r#"
000000000000 root()
eac759b9ab75 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
add workspace 'default'
@ d009cfc04993 test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00
describe commit 230dd059e1b059aefc0da06a2e5a7dbf22362f22
args: jj describe -m 'description 0'
"#);
test_env.jj_cmd_ok(
&repo_path,
&["describe", "-m", "description 1", "--at-op", "@-"],
);
// Should be able to display log with fork and branch points
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["op", "log", "--reversed"]);
insta::assert_snapshot!(&stderr, @"Concurrent modification detected, resolving automatically.");
insta::assert_snapshot!(&stdout, @r#"
000000000000 root()
eac759b9ab75 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
add workspace 'default'
8e3e726be123 test-username@host.example.com 2001-02-03 04:05:10.000 +07:00 - 2001-02-03 04:05:10.000 +07:00
describe commit 230dd059e1b059aefc0da06a2e5a7dbf22362f22
args: jj describe -m 'description 1' --at-op @-
d009cfc04993 test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00
describe commit 230dd059e1b059aefc0da06a2e5a7dbf22362f22
args: jj describe -m 'description 0'
@ e4538ffdc13d test-username@host.example.com 2001-02-03 04:05:11.000 +07:00 - 2001-02-03 04:05:11.000 +07:00
reconcile divergent operations
args: jj op log --reversed
"#);
// Should work correctly with `--no-graph`
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "log", "--reversed", "--no-graph"]);
insta::assert_snapshot!(&stdout, @r#"
000000000000 root()
eac759b9ab75 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
add workspace 'default'
8e3e726be123 test-username@host.example.com 2001-02-03 04:05:10.000 +07:00 - 2001-02-03 04:05:10.000 +07:00
describe commit 230dd059e1b059aefc0da06a2e5a7dbf22362f22
args: jj describe -m 'description 1' --at-op @-
d009cfc04993 test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00
describe commit 230dd059e1b059aefc0da06a2e5a7dbf22362f22
args: jj describe -m 'description 0'
e4538ffdc13d test-username@host.example.com 2001-02-03 04:05:11.000 +07:00 - 2001-02-03 04:05:11.000 +07:00
reconcile divergent operations
args: jj op log --reversed
"#);
// Should work correctly with `--limit`
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "log", "--reversed", "--limit=2"]);
insta::assert_snapshot!(stdout, @r#"
000000000000 root()
eac759b9ab75 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
add workspace 'default'
"#);
}
#[test]
fn test_op_log_no_graph_null_terminated() {
let test_env = TestEnvironment::default();