phase 2 initial test
This commit is contained in:
@@ -2,6 +2,7 @@ package git
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -16,47 +17,69 @@ func SetRepoRoot(root string) {
|
||||
}
|
||||
|
||||
// run executes a git command inside repoPath with strict safety guarantees:
|
||||
// - repoPath is validated to be under the configured repoRoot
|
||||
// - args are passed as discrete values — never via shell interpolation
|
||||
// - the process inherits only a minimal environment
|
||||
// - repoPath is validated to be under the configured repoRoot (path traversal guard)
|
||||
// - args are passed as discrete values — never via shell interpolation
|
||||
// - the process inherits only a minimal, fixed environment
|
||||
func run(repoPath string, args ...string) ([]byte, error) {
|
||||
clean := filepath.Clean(repoPath)
|
||||
if repoRoot != "" && !strings.HasPrefix(clean, repoRoot+string(filepath.Separator)) && clean != repoRoot {
|
||||
return nil, ErrPathTraversal
|
||||
if repoRoot != "" {
|
||||
root := repoRoot + string(filepath.Separator)
|
||||
if !strings.HasPrefix(clean+string(filepath.Separator), root) && clean != repoRoot {
|
||||
return nil, ErrPathTraversal
|
||||
}
|
||||
}
|
||||
|
||||
cmd := exec.Command("git", args...)
|
||||
cmd.Dir = clean
|
||||
cmd.Env = []string{
|
||||
"GIT_TERMINAL_PROMPT=0",
|
||||
"HOME=" + filepath.Dir(repoRoot), // needed for .gitconfig lookups
|
||||
"HOME=/tmp", // prevent .gitconfig side-effects
|
||||
}
|
||||
return cmd.Output()
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
if exitErr, ok := err.(*exec.ExitError); ok {
|
||||
return nil, fmt.Errorf("git %s: %w: %s", args[0], err, exitErr.Stderr)
|
||||
}
|
||||
return nil, fmt.Errorf("git %s: %w", args[0], err)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func Init(path string) error {
|
||||
_, err := run(path, "init", "--bare")
|
||||
return err
|
||||
// git init --bare works even if the directory doesn't exist yet
|
||||
cmd := exec.Command("git", "init", "--bare", path)
|
||||
cmd.Env = []string{"GIT_TERMINAL_PROMPT=0", "HOME=/tmp"}
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("git init --bare: %w: %s", err, out)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Commit struct {
|
||||
Hash string
|
||||
Author string
|
||||
Message string
|
||||
Date string
|
||||
Hash string `json:"hash"`
|
||||
Author string `json:"author"`
|
||||
Message string `json:"message"`
|
||||
Date string `json:"date"`
|
||||
}
|
||||
|
||||
func Log(repoPath, branch string, limit int) ([]Commit, error) {
|
||||
out, err := run(repoPath, "log", branch,
|
||||
out, err := run(repoPath,
|
||||
"log", branch,
|
||||
"--format=%H\x1f%an\x1f%s\x1f%ci",
|
||||
"--max-count", strings.TrimSpace(string(rune(limit+'0'))),
|
||||
fmt.Sprintf("--max-count=%d", limit),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
raw := strings.TrimSpace(string(out))
|
||||
if raw == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var commits []Commit
|
||||
for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") {
|
||||
parts := strings.Split(line, "\x1f")
|
||||
for _, line := range strings.Split(raw, "\n") {
|
||||
parts := strings.SplitN(line, "\x1f", 4)
|
||||
if len(parts) != 4 {
|
||||
continue
|
||||
}
|
||||
@@ -71,18 +94,22 @@ func Log(repoPath, branch string, limit int) ([]Commit, error) {
|
||||
}
|
||||
|
||||
type TreeEntry struct {
|
||||
Mode string
|
||||
Type string
|
||||
Hash string
|
||||
Name string
|
||||
Mode string `json:"mode"`
|
||||
Type string `json:"type"`
|
||||
Hash string `json:"hash"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
func TreeLS(repoPath, ref, subPath string) ([]TreeEntry, error) {
|
||||
treeRef := ref + ":" + subPath
|
||||
treeRef := ref
|
||||
if subPath != "" {
|
||||
treeRef = ref + ":" + subPath
|
||||
}
|
||||
out, err := run(repoPath, "ls-tree", treeRef)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var entries []TreeEntry
|
||||
for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") {
|
||||
if line == "" {
|
||||
|
||||
Reference in New Issue
Block a user