diff --git a/files_dedupe.go b/files_dedupe.go index 73e0b83..bd3a3bf 100644 --- a/files_dedupe.go +++ b/files_dedupe.go @@ -154,6 +154,9 @@ func findWithinFolderDuplicates(indexed []ProjectFolderFile) []DedupGroup { byKey[k] = append(byKey[k], it.File) } for k, group := range byKey { + if k == "" { + continue // dotfiles etc. have no stem: never treat as duplicates + } if len(group) < 2 { continue } @@ -178,6 +181,9 @@ func findCrossFolderDuplicates(indexed []ProjectFolderFile) []DedupGroup { } var out []DedupGroup for k, items := range byKey { + if k == "" { + continue // dotfiles etc. have no stem: never treat as duplicates + } if len(items) < 2 { continue } diff --git a/files_dedupe_test.go b/files_dedupe_test.go index 6d24a85..f75d2a3 100644 --- a/files_dedupe_test.go +++ b/files_dedupe_test.go @@ -84,6 +84,32 @@ func TestMergeProjectRootForDedupe(t *testing.T) { } } +func TestFindDuplicatesSkipsEmptyKey(t *testing.T) { + // Dotfiles (".env", ".gitignore", ".npmrc") normalize to an empty stem, so + // FileDedupKey is "". They are not duplicates of each other and must never + // form a dedup group that would delete one of them. + env := &FileEntry{ID: jsonNum("1"), Title: strPtr(".env")} + gitignore := &FileEntry{ID: jsonNum("2"), Title: strPtr(".gitignore")} + npmrc := &FileEntry{ID: jsonNum("3"), Title: strPtr(".npmrc")} + if FileDedupKey(env) != "" || FileDedupKey(gitignore) != "" || FileDedupKey(npmrc) != "" { + t.Fatalf("dotfiles should have empty dedup key") + } + within := []ProjectFolderFile{ + {FolderID: "500", FolderTitle: "Cfg", File: env}, + {FolderID: "500", FolderTitle: "Cfg", File: gitignore}, + } + if groups := findWithinFolderDuplicates(within); len(groups) != 0 { + t.Fatalf("within-folder empty-key groups = %d, want 0 (%+v)", len(groups), groups) + } + cross := []ProjectFolderFile{ + {FolderID: "500", FolderTitle: "Cfg", File: env}, + {FolderID: "501", FolderTitle: "Other", File: npmrc}, + } + if groups := findCrossFolderDuplicates(cross); len(groups) != 0 { + t.Fatalf("cross-folder empty-key groups = %d, want 0 (%+v)", len(groups), groups) + } +} + func TestIsTrashFolderTitle(t *testing.T) { if !IsTrashFolderTitle("_trash-md") { t.Fatal("expected trash")