Files
byre/internal/server/changes.go
T

124 lines
3.1 KiB
Go
Raw Normal View History

2026-10-14 19:35:34 -07:00
package server
import (
"errors"
"fmt"
"net/http"
"regexp"
"g.fc.run/misc/byre/internal/api"
"g.fc.run/misc/byre/internal/git"
)
var objectID = regexp.MustCompile(`^[0-9a-f]{40}([0-9a-f]{24})?$`)
func (s *Server) GetTaskChanges(w http.ResponseWriter, r *http.Request, taskID string, params api.GetTaskChangesParams) {
t, err := s.store.GetTask(r.Context(), taskID)
if err != nil {
writeStoreError(w, err)
return
}
if t.Status != api.TaskStatusActive {
writeError(w, http.StatusConflict, fmt.Errorf("task %s is %s and has no worktree", t.Id, t.Status))
return
}
p, err := s.store.GetProject(r.Context(), t.ProjectId)
if err != nil {
writeStoreError(w, err)
return
}
2026-10-15 00:08:32 -07:00
step := params.Step != nil && *params.Step
if params.Snapshot != nil {
if params.Since != nil {
writeError(w, http.StatusBadRequest, errors.New("since and snapshot can't be used together"))
return
}
s.snapshotChanges(w, r, t, p, *params.Snapshot, step)
return
}
if step {
writeError(w, http.StatusBadRequest, errors.New("step needs snapshot"))
return
}
2026-10-14 19:35:34 -07:00
since := ""
if params.Since != nil {
since = *params.Since
if !objectID.MatchString(since) {
writeError(w, http.StatusBadRequest, errors.New("since must be a full object id"))
return
}
}
changes, err := git.Changes(t.WorktreePath, p.DefaultBranch, since)
if err != nil {
writeError(w, http.StatusInternalServerError, err)
return
}
writeJSON(w, http.StatusOK, changes)
}
2026-10-15 00:08:32 -07:00
func (s *Server) snapshotChanges(w http.ResponseWriter, r *http.Request, t api.Task, p api.Project, n int, step bool) {
snap, err := s.store.GetSnapshot(r.Context(), t.Id, n)
if err != nil {
writeStoreError(w, err)
return
}
since := ""
if step {
since, err = s.stepBase(r, t, snap)
if err != nil {
writeStoreError(w, err)
return
}
}
changes, err := git.Compare(t.WorktreePath, p.DefaultBranch, snap.CommitSha, snap.TreeSha, since)
if err != nil {
writeError(w, http.StatusInternalServerError, err)
return
}
writeJSON(w, http.StatusOK, changes)
}
func (s *Server) stepBase(r *http.Request, t api.Task, snap api.Snapshot) (string, error) {
if snap.N == 1 {
2026-10-15 21:56:59 -07:00
return "", nil
2026-10-15 00:08:32 -07:00
}
prev, err := s.store.GetSnapshot(r.Context(), t.Id, snap.N-1)
if err != nil {
return "", err
}
return prev.TreeSha, nil
}
2026-10-14 19:35:34 -07:00
func (s *Server) GetTaskDiff(w http.ResponseWriter, r *http.Request, taskID string, params api.GetTaskDiffParams) {
t, err := s.store.GetTask(r.Context(), taskID)
if err != nil {
writeStoreError(w, err)
return
}
if t.Status != api.TaskStatusActive {
writeError(w, http.StatusConflict, fmt.Errorf("task %s is %s and has no worktree", t.Id, t.Status))
return
}
if !objectID.MatchString(params.Base) || !objectID.MatchString(params.Tree) {
writeError(w, http.StatusBadRequest, errors.New("base and tree must be full object ids"))
return
}
oldPath := ""
if params.OldPath != nil {
oldPath = *params.OldPath
}
patch, err := git.FileDiff(t.WorktreePath, params.Base, params.Tree, params.Path, oldPath)
if err != nil {
writeError(w, http.StatusInternalServerError, err)
return
}
writeJSON(w, http.StatusOK, api.FileDiff{Path: params.Path, Patch: patch})
}