Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lib/internal/fs/cp/cp-sync.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,9 @@ function copyDir(src, dest, opts, mkDir, srcMode) {
} finally {
dir.closeSync();

if (opts.preserveTimestamps) {
Comment thread
jasnell marked this conversation as resolved.
setDestTimestamps(src, dest);
}
if (srcMode !== undefined) {
setDestMode(dest, srcMode);
}
Expand Down
8 changes: 7 additions & 1 deletion lib/internal/fs/cp/cp.js
Original file line number Diff line number Diff line change
Expand Up @@ -311,12 +311,18 @@ async function onDir(srcStat, destStat, src, dest, opts) {
code: 'EEXIST',
});
}
return copyDir(src, dest, opts);
await copyDir(src, dest, opts);
if (opts.preserveTimestamps) {
await setDestTimestamps(src, dest);
}
}

async function mkDirAndCopy(srcMode, src, dest, opts) {
await mkdir(dest);
await copyDir(src, dest, opts);
if (opts.preserveTimestamps) {
await setDestTimestamps(src, dest);
}
return setDestMode(dest, srcMode);
}

Expand Down
43 changes: 43 additions & 0 deletions test/parallel/test-fs-cp-async-preserve-timestamps-dir.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// This tests that cp() preserves directory timestamps
// when preserveTimestamps is true.
import { mustCall } from '../common/index.mjs';
import { nextdir } from '../common/fs.js';
import assert from 'node:assert';
import { cp, mkdirSync, writeFileSync, utimesSync, statSync } from 'node:fs';
import { join } from 'node:path';
import tmpdir from '../common/tmpdir.js';

tmpdir.refresh();

// Build a source tree with a known past timestamp on directories.
const src = nextdir();
mkdirSync(join(src, 'subdir'), { recursive: true });
writeFileSync(join(src, 'subdir', 'file.txt'), 'hello');

const pastDate = new Date('2020-01-01T00:00:00Z');
utimesSync(join(src, 'subdir', 'file.txt'), pastDate, pastDate);
utimesSync(join(src, 'subdir'), pastDate, pastDate);
utimesSync(src, pastDate, pastDate);

// Copy with preserveTimestamps.
const dest = nextdir();
cp(src, dest, {
recursive: true,
preserveTimestamps: true,
}, mustCall((err) => {
assert.strictEqual(err, null);

// Verify file timestamps are preserved (existing behaviour).
const srcFileStat = statSync(join(src, 'subdir', 'file.txt'));
const destFileStat = statSync(join(dest, 'subdir', 'file.txt'));
assert.strictEqual(srcFileStat.mtime.getTime(), destFileStat.mtime.getTime());

// Verify directory timestamps are preserved (the bug fix).
const srcDirStat = statSync(join(src, 'subdir'));
const destDirStat = statSync(join(dest, 'subdir'));
assert.strictEqual(srcDirStat.mtime.getTime(), destDirStat.mtime.getTime());

const srcRootStat = statSync(src);
const destRootStat = statSync(dest);
assert.strictEqual(srcRootStat.mtime.getTime(), destRootStat.mtime.getTime());
}));
42 changes: 42 additions & 0 deletions test/parallel/test-fs-cp-sync-preserve-timestamps-dir.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// This tests that cpSync with a filter preserves directory timestamps
// when preserveTimestamps is true.
import '../common/index.mjs';
import { nextdir } from '../common/fs.js';
import assert from 'node:assert';
import { cpSync, mkdirSync, writeFileSync, utimesSync, statSync } from 'node:fs';
import { join } from 'node:path';
import tmpdir from '../common/tmpdir.js';

tmpdir.refresh();

// Build a source tree with a known past timestamp on directories.
const src = nextdir();
mkdirSync(join(src, 'subdir'), { recursive: true });
writeFileSync(join(src, 'subdir', 'file.txt'), 'hello');

const pastDate = new Date('2020-01-01T00:00:00Z');
utimesSync(join(src, 'subdir', 'file.txt'), pastDate, pastDate);
utimesSync(join(src, 'subdir'), pastDate, pastDate);
utimesSync(src, pastDate, pastDate);

// Copy with preserveTimestamps and a filter (to exercise the JS fallback path).
const dest = nextdir();
cpSync(src, dest, {
recursive: true,
preserveTimestamps: true,
filter: () => true,
});

// Verify file timestamps are preserved (existing behaviour).
const srcFileStat = statSync(join(src, 'subdir', 'file.txt'));
const destFileStat = statSync(join(dest, 'subdir', 'file.txt'));
assert.strictEqual(srcFileStat.mtime.getTime(), destFileStat.mtime.getTime());

// Verify directory timestamps are preserved (the bug fix).
const srcDirStat = statSync(join(src, 'subdir'));
const destDirStat = statSync(join(dest, 'subdir'));
assert.strictEqual(srcDirStat.mtime.getTime(), destDirStat.mtime.getTime());

const srcRootStat = statSync(src);
const destRootStat = statSync(dest);
assert.strictEqual(srcRootStat.mtime.getTime(), destRootStat.mtime.getTime());