util gc: allow specifying prune time

This only affects pruning of the operation log for now, and only
supports the string "now" for now.
This commit is contained in:
Jonathan Tan 2024-01-19 11:14:31 -08:00 committed by jonathantanmy
parent 4024fb4880
commit 6fa5d456f8
2 changed files with 57 additions and 4 deletions

View File

@ -62,7 +62,18 @@ pub(crate) struct UtilCompletionArgs {
/// Run backend-dependent garbage collection.
#[derive(clap::Args, Clone, Debug)]
pub(crate) struct UtilGcArgs {}
pub(crate) struct UtilGcArgs {
/// Time threshold
///
/// By default, only obsolete objects and operations older than 2 weeks are
/// pruned.
///
/// Only the string "now" can be passed to this parameter. Support for
/// arbitrary absolute and relative timestamps will come in a subsequent
/// release.
#[arg(long)]
expire: Option<String>,
}
/// Print a ROFF (manpage)
#[derive(clap::Args, Clone, Debug)]
@ -108,16 +119,20 @@ fn cmd_util_completion(
fn cmd_util_gc(
ui: &mut Ui,
command: &CommandHelper,
_args: &UtilGcArgs,
args: &UtilGcArgs,
) -> Result<(), CommandError> {
if command.global_args().at_operation != "@" {
return Err(user_error(
"Cannot garbage collect from a non-head operation",
));
}
let keep_newer = match args.expire.as_deref() {
None => SystemTime::now() - Duration::from_secs(14 * 86400),
Some("now") => SystemTime::now() - Duration::ZERO,
_ => return Err(user_error("--expire only accepts 'now'")),
};
let workspace_command = command.workspace_helper(ui)?;
// TODO: add command argument to specify the expiration time?
let keep_newer = SystemTime::now() - Duration::from_secs(14 * 86400);
let repo = workspace_command.repo();
repo.op_store()
.gc(slice::from_ref(repo.op_id()), keep_newer)?;

View File

@ -55,4 +55,42 @@ fn test_gc_args() {
insta::assert_snapshot!(stderr, @r###"
Error: Cannot garbage collect from a non-head operation
"###);
let stderr = test_env.jj_cmd_failure(&repo_path, &["util", "gc", "--expire=foobar"]);
insta::assert_snapshot!(stderr, @r###"
Error: --expire only accepts 'now'
"###);
}
#[test]
fn test_gc_operation_log() {
let test_env = TestEnvironment::default();
// Use the local backend because GitBackend::gc() depends on the git CLI.
test_env.jj_cmd_ok(
test_env.env_root(),
&["init", "repo", "--config-toml=ui.allow-init-native=true"],
);
let repo_path = test_env.env_root().join("repo");
// Create an operation.
std::fs::write(repo_path.join("file"), "a change\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["commit", "-m", "a change"]);
let op_to_remove = test_env.current_operation_id(&repo_path);
// Make another operation the head.
std::fs::write(repo_path.join("file"), "another change\n").unwrap();
test_env.jj_cmd_ok(&repo_path, &["commit", "-m", "another change"]);
// This works before the operation is removed.
test_env.jj_cmd_ok(&repo_path, &["debug", "operation", &op_to_remove]);
// Remove some operations.
test_env.jj_cmd_ok(&repo_path, &["operation", "abandon", "..@-"]);
test_env.jj_cmd_ok(&repo_path, &["util", "gc", "--expire=now"]);
// Now this doesn't work.
let stderr = test_env.jj_cmd_failure(&repo_path, &["debug", "operation", &op_to_remove]);
insta::assert_snapshot!(stderr, @r###"
Error: No operation ID matching "35688918195690874cbf1f282140cda33c882e48a84dbb0f92c262b52ace4a5753777432b18e9de01bc23121b23261eb2c828622836b9ec7ded7c0ca3c7c1670"
"###);
}