diff --git a/README.md b/README.md index 0a22ae5..6508cbe 100644 --- a/README.md +++ b/README.md @@ -339,11 +339,14 @@ during the hash phase: `content` hash, has its file checked with `lstat` first. A file that is gone, is no longer a regular file, or has changed (a different size, or an mtime newer than recorded) keeps its record - as it is and is not a duplicate. Any other `lstat` error is warned - about and counted as skipped, with the same result. The files that - pass and have no `content` hash are read only if at least two of - those records pass, so a file whose only matches are stale costs no - read; a file that already has a `content` hash is never read again. + as it is and does not count as a match for the others. Any other + `lstat` error is warned about and counted as skipped, with the same + result. If such a record has no `content` hash, it stays out of + duplicate groups; if it has one, it is still reported until a scan + covering its own tree updates or removes it. The files that pass + and have no `content` hash are read only if at least two of those + records pass, so a file whose only matches are stale costs no read; + a file that already has a `content` hash is never read again. They are read by a worker pool as in the hash phase, in inode order and once per inode, and their content hashes are committed in batches. A failed read is warned about and counted as skipped; its diff --git a/scan.go b/scan.go index 2fc73aa..6d7aaf4 100644 --- a/scan.go +++ b/scan.go @@ -607,11 +607,14 @@ func (s *scanState) contentPhase(ctx context.Context, workers int) error { // their records by path. Every record contentCandidatesSQL returns has // its file checked with lstat, whether or not it already has a content // hash: a file that is gone, is no longer a regular file, or has -// changed by the walk's rule keeps its record as it is and is not a -// duplicate, and any other lstat error is warned about and counted as -// skipped. The files of a group that pass and have no content hash are -// read only if at least minGroupSize of the group's files pass, so a -// group whose other members are all stale costs no reads. Only the +// changed by the walk's rule keeps its record as it is and does not +// count as a match for the others, and any other lstat error is warned +// about and counted as skipped, with the same result. If such a record +// has no content hash, it stays out of duplicate groups; if it has one, +// it is still reported until a scan covering its own tree updates or +// removes it. The files of a group that pass and have no content hash +// are read only if at least minGroupSize of the group's files pass, so +// a group whose other members are all stale costs no reads. Only the // records to be read are kept. func (s *scanState) contentCandidates( ctx context.Context,