Dryrun for duplicates (#277)
``` czkawka_cli dup --dryrun -d $(pwd)/folder -m1 -D aen Found 2 duplicated files in 1 groups with same content which took 2 B: Size - 2 B (2) - 2 files /home/thomas/Development/czkawka/folder/a /home/thomas/Development/czkawka/folder/b ---- -------------------------------MESSAGES-------------------------------- Delete /home/thomas/Development/czkawka/folder/a Delete /home/thomas/Development/czkawka/folder/b ---------------------------END OF MESSAGES----------------------------- ```
This commit is contained in:
parent
ed7b197100
commit
1d904a858e
|
@ -32,6 +32,8 @@ pub enum Commands {
|
||||||
not_recursive: NotRecursive,
|
not_recursive: NotRecursive,
|
||||||
#[structopt(flatten)]
|
#[structopt(flatten)]
|
||||||
allow_hard_links: AllowHardLinks,
|
allow_hard_links: AllowHardLinks,
|
||||||
|
#[structopt(flatten)]
|
||||||
|
dryrun: DryRun,
|
||||||
},
|
},
|
||||||
#[structopt(name = "empty-folders", about = "Finds empty folders", help_message = HELP_MESSAGE, after_help = "EXAMPLE:\n czkawka empty-folders -d /home/rafal/rr /home/gateway -f results.txt")]
|
#[structopt(name = "empty-folders", about = "Finds empty folders", help_message = HELP_MESSAGE, after_help = "EXAMPLE:\n czkawka empty-folders -d /home/rafal/rr /home/gateway -f results.txt")]
|
||||||
EmptyFolders {
|
EmptyFolders {
|
||||||
|
@ -235,6 +237,12 @@ pub struct AllowHardLinks {
|
||||||
pub allow_hard_links: bool,
|
pub allow_hard_links: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, StructOpt)]
|
||||||
|
pub struct DryRun {
|
||||||
|
#[structopt(long, help = "Do nothing and print the operation that would happen.")]
|
||||||
|
pub dryrun: bool,
|
||||||
|
}
|
||||||
|
|
||||||
impl FileToSave {
|
impl FileToSave {
|
||||||
pub fn file_name(&self) -> Option<&str> {
|
pub fn file_name(&self) -> Option<&str> {
|
||||||
if let Some(file_name) = &self.file_to_save {
|
if let Some(file_name) = &self.file_to_save {
|
||||||
|
|
|
@ -40,6 +40,7 @@ fn main() {
|
||||||
file_to_save,
|
file_to_save,
|
||||||
not_recursive,
|
not_recursive,
|
||||||
allow_hard_links,
|
allow_hard_links,
|
||||||
|
dryrun,
|
||||||
} => {
|
} => {
|
||||||
let mut df = DuplicateFinder::new();
|
let mut df = DuplicateFinder::new();
|
||||||
|
|
||||||
|
@ -53,6 +54,7 @@ fn main() {
|
||||||
df.set_hash_type(hash_type);
|
df.set_hash_type(hash_type);
|
||||||
df.set_recursive_search(!not_recursive.not_recursive);
|
df.set_recursive_search(!not_recursive.not_recursive);
|
||||||
df.set_ignore_hard_links(!allow_hard_links.allow_hard_links);
|
df.set_ignore_hard_links(!allow_hard_links.allow_hard_links);
|
||||||
|
df.set_dryrun(dryrun.dryrun);
|
||||||
|
|
||||||
df.find_duplicates(None, None);
|
df.find_duplicates(None, None);
|
||||||
|
|
||||||
|
|
|
@ -149,6 +149,7 @@ pub struct DuplicateFinder {
|
||||||
delete_method: DeleteMethod,
|
delete_method: DeleteMethod,
|
||||||
hash_type: HashType,
|
hash_type: HashType,
|
||||||
ignore_hard_links: bool,
|
ignore_hard_links: bool,
|
||||||
|
dryrun: bool,
|
||||||
stopped_search: bool,
|
stopped_search: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,6 +171,7 @@ impl DuplicateFinder {
|
||||||
stopped_search: false,
|
stopped_search: false,
|
||||||
ignore_hard_links: true,
|
ignore_hard_links: true,
|
||||||
hash_type: HashType::Blake3,
|
hash_type: HashType::Blake3,
|
||||||
|
dryrun: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,6 +245,10 @@ impl DuplicateFinder {
|
||||||
self.ignore_hard_links = ignore_hard_links;
|
self.ignore_hard_links = ignore_hard_links;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_dryrun(&mut self, dryrun: bool) {
|
||||||
|
self.dryrun = dryrun;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_check_method(&mut self, check_method: CheckingMethod) {
|
pub fn set_check_method(&mut self, check_method: CheckingMethod) {
|
||||||
self.check_method = check_method;
|
self.check_method = check_method;
|
||||||
}
|
}
|
||||||
|
@ -944,7 +950,7 @@ impl DuplicateFinder {
|
||||||
match self.check_method {
|
match self.check_method {
|
||||||
CheckingMethod::Name => {
|
CheckingMethod::Name => {
|
||||||
for vector in self.files_with_identical_names.values() {
|
for vector in self.files_with_identical_names.values() {
|
||||||
let tuple: (u64, usize, usize) = delete_files(vector, &self.delete_method, &mut self.text_messages.warnings);
|
let tuple: (u64, usize, usize) = delete_files(vector, &self.delete_method, &mut self.text_messages, self.dryrun);
|
||||||
self.information.gained_space += tuple.0;
|
self.information.gained_space += tuple.0;
|
||||||
self.information.number_of_removed_files += tuple.1;
|
self.information.number_of_removed_files += tuple.1;
|
||||||
self.information.number_of_failed_to_remove_files += tuple.2;
|
self.information.number_of_failed_to_remove_files += tuple.2;
|
||||||
|
@ -953,7 +959,7 @@ impl DuplicateFinder {
|
||||||
CheckingMethod::Hash | CheckingMethod::HashMB => {
|
CheckingMethod::Hash | CheckingMethod::HashMB => {
|
||||||
for vector_vectors in self.files_with_identical_hashes.values() {
|
for vector_vectors in self.files_with_identical_hashes.values() {
|
||||||
for vector in vector_vectors.iter() {
|
for vector in vector_vectors.iter() {
|
||||||
let tuple: (u64, usize, usize) = delete_files(vector, &self.delete_method, &mut self.text_messages.warnings);
|
let tuple: (u64, usize, usize) = delete_files(vector, &self.delete_method, &mut self.text_messages, self.dryrun);
|
||||||
self.information.gained_space += tuple.0;
|
self.information.gained_space += tuple.0;
|
||||||
self.information.number_of_removed_files += tuple.1;
|
self.information.number_of_removed_files += tuple.1;
|
||||||
self.information.number_of_failed_to_remove_files += tuple.2;
|
self.information.number_of_failed_to_remove_files += tuple.2;
|
||||||
|
@ -962,7 +968,7 @@ impl DuplicateFinder {
|
||||||
}
|
}
|
||||||
CheckingMethod::Size => {
|
CheckingMethod::Size => {
|
||||||
for vector in self.files_with_identical_size.values() {
|
for vector in self.files_with_identical_size.values() {
|
||||||
let tuple: (u64, usize, usize) = delete_files(vector, &self.delete_method, &mut self.text_messages.warnings);
|
let tuple: (u64, usize, usize) = delete_files(vector, &self.delete_method, &mut self.text_messages, self.dryrun);
|
||||||
self.information.gained_space += tuple.0;
|
self.information.gained_space += tuple.0;
|
||||||
self.information.number_of_removed_files += tuple.1;
|
self.information.number_of_removed_files += tuple.1;
|
||||||
self.information.number_of_failed_to_remove_files += tuple.2;
|
self.information.number_of_failed_to_remove_files += tuple.2;
|
||||||
|
@ -1216,7 +1222,7 @@ impl PrintResults for DuplicateFinder {
|
||||||
|
|
||||||
/// Functions to remove slice(vector) of files with provided method
|
/// Functions to remove slice(vector) of files with provided method
|
||||||
/// Returns size of removed elements, number of deleted and failed to delete files and modified warning list
|
/// Returns size of removed elements, number of deleted and failed to delete files and modified warning list
|
||||||
fn delete_files(vector: &[FileEntry], delete_method: &DeleteMethod, warnings: &mut Vec<String>) -> (u64, usize, usize) {
|
fn delete_files(vector: &[FileEntry], delete_method: &DeleteMethod, text_messages: &mut Messages, dryrun: bool) -> (u64, usize, usize) {
|
||||||
assert!(vector.len() > 1, "Vector length must be bigger than 1(This should be done in previous steps).");
|
assert!(vector.len() > 1, "Vector length must be bigger than 1(This should be done in previous steps).");
|
||||||
let mut gained_space: u64 = 0;
|
let mut gained_space: u64 = 0;
|
||||||
let mut removed_files: usize = 0;
|
let mut removed_files: usize = 0;
|
||||||
|
@ -1238,17 +1244,40 @@ fn delete_files(vector: &[FileEntry], delete_method: &DeleteMethod, warnings: &m
|
||||||
} else if removed_files + failed_to_remove_files >= n {
|
} else if removed_files + failed_to_remove_files >= n {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let r = match delete_method {
|
let r = match delete_method {
|
||||||
DeleteMethod::OneOldest | DeleteMethod::OneNewest | DeleteMethod::AllExceptOldest | DeleteMethod::AllExceptNewest => fs::remove_file(&file.path),
|
DeleteMethod::OneOldest | DeleteMethod::OneNewest | DeleteMethod::AllExceptOldest | DeleteMethod::AllExceptNewest => {
|
||||||
DeleteMethod::HardLink => make_hard_link(&vector[q_index].path, &file.path),
|
if dryrun {
|
||||||
DeleteMethod::None => Ok(()),
|
Ok(Some(format!("Delete {}", file.path.display())))
|
||||||
|
} else {
|
||||||
|
fs::remove_file(&file.path).map(|_| None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DeleteMethod::HardLink => {
|
||||||
|
let src = &vector[q_index].path;
|
||||||
|
if dryrun {
|
||||||
|
Ok(Some(format!("Replace file {} with hard link to {}", file.path.display(), src.display())))
|
||||||
|
} else {
|
||||||
|
make_hard_link(&src, &file.path).map(|_| None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DeleteMethod::None => Ok(None),
|
||||||
};
|
};
|
||||||
if let Err(e) = r {
|
|
||||||
failed_to_remove_files += 1;
|
match r {
|
||||||
warnings.push(format!("Failed to remove {} ({})", file.path.display(), e));
|
Err(e) => {
|
||||||
} else {
|
failed_to_remove_files += 1;
|
||||||
removed_files += 1;
|
text_messages.warnings.push(format!("Failed to remove {} ({})", file.path.display(), e));
|
||||||
gained_space += file.size;
|
}
|
||||||
|
Ok(Some(msg)) => {
|
||||||
|
text_messages.messages.push(msg);
|
||||||
|
removed_files += 1;
|
||||||
|
gained_space += file.size;
|
||||||
|
}
|
||||||
|
Ok(None) => {
|
||||||
|
removed_files += 1;
|
||||||
|
gained_space += file.size;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(gained_space, removed_files, failed_to_remove_files)
|
(gained_space, removed_files, failed_to_remove_files)
|
||||||
|
|
Loading…
Reference in a new issue