diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-09-13 02:31:32 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-09-13 02:31:32 +0200 |
| commit | 26c94eb3db62ec6eebbf8d22c11afe691d9520c4 (patch) | |
| tree | 165e5bf69234b4f96c9b74deb4898d7143ddf120 /internal/dup/dup.go | |
| parent | a6e442a645902011b2081c216daaec052cdc6ce6 (diff) | |
| download | krino-306b2cdd83d429b6c1a94f828f4ad2ab57bcc6ee.tar.gz krino-306b2cdd83d429b6c1a94f828f4ad2ab57bcc6ee.zip | |
krino: release 0.0.1 — man pages, install, examples, cross and release, README, changelogv0.0.1
Also: undo removes the directories its run created; a hardlink is never a
duplicate of its own other name; a flag written before "undo" is honoured;
--version prints no leading v. Duplicate conditions with different scopes
not sharing an original is documented as a known limitation.
Diffstat (limited to 'internal/dup/dup.go')
| -rw-r--r-- | internal/dup/dup.go | 50 |
1 files changed, 40 insertions, 10 deletions
diff --git a/internal/dup/dup.go b/internal/dup/dup.go index e3c7424..32bcfb0 100644 --- a/internal/dup/dup.go +++ b/internal/dup/dup.go @@ -174,18 +174,47 @@ func (x *Index) Lookup(path string) (original string, dup bool, err error) { return path, false, nil } orig := x.candidates[x.original(identical)].path - return orig, orig != path, nil + if orig == path { + return orig, false, nil + } + // Spec §5.5: two names for one file are never duplicates of each other. + // The other name may be a hardlink, or path itself indexed a second time + // under a DIR that overlaps the scanned tree. identicalTo gives every + // member of the content class the same set, so every lookup elects the + // same original, and no name for that original's file is reported as a + // duplicate: its content always keeps at least one name. Portable: + // os.SameFile, never a Stat_t.Dev/Ino read (that field's type differs + // across freebsd/openbsd, which `make ci` vets). + origInfo, err := os.Lstat(orig) + if err != nil { + return "", false, err + } + pathInfo, err := os.Lstat(path) + if err != nil { + return "", false, err + } + if os.SameFile(origInfo, pathInfo) { + return orig, false, nil + } + return orig, true, nil } // identicalTo returns the indexes in group (which all share idx's size, // idx included) whose content matches candidates[idx]: same partial hash, -// then, only for those that collide, the same full hash. idx is the file -// Lookup was asked about; a failure hashing it propagates, since Lookup can -// answer nothing without it. A failure hashing any other candidate in group -// only removes that candidate from consideration: a vanished candidate -// (errors.Is fs.ErrNotExist) is dropped silently, any other failure is -// recorded on the Index (see recordCandidateError) so the caller can warn -// about it once matching is done. +// then, only for those that collide, the same full hash. Every candidate +// with identical bytes is included, whatever its path or inode: a hardlink +// of idx, and idx's own path indexed a second time under an overlapping +// extra directory, are both members. That keeps the set the same whichever +// member Lookup was asked about, so every member elects the same original; +// Lookup, not this function, decides that a name for the elected original's +// own file is not a duplicate of it. +// +// idx is the file Lookup was asked about; a failure hashing it propagates, +// since Lookup can answer nothing without it. A failure hashing any other +// candidate in group only removes that candidate from consideration: a +// vanished candidate (errors.Is fs.ErrNotExist) is dropped silently, any +// other failure is recorded on the Index (see recordCandidateError) so the +// caller can warn about it once matching is done. func (x *Index) identicalTo(idx int, group []int) ([]int, error) { idxPartial, err := x.partialHash(x.candidates[idx].path) if err != nil { @@ -219,9 +248,10 @@ func (x *Index) identicalTo(idx int, group []int) ([]int, error) { x.recordCandidateError(x.candidates[j].path, err) continue } - if jFull == idxFull { - same = append(same, j) + if jFull != idxFull { + continue } + same = append(same, j) } return same, nil } |
