From 20be99b287c65aab9a88821dc6bcbb45b0f58967 Mon Sep 17 00:00:00 2001 From: wonder Date: Sun, 21 Jun 2026 14:31:33 +0800 Subject: [PATCH] feat: add git pull to update cached repositories from remote MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace dead FetchRemote with PullRepo (fetch + merge) in services/git.go - Add POST /api/repos/:id/pull endpoint with DB size/last_used update - Add '更新' button next to each cached repo in the index page --- handlers/repos.go | 35 +++++++++++++++++++++++++++ main.go | 1 + services/git.go | 48 +++++++++++++++++++++++++++++++------- templates/pages/index.html | 12 +++++++--- 4 files changed, 85 insertions(+), 11 deletions(-) diff --git a/handlers/repos.go b/handlers/repos.go index 38f0faa..daeb66f 100644 --- a/handlers/repos.go +++ b/handlers/repos.go @@ -140,6 +140,41 @@ func (h *ReposHandler) CleanupRepos(c *gin.Context) { c.JSON(http.StatusOK, result) } +// PullRepo handles POST /api/repos/:id/pull — fetches and merges latest changes. +func (h *ReposHandler) PullRepo(c *gin.Context) { + user := GetCurrentUser(c) + if user == nil { + c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthenticated"}) + return + } + + id := c.Param("id") + var localPath string + err := h.db.QueryRow(`SELECT local_path FROM repositories WHERE id = ? AND user_id = ?`, id, user.ID).Scan(&localPath) + if err == sql.ErrNoRows { + c.JSON(http.StatusNotFound, gin.H{"error": "repository not found"}) + return + } + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + result, err := services.PullRepo(localPath) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + h.db.Exec(`UPDATE repositories SET size_bytes = ?, last_used = NOW() WHERE id = ?`, result.SizeBytes, id) + + c.JSON(http.StatusOK, gin.H{ + "ok": true, + "repo_id": id, + "size_bytes": result.SizeBytes, + }) +} + // CloneRepo handles POST /api/repos with SSE progress events. func (h *ReposHandler) CloneRepo(c *gin.Context) { user := GetCurrentUser(c) diff --git a/main.go b/main.go index 41679b6..bfd6457 100644 --- a/main.go +++ b/main.go @@ -114,6 +114,7 @@ func main() { r.POST("/api/repos", authMw, repos.CloneRepo) r.DELETE("/api/repos/:id", authMw, repos.DeleteRepo) r.POST("/api/repos/:id/cleanup", authMw, repos.CleanupRepos) + r.POST("/api/repos/:id/pull", authMw, repos.PullRepo) r.GET("/api/repos/:id/graph", authMw, repos.GetGraph) r.GET("/api/repos/:id/refs", authMw, repos.GetRefs) r.GET("/api/repos/:id/commits", authMw, repos.GetCommits) diff --git a/services/git.go b/services/git.go index 9ff11ff..56f40a4 100644 --- a/services/git.go +++ b/services/git.go @@ -441,19 +441,51 @@ func GetBranchCommits(repoPath, branchName string, maxCommits int) ([]CommitInfo return GetCommitLog(repo, branchName, maxCommits) } -// FetchRemote fetches latest changes for a repo. -func FetchRemote(repoPath string) error { +// PullRepo fetches and merges latest changes from origin into the current branch. +func PullRepo(repoPath string) (*CloneResult, error) { repo, err := OpenRepo(repoPath) if err != nil { - return err + return nil, err } -remote, err := repo.Remote("origin") + + w, err := repo.Worktree() if err != nil { - return err + return nil, fmt.Errorf("worktree: %w", err) } - return remote.Fetch(&git.FetchOptions{ - Force: true, - }) + + err = w.Pull(&git.PullOptions{}) + if err != nil && err != git.NoErrAlreadyUpToDate { + return nil, fmt.Errorf("git pull: %w", err) + } + + result := &CloneResult{RepoPath: repoPath} + + iter, err := repo.CommitObjects() + if err == nil { + _ = iter.ForEach(func(c *object.Commit) error { + result.CommitNum++ + return nil + }) + } + + branches, err := repo.Branches() + if err == nil { + _ = branches.ForEach(func(ref *plumbing.Reference) error { + result.Branches = append(result.Branches, ref.Name().Short()) + return nil + }) + } + + tags, err := repo.Tags() + if err == nil { + _ = tags.ForEach(func(ref *plumbing.Reference) error { + result.Tags = append(result.Tags, ref.Name().Short()) + return nil + }) + } + + result.SizeBytes = dirSize(repoPath) + return result, nil } // dirSize returns the total size of files in a directory. diff --git a/templates/pages/index.html b/templates/pages/index.html index c85a510..914c463 100644 --- a/templates/pages/index.html +++ b/templates/pages/index.html @@ -52,9 +52,15 @@ {{.URL}}

最后使用: {{.LastUsed}}

- +
+ + +
{{end}}