diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index f302d4873..b0c779679 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -279,6 +279,21 @@ static PRESERVABLE_ATTRIBUTES: &[&str] = &[ "all", ]; +static CP_UPDATE_LONG_HELP: &str = +"Do not copy a non-directory that has an existing destination with the same or newer modification timestamp; +instead, silently skip the file without failing. If timestamps are being preserved, the comparison is to the +source timestamp truncated to the resolutions of the destination file system and of the system calls used to +update timestamps; this avoids duplicate work if several ‘cp -pu’ commands are executed with the same source +and destination. This option is ignored if the -n or --no-clobber option is also specified. Also, if +--preserve=links is also specified (like with ‘cp -au’ for example), that will take precedence; consequently, +depending on the order that files are processed from the source, newer files in the destination may be +replaced, to mirror hard links in the source. which gives more control over which existing files in the +destination are replaced, and its value can be one of the following: + +all This is the default operation when an --update option is not specified, and results in all existing files in the destination being replaced. +none This is similar to the --no-clobber option, in that no files in the destination are replaced, but also skipping a file does not induce a failure. +older This is the default operation when --update is specified, and results in files being replaced if they’re older than the corresponding source file."; + #[cfg(not(unix))] static PRESERVABLE_ATTRIBUTES: &[&str] = &["mode", "timestamps", "context", "links", "xattr", "all"]; @@ -558,6 +573,7 @@ pub fn uu_app() -> Command { pub fn uumain(args: impl uucore::Args) -> UResult<()> { let matches = uu_app() .after_help(backup_control::BACKUP_CONTROL_LONG_HELP) + .after_help(CP_UPDATE_LONG_HELP) .try_get_matches_from(args); // The error is parsed here because we do not want version or help being printed to stderr. @@ -1628,22 +1644,38 @@ fn copy_file( } CopyMode::Update => { if dest.exists() { - let dest_metadata = fs::symlink_metadata(dest)?; + match options.update { + update_control::UpdateMode::ReplaceAll => { + copy_helper( + source, + dest, + options, + context, + source_is_symlink, + source_is_fifo, + symlinked_files, + )?; + } + update_control::UpdateMode::ReplaceNone => return Ok(()), + update_control::UpdateMode::ReplaceIfOlder => { + let dest_metadata = fs::symlink_metadata(dest)?; - let src_time = source_metadata.modified()?; - let dest_time = dest_metadata.modified()?; - if src_time <= dest_time { - return Ok(()); - } else { - copy_helper( - source, - dest, - options, - context, - source_is_symlink, - source_is_fifo, - symlinked_files, - )?; + let src_time = source_metadata.modified()?; + let dest_time = dest_metadata.modified()?; + if src_time <= dest_time { + return Ok(()); + } else { + copy_helper( + source, + dest, + options, + context, + source_is_symlink, + source_is_fifo, + symlinked_files, + )?; + } + } } } else { copy_helper( diff --git a/src/uu/mv/src/mv.rs b/src/uu/mv/src/mv.rs index 2951550c6..252446600 100644 --- a/src/uu/mv/src/mv.rs +++ b/src/uu/mv/src/mv.rs @@ -67,11 +67,23 @@ static OPT_VERBOSE: &str = "verbose"; static OPT_PROGRESS: &str = "progress"; static ARG_FILES: &str = "files"; +static MV_UPDATE_LONG_HELP: &str = + "Do not move a non-directory that has an existing destination with the same or newer modification timestamp; +instead, silently skip the file without failing. If the move is across file system boundaries, the comparison is +to the source timestamp truncated to the resolutions of the destination file system and of the system calls used +to update timestamps; this avoids duplicate work if several ‘mv -u’ commands are executed with the same source +and destination. This option is ignored if the -n or --no-clobber option is also specified. which gives more control +over which existing files in the destination are replaced, and its value can be one of the following: + +all This is the default operation when an --update option is not specified, and results in all existing files in the destination being replaced. +none This is similar to the --no-clobber option, in that no files in the destination are replaced, but also skipping a file does not induce a failure. +older This is the default operation when --update is specified, and results in files being replaced if they’re older than the corresponding source file."; + #[uucore::main] pub fn uumain(args: impl uucore::Args) -> UResult<()> { let mut app = uu_app() .after_help(backup_control::BACKUP_CONTROL_LONG_HELP) - .after_help(update_control::UPDATE_CONTROL_LONG_HELP); + .after_help(MV_UPDATE_LONG_HELP); let matches = app.try_get_matches_from_mut(args)?; if !matches.contains_id(OPT_TARGET_DIRECTORY) diff --git a/src/uucore/src/lib/mods/update_control.rs b/src/uucore/src/lib/mods/update_control.rs index a743b9340..ab97b0412 100644 --- a/src/uucore/src/lib/mods/update_control.rs +++ b/src/uucore/src/lib/mods/update_control.rs @@ -2,8 +2,6 @@ use clap::ArgMatches; pub static UPDATE_CONTROL_VALUES: &[&str] = &["all", "none", "old", ""]; -pub const UPDATE_CONTROL_LONG_HELP: &str = "VERY LONG HELP"; - #[derive(Clone, Eq, PartialEq)] pub enum UpdateMode { ReplaceAll, @@ -20,10 +18,10 @@ pub mod arguments { pub fn update() -> clap::Arg { clap::Arg::new(OPT_UPDATE) .long("update") - .help("some help") + .help("move only when the SOURCE file is newer than the destination file or when the destination file is missing") .value_parser(["", "none", "all", "older"]) .num_args(0..=1) - .default_missing_value("all") + .default_missing_value("older") .require_equals(true) .overrides_with("update") .action(clap::ArgAction::Set) @@ -32,7 +30,7 @@ pub mod arguments { pub fn update_no_args() -> clap::Arg { clap::Arg::new(OPT_UPDATE_NO_ARG) .short('u') - .help("like ") + .help("like --update but does not accept an argument") .action(ArgAction::SetTrue) } }