forked from martinvonz/jj
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
cli: add "op abandon root..head" command that "reparents" operations
In order to implement GC (martinvonz#12), we'll need to somehow prune old operations. Perhaps the easiest implementation is to just remove unwanted operation files and put tombstone file instead (like git shallow.) However, the removed operations might be referenced by another jj process running in parallel. Since the parallel operation thinks all the historical head commits are reachable, the removed operations would have to be resurrected (or fix up index data, etc.) when the op heads get merged. The idea behind this patch is to split the "op log" GC into two steps: 1. recreate operations to be retained and make the old history unreachable, 2. delete unreachable operations if the head was created e.g. 3 days ago. The latter will be run by "jj util gc". I don't think GC can be implemented 100% safe against lock-less append-only storage, and we'll probably need some timestamp-based mechanism to not remove objects that might be referenced by uncommitted operation. FWIW, another nice thing about this implementation is that the index is automatically invalidated as the op id changes. The bad thing is that the "undo" description would contain an old op id. It seems the performance is pretty okay. This patch also includes support for "op abandon root.." because it can be implemented for free. I'm not sure what should be checked as prerequisite. I made it require "op restore root && op abandon root.." for now.
- Loading branch information
Showing
3 changed files
with
255 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -279,6 +279,130 @@ fn test_op_log_configurable() { | |
assert!(stdout.contains("my-username@my-hostname")); | ||
} | ||
|
||
#[test] | ||
fn test_op_abandon_ancestors() { | ||
let test_env = TestEnvironment::default(); | ||
test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); | ||
let repo_path = test_env.env_root().join("repo"); | ||
|
||
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###" | ||
@ bacc8030a969 [email protected] 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' | ||
◉ bb26fe31d66f [email protected] 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' | ||
◉ 19b8089fc78b [email protected] 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00 | ||
│ add workspace 'default' | ||
◉ f1c462c494be [email protected] 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00 | ||
initialize repo | ||
"###); | ||
|
||
// 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. | ||
"###); | ||
insta::assert_snapshot!(test_env.jj_cmd_success(&repo_path, &["debug", "workingcopy"]), @r###" | ||
Current operation: OperationId("fb5252a68411468f5e3cf480a75b8b54d8ca9231406a3d0ddc4dfb31d851839a855aca5615ba4b09018fe45d11a04e1c051817a98de1c1ef5dd75cb6c2c09ba8") | ||
Current tree: Merge(Resolved(TreeId("4b825dc642cb6eb9a060e54bf8d69288fbee4904"))) | ||
"###); | ||
insta::assert_snapshot!(test_env.jj_cmd_success(&repo_path, &["op", "log"]), @r###" | ||
@ fb5252a68411 [email protected] 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' | ||
◉ f1c462c494be [email protected] 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00 | ||
initialize repo | ||
"###); | ||
|
||
// Abandon a certain operation. It wouldn't be useful, but works. | ||
test_env.jj_cmd_ok(&repo_path, &["commit", "-m", "commit 3"]); | ||
test_env.jj_cmd_ok(&repo_path, &["commit", "-m", "commit 4"]); | ||
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. | ||
"###); | ||
insta::assert_snapshot!(test_env.jj_cmd_success(&repo_path, &["op", "log"]), @r###" | ||
@ 50fc6d9ddaad [email protected] 2001-02-03 04:05:15.000 +07:00 - 2001-02-03 04:05:15.000 +07:00 | ||
│ commit 7e8c543dd4b96597050139a02615492f84b29b0a | ||
│ args: jj commit -m 'commit 4' | ||
◉ fb5252a68411 [email protected] 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' | ||
◉ f1c462c494be [email protected] 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00 | ||
initialize repo | ||
"###); | ||
|
||
// Can't abandon the whole operation history. | ||
let stderr = test_env.jj_cmd_failure(&repo_path, &["op", "abandon", "..@"]); | ||
insta::assert_snapshot!(stderr, @r###" | ||
Error: Cannot roll back to unrelated repository state | ||
Hint: Run `jj op restore` first to move to the desired state | ||
"###); | ||
|
||
// Abandon empty range. | ||
let (_stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["op", "abandon", "@..@"]); | ||
insta::assert_snapshot!(stderr, @r###" | ||
Abandoned 0 operations. | ||
"###); | ||
insta::assert_snapshot!(test_env.jj_cmd_success(&repo_path, &["op", "log", "-l1"]), @r###" | ||
@ 50fc6d9ddaad [email protected] 2001-02-03 04:05:15.000 +07:00 - 2001-02-03 04:05:15.000 +07:00 | ||
│ commit 7e8c543dd4b96597050139a02615492f84b29b0a | ||
│ args: jj commit -m 'commit 4' | ||
"###); | ||
} | ||
|
||
#[test] | ||
fn test_op_abandon_descendants() { | ||
let test_env = TestEnvironment::default(); | ||
test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); | ||
let repo_path = test_env.env_root().join("repo"); | ||
|
||
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###" | ||
@ bacc8030a969 [email protected] 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' | ||
◉ bb26fe31d66f [email protected] 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' | ||
◉ 19b8089fc78b [email protected] 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00 | ||
│ add workspace 'default' | ||
◉ f1c462c494be [email protected] 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00 | ||
initialize repo | ||
"###); | ||
|
||
// For safety, can't abandon without restoring to the destination view | ||
let stderr = test_env.jj_cmd_failure(&repo_path, &["op", "abandon", "@-.."]); | ||
insta::assert_snapshot!(stderr, @r###" | ||
Error: Cannot roll back to unrelated repository state | ||
Hint: Run `jj op restore` first to move to the desired state | ||
"###); | ||
|
||
// Abandon recent operations. The working-copy operation id should be updated. | ||
test_env.jj_cmd_ok(&repo_path, &["op", "restore", "@-"]); | ||
let (_stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["op", "abandon", "@--.."]); | ||
insta::assert_snapshot!(stderr, @r###" | ||
Abandoned 2 operations. | ||
"###); | ||
insta::assert_snapshot!(test_env.jj_cmd_success(&repo_path, &["debug", "workingcopy"]), @r###" | ||
Current operation: OperationId("bb26fe31d66f320e6d2bda90b64a99ad95d54f1ea6ecb2e61af718fcdce91668a9edf4c5b2fe9142c43ce8037f05752b48b052fb2018d667c43d38bf76b098c2") | ||
Current tree: Merge(Resolved(TreeId("4b825dc642cb6eb9a060e54bf8d69288fbee4904"))) | ||
"###); | ||
insta::assert_snapshot!(test_env.jj_cmd_success(&repo_path, &["op", "log"]), @r###" | ||
@ bb26fe31d66f [email protected] 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' | ||
◉ 19b8089fc78b [email protected] 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00 | ||
│ add workspace 'default' | ||
◉ f1c462c494be [email protected] 2001-02-03 04:05:07.000 +07:00 - 2001-02-03 04:05:07.000 +07:00 | ||
initialize repo | ||
"###); | ||
} | ||
|
||
fn get_log_output(test_env: &TestEnvironment, repo_path: &Path, op_id: &str) -> String { | ||
test_env.jj_cmd_success( | ||
repo_path, | ||
|