mirror of
https://github.com/martinvonz/jj.git
synced 2025-01-18 10:07:28 +00:00
cli: op log: add --bookmark
option to filter operations by bookmark
Some checks failed
nix / flake check (push) Has been cancelled
build / build (, macos-13) (push) Has been cancelled
build / build (, macos-14) (push) Has been cancelled
build / build (, ubuntu-24.04) (push) Has been cancelled
build / build (, windows-latest) (push) Has been cancelled
build / build (--all-features, ubuntu-24.04) (push) Has been cancelled
build / Build without Git support (push) Has been cancelled
build / Check protos (push) Has been cancelled
build / Check formatting (push) Has been cancelled
build / Run doctests (push) Has been cancelled
build / Check that MkDocs can build the docs (push) Has been cancelled
build / Check that MkDocs can build the docs with latest Python and uv (push) Has been cancelled
build / cargo-deny (advisories) (push) Has been cancelled
build / cargo-deny (bans licenses sources) (push) Has been cancelled
build / Clippy check (push) Has been cancelled
Some checks failed
nix / flake check (push) Has been cancelled
build / build (, macos-13) (push) Has been cancelled
build / build (, macos-14) (push) Has been cancelled
build / build (, ubuntu-24.04) (push) Has been cancelled
build / build (, windows-latest) (push) Has been cancelled
build / build (--all-features, ubuntu-24.04) (push) Has been cancelled
build / Build without Git support (push) Has been cancelled
build / Check protos (push) Has been cancelled
build / Check formatting (push) Has been cancelled
build / Run doctests (push) Has been cancelled
build / Check that MkDocs can build the docs (push) Has been cancelled
build / Check that MkDocs can build the docs with latest Python and uv (push) Has been cancelled
build / cargo-deny (advisories) (push) Has been cancelled
build / cargo-deny (bans licenses sources) (push) Has been cancelled
build / Clippy check (push) Has been cancelled
This commit is contained in:
parent
3bed4b5d81
commit
c9987ae749
2 changed files with 121 additions and 14 deletions
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -197,29 +212,102 @@ 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);
|
||||
|
||||
Ok(op_bookmarks != parent_bookmarks)
|
||||
},
|
||||
|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))
|
||||
}))
|
||||
};
|
||||
|
||||
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 (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)
|
||||
})?;
|
||||
|
@ -232,11 +320,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 {
|
||||
|
@ -245,7 +351,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)?;
|
||||
|
|
|
@ -1558,6 +1558,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.
|
||||
|
|
Loading…
Reference in a new issue