Skip to content

Commit

Permalink
cli: op log: add --bookmark option to filter operations by bookmark
Browse files Browse the repository at this point in the history
  • Loading branch information
bnjmnt4n committed Jan 11, 2025
1 parent b1ca325 commit bb8ff6d
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 15 deletions.
142 changes: 127 additions & 15 deletions cli/src/commands/operation/log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,21 @@

use std::slice;

use clap_complete::ArgValueCandidates;
use indexmap::IndexMap;
use itertools::Itertools as _;
use jj_lib::config::ConfigGetError;
use jj_lib::config::ConfigGetResultExt as _;
use jj_lib::graph;
use jj_lib::graph::reverse_graph;
use jj_lib::graph::GraphEdge;
use jj_lib::op_store::OpStoreError;
use jj_lib::graph::GraphEdgeType;
use jj_lib::op_walk;
use jj_lib::operation::Operation;
use jj_lib::repo::RepoLoader;
use jj_lib::settings::UserSettings;
use jj_lib::str_util::StringPattern;
use jj_lib::view::View;

use super::diff::show_op_diff;
use crate::cli_util::format_template;
Expand All @@ -32,6 +37,7 @@ use crate::cli_util::LogContentFormat;
use crate::cli_util::WorkspaceCommandEnvironment;
use crate::command_error::CommandError;
use crate::commit_templater::CommitTemplateLanguage;
use crate::complete;
use crate::diff_util::diff_formats_for_log;
use crate::diff_util::DiffFormatArgs;
use crate::diff_util::DiffRenderer;
Expand Down Expand Up @@ -67,6 +73,14 @@ pub struct OperationLogArgs {
/// Show changes to the repository at each operation
#[arg(long)]
op_diff: bool,
/// Display only operations which change the given local bookmark, or local
/// bookmarks matching a pattern (can be repeated)
#[arg(
long, short,
value_parser = StringPattern::parse,
add = ArgValueCandidates::new(complete::local_bookmarks),
)]
bookmark: Vec<StringPattern>,
/// Show patch of modifications to changes (implies --op-diff)
///
/// If the previous version has different parents, it will be temporarily
Expand Down Expand Up @@ -109,6 +123,7 @@ fn do_op_log(
) -> Result<(), CommandError> {
let settings = repo_loader.settings();
let graph_style = GraphStyle::from_settings(settings)?;
let use_elided_nodes = settings.get_bool("ui.log-synthetic-elided-nodes")?;
let with_content_format = LogContentFormat::new(ui, settings)?;

let template;
Expand Down Expand Up @@ -197,29 +212,108 @@ 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));

if !args.no_graph {
let mut raw_output = formatter.raw()?;
let mut graph = get_graphlog(graph_style, raw_output.as_mut());
let iter = iter.map(|op| -> Result<_, OpStoreError> {
let iter: Box<dyn Iterator<Item = Result<_, CommandError>>> = if args.bookmark.is_empty() {
let iter = op_walk::walk_ancestors(slice::from_ref(current_op));
let iter = iter.map(|op| -> Result<_, CommandError> {
let op = op?;
let edges = op.parents().map_ok(GraphEdge::direct).try_collect()?;
Ok((op, edges))
});
Box::new(iter)
} else {
let ops = graph::topo_order_reverse_graph_filter_ok(
slice::from_ref(current_op).iter().cloned().map(Ok),
|op| op.id().clone(),
|op| {
let view = op.view()?;
let parents: Vec<_> = op.parents().try_collect()?;
// TODO: Fix error types?
let parent_op = repo_loader.merge_operations(parents, None).unwrap();
let parent_view = parent_op.view()?;

let get_matching_bookmarks = |view: &View| {
let mut bookmarks = IndexMap::new();
for pattern in &args.bookmark {
let matches = view
.local_bookmarks_matching(pattern)
.map(|(name, targets)| (name.to_owned(), targets.to_owned()));
bookmarks.extend(matches);
}
bookmarks
};

let op_bookmarks = get_matching_bookmarks(&view);
let parent_bookmarks = get_matching_bookmarks(&parent_view);

if op_bookmarks != parent_bookmarks {
Ok(true)
} else {
Ok(false)
}
},
|op| op.parents().collect_vec(),
)?;
Box::new(ops.into_iter().map(|(op, edges)| {
let edges = edges
.iter()
.map(|edge| -> Result<_, CommandError> {
let op_id = edge.target.clone();
let data = repo_loader.op_store().read_operation(&op_id)?;
let op = Operation::new(repo_loader.op_store().clone(), op_id, data);
Ok(GraphEdge {
target: op,
edge_type: edge.edge_type,
})
})
.try_collect()?;
Ok((op, edges))
}))
};
let iter = iter.take(limit);

if !args.no_graph {
let mut raw_output = formatter.raw()?;
let mut graph = get_graphlog(graph_style, raw_output.as_mut());
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 iter_nodes = iter_nodes.collect_vec();

for node in iter_nodes.into_iter().take(limit) {
let (op, edges) = node?;
let edges = edges
.into_iter()
.map(|e| e.map(|e| e.id().clone()))
.collect_vec();

// The graph is keyed by (OperationId, is_synthetic)
let mut graphlog_edges = vec![];
let mut missing_edge_id = None;
let mut elided_targets = vec![];
for edge in edges {
let id = edge.target.id().clone();
match edge.edge_type {
GraphEdgeType::Missing => {
missing_edge_id = Some(id);
}
GraphEdgeType::Direct => {
graphlog_edges.push(GraphEdge::direct((id, false)));
}
GraphEdgeType::Indirect => {
if use_elided_nodes {
elided_targets.push(id.clone());
graphlog_edges.push(GraphEdge::direct((id, true)));
} else {
graphlog_edges.push(GraphEdge::indirect((id, false)));
}
}
}
}
if let Some(missing_edge_id) = missing_edge_id {
graphlog_edges.push(GraphEdge::missing((missing_edge_id, false)));
}
let mut buffer = vec![];
let within_graph = with_content_format.sub_width(graph.width(op.id(), &edges));
let key = (op.id().clone(), false);
let within_graph = with_content_format.sub_width(graph.width(&key, &graphlog_edges));
within_graph.write(ui.new_formatter(&mut buffer).as_mut(), |formatter| {
template.format(&op, formatter)
})?;
Expand All @@ -232,11 +326,29 @@ fn do_op_log(
}
let node_symbol = format_template(ui, &Some(op), &op_node_template);
graph.add_node(
op.id(),
&edges,
&key,
&graphlog_edges,
&node_symbol,
&String::from_utf8_lossy(&buffer),
)?;

for elided_target in elided_targets {
let elided_key = (elided_target, true);
let real_key = (elided_key.0.clone(), false);
let edges = [GraphEdge::direct(real_key)];
let mut buffer = vec![];
let within_graph = with_content_format.sub_width(graph.width(&elided_key, &edges));
within_graph.write(ui.new_formatter(&mut buffer).as_mut(), |formatter| {
writeln!(formatter.labeled("elided"), "(elided revisions)")
})?;
let node_symbol = format_template(ui, &None, &op_node_template);
graph.add_node(
&elided_key,
&edges,
&node_symbol,
&String::from_utf8_lossy(&buffer),
)?;
}
}
} else {
let iter: Box<dyn Iterator<Item = _>> = if args.reversed {
Expand All @@ -245,7 +357,7 @@ fn do_op_log(
Box::new(iter)
};
for op in iter.take(limit) {
let op = op?;
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
1 change: 1 addition & 0 deletions cli/tests/[email protected]
Original file line number Diff line number Diff line change
Expand Up @@ -1552,6 +1552,7 @@ Like other commands, `jj op log` snapshots the current working-copy changes and
For the syntax, see https://jj-vcs.github.io/jj/latest/templates/
* `--op-diff` — Show changes to the repository at each operation
* `-b`, `--bookmark <BOOKMARK>` — Display only operations which change the given local bookmark, or local bookmarks matching a pattern (can be repeated)
* `-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.
Expand Down

0 comments on commit bb8ff6d

Please sign in to comment.