diff options
| author | Jeff Carr <[email protected]> | 2025-10-06 21:59:17 -0500 |
|---|---|---|
| committer | Jeff Carr <[email protected]> | 2025-10-06 21:59:17 -0500 |
| commit | b4e05f2ad96598afe502a86c281bcf3fdfb4faf2 (patch) | |
| tree | 5351987a2c0b8edff195ece35b4d8767a2847195 | |
| parent | 5e3adeadf40b29f63e0e8236e120aeae124ff82a (diff) | |
correctly compute patchId from gitam file
| -rw-r--r-- | linesScanner.go | 80 | ||||
| -rw-r--r-- | patchid.go | 224 |
2 files changed, 200 insertions, 104 deletions
diff --git a/linesScanner.go b/linesScanner.go new file mode 100644 index 0000000..6677b45 --- /dev/null +++ b/linesScanner.go @@ -0,0 +1,80 @@ +// Copyright 2017-2025 WIT.COM Inc. All rights reserved. +// Use of this source code is governed by the GPL 3.0 + +package gitpb + +import ( + "fmt" + sync "sync" +) + +// taken from autogenpb. this should be in some generic library or just in GO +// var allTheLines *LinesScanner + +// newLinesScanner initializes a new LinesScanner iterator with a slice of strings. +func NewLinesScanner(things []string) *LinesScanner { + return &LinesScanner{things: things} +} + +// LinesScanner provides an iterator over a slice of strings, allowing for +// sequential access and the ability to un-scan (step back). +type LinesScanner struct { + sync.Mutex + + things []string + index int +} + +// Scan advances the iterator to the next line. It returns false if there are +// no more lines. +func (it *LinesScanner) Scan() bool { + if it.index >= len(it.things) { + return false + } + it.Lock() + it.index++ + it.Unlock() + return true +} + +// UnScan moves the iterator back one line. It returns false if the iterator +// is already at the beginning. +func (it *LinesScanner) UnScan() bool { + if it.index < 1 { + it.index = 0 + return false + } + it.Lock() + it.index-- + it.Unlock() + return true +} + +// NextRaw returns the current line from the scanner without any modification. +func (it *LinesScanner) NextRaw() string { + if it.index-1 == len(it.things) { + fmt.Println("Next() error in LinesScanner", it.index) + } + return it.things[it.index-1] +} + +func (it *LinesScanner) Err() error { + return nil +} + +// alias as some scanners use Text() +func (it *LinesScanner) Text() string { + return it.Next() +} + +// Next returns the current line from the scanner with leading and trailing +// whitespace removed. +func (it *LinesScanner) Next() string { + if it.index-1 == len(it.things) { + fmt.Println("Next() error in LinesScanner", it.index) + } + // out := commentPreprocessor(it.things[it.index-1]) + out := it.things[it.index-1] + return out + // return out +} @@ -1,20 +1,16 @@ package gitpb import ( - "bufio" "bytes" "crypto/sha1" "encoding/hex" "fmt" - "io" "os/exec" "strings" "go.wit.com/log" ) -// git show 5b277e7686974d2195586d5f5b82838ee9ddb036 |git patch-id --stable -// bf86be06af03b1a89ee155b214358362ec76f7b6 5b277e7686974d2195586d5f5b82838ee9ddb036 // forged patch: "working on ping pong" // The --stable flag is an important detail. When you use it, git patch-id outputs two hashes: // <stable_patch_id> <unstable_patch_id> @@ -71,19 +67,53 @@ func (repo *Repo) FindPatchIdByHash(hash string) (string, error) { return fields[0], nil } -// ComputePatchID calculates the patch ID for a given patch file's content. -// It mimics the behavior of the `git patch-id` command by normalizing the -// patch content before hashing. -// -// Normalization rules: -// 1. It ignores all lines until the first "--- " line is encountered. -// 2. It ignores diff headers (lines starting with "---", "+++", "diff --git", "index"). -// 3. For content lines ('+', '-', or ' '), it keeps the first character and -// trims any trailing whitespace from the rest of the line before hashing. -// 4. All other lines (like hunk headers "@@ ... @@") are ignored. -func FindPatchIdByBytes(patchData io.Reader) (string, error) { - scanner := bufio.NewScanner(patchData) - var normalizedPatch bytes.Buffer +func (repo *Repo) FindPatchIdFromGitAm(gitam []byte) (string, string, error) { + return FindPatchIdFromGitAm(gitam) +} + +func FindPatchIdFromGitAm(gitam []byte) (string, string, error) { + // 1. Create the command. + cmd := exec.Command("git", "patch-id", "--stable") + + // 2. Set the command's Stdin to a reader created from our string. + // This is the key change. + cmd.Stdin = strings.NewReader(string(gitam)) + + // 3. We need buffers to capture the command's output. + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + + // 4. Run the command and wait for it to finish. + // cmd.Run() is a convenient wrapper around Start() and Wait(). + err := cmd.Run() + if err != nil { + // Include stderr in the error message for better debugging. + return "", "", fmt.Errorf("git patch-id command failed: %w\nStderr: %s", err, stderr.String()) + } + + fields := strings.Fields(stdout.String()) + if len(fields) != 2 { + return "", "", fmt.Errorf("git-patch-id produced empty output") + } + + return fields[0], fields[1], nil +} + +// computes the stable git patch-id from the output of git-am +// doesn't work. it needs to add the SHA-1 parts together +// cat WTF.2.diff | git patch-id --stable +// go.wit.com/apps/utils/forged +// git show 5b277e7686974d2195586d5f5b82838ee9ddb036 |git patch-id --stable +// bf86be06af03b1a89ee155b214358362ec76f7b6 5b277e7686974d2195586d5f5b82838ee9ddb036 +// 73d73e12dcd727721253140ea68441dd0b824f8c 0000000000000000000000000000000000000000 +// 515792a0c4965b69f6b9e7f89e2f896148b03c97 0000000000000000000000000000000000000000 +// 1dd8b4a7d7d42a78fd1ff84dcc2ac87bc7318ee6 0000000000000000000000000000000000000000 +// 8478bad3e1f97818a68a014a8f30f1b2951b026b 0000000000000000000000000000000000000000 +func FindPatchIdFromGitAmBroken(gitAmData []byte) string { + var lines []string + scanner := NewLinesScanner(strings.Split(string(gitAmData), "\n")) + // var normalizedPatch bytes.Buffer var inPatchBody bool for scanner.Scan() { @@ -91,7 +121,7 @@ func FindPatchIdByBytes(patchData io.Reader) (string, error) { // The patch content officially starts at the first `---` line. // This helps skip email headers in patches generated by `git format-patch`. - if !inPatchBody && strings.HasPrefix(line, "---") { + if !inPatchBody && strings.HasPrefix(line, "diff ") { inPatchBody = true } @@ -99,101 +129,87 @@ func FindPatchIdByBytes(patchData io.Reader) (string, error) { continue } - // Skip headers and metadata lines - if strings.HasPrefix(line, "---") || - strings.HasPrefix(line, "+++") || - strings.HasPrefix(line, "index") || - strings.HasPrefix(line, "diff --git") { - continue - } - - // Process only the actual content lines (context, addition, deletion) - if len(line) > 0 && (line[0] == ' ' || line[0] == '+' || line[0] == '-') { - // Keep the first character (' ', '+', or '-') - firstChar := line[0] - content := line[1:] - - // Trim trailing whitespace from the content part of the line - trimmedContent := strings.TrimRight(content, " ") - - // Write the normalized line to our buffer for hashing - normalizedPatch.WriteByte(firstChar) - normalizedPatch.WriteString(trimmedContent) - normalizedPatch.WriteByte('\n') + if line == "-- " { + break } - // All other lines (e.g., "@@ ... @@") are ignored. - } - - if err := scanner.Err(); err != nil { - return "", fmt.Errorf("error reading patch data: %w", err) - } + lines = append(lines, line) - // Calculate the SHA-1 hash of the normalized patch content - hash := sha1.Sum(normalizedPatch.Bytes()) - patchID := hex.EncodeToString(hash[:]) - - return patchID, nil -} + /* + // Skip headers and metadata lines + if strings.HasPrefix(line, "---") || + strings.HasPrefix(line, "+++") || + strings.HasPrefix(line, "index") || + strings.HasPrefix(line, "diff --git") { + continue + } -/* -// --- Example Usage --- + normalizedPatch.WriteString(line + "\n") + */ + /* + // Process only the actual content lines (context, addition, deletion) + if len(line) > 0 && (line[0] == ' ' || line[0] == '+' || line[0] == '-') { + // Keep the first character (' ', '+', or '-') + firstChar := line[0] + content := line[1:] -func main() { - // Example 1: Using a string as input - patchString := ` -From 2f42795678c7113f7437c04f56570c08b68850f4 Mon Sep 17 00:00:00 2001 -From: A Developer <[email protected]> -Subject: [PATCH] Example change to demonstrate patch-id + // Trim trailing whitespace from the content part of the line + trimmedContent := strings.TrimRight(content, " ") ---- a/README.md -+++ b/README.md -@@ -1,3 +1,4 @@ - # My Project - --This is a sample project. -+This is a sample project. -+It has an additional line. -` - stringReader := strings.NewReader(patchString) - patchID, err := ComputePatchID(stringReader) - if err != nil { - fmt.Fprintf(os.Stderr, "Error computing patch ID from string: %v\n", err) - return + // Write the normalized line to our buffer for hashing + normalizedPatch.WriteByte(firstChar) + normalizedPatch.WriteString(trimmedContent) + normalizedPatch.WriteByte('\n') + } + // All other lines (e.g., "@@ ... @@") are ignored. + */ } - fmt.Printf("Patch ID from string: %s\n", patchID) - // Example 2: Reading from a file - // Create a temporary patch file for demonstration - patchContent := []byte(patchString) - tmpfile, err := os.CreateTemp("", "example.*.patch") - if err != nil { - fmt.Fprintf(os.Stderr, "Error creating temp file: %v\n", err) - return - } - defer os.Remove(tmpfile.Name()) // clean up + // var fullhash [20]byte - if _, err := tmpfile.Write(patchContent); err != nil { - fmt.Fprintf(os.Stderr, "Error writing to temp file: %v\n", err) - return - } - if err := tmpfile.Close(); err != nil { - fmt.Fprintf(os.Stderr, "Error closing temp file: %v\n", err) - return + var final string + for _, line := range lines { + if strings.HasPrefix(line, "---") { + // final += "---\n" + final += line + "\n" + continue + } + if strings.HasPrefix(line, "+++") { + // final += "+++\n" + final += line + "\n" + continue + } + if strings.HasPrefix(line, "index") { + // final += "index\n" + // final += line + "\n" + continue + } + if strings.HasPrefix(line, "@@") { + // final += "@@\n" + continue + } + if strings.HasPrefix(line, "diff --git") { + if len(final) != 0 { + hash := sha1.Sum([]byte(final)) + // fullhash ?? hash + patchId := hex.EncodeToString(hash[:]) + log.Info("partial hash", patchId) + } + log.Printf("%s", final) + // final += "diff --git\n" + final = line + "\n" + // final = "" + continue + } + final += line + "\n" } - // Open the file to pass to our function - file, err := os.Open(tmpfile.Name()) - if err != nil { - fmt.Fprintf(os.Stderr, "Error opening temp file: %v\n", err) - return - } - defer file.Close() + // final = strings.TrimSuffix(final, "\n") - patchIDFromFile, err := ComputePatchID(file) - if err != nil { - fmt.Fprintf(os.Stderr, "Error computing patch ID from file: %v\n", err) - return - } - fmt.Printf("Patch ID from file: %s\n", patchIDFromFile) + // Calculate the SHA-1 hash of the normalized patch content + // hash := sha1.Sum(normalizedPatch.Bytes()) + hash := sha1.Sum([]byte(final)) + patchID := hex.EncodeToString(hash[:]) + // log.Printf("%s", final) + // log.Info(strings.Join(lines, "\n")) + return patchID } -*/ |
