forked from tangled.org/core
this repo has no description

knotserver: git: improve performance of commit listing

RepoIndex and RepoLog should no longer take longer on bigger
repositories. all endpoints are still backwards compatible.

Signed-off-by: oppiliappan <me@oppi.li>

Changed files
+64 -29
knotserver
+47 -7
knotserver/git/git.go
···
return &g, nil
}
-
func (g *GitRepo) Commits() ([]*object.Commit, error) {
-
ci, err := g.r.Log(&git.LogOptions{From: g.h})
+
func (g *GitRepo) Commits(offset, limit int) ([]*object.Commit, error) {
+
commits := []*object.Commit{}
+
+
output, err := g.revList(
+
fmt.Sprintf("--skip=%d", offset),
+
fmt.Sprintf("--max-count=%d", limit),
+
)
if err != nil {
return nil, fmt.Errorf("commits from ref: %w", err)
}
-
commits := []*object.Commit{}
-
ci.ForEach(func(c *object.Commit) error {
-
commits = append(commits, c)
-
return nil
-
})
+
lines := strings.Split(strings.TrimSpace(string(output)), "\n")
+
if len(lines) == 1 && lines[0] == "" {
+
return commits, nil
+
}
+
+
for _, item := range lines {
+
obj, err := g.r.CommitObject(plumbing.NewHash(item))
+
if err != nil {
+
continue
+
}
+
commits = append(commits, obj)
+
}
return commits, nil
+
}
+
+
func (g *GitRepo) TotalCommits() (int, error) {
+
output, err := g.revList(
+
fmt.Sprintf("--count"),
+
)
+
if err != nil {
+
return 0, fmt.Errorf("failed to run rev-list", err)
+
}
+
+
count, err := strconv.Atoi(strings.TrimSpace(string(output)))
+
if err != nil {
+
return 0, err
+
}
+
+
return count, nil
+
}
+
+
func (g *GitRepo) revList(extraArgs ...string) ([]byte, error) {
+
var args []string
+
args = append(args, "rev-list")
+
args = append(args, g.h.String())
+
args = append(args, extraArgs...)
+
+
cmd := exec.Command("git", args...)
+
cmd.Dir = g.path
+
+
return cmd.Output()
}
func (g *GitRepo) Commit(h plumbing.Hash) (*object.Commit, error) {
+17 -22
knotserver/routes.go
···
}
}
-
commits, err := gr.Commits()
-
total := len(commits)
+
commits, err := gr.Commits(0, 60) // a good preview of commits in this repo
if err != nil {
writeError(w, err.Error(), http.StatusInternalServerError)
l.Error("fetching commits", "error", err.Error())
return
}
-
if len(commits) > 10 {
-
commits = commits[:10]
+
+
total, err := gr.TotalCommits()
+
if err != nil {
+
writeError(w, err.Error(), http.StatusInternalServerError)
+
l.Error("fetching commits", "error", err.Error())
+
return
}
branches, err := gr.Branches()
···
return
}
-
commits, err := gr.Commits()
-
if err != nil {
-
writeError(w, err.Error(), http.StatusInternalServerError)
-
l.Error("fetching commits", "error", err.Error())
-
return
-
}
-
// Get page parameters
page := 1
pageSize := 30
···
}
}
-
// Calculate pagination
-
start := (page - 1) * pageSize
-
end := start + pageSize
-
total := len(commits)
+
// convert to offset/limit
+
offset := (page - 1) * pageSize
+
limit := pageSize
-
if start >= total {
-
commits = []*object.Commit{}
-
} else {
-
if end > total {
-
end = total
-
}
-
commits = commits[start:end]
+
commits, err := gr.Commits(offset, limit)
+
if err != nil {
+
writeError(w, err.Error(), http.StatusInternalServerError)
+
l.Error("fetching commits", "error", err.Error())
+
return
}
+
+
total := len(commits)
resp := types.RepoLogResponse{
Commits: commits,