aboutsummaryrefslogtreecommitdiff
path: root/lmtp_handle_patch.go
diff options
context:
space:
mode:
authorRunxi Yu <me@runxiyu.org>2025-04-02 08:49:03 +0800
committerRunxi Yu <me@runxiyu.org>2025-04-02 09:24:37 +0800
commitc32389d7d54f3fe66d32f849c02c5e75b7d476c8 (patch)
tree6a28b81c35e6ac602cb10ca24869b8bc2342134c /lmtp_handle_patch.go
parentLMTP: Fix mistake in command arguments (diff)
downloadforge-c32389d7d54f3fe66d32f849c02c5e75b7d476c8.tar.gz
forge-c32389d7d54f3fe66d32f849c02c5e75b7d476c8.tar.zst
forge-c32389d7d54f3fe66d32f849c02c5e75b7d476c8.zip
LMTP: Actually apply patches from email
Diffstat (limited to 'lmtp_handle_patch.go')
-rw-r--r--lmtp_handle_patch.go108
1 files changed, 74 insertions, 34 deletions
diff --git a/lmtp_handle_patch.go b/lmtp_handle_patch.go
index 138b592..d69424b 100644
--- a/lmtp_handle_patch.go
+++ b/lmtp_handle_patch.go
@@ -5,16 +5,16 @@ package main
import (
"bytes"
- // "crypto/rand"
- // "fmt"
+ "crypto/rand"
+ "encoding/hex"
"os"
"os/exec"
+ "strings"
+ "time"
"github.com/bluekeyes/go-gitdiff/gitdiff"
"github.com/emersion/go-message"
"github.com/go-git/go-git/v5"
- "github.com/go-git/go-git/v5/plumbing"
- "github.com/go-git/go-git/v5/plumbing/object"
)
func lmtpHandlePatch(session *lmtpSession, groupPath []string, repoName string, email *message.Entity) (err error) {
@@ -24,6 +24,11 @@ func lmtpHandlePatch(session *lmtpSession, groupPath []string, repoName string,
return
}
+ var header *gitdiff.PatchHeader
+ if header, err = gitdiff.ParsePatchHeader(preamble); err != nil {
+ return
+ }
+
var repo *git.Repository
var fsPath string
repo, _, _, fsPath, err = openRepo(session.ctx, groupPath, repoName)
@@ -31,61 +36,96 @@ func lmtpHandlePatch(session *lmtpSession, groupPath []string, repoName string,
return
}
- var headRef *plumbing.Reference
- if headRef, err = repo.Head(); err != nil {
+ headRef, err := repo.Head()
+ if err != nil {
return
}
-
- var headCommit *object.Commit
- if headCommit, err = repo.CommitObject(headRef.Hash()); err != nil {
+ headCommit, err := repo.CommitObject(headRef.Hash())
+ if err != nil {
return
}
-
- var headTree *object.Tree
- if headTree, err = headCommit.Tree(); err != nil {
+ headTree, err := headCommit.Tree()
+ if err != nil {
return
}
- // TODO: Try to not shell out
+ headTreeHash := headTree.Hash.String()
+ blobUpdates := make(map[string][]byte)
for _, diffFile := range diffFiles {
- var sourceFile *object.File
- if sourceFile, err = headTree.File(diffFile.OldName); err != nil {
+ sourceFile, err := headTree.File(diffFile.OldName)
+ if err != nil {
return err
}
- var sourceString string
- if sourceString, err = sourceFile.Contents(); err != nil {
+ sourceString, err := sourceFile.Contents()
+ if err != nil {
return err
}
- hashBuf := bytes.Buffer{}
- patchedBuf := bytes.Buffer{}
+
sourceBuf := bytes.NewReader(stringToBytes(sourceString))
- if err = gitdiff.Apply(&patchedBuf, sourceBuf, diffFile); err != nil {
+ var patchedBuf bytes.Buffer
+ if err := gitdiff.Apply(&patchedBuf, sourceBuf, diffFile); err != nil {
return err
}
- proc := exec.CommandContext(session.ctx, "git", "hash-object", "-w", "-t", "blob", "--stdin")
- proc.Env = append(os.Environ(), "GIT_DIR="+fsPath)
- proc.Stdout = &hashBuf
- proc.Stdin = &patchedBuf
- if err = proc.Start(); err != nil {
+
+ var hashBuf bytes.Buffer
+
+ // It's really difficult to do this via go-git so we're just
+ // going to use upstream git for now.
+ // TODO
+ cmd := exec.CommandContext(session.ctx, "git", "hash-object", "-w", "-t", "blob", "--stdin")
+ cmd.Env = append(os.Environ(), "GIT_DIR="+fsPath)
+ cmd.Stdout = &hashBuf
+ cmd.Stdin = &patchedBuf
+ if err := cmd.Run(); err != nil {
return err
}
- if err = proc.Wait(); err != nil {
+
+ newHashStr := strings.TrimSpace(hashBuf.String())
+ newHash, err := hex.DecodeString(newHashStr)
+ if err != nil {
return err
}
- newHash := hashBuf.Bytes()
- if len(newHash) != 20*2+1 { // TODO: Hardcoded from the size of plumbing.Hash
- panic("unexpected hash size")
+
+ blobUpdates[diffFile.NewName] = newHash
+ if diffFile.NewName != diffFile.OldName {
+ blobUpdates[diffFile.OldName] = nil // Mark for deletion.
}
- // TODO: Add to tree
}
- // contribBranchName := rand.Text()
+ newTreeSha, err := buildTreeRecursive(session.ctx, fsPath, headTreeHash, blobUpdates)
+ if err != nil {
+ return err
+ }
+
+ commitMsg := header.Title
+ if header.Body != "" {
+ commitMsg += "\n\n" + header.Body
+ }
+
+ env := append(os.Environ(),
+ "GIT_DIR="+fsPath,
+ "GIT_AUTHOR_NAME="+header.Author.Name,
+ "GIT_AUTHOR_EMAIL="+header.Author.Email,
+ "GIT_AUTHOR_DATE="+header.AuthorDate.Format(time.RFC3339),
+ )
+ commitCmd := exec.CommandContext(session.ctx, "git", "commit-tree", newTreeSha, "-p", headCommit.Hash.String(), "-m", commitMsg)
+ commitCmd.Env = env
- // TODO: Store the branch
+ var commitOut bytes.Buffer
+ commitCmd.Stdout = &commitOut
+ if err := commitCmd.Run(); err != nil {
+ return err
+ }
+ newCommitSha := strings.TrimSpace(commitOut.String())
- // fmt.Println(repo, diffFiles, preamble)
- _ = preamble
+ newBranchName := rand.Text()
+
+ refCmd := exec.CommandContext(session.ctx, "git", "update-ref", "refs/heads/contrib/"+newBranchName, newCommitSha) //#nosec G204
+ refCmd.Env = append(os.Environ(), "GIT_DIR="+fsPath)
+ if err := refCmd.Run(); err != nil {
+ return err
+ }
return nil
}