package main import ( "bufio" "fmt" "log" "os" "os/exec" "path/filepath" "strings" "sync" ) func runFindDelete(searchString, dir, workDir string) { cmd := exec.Command("git", "grep", "-l", "-F", searchString) cmd.Stderr = os.Stderr cmd.Dir = dir stdout, err := cmd.StdoutPipe() if err != nil { log.Fatalf("Failed to create stdout pipe for git grep: %s\n", err) } if err := cmd.Start(); err != nil { log.Fatalf("Failed to start git grep: %s\n", err) } var filePaths []string scanner := bufio.NewScanner(stdout) for scanner.Scan() { filePaths = append(filePaths, scanner.Text()) } if err := cmd.Wait(); err != nil { if strings.HasSuffix(err.Error(), "exit status 1") { fmt.Fprintf(os.Stderr, "Pattern not found in: %s\n", dir) return } log.Fatalf("Failed to wait for git grep: %s\n", err) } relDir, err := filepath.Rel(workDir, dir) if err != nil { fmt.Fprintf(os.Stderr, "Error: %s", err.Error()) return } fmt.Fprintf(os.Stderr, "Found at %s in %d files...\n", relDir, len(filePaths)) var wg sync.WaitGroup for i, relFilePath := range filePaths { fmt.Fprintf(os.Stderr, "Deleting in: %s\n", relFilePath) absFilePath := filepath.Join(dir, relFilePath) wg.Add(1) go func(i int, filePath string) { defer wg.Done() fileContent, err := os.ReadFile(filePath) if err != nil { log.Printf("Failed to read file %s: %s\n", filePath, err) return } lines := strings.Split(string(fileContent), "\n") var newLines []string for _, line := range lines { if !strings.Contains(line, searchString) { newLines = append(newLines, line) } } err = os.WriteFile(filePath, []byte(strings.Join(newLines, "\n")), 0644) if err != nil { log.Printf("Failed to write to file %s: %s\n", filePath, err) return } fmt.Fprintf(os.Stderr, "[%d/%d]: deleted in %s\n", i+1, len(filePaths), filepath.Base(filePath)) }(i, absFilePath) } wg.Wait() } func main() { if len(os.Args) != 2 { log.Fatalf("Usage: %s <search string>\n", os.Args[0]) } searchString := os.Args[1] currentDir, err := os.Getwd() if err != nil { log.Fatalf("Failed to get current directory: %s\n", err) } // If the .git directory does not exist in the current dir, check if it exists in child dirs. if _, err := os.Stat(filepath.Join(currentDir, ".git")); os.IsNotExist(err) { // .git does not exist, iterate through subdirectories filepath.Walk(currentDir, func(path string, info os.FileInfo, err error) error { if err != nil { return err // propagate the error } if info.IsDir() && info.Name() == ".git" { // Found a .git directory, run find-delete in its parent directory gitDir := filepath.Dir(path) runFindDelete(searchString, gitDir, currentDir) return filepath.SkipDir // skip remaining files in this directory } return nil }) } // Try running the delete in the current dir as well runFindDelete(searchString, currentDir, currentDir) }