tangled
alpha
login
or
join now
dzming.li
/
core
0
fork
atom
A vibe coded tangled fork which supports pijul.
0
fork
atom
overview
issues
pulls
pipelines
remove old code
idk how this came back
Anirudh Oppiliappan
1 year ago
b3b045d7
58b0c958
-345
1 changed file
expand all
collapse all
unified
split
git
git.go
-345
git/git.go
reviewed
···
1
1
-
package git
2
2
-
3
3
-
import (
4
4
-
"archive/tar"
5
5
-
"fmt"
6
6
-
"io"
7
7
-
"io/fs"
8
8
-
"path"
9
9
-
"sort"
10
10
-
"time"
11
11
-
12
12
-
"github.com/go-git/go-git/v5"
13
13
-
"github.com/go-git/go-git/v5/plumbing"
14
14
-
"github.com/go-git/go-git/v5/plumbing/object"
15
15
-
)
16
16
-
17
17
-
type GitRepo struct {
18
18
-
r *git.Repository
19
19
-
h plumbing.Hash
20
20
-
}
21
21
-
22
22
-
type TagList struct {
23
23
-
refs []*TagReference
24
24
-
r *git.Repository
25
25
-
}
26
26
-
27
27
-
// TagReference is used to list both tag and non-annotated tags.
28
28
-
// Non-annotated tags should only contains a reference.
29
29
-
// Annotated tags should contain its reference and its tag information.
30
30
-
type TagReference struct {
31
31
-
ref *plumbing.Reference
32
32
-
tag *object.Tag
33
33
-
}
34
34
-
35
35
-
// infoWrapper wraps the property of a TreeEntry so it can export fs.FileInfo
36
36
-
// to tar WriteHeader
37
37
-
type infoWrapper struct {
38
38
-
name string
39
39
-
size int64
40
40
-
mode fs.FileMode
41
41
-
modTime time.Time
42
42
-
isDir bool
43
43
-
}
44
44
-
45
45
-
func (self *TagList) Len() int {
46
46
-
return len(self.refs)
47
47
-
}
48
48
-
49
49
-
func (self *TagList) Swap(i, j int) {
50
50
-
self.refs[i], self.refs[j] = self.refs[j], self.refs[i]
51
51
-
}
52
52
-
53
53
-
// sorting tags in reverse chronological order
54
54
-
func (self *TagList) Less(i, j int) bool {
55
55
-
var dateI time.Time
56
56
-
var dateJ time.Time
57
57
-
58
58
-
if self.refs[i].tag != nil {
59
59
-
dateI = self.refs[i].tag.Tagger.When
60
60
-
} else {
61
61
-
c, err := self.r.CommitObject(self.refs[i].ref.Hash())
62
62
-
if err != nil {
63
63
-
dateI = time.Now()
64
64
-
} else {
65
65
-
dateI = c.Committer.When
66
66
-
}
67
67
-
}
68
68
-
69
69
-
if self.refs[j].tag != nil {
70
70
-
dateJ = self.refs[j].tag.Tagger.When
71
71
-
} else {
72
72
-
c, err := self.r.CommitObject(self.refs[j].ref.Hash())
73
73
-
if err != nil {
74
74
-
dateJ = time.Now()
75
75
-
} else {
76
76
-
dateJ = c.Committer.When
77
77
-
}
78
78
-
}
79
79
-
80
80
-
return dateI.After(dateJ)
81
81
-
}
82
82
-
83
83
-
func Open(path string, ref string) (*GitRepo, error) {
84
84
-
var err error
85
85
-
g := GitRepo{}
86
86
-
g.r, err = git.PlainOpen(path)
87
87
-
if err != nil {
88
88
-
return nil, fmt.Errorf("opening %s: %w", path, err)
89
89
-
}
90
90
-
91
91
-
if ref == "" {
92
92
-
head, err := g.r.Head()
93
93
-
if err != nil {
94
94
-
return nil, fmt.Errorf("getting head of %s: %w", path, err)
95
95
-
}
96
96
-
g.h = head.Hash()
97
97
-
} else {
98
98
-
hash, err := g.r.ResolveRevision(plumbing.Revision(ref))
99
99
-
if err != nil {
100
100
-
return nil, fmt.Errorf("resolving rev %s for %s: %w", ref, path, err)
101
101
-
}
102
102
-
g.h = *hash
103
103
-
}
104
104
-
return &g, nil
105
105
-
}
106
106
-
107
107
-
func (g *GitRepo) Commits() ([]*object.Commit, error) {
108
108
-
ci, err := g.r.Log(&git.LogOptions{From: g.h})
109
109
-
if err != nil {
110
110
-
return nil, fmt.Errorf("commits from ref: %w", err)
111
111
-
}
112
112
-
113
113
-
commits := []*object.Commit{}
114
114
-
ci.ForEach(func(c *object.Commit) error {
115
115
-
commits = append(commits, c)
116
116
-
return nil
117
117
-
})
118
118
-
119
119
-
return commits, nil
120
120
-
}
121
121
-
122
122
-
func (g *GitRepo) LastCommit() (*object.Commit, error) {
123
123
-
c, err := g.r.CommitObject(g.h)
124
124
-
if err != nil {
125
125
-
return nil, fmt.Errorf("last commit: %w", err)
126
126
-
}
127
127
-
return c, nil
128
128
-
}
129
129
-
130
130
-
func (g *GitRepo) FileContent(path string) (string, error) {
131
131
-
c, err := g.r.CommitObject(g.h)
132
132
-
if err != nil {
133
133
-
return "", fmt.Errorf("commit object: %w", err)
134
134
-
}
135
135
-
136
136
-
tree, err := c.Tree()
137
137
-
if err != nil {
138
138
-
return "", fmt.Errorf("file tree: %w", err)
139
139
-
}
140
140
-
141
141
-
file, err := tree.File(path)
142
142
-
if err != nil {
143
143
-
return "", err
144
144
-
}
145
145
-
146
146
-
isbin, _ := file.IsBinary()
147
147
-
148
148
-
if !isbin {
149
149
-
return file.Contents()
150
150
-
} else {
151
151
-
return "Not displaying binary file", nil
152
152
-
}
153
153
-
}
154
154
-
155
155
-
func (g *GitRepo) Tags() ([]*TagReference, error) {
156
156
-
iter, err := g.r.Tags()
157
157
-
if err != nil {
158
158
-
return nil, fmt.Errorf("tag objects: %w", err)
159
159
-
}
160
160
-
161
161
-
tags := make([]*TagReference, 0)
162
162
-
163
163
-
if err := iter.ForEach(func(ref *plumbing.Reference) error {
164
164
-
obj, err := g.r.TagObject(ref.Hash())
165
165
-
switch err {
166
166
-
case nil:
167
167
-
tags = append(tags, &TagReference{
168
168
-
ref: ref,
169
169
-
tag: obj,
170
170
-
})
171
171
-
case plumbing.ErrObjectNotFound:
172
172
-
tags = append(tags, &TagReference{
173
173
-
ref: ref,
174
174
-
})
175
175
-
default:
176
176
-
return err
177
177
-
}
178
178
-
return nil
179
179
-
}); err != nil {
180
180
-
return nil, err
181
181
-
}
182
182
-
183
183
-
tagList := &TagList{r: g.r, refs: tags}
184
184
-
sort.Sort(tagList)
185
185
-
return tags, nil
186
186
-
}
187
187
-
188
188
-
func (g *GitRepo) Branches() ([]*plumbing.Reference, error) {
189
189
-
bi, err := g.r.Branches()
190
190
-
if err != nil {
191
191
-
return nil, fmt.Errorf("branchs: %w", err)
192
192
-
}
193
193
-
194
194
-
branches := []*plumbing.Reference{}
195
195
-
196
196
-
_ = bi.ForEach(func(ref *plumbing.Reference) error {
197
197
-
branches = append(branches, ref)
198
198
-
return nil
199
199
-
})
200
200
-
201
201
-
return branches, nil
202
202
-
}
203
203
-
204
204
-
func (g *GitRepo) FindMainBranch(branches []string) (string, error) {
205
205
-
206
206
-
for _, b := range branches {
207
207
-
_, err := g.r.ResolveRevision(plumbing.Revision(b))
208
208
-
if err == nil {
209
209
-
return b, nil
210
210
-
}
211
211
-
}
212
212
-
return "", fmt.Errorf("unable to find main branch")
213
213
-
}
214
214
-
215
215
-
// WriteTar writes itself from a tree into a binary tar file format.
216
216
-
// prefix is root folder to be appended.
217
217
-
func (g *GitRepo) WriteTar(w io.Writer, prefix string) error {
218
218
-
tw := tar.NewWriter(w)
219
219
-
defer tw.Close()
220
220
-
221
221
-
c, err := g.r.CommitObject(g.h)
222
222
-
if err != nil {
223
223
-
return fmt.Errorf("commit object: %w", err)
224
224
-
}
225
225
-
226
226
-
tree, err := c.Tree()
227
227
-
if err != nil {
228
228
-
return err
229
229
-
}
230
230
-
231
231
-
walker := object.NewTreeWalker(tree, true, nil)
232
232
-
defer walker.Close()
233
233
-
234
234
-
name, entry, err := walker.Next()
235
235
-
for ; err == nil; name, entry, err = walker.Next() {
236
236
-
info, err := newInfoWrapper(name, prefix, &entry, tree)
237
237
-
if err != nil {
238
238
-
return err
239
239
-
}
240
240
-
241
241
-
header, err := tar.FileInfoHeader(info, "")
242
242
-
if err != nil {
243
243
-
return err
244
244
-
}
245
245
-
246
246
-
err = tw.WriteHeader(header)
247
247
-
if err != nil {
248
248
-
return err
249
249
-
}
250
250
-
251
251
-
if !info.IsDir() {
252
252
-
file, err := tree.File(name)
253
253
-
if err != nil {
254
254
-
return err
255
255
-
}
256
256
-
257
257
-
reader, err := file.Blob.Reader()
258
258
-
if err != nil {
259
259
-
return err
260
260
-
}
261
261
-
262
262
-
_, err = io.Copy(tw, reader)
263
263
-
if err != nil {
264
264
-
reader.Close()
265
265
-
return err
266
266
-
}
267
267
-
reader.Close()
268
268
-
}
269
269
-
}
270
270
-
271
271
-
return nil
272
272
-
}
273
273
-
274
274
-
func newInfoWrapper(
275
275
-
name string,
276
276
-
prefix string,
277
277
-
entry *object.TreeEntry,
278
278
-
tree *object.Tree,
279
279
-
) (*infoWrapper, error) {
280
280
-
var (
281
281
-
size int64
282
282
-
mode fs.FileMode
283
283
-
isDir bool
284
284
-
)
285
285
-
286
286
-
if entry.Mode.IsFile() {
287
287
-
file, err := tree.TreeEntryFile(entry)
288
288
-
if err != nil {
289
289
-
return nil, err
290
290
-
}
291
291
-
mode = fs.FileMode(file.Mode)
292
292
-
293
293
-
size, err = tree.Size(name)
294
294
-
if err != nil {
295
295
-
return nil, err
296
296
-
}
297
297
-
} else {
298
298
-
isDir = true
299
299
-
mode = fs.ModeDir | fs.ModePerm
300
300
-
}
301
301
-
302
302
-
fullname := path.Join(prefix, name)
303
303
-
return &infoWrapper{
304
304
-
name: fullname,
305
305
-
size: size,
306
306
-
mode: mode,
307
307
-
modTime: time.Unix(0, 0),
308
308
-
isDir: isDir,
309
309
-
}, nil
310
310
-
}
311
311
-
312
312
-
func (i *infoWrapper) Name() string {
313
313
-
return i.name
314
314
-
}
315
315
-
316
316
-
func (i *infoWrapper) Size() int64 {
317
317
-
return i.size
318
318
-
}
319
319
-
320
320
-
func (i *infoWrapper) Mode() fs.FileMode {
321
321
-
return i.mode
322
322
-
}
323
323
-
324
324
-
func (i *infoWrapper) ModTime() time.Time {
325
325
-
return i.modTime
326
326
-
}
327
327
-
328
328
-
func (i *infoWrapper) IsDir() bool {
329
329
-
return i.isDir
330
330
-
}
331
331
-
332
332
-
func (i *infoWrapper) Sys() any {
333
333
-
return nil
334
334
-
}
335
335
-
336
336
-
func (t *TagReference) Name() string {
337
337
-
return t.ref.Name().Short()
338
338
-
}
339
339
-
340
340
-
func (t *TagReference) Message() string {
341
341
-
if t.tag != nil {
342
342
-
return t.tag.Message
343
343
-
}
344
344
-
return ""
345
345
-
}