From c37478ed843d043e0667f2b16f9a4d52c9a52286 Mon Sep 17 00:00:00 2001 From: mtkennerly Date: Sun, 30 Jun 2024 08:39:45 -0400 Subject: [PATCH] Fix different ignored saves in consecutive differential backup --- CHANGELOG.md | 4 ++++ src/scan/layout.rs | 60 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cac3c34..e0401c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ ## Unreleased * Fixed: + * If two consecutive differential backups both ignored *different* save files + *and* none of those files were ignored in the associated full backup, + then the second differential backup would fail to redeclare + the first differential backup's ignored saves. * GUI: On Mac, the file/folder selector would cause the app to crash. ## v0.24.2 (2024-06-28) diff --git a/src/scan/layout.rs b/src/scan/layout.rs index 3650ab1..8e23956 100644 --- a/src/scan/layout.rs +++ b/src/scan/layout.rs @@ -1010,7 +1010,7 @@ impl GameLayout { ScanChange::New | ScanChange::Different | ScanChange::Same => { files.insert( file.mapping_key(), - Some(IndividualMappingFile { + (!file.ignored).then(|| IndividualMappingFile { hash: file.hash.clone(), size: file.size, }), @@ -2589,6 +2589,64 @@ mod tests { ); } + #[test] + fn can_plan_second_differential_backup_with_different_ignored_files() { + let scan = ScanInfo { + found_files: hash_set! { + // Ignored in first differential backup: + ScannedFile::with_change(repo_file("file1"), 1, "1", ScanChange::New).ignored(), + // Newly ignored: + ScannedFile::with_change(repo_file("file2"), 2, "2", ScanChange::Same).ignored(), + // Just here to keep the backup from being inert (all ignores): + ScannedFile::with_change(repo_file("file3"), 3, "3", ScanChange::Same), + }, + ..Default::default() + }; + let layout = GameLayout { + mapping: IndividualMapping { + drives: drives(), + backups: VecDeque::from_iter(vec![FullBackup { + name: ".".to_string(), + when: past(), + files: btree_map! { + StrictPath::new(repo_file("file1")).render(): IndividualMappingFile { hash: "1".into(), size: 1 }, + StrictPath::new(repo_file("file2")).render(): IndividualMappingFile { hash: "2".into(), size: 2 }, + StrictPath::new(repo_file("file3")).render(): IndividualMappingFile { hash: "3".into(), size: 3 }, + }, + children: VecDeque::from([DifferentialBackup { + name: format!("backup-{}-diff", now_str()), + when: now(), + os: Some(Os::HOST), + files: btree_map! { + StrictPath::new(repo_file("file1")).render(): None, + }, + ..Default::default() + }]), + ..Default::default() + }]), + ..Default::default() + }, + ..Default::default() + }; + assert_eq!( + DifferentialBackup { + name: format!("backup-{}-diff", now_str()), + when: now(), + os: Some(Os::HOST), + files: btree_map! { + // This matches the latest composite, + // but we have to reiterate this in the new differential: + StrictPath::new(repo_file("file1")).render(): None, + // New ignore: + StrictPath::new(repo_file("file2")).render(): None, + }, + registry: None, + ..Default::default() + }, + layout.plan_differential_backup(&scan, &now(), &BackupFormats::default()), + ); + } + #[test] #[cfg(target_os = "windows")] fn can_plan_differential_backup_with_registry_new() {