Skip to content

Commit 6786fa0

Browse files
committed
cp: correctly copy ancestor dirs in --parents mode
Fix a bug where `cp` failed to copy ancestor directories when using the `--parents` option. For example, before this commit: $ mkdir -p a/b/c d $ cp --parents a/b/c d $ find d d d/c After this commit $ mkdir -p a/b/c d $ cp --parents a/b/c d $ find d d d/a d/a/b d/a/b/c This commit also adds the correct messages for `--verbose` mode: $ cp -r --parents --verbose a/b/c d a -> d/a a/b -> d/a/b 'a/b/c' -> 'd/a/b/c' Fixes #3332.
1 parent 42b1618 commit 6786fa0

File tree

2 files changed

+48
-1
lines changed

2 files changed

+48
-1
lines changed

src/uu/cp/src/cp.rs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1076,6 +1076,38 @@ fn copy_directory(
10761076
.into());
10771077
}
10781078

1079+
// If in `--parents` mode, create all the necessary ancestor directories.
1080+
//
1081+
// For example, if the command is `cp --parents a/b/c d`, that
1082+
// means we need to copy the two ancestor directories first:
1083+
//
1084+
// a -> d/a
1085+
// a/b -> d/a/b
1086+
//
1087+
let tmp = if options.parents {
1088+
if let Some(parent) = root.parent() {
1089+
let new_target = target.join(parent);
1090+
std::fs::create_dir_all(&new_target)?;
1091+
1092+
if options.verbose {
1093+
let mut ancestors = vec![];
1094+
for p in parent.ancestors() {
1095+
ancestors.push(p);
1096+
}
1097+
for p in ancestors.iter().rev().skip(1) {
1098+
println!("{}", context_for(p, &target.join(p)));
1099+
}
1100+
}
1101+
1102+
new_target
1103+
} else {
1104+
target.to_path_buf()
1105+
}
1106+
} else {
1107+
target.to_path_buf()
1108+
};
1109+
let target = tmp.as_path();
1110+
10791111
let current_dir =
10801112
env::current_dir().unwrap_or_else(|e| crash!(1, "failed to get current directory {}", e));
10811113

@@ -1134,7 +1166,10 @@ fn copy_directory(
11341166
if target.is_file() {
11351167
return Err("cannot overwrite non-directory with directory".into());
11361168
}
1137-
fs::create_dir_all(local_to_target)?;
1169+
fs::create_dir_all(&local_to_target)?;
1170+
if options.verbose {
1171+
println!("{}", context_for(p.path(), &local_to_target));
1172+
}
11381173
} else if !path.is_dir() {
11391174
if preserve_hard_links {
11401175
let mut found_hard_link = false;

tests/by-util/test_cp.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1956,6 +1956,18 @@ fn test_copy_same_symlink_no_dereference_dangling() {
19561956
ucmd.args(&["-d", "a", "b"]).succeeds();
19571957
}
19581958

1959+
#[test]
1960+
fn test_cp_parents_2_dirs() {
1961+
let (at, mut ucmd) = at_and_ucmd!();
1962+
at.mkdir_all("a/b/c");
1963+
at.mkdir("d");
1964+
ucmd.args(&["--verbose", "-a", "--parents", "a/b/c", "d"])
1965+
.succeeds()
1966+
.no_stderr()
1967+
.stdout_is("a -> d/a\na/b -> d/a/b\n'a/b/c' -> 'd/a/b/c'\n");
1968+
assert!(at.dir_exists("d/a/b/c"));
1969+
}
1970+
19591971
#[test]
19601972
#[ignore = "issue #3332"]
19611973
fn test_cp_parents_2() {

0 commit comments

Comments
 (0)