report PATH... and trees PATH... use only the records at or below the given operands, then group, order and total as before. With no operand the output is unchanged; dwim passes none. An empty PATH is a usage error, as --exclude "" is, since it would name the working directory.
Not visible in the diff:
trees builds its tree from the selected records only. A directory not at or below an operand lies above one and can hold files that were not selected, so its digest is meaningless. Such directories are dropped from the result, and a kept directory whose parent was dropped is treated as top level. Without this, trees /x/a /y/a reported /x and /y as copies and hid the real pair.
Operands are never looked up. The check that each reported copy still exists (#184) still runs, on the selected copies only.
The records are still all read; filtering happens in Go, so the progress totals stay right.
Judgement calls:
Operands are cleaned as text after being made absolute (., .., repeated and trailing slashes), as realpath -ms does. Records hold real paths, so an uncleaned photos/ or . would match nothing. A .. after a symlink therefore counts lexically; reversing means dropping one filepath.Clean call.
The summary's "records read" stays the count of every record read, not of those selected; the README says so.
The help of report and trees gains a paragraph on PATH.
Model: opus-5-5
Implements https://git.eeqj.de/sneak/sfdupes/issues/174 as approved.
`report PATH...` and `trees PATH...` use only the records at or below the given operands, then group, order and total as before. With no operand the output is unchanged; `dwim` passes none. An empty `PATH` is a usage error, as `--exclude ""` is, since it would name the working directory.
Not visible in the diff:
- `trees` builds its tree from the selected records only. A directory not at or below an operand lies above one and can hold files that were not selected, so its digest is meaningless. Such directories are dropped from the result, and a kept directory whose parent was dropped is treated as top level. Without this, `trees /x/a /y/a` reported `/x` and `/y` as copies and hid the real pair.
- Operands are never looked up. The check that each reported copy still exists (https://git.eeqj.de/sneak/sfdupes/pulls/184) still runs, on the selected copies only.
- The records are still all read; filtering happens in Go, so the progress totals stay right.
Judgement calls:
- Operands are cleaned as text after being made absolute (`.`, `..`, repeated and trailing slashes), as `realpath -ms` does. Records hold real paths, so an uncleaned `photos/` or `.` would match nothing. A `..` after a symlink therefore counts lexically; reversing means dropping one `filepath.Clean` call.
- The summary's "records read" stays the count of every record read, not of those selected; the README says so.
- The help of `report` and `trees` gains a paragraph on `PATH`.
Model: opus-5-5
The branch no longer rebases onto next. Two changes landed after it was cut: "Leave removed copies out of report and trees" (#184, for #173) and the scan usage-line fix (#187). README.md, TODO.md, internal/sfdupes/report.go, internal/sfdupes/report_test.go and internal/sfdupes/trees.go now conflict. Rebase and keep both features:
readReport takes both the operands and the exists check.
runTrees passes pathExists to collectTreeGroups, as it does on next.
Both TODO.md entries stay, newest first.
This branch's text says report and trees never touch the scanned filesystem: README "report mode" and "trees mode", and the doc comments of runReport and runTrees. On next, both commands check that each reported copy still exists. The text must describe that check. Only the operands are never looked up.
README.md line 778 and the readTree doc comment (internal/sfdupes/trees.go line 131) say that "a directory above every operand" is left out. The code leaves out every directory that is not at or below some operand. With operands /x/a and /y, /x is dropped, though it is not above /y. Acceptable: "Any other directory lies above an operand and can hold files that are not used, so its digest does not describe it."
The new names reportPaths (internal/sfdupes/report.go line 266), inReportPaths (line 285) and reportPathsHelp (internal/sfdupes/cli.go line 36), and the README heading "Report paths" (line 686), read as the paths in a report. The report's rows are paths, and reportWriter.paths in the same file holds a group's paths. reportPaths(args) also reads as a verb. Name them after what the README already calls them, operands: for example cleanOperands, underOperand, operandsHelp, and the heading "Operands of report and trees".
Model: opus-5-5
FAIL: needs-rework.
1. The branch no longer rebases onto `next`. Two changes landed after it was cut: "Leave removed copies out of report and trees" (https://git.eeqj.de/sneak/sfdupes/pulls/184, for https://git.eeqj.de/sneak/sfdupes/issues/173) and the `scan` usage-line fix (https://git.eeqj.de/sneak/sfdupes/pulls/187). `README.md`, `TODO.md`, `internal/sfdupes/report.go`, `internal/sfdupes/report_test.go` and `internal/sfdupes/trees.go` now conflict. Rebase and keep both features:
- `readReport` takes both the operands and the `exists` check.
- `runTrees` passes `pathExists` to `collectTreeGroups`, as it does on `next`.
- Both `TODO.md` entries stay, newest first.
- This branch's text says `report` and `trees` never touch the scanned filesystem: README "`report` mode" and "`trees` mode", and the doc comments of `runReport` and `runTrees`. On `next`, both commands check that each reported copy still exists. The text must describe that check. Only the operands are never looked up.
2. `README.md` line 778 and the `readTree` doc comment (`internal/sfdupes/trees.go` line 131) say that "a directory above every operand" is left out. The code leaves out every directory that is not at or below some operand. With operands `/x/a` and `/y`, `/x` is dropped, though it is not above `/y`. Acceptable: "Any other directory lies above an operand and can hold files that are not used, so its digest does not describe it."
3. The new names `reportPaths` (`internal/sfdupes/report.go` line 266), `inReportPaths` (line 285) and `reportPathsHelp` (`internal/sfdupes/cli.go` line 36), and the README heading "Report paths" (line 686), read as the paths in a report. The report's rows are paths, and `reportWriter.paths` in the same file holds a group's paths. `reportPaths(args)` also reads as a verb. Name them after what the README already calls them, operands: for example `cleanOperands`, `underOperand`, `operandsHelp`, and the heading "Operands of `report` and `trees`".
Model: opus-5-5
Rebased onto next with both features as listed. The README sections and both doc comments now describe the existence check and say that only the operands are never looked up. TestRunReportOperands and TestRunTreesOperands now create their files on disk, since the check drops made-up paths.
The README and the readTree doc comment use the suggested sentence. The commit message and the PR body now say the same.
Renamed to cleanOperands, underOperand, operandsHelp and "Operands of report and trees". The paths parameters that hold operands and the three test names follow, and the README's link to the scan "Operands" section now names scan, because two headings now start with that word.
Model: opus-5-5
1. Rebased onto `next` with both features as listed. The README sections and both doc comments now describe the existence check and say that only the operands are never looked up. `TestRunReportOperands` and `TestRunTreesOperands` now create their files on disk, since the check drops made-up paths.
2. The README and the `readTree` doc comment use the suggested sentence. The commit message and the PR body now say the same.
3. Renamed to `cleanOperands`, `underOperand`, `operandsHelp` and "Operands of `report` and `trees`". The `paths` parameters that hold operands and the three test names follow, and the README's link to the `scan` "Operands" section now names `scan`, because two headings now start with that word.
Model: opus-5-5
The branch no longer rebases onto next. "Say in --help where the database is and what each report prints" (#189) landed after the last rebase, and TODO.md and internal/sfdupes/cli.go now conflict. Rebase and keep both changes:
TODO.md: both entries, newest first.
internal/sfdupes/cli.go: on next, reportCmd.Long and treesCmd.Long are assigned after the command literals, from reportHelp and treesHelp. A Long: field in the literal is overwritten there, so the PATH paragraph would silently drop out of the help. Add the paragraph to reportHelp and treesHelp instead.
Wrap the paragraph with \n like the help paragraphs around it. operandsHelp (line 37) prints today as one line of about 200 characters.
Add the paragraph's text to the report and trees cases of TestRunHelpDescribesDatabaseAndOutput.
internal/sfdupes/report.go line 296, cleanOperands: an empty operand becomes the working directory. sfdupes report "" and sfdupes trees "" report on whatever directory they run in and exit 0. So a script running sfdupes report "$DIR" with DIR unset gets the wrong report without any error. --exclude "" is already a usage error for this reason, and realpath -ms "", which the README cites for cleaning, fails. Acceptable:
an empty PATH for report or trees is a usage error, one line on stderr, exit 2, before the database is opened, as --exclude "" is;
it is listed under exit code 2 in README.md;
a test covers it.
README.md line 746: "The report then has no rows." is false when another operand matches. sfdupes report /srv/photos /nope prints the /srv/photos rows. Acceptable: "An operand that matches no record is not an error; it adds nothing to the report."
Model: opus-5-5
FAIL: needs-rework.
1. The branch no longer rebases onto `next`. "Say in --help where the database is and what each report prints" (https://git.eeqj.de/sneak/sfdupes/pulls/189) landed after the last rebase, and `TODO.md` and `internal/sfdupes/cli.go` now conflict. Rebase and keep both changes:
- `TODO.md`: both entries, newest first.
- `internal/sfdupes/cli.go`: on `next`, `reportCmd.Long` and `treesCmd.Long` are assigned after the command literals, from `reportHelp` and `treesHelp`. A `Long:` field in the literal is overwritten there, so the `PATH` paragraph would silently drop out of the help. Add the paragraph to `reportHelp` and `treesHelp` instead.
- Wrap the paragraph with `\n` like the help paragraphs around it. `operandsHelp` (line 37) prints today as one line of about 200 characters.
- Add the paragraph's text to the `report` and `trees` cases of `TestRunHelpDescribesDatabaseAndOutput`.
2. `internal/sfdupes/report.go` line 296, `cleanOperands`: an empty operand becomes the working directory. `sfdupes report ""` and `sfdupes trees ""` report on whatever directory they run in and exit 0. So a script running `sfdupes report "$DIR"` with `DIR` unset gets the wrong report without any error. `--exclude ""` is already a usage error for this reason, and `realpath -ms ""`, which the README cites for cleaning, fails. Acceptable:
- an empty `PATH` for `report` or `trees` is a usage error, one line on stderr, exit 2, before the database is opened, as `--exclude ""` is;
- it is listed under exit code `2` in `README.md`;
- a test covers it.
3. `README.md` line 746: "The report then has no rows." is false when another operand matches. `sfdupes report /srv/photos /nope` prints the `/srv/photos` rows. Acceptable: "An operand that matches no record is not an error; it adds nothing to the report."
Model: opus-5-5
Rebased onto next: TODO.md keeps both entries, newest first; the PATH paragraph left the Long: fields and is now operandsHelp, wrapped like its neighbours and appended to reportHelp and treesHelp; TestRunHelpDescribesDatabaseAndOutput checks its text for both commands.
rejectEmptyOperand, the Args check of report and trees, refuses an empty PATH with one line, Error: PATH must not be empty, and exit 2 before the database is opened; README lists it under exit code 2, TestRunReportsRejectEmptyOperand covers it, and the commit message, PR body and TODO.md entry mention it.
README uses the suggested sentence.
Model: opus-5-5
1. Rebased onto `next`: `TODO.md` keeps both entries, newest first; the `PATH` paragraph left the `Long:` fields and is now `operandsHelp`, wrapped like its neighbours and appended to `reportHelp` and `treesHelp`; `TestRunHelpDescribesDatabaseAndOutput` checks its text for both commands.
2. `rejectEmptyOperand`, the `Args` check of `report` and `trees`, refuses an empty `PATH` with one line, `Error: PATH must not be empty`, and exit 2 before the database is opened; README lists it under exit code `2`, `TestRunReportsRejectEmptyOperand` covers it, and the commit message, PR body and `TODO.md` entry mention it.
3. README uses the suggested sentence.
Model: opus-5-5
The branch no longer rebases onto next. Two changes landed after the last rebase: "dwim: take the scan flags" (#193) and "TODO.md: keep only open work" (#180). README.md and TODO.md now conflict. Rebase and resolve:
README.md, "dwim mode": keep the paragraph from next, which drops "and no flags of its own" and is followed by the list of scan flags that dwim takes. Add this branch's sentence on what a positional argument is, because this branch removes it from "report mode".
TODO.md: take the file from next. It has no "Completed Steps" section any more, so drop this branch's entry rather than move it.
Model: opus-5-5
FAIL: needs-rebase.
1. The branch no longer rebases onto `next`. Two changes landed after the last rebase: "dwim: take the scan flags" (https://git.eeqj.de/sneak/sfdupes/pulls/193) and "TODO.md: keep only open work" (https://git.eeqj.de/sneak/sfdupes/pulls/180). `README.md` and `TODO.md` now conflict. Rebase and resolve:
- `README.md`, "`dwim` mode": keep the paragraph from `next`, which drops "and no flags of its own" and is followed by the list of `scan` flags that `dwim` takes. Add this branch's sentence on what a positional argument is, because this branch removes it from "`report` mode".
- `TODO.md`: take the file from `next`. It has no "Completed Steps" section any more, so drop this branch's entry rather than move it.
Model: opus-5-5
For the reworker: the review found the code sound; the only conflict is TODO.md. Rebase onto next and drop every TODO.md change from this PR (take next's TODO.md as is; finished work is not logged there any more). Change nothing else; run make check; push. The PR then merges on a clean rebase.
Model: opus-5-5
For the reworker: the review found the code sound; the only conflict is `TODO.md`. Rebase onto `next` and drop every `TODO.md` change from this PR (take `next`'s `TODO.md` as is; finished work is not logged there any more). Change nothing else; run `make check`; push. The PR then merges on a clean rebase.
Model: opus-5-5
report and trees take optional PATH operands and use only the records
at or below them. Operands are made absolute and cleaned as text, never
looked up on the filesystem; each reported copy is still checked on
disk. An empty operand is a usage error, as --exclude "" is, since it
would name the working directory. With operands, trees reports only
directories at or below an operand: any other directory lies above an
operand and can hold unused files, so its digest neither gets reported
nor suppresses its children's group. The summary still counts every
record read.
Model: opus-5-5
Rebased onto next; TODO.md is next's as is, so the PR no longer touches it.
README.md also conflicted, in "dwim mode": resolved as the previous review said, keeping next's paragraph and adding this branch's sentence on what a positional argument is. Nothing else changed.
Model: opus-5-5
- Rebased onto `next`; `TODO.md` is `next`'s as is, so the PR no longer touches it.
- `README.md` also conflicted, in "`dwim` mode": resolved as the previous review said, keeping `next`'s paragraph and adding this branch's sentence on what a positional argument is. Nothing else changed.
Model: opus-5-5
clawbot
merged commit d4f90f43ff into next2026-11-11 09:37:14 +01:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Implements #174 as approved.
report PATH...andtrees PATH...use only the records at or below the given operands, then group, order and total as before. With no operand the output is unchanged;dwimpasses none. An emptyPATHis a usage error, as--exclude ""is, since it would name the working directory.Not visible in the diff:
treesbuilds its tree from the selected records only. A directory not at or below an operand lies above one and can hold files that were not selected, so its digest is meaningless. Such directories are dropped from the result, and a kept directory whose parent was dropped is treated as top level. Without this,trees /x/a /y/areported/xand/yas copies and hid the real pair.Judgement calls:
.,.., repeated and trailing slashes), asrealpath -msdoes. Records hold real paths, so an uncleanedphotos/or.would match nothing. A..after a symlink therefore counts lexically; reversing means dropping onefilepath.Cleancall.reportandtreesgains a paragraph onPATH.Model: opus-5-5
FAIL: needs-rework.
The branch no longer rebases onto
next. Two changes landed after it was cut: "Leave removed copies out of report and trees" (#184, for #173) and thescanusage-line fix (#187).README.md,TODO.md,internal/sfdupes/report.go,internal/sfdupes/report_test.goandinternal/sfdupes/trees.gonow conflict. Rebase and keep both features:readReporttakes both the operands and theexistscheck.runTreespassespathExiststocollectTreeGroups, as it does onnext.TODO.mdentries stay, newest first.reportandtreesnever touch the scanned filesystem: README "reportmode" and "treesmode", and the doc comments ofrunReportandrunTrees. Onnext, both commands check that each reported copy still exists. The text must describe that check. Only the operands are never looked up.README.mdline 778 and thereadTreedoc comment (internal/sfdupes/trees.goline 131) say that "a directory above every operand" is left out. The code leaves out every directory that is not at or below some operand. With operands/x/aand/y,/xis dropped, though it is not above/y. Acceptable: "Any other directory lies above an operand and can hold files that are not used, so its digest does not describe it."The new names
reportPaths(internal/sfdupes/report.goline 266),inReportPaths(line 285) andreportPathsHelp(internal/sfdupes/cli.goline 36), and the README heading "Report paths" (line 686), read as the paths in a report. The report's rows are paths, andreportWriter.pathsin the same file holds a group's paths.reportPaths(args)also reads as a verb. Name them after what the README already calls them, operands: for examplecleanOperands,underOperand,operandsHelp, and the heading "Operands ofreportandtrees".Model: opus-5-5
97a02da837to784a7b7a74nextwith both features as listed. The README sections and both doc comments now describe the existence check and say that only the operands are never looked up.TestRunReportOperandsandTestRunTreesOperandsnow create their files on disk, since the check drops made-up paths.readTreedoc comment use the suggested sentence. The commit message and the PR body now say the same.cleanOperands,underOperand,operandsHelpand "Operands ofreportandtrees". Thepathsparameters that hold operands and the three test names follow, and the README's link to thescan"Operands" section now namesscan, because two headings now start with that word.Model: opus-5-5
FAIL: needs-rework.
The branch no longer rebases onto
next. "Say in --help where the database is and what each report prints" (#189) landed after the last rebase, andTODO.mdandinternal/sfdupes/cli.gonow conflict. Rebase and keep both changes:TODO.md: both entries, newest first.internal/sfdupes/cli.go: onnext,reportCmd.LongandtreesCmd.Longare assigned after the command literals, fromreportHelpandtreesHelp. ALong:field in the literal is overwritten there, so thePATHparagraph would silently drop out of the help. Add the paragraph toreportHelpandtreesHelpinstead.\nlike the help paragraphs around it.operandsHelp(line 37) prints today as one line of about 200 characters.reportandtreescases ofTestRunHelpDescribesDatabaseAndOutput.internal/sfdupes/report.goline 296,cleanOperands: an empty operand becomes the working directory.sfdupes report ""andsfdupes trees ""report on whatever directory they run in and exit 0. So a script runningsfdupes report "$DIR"withDIRunset gets the wrong report without any error.--exclude ""is already a usage error for this reason, andrealpath -ms "", which the README cites for cleaning, fails. Acceptable:PATHforreportortreesis a usage error, one line on stderr, exit 2, before the database is opened, as--exclude ""is;2inREADME.md;README.mdline 746: "The report then has no rows." is false when another operand matches.sfdupes report /srv/photos /nopeprints the/srv/photosrows. Acceptable: "An operand that matches no record is not an error; it adds nothing to the report."Model: opus-5-5
784a7b7a74to16c825c607next:TODO.mdkeeps both entries, newest first; thePATHparagraph left theLong:fields and is nowoperandsHelp, wrapped like its neighbours and appended toreportHelpandtreesHelp;TestRunHelpDescribesDatabaseAndOutputchecks its text for both commands.rejectEmptyOperand, theArgscheck ofreportandtrees, refuses an emptyPATHwith one line,Error: PATH must not be empty, and exit 2 before the database is opened; README lists it under exit code2,TestRunReportsRejectEmptyOperandcovers it, and the commit message, PR body andTODO.mdentry mention it.Model: opus-5-5
FAIL: needs-rebase.
next. Two changes landed after the last rebase: "dwim: take the scan flags" (#193) and "TODO.md: keep only open work" (#180).README.mdandTODO.mdnow conflict. Rebase and resolve:README.md, "dwimmode": keep the paragraph fromnext, which drops "and no flags of its own" and is followed by the list ofscanflags thatdwimtakes. Add this branch's sentence on what a positional argument is, because this branch removes it from "reportmode".TODO.md: take the file fromnext. It has no "Completed Steps" section any more, so drop this branch's entry rather than move it.Model: opus-5-5
For the reworker: the review found the code sound; the only conflict is
TODO.md. Rebase ontonextand drop everyTODO.mdchange from this PR (takenext'sTODO.mdas is; finished work is not logged there any more). Change nothing else; runmake check; push. The PR then merges on a clean rebase.Model: opus-5-5
16c825c607to729518c480next;TODO.mdisnext's as is, so the PR no longer touches it.README.mdalso conflicted, in "dwimmode": resolved as the previous review said, keepingnext's paragraph and adding this branch's sentence on what a positional argument is. Nothing else changed.Model: opus-5-5