Full project scaffold: multi-stage Dockerfile (ARM64/AMD64), AsciiDoc↔TipTap bridge, Setup Wizard, CodeMirror source editor, Git-backed storage layer, LDAP+JWT auth skeleton, Tailwind mobile-first layout, and VS Code build/push tasks targeting registry at 192.168.0.19:5000. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
105 lines
2.4 KiB
Go
105 lines
2.4 KiB
Go
package git
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
// Repo wraps a bare directory that is a Git repository.
|
|
type Repo struct {
|
|
root string
|
|
}
|
|
|
|
// Open returns a Repo for an existing directory, initialising Git if needed.
|
|
func Open(root string) (*Repo, error) {
|
|
r := &Repo{root: root}
|
|
if err := r.initIfNeeded(); err != nil {
|
|
return nil, err
|
|
}
|
|
return r, nil
|
|
}
|
|
|
|
func (r *Repo) initIfNeeded() error {
|
|
out, _ := r.run("rev-parse", "--is-inside-work-tree")
|
|
if strings.TrimSpace(out) == "true" {
|
|
return nil
|
|
}
|
|
_, err := r.run("init")
|
|
return err
|
|
}
|
|
|
|
// Commit writes data to path and creates a Git commit authored by author.
|
|
func (r *Repo) Commit(path, content, message, author, email string) error {
|
|
full := filepath.Join(r.root, path)
|
|
if err := writeFile(full, content); err != nil {
|
|
return err
|
|
}
|
|
if _, err := r.run("add", path); err != nil {
|
|
return err
|
|
}
|
|
_, err := r.run(
|
|
"-c", fmt.Sprintf("user.name=%s", author),
|
|
"-c", fmt.Sprintf("user.email=%s", email),
|
|
"commit", "-m", message,
|
|
)
|
|
return err
|
|
}
|
|
|
|
// Log returns the commit history for a file.
|
|
func (r *Repo) Log(path string) ([]LogEntry, error) {
|
|
out, err := r.run("log", "--format=%H|%an|%ae|%ai|%s", "--", path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var entries []LogEntry
|
|
for _, line := range strings.Split(strings.TrimSpace(out), "\n") {
|
|
if line == "" {
|
|
continue
|
|
}
|
|
parts := strings.SplitN(line, "|", 5)
|
|
if len(parts) == 5 {
|
|
entries = append(entries, LogEntry{
|
|
Hash: parts[0],
|
|
Author: parts[1],
|
|
Email: parts[2],
|
|
Date: parts[3],
|
|
Subject: parts[4],
|
|
})
|
|
}
|
|
}
|
|
return entries, nil
|
|
}
|
|
|
|
// Diff returns the unified diff between two commits for a file.
|
|
func (r *Repo) Diff(path, fromHash, toHash string) (string, error) {
|
|
out, err := r.run("diff", fromHash, toHash, "--", path)
|
|
return out, err
|
|
}
|
|
|
|
// Show returns the file content at a specific commit.
|
|
func (r *Repo) Show(hash, path string) (string, error) {
|
|
return r.run("show", fmt.Sprintf("%s:%s", hash, path))
|
|
}
|
|
|
|
type LogEntry struct {
|
|
Hash string
|
|
Author string
|
|
Email string
|
|
Date string
|
|
Subject string
|
|
}
|
|
|
|
func (r *Repo) run(args ...string) (string, error) {
|
|
cmd := exec.Command("git", append([]string{"-C", r.root}, args...)...)
|
|
var stdout, stderr bytes.Buffer
|
|
cmd.Stdout = &stdout
|
|
cmd.Stderr = &stderr
|
|
if err := cmd.Run(); err != nil {
|
|
return "", fmt.Errorf("git %v: %w — %s", args, err, stderr.String())
|
|
}
|
|
return stdout.String(), nil
|
|
}
|