Compare commits

..
Author SHA1 Message Date
sneak f9f309e94a Hash a file again when its mtime moved back, not only forward (closes #176)
check / check (push) Successful in 3m46s
scan treated a recorded file as unchanged when its size matched and its
mtime was not newer. A file replaced by other content of the same size
with an older mtime, as cp -p, rsync -a or a restore writes it, kept the
hash of the file it replaced. Any mtime difference now counts as a
change. README.md says so, and a new test covers the older mtime.

Model: opus-5-5
2026-11-11 02:54:08 +00:00
4 changed files with 8 additions and 50 deletions

No files matched your search

+4 -9
View File
@@ -112,20 +112,15 @@ docker run --rm -v /var/lib/sfdupes:/var/lib/sfdupes sfdupes report > dupes.tsv
Run `scan` as root, so that it can read every file. `scan` warns about a
directory it cannot read, and about a new or changed file it cannot read. It
skips the path and deletes the records at or below it (see "Rules for the
walk").
An unmounted disk leaves its mount point empty, so `scan` deletes every record
under it, and the next scan reads every file on the disk again. For each other
disk mounted below `/srv`, add `&& mountpoint -q` and its mount point after the
first test. As the file `/etc/cron.d/sfdupes`:
walk"). As the file `/etc/cron.d/sfdupes`:
```
30 3 * * * root if mountpoint -q /srv; then /usr/local/bin/sfdupes scan /srv 2>>/var/log/sfdupes.log || tail -n 3 /var/log/sfdupes.log; else echo "a disk is not mounted; scan of /srv skipped"; fi
30 3 * * * root /usr/local/bin/sfdupes scan /srv 2>>/var/log/sfdupes.log || tail -n 3 /var/log/sfdupes.log
```
- The first scan creates `/var/lib/sfdupes/db.sqlite` and its directory. To keep
the database elsewhere, put `SFDUPES_DATABASE=/path/to/db.sqlite` directly
before `/usr/local/bin/sfdupes`.
the database elsewhere, put `SFDUPES_DATABASE=/path/to/db.sqlite` before the
command on the same line.
- `scan` writes nothing to stdout. Its stderr, appended here to
`/var/log/sfdupes.log`, holds:
- one line naming the database;
-6
View File
@@ -29,12 +29,6 @@
Newest first. An entry ends with its date and its issue or branch, where those
are known.
- `README.md` says what a disk that is not mounted does to the database. The
daily `cron` line runs `scan` only when `mountpoint` reports `/srv` as mounted
(2026-11-11, https://git.eeqj.de/sneak/sfdupes/issues/175)
- The usage line of `scan` no longer ends in `[flags]`. It is the line
`README.md` shows under "Subcommands" (2026-11-11,
https://git.eeqj.de/sneak/sfdupes/issues/169)
- `report` and `trees` leave out empty files. `report` writes no group of size
0, and `trees` writes no group whose trees hold 0 bytes. An empty file still
counts toward a tree's digest (2026-11-11,
+4 -7
View File
@@ -215,13 +215,10 @@ func newScanCommand() *cobra.Command {
short := "Walk trees and synchronize the scan database"
scanCmd := &cobra.Command{
Use: cmdScan + " [--workers N] [-x] [--no-default-excludes] PATH...",
// The Use line already names every flag, so cobra must not
// append its generic "[flags]" to it.
DisableFlagsInUseLine: true,
Short: short,
Long: short + ".\n\n" + defaultExcludesHelp(runtime.GOOS),
Args: cobra.MinimumNArgs(1),
Use: cmdScan + " [--workers N] [-x] [--no-default-excludes] PATH...",
Short: short,
Long: short + ".\n\n" + defaultExcludesHelp(runtime.GOOS),
Args: cobra.MinimumNArgs(1),
PreRunE: func(cmd *cobra.Command, _ []string) error {
return checkScanWorkers(cmd, scanWorkers)
},
-28
View File
@@ -714,34 +714,6 @@ func TestRunHelp(t *testing.T) {
}
}
func TestRunScanUsageLine(t *testing.T) {
t.Parallel()
// README §Subcommands shows this usage line for scan, and the help
// and the usage error both print it.
const want = "\n sfdupes scan [--workers N] [-x] [--no-default-excludes] PATH...\n"
cases := map[string][]string{
"help": {cmdScan, "-h"},
"missing paths": {cmdScan},
}
for name, args := range cases {
t.Run(name, func(t *testing.T) {
t.Parallel()
var stdout, stderr bytes.Buffer
run(args, &stdout, &stderr)
if !strings.Contains(stderr.String(), want) {
t.Errorf("run(%v) stderr = %q, want the usage line %q",
args, stderr.String(), want)
}
})
}
}
func TestRunVersion(t *testing.T) {
t.Parallel()