log: emit working-copy branch first if included in the revset

The working-copy revision is usually the latest commit, but it's not always
true. This patch ensures that the wc branch is emitted first so the graph node
order is less dependent on rewrites.
This commit is contained in:
Yuya Nishihara 2024-09-21 15:01:33 +09:00
parent fb79f2024d
commit 1973c712a3
17 changed files with 1309 additions and 988 deletions

View File

@ -43,6 +43,8 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
`--insert-before` options to customize the location of the duplicated
revisions.
* `jj log` now displays the working-copy branch first.
* New `fork_point()` revset function can be used to obtain the fork point
of multiple commits.

View File

@ -187,11 +187,21 @@ pub(crate) fn cmd_log(
if !args.no_graph {
let mut raw_output = formatter.raw()?;
let mut graph = get_graphlog(graph_style, raw_output.as_mut());
let forward_iter = TopoGroupedGraphIterator::new(revset.iter_graph());
let iter: Box<dyn Iterator<Item = _>> = if args.reversed {
Box::new(ReverseGraphIterator::new(forward_iter)?)
} else {
Box::new(forward_iter)
let iter: Box<dyn Iterator<Item = _>> = {
let mut forward_iter = TopoGroupedGraphIterator::new(revset.iter_graph());
// Emit the working-copy branch first, which is usually most
// interesting. This also helps stabilize output order.
if let Some(id) = workspace_command.get_wc_commit_id() {
let has_commit = revset.containing_fn();
if has_commit(id)? {
forward_iter.prioritize_branch(id.clone());
}
}
if args.reversed {
Box::new(ReverseGraphIterator::new(forward_iter)?)
} else {
Box::new(forward_iter)
}
};
for node in iter.take(limit) {
let (commit_id, edges) = node?;

View File

@ -940,12 +940,12 @@ fn test_bookmark_track_untrack() {
feature2@origin: sptzoqmo 7b33f629 commit 1
main@origin: sptzoqmo 7b33f629 commit 1
"###);
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
feature1@origin feature2@origin main@origin 7b33f6295eda
@ 230dd059e1b0
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r#"
@ 230dd059e1b0
feature1@origin feature2@origin main@origin 7b33f6295eda
000000000000
"###);
"#);
// Track new bookmark. Local bookmark should be created.
test_env.jj_cmd_ok(
@ -988,12 +988,12 @@ fn test_bookmark_track_untrack() {
main: sptzoqmo 7b33f629 commit 1
@origin: sptzoqmo 7b33f629 commit 1
"###);
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
feature1 feature1@origin feature2@origin main 7b33f6295eda
@ 230dd059e1b0
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r#"
@ 230dd059e1b0
feature1 feature1@origin feature2@origin main 7b33f6295eda
000000000000
"###);
"#);
// Fetch new commit. Only tracking bookmark "main" should be merged.
create_remote_commit(
@ -1018,14 +1018,14 @@ fn test_bookmark_track_untrack() {
main: mmqqkyyt 40dabdaf commit 2
@origin: mmqqkyyt 40dabdaf commit 2
"###);
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
feature1@origin feature2@origin main 40dabdaf4abe
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r#"
@ 230dd059e1b0
feature1@origin feature2@origin main 40dabdaf4abe
feature1 7b33f6295eda
@ 230dd059e1b0
000000000000
"###);
"#);
// Fetch new commit with auto tracking. Tracking bookmark "main" and new
// bookmark "feature3" should be merged.
@ -1057,14 +1057,14 @@ fn test_bookmark_track_untrack() {
main: wwnpyzpo 3f0f86fa commit 3
@origin: wwnpyzpo 3f0f86fa commit 3
"###);
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
feature1@origin feature2@origin feature3 main 3f0f86fa0e57
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r#"
@ 230dd059e1b0
feature1@origin feature2@origin feature3 main 3f0f86fa0e57
feature1 7b33f6295eda
@ 230dd059e1b0
000000000000
"###);
"#);
}
#[test]
@ -1447,9 +1447,9 @@ fn test_bookmark_list_filtered() {
&local_path,
&["log", "-r::(bookmarks() | remote_bookmarks())", "-T", template],
),
@r###"
e31634b64294 remote-rewrite*
@ c7b4c09cd77c local-keep
@r#"
@ c7b4c09cd77c local-keep
e31634b64294 remote-rewrite*
3e9a5af6ef15 remote-rewrite@origin (hidden)
@ -1458,7 +1458,7 @@ fn test_bookmark_list_filtered() {
911e912015fb remote-keep
000000000000
"###);
"#);
// All bookmarks are listed by default.
let (stdout, stderr) = test_env.jj_cmd_ok(&local_path, &["bookmark", "list"]);

View File

@ -503,26 +503,26 @@ fn test_log_evolog_divergence() {
&["describe", "-m", "description 2", "--at-operation", "@-"],
);
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["log"]);
insta::assert_snapshot!(stdout, @r###"
qpvuntsm?? test.user@example.com 2001-02-03 08:05:10 6ba70e00
description 2
@ qpvuntsm?? test.user@example.com 2001-02-03 08:05:08 ff309c29
description 1
insta::assert_snapshot!(stdout, @r#"
@ qpvuntsm?? test.user@example.com 2001-02-03 08:05:08 ff309c29
description 1
qpvuntsm?? test.user@example.com 2001-02-03 08:05:10 6ba70e00
description 2
zzzzzzzz root() 00000000
"###);
"#);
insta::assert_snapshot!(stderr, @r###"
Concurrent modification detected, resolving automatically.
"###);
// Color
let stdout = test_env.jj_cmd_success(&repo_path, &["log", "--color=always"]);
insta::assert_snapshot!(stdout, @r###"
qpvuntsm?? test.user@example.com 2001-02-03 08:05:10 6ba70e00
description 2
@ qpvuntsm?? test.user@example.com 2001-02-03 08:05:08 ff309c29
description 1
insta::assert_snapshot!(stdout, @r#"
@ qpvuntsm?? test.user@example.com 2001-02-03 08:05:08 ff309c29
description 1
qpvuntsm?? test.user@example.com 2001-02-03 08:05:10 6ba70e00
description 2
 zzzzzzzz root() 00000000
"###);
"#);
// Evolog and hidden divergent
let stdout = test_env.jj_cmd_success(&repo_path, &["evolog"]);
@ -599,45 +599,45 @@ fn test_log_bookmarks() {
let template = r#"commit_id.short() ++ " " ++ if(bookmarks, bookmarks, "(no bookmarks)")"#;
let output = test_env.jj_cmd_success(&workspace_root, &["log", "-T", template]);
insta::assert_snapshot!(output, @r###"
fed794e2ba44 bookmark3?? bookmark3@origin
insta::assert_snapshot!(output, @r#"
@ a5b4d15489cc bookmark2* new-bookmark
8476341eb395 bookmark2@origin unchanged
fed794e2ba44 bookmark3?? bookmark3@origin
b1bb3766d584 bookmark3??
4a7e4246fc4d bookmark1*
@ a5b4d15489cc bookmark2* new-bookmark
8476341eb395 bookmark2@origin unchanged
000000000000 (no bookmarks)
"###);
"#);
let template = r#"bookmarks.map(|b| separate("/", b.remote(), b.name())).join(", ")"#;
let output = test_env.jj_cmd_success(&workspace_root, &["log", "-T", template]);
insta::assert_snapshot!(output, @r###"
bookmark3, origin/bookmark3
insta::assert_snapshot!(output, @r#"
@ bookmark2, new-bookmark
origin/bookmark2, unchanged
bookmark3, origin/bookmark3
bookmark3
bookmark1
@ bookmark2, new-bookmark
origin/bookmark2, unchanged
"###);
"#);
let template = r#"separate(" ", "L:", local_bookmarks, "R:", remote_bookmarks)"#;
let output = test_env.jj_cmd_success(&workspace_root, &["log", "-T", template]);
insta::assert_snapshot!(output, @r###"
L: bookmark3?? R: bookmark3@origin
insta::assert_snapshot!(output, @r#"
@ L: bookmark2* new-bookmark R:
L: unchanged R: bookmark2@origin unchanged@origin
L: bookmark3?? R: bookmark3@origin
L: bookmark3?? R:
L: bookmark1* R:
@ L: bookmark2* new-bookmark R:
L: unchanged R: bookmark2@origin unchanged@origin
L: R:
"###);
"#);
let template = r#"
remote_bookmarks.map(|ref| concat(

View File

@ -50,12 +50,12 @@ fn test_concurrent_operation_divergence() {
// We should be informed about the concurrent modification
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["log", "-T", "description"]);
insta::assert_snapshot!(stdout, @r###"
message 2
@ message 1
insta::assert_snapshot!(stdout, @r#"
@ message 1
message 2
"###);
"#);
insta::assert_snapshot!(stderr, @r###"
Concurrent modification detected, resolving automatically.
"###);

File diff suppressed because it is too large Load Diff

View File

@ -554,8 +554,8 @@ fn test_git_colocated_fetch_deleted_or_moved_bookmark() {
// "original C" and "B_to_delete" are abandoned, as the corresponding bookmarks
// were deleted or moved on the remote (#864)
insta::assert_snapshot!(get_log_output(&test_env, &clone_path), @r#"
4f3d13296f978cbc351c46a43b4619c91b888475 C_to_move moved C
@ 9c2de797c3c299a40173c5af724329012b77cbdd
@ 9c2de797c3c299a40173c5af724329012b77cbdd
4f3d13296f978cbc351c46a43b4619c91b888475 C_to_move moved C
a7e4cec4256b7995129b9d1e1bda7e1df6e60678 A git_head() A
0000000000000000000000000000000000000000

View File

@ -482,17 +482,17 @@ fn test_git_fetch_all() {
trunk1: zowqyktl ff36dc55 descr_for_trunk1
@origin: zowqyktl ff36dc55 descr_for_trunk1
"###);
insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###"
c7d4bdcbc215 descr_for_b b
decaa3966c83 descr_for_a2 a2
359a9a02457d descr_for_a1 a1
ff36dc55760e descr_for_trunk1 trunk1
@ 230dd059e1b0
insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r#"
@ 230dd059e1b0
c7d4bdcbc215 descr_for_b b
decaa3966c83 descr_for_a2 a2
359a9a02457d descr_for_a1 a1
ff36dc55760e descr_for_trunk1 trunk1
000000000000
"###);
"#);
// ==== Change both repos ====
// First, change the target repo:
@ -515,17 +515,17 @@ fn test_git_fetch_all() {
);
// Our repo before and after fetch
insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###"
061eddbb43ab new_descr_for_b_to_create_conflict b*
decaa3966c83 descr_for_a2 a2
359a9a02457d descr_for_a1 a1
ff36dc55760e descr_for_trunk1 trunk1
@ 230dd059e1b0
insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r#"
@ 230dd059e1b0
061eddbb43ab new_descr_for_b_to_create_conflict b*
decaa3966c83 descr_for_a2 a2
359a9a02457d descr_for_a1 a1
ff36dc55760e descr_for_trunk1 trunk1
000000000000
"###);
"#);
insta::assert_snapshot!(get_bookmark_output(&test_env, &target_jj_repo_path), @r###"
a1: nknoxmzm 359a9a02 descr_for_a1
@origin: nknoxmzm 359a9a02 descr_for_a1
@ -560,20 +560,20 @@ fn test_git_fetch_all() {
trunk2: umznmzko 8f1f14fb descr_for_trunk2
@origin: umznmzko 8f1f14fb descr_for_trunk2
"###);
insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###"
babc49226c14 descr_for_b b?? b@origin
91e46b4b2653 descr_for_a2 a2
0424f6dfc1ff descr_for_a1 a1
8f1f14fbbf42 descr_for_trunk2 trunk2
061eddbb43ab new_descr_for_b_to_create_conflict b??
ff36dc55760e descr_for_trunk1 trunk1
@ 230dd059e1b0
insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r#"
@ 230dd059e1b0
babc49226c14 descr_for_b b?? b@origin
91e46b4b2653 descr_for_a2 a2
0424f6dfc1ff descr_for_a1 a1
8f1f14fbbf42 descr_for_trunk2 trunk2
061eddbb43ab new_descr_for_b_to_create_conflict b??
ff36dc55760e descr_for_trunk1 trunk1
000000000000
"###);
"#);
}
#[test]
@ -633,13 +633,13 @@ fn test_git_fetch_some_of_many_bookmarks() {
insta::assert_snapshot!(stderr, @r###"
bookmark: b@origin [new] tracked
"###);
insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###"
c7d4bdcbc215 descr_for_b b
ff36dc55760e descr_for_trunk1
@ 230dd059e1b0
insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r#"
@ 230dd059e1b0
c7d4bdcbc215 descr_for_b b
ff36dc55760e descr_for_trunk1
000000000000
"###);
"#);
// ...check what the intermediate state looks like...
insta::assert_snapshot!(get_bookmark_output(&test_env, &target_jj_repo_path), @r###"
b: vpupmnsl c7d4bdcb descr_for_b
@ -655,17 +655,17 @@ fn test_git_fetch_some_of_many_bookmarks() {
bookmark: a1@origin [new] tracked
bookmark: a2@origin [new] tracked
"###);
insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###"
decaa3966c83 descr_for_a2 a2
359a9a02457d descr_for_a1 a1
c7d4bdcbc215 descr_for_b b
ff36dc55760e descr_for_trunk1
@ 230dd059e1b0
insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r#"
@ 230dd059e1b0
decaa3966c83 descr_for_a2 a2
359a9a02457d descr_for_a1 a1
c7d4bdcbc215 descr_for_b b
ff36dc55760e descr_for_trunk1
000000000000
"###);
"#);
// Fetching the same bookmark again
let (stdout, stderr) =
test_env.jj_cmd_ok(&target_jj_repo_path, &["git", "fetch", "--branch", "a1"]);
@ -673,17 +673,17 @@ fn test_git_fetch_some_of_many_bookmarks() {
insta::assert_snapshot!(stderr, @r###"
Nothing changed.
"###);
insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###"
decaa3966c83 descr_for_a2 a2
359a9a02457d descr_for_a1 a1
c7d4bdcbc215 descr_for_b b
ff36dc55760e descr_for_trunk1
@ 230dd059e1b0
insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r#"
@ 230dd059e1b0
decaa3966c83 descr_for_a2 a2
359a9a02457d descr_for_a1 a1
c7d4bdcbc215 descr_for_b b
ff36dc55760e descr_for_trunk1
000000000000
"###);
"#);
// ==== Change both repos ====
// First, change the target repo:
@ -706,17 +706,17 @@ fn test_git_fetch_some_of_many_bookmarks() {
);
// Our repo before and after fetch of two bookmarks
insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###"
6ebd41dc4f13 new_descr_for_b_to_create_conflict b*
decaa3966c83 descr_for_a2 a2
359a9a02457d descr_for_a1 a1
ff36dc55760e descr_for_trunk1
@ 230dd059e1b0
insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r#"
@ 230dd059e1b0
6ebd41dc4f13 new_descr_for_b_to_create_conflict b*
decaa3966c83 descr_for_a2 a2
359a9a02457d descr_for_a1 a1
ff36dc55760e descr_for_trunk1
000000000000
"###);
"#);
let (stdout, stderr) = test_env.jj_cmd_ok(
&target_jj_repo_path,
&["git", "fetch", "--branch", "b", "--branch", "a1"],
@ -727,20 +727,20 @@ fn test_git_fetch_some_of_many_bookmarks() {
bookmark: b@origin [updated] tracked
Abandoned 1 commits that are no longer reachable.
"###);
insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###"
01d115196c39 descr_for_b b?? b@origin
6df2d34cf0da descr_for_a1 a1
2bb3ebd2bba3 descr_for_trunk2
6ebd41dc4f13 new_descr_for_b_to_create_conflict b??
decaa3966c83 descr_for_a2 a2
ff36dc55760e descr_for_trunk1
@ 230dd059e1b0
insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r#"
@ 230dd059e1b0
01d115196c39 descr_for_b b?? b@origin
6df2d34cf0da descr_for_a1 a1
2bb3ebd2bba3 descr_for_trunk2
6ebd41dc4f13 new_descr_for_b_to_create_conflict b??
decaa3966c83 descr_for_a2 a2
ff36dc55760e descr_for_trunk1
000000000000
"###);
"#);
// We left a2 where it was before, let's see how `jj bookmark list` sees this.
insta::assert_snapshot!(get_bookmark_output(&test_env, &target_jj_repo_path), @r###"
@ -765,20 +765,20 @@ fn test_git_fetch_some_of_many_bookmarks() {
bookmark: a2@origin [updated] tracked
Abandoned 1 commits that are no longer reachable.
"###);
insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###"
31c7d94b1f29 descr_for_a2 a2
01d115196c39 descr_for_b b?? b@origin
6df2d34cf0da descr_for_a1 a1
2bb3ebd2bba3 descr_for_trunk2
6ebd41dc4f13 new_descr_for_b_to_create_conflict b??
ff36dc55760e descr_for_trunk1
@ 230dd059e1b0
insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r#"
@ 230dd059e1b0
31c7d94b1f29 descr_for_a2 a2
01d115196c39 descr_for_b b?? b@origin
6df2d34cf0da descr_for_a1 a1
2bb3ebd2bba3 descr_for_trunk2
6ebd41dc4f13 new_descr_for_b_to_create_conflict b??
ff36dc55760e descr_for_trunk1
000000000000
"###);
"#);
insta::assert_snapshot!(get_bookmark_output(&test_env, &target_jj_repo_path), @r###"
a1: ypowunwp 6df2d34c descr_for_a1
@origin: ypowunwp 6df2d34c descr_for_a1
@ -920,15 +920,15 @@ fn test_git_fetch_undo() {
bookmark: a1@origin [new] tracked
bookmark: b@origin [new] tracked
"###);
insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###"
c7d4bdcbc215 descr_for_b b
359a9a02457d descr_for_a1 a1
ff36dc55760e descr_for_trunk1
@ 230dd059e1b0
insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r#"
@ 230dd059e1b0
c7d4bdcbc215 descr_for_b b
359a9a02457d descr_for_a1 a1
ff36dc55760e descr_for_trunk1
000000000000
"###);
"#);
let (stdout, stderr) = test_env.jj_cmd_ok(&target_jj_repo_path, &["undo"]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r#"
@ -946,13 +946,13 @@ fn test_git_fetch_undo() {
insta::assert_snapshot!(stderr, @r###"
bookmark: b@origin [new] tracked
"###);
insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###"
c7d4bdcbc215 descr_for_b b
ff36dc55760e descr_for_trunk1
@ 230dd059e1b0
insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r#"
@ 230dd059e1b0
c7d4bdcbc215 descr_for_b b
ff36dc55760e descr_for_trunk1
000000000000
"###);
"#);
}
// Compare to `test_git_import_undo` in test_git_import_export
@ -998,13 +998,13 @@ fn test_fetch_undo_what() {
insta::assert_snapshot!(stderr, @r###"
bookmark: b@origin [new] tracked
"###);
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
c7d4bdcbc215 descr_for_b b
ff36dc55760e descr_for_trunk1
@ 230dd059e1b0
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r#"
@ 230dd059e1b0
c7d4bdcbc215 descr_for_b b
ff36dc55760e descr_for_trunk1
000000000000
"###);
"#);
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
b: vpupmnsl c7d4bdcb descr_for_b
@origin: vpupmnsl c7d4bdcb descr_for_b
@ -1178,17 +1178,17 @@ fn test_git_fetch_removed_bookmark() {
bookmark: b@origin [new] tracked
bookmark: trunk1@origin [new] tracked
"###);
insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###"
c7d4bdcbc215 descr_for_b b
decaa3966c83 descr_for_a2 a2
359a9a02457d descr_for_a1 a1
ff36dc55760e descr_for_trunk1 trunk1
@ 230dd059e1b0
insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r#"
@ 230dd059e1b0
c7d4bdcbc215 descr_for_b b
decaa3966c83 descr_for_a2 a2
359a9a02457d descr_for_a1 a1
ff36dc55760e descr_for_trunk1 trunk1
000000000000
"###);
"#);
// Remove a2 bookmark in origin
test_env.jj_cmd_ok(&source_git_repo_path, &["bookmark", "forget", "a2"]);
@ -1200,17 +1200,17 @@ fn test_git_fetch_removed_bookmark() {
insta::assert_snapshot!(stderr, @r###"
Nothing changed.
"###);
insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###"
c7d4bdcbc215 descr_for_b b
decaa3966c83 descr_for_a2 a2
359a9a02457d descr_for_a1 a1
ff36dc55760e descr_for_trunk1 trunk1
@ 230dd059e1b0
insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r#"
@ 230dd059e1b0
c7d4bdcbc215 descr_for_b b
decaa3966c83 descr_for_a2 a2
359a9a02457d descr_for_a1 a1
ff36dc55760e descr_for_trunk1 trunk1
000000000000
"###);
"#);
// Fetch bookmarks a2 from origin, and check that it has been removed locally
let (stdout, stderr) =
@ -1220,15 +1220,15 @@ fn test_git_fetch_removed_bookmark() {
bookmark: a2@origin [deleted] untracked
Abandoned 1 commits that are no longer reachable.
"###);
insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###"
c7d4bdcbc215 descr_for_b b
359a9a02457d descr_for_a1 a1
ff36dc55760e descr_for_trunk1 trunk1
@ 230dd059e1b0
insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r#"
@ 230dd059e1b0
c7d4bdcbc215 descr_for_b b
359a9a02457d descr_for_a1 a1
ff36dc55760e descr_for_trunk1 trunk1
000000000000
"###);
"#);
}
#[test]
@ -1270,17 +1270,17 @@ fn test_git_fetch_removed_parent_bookmark() {
bookmark: b@origin [new] tracked
bookmark: trunk1@origin [new] tracked
"###);
insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###"
c7d4bdcbc215 descr_for_b b
decaa3966c83 descr_for_a2 a2
359a9a02457d descr_for_a1 a1
ff36dc55760e descr_for_trunk1 trunk1
@ 230dd059e1b0
insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r#"
@ 230dd059e1b0
c7d4bdcbc215 descr_for_b b
decaa3966c83 descr_for_a2 a2
359a9a02457d descr_for_a1 a1
ff36dc55760e descr_for_trunk1 trunk1
000000000000
"###);
"#);
// Remove all bookmarks in origin.
test_env.jj_cmd_ok(&source_git_repo_path, &["bookmark", "forget", "glob:*"]);
@ -1301,15 +1301,15 @@ fn test_git_fetch_removed_parent_bookmark() {
Abandoned 1 commits that are no longer reachable.
Warning: No branch matching `master` found on any specified/configured remote
"###);
insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###"
c7d4bdcbc215 descr_for_b b
decaa3966c83 descr_for_a2 a2
ff36dc55760e descr_for_trunk1
@ 230dd059e1b0
insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r#"
@ 230dd059e1b0
c7d4bdcbc215 descr_for_b b
decaa3966c83 descr_for_a2 a2
ff36dc55760e descr_for_trunk1
000000000000
"###);
"#);
}
#[test]
@ -1368,12 +1368,12 @@ fn test_git_fetch_remote_only_bookmark() {
// Fetch using git.auto_local_bookmark = false
test_env.add_config("git.auto-local-bookmark = false");
test_env.jj_cmd_ok(&repo_path, &["git", "fetch", "--remote=origin"]);
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
9f01a0e04879 message feature1 feature2@origin
@ 230dd059e1b0
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r#"
@ 230dd059e1b0
9f01a0e04879 message feature1 feature2@origin
000000000000
"###);
"#);
insta::assert_snapshot!(get_bookmark_output(&test_env, &repo_path), @r###"
feature1: mzyxwzks 9f01a0e0 message
@origin: mzyxwzks 9f01a0e0 message

View File

@ -1151,15 +1151,15 @@ fn test_git_push_deleted() {
Delete bookmark bookmark1 from d13ecdbda2a2
"#);
let stdout = test_env.jj_cmd_success(&workspace_root, &["log", "-rall()"]);
insta::assert_snapshot!(stdout, @r###"
rlzusymt test.user@example.com 2001-02-03 08:05:10 bookmark2 8476341e
(empty) description 2
insta::assert_snapshot!(stdout, @r#"
@ yqosqzyt test.user@example.com 2001-02-03 08:05:13 5b36783c
(empty) (no description set)
rlzusymt test.user@example.com 2001-02-03 08:05:10 bookmark2 8476341e
(empty) description 2
xtvrqkyv test.user@example.com 2001-02-03 08:05:08 d13ecdbd
(empty) description 1
@ yqosqzyt test.user@example.com 2001-02-03 08:05:13 5b36783c
(empty) (no description set)
zzzzzzzz root() 00000000
"###);
"#);
let (stdout, stderr) = test_env.jj_cmd_ok(&workspace_root, &["git", "push", "--deleted"]);
insta::assert_snapshot!(stdout, @"");
insta::assert_snapshot!(stderr, @r###"

View File

@ -776,12 +776,12 @@ fn test_log_divergence() {
&["describe", "-m", "description 2", "--at-operation", "@-"],
);
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["log", "-T", template]);
insta::assert_snapshot!(stdout, @r###"
description 2 !divergence!
@ description 1 !divergence!
insta::assert_snapshot!(stdout, @r#"
@ description 1 !divergence!
description 2 !divergence!
"###);
"#);
insta::assert_snapshot!(stderr, @r###"
Concurrent modification detected, resolving automatically.
"###);

View File

@ -1125,17 +1125,17 @@ fn test_next_offset_when_wc_has_descendants() {
test_env.jj_cmd_ok(&repo_path, &["commit", "-m", "left-2"]);
test_env.jj_cmd_ok(&repo_path, &["edit", "description(right-wc)"]);
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
vruxwmqvtpmx left-2
yqosqzytrlsw left-1
royxmykxtrkr left-wc
zsuskulnrvyr right-2
kkmpptxzrspx right-1
@ rlvkpnrzqnoo right-wc
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r#"
zsuskulnrvyr right-2
kkmpptxzrspx right-1
@ rlvkpnrzqnoo right-wc
vruxwmqvtpmx left-2
yqosqzytrlsw left-1
royxmykxtrkr left-wc
qpvuntsmwlqt base
zzzzzzzzzzzz
"###);
"#);
test_env.jj_cmd_ok(&repo_path, &["next", "2"]);
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"

View File

@ -769,10 +769,10 @@ fn test_op_recover_from_bad_gc() {
// The repo should no longer be corrupt.
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["log"]);
insta::assert_snapshot!(stdout, @r#"
mzvwutvl?? test.user@example.com 2001-02-03 08:05:15 dc2c6d52
(empty) 4.1
@ mzvwutvl?? test.user@example.com 2001-02-03 08:05:12 6d868f04
(empty) 4
@ mzvwutvl?? test.user@example.com 2001-02-03 08:05:12 6d868f04
(empty) 4
mzvwutvl?? test.user@example.com 2001-02-03 08:05:15 dc2c6d52
(empty) 4.1
zsuskuln test.user@example.com 2001-02-03 08:05:10 git_head() f652c321
(empty) (no description set)
zzzzzzzz root() 00000000

View File

@ -435,13 +435,13 @@ fn test_parallelize_multiple_heads_with_and_without_children() {
&workspace_path,
&["parallelize", "description(0)", "description(1)"],
);
insta::assert_snapshot!(get_log_output(&test_env, &workspace_path), @r###"
6270540ee067 1 parents:
@ 97d7522f40e8 2 parents: 0
745bea8029c1 0 parents:
insta::assert_snapshot!(get_log_output(&test_env, &workspace_path), @r#"
@ 97d7522f40e8 2 parents: 0
745bea8029c1 0 parents:
6270540ee067 1 parents:
000000000000 parents:
"###);
"#);
}
#[test]

View File

@ -561,12 +561,12 @@ fn test_rebase_multiple_revisions() {
Parent commit : kmkuslsw d1bfda8c f | f
Added 0 files, modified 0 files, removed 2 files
"###);
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
h: g
g: f
e: d
d: i
@ i: f
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r#"
e: d
d: i
@ i: f
h: g
g: f
f: c a
@ -575,7 +575,7 @@ fn test_rebase_multiple_revisions() {
a
"###);
"#);
}
#[test]
@ -837,7 +837,7 @@ fn test_rebase_with_descendants() {
Parent commit : rlvkpnrz 2443ea76 a | a
Added 0 files, modified 0 files, removed 2 files
"#);
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r#"
@ d: a
c: a b
@ -845,7 +845,7 @@ fn test_rebase_with_descendants() {
a
"###);
"#);
// Same test as above, but with multiple commits per argument
test_env.jj_cmd_ok(&repo_path, &["undo"]);
@ -865,7 +865,7 @@ fn test_rebase_with_descendants() {
Parent commit : rlvkpnrz 2443ea76 a | a
Added 0 files, modified 0 files, removed 2 files
"#);
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r#"
@ d: a
c: a b
@ -873,7 +873,7 @@ fn test_rebase_with_descendants() {
a
"###);
"#);
}
#[test]
@ -1539,23 +1539,23 @@ fn test_rebase_after() {
Parent commit : nkmrtpmo 0d7e4ce9 e | e
Added 0 files, modified 0 files, removed 3 files
"###);
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
d: b1 b3
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r#"
c: b2 b4
c: b2 b4
b4: f
b2: f
@ f: e
e: b1 b3
b4: f
b2: f
@ f: e
e: b1 b3
d: b1 b3
b3: a
b1: a
a
"###);
"#);
test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
// Rebase a subgraph with four commits after one of the commits itself.

View File

@ -607,14 +607,14 @@ fn test_squash_from_to_partial() {
insta::assert_snapshot!(stderr, @r###"
Rebased 1 descendant commits
"###);
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
d2a587ae205d c
a53394306362 b
@ e0dac715116f d
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r#"
@ e0dac715116f d
d2a587ae205d c
a53394306362 b
b7b767179c44 a
000000000000 (empty)
"###);
"#);
// The selected change from the source has been applied
let stdout = test_env.jj_cmd_success(&repo_path, &["file", "show", "file1", "-r", "b"]);
insta::assert_snapshot!(stdout, @r###"

View File

@ -48,13 +48,13 @@ fn test_workspaces_add_second_workspace() {
// Can see the working-copy commit in each workspace in the log output. The "@"
// node in the graph indicates the current workspace's working-copy commit.
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###"
5ed2222c28e2 second@
@ 8183d0fcaa4c default@
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r"
@ 8183d0fcaa4c default@
5ed2222c28e2 second@
751b12b7b981
000000000000
"###);
");
insta::assert_snapshot!(get_log_output(&test_env, &secondary_path), @r###"
@ 5ed2222c28e2 second@
8183d0fcaa4c default@
@ -145,16 +145,16 @@ fn test_workspaces_add_second_workspace_on_merge() {
);
// The new workspace's working-copy commit shares all parents with the old one.
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###"
7013a493bd09 second@
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r"
@ 35e47bff781e default@
@ 35e47bff781e default@
7013a493bd09 second@
444b77e99d43
1694f2ddf8ec
000000000000
"###);
");
}
/// Test that --ignore-working-copy is respected
@ -282,14 +282,14 @@ fn test_workspaces_add_workspace_at_revision() {
// Can see the working-copy commit in each workspace in the log output. The "@"
// node in the graph indicates the current workspace's working-copy commit.
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###"
e374e74aa0c8 second@
@ dadeedb493e8 default@
c420244c6398
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r"
@ dadeedb493e8 default@
c420244c6398
e374e74aa0c8 second@
f6097c2f7cac
000000000000
"###);
");
insta::assert_snapshot!(get_log_output(&test_env, &secondary_path), @r###"
@ e374e74aa0c8 second@
dadeedb493e8 default@
@ -352,18 +352,18 @@ fn test_workspaces_add_workspace_multiple_revisions() {
Added 3 files, modified 0 files, removed 0 files
"###);
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###"
f4fa64f40944 merge@
f6097c2f7cac
544cd61f2d26
6c843d62ca29
@ 5b36783cd11c default@
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r"
@ 5b36783cd11c default@
f4fa64f40944 merge@
f6097c2f7cac
544cd61f2d26
6c843d62ca29
000000000000
"###);
");
}
#[test]
@ -471,13 +471,13 @@ fn test_workspaces_conflicting_edits() {
test_env.jj_cmd_ok(&main_path, &["workspace", "add", "../secondary"]);
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###"
3224de8ae048 secondary@
@ 06b57f44a3ca default@
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r"
@ 06b57f44a3ca default@
3224de8ae048 secondary@
506f4ec3c2c6
000000000000
"###);
");
// Make changes in both working copies
std::fs::write(main_path.join("file"), "changed in main\n").unwrap();
@ -526,27 +526,27 @@ fn test_workspaces_conflicting_edits() {
Updated working copy to fresh commit e82cd4ee8faa
"###);
insta::assert_snapshot!(get_log_output(&test_env, &secondary_path),
@r###"
× a28c85ce128b (divergent)
a58c9a9b19ce default@
@r"
@ e82cd4ee8faa secondary@ (divergent)
× a28c85ce128b (divergent)
@ e82cd4ee8faa secondary@ (divergent)
a58c9a9b19ce default@
d41244767d45
000000000000
"###);
");
// The stale working copy should have been resolved by the previous command
let stdout = get_log_output(&test_env, &secondary_path);
assert!(!stdout.starts_with("The working copy is stale"));
insta::assert_snapshot!(stdout, @r###"
× a28c85ce128b (divergent)
a58c9a9b19ce default@
insta::assert_snapshot!(stdout, @r"
@ e82cd4ee8faa secondary@ (divergent)
× a28c85ce128b (divergent)
@ e82cd4ee8faa secondary@ (divergent)
a58c9a9b19ce default@
d41244767d45
000000000000
"###);
");
}
/// Test a clean working copy that gets rewritten from another workspace
@ -562,13 +562,13 @@ fn test_workspaces_updated_by_other() {
test_env.jj_cmd_ok(&main_path, &["workspace", "add", "../secondary"]);
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###"
3224de8ae048 secondary@
@ 06b57f44a3ca default@
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r"
@ 06b57f44a3ca default@
3224de8ae048 secondary@
506f4ec3c2c6
000000000000
"###);
");
// Rewrite the check-out commit in one workspace.
std::fs::write(main_path.join("file"), "changed in main\n").unwrap();
@ -604,13 +604,13 @@ fn test_workspaces_updated_by_other() {
Updated working copy to fresh commit e82cd4ee8faa
"###);
insta::assert_snapshot!(get_log_output(&test_env, &secondary_path),
@r###"
a58c9a9b19ce default@
@ e82cd4ee8faa secondary@
@r"
@ e82cd4ee8faa secondary@
a58c9a9b19ce default@
d41244767d45
000000000000
"###);
");
}
/// Test a clean working copy that gets rewritten from another workspace
@ -628,13 +628,13 @@ fn test_workspaces_updated_by_other_automatic() {
test_env.jj_cmd_ok(&main_path, &["workspace", "add", "../secondary"]);
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###"
3224de8ae048 secondary@
@ 06b57f44a3ca default@
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r"
@ 06b57f44a3ca default@
3224de8ae048 secondary@
506f4ec3c2c6
000000000000
"###);
");
// Rewrite the check-out commit in one workspace.
std::fs::write(main_path.join("file"), "changed in main\n").unwrap();
@ -669,13 +669,13 @@ fn test_workspaces_updated_by_other_automatic() {
"###);
insta::assert_snapshot!(get_log_output(&test_env, &secondary_path),
@r###"
a58c9a9b19ce default@
@ e82cd4ee8faa secondary@
@r"
@ e82cd4ee8faa secondary@
a58c9a9b19ce default@
d41244767d45
000000000000
"###);
");
}
#[test_case(false; "manual")]
@ -750,13 +750,13 @@ fn test_workspaces_current_op_discarded_by_other(automatic: bool) {
test_env.jj_cmd_ok(&main_path, &["util", "gc", "--expire=now"]);
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###"
96b31dafdc41 secondary@
@ 6c051bd1ccd5 default@
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r"
@ 6c051bd1ccd5 default@
96b31dafdc41 secondary@
7c5b25a4fc8f
000000000000
"###);
");
}
if automatic {
@ -793,14 +793,14 @@ fn test_workspaces_current_op_discarded_by_other(automatic: bool) {
}
insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###"
15df8cb57d3f secondary@
96b31dafdc41
@ 6c051bd1ccd5 default@
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r"
@ 6c051bd1ccd5 default@
15df8cb57d3f secondary@
96b31dafdc41
7c5b25a4fc8f
000000000000
"###);
");
}
// The sparse patterns should remain
@ -1037,12 +1037,12 @@ fn test_workspaces_forget_abandon_commits() {
second: uuqppmxq 57d63245 (empty) (no description set)
third: uuqppmxq 57d63245 (empty) (no description set)
"###);
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###"
57d63245a308 fourth@ second@ third@
@ 4e8f9d2be039 default@
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r"
@ 4e8f9d2be039 default@
57d63245a308 fourth@ second@ third@
000000000000
"###);
");
// delete the default workspace (should not abandon commit since not empty)
test_env.jj_cmd_success(&main_path, &["workspace", "forget", "default"]);
@ -1247,12 +1247,12 @@ fn test_workspaces_rename_workspace() {
"###);
// Can see the working-copy commit in each workspace in the log output.
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###"
57d63245a308 third@
@ 230dd059e1b0 default@
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r"
@ 230dd059e1b0 default@
57d63245a308 third@
000000000000
"###);
");
insta::assert_snapshot!(get_log_output(&test_env, &secondary_path), @r###"
@ 57d63245a308 third@
230dd059e1b0 default@

View File

@ -121,9 +121,10 @@ impl<N> Iterator for ReverseGraphIterator<N> {
/// branches will be visited. At merge point, the second (or the last) ancestor
/// branch will be visited first. This is practically [the same as Git][Git].
///
/// The branch containing the first commit in the input iterator will be emitted
/// first. It is often the working-copy ancestor branch. The other head branches
/// won't be enqueued eagerly, and will be emitted as late as possible.
/// If no branches are prioritized, the branch containing the first commit in
/// the input iterator will be emitted first. It is often the working-copy
/// ancestor branch. The other head branches won't be enqueued eagerly, and will
/// be emitted as late as possible.
///
/// [Git]: https://github.blog/2022-08-30-gits-database-internals-ii-commit-history-queries/#topological-sorting
#[derive(Clone, Debug)]
@ -133,7 +134,8 @@ pub struct TopoGroupedGraphIterator<N, I> {
nodes: HashMap<N, TopoGroupedGraphNode<N>>,
/// Stack of graph nodes to be emitted.
emittable_ids: Vec<N>,
/// List of new head nodes found while processing unpopulated nodes.
/// List of new head nodes found while processing unpopulated nodes, or
/// prioritized branch nodes added by caller.
new_head_ids: VecDeque<N>,
/// Set of nodes which may be ancestors of `new_head_ids`.
blocked_ids: HashSet<N>,
@ -173,6 +175,22 @@ where
}
}
/// Makes the branch containing the specified node be emitted earlier than
/// the others.
///
/// The `id` usually points to a head node, but this isn't a requirement.
/// If the specified node isn't a head, all preceding nodes will be queued.
///
/// The specified node must exist in the input iterator. If it didn't, the
/// iterator would panic.
pub fn prioritize_branch(&mut self, id: N) {
// Mark existence of unpopulated node
self.nodes.entry(id.clone()).or_default();
// Push to non-emitting list so the prioritized heads wouldn't be
// interleaved
self.new_head_ids.push_back(id);
}
fn populate_one(&mut self) -> Result<Option<()>, E> {
let (current_id, edges) = match self.input_iter.next() {
Some(Ok(data)) => data,
@ -278,7 +296,8 @@ where
}
let Some(edges) = current_node.edges.take() else {
// Not yet populated
self.populate_one()?.expect("parent node should exist");
self.populate_one()?
.expect("parent or prioritized node should exist");
continue;
};
// The second (or the last) parent will be visited first
@ -1532,6 +1551,296 @@ mod tests {
"###);
}
#[test]
fn test_topo_grouped_prioritized_branches_trivial_fork() {
// The same setup as test_topo_grouped_trivial_fork()
let graph = [
('E', vec![direct('B')]),
('D', vec![direct('A')]),
('C', vec![direct('B')]),
('B', vec![direct('A')]),
('A', vec![]),
]
.map(Ok);
insta::assert_snapshot!(format_graph(graph.iter().cloned()), @r"
E direct(B)
D direct(A)
C direct(B)
B direct(A)
A
");
// Emit the branch C first
let mut iter = topo_grouped(graph.iter().cloned());
iter.prioritize_branch('C');
insta::assert_snapshot!(format_graph(iter), @r"
C direct(B)
E direct(B)
B direct(A)
D direct(A)
A
");
// Emit the branch D first
let mut iter = topo_grouped(graph.iter().cloned());
iter.prioritize_branch('D');
insta::assert_snapshot!(format_graph(iter), @r"
D direct(A)
E direct(B)
C direct(B)
B direct(A)
A
");
// Emit the branch C first, then D. E is emitted earlier than D because
// E belongs to the branch C compared to the branch D.
let mut iter = topo_grouped(graph.iter().cloned());
iter.prioritize_branch('C');
iter.prioritize_branch('D');
insta::assert_snapshot!(format_graph(iter), @r"
C direct(B)
E direct(B)
B direct(A)
D direct(A)
A
");
// Non-head node can be prioritized
let mut iter = topo_grouped(graph.iter().cloned());
iter.prioritize_branch('B');
insta::assert_snapshot!(format_graph(iter), @r"
E direct(B)
C direct(B)
B direct(A)
D direct(A)
A
");
// Root node can be prioritized
let mut iter = topo_grouped(graph.iter().cloned());
iter.prioritize_branch('A');
insta::assert_snapshot!(format_graph(iter), @r"
D direct(A)
E direct(B)
C direct(B)
B direct(A)
A
");
}
#[test]
fn test_topo_grouped_prioritized_branches_fork_multiple_heads() {
// The same setup as test_topo_grouped_fork_multiple_heads()
let graph = [
('I', vec![direct('E')]),
('H', vec![direct('C')]),
('G', vec![direct('A')]),
('F', vec![direct('E')]),
('E', vec![direct('C')]),
('D', vec![direct('C')]),
('C', vec![direct('A')]),
('B', vec![direct('A')]),
('A', vec![]),
]
.map(Ok);
insta::assert_snapshot!(format_graph(graph.iter().cloned()), @r"
I direct(E)
H direct(C)
G direct(A)
F direct(E)
E direct(C)
D direct(C)
C direct(A)
B direct(A)
A
");
// Emit B, G, then remainders
let mut iter = topo_grouped(graph.iter().cloned());
iter.prioritize_branch('B');
iter.prioritize_branch('G');
insta::assert_snapshot!(format_graph(iter), @r"
B direct(A)
G direct(A)
I direct(E)
F direct(E)
E direct(C)
H direct(C)
D direct(C)
C direct(A)
A
");
// Emit D, H, then descendants of C. The order of B and G is not
// respected because G can be found earlier through C->A->G. At this
// point, B is not populated yet, so A is blocked only by {G}. This is
// a limitation of the current node reordering logic.
let mut iter = topo_grouped(graph.iter().cloned());
iter.prioritize_branch('D');
iter.prioritize_branch('H');
iter.prioritize_branch('B');
iter.prioritize_branch('G');
insta::assert_snapshot!(format_graph(iter), @r"
D direct(C)
H direct(C)
I direct(E)
F direct(E)
E direct(C)
C direct(A)
G direct(A)
B direct(A)
A
");
}
#[test]
fn test_topo_grouped_prioritized_branches_fork_parallel() {
// The same setup as test_topo_grouped_fork_parallel()
let graph = [
// Pull all sub graphs in reverse order:
('I', vec![direct('A')]),
('H', vec![direct('C')]),
('G', vec![direct('E')]),
// Orphan sub graph G,F-E:
('F', vec![direct('E')]),
('E', vec![missing('Y')]),
// Orphan sub graph H,D-C:
('D', vec![direct('C')]),
('C', vec![missing('X')]),
// Orphan sub graph I,B-A:
('B', vec![direct('A')]),
('A', vec![]),
]
.map(Ok);
insta::assert_snapshot!(format_graph(graph.iter().cloned()), @r"
I direct(A)
H direct(C)
G direct(E)
F direct(E)
E missing(Y)
~
D direct(C)
C missing(X)
~
B direct(A)
A
");
// Emit the sub graph G first
let mut iter = topo_grouped(graph.iter().cloned());
iter.prioritize_branch('G');
insta::assert_snapshot!(format_graph(iter), @r"
G direct(E)
F direct(E)
E missing(Y)
~
I direct(A)
B direct(A)
A
H direct(C)
D direct(C)
C missing(X)
~
");
// Emit sub graphs in reverse order by selecting roots
let mut iter = topo_grouped(graph.iter().cloned());
iter.prioritize_branch('E');
iter.prioritize_branch('C');
iter.prioritize_branch('A');
insta::assert_snapshot!(format_graph(iter), @r"
G direct(E)
F direct(E)
E missing(Y)
~
H direct(C)
D direct(C)
C missing(X)
~
I direct(A)
B direct(A)
A
");
}
#[test]
fn test_topo_grouped_requeue_unpopulated() {
let graph = [