You've already forked agentic-coding-workflow
功能补齐、查看git提交历史
This commit is contained in:
@@ -77,12 +77,12 @@ type Workspace struct {
|
||||
|
||||
// Worktree 表示一个支线 worktree(不含 main_path)。
|
||||
type Worktree struct {
|
||||
ID uuid.UUID
|
||||
WorkspaceID uuid.UUID
|
||||
Branch string
|
||||
Path string
|
||||
Status WorktreeStatus
|
||||
ActiveHolder string // "user:<uid>" / "session:<sid>" / 空
|
||||
ID uuid.UUID
|
||||
WorkspaceID uuid.UUID
|
||||
Branch string
|
||||
Path string
|
||||
Status WorktreeStatus
|
||||
ActiveHolder string // "user:<uid>" / "session:<sid>" / 空
|
||||
AcquiredAt *time.Time
|
||||
LastUsedAt time.Time
|
||||
PruneWarningAt *time.Time
|
||||
@@ -167,10 +167,12 @@ type GitOpsService interface {
|
||||
StatusOnMain(ctx context.Context, c Caller, wsID uuid.UUID) ([]git.FileStatus, error)
|
||||
CommitOnMain(ctx context.Context, c Caller, wsID uuid.UUID, in CommitInput) (string, error)
|
||||
PushOnMain(ctx context.Context, c Caller, wsID uuid.UUID) error
|
||||
LogOnMain(ctx context.Context, c Caller, wsID uuid.UUID, n int) ([]git.Commit, error)
|
||||
|
||||
StatusOnWorktree(ctx context.Context, c Caller, wtID uuid.UUID) ([]git.FileStatus, error)
|
||||
CommitOnWorktree(ctx context.Context, c Caller, wtID uuid.UUID, in CommitInput) (string, error)
|
||||
PushOnWorktree(ctx context.Context, c Caller, wtID uuid.UUID) error
|
||||
LogOnWorktree(ctx context.Context, c Caller, wtID uuid.UUID, n int) ([]git.Commit, error)
|
||||
}
|
||||
|
||||
// ===== Input DTO =====
|
||||
|
||||
@@ -119,3 +119,15 @@ type commitResp struct {
|
||||
type branchListResp struct {
|
||||
Branches []string `json:"branches"`
|
||||
}
|
||||
|
||||
type commitLogResp struct {
|
||||
Hash string `json:"hash"`
|
||||
Author string `json:"author"`
|
||||
Email string `json:"email"`
|
||||
Date time.Time `json:"date"`
|
||||
Subject string `json:"subject"`
|
||||
}
|
||||
|
||||
func toCommitLogResp(c git.Commit) commitLogResp {
|
||||
return commitLogResp{Hash: c.Hash, Author: c.Author, Email: c.Email, Date: c.Date, Subject: c.Subject}
|
||||
}
|
||||
|
||||
@@ -79,6 +79,18 @@ func (s *gitOpsService) PushOnMain(ctx context.Context, c Caller, wsID uuid.UUID
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *gitOpsService) LogOnMain(ctx context.Context, c Caller, wsID uuid.UUID, n int) ([]git.Commit, error) {
|
||||
ws, err := s.guardWorkspaceRead(ctx, c, wsID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
commits, err := s.gitr.Log(ctx, ws.MainPath, n)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err, errs.CodeGitCmdFailed, "git log")
|
||||
}
|
||||
return commits, nil
|
||||
}
|
||||
|
||||
// ===== Worktree =====
|
||||
|
||||
func (s *gitOpsService) StatusOnWorktree(ctx context.Context, c Caller, wtID uuid.UUID) ([]git.FileStatus, error) {
|
||||
@@ -133,6 +145,18 @@ func (s *gitOpsService) PushOnWorktree(ctx context.Context, c Caller, wtID uuid.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *gitOpsService) LogOnWorktree(ctx context.Context, c Caller, wtID uuid.UUID, n int) ([]git.Commit, error) {
|
||||
wt, _, err := s.guardWorktreeRead(ctx, c, wtID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
commits, err := s.gitr.Log(ctx, wt.Path, n)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(err, errs.CodeGitCmdFailed, "git log")
|
||||
}
|
||||
return commits, nil
|
||||
}
|
||||
|
||||
// ===== guards =====
|
||||
|
||||
func (s *gitOpsService) guardWorkspaceRead(ctx context.Context, c Caller, wsID uuid.UUID) (*Workspace, error) {
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/google/uuid"
|
||||
@@ -59,6 +60,7 @@ func (h *Handler) Mount(r chi.Router) {
|
||||
r.Get("/main/status", h.statusOnMain)
|
||||
r.Post("/main/commit", h.commitOnMain)
|
||||
r.Post("/main/push", h.pushOnMain)
|
||||
r.Get("/main/log", h.logOnMain)
|
||||
|
||||
r.Get("/worktrees", h.listWorktrees)
|
||||
r.Post("/worktrees", h.createWorktree)
|
||||
@@ -71,6 +73,7 @@ func (h *Handler) Mount(r chi.Router) {
|
||||
r.Get("/status", h.statusOnWorktree)
|
||||
r.Post("/commit", h.commitOnWorktree)
|
||||
r.Post("/push", h.pushOnWorktree)
|
||||
r.Get("/log", h.logOnWorktree)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -536,3 +539,59 @@ func (h *Handler) pushGeneric(w http.ResponseWriter, r *http.Request, onMain boo
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func (h *Handler) logOnMain(w http.ResponseWriter, r *http.Request) {
|
||||
h.logGeneric(w, r, true)
|
||||
}
|
||||
|
||||
func (h *Handler) logOnWorktree(w http.ResponseWriter, r *http.Request) {
|
||||
h.logGeneric(w, r, false)
|
||||
}
|
||||
|
||||
func (h *Handler) logGeneric(w http.ResponseWriter, r *http.Request, onMain bool) {
|
||||
c, err := h.caller(r)
|
||||
if err != nil {
|
||||
writeErr(w, r, err)
|
||||
return
|
||||
}
|
||||
param := "wsID"
|
||||
if !onMain {
|
||||
param = "wtID"
|
||||
}
|
||||
id, err := parseUUID(chi.URLParam(r, param))
|
||||
if err != nil {
|
||||
writeErr(w, r, err)
|
||||
return
|
||||
}
|
||||
n := parseLogN(r)
|
||||
var commits []git.Commit
|
||||
if onMain {
|
||||
commits, err = h.gop.LogOnMain(r.Context(), c, id, n)
|
||||
} else {
|
||||
commits, err = h.gop.LogOnWorktree(r.Context(), c, id, n)
|
||||
}
|
||||
if err != nil {
|
||||
writeErr(w, r, err)
|
||||
return
|
||||
}
|
||||
resp := make([]commitLogResp, 0, len(commits))
|
||||
for _, cc := range commits {
|
||||
resp = append(resp, toCommitLogResp(cc))
|
||||
}
|
||||
writeJSON(w, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func parseLogN(r *http.Request) int {
|
||||
nStr := r.URL.Query().Get("n")
|
||||
if nStr == "" {
|
||||
return 50
|
||||
}
|
||||
n, err := strconv.Atoi(nStr)
|
||||
if err != nil || n <= 0 {
|
||||
return 50
|
||||
}
|
||||
if n > 200 {
|
||||
return 200
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
@@ -226,11 +226,14 @@ func (f *fakeGit) WorktreeRemove(_ context.Context, _, _ string) error
|
||||
func (f *fakeGit) WorktreeList(_ context.Context, _ string) ([]git.Worktree, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (f *fakeGit) FetchRemoteBranch(_ context.Context, _, _ string, _ *git.Credential) error { return nil }
|
||||
func (f *fakeGit) Checkout(_ context.Context, _, _ string) error { return nil }
|
||||
func (f *fakeGit) FetchRemoteBranch(_ context.Context, _, _ string, _ *git.Credential) error {
|
||||
return nil
|
||||
}
|
||||
func (f *fakeGit) Checkout(_ context.Context, _, _ string) error { return nil }
|
||||
func (f *fakeGit) ListRemoteBranches(_ context.Context, _ string, _ *git.Credential) ([]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (f *fakeGit) Log(_ context.Context, _ string, _ int) ([]git.Commit, error) { return nil, nil }
|
||||
|
||||
// trackGit 包装 git.Runner,记录 FetchRemoteBranch / Checkout 调用以供断言。
|
||||
// 嵌入 Runner 接口本身可避免为每个方法重复转发。
|
||||
|
||||
Reference in New Issue
Block a user