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
|
// SPDX-License-Identifier: AGPL-3.0-only
// SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
package main
import (
"bytes"
// "crypto/rand"
// "fmt"
"os"
"os/exec"
"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) {
var diffFiles []*gitdiff.File
var preamble string
if diffFiles, preamble, err = gitdiff.Parse(email.Body); err != nil {
return
}
var repo *git.Repository
var fsPath string
repo, _, _, fsPath, err = openRepo(session.ctx, groupPath, repoName)
if err != nil {
return
}
var headRef *plumbing.Reference
if headRef, err = repo.Head(); err != nil {
return
}
var headCommit *object.Commit
if headCommit, err = repo.CommitObject(headRef.Hash()); err != nil {
return
}
var headTree *object.Tree
if headTree, err = headCommit.Tree(); err != nil {
return
}
// TODO: Try to not shell out
for _, diffFile := range diffFiles {
var sourceFile *object.File
if sourceFile, err = headTree.File(diffFile.OldName); err != nil {
return err
}
var sourceString string
if sourceString, err = sourceFile.Contents(); err != nil {
return err
}
hashBuf := bytes.Buffer{}
patchedBuf := bytes.Buffer{}
sourceBuf := bytes.NewReader(stringToBytes(sourceString))
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 {
return err
}
newHash := hashBuf.Bytes()
if len(newHash) != 20*2+1 { // TODO: Hardcoded from the size of plumbing.Hash
panic("unexpected hash size")
}
// TODO: Add to tree
}
// contribBranchName := rand.Text()
// TODO: Store the branch
// fmt.Println(repo, diffFiles, preamble)
_ = preamble
return nil
}
|