feat: store duplicated file contents only once when packing - #465
Merged
Conversation
Files whose contents hash identically are now written into the archive a single time. Each copy still gets its own header entry with its own size, integrity, and executable bit, but the entries share one offset, so the redundant bytes are never written. Detection reuses the SHA-256 already computed for each entry's integrity field, so there is no extra read or hashing pass. Unpacked files are excluded: they are copied out as standalone files next to the archive, so every copy has to exist on disk. Readers are unaffected — nothing in the format required offsets to be unique — and archives without duplicates are byte-for-byte unchanged. On a 2000-file tree the archive shrinks in proportion to how much of the input is duplicated (-9.4% at a 10% duplicate share, -47.1% at 50%, -84.8% at 90%), while pack time is unchanged within measurement noise both with and without duplicates.
Archive lookups split on path.sep, so a forward-slashed path more than one level deep never splits into segments on Windows and the entry is not found. Build those paths with path.join instead. No-Verification-Needed: test-only change, no runtime surface
VerteDinde
approved these changes
Aug 18, 2026
|
🎉 This PR is included in version 4.3.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Files whose contents hash identically are now written into the archive a single time. Each copy still gets its own header entry with its own
size,integrity, andexecutablebit — only theoffsetis shared, so the redundant bytes are never written.How
Filesystem.storeFileEntrykeeps aMap<sha256, offset>keyed on the hash that is already computed for every entry'sintegrityfield, so detection costs no extra read and no extra hashing pass. On a hit the entry takes the stored offset and the archive does not grow;insertFilereports the duplicate, and that rides alongside the existingunpackflag through the file lists so both writers skip the payload. Small files also release their cached buffer immediately instead of holding it until the write pass, and thetransformpath deletes a duplicate's temp file instead of leaving it behind.Unpacked files (
unpack/unpackDir) are excluded — they are copied out as standalone files next to the archive, so every copy has to exist on disk.Compatibility
Nothing in the format required offsets to be unique; readers consume
offset+sizeper entry either way. Archives whose inputs contain no duplicates are byte-for-byte unchanged, and packing stays deterministic. An archive packed by this branch extracts correctly with the CLI built frommain.No new option or flag: it applies to every pack.
Benchmarks
2000 files × 4 KB, varying how much of the input is duplicated (before =
main, after = this branch):Savings track the duplicated share of bytes, which is the ceiling. A hoisted
node_modules(3910 files / 181 MB) only shrinks 0.4%, since the package manager already deduplicates; nested installs and repeated assets are where the win lands.Pack time, interleaved A/B with a before-vs-before control run each iteration to establish the noise floor:
Every delta sits inside the noise floor. On the worst case for this change — zero duplicates, pure map overhead — three repeat runs gave +3.5%, -2.6%, -1.9% against a ±2.7% floor, so the overhead is not measurable. Pack time does not improve even at 50% duplication: every file still has to be hashed to know it is a duplicate, and skipped writes land in the page cache anyway. The win is bytes on disk and the reads of them, not wall clock.
benchmark/generate-fixtures.tsgains aduplicateRatioknob and aduplicate-heavyfixture so this is reproducible.Testing
10 new tests: shared offsets, files larger than the 2 MB buffer-hash threshold (streaming path), same-size-different-content, executable-bit divergence between copies, unpacked copies still all written, dedup computed on transformed bytes, the streams API, and empty files.
Also driven by hand through the CLI: pack/extract/
diff -rround trips with--unpack,--unpack-dir,--ordering, symlinks pointing at deduped files, and 3 × 3 MB identical files (9 MB in → 3146492 bytes out).Generated by Claude Code