A vibe coded tangled fork which supports pijul.
1package vcs
2
3import (
4 "context"
5 "io"
6
7 "tangled.org/core/knotserver/git"
8)
9
10// gitReadAdapter wraps a git.GitRepo to implement ReadRepo.
11type gitReadAdapter struct {
12 g *git.GitRepo
13}
14
15func newGitReadAdapter(g *git.GitRepo) *gitReadAdapter {
16 return &gitReadAdapter{g: g}
17}
18
19func (a *gitReadAdapter) VCSType() string { return "git" }
20func (a *gitReadAdapter) Path() string { return a.g.Path() }
21
22func (a *gitReadAdapter) History(offset, limit int) ([]HistoryEntry, error) {
23 commits, err := a.g.Commits(offset, limit)
24 if err != nil {
25 return nil, err
26 }
27
28 entries := make([]HistoryEntry, 0, len(commits))
29 for _, c := range commits {
30 parents := make([]string, 0, len(c.ParentHashes))
31 for _, p := range c.ParentHashes {
32 parents = append(parents, p.String())
33 }
34 entries = append(entries, HistoryEntry{
35 Hash: c.Hash.String(),
36 Author: Author{
37 Name: c.Author.Name,
38 Email: c.Author.Email,
39 When: c.Author.When,
40 },
41 Committer: Author{
42 Name: c.Committer.Name,
43 Email: c.Committer.Email,
44 When: c.Committer.When,
45 },
46 Message: c.Message,
47 Timestamp: c.Committer.When,
48 Parents: parents,
49 })
50 }
51 return entries, nil
52}
53
54func (a *gitReadAdapter) TotalHistoryEntries() (int, error) {
55 return a.g.TotalCommits()
56}
57
58func (a *gitReadAdapter) HistoryEntry(hash string) (*HistoryEntry, error) {
59 c, err := a.g.Commit(gitHash(hash))
60 if err != nil {
61 return nil, err
62 }
63
64 parents := make([]string, 0, len(c.ParentHashes))
65 for _, p := range c.ParentHashes {
66 parents = append(parents, p.String())
67 }
68
69 return &HistoryEntry{
70 Hash: c.Hash.String(),
71 Author: Author{
72 Name: c.Author.Name,
73 Email: c.Author.Email,
74 When: c.Author.When,
75 },
76 Committer: Author{
77 Name: c.Committer.Name,
78 Email: c.Committer.Email,
79 When: c.Committer.When,
80 },
81 Message: c.Message,
82 Timestamp: c.Committer.When,
83 Parents: parents,
84 }, nil
85}
86
87func (a *gitReadAdapter) Branches(opts *PaginationOpts) ([]BranchInfo, error) {
88 var gitOpts *git.BranchesOptions
89 if opts != nil {
90 gitOpts = &git.BranchesOptions{
91 Limit: opts.Limit,
92 Offset: opts.Offset,
93 }
94 }
95
96 branches, err := a.g.Branches(gitOpts)
97 if err != nil {
98 return nil, err
99 }
100
101 infos := make([]BranchInfo, 0, len(branches))
102 for _, b := range branches {
103 info := BranchInfo{
104 Name: b.Name,
105 Hash: b.Hash,
106 IsDefault: b.IsDefault,
107 }
108 if b.Commit != nil {
109 info.LatestEntry = &HistoryEntry{
110 Hash: b.Commit.Hash.String(),
111 Author: Author{
112 Name: b.Commit.Author.Name,
113 Email: b.Commit.Author.Email,
114 When: b.Commit.Author.When,
115 },
116 Committer: Author{
117 Name: b.Commit.Committer.Name,
118 Email: b.Commit.Committer.Email,
119 When: b.Commit.Committer.When,
120 },
121 Message: b.Commit.Message,
122 Timestamp: b.Commit.Committer.When,
123 }
124 }
125 infos = append(infos, info)
126 }
127 return infos, nil
128}
129
130func (a *gitReadAdapter) DefaultBranch() (string, error) {
131 return a.g.FindMainBranch()
132}
133
134func (a *gitReadAdapter) FileTree(ctx context.Context, path string) ([]TreeEntry, error) {
135 files, err := a.g.FileTree(ctx, path)
136 if err != nil {
137 return nil, err
138 }
139
140 entries := make([]TreeEntry, 0, len(files))
141 for _, f := range files {
142 entry := TreeEntry{
143 Name: f.Name,
144 Mode: f.Mode,
145 Size: f.Size,
146 }
147 if f.LastCommit != nil {
148 entry.LastCommit = &LastCommitInfo{
149 Hash: f.LastCommit.Hash.String(),
150 Message: f.LastCommit.Message,
151 When: f.LastCommit.When,
152 }
153 }
154 entries = append(entries, entry)
155 }
156 return entries, nil
157}
158
159func (a *gitReadAdapter) FileContentN(path string, cap int64) ([]byte, error) {
160 return a.g.FileContentN(path, cap)
161}
162
163func (a *gitReadAdapter) RawContent(path string) ([]byte, error) {
164 return a.g.RawContent(path)
165}
166
167func (a *gitReadAdapter) WriteTar(w io.Writer, prefix string) error {
168 return a.g.WriteTar(w, prefix)
169}
170
171func (a *gitReadAdapter) Tags(opts *PaginationOpts) ([]TagInfo, error) {
172 var gitOpts *git.TagsOptions
173 if opts != nil {
174 gitOpts = &git.TagsOptions{
175 Limit: opts.Limit,
176 Offset: opts.Offset,
177 }
178 }
179
180 tags, err := a.g.Tags(gitOpts)
181 if err != nil {
182 return nil, err
183 }
184
185 infos := make([]TagInfo, 0, len(tags))
186 for _, t := range tags {
187 info := TagInfo{
188 Name: t.Name,
189 Hash: t.Hash.String(),
190 Message: t.Message,
191 Target: t.Target.String(),
192 }
193 if t.Tagger.Name != "" {
194 info.Tagger = &Author{
195 Name: t.Tagger.Name,
196 Email: t.Tagger.Email,
197 When: t.Tagger.When,
198 }
199 }
200 infos = append(infos, info)
201 }
202 return infos, nil
203}
204
205// gitMutableAdapter wraps a git.GitRepo to implement MutableRepo.
206type gitMutableAdapter struct {
207 *gitReadAdapter
208}
209
210func newGitMutableAdapter(g *git.GitRepo) *gitMutableAdapter {
211 return &gitMutableAdapter{gitReadAdapter: newGitReadAdapter(g)}
212}
213
214func (a *gitMutableAdapter) SetDefaultBranch(name string) error {
215 return a.g.SetDefaultBranch(name)
216}
217
218func (a *gitMutableAdapter) DeleteBranch(name string) error {
219 return a.g.DeleteBranch(name)
220}
221
222// Git returns the underlying *git.GitRepo for git-specific operations
223// (diff, merge, format-patch, etc.) that don't belong in the VCS interface.
224func (a *gitReadAdapter) Git() *git.GitRepo {
225 return a.g
226}