Add file attribute handling flag to cp (#11491)

# Description
This PR adds possibility to preserve/strip attributes from files when
using `cp` (via uu_cp::Attributes). To achieve this a single `--preserve
<list of attributes>` flag is added. This is different from how
coreutils and uutils cp function, but I believe this is better for
nushell.

Coreutils cp has three options `-p`, `--preserve` and `--no-presevce`.
The logic of these two options is not straightforward. As far as I
understand it is:
1. By default only mode attributes are preserved
2. `--preserve` option adds to default preserved attributes specified
ones (e.g. `--preserve=xattr,timestamps` will preserve mode, timestamps
and xattr)
3. `-p` is the same as `--preserve=mode,ownership,timestamps`
4. `--no-preserve` option rejects specified attributes (having priority
over `--preserve`)

However (in my opinion) the `--no-preserve` option is not needed,
because its only use seems to be rejecting attributes preserved by
default. But there is no need for this in nushell, because `--preserve`
can be specified with empty list as argument (whereas coreutils cp will
display a `cp: ambiguous argument ‘’ for ‘--preserve’` error if
`--preserve` is used with empty string as argument).

So to simplify this command is suggest (and implemented) only the
`--preserve` with the following logic:
1. By default mode attribute is preserved (as in coreutils cp)
2. `--preserve [ ... ]` will overwrite default with whatever is
specified in list (empty list meaning preserve nothing)

This way cp without `--preserve` behaves the same as coreutils `cp`, but
instead of using combinations of `--preserve` and `--no-preserve` one
needs to use `--preserve [ ... ]` with all attributes specified
explicitly. This seems more user-friendly to me as it does not require
remembering what the attributes preserved by default are and rejecting
them manually. However I see the possible problem with behavior
different from coreutils implementation, so some feedback is
apprecieated!

# User-Facing Changes
Users can now preserve or reject file attributes when using `cp`

# Tests + Formatting
Added tests manipulating mode and timestamps attributes.
This commit is contained in:
Artemiy
2024-01-12 21:02:55 +03:00
committed by GitHub
parent 724818030d
commit 387c5462e9
2 changed files with 177 additions and 2 deletions

View File

@ -1027,3 +1027,76 @@ fn copies_files_with_glob_metachars(#[case] src_name: &str) {
fn copies_files_with_glob_metachars_nw(#[case] src_name: &str) {
copies_files_with_glob_metachars(src_name);
}
#[cfg(not(windows))]
#[test]
fn test_cp_preserve_timestamps() {
// Preserve timestamp and mode
Playground::setup("ucp_test_35", |dirs, sandbox| {
sandbox.with_files(vec![EmptyFile("file.txt")]);
let actual = nu!(
cwd: dirs.test(),
"
chmod +x file.txt
cp --preserve [ mode timestamps ] file.txt other.txt
let old_attrs = ls -l file.txt | get 0 | select mode accessed modified
let new_attrs = ls -l other.txt | get 0 | select mode accessed modified
$old_attrs == $new_attrs
",
);
assert!(actual.err.is_empty());
assert_eq!(actual.out, "true");
});
}
#[cfg(not(windows))]
#[test]
fn test_cp_preserve_only_timestamps() {
// Preserve timestamps and discard all other attributes including mode
Playground::setup("ucp_test_35", |dirs, sandbox| {
sandbox.with_files(vec![EmptyFile("file.txt")]);
let actual = nu!(
cwd: dirs.test(),
"
chmod +x file.txt
cp --preserve [ timestamps ] file.txt other.txt
let old_attrs = ls -l file.txt | get 0 | select mode accessed modified
let new_attrs = ls -l other.txt | get 0 | select mode accessed modified
print (($old_attrs | select mode) != ($new_attrs | select mode))
print (($old_attrs | select accessed modified) == ($new_attrs | select accessed modified))
",
);
assert!(actual.err.is_empty());
assert_eq!(actual.out, "truetrue");
});
}
#[cfg(not(windows))]
#[test]
fn test_cp_preserve_nothing() {
// Preserve no attributes
Playground::setup("ucp_test_35", |dirs, sandbox| {
sandbox.with_files(vec![EmptyFile("file.txt")]);
let actual = nu!(
cwd: dirs.test(),
"
chmod +x file.txt
cp --preserve [] file.txt other.txt
let old_attrs = ls -l file.txt | get 0 | select mode accessed modified
let new_attrs = ls -l other.txt | get 0 | select mode accessed modified
$old_attrs != $new_attrs
",
);
assert!(actual.err.is_empty());
assert_eq!(actual.out, "true");
});
}