1 Commits
Author SHA1 Message Date
sneak e726c18ac7 Add sfdupes dwim: scan / then print duplicate trees (closes #154)
check / check (push) Waiting to run
dwim runs what "sfdupes scan /" runs with no flags, then what
"sfdupes trees" runs, and ends with a stderr line saying that
"sfdupes trees" prints the report again. An interrupt or fatal scan
error ends it before the report, as scan ends. It catches signals only
during the scan, so a signal during the report ends it at once, as it
ends trees.

Model: opus-5-5
2026-11-06 19:21:15 +00:00
4 changed files with 21 additions and 15 deletions
+7 -4
View File
@@ -256,7 +256,7 @@ sfdupes scan [--workers N] [-x] [--no-default-excludes] PATH...
sfdupes report > dupes.tsv
sfdupes trees > dupetrees.tsv
sfdupes info
sfdupes dwim > dupetrees.tsv
sfdupes dwim
sfdupes --version
sfdupes [command] --help
```
@@ -828,7 +828,8 @@ even after the terminal is gone.
`SIGINT` or `SIGTERM` stops the scan as it stops `scan` (see "Stopping a scan").
`dwim` then prints no report and exits 1. A fatal scan error also ends it before
the report, with the same message and exit code as `scan`. Warnings for skipped
files do not stop it.
files do not stop it. `dwim` stops catching signals when the scan ends, so a
signal during the report ends it at once, as it ends `trees`.
### `info` mode
@@ -886,6 +887,8 @@ scan: new database /var/lib/sfdupes/db.sqlite (default)
trees: database /srv/tmp/sfdupes/db.db (from SFDUPES_DATABASE)
```
`dwim` prints the `scan` line first, and the `trees` line after the scan.
A relative path is printed with the working directory in front of it. The
working directory is printed as `pwd -P` prints it, with its symlinks resolved.
The rest of the path is printed as set, so a `..` in it stays. The line names
@@ -1169,8 +1172,8 @@ showing the count it stopped at.
### Error handling and exit codes
- `0`: success, even when files were skipped with warnings.
- `1`: a fatal error, or a `scan` or `dwim` stopped by `SIGINT` or `SIGTERM`. A
fatal error is one of these:
- `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;
- a list of mounted filesystems that `scan` cannot read;
- another `scan` holding the lock (see "The scan lock");
+9 -3
View File
@@ -11,10 +11,16 @@ import (
// runDwim implements the dwim subcommand: it runs what "sfdupes scan
// root" runs with no flags, then what "sfdupes trees" runs, and ends
// with a line on stderr saying how to print the report again. The dwim
// command passes "/" as root. An error from the scan, an interrupt
// included, ends it before the report.
// command passes "/" as root. It catches SIGINT and SIGTERM as scan
// does, but only during the scan. An error from the scan, an interrupt
// included, ends it before the report. During the report, a signal
// ends it at once, as it ends trees.
func runDwim(ctx context.Context, root string, stdout io.Writer) error {
err := runScan(ctx, []string{root}, runtime.NumCPU(), false, false)
scanCtx, stop := interruptContext(ctx)
err := runScan(scanCtx, []string{root}, runtime.NumCPU(), false, false)
stop()
if err != nil {
return err
}
+4 -4
View File
@@ -16,12 +16,12 @@ func TestRunDwimPrintsLargestTreesFirst(t *testing.T) {
root := realTempDir(t)
small := []string{
filepath.Dir(writeFile(t, root, "small1/f.bin", pattern(1, 300))),
filepath.Dir(writeFile(t, root, "small2/f.bin", pattern(1, 300))),
filepath.Dir(writeFile(t, root, "a1/f.bin", pattern(1, 300))),
filepath.Dir(writeFile(t, root, "a2/f.bin", pattern(1, 300))),
}
large := []string{
filepath.Dir(writeFile(t, root, "large1/f.bin", pattern(2, 5000))),
filepath.Dir(writeFile(t, root, "large2/f.bin", pattern(2, 5000))),
filepath.Dir(writeFile(t, root, "b1/f.bin", pattern(2, 5000))),
filepath.Dir(writeFile(t, root, "b2/f.bin", pattern(2, 5000))),
}
var stdout bytes.Buffer
+1 -4
View File
@@ -14,7 +14,7 @@
// sfdupes report > dupes.tsv
// sfdupes trees > dupetrees.tsv
// sfdupes info
// sfdupes dwim > dupetrees.tsv
// sfdupes dwim
// sfdupes --version
//
// See README.md for the complete specification.
@@ -208,9 +208,6 @@ func newRootCommand(stdout, stderr io.Writer) *cobra.Command {
Short: "Scan / with the default excludes, then print the duplicate-tree report",
Args: cobra.NoArgs,
RunE: runE(func(ctx context.Context, _ []string) error {
ctx, stop := interruptContext(ctx)
defer stop()
return runDwim(ctx, "/", stdout)
}),
}