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 `--insert-before` options to customize the location of the duplicated
revisions. revisions.
* `jj log` now displays the working-copy branch first.
* New `fork_point()` revset function can be used to obtain the fork point * New `fork_point()` revset function can be used to obtain the fork point
of multiple commits. of multiple commits.

View File

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

View File

@ -940,12 +940,12 @@ fn test_bookmark_track_untrack() {
feature2@origin: sptzoqmo 7b33f629 commit 1 feature2@origin: sptzoqmo 7b33f629 commit 1
main@origin: sptzoqmo 7b33f629 commit 1 main@origin: sptzoqmo 7b33f629 commit 1
"###); "###);
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###" insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r#"
feature1@origin feature2@origin main@origin 7b33f6295eda @ 230dd059e1b0
@ 230dd059e1b0 feature1@origin feature2@origin main@origin 7b33f6295eda
000000000000 000000000000
"###); "#);
// Track new bookmark. Local bookmark should be created. // Track new bookmark. Local bookmark should be created.
test_env.jj_cmd_ok( test_env.jj_cmd_ok(
@ -988,12 +988,12 @@ fn test_bookmark_track_untrack() {
main: sptzoqmo 7b33f629 commit 1 main: sptzoqmo 7b33f629 commit 1
@origin: sptzoqmo 7b33f629 commit 1 @origin: sptzoqmo 7b33f629 commit 1
"###); "###);
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###" insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r#"
feature1 feature1@origin feature2@origin main 7b33f6295eda @ 230dd059e1b0
@ 230dd059e1b0 feature1 feature1@origin feature2@origin main 7b33f6295eda
000000000000 000000000000
"###); "#);
// Fetch new commit. Only tracking bookmark "main" should be merged. // Fetch new commit. Only tracking bookmark "main" should be merged.
create_remote_commit( create_remote_commit(
@ -1018,14 +1018,14 @@ fn test_bookmark_track_untrack() {
main: mmqqkyyt 40dabdaf commit 2 main: mmqqkyyt 40dabdaf commit 2
@origin: mmqqkyyt 40dabdaf commit 2 @origin: mmqqkyyt 40dabdaf commit 2
"###); "###);
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###" insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r#"
feature1@origin feature2@origin main 40dabdaf4abe @ 230dd059e1b0
feature1@origin feature2@origin main 40dabdaf4abe
feature1 7b33f6295eda feature1 7b33f6295eda
@ 230dd059e1b0
000000000000 000000000000
"###); "#);
// Fetch new commit with auto tracking. Tracking bookmark "main" and new // Fetch new commit with auto tracking. Tracking bookmark "main" and new
// bookmark "feature3" should be merged. // bookmark "feature3" should be merged.
@ -1057,14 +1057,14 @@ fn test_bookmark_track_untrack() {
main: wwnpyzpo 3f0f86fa commit 3 main: wwnpyzpo 3f0f86fa commit 3
@origin: wwnpyzpo 3f0f86fa commit 3 @origin: wwnpyzpo 3f0f86fa commit 3
"###); "###);
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###" insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r#"
feature1@origin feature2@origin feature3 main 3f0f86fa0e57 @ 230dd059e1b0
feature1@origin feature2@origin feature3 main 3f0f86fa0e57
feature1 7b33f6295eda feature1 7b33f6295eda
@ 230dd059e1b0
000000000000 000000000000
"###); "#);
} }
#[test] #[test]
@ -1447,9 +1447,9 @@ fn test_bookmark_list_filtered() {
&local_path, &local_path,
&["log", "-r::(bookmarks() | remote_bookmarks())", "-T", template], &["log", "-r::(bookmarks() | remote_bookmarks())", "-T", template],
), ),
@r###" @r#"
e31634b64294 remote-rewrite* @ c7b4c09cd77c local-keep
@ c7b4c09cd77c local-keep e31634b64294 remote-rewrite*
3e9a5af6ef15 remote-rewrite@origin (hidden) 3e9a5af6ef15 remote-rewrite@origin (hidden)
@ -1458,7 +1458,7 @@ fn test_bookmark_list_filtered() {
911e912015fb remote-keep 911e912015fb remote-keep
000000000000 000000000000
"###); "#);
// All bookmarks are listed by default. // All bookmarks are listed by default.
let (stdout, stderr) = test_env.jj_cmd_ok(&local_path, &["bookmark", "list"]); 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", "@-"], &["describe", "-m", "description 2", "--at-operation", "@-"],
); );
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["log"]); let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["log"]);
insta::assert_snapshot!(stdout, @r###" insta::assert_snapshot!(stdout, @r#"
qpvuntsm?? test.user@example.com 2001-02-03 08:05:10 6ba70e00 @ qpvuntsm?? test.user@example.com 2001-02-03 08:05:08 ff309c29
description 2 description 1
@ qpvuntsm?? test.user@example.com 2001-02-03 08:05:08 ff309c29 qpvuntsm?? test.user@example.com 2001-02-03 08:05:10 6ba70e00
description 1 description 2
zzzzzzzz root() 00000000 zzzzzzzz root() 00000000
"###); "#);
insta::assert_snapshot!(stderr, @r###" insta::assert_snapshot!(stderr, @r###"
Concurrent modification detected, resolving automatically. Concurrent modification detected, resolving automatically.
"###); "###);
// Color // Color
let stdout = test_env.jj_cmd_success(&repo_path, &["log", "--color=always"]); let stdout = test_env.jj_cmd_success(&repo_path, &["log", "--color=always"]);
insta::assert_snapshot!(stdout, @r###" insta::assert_snapshot!(stdout, @r#"
qpvuntsm?? test.user@example.com 2001-02-03 08:05:10 6ba70e00 @ qpvuntsm?? test.user@example.com 2001-02-03 08:05:08 ff309c29
description 2 description 1
@ qpvuntsm?? test.user@example.com 2001-02-03 08:05:08 ff309c29 qpvuntsm?? test.user@example.com 2001-02-03 08:05:10 6ba70e00
description 1 description 2
 zzzzzzzz root() 00000000  zzzzzzzz root() 00000000
"###); "#);
// Evolog and hidden divergent // Evolog and hidden divergent
let stdout = test_env.jj_cmd_success(&repo_path, &["evolog"]); 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 template = r#"commit_id.short() ++ " " ++ if(bookmarks, bookmarks, "(no bookmarks)")"#;
let output = test_env.jj_cmd_success(&workspace_root, &["log", "-T", template]); let output = test_env.jj_cmd_success(&workspace_root, &["log", "-T", template]);
insta::assert_snapshot!(output, @r###" insta::assert_snapshot!(output, @r#"
fed794e2ba44 bookmark3?? bookmark3@origin @ a5b4d15489cc bookmark2* new-bookmark
8476341eb395 bookmark2@origin unchanged
fed794e2ba44 bookmark3?? bookmark3@origin
b1bb3766d584 bookmark3?? b1bb3766d584 bookmark3??
4a7e4246fc4d bookmark1* 4a7e4246fc4d bookmark1*
@ a5b4d15489cc bookmark2* new-bookmark
8476341eb395 bookmark2@origin unchanged
000000000000 (no bookmarks) 000000000000 (no bookmarks)
"###); "#);
let template = r#"bookmarks.map(|b| separate("/", b.remote(), b.name())).join(", ")"#; let template = r#"bookmarks.map(|b| separate("/", b.remote(), b.name())).join(", ")"#;
let output = test_env.jj_cmd_success(&workspace_root, &["log", "-T", template]); let output = test_env.jj_cmd_success(&workspace_root, &["log", "-T", template]);
insta::assert_snapshot!(output, @r###" insta::assert_snapshot!(output, @r#"
bookmark3, origin/bookmark3 @ bookmark2, new-bookmark
origin/bookmark2, unchanged
bookmark3, origin/bookmark3
bookmark3 bookmark3
bookmark1 bookmark1
@ bookmark2, new-bookmark
origin/bookmark2, unchanged
"###); "#);
let template = r#"separate(" ", "L:", local_bookmarks, "R:", remote_bookmarks)"#; let template = r#"separate(" ", "L:", local_bookmarks, "R:", remote_bookmarks)"#;
let output = test_env.jj_cmd_success(&workspace_root, &["log", "-T", template]); let output = test_env.jj_cmd_success(&workspace_root, &["log", "-T", template]);
insta::assert_snapshot!(output, @r###" insta::assert_snapshot!(output, @r#"
L: bookmark3?? R: bookmark3@origin @ L: bookmark2* new-bookmark R:
L: unchanged R: bookmark2@origin unchanged@origin
L: bookmark3?? R: bookmark3@origin
L: bookmark3?? R: L: bookmark3?? R:
L: bookmark1* R: L: bookmark1* R:
@ L: bookmark2* new-bookmark R:
L: unchanged R: bookmark2@origin unchanged@origin
L: R: L: R:
"###); "#);
let template = r#" let template = r#"
remote_bookmarks.map(|ref| concat( remote_bookmarks.map(|ref| concat(

View File

@ -50,12 +50,12 @@ fn test_concurrent_operation_divergence() {
// We should be informed about the concurrent modification // We should be informed about the concurrent modification
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["log", "-T", "description"]); let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["log", "-T", "description"]);
insta::assert_snapshot!(stdout, @r###" insta::assert_snapshot!(stdout, @r#"
message 2 @ message 1
@ message 1 message 2
"###); "#);
insta::assert_snapshot!(stderr, @r###" insta::assert_snapshot!(stderr, @r###"
Concurrent modification detected, resolving automatically. 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 // "original C" and "B_to_delete" are abandoned, as the corresponding bookmarks
// were deleted or moved on the remote (#864) // were deleted or moved on the remote (#864)
insta::assert_snapshot!(get_log_output(&test_env, &clone_path), @r#" 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 a7e4cec4256b7995129b9d1e1bda7e1df6e60678 A git_head() A
0000000000000000000000000000000000000000 0000000000000000000000000000000000000000

View File

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

View File

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

View File

@ -776,12 +776,12 @@ fn test_log_divergence() {
&["describe", "-m", "description 2", "--at-operation", "@-"], &["describe", "-m", "description 2", "--at-operation", "@-"],
); );
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["log", "-T", template]); let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["log", "-T", template]);
insta::assert_snapshot!(stdout, @r###" insta::assert_snapshot!(stdout, @r#"
description 2 !divergence! @ description 1 !divergence!
@ description 1 !divergence! description 2 !divergence!
"###); "#);
insta::assert_snapshot!(stderr, @r###" insta::assert_snapshot!(stderr, @r###"
Concurrent modification detected, resolving automatically. 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, &["commit", "-m", "left-2"]);
test_env.jj_cmd_ok(&repo_path, &["edit", "description(right-wc)"]); test_env.jj_cmd_ok(&repo_path, &["edit", "description(right-wc)"]);
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###" insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r#"
vruxwmqvtpmx left-2 zsuskulnrvyr right-2
yqosqzytrlsw left-1 kkmpptxzrspx right-1
royxmykxtrkr left-wc @ rlvkpnrzqnoo right-wc
zsuskulnrvyr right-2 vruxwmqvtpmx left-2
kkmpptxzrspx right-1 yqosqzytrlsw left-1
@ rlvkpnrzqnoo right-wc royxmykxtrkr left-wc
qpvuntsmwlqt base qpvuntsmwlqt base
zzzzzzzzzzzz zzzzzzzzzzzz
"###); "#);
test_env.jj_cmd_ok(&repo_path, &["next", "2"]); test_env.jj_cmd_ok(&repo_path, &["next", "2"]);
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###" 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. // The repo should no longer be corrupt.
let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["log"]); let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["log"]);
insta::assert_snapshot!(stdout, @r#" insta::assert_snapshot!(stdout, @r#"
mzvwutvl?? test.user@example.com 2001-02-03 08:05:15 dc2c6d52 @ mzvwutvl?? test.user@example.com 2001-02-03 08:05:12 6d868f04
(empty) 4.1 (empty) 4
@ mzvwutvl?? test.user@example.com 2001-02-03 08:05:12 6d868f04 mzvwutvl?? test.user@example.com 2001-02-03 08:05:15 dc2c6d52
(empty) 4 (empty) 4.1
zsuskuln test.user@example.com 2001-02-03 08:05:10 git_head() f652c321 zsuskuln test.user@example.com 2001-02-03 08:05:10 git_head() f652c321
(empty) (no description set) (empty) (no description set)
zzzzzzzz root() 00000000 zzzzzzzz root() 00000000

View File

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

View File

@ -561,12 +561,12 @@ fn test_rebase_multiple_revisions() {
Parent commit : kmkuslsw d1bfda8c f | f Parent commit : kmkuslsw d1bfda8c f | f
Added 0 files, modified 0 files, removed 2 files 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#"
h: g e: d
g: f d: i
e: d @ i: f
d: i h: g
@ i: f g: f
f: c a f: c a
@ -575,7 +575,7 @@ fn test_rebase_multiple_revisions() {
a a
"###); "#);
} }
#[test] #[test]
@ -837,7 +837,7 @@ fn test_rebase_with_descendants() {
Parent commit : rlvkpnrz 2443ea76 a | a Parent commit : rlvkpnrz 2443ea76 a | a
Added 0 files, modified 0 files, removed 2 files 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 @ d: a
c: a b c: a b
@ -845,7 +845,7 @@ fn test_rebase_with_descendants() {
a a
"###); "#);
// Same test as above, but with multiple commits per argument // Same test as above, but with multiple commits per argument
test_env.jj_cmd_ok(&repo_path, &["undo"]); test_env.jj_cmd_ok(&repo_path, &["undo"]);
@ -865,7 +865,7 @@ fn test_rebase_with_descendants() {
Parent commit : rlvkpnrz 2443ea76 a | a Parent commit : rlvkpnrz 2443ea76 a | a
Added 0 files, modified 0 files, removed 2 files 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 @ d: a
c: a b c: a b
@ -873,7 +873,7 @@ fn test_rebase_with_descendants() {
a a
"###); "#);
} }
#[test] #[test]
@ -1539,23 +1539,23 @@ fn test_rebase_after() {
Parent commit : nkmrtpmo 0d7e4ce9 e | e Parent commit : nkmrtpmo 0d7e4ce9 e | e
Added 0 files, modified 0 files, removed 3 files Added 0 files, modified 0 files, removed 3 files
"###); "###);
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###" insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r#"
d: b1 b3 c: b2 b4
c: b2 b4 b4: f
b2: f
b4: f
b2: f @ f: e
e: b1 b3
@ f: e
e: b1 b3 d: b1 b3
b3: a b3: a
b1: a b1: a
a a
"###); "#);
test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]); test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
// Rebase a subgraph with four commits after one of the commits itself. // 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###" insta::assert_snapshot!(stderr, @r###"
Rebased 1 descendant commits Rebased 1 descendant commits
"###); "###);
insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###" insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r#"
d2a587ae205d c @ e0dac715116f d
a53394306362 b d2a587ae205d c
@ e0dac715116f d a53394306362 b
b7b767179c44 a b7b767179c44 a
000000000000 (empty) 000000000000 (empty)
"###); "#);
// The selected change from the source has been applied // The selected change from the source has been applied
let stdout = test_env.jj_cmd_success(&repo_path, &["file", "show", "file1", "-r", "b"]); let stdout = test_env.jj_cmd_success(&repo_path, &["file", "show", "file1", "-r", "b"]);
insta::assert_snapshot!(stdout, @r###" 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 "@" // 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. // node in the graph indicates the current workspace's working-copy commit.
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###" insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r"
5ed2222c28e2 second@ @ 8183d0fcaa4c default@
@ 8183d0fcaa4c default@ 5ed2222c28e2 second@
751b12b7b981 751b12b7b981
000000000000 000000000000
"###); ");
insta::assert_snapshot!(get_log_output(&test_env, &secondary_path), @r###" insta::assert_snapshot!(get_log_output(&test_env, &secondary_path), @r###"
@ 5ed2222c28e2 second@ @ 5ed2222c28e2 second@
8183d0fcaa4c default@ 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. // 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###" insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r"
7013a493bd09 second@ @ 35e47bff781e default@
@ 35e47bff781e default@ 7013a493bd09 second@
444b77e99d43 444b77e99d43
1694f2ddf8ec 1694f2ddf8ec
000000000000 000000000000
"###); ");
} }
/// Test that --ignore-working-copy is respected /// 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 "@" // 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. // node in the graph indicates the current workspace's working-copy commit.
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###" insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r"
e374e74aa0c8 second@ @ dadeedb493e8 default@
@ dadeedb493e8 default@ c420244c6398
c420244c6398 e374e74aa0c8 second@
f6097c2f7cac f6097c2f7cac
000000000000 000000000000
"###); ");
insta::assert_snapshot!(get_log_output(&test_env, &secondary_path), @r###" insta::assert_snapshot!(get_log_output(&test_env, &secondary_path), @r###"
@ e374e74aa0c8 second@ @ e374e74aa0c8 second@
dadeedb493e8 default@ dadeedb493e8 default@
@ -352,18 +352,18 @@ fn test_workspaces_add_workspace_multiple_revisions() {
Added 3 files, modified 0 files, removed 0 files Added 3 files, modified 0 files, removed 0 files
"###); "###);
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###" insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r"
f4fa64f40944 merge@ @ 5b36783cd11c default@
f4fa64f40944 merge@
f6097c2f7cac
544cd61f2d26 f6097c2f7cac
6c843d62ca29 544cd61f2d26
@ 5b36783cd11c default@ 6c843d62ca29
000000000000 000000000000
"###); ");
} }
#[test] #[test]
@ -471,13 +471,13 @@ fn test_workspaces_conflicting_edits() {
test_env.jj_cmd_ok(&main_path, &["workspace", "add", "../secondary"]); test_env.jj_cmd_ok(&main_path, &["workspace", "add", "../secondary"]);
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###" insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r"
3224de8ae048 secondary@ @ 06b57f44a3ca default@
@ 06b57f44a3ca default@ 3224de8ae048 secondary@
506f4ec3c2c6 506f4ec3c2c6
000000000000 000000000000
"###); ");
// Make changes in both working copies // Make changes in both working copies
std::fs::write(main_path.join("file"), "changed in main\n").unwrap(); 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 Updated working copy to fresh commit e82cd4ee8faa
"###); "###);
insta::assert_snapshot!(get_log_output(&test_env, &secondary_path), insta::assert_snapshot!(get_log_output(&test_env, &secondary_path),
@r###" @r"
× a28c85ce128b (divergent) @ e82cd4ee8faa secondary@ (divergent)
a58c9a9b19ce default@ × a28c85ce128b (divergent)
@ e82cd4ee8faa secondary@ (divergent) a58c9a9b19ce default@
d41244767d45 d41244767d45
000000000000 000000000000
"###); ");
// The stale working copy should have been resolved by the previous command // The stale working copy should have been resolved by the previous command
let stdout = get_log_output(&test_env, &secondary_path); let stdout = get_log_output(&test_env, &secondary_path);
assert!(!stdout.starts_with("The working copy is stale")); assert!(!stdout.starts_with("The working copy is stale"));
insta::assert_snapshot!(stdout, @r###" insta::assert_snapshot!(stdout, @r"
× a28c85ce128b (divergent) @ e82cd4ee8faa secondary@ (divergent)
a58c9a9b19ce default@ × a28c85ce128b (divergent)
@ e82cd4ee8faa secondary@ (divergent) a58c9a9b19ce default@
d41244767d45 d41244767d45
000000000000 000000000000
"###); ");
} }
/// Test a clean working copy that gets rewritten from another workspace /// 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"]); test_env.jj_cmd_ok(&main_path, &["workspace", "add", "../secondary"]);
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###" insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r"
3224de8ae048 secondary@ @ 06b57f44a3ca default@
@ 06b57f44a3ca default@ 3224de8ae048 secondary@
506f4ec3c2c6 506f4ec3c2c6
000000000000 000000000000
"###); ");
// Rewrite the check-out commit in one workspace. // Rewrite the check-out commit in one workspace.
std::fs::write(main_path.join("file"), "changed in main\n").unwrap(); 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 Updated working copy to fresh commit e82cd4ee8faa
"###); "###);
insta::assert_snapshot!(get_log_output(&test_env, &secondary_path), insta::assert_snapshot!(get_log_output(&test_env, &secondary_path),
@r###" @r"
a58c9a9b19ce default@ @ e82cd4ee8faa secondary@
@ e82cd4ee8faa secondary@ a58c9a9b19ce default@
d41244767d45 d41244767d45
000000000000 000000000000
"###); ");
} }
/// Test a clean working copy that gets rewritten from another workspace /// 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"]); test_env.jj_cmd_ok(&main_path, &["workspace", "add", "../secondary"]);
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###" insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r"
3224de8ae048 secondary@ @ 06b57f44a3ca default@
@ 06b57f44a3ca default@ 3224de8ae048 secondary@
506f4ec3c2c6 506f4ec3c2c6
000000000000 000000000000
"###); ");
// Rewrite the check-out commit in one workspace. // Rewrite the check-out commit in one workspace.
std::fs::write(main_path.join("file"), "changed in main\n").unwrap(); 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), insta::assert_snapshot!(get_log_output(&test_env, &secondary_path),
@r###" @r"
a58c9a9b19ce default@ @ e82cd4ee8faa secondary@
@ e82cd4ee8faa secondary@ a58c9a9b19ce default@
d41244767d45 d41244767d45
000000000000 000000000000
"###); ");
} }
#[test_case(false; "manual")] #[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"]); test_env.jj_cmd_ok(&main_path, &["util", "gc", "--expire=now"]);
insta::allow_duplicates! { insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###" insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r"
96b31dafdc41 secondary@ @ 6c051bd1ccd5 default@
@ 6c051bd1ccd5 default@ 96b31dafdc41 secondary@
7c5b25a4fc8f 7c5b25a4fc8f
000000000000 000000000000
"###); ");
} }
if automatic { if automatic {
@ -793,14 +793,14 @@ fn test_workspaces_current_op_discarded_by_other(automatic: bool) {
} }
insta::allow_duplicates! { insta::allow_duplicates! {
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###" insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r"
15df8cb57d3f secondary@ @ 6c051bd1ccd5 default@
96b31dafdc41 15df8cb57d3f secondary@
@ 6c051bd1ccd5 default@ 96b31dafdc41
7c5b25a4fc8f 7c5b25a4fc8f
000000000000 000000000000
"###); ");
} }
// The sparse patterns should remain // The sparse patterns should remain
@ -1037,12 +1037,12 @@ fn test_workspaces_forget_abandon_commits() {
second: uuqppmxq 57d63245 (empty) (no description set) second: uuqppmxq 57d63245 (empty) (no description set)
third: uuqppmxq 57d63245 (empty) (no description set) third: uuqppmxq 57d63245 (empty) (no description set)
"###); "###);
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###" insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r"
57d63245a308 fourth@ second@ third@ @ 4e8f9d2be039 default@
@ 4e8f9d2be039 default@ 57d63245a308 fourth@ second@ third@
000000000000 000000000000
"###); ");
// delete the default workspace (should not abandon commit since not empty) // delete the default workspace (should not abandon commit since not empty)
test_env.jj_cmd_success(&main_path, &["workspace", "forget", "default"]); 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. // Can see the working-copy commit in each workspace in the log output.
insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r###" insta::assert_snapshot!(get_log_output(&test_env, &main_path), @r"
57d63245a308 third@ @ 230dd059e1b0 default@
@ 230dd059e1b0 default@ 57d63245a308 third@
000000000000 000000000000
"###); ");
insta::assert_snapshot!(get_log_output(&test_env, &secondary_path), @r###" insta::assert_snapshot!(get_log_output(&test_env, &secondary_path), @r###"
@ 57d63245a308 third@ @ 57d63245a308 third@
230dd059e1b0 default@ 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 /// 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]. /// 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 /// If no branches are prioritized, the branch containing the first commit in
/// first. It is often the working-copy ancestor branch. The other head branches /// the input iterator will be emitted first. It is often the working-copy
/// won't be enqueued eagerly, and will be emitted as late as possible. /// 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 /// [Git]: https://github.blog/2022-08-30-gits-database-internals-ii-commit-history-queries/#topological-sorting
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -133,7 +134,8 @@ pub struct TopoGroupedGraphIterator<N, I> {
nodes: HashMap<N, TopoGroupedGraphNode<N>>, nodes: HashMap<N, TopoGroupedGraphNode<N>>,
/// Stack of graph nodes to be emitted. /// Stack of graph nodes to be emitted.
emittable_ids: Vec<N>, 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>, new_head_ids: VecDeque<N>,
/// Set of nodes which may be ancestors of `new_head_ids`. /// Set of nodes which may be ancestors of `new_head_ids`.
blocked_ids: HashSet<N>, 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> { fn populate_one(&mut self) -> Result<Option<()>, E> {
let (current_id, edges) = match self.input_iter.next() { let (current_id, edges) = match self.input_iter.next() {
Some(Ok(data)) => data, Some(Ok(data)) => data,
@ -278,7 +296,8 @@ where
} }
let Some(edges) = current_node.edges.take() else { let Some(edges) = current_node.edges.take() else {
// Not yet populated // Not yet populated
self.populate_one()?.expect("parent node should exist"); self.populate_one()?
.expect("parent or prioritized node should exist");
continue; continue;
}; };
// The second (or the last) parent will be visited first // 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] #[test]
fn test_topo_grouped_requeue_unpopulated() { fn test_topo_grouped_requeue_unpopulated() {
let graph = [ let graph = [