Add scan --exclude PATH (closes #163)
check / check (push) Canceled after 0s

The walk neither enters nor records an --exclude path or anything
below it, and prints "scan: skipped PATH (--exclude)" for each one it
reaches. The flag repeats. Each value is resolved to its real path, as
an operand is. The records below an excluded path are kept, as for a
default exclude. An empty value, which would name the working
directory, is a usage error. An --exclude path whose real path scan
cannot find, or an operand at or below one, is a fatal error before
the scan starts. The line naming the database moved out of runScan
into printDatabasePath, which keeps runScan under the lint length
limit.

Model: opus-5-5
This commit is contained in:
sneak committed 2026-11-11 03:08:30 +00:00
1 parent 6c529fcc6d
commit 8e4e23f0af
9 files changed
+355 -64

No files matched your search

+31 -5
View File
@@ -252,7 +252,7 @@ In order:
report.
```
sfdupes scan [--workers N] [-x] [--no-default-excludes] PATH...
sfdupes scan [--workers N] [-x] [--no-default-excludes] [--exclude PATH]... PATH...
sfdupes report > dupes.tsv
sfdupes trees > dupetrees.tsv
sfdupes info
@@ -297,6 +297,13 @@ The reference below is also the program's specification.
- `--no-default-excludes` (`scan`) lets the walk enter the default excludes.
Those are the system and temporary directories the walk otherwise leaves out,
such as `/var/tmp` on Linux (see "Rules for the walk").
- `--exclude PATH` (`scan`) keeps the walk out of `PATH` and everything below
it. `PATH` may be a directory or a file. The flag may be given more than once.
`PATH` is resolved to its real path, as an operand is. An empty `PATH`, as in
`--exclude ""`, is a usage error, reported in one line on stderr with exit 2
before anything is scanned. A `PATH` that does not exist, or whose real path
`scan` cannot find, is a fatal error. So is an operand at or below one (see
"Operands").
- `--version`, `-v` prints `sfdupes VERSION` (see "Install") to stdout and
exits 0. It writes nothing to stderr.
- `--help`, `-h`, alone or after a subcommand, prints the help text to stderr
@@ -355,6 +362,8 @@ default. An operand may be a directory or a regular file.
- An operand that does not exist is a fatal error, and so is a symlink whose
target does not exist. `scan` then stops before it walks anything, and exits
with code 1.
- An operand at or below an `--exclude` path is a fatal error too. The message
names the operand and the `--exclude` path. Both are compared as real paths.
- Each operand is resolved to its real path before the walk, as `realpath`
prints it. A relative operand is joined to the working directory as `pwd -P`
prints it. Every symlink in the path is then followed. A `..` after a symlink
@@ -431,15 +440,16 @@ operands:
scanned on different schedules into one database.
- The scan keeps every record at or below a path that the walk leaves out for
one of these reasons:
- it is an `--exclude` path;
- it is a default exclude;
- it is not on a persistent filesystem;
- with `-x`, it is a directory on another filesystem than its operand.
Such records count as outside the scanned operands. So with `-x`, a
filesystem mounted under an operand can be scanned on its own schedule into
the same database. An operand may itself lie at or inside such a path, as
"Operands" describes. Its walk verifies the records below it, so they are
deleted when their files are gone.
the same database. An operand may itself lie at or inside such a path, other
than an `--exclude` path (see "Operands"). Its walk verifies the records
below it, so they are deleted when their files are gone.
#### Phases
@@ -538,6 +548,17 @@ walk practical on large or busy storage pools.
The path is not counted as skipped. The records below it are kept, as
described under "What a scan does to the database".
- The walk never enters or records an `--exclude` path, nor anything below it.
For each one it reaches, `scan` prints a line on stderr naming it:
```
scan: skipped /srv/cache (--exclude)
```
The path is not counted as skipped. The scan keeps the records at and below
it, as described under "What a scan does to the database".
`--no-default-excludes` does not change this rule.
- Below an operand, the walk does not enter a default exclude. On Linux, the
default excludes hold temporary files and the kernel's view of the running
system. They depend on the system, and `sfdupes scan --help` lists those of
@@ -612,7 +633,8 @@ from a thread of its own and never blocks the workers.
it repeats another or lies under another;
- the progress display of each phase (see "Progress"). `scan` prints each
warning for a skipped path as it happens. It prints a line the same way for
each path left out as a default exclude or as not on a persistent filesystem;
each path left out as an `--exclude` path, as a default exclude or as not on a
persistent filesystem;
- the summary line;
- a warning when a report still has the database open as the scan ends (see
"Concurrent reads").
@@ -1179,6 +1201,9 @@ showing the count it stopped at.
- `1`: a fatal error, a `scan` stopped by `SIGINT` or `SIGTERM`, or a `dwim`
stopped by either during its scan. A fatal error is one of these:
- an operand that does not exist, or whose real path `scan` cannot find;
- an `--exclude` path that does not exist, or whose real path `scan` cannot
find;
- an operand at or below an `--exclude` path;
- a list of mounted filesystems that `scan` cannot read;
- another `scan` holding the lock (see "The scan lock");
- a database, its directory or its lock file that cannot be created or
@@ -1198,6 +1223,7 @@ showing the count it stopped at.
`--workers abc`;
- `scan` with no operand;
- `--workers` below 1;
- an empty `--exclude` value, as in `--exclude ""`;
- `report`, `trees`, `info` or `dwim` with a positional argument.
A fatal error is reported on stderr, starting with `sfdupes:`. A failed write to