This commit is contained in:
Adam Majer
2024-07-14 23:56:37 +02:00
parent f40888ea45
commit a2f2631bdf
2 changed files with 434 additions and 12 deletions

View File

@@ -1,12 +1,15 @@
package common
import (
"errors"
"fmt"
"io"
"os"
"os/exec"
"path"
"path/filepath"
"strings"
"sync"
)
//func (h *RequestHandler) ProcessBranchList() []string {
@@ -203,23 +206,223 @@ func (e *RequestHandler) GitExec(cwd string, params ...string) ExecStream {
return e
}
type writerFunc func([]byte) (int, error)
// Implement the Write method for the custom type
func (f writerFunc) Write(p []byte) (int, error) {
return f(p)
type ChanIO struct {
ch chan byte
}
func (e *RequestHandler) GitSubmoduleCommitId(cwd, packageName string) (string, bool) {
func (c *ChanIO) Write(p []byte) (int, error) {
for i := range len(p) {
c.ch <- p[i]
}
return len(p), nil
}
// read at least 1 byte, but don't block if nothing in channel
func (c *ChanIO) Read(p []byte) (int, error) {
data, ok := <-c.ch
for i := 0; i < cap(p); i++ {
data, ok = <-c.ch
if !ok {
return len(p), io.EOF
}
p[i] = data
if len(c.ch) < 1 {
break
}
}
return len(p), nil
}
type gitMsg struct {
hash string
itemType string
size int
}
type commit struct {
Tree string
Msg string
}
type tree_entry struct {
name string
mode int
hash string
}
type tree struct {
items []tree_entry
}
func parseGitMsg(data <-chan byte) (gitMsg, error) {
var id []byte = make([]byte, 64)
var msgType []byte = make([]byte, 16)
var size int
pos := 0
for c := <-data; c != ' '; c = <-data {
if (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') {
id[pos] = c
pos++
} else {
return gitMsg{}, errors.New("Invalid character during object hash parse")
}
}
id = id[:pos]
pos = 0
for c := <-data; c != ' '; c = <-data {
if c >= 'a' && c <= 'z' {
msgType[pos] = c
pos++
} else {
return gitMsg{}, errors.New("Invalid character during object type parse")
}
}
msgType = msgType[:pos]
switch string(msgType) {
case "commit", "tree":
break
default:
return gitMsg{}, fmt.Errorf("Invalid object type: '%s'", string(msgType))
}
for c := <-data; c != '\000'; c = <-data {
if c >= '0' && c <= '9' {
size = size*10 + (int(c) - '0')
} else {
return gitMsg{}, errors.New("Invalid character during commit size parse")
}
}
return gitMsg{
hash: string(id[:]),
itemType: string(msgType),
size: size,
}, nil
}
func parseGitCommitHdr(data <-chan byte) ([2]string, error) {
hdr := make([]byte, 0, 60)
val := make([]byte, 0, 1000)
c := <-data
if c != '\n' { // end of header marker
for ; c != ' '; c = <-data {
hdr = append(hdr, c)
}
for c := <-data; c != '\n'; c = <-data {
val = append(val, c)
}
}
return [2]string{string(hdr), string(val)}, nil
}
func parseGitCommitMsg(data <-chan byte, l int) (string, error) {
msg := make([]byte, 0, l)
for c := <-data; c != '\x00'; c = <-data {
msg = append(msg, c)
}
return string(msg), nil
}
func parseGitCommit(data <-chan byte) (commit, error) {
hdr, err := parseGitMsg(data)
if err != nil {
return commit{}, err
} else if hdr.itemType != "commit" {
return commit{}, fmt.Errorf("expected commit but parsed %s", hdr.itemType)
}
var c commit
l := hdr.size
for {
hdr, err := parseGitCommitHdr(data)
if err != nil {
return commit{}, nil
}
if len(hdr[0])+len(hdr[1]) == 0 { // hdr end marker
break
}
switch hdr[0] {
case "tree":
c.Tree = hdr[1]
}
l -= len(hdr[0]) + len(hdr[1]) + 2
}
l--
c.Msg, err = parseGitCommitMsg(data, l)
return c, err
}
func parseTreeEntry(data <-chan byte) (tree_entry, error) {
var e tree_entry
return e, nil
}
func parseGitTree(data <-chan byte) (tree, error) {
return tree{}, nil
}
func (e *RequestHandler) GitSubmoduleCommitId(cwd, packageName, headId string) (string, bool) {
if e.Error != nil {
return "", false
}
cmd := exec.Command("/usr/bin/git", "cat-file", "--batch")
cmd.Dir = filepath.Join(e.GitPath, cwd)
cmd.Stdout = writerFunc(func(p []byte) (int, error) {
return len(p), nil
})
data_in, data_out := ChanIO{make(chan byte, 256)}, ChanIO{make(chan byte, 70)}
var commitId string
var foundLock sync.Mutex
return "", false
foundLock.Lock()
go func() {
defer foundLock.Unlock()
defer close(data_out.ch)
data_out.Write([]byte("HEAD\n"))
c, err := parseGitCommit(data_in.ch)
if err != nil {
e.Error = err
e.LogError("Error parsing git commit: %v", err)
return
}
data_out.Write([]byte(c.Tree))
data_out.ch <- '\n'
tree, err := parseGitTree(data_in.ch)
if err != nil {
e.Error = err
e.LogError("Error parsing git tree: %v", err)
return
}
for _, te := range tree.items {
if te.name == packageName {
commitId = te.hash
return
}
}
}()
cmd := exec.Command("/usr/bin/git", "cat-file", "--batch", "-Z")
cmd.Dir = filepath.Join(e.GitPath, cwd)
cmd.Stdout = &data_in
cmd.Stdin = &data_out
e.Log("command run: %v", cmd.Run())
foundLock.Lock()
return commitId, len(commitId) == len(headId)
}