package main import ( "bufio" "bytes" "errors" "fmt" "io" "os" "os/exec" "strings" ) func searchAllCommits(targetPatchID string) (string, error) { revListArgs := []string{"--all"} cmdRevList := exec.Command("git", append([]string{"rev-list"}, revListArgs...)...) // We'll read the commit hashes from the command's standard output. revListStdout, err := cmdRevList.StdoutPipe() if err != nil { fmt.Fprintf(os.Stderr, "Error creating rev-list stdout pipe: %v\n", err) return "", err } // Start the command but don't wait for it to finish yet. if err := cmdRevList.Start(); err != nil { fmt.Fprintf(os.Stderr, "Error starting git rev-list: %v\n", err) return "", err } var commitHash string // 3. Process the stream of commit hashes line by line. scanner := bufio.NewScanner(revListStdout) found := false for scanner.Scan() { commitHash = scanner.Text() // 4. For each commit, calculate its patch ID. calculatedIDs, err := getPatchIDForCommit(commitHash) if err != nil { // Log the error but continue trying other commits. fmt.Fprintf(os.Stderr, "Warning: could not get patch-id for %s: %v\n", commitHash, err) continue } // 5. Compare the calculated IDs with our target. for _, id := range calculatedIDs { if id == targetPatchID { fmt.Printf("Found matching commit: %s\n", commitHash) found = true return commitHash, nil // We could exit here, but continuing will find all duplicates (e.g., from cherry-picks). } } } // Wait for the rev-list command to finish and check for errors. if err := cmdRevList.Wait(); err != nil { fmt.Fprintf(os.Stderr, "Error running git rev-list: %v\n", err) return "", err } if err := scanner.Err(); err != nil { fmt.Fprintf(os.Stderr, "Error reading commit hashes: %v\n", err) return "", err } if !found { fmt.Fprintf(os.Stderr, "No commit found with patch ID: %s\n", targetPatchID) return "not found", errors.New("patchId not found") } return commitHash, nil } // getPatchIDForCommit runs `git show | git patch-id --stable` // and returns the stable and unstable patch IDs. func getPatchIDForCommit(commitHash string) ([]string, error) { // Command to get the commit's diff cmdShow := exec.Command("git", "show", commitHash) // Command to calculate the patch-id from stdin cmdPatchID := exec.Command("git", "patch-id", "--stable") // Create a pipe to connect the output of cmdShow to the input of cmdPatchID. r, w := io.Pipe() cmdShow.Stdout = w cmdPatchID.Stdin = r var patchIDOutput bytes.Buffer cmdPatchID.Stdout = &patchIDOutput // Start both commands. if err := cmdShow.Start(); err != nil { return nil, fmt.Errorf("failed to start git-show: %w", err) } if err := cmdPatchID.Start(); err != nil { return nil, fmt.Errorf("failed to start git-patch-id: %w", err) } // Wait for 'show' to finish writing to the pipe, then close the writer. // This will signal EOF to the 'patch-id' command's stdin. errShow := cmdShow.Wait() w.Close() if errShow != nil { return nil, fmt.Errorf("git-show failed: %w", errShow) } // Now wait for 'patch-id' to finish reading and processing. errPatchID := cmdPatchID.Wait() if errPatchID != nil { return nil, fmt.Errorf("git-patch-id failed: %w", errPatchID) } // The output is typically " ". // We split the string by whitespace to get both. ids := strings.Fields(patchIDOutput.String()) return ids, nil }