aboutsummaryrefslogtreecommitdiff
path: root/forged/internal/ipc/git2c/extra.go
diff options
context:
space:
mode:
authorRunxi Yu <me@runxiyu.org>2025-08-12 11:01:07 +0800
committerRunxi Yu <me@runxiyu.org>2025-09-16 08:58:16 +0800
commitc12fe030fe5935882047e75ac8a3792faea27574 (patch)
treee2b6f795410348596a7965694bed7e85511d0874 /forged/internal/ipc/git2c/extra.go
parentRemove forge-specific functions from misc (diff)
downloadforge-c12fe030fe5935882047e75ac8a3792faea27574.tar.gz
forge-c12fe030fe5935882047e75ac8a3792faea27574.tar.zst
forge-c12fe030fe5935882047e75ac8a3792faea27574.zip
RefactorHEADmaster
Diffstat (limited to 'forged/internal/ipc/git2c/extra.go')
-rw-r--r--forged/internal/ipc/git2c/extra.go413
1 files changed, 413 insertions, 0 deletions
diff --git a/forged/internal/ipc/git2c/extra.go b/forged/internal/ipc/git2c/extra.go
new file mode 100644
index 0000000..1a3e3a6
--- /dev/null
+++ b/forged/internal/ipc/git2c/extra.go
@@ -0,0 +1,413 @@
+// SPDX-License-Identifier: AGPL-3.0-only
+// SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
+
+package git2c
+
+import (
+ "encoding/hex"
+ "fmt"
+ "time"
+)
+
+type DiffChunk struct {
+ Op uint64
+ Content string
+}
+
+type FileDiff struct {
+ FromMode uint64
+ ToMode uint64
+ FromPath string
+ ToPath string
+ Chunks []DiffChunk
+}
+
+type CommitInfo struct {
+ Hash string
+ AuthorName string
+ AuthorEmail string
+ AuthorWhen int64 // unix secs
+ AuthorTZMin int64 // minutes ofs
+ CommitterName string
+ CommitterEmail string
+ CommitterWhen int64
+ CommitterTZMin int64
+ Message string
+ Parents []string // hex
+ Files []FileDiff
+}
+
+func (c *Client) ResolveRef(repoPath, refType, refName string) (string, error) {
+ if err := c.writer.WriteData([]byte(repoPath)); err != nil {
+ return "", fmt.Errorf("sending repo path failed: %w", err)
+ }
+ if err := c.writer.WriteUint(3); err != nil {
+ return "", fmt.Errorf("sending command failed: %w", err)
+ }
+ if err := c.writer.WriteData([]byte(refType)); err != nil {
+ return "", fmt.Errorf("sending ref type failed: %w", err)
+ }
+ if err := c.writer.WriteData([]byte(refName)); err != nil {
+ return "", fmt.Errorf("sending ref name failed: %w", err)
+ }
+
+ status, err := c.reader.ReadUint()
+ if err != nil {
+ return "", fmt.Errorf("reading status failed: %w", err)
+ }
+ if status != 0 {
+ return "", Perror(status)
+ }
+ id, err := c.reader.ReadData()
+ if err != nil {
+ return "", fmt.Errorf("reading oid failed: %w", err)
+ }
+ return hex.EncodeToString(id), nil
+}
+
+func (c *Client) ListBranches(repoPath string) ([]string, error) {
+ if err := c.writer.WriteData([]byte(repoPath)); err != nil {
+ return nil, fmt.Errorf("sending repo path failed: %w", err)
+ }
+ if err := c.writer.WriteUint(4); err != nil {
+ return nil, fmt.Errorf("sending command failed: %w", err)
+ }
+ status, err := c.reader.ReadUint()
+ if err != nil {
+ return nil, fmt.Errorf("reading status failed: %w", err)
+ }
+ if status != 0 {
+ return nil, Perror(status)
+ }
+ count, err := c.reader.ReadUint()
+ if err != nil {
+ return nil, fmt.Errorf("reading count failed: %w", err)
+ }
+ branches := make([]string, 0, count)
+ for range count {
+ name, err := c.reader.ReadData()
+ if err != nil {
+ return nil, fmt.Errorf("reading branch name failed: %w", err)
+ }
+ branches = append(branches, string(name))
+ }
+ return branches, nil
+}
+
+func (c *Client) FormatPatch(repoPath, commitHex string) (string, error) {
+ if err := c.writer.WriteData([]byte(repoPath)); err != nil {
+ return "", fmt.Errorf("sending repo path failed: %w", err)
+ }
+ if err := c.writer.WriteUint(5); err != nil {
+ return "", fmt.Errorf("sending command failed: %w", err)
+ }
+ if err := c.writer.WriteData([]byte(commitHex)); err != nil {
+ return "", fmt.Errorf("sending commit failed: %w", err)
+ }
+ status, err := c.reader.ReadUint()
+ if err != nil {
+ return "", fmt.Errorf("reading status failed: %w", err)
+ }
+ if status != 0 {
+ return "", Perror(status)
+ }
+ buf, err := c.reader.ReadData()
+ if err != nil {
+ return "", fmt.Errorf("reading patch failed: %w", err)
+ }
+ return string(buf), nil
+}
+
+func (c *Client) MergeBase(repoPath, hexA, hexB string) (string, error) {
+ if err := c.writer.WriteData([]byte(repoPath)); err != nil {
+ return "", fmt.Errorf("sending repo path failed: %w", err)
+ }
+ if err := c.writer.WriteUint(7); err != nil {
+ return "", fmt.Errorf("sending command failed: %w", err)
+ }
+ if err := c.writer.WriteData([]byte(hexA)); err != nil {
+ return "", fmt.Errorf("sending oid A failed: %w", err)
+ }
+ if err := c.writer.WriteData([]byte(hexB)); err != nil {
+ return "", fmt.Errorf("sending oid B failed: %w", err)
+ }
+ status, err := c.reader.ReadUint()
+ if err != nil {
+ return "", fmt.Errorf("reading status failed: %w", err)
+ }
+ if status != 0 {
+ return "", Perror(status)
+ }
+ base, err := c.reader.ReadData()
+ if err != nil {
+ return "", fmt.Errorf("reading base oid failed: %w", err)
+ }
+ return hex.EncodeToString(base), nil
+}
+
+func (c *Client) Log(repoPath, refSpec string, n uint) ([]Commit, error) {
+ if err := c.writer.WriteData([]byte(repoPath)); err != nil {
+ return nil, fmt.Errorf("sending repo path failed: %w", err)
+ }
+ if err := c.writer.WriteUint(8); err != nil {
+ return nil, fmt.Errorf("sending command failed: %w", err)
+ }
+ if err := c.writer.WriteData([]byte(refSpec)); err != nil {
+ return nil, fmt.Errorf("sending refspec failed: %w", err)
+ }
+ if err := c.writer.WriteUint(uint64(n)); err != nil {
+ return nil, fmt.Errorf("sending limit failed: %w", err)
+ }
+ status, err := c.reader.ReadUint()
+ if err != nil {
+ return nil, fmt.Errorf("reading status failed: %w", err)
+ }
+ if status != 0 {
+ return nil, Perror(status)
+ }
+ var out []Commit
+ for {
+ id, err := c.reader.ReadData()
+ if err != nil {
+ break
+ }
+ title, _ := c.reader.ReadData()
+ authorName, _ := c.reader.ReadData()
+ authorEmail, _ := c.reader.ReadData()
+ date, _ := c.reader.ReadData()
+ out = append(out, Commit{
+ Hash: hex.EncodeToString(id),
+ Author: string(authorName),
+ Email: string(authorEmail),
+ Date: string(date),
+ Message: string(title),
+ })
+ }
+ return out, nil
+}
+
+func (c *Client) CommitTreeOID(repoPath, commitHex string) (string, error) {
+ if err := c.writer.WriteData([]byte(repoPath)); err != nil {
+ return "", fmt.Errorf("sending repo path failed: %w", err)
+ }
+ if err := c.writer.WriteUint(12); err != nil {
+ return "", fmt.Errorf("sending command failed: %w", err)
+ }
+ if err := c.writer.WriteData([]byte(commitHex)); err != nil {
+ return "", fmt.Errorf("sending oid failed: %w", err)
+ }
+ status, err := c.reader.ReadUint()
+ if err != nil {
+ return "", fmt.Errorf("reading status failed: %w", err)
+ }
+ if status != 0 {
+ return "", Perror(status)
+ }
+ id, err := c.reader.ReadData()
+ if err != nil {
+ return "", fmt.Errorf("reading tree oid failed: %w", err)
+ }
+ return hex.EncodeToString(id), nil
+}
+
+func (c *Client) CommitCreate(repoPath, treeHex string, parents []string, authorName, authorEmail string, when time.Time, message string) (string, error) {
+ if err := c.writer.WriteData([]byte(repoPath)); err != nil {
+ return "", fmt.Errorf("sending repo path failed: %w", err)
+ }
+ if err := c.writer.WriteUint(13); err != nil {
+ return "", fmt.Errorf("sending command failed: %w", err)
+ }
+ if err := c.writer.WriteData([]byte(treeHex)); err != nil {
+ return "", fmt.Errorf("sending tree oid failed: %w", err)
+ }
+ if err := c.writer.WriteUint(uint64(len(parents))); err != nil {
+ return "", fmt.Errorf("sending parents count failed: %w", err)
+ }
+ for _, p := range parents {
+ if err := c.writer.WriteData([]byte(p)); err != nil {
+ return "", fmt.Errorf("sending parent oid failed: %w", err)
+ }
+ }
+ if err := c.writer.WriteData([]byte(authorName)); err != nil {
+ return "", fmt.Errorf("sending author name failed: %w", err)
+ }
+ if err := c.writer.WriteData([]byte(authorEmail)); err != nil {
+ return "", fmt.Errorf("sending author email failed: %w", err)
+ }
+ if err := c.writer.WriteInt(when.Unix()); err != nil {
+ return "", fmt.Errorf("sending when failed: %w", err)
+ }
+ _, offset := when.Zone()
+ if err := c.writer.WriteInt(int64(offset / 60)); err != nil {
+ return "", fmt.Errorf("sending tz offset failed: %w", err)
+ }
+ if err := c.writer.WriteData([]byte(message)); err != nil {
+ return "", fmt.Errorf("sending message failed: %w", err)
+ }
+ status, err := c.reader.ReadUint()
+ if err != nil {
+ return "", fmt.Errorf("reading status failed: %w", err)
+ }
+ if status != 0 {
+ return "", Perror(status)
+ }
+ id, err := c.reader.ReadData()
+ if err != nil {
+ return "", fmt.Errorf("reading commit oid failed: %w", err)
+ }
+ return hex.EncodeToString(id), nil
+}
+
+func (c *Client) UpdateRef(repoPath, refName, commitHex string) error {
+ if err := c.writer.WriteData([]byte(repoPath)); err != nil {
+ return fmt.Errorf("sending repo path failed: %w", err)
+ }
+ if err := c.writer.WriteUint(14); err != nil {
+ return fmt.Errorf("sending command failed: %w", err)
+ }
+ if err := c.writer.WriteData([]byte(refName)); err != nil {
+ return fmt.Errorf("sending ref name failed: %w", err)
+ }
+ if err := c.writer.WriteData([]byte(commitHex)); err != nil {
+ return fmt.Errorf("sending commit oid failed: %w", err)
+ }
+ status, err := c.reader.ReadUint()
+ if err != nil {
+ return fmt.Errorf("reading status failed: %w", err)
+ }
+ if status != 0 {
+ return Perror(status)
+ }
+ return nil
+}
+
+func (c *Client) CommitInfo(repoPath, commitHex string) (*CommitInfo, error) {
+ if err := c.writer.WriteData([]byte(repoPath)); err != nil {
+ return nil, fmt.Errorf("sending repo path failed: %w", err)
+ }
+ if err := c.writer.WriteUint(6); err != nil {
+ return nil, fmt.Errorf("sending command failed: %w", err)
+ }
+ if err := c.writer.WriteData([]byte(commitHex)); err != nil {
+ return nil, fmt.Errorf("sending commit failed: %w", err)
+ }
+ status, err := c.reader.ReadUint()
+ if err != nil {
+ return nil, fmt.Errorf("reading status failed: %w", err)
+ }
+ if status != 0 {
+ return nil, Perror(status)
+ }
+ id, err := c.reader.ReadData()
+ if err != nil {
+ return nil, fmt.Errorf("reading id failed: %w", err)
+ }
+ aname, err := c.reader.ReadData()
+ if err != nil {
+ return nil, fmt.Errorf("reading author name failed: %w", err)
+ }
+ aemail, err := c.reader.ReadData()
+ if err != nil {
+ return nil, fmt.Errorf("reading author email failed: %w", err)
+ }
+ awhen, err := c.reader.ReadI64()
+ if err != nil {
+ return nil, fmt.Errorf("reading author time failed: %w", err)
+ }
+ aoff, err := c.reader.ReadI64()
+ if err != nil {
+ return nil, fmt.Errorf("reading author tz failed: %w", err)
+ }
+ cname, err := c.reader.ReadData()
+ if err != nil {
+ return nil, fmt.Errorf("reading committer name failed: %w", err)
+ }
+ cemail, err := c.reader.ReadData()
+ if err != nil {
+ return nil, fmt.Errorf("reading committer email failed: %w", err)
+ }
+ cwhen, err := c.reader.ReadI64()
+ if err != nil {
+ return nil, fmt.Errorf("reading committer time failed: %w", err)
+ }
+ coff, err := c.reader.ReadI64()
+ if err != nil {
+ return nil, fmt.Errorf("reading committer tz failed: %w", err)
+ }
+ msg, err := c.reader.ReadData()
+ if err != nil {
+ return nil, fmt.Errorf("reading message failed: %w", err)
+ }
+ pcnt, err := c.reader.ReadUint()
+ if err != nil {
+ return nil, fmt.Errorf("reading parents count failed: %w", err)
+ }
+ parents := make([]string, 0, pcnt)
+ for i := uint64(0); i < pcnt; i++ {
+ praw, perr := c.reader.ReadData()
+ if perr != nil {
+ return nil, fmt.Errorf("reading parent failed: %w", perr)
+ }
+ parents = append(parents, hex.EncodeToString(praw))
+ }
+ fcnt, err := c.reader.ReadUint()
+ if err != nil {
+ return nil, fmt.Errorf("reading file count failed: %w", err)
+ }
+ files := make([]FileDiff, 0, fcnt)
+ for i := uint64(0); i < fcnt; i++ {
+ fromMode, err := c.reader.ReadUint()
+ if err != nil {
+ return nil, fmt.Errorf("reading from mode failed: %w", err)
+ }
+ toMode, err := c.reader.ReadUint()
+ if err != nil {
+ return nil, fmt.Errorf("reading to mode failed: %w", err)
+ }
+ fromPath, err := c.reader.ReadData()
+ if err != nil {
+ return nil, fmt.Errorf("reading from path failed: %w", err)
+ }
+ toPath, err := c.reader.ReadData()
+ if err != nil {
+ return nil, fmt.Errorf("reading to path failed: %w", err)
+ }
+ ccnt, err := c.reader.ReadUint()
+ if err != nil {
+ return nil, fmt.Errorf("reading chunk count failed: %w", err)
+ }
+ chunks := make([]DiffChunk, 0, ccnt)
+ for j := uint64(0); j < ccnt; j++ {
+ op, err := c.reader.ReadUint()
+ if err != nil {
+ return nil, fmt.Errorf("reading chunk op failed: %w", err)
+ }
+ content, err := c.reader.ReadData()
+ if err != nil {
+ return nil, fmt.Errorf("reading chunk content failed: %w", err)
+ }
+ chunks = append(chunks, DiffChunk{Op: op, Content: string(content)})
+ }
+ files = append(files, FileDiff{
+ FromMode: fromMode,
+ ToMode: toMode,
+ FromPath: string(fromPath),
+ ToPath: string(toPath),
+ Chunks: chunks,
+ })
+ }
+ return &CommitInfo{
+ Hash: hex.EncodeToString(id),
+ AuthorName: string(aname),
+ AuthorEmail: string(aemail),
+ AuthorWhen: awhen,
+ AuthorTZMin: aoff,
+ CommitterName: string(cname),
+ CommitterEmail: string(cemail),
+ CommitterWhen: cwhen,
+ CommitterTZMin: coff,
+ Message: string(msg),
+ Parents: parents,
+ Files: files,
+ }, nil
+}