add ignore file to add command with force option

This commit is contained in:
grimhilt 2023-06-16 18:37:09 +02:00
parent ec6f1ebc96
commit 9956727cc9
6 changed files with 122 additions and 69 deletions

View File

@ -1,42 +1,74 @@
use clap::Values; use clap::Values;
use crate::utils; use crate::utils::{self, nextsyncignore};
use crate::store; use crate::store;
use std::path::Path; use std::path::{Path, PathBuf};
use std::io::Write; use std::io::Write;
use glob::glob;
pub fn add(files: Values<'_>) { pub struct AddArgs<'a> {
let root = match utils::path::nextsync_root() { pub files: Values<'a>,
Some(path) => path, pub force: bool,
None => { }
eprintln!("fatal: not a nextsync repository (or any of the parent directories): .nextsync");
std::process::exit(1);
}
};
let mut index_path = root.clone(); pub fn add(args: AddArgs) {
index_path.push(".nextsync"); let mut index_file = store::index::open();
let mut index_file = store::index::open(index_path); let mut added_files: Vec<String> = vec![];
// todo avoid duplicate
// ./folder ./folder/file
let file_vec: Vec<&str> = files.collect(); let file_vec: Vec<&str> = args.files.collect();
for file in file_vec { for file in file_vec {
let path = Path::new(file); let path = Path::new(file);
println!("{}", file);
match path.exists() { match path.exists() {
true => { true => {
match writeln!(index_file, "{}", path.display()) { if path.is_dir() {
Ok(()) => (), added_files.push(String::from(path.to_str().unwrap()));
Err(err) => eprintln!("{}", err), add_folder_content(path.to_path_buf(), &mut added_files);
} else {
added_files.push(String::from(path.to_str().unwrap()));
} }
}, },
false => { false => {
match writeln!(index_file, "{}", path.display()) { // todo deleted file/folder verif if exists
Ok(()) => (), added_files.push(String::from(path.to_str().unwrap()));
Err(err) => eprintln!("{}", err),
}
// todo can be regex
} }
} }
} }
// check ignored file if not forced
if !args.force {
let (ignored, ignored_files) = nextsyncignore::ignore_files(&mut added_files);
if ignored {
// todo multiple nextsyncignore
println!("The following paths are ignored by your .nextsyncignore file:");
for file in ignored_files {
println!("{}", file);
}
}
}
// save all added_files in index
for file in added_files {
match writeln!(index_file, "{}", file) {
Ok(()) => (),
Err(err) => eprintln!("{}", err),
}
}
drop(index_file);
}
fn add_folder_content(path: PathBuf, added_files: &mut Vec<String>) {
let mut folders: Vec<PathBuf> = vec![];
folders.push(path);
while let Some(folder) = folders.pop() {
if let Ok(entries) = utils::read::read_folder(folder.clone()) {
for entry in entries {
let path_entry = PathBuf::from(entry);
if path_entry.is_dir() {
folders.push(path_entry.clone());
}
added_files.push(String::from(path_entry.to_str().unwrap()));
}
}
}
} }

View File

@ -15,9 +15,7 @@ enum RemoveSide {
Right, Right,
} }
#[derive(PartialEq)] #[derive(PartialEq, Debug, Clone)]
#[derive(Debug)]
#[derive(Clone)]
pub enum State { pub enum State {
Default, Default,
New, New,
@ -39,8 +37,7 @@ pub fn status() {
print_status(staged_objs, objs); print_status(staged_objs, objs);
} }
#[derive(Debug)] #[derive(Debug, Clone)]
#[derive(Clone)]
pub struct Obj { pub struct Obj {
pub otype: String, pub otype: String,
pub name: String, pub name: String,
@ -324,10 +321,16 @@ mod tests {
let hash4 = hasher.result_str(); let hash4 = hasher.result_str();
hasher.reset(); hasher.reset();
let mut hashes = HashSet::new(); let mut hashes = HashMap::new();
hashes.insert(hash1.clone()); let default_obj = Obj {
hashes.insert(hash2.clone()); otype: String::from("tree"),
hashes.insert(hash4.clone()); name: String::from("test"),
path: PathBuf::from(""),
state: State::Default,
};
hashes.insert(hash1.clone(), default_obj.clone());
hashes.insert(hash2.clone(), default_obj.clone());
hashes.insert(hash4.clone(), default_obj.clone());
let mut objects: Vec<String> = vec![]; let mut objects: Vec<String> = vec![];
objects.push(String::from("file1")); objects.push(String::from("file1"));
@ -336,7 +339,7 @@ mod tests {
remove_duplicate(&mut hashes, &mut objects, RemoveSide::Both); remove_duplicate(&mut hashes, &mut objects, RemoveSide::Both);
dbg!(hashes.clone()); dbg!(hashes.clone());
dbg!(objects.clone()); dbg!(objects.clone());
assert_eq!(hashes.contains(&hash4), true); assert_eq!(hashes.contains_key(&hash4), true);
assert_eq!(hashes.len(), 1); assert_eq!(hashes.len(), 1);
assert_eq!(objects, vec!["file3"]); assert_eq!(objects, vec!["file3"]);
} }

View File

@ -1,4 +1,5 @@
use clap::{App, Arg, SubCommand}; use clap::{App, Arg, SubCommand};
use crate::commands::add::AddArgs;
mod commands; mod commands;
mod utils; mod utils;
mod services; mod services;
@ -55,6 +56,12 @@ fn main() {
.value_name("FILE") .value_name("FILE")
.help("Files to add"), .help("Files to add"),
) )
.arg(
Arg::with_name("force")
.short("f")
.long("force")
.help("Allow adding otherwise ignored files."),
)
) )
.subcommand( .subcommand(
SubCommand::with_name("config") SubCommand::with_name("config")
@ -85,7 +92,10 @@ fn main() {
commands::status::status(); commands::status::status();
} else if let Some(matches) = matches.subcommand_matches("add") { } else if let Some(matches) = matches.subcommand_matches("add") {
if let Some(files) = matches.values_of("files") { if let Some(files) = matches.values_of("files") {
commands::add::add(files); commands::add::add(AddArgs {
files: files,
force: matches.is_present("force"),
});
} }
} else if let Some(_) = matches.subcommand_matches("reset") { } else if let Some(_) = matches.subcommand_matches("reset") {
commands::reset::reset(); commands::reset::reset();

View File

@ -11,7 +11,12 @@ pub fn _read_only(mut path: PathBuf) -> File {
.open(path).expect("Cannot open index file") .open(path).expect("Cannot open index file")
} }
pub fn open(mut path: PathBuf) -> File { pub fn open() -> File {
let mut path = match path::nextsync() {
Some(p) => p,
None => todo!(),
};
path.push("index"); path.push("index");
OpenOptions::new() OpenOptions::new()
.read(true) .read(true)
@ -27,12 +32,11 @@ pub fn read_line(mut path: PathBuf) -> io::Result<io::Lines<io::BufReader<File>>
} }
pub fn rm_line(line: &str) -> io::Result<()> { pub fn rm_line(line: &str) -> io::Result<()> {
let mut root = match path::nextsync_root() { let mut root = match path::nextsync() {
Some(path) => path, Some(path) => path,
None => todo!(), None => todo!(),
}; };
root.push(".nextsync");
root.push("index"); root.push("index");
read::rm_line(root, line)?; read::rm_line(root, line)?;
Ok(()) Ok(())

View File

@ -1,2 +1,3 @@
pub mod path; pub mod path;
pub mod read; pub mod read;
pub mod nextsyncignore;

View File

@ -23,15 +23,14 @@ fn read_lines() -> Result<Vec<String>, ()> {
Ok(vec![]) Ok(vec![])
} }
fn ignore_files(files: &mut Vec<String>) -> bool { pub fn ignore_files(files: &mut Vec<String>) -> (bool, Vec<String>) {
let ignored = false; let mut ignored_f = vec![];
let origin_len = files.len();
if let Some(path) = path::nextsyncignore() { if let Some(path) = path::nextsyncignore() {
if let Ok(lines) = read_lines() { if let Ok(lines) = read_lines() {
files.retain(|file| ignore_file(file, lines.clone())); files.retain(|file| !ignore_file(file, lines.clone(), &mut ignored_f));
} }
} }
files.len() != origin_len (ignored_f.len() > 0, ignored_f)
} }
fn normalize_rule(l: String) -> String { fn normalize_rule(l: String) -> String {
@ -55,7 +54,7 @@ fn normalize_rule(l: String) -> String {
line line
} }
pub fn ignore_file(path: &String, lines: Vec<String>) -> bool { pub fn ignore_file(path: &String, lines: Vec<String>, ignored_f: &mut Vec<String>) -> bool {
let mut ignored = false; let mut ignored = false;
for mut line in lines { for mut line in lines {
if line.starts_with("!") { if line.starts_with("!") {
@ -74,6 +73,9 @@ pub fn ignore_file(path: &String, lines: Vec<String>) -> bool {
} }
} }
} }
if ignored {
ignored_f.push(path.to_string());
}
ignored ignored
} }
@ -92,29 +94,29 @@ mod tests {
lines.push(normalize_rule(l.clone())); lines.push(normalize_rule(l.clone()));
} }
} }
let mut ignf = vec![];
assert_eq!(ignore_file(&String::from("error.log"), lines.clone(), &mut ignf), true);
assert_eq!(ignore_file(&String::from("./error.log"), lines.clone(), &mut ignf), true);
assert_eq!(ignore_file(&String::from("dir/error.log"), lines.clone(), &mut ignf), true);
assert_eq!(ignore_file(&String::from("error.log"), lines.clone()), true); assert_eq!(ignore_file(&String::from("exclude"), lines.clone(), &mut ignf), true);
assert_eq!(ignore_file(&String::from("./error.log"), lines.clone()), true); assert_eq!(ignore_file(&String::from("dir/exclude"), lines.clone(), &mut ignf), true);
assert_eq!(ignore_file(&String::from("dir/error.log"), lines.clone()), true);
assert_eq!(ignore_file(&String::from("exclude"), lines.clone()), true); assert_eq!(ignore_file(&String::from("logs/dir/file2"), lines.clone(), &mut ignf), true);
assert_eq!(ignore_file(&String::from("dir/exclude"), lines.clone()), true); assert_eq!(ignore_file(&String::from("dir/logs/dir/file2"), lines.clone(), &mut ignf), false);
assert_eq!(ignore_file(&String::from("logs/dir/file2"), lines.clone()), true); assert_eq!(ignore_file(&String::from("build/target/file1"), lines.clone(), &mut ignf), true);
assert_eq!(ignore_file(&String::from("dir/logs/dir/file2"), lines.clone()), false); assert_eq!(ignore_file(&String::from("build/target/dir/file1"), lines.clone(), &mut ignf), true);
assert_eq!(ignore_file(&String::from("build"), lines.clone(), &mut ignf), false);
assert_eq!(ignore_file(&String::from("build/target"), lines.clone(), &mut ignf), true);
assert_eq!(ignore_file(&String::from("dir/build/target"), lines.clone(), &mut ignf), false);
assert_eq!(ignore_file(&String::from("build/target/file1"), lines.clone()), true); assert_eq!(ignore_file(&String::from("dir/file.swp"), lines.clone(), &mut ignf), true);
assert_eq!(ignore_file(&String::from("build/target/dir/file1"), lines.clone()), true); assert_eq!(ignore_file(&String::from(".swp"), lines.clone(), &mut ignf), false);
assert_eq!(ignore_file(&String::from("build"), lines.clone()), false);
assert_eq!(ignore_file(&String::from("build/target"), lines.clone()), true);
assert_eq!(ignore_file(&String::from("dir/build/target"), lines.clone()), false);
assert_eq!(ignore_file(&String::from("dir/file.swp"), lines.clone()), true); assert_eq!(ignore_file(&String::from("secret"), lines.clone(), &mut ignf), true);
assert_eq!(ignore_file(&String::from(".swp"), lines.clone()), false); assert_eq!(ignore_file(&String::from("dir/secret"), lines.clone(), &mut ignf), true);
assert_eq!(ignore_file(&String::from("dir/secret/file"), lines.clone(), &mut ignf), true);
assert_eq!(ignore_file(&String::from("secret"), lines.clone()), true);
assert_eq!(ignore_file(&String::from("dir/secret"), lines.clone()), true);
assert_eq!(ignore_file(&String::from("dir/secret/file"), lines.clone()), true);
} }
#[test] #[test]
@ -129,12 +131,13 @@ mod tests {
} }
} }
assert_eq!(ignore_file(&String::from("file"), lines.clone()), true); let mut ignf = vec![];
assert_eq!(ignore_file(&String::from("dir/file"), lines.clone()), true); assert_eq!(ignore_file(&String::from("file"), lines.clone(), &mut ignf), true);
assert_eq!(ignore_file(&String::from("file.log"), lines.clone()), false); assert_eq!(ignore_file(&String::from("dir/file"), lines.clone(), &mut ignf), true);
assert_eq!(ignore_file(&String::from("log.file"), lines.clone()), false); assert_eq!(ignore_file(&String::from("file.log"), lines.clone(), &mut ignf), false);
assert_eq!(ignore_file(&String::from("dir/file.log"), lines.clone()), false); assert_eq!(ignore_file(&String::from("log.file"), lines.clone(), &mut ignf), false);
assert_eq!(ignore_file(&String::from("dir/log.file"), lines.clone()), false); assert_eq!(ignore_file(&String::from("dir/file.log"), lines.clone(), &mut ignf), false);
assert_eq!(ignore_file(&String::from("dir/log.file"), lines.clone(), &mut ignf), false);
} }
} }