diff --git a/pkg/util/path.go b/pkg/util/path.go index a6a78a9d2..9855118e4 100644 --- a/pkg/util/path.go +++ b/pkg/util/path.go @@ -50,12 +50,17 @@ func CheckDuplicateAndRename(path string) (string, error) { return "", err } - index := strings.LastIndex(name, ".") + ext := syspath.Ext(name) var nameTpl string - if index == -1 { + // Special case: if the extension is the entire filename (like .gitignore), + // or if index of last dot is 0 (starts with dot), treat it as no extension + if ext == "" || ext == name || (len(ext) > 0 && strings.LastIndex(name, ".") == 0) { + // No extension or hidden file without extension nameTpl = name + " (%d)" } else { - nameTpl = name[:index] + " (%d)" + name[index:] + // Has extension + nameWithoutExt := name[:len(name)-len(ext)] + nameTpl = nameWithoutExt + " (%d)" + ext } for i := 1; ; i++ { newName := fmt.Sprintf(nameTpl, i) diff --git a/pkg/util/path_test.go b/pkg/util/path_test.go index 13d558dff..3f5cf967b 100644 --- a/pkg/util/path_test.go +++ b/pkg/util/path_test.go @@ -106,13 +106,28 @@ func TestSafeRemove(t *testing.T) { } func TestCheckDuplicateAndRename(t *testing.T) { + // Test with extension doCheckDuplicateAndRename(t, []string{}, "a.txt", "a.txt") doCheckDuplicateAndRename(t, []string{"a.txt"}, "a.txt", "a (1).txt") doCheckDuplicateAndRename(t, []string{"a.txt", "a (1).txt"}, "a.txt", "a (2).txt") + // Test without extension doCheckDuplicateAndRename(t, []string{}, "a", "a") doCheckDuplicateAndRename(t, []string{"a"}, "a", "a (1)") doCheckDuplicateAndRename(t, []string{"a", "a (1)"}, "a", "a (2)") + + // Test hidden files (starting with dot) + doCheckDuplicateAndRename(t, []string{}, ".gitignore", ".gitignore") + doCheckDuplicateAndRename(t, []string{".gitignore"}, ".gitignore", ".gitignore (1)") + doCheckDuplicateAndRename(t, []string{".gitignore", ".gitignore (1)"}, ".gitignore", ".gitignore (2)") + + // Test hidden files with extension + doCheckDuplicateAndRename(t, []string{}, ".config.json", ".config.json") + doCheckDuplicateAndRename(t, []string{".config.json"}, ".config.json", ".config (1).json") + + // Test multiple dots + doCheckDuplicateAndRename(t, []string{}, "test.tar.gz", "test.tar.gz") + doCheckDuplicateAndRename(t, []string{"test.tar.gz"}, "test.tar.gz", "test.tar (1).gz") } func doCheckDuplicateAndRename(t *testing.T, exitsPaths []string, path string, except string) {