summaryrefslogtreecommitdiff
path: root/findByPatchId.go
blob: 6882055cf823c041850fbbd373f88c4d6b42bc6a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
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 <commit> | 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 "<stable_id> <unstable_id>".
	// We split the string by whitespace to get both.
	ids := strings.Fields(patchIDOutput.String())
	return ids, nil
}