From ade3edb7fd58cfd2432283de58a5282489e5d2d9 Mon Sep 17 00:00:00 2001 From: Jasha Date: Sat, 16 Nov 2024 13:37:13 -0500 Subject: [PATCH 1/5] enable test_cp_recurse on macos --- crates/nu-command/tests/commands/ucp.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/crates/nu-command/tests/commands/ucp.rs b/crates/nu-command/tests/commands/ucp.rs index d434d77824..4b44e44951 100644 --- a/crates/nu-command/tests/commands/ucp.rs +++ b/crates/nu-command/tests/commands/ucp.rs @@ -615,9 +615,7 @@ static TEST_COPY_TO_FOLDER: &str = "hello_dir/"; static TEST_COPY_TO_FOLDER_FILE: &str = "hello_dir/hello_world.txt"; static TEST_COPY_FROM_FOLDER: &str = "hello_dir_with_file/"; static TEST_COPY_FROM_FOLDER_FILE: &str = "hello_dir_with_file/hello_world.txt"; -#[cfg(not(target_os = "macos"))] static TEST_COPY_TO_FOLDER_NEW: &str = "hello_dir_new"; -#[cfg(not(target_os = "macos"))] static TEST_COPY_TO_FOLDER_NEW_FILE: &str = "hello_dir_new/hello_world.txt"; #[test] @@ -712,7 +710,6 @@ fn test_cp_multiple_files() { } #[test] -#[cfg(not(target_os = "macos"))] fn test_cp_recurse() { Playground::setup("ucp_test_22", |dirs, sandbox| { // Create the relevant target directories From 0ab9337eccee4c37c7a9bde430e7d48db6f5d966 Mon Sep 17 00:00:00 2001 From: Jasha Date: Sat, 16 Nov 2024 14:10:04 -0500 Subject: [PATCH 2/5] Write tests passing -l flag to `cp` --- crates/nu-command/tests/commands/ucp.rs | 81 +++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/crates/nu-command/tests/commands/ucp.rs b/crates/nu-command/tests/commands/ucp.rs index 4b44e44951..b8b7224a62 100644 --- a/crates/nu-command/tests/commands/ucp.rs +++ b/crates/nu-command/tests/commands/ucp.rs @@ -47,6 +47,49 @@ fn copies_a_file_impl(progress: bool) { }); } +#[test] +fn copies_a_file_with_hardlink() { + copies_a_file_with_hardlink_impl(false); + copies_a_file_with_hardlink_impl(true); +} + +fn copies_a_file_with_hardlink_impl(progress: bool) { + Playground::setup("ucp_test_1-hardlink", |dirs, _| { + let test_file = dirs.formats().join("sample.ini"); + let progress_flag = if progress { "-p" } else { "" }; + + // Get the hash of the file content to check integrity after copy. + let first_hash = get_file_hash(test_file.display()); + + nu!( + cwd: dirs.root(), + "cp -l {} `{}` ucp_test_1-hardlink/sample.ini", + progress_flag, + test_file.display() + ); + + assert!(dirs.test().join("sample.ini").exists()); + + // Get the hash of the copied file content to check against first_hash. + let after_cp_hash = get_file_hash(dirs.test().join("sample.ini").display()); + assert_eq!(first_hash, after_cp_hash); + + // Modify file 1 by appending a line + nu!( + cwd: dirs.root(), + "echo 'new line' | save `{}` --append", + test_file.display() + ); + // We will compare the contents of file 1 and file 2. + // Since we're using a hardlink, the updated hashes should be the same. + let first_hash_modified = get_file_hash(test_file.display()); + assert_ne!(first_hash, first_hash_modified); + let after_cp_hash_modified = get_file_hash(dirs.test().join("sample.ini").display()); + assert_eq!(first_hash_modified, after_cp_hash_modified); + }); +} + + #[test] fn copies_the_file_inside_directory_if_path_to_copy_is_directory() { copies_the_file_inside_directory_if_path_to_copy_is_directory_impl(false); @@ -734,6 +777,44 @@ fn test_cp_recurse() { }); } +#[test] +fn test_cp_recurse_with_hardlink_flag() { + Playground::setup("ucp_test_22-hardlink", |dirs, sandbox| { + // Create the relevant target directories + sandbox.mkdir(TEST_COPY_FROM_FOLDER); + sandbox.mkdir(TEST_COPY_TO_FOLDER_NEW); + let src = dirs + .fixtures + .join("cp") + .join(TEST_COPY_FROM_FOLDER) + .join(TEST_COPY_FROM_FOLDER_FILE); + + let src_hash = get_file_hash(src.display()); + // Start test + nu!( + cwd: dirs.root(), + "cp -r -l {} ucp_test_22-hardlink/{}", + TEST_COPY_FROM_FOLDER, + TEST_COPY_TO_FOLDER_NEW, + ); + let after_cp_hash = get_file_hash(dirs.test().join(TEST_COPY_TO_FOLDER_NEW_FILE).display()); + assert_eq!(src_hash, after_cp_hash); + + // Modify file 1 by appending a line + nu!( + cwd: dirs.root(), + "echo 'new line' | save `{}` --append", + src.display() + ); + // We will compare the contents of file 1 and file 2. + // Since we're using a hardlink, the updated hashes should be the same. + let src_hash_modified = get_file_hash(src.display()); + assert_ne!(src_hash, src_hash_modified); + let after_cp_hash_modified = get_file_hash(dirs.test().join(TEST_COPY_TO_FOLDER_NEW_FILE).display()); + assert_eq!(src_hash_modified, after_cp_hash_modified); + }); +} + #[test] fn test_cp_with_dirs() { Playground::setup("ucp_test_23", |dirs, sandbox| { From c3e99bb597b8d540d502859aa2b4aa541750861b Mon Sep 17 00:00:00 2001 From: Jasha Date: Sun, 17 Nov 2024 09:13:16 -0500 Subject: [PATCH 3/5] cargo fmt --- crates/nu-command/tests/commands/ucp.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/nu-command/tests/commands/ucp.rs b/crates/nu-command/tests/commands/ucp.rs index b8b7224a62..6a509711b1 100644 --- a/crates/nu-command/tests/commands/ucp.rs +++ b/crates/nu-command/tests/commands/ucp.rs @@ -89,7 +89,6 @@ fn copies_a_file_with_hardlink_impl(progress: bool) { }); } - #[test] fn copies_the_file_inside_directory_if_path_to_copy_is_directory() { copies_the_file_inside_directory_if_path_to_copy_is_directory_impl(false); @@ -810,7 +809,8 @@ fn test_cp_recurse_with_hardlink_flag() { // Since we're using a hardlink, the updated hashes should be the same. let src_hash_modified = get_file_hash(src.display()); assert_ne!(src_hash, src_hash_modified); - let after_cp_hash_modified = get_file_hash(dirs.test().join(TEST_COPY_TO_FOLDER_NEW_FILE).display()); + let after_cp_hash_modified = + get_file_hash(dirs.test().join(TEST_COPY_TO_FOLDER_NEW_FILE).display()); assert_eq!(src_hash_modified, after_cp_hash_modified); }); } From 0c812bfbbfc4299ce6064c346b962a06031822cb Mon Sep 17 00:00:00 2001 From: Jasha Date: Sun, 17 Nov 2024 10:23:54 -0500 Subject: [PATCH 4/5] implement --link and --symbolic-link --- crates/nu-command/src/filesystem/ucp.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/crates/nu-command/src/filesystem/ucp.rs b/crates/nu-command/src/filesystem/ucp.rs index 79980e30db..fac683d8b8 100644 --- a/crates/nu-command/src/filesystem/ucp.rs +++ b/crates/nu-command/src/filesystem/ucp.rs @@ -47,6 +47,8 @@ impl Command for UCp { ) .switch("progress", "display a progress bar", Some('p')) .switch("no-clobber", "do not overwrite an existing file", Some('n')) + .switch("link", "hard-link files instead of copying", Some('l')) + .switch("symbolic-link", "make symbolic links instead of copying", Some('s')) .named( "preserve", SyntaxShape::List(Box::new(SyntaxShape::String)), @@ -109,10 +111,19 @@ impl Command for UCp { _input: PipelineData, ) -> Result { let interactive = call.has_flag(engine_state, stack, "interactive")?; - let (update, copy_mode) = if call.has_flag(engine_state, stack, "update")? { - (UpdateMode::ReplaceIfOlder, CopyMode::Update) + let update = if call.has_flag(engine_state, stack, "update")? { + UpdateMode::ReplaceIfOlder } else { - (UpdateMode::ReplaceAll, CopyMode::Copy) + UpdateMode::ReplaceAll + }; + let copy_mode = if call.has_flag(engine_state, stack, "link")? { + CopyMode::Link + } else if call.has_flag(engine_state, stack, "symbolic-link")? { + CopyMode::SymLink + } else if call.has_flag(engine_state, stack, "update")? { + CopyMode::Update + } else { + CopyMode::Copy }; let force = call.has_flag(engine_state, stack, "force")?; From 4d5ef57af7b866ffe72ba256693711551af9daab Mon Sep 17 00:00:00 2001 From: Jasha Date: Sun, 17 Nov 2024 11:13:52 -0500 Subject: [PATCH 5/5] wip on examples --- crates/nu-command/src/filesystem/ucp.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/crates/nu-command/src/filesystem/ucp.rs b/crates/nu-command/src/filesystem/ucp.rs index fac683d8b8..0d2ef6aed7 100644 --- a/crates/nu-command/src/filesystem/ucp.rs +++ b/crates/nu-command/src/filesystem/ucp.rs @@ -90,6 +90,21 @@ impl Command for UCp { example: "cp -u a b", result: None, }, + Example { + description: "", + example: "cp -s a b", + result: None, + }, + Example { + description: "", + example: "cp -l a b", + result: None, + }, + Example { + description: "", + example: "cp -r -l a b", + result: None, + }, Example { description: "Copy file preserving mode and timestamps attributes", example: "cp --preserve [ mode timestamps ] a b",