Summary
Numbered-copy detection strips only the final extension, so it fails completely on multi-part extensions like .tar.gz. The copy scores identically to the original, and which one survives becomes a coin flip.
Reproduction
Measured calculateFilePriority:
| filename |
priority |
expected |
archive.tar.gz |
0 |
0 |
archive (1).tar.gz |
0 |
~1001 |
Compare with the single-extension case, which works:
| invoice.pdf | 0 |
| invoice (1).pdf | 1001 |
Root cause
main.go:28:
nameWithoutExt := strings.TrimSuffix(name, filepath.Ext(name))
filepath.Ext("archive (1).tar.gz") returns .gz, leaving archive (1).tar. The numbered patterns at main.go:32-34 are all $-anchored, so the trailing .tar prevents any match.
Affects .tar.gz, .tar.bz2, .tar.xz, .tar.zst, and any other compound suffix.
Consequence
archive.tar.gz and archive (1).tar.gz land in the same duplicate group with equal priority, which is exactly the ambiguous-tie situation described in #7 — except here the tool had the evidence to decide correctly and threw it away.
Related edge case
filepath.Ext(".bashrc") returns .bashrc (the leading dot is the final dot), so nameWithoutExt becomes the empty string for any dotfile without a second extension. Scoring currently returns 0 for those by luck rather than by design; worth pinning down with a test either way.
Expected behaviour
Detect the copy marker without depending on correct extension splitting — match \((\d+)\) immediately before the first extension separator, or strip known compound extensions before scoring.
Test to write first
archive (1).tar.gz scores as a numbered copy.
archive.tar.gz scores 0.
- The
invoice.pdf / invoice (1).pdf pair keeps working (regression guard).
.bashrc and .gitignore score 0 and do not panic.
Summary
Numbered-copy detection strips only the final extension, so it fails completely on multi-part extensions like
.tar.gz. The copy scores identically to the original, and which one survives becomes a coin flip.Reproduction
Measured
calculateFilePriority:archive.tar.gzarchive (1).tar.gzCompare with the single-extension case, which works:
|
invoice.pdf| 0 ||
invoice (1).pdf| 1001 |Root cause
main.go:28:filepath.Ext("archive (1).tar.gz")returns.gz, leavingarchive (1).tar. The numbered patterns atmain.go:32-34are all$-anchored, so the trailing.tarprevents any match.Affects
.tar.gz,.tar.bz2,.tar.xz,.tar.zst, and any other compound suffix.Consequence
archive.tar.gzandarchive (1).tar.gzland in the same duplicate group with equal priority, which is exactly the ambiguous-tie situation described in #7 — except here the tool had the evidence to decide correctly and threw it away.Related edge case
filepath.Ext(".bashrc")returns.bashrc(the leading dot is the final dot), sonameWithoutExtbecomes the empty string for any dotfile without a second extension. Scoring currently returns 0 for those by luck rather than by design; worth pinning down with a test either way.Expected behaviour
Detect the copy marker without depending on correct extension splitting — match
\((\d+)\)immediately before the first extension separator, or strip known compound extensions before scoring.Test to write first
archive (1).tar.gzscores as a numbered copy.archive.tar.gzscores 0.invoice.pdf/invoice (1).pdfpair keeps working (regression guard)..bashrcand.gitignorescore 0 and do not panic.