fs: keep timestamps of files skipped by cpSync

With `force: false` and `preserveTimestamps: true`, the native
directory copy used by `cpSync()` left existing destination files in
place but still copied the source timestamps onto them. The JavaScript
walk, used by `cpSync()` with a `filter` and by `fs.cp()`, leaves
those files untouched.

Only copy the timestamps of files that were actually copied, as is
already done for their mode.

Signed-off-by: Christian Aurich Zanettini Martins <christian.aurichzm@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66305
Reviewed-By: Anna Henningsen <anna@addaleax.net>
This commit is contained in:
Christian Aurich Zanettini Martins authored and GitHub committed 2026-10-02 22:33:38 +00:00
1 parent d880e7474c
commit 7fab6566c0
2 files changed
+35 -1

No files matched your search

+1 -1
View File
@@ -5079,7 +5079,7 @@ CpError CopyDirRecursive(const std::filesystem::path& src_path,
return CpError::Std(error, dest_str);
}
if (options.preserve_timestamps) {
if (copied && options.preserve_timestamps) {
CpError utimes = CopyUtimes(dir_entry.path(), dest_file_path);
if (utimes.kind != CpError::kNone) {
return utimes;
@@ -0,0 +1,34 @@
// This tests that cpSync with force: false and preserveTimestamps: true leaves
// the timestamps of the destination files it skips untouched.
import { mustNotMutateObjectDeep } from '../common/index.mjs';
import { nextdir } from '../common/fs.js';
import assert from 'node:assert';
import { cpSync, mkdirSync, readFileSync, statSync, utimesSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';
import tmpdir from '../common/tmpdir.js';
tmpdir.refresh();
const src = nextdir();
mkdirSync(src, { recursive: true });
writeFileSync(join(src, 'file.txt'), 'src', 'utf8');
utimesSync(join(src, 'file.txt'), 1000, 1000);
// Without a filter the tree is copied in C++, with one it is walked in
// JavaScript.
for (const filter of [undefined, () => true]) {
const dest = nextdir();
mkdirSync(dest, { recursive: true });
writeFileSync(join(dest, 'file.txt'), 'dest', 'utf8');
utimesSync(join(dest, 'file.txt'), 2000, 2000);
cpSync(src, dest, mustNotMutateObjectDeep({
filter,
force: false,
preserveTimestamps: true,
recursive: true,
}));
assert.strictEqual(readFileSync(join(dest, 'file.txt'), 'utf8'), 'dest');
assert.strictEqual(statSync(join(dest, 'file.txt')).mtime.getTime(), 2000 * 1000);
}