Skip to content

Hash only files whose size another file shares [patch] - #180

Merged
matt-edmondson merged 2 commits into
mainfrom
fix/147-hash-only-shared-sizes
Oct 8, 2026
Merged

matt-edmondson merged 2 commits into
mainfrom
fix/147-hash-only-shared-sizes

Conversation

@matt-edmondson

@matt-edmondson matt-edmondson commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #147

Problem

Every verb read and SHA-256-hashed every file it scanned, but two files of different lengths can never be duplicates. In a tree where most sizes are unique, such as a media library on a cold disk, USB drive or network share, most of that reading was wasted.

Change

  • New FileHasher.HashPossibleDuplicates groups the scanned files by size and hashes only those whose size another file shares. It also returns the files it skipped because their size was unique.
  • New DuplicateScan.Run handles the discover, hash and group steps that Scan, DryRun and Deduplicate each had a copy of, with Stats holding a near copy. It prints the same progress lines, and all four verbs now get their data from it. This is the one helper the issue asks for. It also removes the duplicated block SonarCloud flagged.
  • Empty files still group with each other, as EmptyFilesAreDuplicatesOfEachOther requires. A file whose size can't be read is still hashed, so it is reported as unreadable as before.
  • Stats counts the skipped files as unique and includes them in the total size. Before this change it took "Unique files" from hashGroups.Count.
  • Deduplicator.TryGetSize is now internal so the size check can share it.
  • The optional partial-hash second filter from the issue is not included.

Tests

  • HashPossibleDuplicatesSkipsFilesWithAUniqueSize: files that all have different sizes produce no hashes.
  • HashPossibleDuplicatesHashesEveryFileThatSharesASize: files of the same size but different content, and empty files, are hashed. A file with its own size is not.
  • ScanHashesNothingWhenEveryFileHasItsOwnSize: Scan prints no Hashed: lines for such a tree.
  • StatsCountsFilesWithAUniqueSizeAsUnique: Stats reports the right totals without hashing.
  • AHashingErrorNamesTheFullPath changed: the locked file now has the same length as its sibling. Otherwise it would never be read, and the error the test checks would not occur.

All four new tests fail when HashPossibleDuplicates is temporarily made to hash every file, and pass with the filter on. The full suite passes locally on Linux: 94 passed, 3 skipped. The skipped tests need a non-root user. dotnet format --verify-no-changes reports nothing to change.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FtvW1bPVVfCTF7fCWpeyRE

claude added 2 commits October 7, 2026 19:25
Two files of different lengths can never be duplicates, but every verb
read and SHA-256-hashed every scanned file in full. A new
FileHasher.HashPossibleDuplicates groups the scan by size first and hashes
only the files whose size is shared, so a file with a unique size is never
read. Scan, DryRun, Deduplicate and Stats all go through it. Empty files
still group with each other, and a file that cannot be sized is still
hashed so it is reported as unreadable as before.

Stats counts the files skipped for a unique size as unique and includes
them in the total size.

Fixes #147

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtvW1bPVVfCTF7fCWpeyRE
Scan, DryRun and Deduplicate each carried the same discover, hash and
group block, and Stats a near copy of it. The size pre-filter changed a
line in every copy, which SonarCloud then counted as new duplicated code.
DuplicateScan.Run now does those steps once, printing the same progress
lines, and all four verbs read the files, hashes, unique-size files and
groups from it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtvW1bPVVfCTF7fCWpeyRE
@sonarqubecloud

sonarqubecloud Bot commented Oct 7, 2026

Copy link
Copy Markdown

@matt-edmondson
matt-edmondson merged commit 11b903c into main Oct 8, 2026
14 checks passed
@matt-edmondson
matt-edmondson deleted the fix/147-hash-only-shared-sizes branch October 8, 2026 04:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Every verb reads and SHA-256-hashes every file in full, even files whose size no other file shares

2 participants