phase 3 initial completion

This commit is contained in:
2026-05-07 00:22:45 +02:00
parent 5d8662595c
commit ce2aa2c776
19 changed files with 1216 additions and 37 deletions
+25
View File
@@ -169,6 +169,31 @@ func (h *RepoHandler) Commits(w http.ResponseWriter, r *http.Request) {
jsonOK(w, commits)
}
func (h *RepoHandler) Diff(w http.ResponseWriter, r *http.Request) {
repo, ok := h.lookupRepo(w, r)
if !ok {
return
}
base := r.URL.Query().Get("base")
head := r.URL.Query().Get("head")
if base == "" || head == "" {
jsonError(w, "base and head query params are required", http.StatusBadRequest)
return
}
gitdomain.SetRepoRoot(h.cfg.RepoRoot)
diffs, err := gitdomain.Diff(repo.DiskPath, base, head)
if err != nil {
jsonError(w, "could not compute diff", http.StatusInternalServerError)
return
}
if diffs == nil {
diffs = []gitdomain.FileDiff{}
}
jsonOK(w, diffs)
}
// lookupRepo resolves {owner}/{repo} URL params to a DB row, enforcing access.
func (h *RepoHandler) lookupRepo(w http.ResponseWriter, r *http.Request) (*models.Repository, bool) {
ownerName := chi.URLParam(r, "owner")
+1
View File
@@ -77,6 +77,7 @@ func New(cfg *config.Config, engine *xorm.Engine, store sessions.Store, staticFi
r.Get("/tree", repoH.Tree)
r.Get("/blob", repoH.Blob)
r.Get("/commits", repoH.Commits)
r.Get("/diff", repoH.Diff)
r.Route("/pulls", func(r chi.Router) {
r.Get("/", prH.List)
r.With(csrf).Post("/", prH.Create)
+64
View File
@@ -138,3 +138,67 @@ func TreeLS(repoPath, ref, subPath string) ([]TreeEntry, error) {
func BlobCat(repoPath, ref, filePath string) ([]byte, error) {
return run(repoPath, "show", ref+":"+filePath)
}
// FileDiff represents a single changed file in a diff.
type FileDiff struct {
Path string `json:"path"`
OldPath string `json:"oldPath,omitempty"` // set on renames
Additions int `json:"additions"`
Deletions int `json:"deletions"`
Patch string `json:"patch"` // unified diff hunk(s) for this file
}
// Diff returns per-file diffs between two refs.
func Diff(repoPath, base, head string) ([]FileDiff, error) {
out, err := run(repoPath,
"diff", "--unified=5", "--no-color",
"--diff-filter=ACDMRT", // exclude untracked
base+".."+head,
)
if err != nil {
return nil, err
}
return parseUnifiedDiff(string(out)), nil
}
// parseUnifiedDiff splits a multi-file unified diff into per-file FileDiff entries.
func parseUnifiedDiff(raw string) []FileDiff {
var files []FileDiff
var cur *FileDiff
var patchLines []string
commit := func() {
if cur != nil {
cur.Patch = strings.Join(patchLines, "\n")
files = append(files, *cur)
}
}
for _, line := range strings.Split(raw, "\n") {
if strings.HasPrefix(line, "diff --git ") {
commit()
cur = &FileDiff{}
patchLines = nil
continue
}
if cur == nil {
continue
}
switch {
case strings.HasPrefix(line, "--- a/"):
cur.OldPath = strings.TrimPrefix(line, "--- a/")
case strings.HasPrefix(line, "+++ b/"):
cur.Path = strings.TrimPrefix(line, "+++ b/")
case strings.HasPrefix(line, "+") && !strings.HasPrefix(line, "+++"):
cur.Additions++
patchLines = append(patchLines, line)
case strings.HasPrefix(line, "-") && !strings.HasPrefix(line, "---"):
cur.Deletions++
patchLines = append(patchLines, line)
default:
patchLines = append(patchLines, line)
}
}
commit()
return files
}