Monorepo for Tangled tangled.org
2

Configure Feed

Select the types of activity you want to include in your feed.

knotmirror/xrpc: `cat-file` based `listCommits`

performance improvement for `listCommits`

Signed-off-by: Seongmin Lee <git@boltless.me>

author
Seongmin Lee
committer
Lewis
date (May 29, 2026, 2:50 PM +0300) commit 400819bc parent 08cb1ad1 change-id vnlozxqm
+76 -14
+76 -14
knotmirror/xrpc/git_list_commits.go
··· 1 1 package xrpc 2 2 3 3 import ( 4 + "bytes" 4 5 "context" 5 6 "fmt" 7 + "io" 6 8 "net/http" 9 + "os/exec" 7 10 "strconv" 8 11 9 12 "github.com/bluesky-social/indigo/atproto/atclient" 10 13 "github.com/bluesky-social/indigo/atproto/syntax" 11 - "tangled.org/core/knotserver/git" 14 + "github.com/go-git/go-git/v5/plumbing" 15 + "github.com/go-git/go-git/v5/plumbing/object" 16 + "tangled.org/core/knotmirror/xrpc/gitea" 12 17 "tangled.org/core/types" 13 18 ) 14 19 ··· 61 66 if err != nil { 62 67 return nil, fmt.Errorf("resolving repo did: %w", err) 63 68 } 69 + rev := ref 70 + if rev == "" { 71 + rev = "HEAD" 72 + } 64 73 65 - gr, err := git.Open(repoPath, ref) 74 + // -> []hash 75 + logs, err := func(repoPath, rev string) ([]byte, error) { 76 + out, err := exec.Command( 77 + "git", 78 + "rev-list", 79 + rev, 80 + fmt.Sprintf("--skip=%d", cursor), 81 + fmt.Sprintf("--max-count=%d", limit), 82 + ).Output() 83 + if err != nil { 84 + return nil, err 85 + } 86 + return bytes.TrimSpace(out), nil 87 + }(repoPath, rev) 66 88 if err != nil { 67 - return nil, fmt.Errorf("opening git repo: %w", err) 89 + return nil, fmt.Errorf("reading rev-list: %w", err) 68 90 } 69 91 70 - offset := int(cursor) 71 - 72 - commits, err := gr.Commits(offset, limit) 92 + commits, err := func(repoPath string, logs []byte) ([]*object.Commit, error) { 93 + bw, br, cancel := gitea.CatFileBatch(ctx, repoPath) 94 + defer cancel() 95 + var commits []*object.Commit 96 + for commitId := range bytes.SplitSeq(logs, []byte{'\n'}) { 97 + _, err := bw.Write([]byte(string(commitId) + "\n")) 98 + if err != nil { 99 + return nil, err 100 + } 101 + _, typ, size, err := gitea.ReadBatchLine(br) 102 + if err != nil { 103 + return nil, err 104 + } 105 + if typ != "commit" { 106 + if err := gitea.DiscardFull(br, size+1); err != nil { 107 + return nil, err 108 + } 109 + return nil, fmt.Errorf("unexpected type: %s for commit id: %s", typ, commitId) 110 + } 111 + c, err := gitea.ReadCommit(plumbing.NewHash(string(commitId)), io.LimitReader(br, size)) 112 + if _, err := br.Discard(1); err != nil { 113 + return nil, err 114 + } 115 + commits = append(commits, c) 116 + } 117 + return commits, nil 118 + }(repoPath, logs) 73 119 if err != nil { 74 - return nil, fmt.Errorf("listing git commits: %w", err) 120 + return nil, fmt.Errorf("parsing commits: %w", err) 75 121 } 76 122 77 - tcommits := make([]types.Commit, len(commits)) 78 - for i, c := range commits { 79 - tcommits[i].FromGoGitCommit(c) 123 + // -> total 124 + total, err := func(repoPath, rev string) (int, error) { 125 + out, err := exec.Command( 126 + "git", 127 + "rev-list", 128 + rev, 129 + "--count", 130 + ).Output() 131 + if err != nil { 132 + return 0, err 133 + } 134 + count, err := strconv.Atoi(string(bytes.TrimSpace(out))) 135 + if err != nil { 136 + return 0, err 137 + } 138 + return count, nil 139 + }(repoPath, rev) 140 + if err != nil { 141 + return nil, fmt.Errorf("parsing total commits: %w", err) 80 142 } 81 143 82 - total, err := gr.TotalCommits() 83 - if err != nil { 84 - return nil, fmt.Errorf("counting total commits: %w", err) 144 + tcommits := make([]types.Commit, len(commits)) 145 + for i, commit := range commits { 146 + tcommits[i].FromGoGitCommit(commit) 85 147 } 86 148 87 149 return &types.RepoLogResponse{ 88 150 Commits: tcommits, 89 151 Ref: ref, 90 - Page: (offset / limit) + 1, 152 + Page: (int(cursor) / limit) + 1, 91 153 PerPage: limit, 92 154 Total: total, 93 155 Log: true,