op_store: add a virtual root operation, similar to root commit

It seems obvious in hindsight to have a virtual root operation just
like we have a virtual root commit. It removes the same kind of
problems by making sure there's always a common ancestor (or multiple)
between any two commits.

I think the reason I didn't add a root operation from the beginning
was that there used to be a mandatory working-copy commit in the view
(this was before support for multiple workspaces).

Perhaps we should remove the "initialize repo" operation now. The only
difference between their view objects is that the "initialize repo"
operation adds the root commit as a head. We could add that to the
root operation, but then the root operation's value depends on the
commit backend.
This commit is contained in:
Martin von Zweigbergk 2024-01-07 10:32:31 -08:00 committed by Martin von Zweigbergk
parent c9af8bf43a
commit 6e302bb3a2
10 changed files with 183 additions and 104 deletions

View File

@ -19,6 +19,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* `jj branch rename` will now warn if the renamed branch has a remote branch, since
those will have to be manually renamed outside of `jj`.
* There's now a virtual root operation, similar to the [virtual root
commit](docs/glossary.md#root-commit). It appears at the end of `jj op log`.
### Fixed bugs

View File

@ -35,16 +35,18 @@ fn test_concurrent_operation_divergence() {
// "op log" doesn't merge the concurrent operations
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "log"]);
insta::assert_snapshot!(stdout, @r###"
a0cb96855e6b test-username@host.example.com 2001-02-03 04:05:09.000 +07:00 - 2001-02-03 04:05:09.000 +07:00
31e3dc1f7c87 test-username@host.example.com 2001-02-03 04:05:09.000 +07:00 - 2001-02-03 04:05:09.000 +07:00
describe commit 230dd059e1b059aefc0da06a2e5a7dbf22362f22
args: jj describe -m 'message 2' --at-op @-
c208376a3fee test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00
e914ad151dae test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00
describe commit 230dd059e1b059aefc0da06a2e5a7dbf22362f22
args: jj describe -m 'message 1'
90e23f7e6c7c test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
27143b59c690 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
add workspace 'default'
b73abeebcf94 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
initialize repo
0e8aee02e242 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
initialize repo
000000000000 @ 1970-01-01 00:00:00.000 +00:00 - 1970-01-01 00:00:00.000 +00:00
"###);
// We should be informed about the concurrent modification
@ -70,16 +72,18 @@ fn test_concurrent_operations_auto_rebase() {
test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "initial"]);
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "log"]);
insta::assert_snapshot!(stdout, @r###"
@ ec9298123a0d test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00
@ 621f22ef65b4 test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00
describe commit 123ed18e4c4c0d77428df41112bc02ffc83fb935
args: jj describe -m initial
7ba3da0db37d test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00
17cb042ae103 test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00
snapshot working copy
args: jj describe -m initial
90e23f7e6c7c test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
27143b59c690 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
add workspace 'default'
b73abeebcf94 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
initialize repo
0e8aee02e242 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
initialize repo
000000000000 @ 1970-01-01 00:00:00.000 +00:00 - 1970-01-01 00:00:00.000 +00:00
"###);
let op_id_hex = stdout[3..15].to_string();
@ -159,6 +163,7 @@ fn test_concurrent_operations_wc_modified() {
snapshot working copy
add workspace 'default'
initialize repo
"###);
}
@ -183,22 +188,24 @@ fn test_concurrent_snapshot_wc_reloadable() {
let template = r#"id ++ "\n" ++ description ++ "\n" ++ tags"#;
let op_log_stdout = test_env.jj_cmd_success(&repo_path, &["op", "log", "-T", template]);
insta::assert_snapshot!(op_log_stdout, @r###"
@ 4f927bf9d6694ed9c409828dc0f9f5f64897ba95ac850f63df92572164b5f146cc09c3217481e74aadbc3a581ded02a487dc73911fe03d86cf1b2c6a20bbe8ed
@ d726ace5612d3c99c793f61d5e86583f430fec332859af62a962f08c93cfac1459884371badcdaccbfd7b065c7df191d68fb35f6d537d1c0cc7ed18c70d6f1e5
commit 323b414dd255b51375d7f4392b7b2641ffe4289f
args: jj commit -m 'new child1'
def64cf8aa9890a8a5f607890cc6017103a1affbff84addd5f0546c9f9be732f6c2348c082cc81a0654928263978e3d9690e684b9a08214c420311e978487f08
5de87db156fa59f084be7beafb2e8921976cef54061d19671f1406f0f4bc3ad114e6bfee8d2e88e8d2614a49652b8f88071c48a9d78ff951dc81fcac8eb592f3
snapshot working copy
args: jj commit -m 'new child1'
42881255f9267d7713b5a2621eb1cabfe6654ac77913642522d5dd482b94b2dee321b75cda544b4252cfc4b18a6c43bcd291f9b1e2838e44843c38fba21d49fa
1145f7c8a11bf57eb9bf7ee241a72c19e0fa536a35c099d9e72ad25a4475293f48e1f29eab90e31fa8770566199bf384dcb4f44227635c20d1e21ae2b9700303
commit 3d918700494a9895696e955b85fa05eb0d314cc6
args: jj commit -m initial
8816f70c50c1393c80b6538c8833209802d554600de08adac94c1150ef81af323725f4ed2b52c78e6757c9821400de23483dad5f019ea1a1bf7d9ea49f6b91ab
a10989671b09d46b636fa4dee86182a170f2b6a9d127a785e8d1388b0680affcc4aeed8bba1face7f3fc1637b7f9327f6ac7f4d9384f468333805a215363ccbd
snapshot working copy
args: jj commit -m initial
90e23f7e6c7ce885df899e0b4990b39a43816549596a834b727d7eed9650cfe917ac2d043518734223fd7537b9d464e556d28e73cdfed7a0aceb592b89fdba4b
27143b59c6904046f6be83ad6fe145d819944f9abbd7247ea9c57848d1d2c678ea8265598a156fe8aeef31d24d958bf6cfa0c2eb3afef40bdae2c5e98d73d0ee
add workspace 'default'
b73abeebcf94a1528dfeeed335d6f8c8bac623d060254b01fb6b329d0407f1271eb2c16ecf56f6bac5de73dab988a1524dfb98bfff960eba5e54fa01547fea0d
initialize repo
0e8aee02e24230c99d6d90d469c582a60fdb2ae8329341bbdb09f4a0beceba1ce7c84fc9ba6c7657d6d275b392b89b825502475ad2501be1ddebd4a09b07668c
initialize repo
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
"###);
let op_log_lines = op_log_stdout.lines().collect_vec();
let current_op_id = op_log_lines[0].split_once(" ").unwrap().1;

View File

@ -127,7 +127,7 @@ fn test_debug_operation_id() {
let stdout =
test_env.jj_cmd_success(&workspace_path, &["debug", "operation", "--display", "id"]);
assert_snapshot!(filter_index_stats(&stdout), @r###"
90e23f7e6c7ce885df899e0b4990b39a43816549596a834b727d7eed9650cfe917ac2d043518734223fd7537b9d464e556d28e73cdfed7a0aceb592b89fdba4b
27143b59c6904046f6be83ad6fe145d819944f9abbd7247ea9c57848d1d2c678ea8265598a156fe8aeef31d24d958bf6cfa0c2eb3afef40bdae2c5e98d73d0ee
"###
);
}

View File

@ -38,13 +38,15 @@ fn test_op_log() {
],
);
insta::assert_snapshot!(&stdout, @r###"
@ 00cc30b6a1b8 test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00
@ 826c45dd2457 test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00
describe commit 230dd059e1b059aefc0da06a2e5a7dbf22362f22
args: jj describe -m 'description 0'
90e23f7e6c7c test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
27143b59c690 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
add workspace 'default'
b73abeebcf94 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
initialize repo
0e8aee02e242 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
initialize repo
000000000000 @ 1970-01-01 00:00:00.000 +00:00 - 1970-01-01 00:00:00.000 +00:00
"###);
let op_log_lines = stdout.lines().collect_vec();
let add_workspace_id = op_log_lines[3].split(' ').nth(2).unwrap();
@ -69,8 +71,8 @@ fn test_op_log() {
0000000000000000000000000000000000000000
"###);
insta::assert_snapshot!(
test_env.jj_cmd_failure(&repo_path, &["log", "--at-op", "@---"]), @r###"
Error: The "@---" expression resolved to no operations
test_env.jj_cmd_failure(&repo_path, &["log", "--at-op", "@----"]), @r###"
Error: The "@----" expression resolved to no operations
"###);
// We get a reasonable message if an invalid operation ID is specified
@ -115,10 +117,12 @@ fn test_op_log_no_graph() {
let stdout =
test_env.jj_cmd_success(&repo_path, &["op", "log", "--no-graph", "--color=always"]);
insta::assert_snapshot!(stdout, @r###"
90e23f7e6c7c test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
27143b59c690 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
add workspace 'default'
b73abeebcf94 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
0e8aee02e242 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
initialize repo
000000000000 @ 1970-01-01 00:00:00.000 +00:00 - 1970-01-01 00:00:00.000 +00:00
"###);
}
@ -140,7 +144,7 @@ fn test_op_log_no_graph_null_terminated() {
r#"id.short(4) ++ "\0""#,
],
);
insta::assert_debug_snapshot!(stdout, @r###""6dec\08542\090e2\0b73a\0""###);
insta::assert_debug_snapshot!(stdout, @r###""f5e4\05ff2\02714\00e8a\00000\0""###);
}
#[test]
@ -151,14 +155,16 @@ fn test_op_log_template() {
let render = |template| test_env.jj_cmd_success(&repo_path, &["op", "log", "-T", template]);
insta::assert_snapshot!(render(r#"id ++ "\n""#), @r###"
@ 90e23f7e6c7ce885df899e0b4990b39a43816549596a834b727d7eed9650cfe917ac2d043518734223fd7537b9d464e556d28e73cdfed7a0aceb592b89fdba4b
b73abeebcf94a1528dfeeed335d6f8c8bac623d060254b01fb6b329d0407f1271eb2c16ecf56f6bac5de73dab988a1524dfb98bfff960eba5e54fa01547fea0d
@ 27143b59c6904046f6be83ad6fe145d819944f9abbd7247ea9c57848d1d2c678ea8265598a156fe8aeef31d24d958bf6cfa0c2eb3afef40bdae2c5e98d73d0ee
0e8aee02e24230c99d6d90d469c582a60fdb2ae8329341bbdb09f4a0beceba1ce7c84fc9ba6c7657d6d275b392b89b825502475ad2501be1ddebd4a09b07668c
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
"###);
insta::assert_snapshot!(
render(r#"separate(" ", id.short(5), current_operation, user,
time.start(), time.end(), time.duration()) ++ "\n""#), @r###"
@ 90e23 true test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 2001-02-03 04:05:07.000 +07:00 less than a microsecond
b73ab false test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 2001-02-03 04:05:07.000 +07:00 less than a microsecond
@ 27143 true test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 2001-02-03 04:05:07.000 +07:00 less than a microsecond
0e8ae false test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 2001-02-03 04:05:07.000 +07:00 less than a microsecond
00000 false @ 1970-01-01 00:00:00.000 +00:00 1970-01-01 00:00:00.000 +00:00 less than a microsecond
"###);
// Negative length shouldn't cause panic (and is clamped.)
@ -166,6 +172,7 @@ fn test_op_log_template() {
insta::assert_snapshot!(render(r#"id.short(-1) ++ "|""#), @r###"
@ |
|
|
"###);
// Test the default template, i.e. with relative start time and duration. We
@ -180,10 +187,12 @@ fn test_op_log_template() {
let regex = Regex::new(r"\d\d years").unwrap();
let stdout = test_env.jj_cmd_success(&repo_path, &["op", "log"]);
insta::assert_snapshot!(regex.replace_all(&stdout, "NN years"), @r###"
@ 90e23f7e6c7c test-username@host.example.com NN years ago, lasted less than a microsecond
@ 27143b59c690 test-username@host.example.com NN years ago, lasted less than a microsecond
add workspace 'default'
b73abeebcf94 test-username@host.example.com NN years ago, lasted less than a microsecond
initialize repo
0e8aee02e242 test-username@host.example.com NN years ago, lasted less than a microsecond
initialize repo
000000000000 @ NN years ago, lasted less than a microsecond
"###);
}
@ -196,25 +205,30 @@ fn test_op_log_builtin_templates() {
test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "description 0"]);
insta::assert_snapshot!(render(r#"builtin_op_log_compact"#), @r###"
@ 00cc30b6a1b8 test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00
@ 826c45dd2457 test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00
describe commit 230dd059e1b059aefc0da06a2e5a7dbf22362f22
args: jj describe -m 'description 0'
90e23f7e6c7c test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
27143b59c690 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
add workspace 'default'
b73abeebcf94 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
initialize repo
0e8aee02e242 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
initialize repo
000000000000 @ 1970-01-01 00:00:00.000 +00:00 - 1970-01-01 00:00:00.000 +00:00
"###);
insta::assert_snapshot!(render(r#"builtin_op_log_comfortable"#), @r###"
@ 00cc30b6a1b8 test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00
@ 826c45dd2457 test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00
describe commit 230dd059e1b059aefc0da06a2e5a7dbf22362f22
args: jj describe -m 'description 0'
90e23f7e6c7c test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
27143b59c690 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
add workspace 'default'
b73abeebcf94 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
initialize repo
0e8aee02e242 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
initialize repo
000000000000 @ 1970-01-01 00:00:00.000 +00:00 - 1970-01-01 00:00:00.000 +00:00
"###);
}
@ -240,22 +254,28 @@ fn test_op_log_word_wrap() {
// ui.log-word-wrap option works
insta::assert_snapshot!(render(&["op", "log"], 40, false), @r###"
@ 90e23f7e6c7c test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
@ 27143b59c690 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
add workspace 'default'
b73abeebcf94 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
initialize repo
0e8aee02e242 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
initialize repo
000000000000 @ 1970-01-01 00:00:00.000 +00:00 - 1970-01-01 00:00:00.000 +00:00
"###);
insta::assert_snapshot!(render(&["op", "log"], 40, true), @r###"
@ 90e23f7e6c7c
@ 27143b59c690
test-username@host.example.com
2001-02-03 04:05:07.000 +07:00 -
2001-02-03 04:05:07.000 +07:00
add workspace 'default'
b73abeebcf94
test-username@host.example.com
2001-02-03 04:05:07.000 +07:00 -
2001-02-03 04:05:07.000 +07:00
initialize repo
0e8aee02e242
test-username@host.example.com
2001-02-03 04:05:07.000 +07:00 -
2001-02-03 04:05:07.000 +07:00
initialize repo
000000000000 @ 1970-01-01
00:00:00.000 +00:00 - 1970-01-01
00:00:00.000 +00:00
"###);
}
@ -288,34 +308,36 @@ fn test_op_abandon_ancestors() {
test_env.jj_cmd_ok(&repo_path, &["commit", "-m", "commit 1"]);
test_env.jj_cmd_ok(&repo_path, &["commit", "-m", "commit 2"]);
insta::assert_snapshot!(test_env.jj_cmd_success(&repo_path, &["op", "log"]), @r###"
@ 362b35cdcee0 test-username@host.example.com 2001-02-03 04:05:09.000 +07:00 - 2001-02-03 04:05:09.000 +07:00
@ 85137561ef60 test-username@host.example.com 2001-02-03 04:05:09.000 +07:00 - 2001-02-03 04:05:09.000 +07:00
commit a8ac27b29a157ae7dabc0deb524df68823505730
args: jj commit -m 'commit 2'
b46d00bc0741 test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00
db27d55e457f test-username@host.example.com 2001-02-03 04:05:08.000 +07:00 - 2001-02-03 04:05:08.000 +07:00
commit 230dd059e1b059aefc0da06a2e5a7dbf22362f22
args: jj commit -m 'commit 1'
90e23f7e6c7c test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
27143b59c690 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
add workspace 'default'
b73abeebcf94 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
initialize repo
0e8aee02e242 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
initialize repo
000000000000 @ 1970-01-01 00:00:00.000 +00:00 - 1970-01-01 00:00:00.000 +00:00
"###);
// Abandon old operations. The working-copy operation id should be updated.
let (_stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["op", "abandon", "..@-"]);
insta::assert_snapshot!(stderr, @r###"
Abandoned 2 operations and reparented 1 descendant operations.
Abandoned 3 operations and reparented 1 descendant operations.
"###);
insta::assert_snapshot!(
test_env.jj_cmd_success(&repo_path, &["debug", "workingcopy", "--ignore-working-copy"]), @r###"
Current operation: OperationId("3a0226ca7b0f364650c64a8b8ffe3c9802c5db40d9cc19387a29a28826ccada0dd4ec6926e649c5eb3c8150ebd5cd8ffb3c2c8922445c5dc8ccf474cd83d0d9c")
Current operation: OperationId("1c88fada5b95d13ca136baa13c4b4aae1b79f3d453fe2f56539dbcd5d779642439314a15f685d6737eb414fffb3e53519f65f1d5cc5947a7821331137f4a91e2")
Current tree: Legacy(TreeId("4b825dc642cb6eb9a060e54bf8d69288fbee4904"))
"###);
insta::assert_snapshot!(test_env.jj_cmd_success(&repo_path, &["op", "log"]), @r###"
@ 3a0226ca7b0f test-username@host.example.com 2001-02-03 04:05:09.000 +07:00 - 2001-02-03 04:05:09.000 +07:00
@ 1c88fada5b95 test-username@host.example.com 2001-02-03 04:05:09.000 +07:00 - 2001-02-03 04:05:09.000 +07:00
commit a8ac27b29a157ae7dabc0deb524df68823505730
args: jj commit -m 'commit 2'
b73abeebcf94 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
initialize repo
000000000000 @ 1970-01-01 00:00:00.000 +00:00 - 1970-01-01 00:00:00.000 +00:00
"###);
// Abandon operation range.
@ -327,14 +349,14 @@ fn test_op_abandon_ancestors() {
Abandoned 2 operations and reparented 1 descendant operations.
"###);
insta::assert_snapshot!(test_env.jj_cmd_success(&repo_path, &["op", "log"]), @r###"
@ 54d9d0dfc198 test-username@host.example.com 2001-02-03 04:05:16.000 +07:00 - 2001-02-03 04:05:16.000 +07:00
@ 459e01910446 test-username@host.example.com 2001-02-03 04:05:16.000 +07:00 - 2001-02-03 04:05:16.000 +07:00
commit e184d62c9ab118b0f62de91959b857550a9273a5
args: jj commit -m 'commit 5'
3a0226ca7b0f test-username@host.example.com 2001-02-03 04:05:09.000 +07:00 - 2001-02-03 04:05:09.000 +07:00
1c88fada5b95 test-username@host.example.com 2001-02-03 04:05:09.000 +07:00 - 2001-02-03 04:05:09.000 +07:00
commit a8ac27b29a157ae7dabc0deb524df68823505730
args: jj commit -m 'commit 2'
b73abeebcf94 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
initialize repo
000000000000 @ 1970-01-01 00:00:00.000 +00:00 - 1970-01-01 00:00:00.000 +00:00
"###);
// Can't abandon the current operation.
@ -358,18 +380,18 @@ fn test_op_abandon_ancestors() {
"###);
insta::assert_snapshot!(
test_env.jj_cmd_success(&repo_path, &["debug", "workingcopy", "--ignore-working-copy"]), @r###"
Current operation: OperationId("25cd03cd1029c1d22ea67cc11b89d357000bae5836f482be46d054220b1b08852713e32d0eec542bb275803214415ef8bb25b65a740fbc5c7b37094a311cac7c")
Current operation: OperationId("dbe91445f72184c82b2ad4c13063a20a764dd5a3bab42fc22652a0ca5d9f5a811d2b051560ca8b45c31f29e9d17ea408b224f761165b30f6df5f68ca74c90435")
Current tree: Legacy(TreeId("4b825dc642cb6eb9a060e54bf8d69288fbee4904"))
"###);
insta::assert_snapshot!(test_env.jj_cmd_success(&repo_path, &["op", "log"]), @r###"
@ 25cd03cd1029 test-username@host.example.com 2001-02-03 04:05:21.000 +07:00 - 2001-02-03 04:05:21.000 +07:00
undo operation 54d9d0dfc198e3550d04acd89329f8b49765a94c4c85239ee44d6a602127ac658ed91933ad32211cc26e32b3ef2f384e9e0bab471327338fb1b3f04df7916cc2
@ dbe91445f721 test-username@host.example.com 2001-02-03 04:05:21.000 +07:00 - 2001-02-03 04:05:21.000 +07:00
undo operation 459e01910446fb8f6f6447112abc2227cb3b054b7831eca45bd527145719152931b1707d299f5e4e8484352e67d3622265a0baa58bfa01c459fd753f58791e0d
args: jj undo
3a0226ca7b0f test-username@host.example.com 2001-02-03 04:05:09.000 +07:00 - 2001-02-03 04:05:09.000 +07:00
1c88fada5b95 test-username@host.example.com 2001-02-03 04:05:09.000 +07:00 - 2001-02-03 04:05:09.000 +07:00
commit a8ac27b29a157ae7dabc0deb524df68823505730
args: jj commit -m 'commit 2'
b73abeebcf94 test-username@host.example.com 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00
initialize repo
000000000000 @ 1970-01-01 00:00:00.000 +00:00 - 1970-01-01 00:00:00.000 +00:00
"###);
// Abandon empty range.
@ -378,8 +400,8 @@ fn test_op_abandon_ancestors() {
Nothing changed.
"###);
insta::assert_snapshot!(test_env.jj_cmd_success(&repo_path, &["op", "log", "-l1"]), @r###"
@ 25cd03cd1029 test-username@host.example.com 2001-02-03 04:05:21.000 +07:00 - 2001-02-03 04:05:21.000 +07:00
undo operation 54d9d0dfc198e3550d04acd89329f8b49765a94c4c85239ee44d6a602127ac658ed91933ad32211cc26e32b3ef2f384e9e0bab471327338fb1b3f04df7916cc2
@ dbe91445f721 test-username@host.example.com 2001-02-03 04:05:21.000 +07:00 - 2001-02-03 04:05:21.000 +07:00
undo operation 459e01910446fb8f6f6447112abc2227cb3b054b7831eca45bd527145719152931b1707d299f5e4e8484352e67d3622265a0baa58bfa01c459fd753f58791e0d
args: jj undo
"###);
}
@ -404,12 +426,12 @@ fn test_op_abandon_without_updating_working_copy() {
"###);
insta::assert_snapshot!(
test_env.jj_cmd_success(&repo_path, &["debug", "workingcopy", "--ignore-working-copy"]), @r###"
Current operation: OperationId("324622dffebde1f5b9c75347254d0481aa5f51931816782a8a5c0578fe74893fbf63ba7921e94f44058b1e1162e932648146136b4c13236cf2eccc42347e6dcf")
Current operation: OperationId("0229bff5a5244b0804cc677c77e318887b6d00422257de108158ef41fa5edcff38aa1683a3c00364835e0565b841eb41c5092300ec23a1aa5e393914d18fef32")
Current tree: Legacy(TreeId("4b825dc642cb6eb9a060e54bf8d69288fbee4904"))
"###);
insta::assert_snapshot!(
test_env.jj_cmd_success(&repo_path, &["op", "log", "-l1", "--ignore-working-copy"]), @r###"
@ bc4e5d99e86e test-username@host.example.com 2001-02-03 04:05:10.000 +07:00 - 2001-02-03 04:05:10.000 +07:00
@ 0173d6fbe6a2 test-username@host.example.com 2001-02-03 04:05:10.000 +07:00 - 2001-02-03 04:05:10.000 +07:00
commit 268f5f16139313ff25bef31280b2ec2e675200f3
args: jj commit -m 'commit 3'
"###);
@ -420,16 +442,16 @@ fn test_op_abandon_without_updating_working_copy() {
let (_stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["op", "abandon", "@-"]);
insta::assert_snapshot!(stderr, @r###"
Abandoned 1 operations and reparented 1 descendant operations.
The working copy operation 324622dffebd is not updated because it differs from the repo bc4e5d99e86e.
The working copy operation 0229bff5a524 is not updated because it differs from the repo 0173d6fbe6a2.
"###);
insta::assert_snapshot!(
test_env.jj_cmd_success(&repo_path, &["debug", "workingcopy", "--ignore-working-copy"]), @r###"
Current operation: OperationId("324622dffebde1f5b9c75347254d0481aa5f51931816782a8a5c0578fe74893fbf63ba7921e94f44058b1e1162e932648146136b4c13236cf2eccc42347e6dcf")
Current operation: OperationId("0229bff5a5244b0804cc677c77e318887b6d00422257de108158ef41fa5edcff38aa1683a3c00364835e0565b841eb41c5092300ec23a1aa5e393914d18fef32")
Current tree: Legacy(TreeId("4b825dc642cb6eb9a060e54bf8d69288fbee4904"))
"###);
insta::assert_snapshot!(
test_env.jj_cmd_success(&repo_path, &["op", "log", "-l1", "--ignore-working-copy"]), @r###"
@ d7c3591bc1df test-username@host.example.com 2001-02-03 04:05:10.000 +07:00 - 2001-02-03 04:05:10.000 +07:00
@ b66dc21d579b test-username@host.example.com 2001-02-03 04:05:10.000 +07:00 - 2001-02-03 04:05:10.000 +07:00
commit 268f5f16139313ff25bef31280b2ec2e675200f3
args: jj commit -m 'commit 3'
"###);

View File

@ -275,14 +275,14 @@ fn test_workspaces_conflicting_edits() {
"###);
let stderr = test_env.jj_cmd_failure(&secondary_path, &["st"]);
insta::assert_snapshot!(stderr, @r###"
Error: The working copy is stale (not updated since operation 86c5150a3a9c).
Error: The working copy is stale (not updated since operation a07b009d6eba).
Hint: Run `jj workspace update-stale` to update it.
See https://github.com/martinvonz/jj/blob/main/docs/working-copy.md#stale-working-copy for more information.
"###);
// Same error on second run, and from another command
let stderr = test_env.jj_cmd_failure(&secondary_path, &["log"]);
insta::assert_snapshot!(stderr, @r###"
Error: The working copy is stale (not updated since operation 86c5150a3a9c).
Error: The working copy is stale (not updated since operation a07b009d6eba).
Hint: Run `jj workspace update-stale` to update it.
See https://github.com/martinvonz/jj/blob/main/docs/working-copy.md#stale-working-copy for more information.
"###);
@ -362,7 +362,7 @@ fn test_workspaces_updated_by_other() {
"###);
let stderr = test_env.jj_cmd_failure(&secondary_path, &["st"]);
insta::assert_snapshot!(stderr, @r###"
Error: The working copy is stale (not updated since operation 86c5150a3a9c).
Error: The working copy is stale (not updated since operation a07b009d6eba).
Hint: Run `jj workspace update-stale` to update it.
See https://github.com/martinvonz/jj/blob/main/docs/working-copy.md#stale-working-copy for more information.
"###);
@ -409,6 +409,7 @@ fn test_workspaces_update_stale_noop() {
insta::assert_snapshot!(stdout, @r###"
@ add workspace 'default'
initialize repo
"###);
}
@ -543,7 +544,7 @@ fn test_workspaces_forget_multi_transaction() {
// the op log should have multiple workspaces forgotten in a single tx
let stdout = test_env.jj_cmd_success(&main_path, &["op", "log", "--limit", "1"]);
insta::assert_snapshot!(stdout, @r###"
@ a2acfd91b649 test-username@host.example.com 2001-02-03 04:05:12.000 +07:00 - 2001-02-03 04:05:12.000 +07:00
@ f96865b00a04 test-username@host.example.com 2001-02-03 04:05:12.000 +07:00 - 2001-02-03 04:05:12.000 +07:00
forget workspaces second, third
args: jj workspace forget second third
"###);

View File

@ -23,7 +23,7 @@ use itertools::Itertools as _;
use once_cell::sync::Lazy;
use thiserror::Error;
use crate::backend::{CommitId, Timestamp};
use crate::backend::{CommitId, MillisSinceEpoch, Timestamp};
use crate::content_hash::ContentHash;
use crate::merge::Merge;
use crate::object_id::{id_type, HexPrefix, ObjectId, PrefixResolution};
@ -369,6 +369,28 @@ content_hash! {
}
}
impl Operation {
pub fn make_root(empty_view_id: ViewId) -> Operation {
let timestamp = Timestamp {
timestamp: MillisSinceEpoch(0),
tz_offset: 0,
};
let metadata = OperationMetadata {
start_time: timestamp.clone(),
end_time: timestamp,
description: "".to_string(),
hostname: "".to_string(),
username: "".to_string(),
tags: HashMap::new(),
};
Operation {
view_id: empty_view_id,
parents: vec![],
metadata,
}
}
}
content_hash! {
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct OperationMetadata {
@ -410,6 +432,8 @@ pub type OpStoreResult<T> = Result<T, OpStoreError>;
pub trait OpStore: Send + Sync + Debug {
fn name(&self) -> &str;
fn root_operation_id(&self) -> &OperationId;
fn read_view(&self, id: &ViewId) -> OpStoreResult<View>;
fn write_view(&self, contents: &View) -> OpStoreResult<ViewId>;

View File

@ -177,7 +177,7 @@ impl ReadonlyRepo {
let root_view_id = op_store.write_view(&root_view).unwrap();
let init_operation = op_store::Operation {
view_id: root_view_id,
parents: vec![],
parents: vec![op_store.root_operation_id().clone()],
metadata: operation_metadata,
};
let init_operation_id = op_store.write_operation(&init_operation).unwrap();

View File

@ -39,6 +39,7 @@ use crate::{dag_walk, git, op_store};
// BLAKE2b-512 hash length in bytes
const OPERATION_ID_LENGTH: usize = 64;
const VIEW_ID_LENGTH: usize = 64;
#[derive(Debug, Error)]
#[error("Failed to read {kind} with ID {id}: {err}")]
@ -58,6 +59,8 @@ impl From<DecodeError> for OpStoreError {
#[derive(Debug)]
pub struct SimpleOpStore {
path: PathBuf,
empty_view_id: ViewId,
root_operation_id: OperationId,
}
impl SimpleOpStore {
@ -69,15 +72,15 @@ impl SimpleOpStore {
pub fn init(store_path: &Path) -> Self {
fs::create_dir(store_path.join("views")).unwrap();
fs::create_dir(store_path.join("operations")).unwrap();
SimpleOpStore {
path: store_path.to_owned(),
}
Self::load(store_path)
}
/// Load an existing OpStore
pub fn load(store_path: &Path) -> Self {
SimpleOpStore {
path: store_path.to_path_buf(),
empty_view_id: ViewId::from_bytes(&[0; VIEW_ID_LENGTH]),
root_operation_id: OperationId::from_bytes(&[0; OPERATION_ID_LENGTH]),
}
}
@ -95,7 +98,15 @@ impl OpStore for SimpleOpStore {
Self::name()
}
fn root_operation_id(&self) -> &OperationId {
&self.root_operation_id
}
fn read_view(&self, id: &ViewId) -> OpStoreResult<View> {
if *id == self.empty_view_id {
return Ok(View::default());
}
let path = self.view_path(id);
let buf = fs::read(path).map_err(|err| io_to_read_error(err, id))?;
@ -125,6 +136,10 @@ impl OpStore for SimpleOpStore {
}
fn read_operation(&self, id: &OperationId) -> OpStoreResult<Operation> {
if *id == self.root_operation_id {
return Ok(Operation::make_root(self.empty_view_id.clone()));
}
let path = self.operation_path(id);
let buf = fs::read(path).map_err(|err| io_to_read_error(err, id))?;
@ -134,10 +149,17 @@ impl OpStore for SimpleOpStore {
id: id.hex(),
err,
})?;
Ok(operation_from_proto(proto))
let mut operation = operation_from_proto(proto);
if operation.parents.is_empty() {
// Repos created before we had the root operation will have an operation without
// parents.
operation.parents.push(self.root_operation_id.clone());
}
Ok(operation)
}
fn write_operation(&self, operation: &Operation) -> OpStoreResult<OperationId> {
assert!(!operation.parents.is_empty());
let temp_file =
NamedTempFile::new_in(&self.path).map_err(|err| io_to_write_error(err, "operation"))?;

View File

@ -65,9 +65,9 @@ fn test_commit_parallel(backend: TestRepoBackend) {
// the root commit
assert_eq!(repo.view().heads().len(), num_threads + 1);
// One additional operation for initializing the repo, one for checking out the
// initial commit.
assert_eq!(count_non_merge_operations(&repo), num_threads + 2);
// One additional operation for the root commit, one for initializing the repo,
// one for checking out the initial commit.
assert_eq!(count_non_merge_operations(&repo), num_threads + 3);
}
#[test_case(TestRepoBackend::Local ; "local backend")]
@ -96,7 +96,7 @@ fn test_commit_parallel_instances(backend: TestRepoBackend) {
let repo = load_repo_at_head(&settings, repo.repo_path());
assert_eq!(repo.view().heads().len(), num_threads + 1);
// One addition operation for initializing the repo, one for checking out the
// initial commit.
assert_eq!(count_non_merge_operations(&repo), num_threads + 2);
// One additional operation for the root commit, one for initializing the repo,
// one for checking out the initial commit.
assert_eq!(count_non_merge_operations(&repo), num_threads + 3);
}

View File

@ -441,15 +441,15 @@ fn test_resolve_op_id() {
repo = tx.commit(format!("transaction {i}"));
operations.push(repo.operation().clone());
}
// "8" is ambiguous
// "1" is ambiguous
insta::assert_debug_snapshot!(operations.iter().map(|op| op.id().hex()).collect_vec(), @r###"
[
"c304846fd5419130f961dc29a19e382ba1b48eeef92505da2f6be65c09a5321e36b3642bc8bf052cd47c2f0fa6cb103a837fc0d6b86a2ab5ef18c7ecfa6bec38",
"bffef6b286529cdebb9e48e209ea0eef96989bb23d8af4773847564d0b3b7961676ad9e23ddcd46774712198f5b3e28c2eb4fbba159ac640919b0c5f7d5a71d8",
"98d92c1e65617b594741968302d7225238f9f13e8126a6e12ee73f17def310a7b5be75a0d761399e2a9f3d13f066a274a4f40e6b9d588824da606a8888854f62",
"34d4b6ec40a425036a3e267cbe61ddc99582a1ec2601501d19a6a6c402d255aef5109ddc5bb6a79afaa17acc56a2e04bba8e69ef90a764aebb769f5038d24b64",
"89f50ea6a566fc5794164653511f35c40011321989a2499a6aec18780f0110cb5ce0fc0b248026cb32aa2b6a017a65a9472c497920933d13f68a95211c30bf2d",
"8913bd72461b92b2323b49a8789219f842942fd2040ddc078a4b7f731630f4bce8dabf181c3c55a3c54950023c04f6fc8f4174b92e9953230592a534222a5297",
"4ff2007de55a2f649f7ab0c98618e4126ef49f0d40a086c8e0a4612a0d5ab4992e1baf4b4fa0a2a224fab39fc5e5b200ac4cddf964db29c6be1379ab2b6d4572",
"cf1fd9ea3065ae077e13641278a213b371f98eb237334489c059e50e0a92a79cb058939f6fd2a89492f32dcecb14b6237f3e1fcf6fa454a9c21ef5425d78574c",
"5b29edf367805ca7e1dc9219ec25dae70ec853afbeed9a113dc2adba428f71eea944122097276fb664e2c8d6cc99a080d49ef58ce14356b5887d27b45952bfef",
"126647a88e08bc46f72db1eff34abe426cdd54e4e4c05b9b6773c288442bed2c5add304d4978e377694516ee89ed3436cec20ef2af4921247e6b6a90c3685a2a",
"5131849d86fe586dbbad3992d85b953b0ebe554f0bd7c42ee09628ab85db7ad909242901688a7d00590d326c8bdde21e8ed933f7ab4d6b7b16077e3d1c07b284",
"14073ae915621d1ec2358129440dc2a26e67e45995f46cf6199a6bb09b44d16a522451079a2480e4d2026d368759eacd1d5b3bc7a5915d9345d354b8e7ad46b4",
]
"###);
@ -470,7 +470,7 @@ fn test_resolve_op_id() {
);
// Ambiguous id
assert_matches!(
op_walk::resolve_op_with_repo(&repo, "8"),
op_walk::resolve_op_with_repo(&repo, "1"),
Err(OpsetEvaluationError::OpsetResolution(
OpsetResolutionError::AmbiguousIdPrefix(_)
))
@ -521,9 +521,9 @@ fn test_resolve_op_parents_children() {
op_walk::resolve_op_with_repo(repo, &format!("{op2_id_hex}--")).unwrap(),
*operations[0]
);
// "{op2_id_hex}---" is the operation to initialize the repo.
// "{op2_id_hex}----" is the root operation
assert_matches!(
op_walk::resolve_op_with_repo(repo, &format!("{op2_id_hex}----")),
op_walk::resolve_op_with_repo(repo, &format!("{op2_id_hex}-----")),
Err(OpsetEvaluationError::OpsetResolution(
OpsetResolutionError::EmptyOperations(_)
))