readme file is now rendering in repo. can now view files and edit them. can switch between branches with dropdown menu

This commit is contained in:
2026-05-07 11:28:06 +02:00
parent 779a1fdb82
commit dad82a79de
41 changed files with 2463 additions and 141 deletions
+98 -10
View File
@@ -3,6 +3,7 @@ package git
import (
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
@@ -117,14 +118,17 @@ func Log(repoPath, branch string, limit int) ([]Commit, error) {
}
type TreeEntry struct {
Mode string `json:"mode"`
Type string `json:"type"`
Hash string `json:"hash"`
Name string `json:"name"`
Mode string `json:"mode"`
Type string `json:"type"`
Hash string `json:"hash"`
Name string `json:"name"`
Size int64 `json:"size"`
CommitHash string `json:"commitHash"`
CommitMsg string `json:"commitMsg"`
CommitDate string `json:"commitDate"`
}
func TreeLS(repoPath, ref, subPath string) ([]TreeEntry, error) {
// Short-circuit for repos with no commits yet.
if IsEmpty(repoPath) {
return nil, nil
}
@@ -132,7 +136,8 @@ func TreeLS(repoPath, ref, subPath string) ([]TreeEntry, error) {
if subPath != "" {
treeRef = ref + ":" + subPath
}
out, err := run(repoPath, "ls-tree", treeRef)
// -l adds size column: <mode> SP <type> SP <hash> SP <size> TAB <name>
out, err := run(repoPath, "ls-tree", "-l", treeRef)
if err != nil {
return nil, err
}
@@ -142,26 +147,109 @@ func TreeLS(repoPath, ref, subPath string) ([]TreeEntry, error) {
if line == "" {
continue
}
// format: <mode> SP <type> SP <hash> TAB <name>
tabIdx := strings.Index(line, "\t")
if tabIdx < 0 {
continue
}
name := line[tabIdx+1:]
fields := strings.Fields(line[:tabIdx])
if len(fields) != 3 {
if len(fields) < 4 {
continue
}
entries = append(entries, TreeEntry{
e := TreeEntry{
Mode: fields[0],
Type: fields[1],
Hash: fields[2],
Name: name,
})
}
if fields[3] != "-" {
fmt.Sscanf(fields[3], "%d", &e.Size)
}
entries = append(entries, e)
}
// Fetch last commit info for each entry.
for i, e := range entries {
filePath := e.Name
if subPath != "" {
filePath = subPath + "/" + e.Name
}
commitOut, err := run(repoPath, "log", "-1", "--format=%h\x1f%s\x1f%aI", "--", filePath)
if err == nil {
parts := strings.SplitN(strings.TrimSpace(string(commitOut)), "\x1f", 3)
if len(parts) == 3 {
entries[i].CommitHash = parts[0]
entries[i].CommitMsg = parts[1]
entries[i].CommitDate = parts[2]
}
}
}
return entries, nil
}
// WriteFile writes content to filePath on branch inside a temporary worktree,
// then commits with the given author and message. Uses git plumbing directly
// so it works on bare repositories.
func WriteFile(repoPath, branch, filePath, content, authorName, authorEmail, message string) error {
clean := filepath.Clean(filepath.FromSlash(filePath))
if strings.HasPrefix(clean, "..") || filepath.IsAbs(clean) {
return errors.New("invalid file path")
}
tmpDir, err := os.MkdirTemp("", "fb-edit-*")
if err != nil {
return fmt.Errorf("mktemp: %w", err)
}
baseEnv := []string{"GIT_TERMINAL_PROMPT=0", "HOME=/tmp"}
authorEnv := append(baseEnv,
"GIT_AUTHOR_NAME="+authorName,
"GIT_AUTHOR_EMAIL="+authorEmail,
"GIT_COMMITTER_NAME="+authorName,
"GIT_COMMITTER_EMAIL="+authorEmail,
)
addWt := exec.Command("git", "worktree", "add", "--force", tmpDir, branch)
addWt.Dir = filepath.Clean(repoPath)
addWt.Env = baseEnv
if out, err := addWt.CombinedOutput(); err != nil {
os.RemoveAll(tmpDir)
return fmt.Errorf("worktree add: %w: %s", err, out)
}
defer func() {
rmWt := exec.Command("git", "worktree", "remove", "--force", tmpDir)
rmWt.Dir = filepath.Clean(repoPath)
rmWt.Env = baseEnv
rmWt.Run()
os.RemoveAll(tmpDir)
}()
fullPath := filepath.Join(tmpDir, clean)
if err := os.MkdirAll(filepath.Dir(fullPath), 0755); err != nil {
return fmt.Errorf("mkdirall: %w", err)
}
if err := os.WriteFile(fullPath, []byte(content), 0644); err != nil {
return fmt.Errorf("writefile: %w", err)
}
addC := exec.Command("git", "add", clean)
addC.Dir = tmpDir
addC.Env = authorEnv
if out, err := addC.CombinedOutput(); err != nil {
return fmt.Errorf("git add: %w: %s", err, out)
}
commitC := exec.Command("git", "commit", "-m", message)
commitC.Dir = tmpDir
commitC.Env = authorEnv
if out, err := commitC.CombinedOutput(); err != nil {
return fmt.Errorf("git commit: %w: %s", err, out)
}
return nil
}
type Branch struct {
Name string `json:"name"`
}