Skip to content

Commit fae99a0

Browse files
authored
Add --prune-repeated-keep <number> option to spfs clean (#1176)
--------- Signed-off-by: David Gilligan-Cook <[email protected]>
1 parent 54ebd84 commit fae99a0

File tree

2 files changed

+38
-13
lines changed

2 files changed

+38
-13
lines changed

crates/spfs-cli/cmd-clean/src/cmd_clean.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// https://github.com/spkenv/spk
44

55
use std::collections::HashSet;
6+
use std::num::NonZero;
67

78
use chrono::prelude::*;
89
use clap::Parser;
@@ -53,6 +54,11 @@ pub struct CmdClean {
5354
#[clap(long = "prune-repeated", group = "repo_data")]
5455
prune_repeated: bool,
5556

57+
/// When pruning old tag that have the same target as a more
58+
/// recent version, keep this many of the repeated tags
59+
#[clap(long = "prune-repeated-keep", group = "repo_data")]
60+
prune_repeated_keep: Option<NonZero<u64>>,
61+
5662
/// Prune tags older that the given age (eg: 1y, 8w, 10d, 3h, 4m, 8s)
5763
#[clap(long = "prune-if-older-than", group = "repo_data", value_parser = age_to_date)]
5864
prune_if_older_than: Option<DateTime<Utc>>,
@@ -149,11 +155,17 @@ impl CmdClean {
149155
return Ok(0);
150156
}
151157

158+
let prune_repeated_tags = if self.prune_repeated {
159+
NonZero::new(1)
160+
} else {
161+
self.prune_repeated_keep
162+
};
163+
152164
let cleaner = spfs::Cleaner::new(&repo)
153165
.with_reporter(spfs::clean::ConsoleCleanReporter::default())
154166
.with_dry_run(self.dry_run)
155167
.with_required_age(chrono::Duration::minutes(15))
156-
.with_prune_repeated_tags(self.prune_repeated)
168+
.with_prune_repeated_tags(prune_repeated_tags)
157169
.with_prune_tags_older_than(self.prune_if_older_than)
158170
.with_keep_tags_newer_than(self.keep_if_newer_than)
159171
.with_prune_tags_if_version_more_than(self.prune_if_more_than)

crates/spfs/src/clean.rs

+25-12
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use std::collections::{HashMap, HashSet};
66
use std::fmt::Write;
77
use std::future::ready;
8+
use std::num::NonZero;
89
#[cfg(unix)]
910
use std::os::linux::fs::MetadataExt;
1011

@@ -16,10 +17,11 @@ use once_cell::sync::OnceCell;
1617
use progress_bar_derive_macro::ProgressBar;
1718

1819
use super::prune::PruneParameters;
20+
use crate::io::Pluralize;
1921
use crate::prelude::*;
2022
use crate::runtime::makedirs_with_perms;
2123
use crate::storage::fs::OpenFsRepository;
22-
use crate::{encoding, graph, storage, tracking, Error, Result};
24+
use crate::{encoding, graph, storage, tracking, Digest, Error, Result};
2325

2426
#[cfg(test)]
2527
#[path = "./clean_test.rs"]
@@ -43,7 +45,7 @@ where
4345
attached: DashSet<encoding::Digest>,
4446
dry_run: bool,
4547
must_be_older_than: DateTime<Utc>,
46-
prune_repeated_tags: bool,
48+
prune_repeated_tags: Option<NonZero<u64>>,
4749
prune_params: PruneParameters,
4850
remove_proxies_with_no_links: bool,
4951
}
@@ -66,7 +68,7 @@ impl<'repo> Cleaner<'repo, SilentCleanReporter> {
6668
attached: Default::default(),
6769
dry_run: false,
6870
must_be_older_than: Utc::now(),
69-
prune_repeated_tags: false,
71+
prune_repeated_tags: None,
7072
prune_params: Default::default(),
7173
remove_proxies_with_no_links: true,
7274
}
@@ -154,7 +156,7 @@ where
154156

155157
/// When walking the history of a tag, delete older entries
156158
/// that have the same target as a more recent one.
157-
pub fn with_prune_repeated_tags(mut self, prune_repeated_tags: bool) -> Self {
159+
pub fn with_prune_repeated_tags(mut self, prune_repeated_tags: Option<NonZero<u64>>) -> Self {
158160
self.prune_repeated_tags = prune_repeated_tags;
159161
self
160162
}
@@ -229,11 +231,12 @@ where
229231
" - {} each item in the tag's history, and for each one:",
230232
"VISIT".cyan()
231233
);
232-
if self.prune_repeated_tags || !self.prune_params.is_empty() {
233-
if self.prune_repeated_tags {
234+
if self.prune_repeated_tags.is_some() || !self.prune_params.is_empty() {
235+
if let Some(number) = self.prune_repeated_tags {
236+
let entries = "entry".pluralize(number.get());
234237
let _ = writeln!(
235238
&mut out,
236-
" - {prune} any entry that has a the same target as a more recent entry",
239+
" - {prune} all but {number} {entries} with the same target as a more recent entry"
237240
);
238241
}
239242
let PruneParameters {
@@ -394,14 +397,24 @@ where
394397
.await?;
395398
let mut to_prune = Vec::with_capacity(history.len() / 2);
396399
let mut to_keep = Vec::with_capacity(history.len() / 2);
397-
let mut seen_targets = std::collections::HashSet::new();
400+
let mut seen_targets: HashMap<Digest, u64> = std::collections::HashMap::new();
398401
for (i, tag) in history.into_iter().enumerate() {
399402
let spec = tag.to_spec(i as u64);
400403
self.reporter.visit_tag(&tag);
401-
if !seen_targets.insert(tag.target) && self.prune_repeated_tags {
402-
to_prune.push(tag);
403-
continue;
404-
}
404+
let count = if let Some(seen_count) = seen_targets.get(&tag.target) {
405+
if let Some(keep_number) = self.prune_repeated_tags {
406+
if *seen_count >= keep_number.get() {
407+
to_prune.push(tag);
408+
continue;
409+
}
410+
}
411+
*seen_count + 1
412+
} else {
413+
1
414+
};
415+
416+
seen_targets.insert(tag.target, count);
417+
405418
if self.prune_params.should_prune(&spec, &tag) {
406419
to_prune.push(tag);
407420
} else {

0 commit comments

Comments
 (0)