From 15ee2fa0a48580beb6fdf667b28cb4283577838f Mon Sep 17 00:00:00 2001 From: Vyacheslav Pukhanov Date: Fri, 5 Jul 2024 20:31:20 +0300 Subject: [PATCH 1/2] feat: add `remove` command --- cmd/remove.go | 26 +++++++++++++++++++++++ pkg/glee/glee.go | 43 +++++++++++++++++++++++++++++++++++++ pkg/glee/glee_test.go | 49 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+) create mode 100644 cmd/remove.go diff --git a/cmd/remove.go b/cmd/remove.go new file mode 100644 index 0000000..da8b62a --- /dev/null +++ b/cmd/remove.go @@ -0,0 +1,26 @@ +package cmd + +import ( + "github.com/spf13/cobra" + "github.com/vpukhanov/glee/pkg/glee" +) + +var removeCmd = &cobra.Command{ + Use: "remove [files to unignore]", + Short: "Remove entries from git exclude list", + Long: `Remove files from the local exclude list, allowing them to be tracked by git again. + +You can remove multiple files using glob patterns: + glee remove filename*.txt + +To remove a glob pattern itself from the exclude list, escape special characters with a backslash: + glee remove filename\*.txt`, + Args: cobra.MinimumNArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + return glee.RemoveExcludes(args) + }, +} + +func init() { + rootCmd.AddCommand(removeCmd) +} diff --git a/pkg/glee/glee.go b/pkg/glee/glee.go index 185a30e..5b7fec3 100644 --- a/pkg/glee/glee.go +++ b/pkg/glee/glee.go @@ -35,6 +35,49 @@ func AddExcludes(entries []string) error { return nil } +func RemoveExcludes(entries []string) error { + _, excludeFile, err := getGitRootAndExcludePath() + if err != nil { + return err + } + + content, err := os.ReadFile(excludeFile) + if err != nil { + return fmt.Errorf("reading exclude file: %w", err) + } + + lines := strings.Split(strings.TrimRight(string(content), "\n"), "\n") + var newLines []string + + for _, line := range lines { + trimmedLine := strings.TrimSpace(line) + if trimmedLine == "" || strings.HasPrefix(trimmedLine, "#") { + newLines = append(newLines, line) + continue + } + + shouldKeep := true + for _, entry := range entries { + if trimmedLine == entry { + shouldKeep = false + break + } + } + + if shouldKeep { + newLines = append(newLines, line) + } + } + + newContent := strings.Join(newLines, "\n") + "\n" + + if err := os.WriteFile(excludeFile, []byte(newContent), 0644); err != nil { + return fmt.Errorf("writing updated exclude file: %w", err) + } + + return nil +} + func ListExcludes() error { _, excludeFile, err := getGitRootAndExcludePath() if err != nil { diff --git a/pkg/glee/glee_test.go b/pkg/glee/glee_test.go index f172266..d9f71b2 100644 --- a/pkg/glee/glee_test.go +++ b/pkg/glee/glee_test.go @@ -55,6 +55,55 @@ func TestAddExcludes(t *testing.T) { } } +func TestRemoveExcludes(t *testing.T) { + tempDir, cleanup := setupTestRepo(t) + defer cleanup() + + // Change to the temp directory + oldWd, _ := os.Getwd() + os.Chdir(tempDir) + defer os.Chdir(oldWd) + + // Add some excludes + excludeFile := filepath.Join(tempDir, ".git", "info", "exclude") + content := "file1.txt\n# Comment\ndir/file2.txt\nfile3.txt\n" + if err := os.WriteFile(excludeFile, []byte(content), 0644); err != nil { + t.Fatalf("Failed to write initial exclude file: %v", err) + } + + // Remove some excludes + entriesToRemove := []string{"file1.txt", "dir/file2.txt"} + if err := RemoveExcludes(entriesToRemove); err != nil { + t.Fatalf("RemoveExcludes failed: %v", err) + } + + // Check the content of the exclude file after removal + newContent, err := os.ReadFile(excludeFile) + if err != nil { + t.Fatalf("Failed to read exclude file after removal: %v", err) + } + + expectedContent := "# Comment\nfile3.txt\n" + if string(newContent) != expectedContent { + t.Errorf("Exclude file content mismatch after removal. Expected:\n%q\nGot:\n%q", expectedContent, string(newContent)) + } + + // Try to remove a non-existent entry (should not error) + if err := RemoveExcludes([]string{"non-existent-file.txt"}); err != nil { + t.Errorf("RemoveExcludes errored on non-existent entry: %v", err) + } + + // Check content hasn't changed + newContent, err = os.ReadFile(excludeFile) + if err != nil { + t.Fatalf("Failed to read exclude file after non-existent removal: %v", err) + } + + if string(newContent) != expectedContent { + t.Errorf("Exclude file content changed after non-existent removal. Expected:\n%q\nGot:\n%q", expectedContent, string(newContent)) + } +} + func TestListExcludes(t *testing.T) { tempDir, cleanup := setupTestRepo(t) defer cleanup() From b570e6ae94eb1eb756bab95cfc533192085c7d7a Mon Sep 17 00:00:00 2001 From: Vyacheslav Pukhanov Date: Fri, 5 Jul 2024 20:33:30 +0300 Subject: [PATCH 2/2] docs: document `remove` command --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 12016e2..c6bcc0c 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,15 @@ glee add *.txt # Add a glob pattern itself to the exclude list (escaping the special character) glee add \*.txt +# Remove specific files from the exclude list +glee remove filename1 filename2 + +# Remove multiple files using a glob pattern +glee remove *.txt + +# Remove a glob pattern itself from the exclude list (escaping the special character) +glee remove \*.txt + # Display current entries in the exclude list glee list