diff options
Diffstat (limited to 'lmtp_handle_patch.go')
-rw-r--r-- | lmtp_handle_patch.go | 82 |
1 files changed, 79 insertions, 3 deletions
diff --git a/lmtp_handle_patch.go b/lmtp_handle_patch.go index a3064d1..ed47e25 100644 --- a/lmtp_handle_patch.go +++ b/lmtp_handle_patch.go @@ -4,12 +4,88 @@ package main import ( - "log/slog" + "crypto/rand" + "fmt" + "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/config" + "github.com/go-git/go-git/v5/plumbing" + "github.com/go-git/go-git/v5/plumbing/object" ) -func lmtpHandlePatch(groupPath []string, repoName string, email *message.Entity) (err error) { - slog.Info("Pretend like I'm handling a patch!") +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 + repo, _, _, 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 + } + + // What's left to do: apply the patch on a separate branch. + // I'm not sure how to do this in go-git. I have a few thoughts: + // Method 1. Create a copy of the tree object; then iterate through + // diffFiles, traversing the tree accordingly, then hash + // blobs, insert them into the repo, replace the entry in + // the tree, then commit the tree onto a new branch. + // Perhaps storer can help with this? + // Method 2. Create an index, run the patch on it, and commit the + // index. + // I think Method 1 technically suffers from a race if the repo is + // garbage collected between the time that the blob is created and + // the time it is committed to a branch. We could just prevent + // external GCs and lock GCs ourselves though, so it's no big deal. + // Method 2 is a bit annoying and I have this impression that + // worktrees/indexes are fragile and bloated. + + myTree := *headTree + // TODO: Check if it's actually safe to modify myTree. We don't + // own these slices, so this might interfere with go-git's internal + // state. + + for _, diffFile := range diffFiles { + blobObject := plumbing.EncodedObject + var blobHash plumbing.Hash + if blobHash, err = repo.Storer.SetEncodedObject(blobObject); err != nil { + return + } + } + + /* + contribBranchName := rand.Text() + + if err = repo.CreateBranch(&config.Branch{ Name: contribBranchName, }); err != nil { + return + } + + var contribRef *plumbing.Reference + if contribRef, err = repo.Reference(plumbing.NewBranchReferenceName(contribBranchName, true); err != nil { + return + } + */ + + fmt.Println(repo, diffFiles, preamble) + return nil } |