Compare commits
2
Commits
846288731f
...
20be99b287
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20be99b287 | ||
|
|
25291a81a3 |
@@ -22,7 +22,7 @@ jobs:
|
||||
|
||||
- name: Write .env
|
||||
env:
|
||||
DEPLOY_ENV: ${{ secrets.DEPLOY_ENV }}
|
||||
DEPLOY_ENV: ${{ variables.DEPLOY_ENV }}
|
||||
run: echo "$DEPLOY_ENV" > .env
|
||||
|
||||
- name: Build & Deploy
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
+40
-8
@@ -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.
|
||||
|
||||
@@ -52,9 +52,15 @@
|
||||
<a href="/repo/{{.ID}}" class="text-sm font-medium text-indigo-600 hover:text-indigo-800">{{.URL}}</a>
|
||||
<p class="text-xs text-gray-400 mt-1">最后使用: {{.LastUsed}}</p>
|
||||
</div>
|
||||
<button hx-delete="/api/repos/{{.ID}}" hx-confirm="确定删除此仓库缓存?"
|
||||
hx-swap="none" hx-on::after-request="window.location.reload()"
|
||||
class="text-xs text-red-500 hover:text-red-700">删除</button>
|
||||
<div class="flex gap-2">
|
||||
<button hx-post="/api/repos/{{.ID}}/pull" hx-swap="none"
|
||||
hx-on::before-request="this.disabled=true;this.textContent='更新中...'"
|
||||
hx-on::after-request="window.location.reload()"
|
||||
class="text-xs text-indigo-500 hover:text-indigo-700">更新</button>
|
||||
<button hx-delete="/api/repos/{{.ID}}" hx-confirm="确定删除此仓库缓存?"
|
||||
hx-swap="none" hx-on::after-request="window.location.reload()"
|
||||
class="text-xs text-red-500 hover:text-red-700">删除</button>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user