Skip to content

Commit

Permalink
evolog: Implement --reversed flag
Browse files Browse the repository at this point in the history
Refactors the ReverseGraphIterator into a function to make it easier to use
between the evolog and log commands.

Adds some additional helper functions for converting between jj_lib's
GraphEdge and the graphlog's Edge type.
  • Loading branch information
JDSeiler committed Dec 27, 2024
1 parent 2f14ad2 commit edcb463
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 20 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
below it, similar to a "scissor line" in git. When editing multiple commits,
only ignore until the next `JJ: describe` line.

* `jj evolog` now accepts `--reversed`.

### Fixed bugs

* The `$NO_COLOR` environment variable must now be non-empty to be respected.
Expand Down
57 changes: 46 additions & 11 deletions cli/src/commands/evolog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,12 @@

use clap_complete::ArgValueCandidates;
use itertools::Itertools;
use jj_lib::backend::CommitId;
use jj_lib::commit::Commit;
use jj_lib::dag_walk::topo_order_reverse_ok;
use jj_lib::graph::reverse_graph;
use jj_lib::graph::GraphEdge;
use jj_lib::graph::GraphNode;
use jj_lib::matchers::EverythingMatcher;
use tracing::instrument;

Expand All @@ -28,6 +32,7 @@ use crate::command_error::CommandError;
use crate::commit_templater::CommitTemplateLanguage;
use crate::complete;
use crate::diff_util::DiffFormatArgs;
use crate::graphlog::convert_edge_type;
use crate::graphlog::get_graphlog;
use crate::graphlog::Edge;
use crate::graphlog::GraphStyle;
Expand Down Expand Up @@ -57,6 +62,9 @@ pub(crate) struct EvologArgs {
value_name = "LIMIT"
)]
deprecated_limit: Option<usize>,
/// Show revisions in the opposite order (older revisions first)
#[arg(long)]
reversed: bool,
/// Don't show the graph, show a flat list of revisions
#[arg(long)]
no_graph: bool,
Expand Down Expand Up @@ -151,14 +159,36 @@ pub(crate) fn cmd_evolog(
if !args.no_graph {
let mut raw_output = formatter.raw()?;
let mut graph = get_graphlog(graph_style, raw_output.as_mut());
for commit in commits {
let edges = commit
.predecessor_ids()

let commit_dag: Vec<Result<GraphNode<Commit>, ()>> = commits
.iter()
.map(|c| {
let edges = c
.predecessors()
.map(|p| GraphEdge::direct(p.unwrap().clone()))
.collect_vec();
Ok((c.clone(), edges))
})
.collect_vec();

let iter_nodes: Box<dyn Iterator<Item = _>> = if args.reversed {
let reversed = reverse_graph(commit_dag.iter().cloned()).unwrap();
Box::new(reversed.into_iter().map(Ok))
} else {
Box::new(commit_dag.into_iter())
};

for node in iter_nodes {
let (commit, edges) = node.unwrap();
// Slightly different edge type used for display.
let graphlog_edges: Vec<Edge<CommitId>> = edges
.iter()
.map(|id| Edge::Direct(id.clone()))
.collect_vec();
.cloned()
.map(|e| convert_edge_type(e).map(|e| e.id().clone()))
.collect();
let mut buffer = vec![];
let within_graph = with_content_format.sub_width(graph.width(commit.id(), &edges));
let within_graph =
with_content_format.sub_width(graph.width(commit.id(), &graphlog_edges));
within_graph.write(ui.new_formatter(&mut buffer).as_mut(), |formatter| {
template.format(&commit, formatter)
})?;
Expand All @@ -180,23 +210,28 @@ pub(crate) fn cmd_evolog(
let node_symbol = format_template(ui, &Some(commit.clone()), &node_template);
graph.add_node(
commit.id(),
&edges,
&graphlog_edges,
&node_symbol,
&String::from_utf8_lossy(&buffer),
)?;
}
} else {
for commit in commits {
with_content_format
.write(formatter, |formatter| template.format(&commit, formatter))?;
let commits_iter: Box<dyn Iterator<Item = _>> = if args.reversed {
Box::new(commits.iter().rev())
} else {
Box::new(commits.iter())
};

for commit in commits_iter {
with_content_format.write(formatter, |formatter| template.format(commit, formatter))?;
if let Some(renderer) = &diff_renderer {
let predecessors: Vec<_> = commit.predecessors().try_collect()?;
let width = ui.term_width();
renderer.show_inter_diff(
ui,
formatter,
&predecessors,
&commit,
commit,
&EverythingMatcher,
width,
)?;
Expand Down
3 changes: 2 additions & 1 deletion cli/src/commands/log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,8 @@ pub(crate) fn cmd_log(
}
}
if args.reversed {
Box::new(reverse_graph(forward_iter)?.into_iter().map(Ok))
let reversed = reverse_graph(forward_iter)?;
Box::new(reversed.into_iter().map(Ok))
} else {
Box::new(forward_iter)
}
Expand Down
24 changes: 24 additions & 0 deletions cli/src/graphlog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ use std::io::Write;

use itertools::Itertools;
use jj_lib::config::ConfigGetError;
use jj_lib::graph::GraphEdge;
use jj_lib::graph::GraphEdgeType;
use jj_lib::settings::UserSettings;
use renderdag::Ancestor;
use renderdag::GraphRowRenderer;
Expand All @@ -31,6 +33,28 @@ pub enum Edge<T> {
Missing,
}

impl<T> Edge<T> {
pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Edge<U> {
match self {
Edge::Direct(inner) => Edge::Direct(f(inner)),
Edge::Indirect(inner) => Edge::Indirect(f(inner)),
Edge::Missing => Edge::Missing,
}
}
}

/// Used to convert a jj_lib GraphEdge into a graphlog Edge.
pub fn convert_edge_type<N>(e: GraphEdge<N>) -> Edge<N> {
// TODO: Is it possible for the ClI to use the GraphEdge type directly?
// These two types seem to correspond exactly, only differing in minor
// implementation details.
match e.edge_type {
GraphEdgeType::Missing => Edge::Missing,
GraphEdgeType::Direct => Edge::Direct(e.target),
GraphEdgeType::Indirect => Edge::Indirect(e.target),
}
}

pub trait GraphLog<K: Clone + Eq + Hash> {
fn add_node(
&mut self,
Expand Down
1 change: 1 addition & 0 deletions cli/tests/[email protected]
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,7 @@ Lists the previous commits which a change has pointed to. The current commit of
Default value: `@`
* `-n`, `--limit <LIMIT>` — Limit number of revisions to show
* `--reversed` — Show revisions in the opposite order (older revisions first)
* `--no-graph` — Don't show the graph, show a flat list of revisions
* `-T`, `--template <TEMPLATE>` — Render each revision using the given template
Expand Down
67 changes: 67 additions & 0 deletions cli/tests/test_evolog_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,3 +348,70 @@ fn test_evolog_with_no_template() {
- name_placeholder
"#);
}

#[test]
fn test_evolog_reversed_no_graph() {
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", "a"]);
test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "b"]);
test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "c"]);
let stdout = test_env.jj_cmd_success(&repo_path, &["evolog", "--reversed", "--no-graph"]);
insta::assert_snapshot!(stdout, @r"
qpvuntsm hidden [email protected] 2001-02-03 08:05:07 230dd059
(empty) (no description set)
qpvuntsm hidden [email protected] 2001-02-03 08:05:08 d8d5f980
(empty) a
qpvuntsm hidden [email protected] 2001-02-03 08:05:09 b4584f54
(empty) b
qpvuntsm [email protected] 2001-02-03 08:05:10 5cb22a87
(empty) c
");
}

#[test]
fn test_evolog_reverse_with_graph() {
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", "a"]);
test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "b"]);
test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "c"]);
test_env.jj_cmd_ok(&repo_path, &["new", "-r", "description(c)", "-m", "d"]);
test_env.jj_cmd_ok(&repo_path, &["new", "-r", "description(c)", "-m", "e"]);
test_env.jj_cmd_ok(
&repo_path,
&[
"squash",
"--from",
"description(d)|description(e)",
"--to",
"description(c)",
"-m",
"c+d+e",
],
);
let stdout = test_env.jj_cmd_success(
&repo_path,
&["evolog", "-r", "description(c+d+e)", "--reversed"],
);
insta::assert_snapshot!(stdout, @r"
○ qpvuntsm hidden [email protected] 2001-02-03 08:05:07 230dd059
│ (empty) (no description set)
○ qpvuntsm hidden [email protected] 2001-02-03 08:05:08 d8d5f980
│ (empty) a
○ qpvuntsm hidden [email protected] 2001-02-03 08:05:09 b4584f54
│ (empty) b
○ qpvuntsm hidden [email protected] 2001-02-03 08:05:10 5cb22a87
│ (empty) c
│ ○ mzvwutvl hidden [email protected] 2001-02-03 08:05:11 280cbb6e
├─╯ (empty) d
│ ○ royxmykx hidden [email protected] 2001-02-03 08:05:12 031df638
├─╯ (empty) e
○ qpvuntsm [email protected] 2001-02-03 08:05:13 a177c2f2
(empty) c+d+e
");
}
17 changes: 10 additions & 7 deletions lib/src/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,13 @@ fn reachable_targets<N>(edges: &[GraphEdge<N>]) -> impl DoubleEndedIterator<Item
.map(|edge| &edge.target)
}

/// Creates new graph in which nodes and edges are reversed.
/// Reverses the edge direction and node order of the input graph.
pub fn reverse_graph<N: Clone + Eq + Hash, E>(
input: impl Iterator<Item = Result<GraphNode<N>, E>>,
) -> Result<Vec<GraphNode<N>>, E> {
let mut entries = vec![];
let mut original_nodes = vec![];
let mut reverse_edges: HashMap<N, Vec<GraphEdge<N>>> = HashMap::new();

for item in input {
let (node, edges) = item?;
for GraphEdge { target, edge_type } in edges {
Expand All @@ -86,15 +87,17 @@ pub fn reverse_graph<N: Clone + Eq + Hash, E>(
edge_type,
});
}
entries.push(node);
original_nodes.push(node);
}

let mut items = vec![];
for node in entries.into_iter().rev() {
// Reverse the order of nodes in the graph while we pair nodes
// with their reverse edges.
let mut reverse_graph = vec![];
while let Some(node) = original_nodes.pop() {
let edges = reverse_edges.get(&node).cloned().unwrap_or_default();
items.push((node, edges));
reverse_graph.push((node, edges));
}
Ok(items)
Ok(reverse_graph)
}

/// Graph iterator adapter to group topological branches.
Expand Down
2 changes: 1 addition & 1 deletion lib/tests/test_revset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3976,7 +3976,7 @@ fn test_reverse_graph() {
repo.as_ref(),
&[&commit_a, &commit_c, &commit_d, &commit_e, &commit_f],
);
let commits = reverse_graph(revset.iter_graph()).unwrap();
let commits: Vec<_> = reverse_graph(revset.iter_graph()).unwrap();
assert_eq!(commits.len(), 5);
assert_eq!(commits[0].0, *commit_a.id());
assert_eq!(commits[1].0, *commit_c.id());
Expand Down

0 comments on commit edcb463

Please sign in to comment.