+2
go.mod
+2
go.mod
···
11
11
github.com/bluesky-social/jetstream v0.0.0-20241210005130-ea96859b93d1
12
12
github.com/casbin/casbin/v2 v2.103.0
13
13
github.com/cyphar/filepath-securejoin v0.3.3
14
+
github.com/dgraph-io/ristretto v0.2.0
14
15
github.com/dustin/go-humanize v1.0.1
15
16
github.com/gliderlabs/ssh v0.3.5
16
17
github.com/go-chi/chi/v5 v5.2.0
···
81
82
github.com/multiformats/go-varint v0.0.7 // indirect
82
83
github.com/opentracing/opentracing-go v1.2.0 // indirect
83
84
github.com/pjbgf/sha1cd v0.3.0 // indirect
85
+
github.com/pkg/errors v0.9.1 // indirect
84
86
github.com/pmezard/go-difflib v1.0.0 // indirect
85
87
github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f // indirect
86
88
github.com/prometheus/client_golang v1.19.1 // indirect
+4
go.sum
+4
go.sum
···
50
50
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
51
51
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
52
52
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
53
+
github.com/dgraph-io/ristretto v0.2.0 h1:XAfl+7cmoUDWW/2Lx8TGZQjjxIQ2Ley9DSf52dru4WE=
54
+
github.com/dgraph-io/ristretto v0.2.0/go.mod h1:8uBHCU/PBV4Ag0CJrP47b9Ofby5dqWNh4FicAdoqFNU=
55
+
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
56
+
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
53
57
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
54
58
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
55
59
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
+48
knotserver/git/git.go
+48
knotserver/git/git.go
···
7
7
"io/fs"
8
8
"path"
9
9
"sort"
10
+
"sync"
10
11
"time"
11
12
13
+
"github.com/dgraph-io/ristretto"
12
14
"github.com/go-git/go-git/v5"
13
15
"github.com/go-git/go-git/v5/plumbing"
14
16
"github.com/go-git/go-git/v5/plumbing/object"
15
17
)
18
+
19
+
var (
20
+
commitCache *ristretto.Cache
21
+
cacheMu sync.RWMutex
22
+
)
23
+
24
+
func init() {
25
+
cache, _ := ristretto.NewCache(&ristretto.Config{
26
+
NumCounters: 1e7,
27
+
MaxCost: 1 << 30,
28
+
BufferItems: 64,
29
+
})
30
+
commitCache = cache
31
+
}
16
32
17
33
var (
18
34
ErrBinaryFile = fmt.Errorf("binary file")
···
277
293
}
278
294
279
295
return nil
296
+
}
297
+
298
+
func (g *GitRepo) LastCommitTime(filePath string) (*object.Commit, error) {
299
+
cacheMu.RLock()
300
+
if commit, exists := commitCache.Get(filePath); exists {
301
+
cacheMu.RUnlock()
302
+
return commit.(*object.Commit), nil
303
+
}
304
+
cacheMu.RUnlock()
305
+
306
+
commitIter, err := g.r.Log(&git.LogOptions{
307
+
From: g.h,
308
+
PathFilter: func(s string) bool {
309
+
return s == filePath
310
+
},
311
+
Order: git.LogOrderCommitterTime,
312
+
})
313
+
314
+
if err != nil {
315
+
return nil, fmt.Errorf("failed to get commit log for %s: %w", filePath, err)
316
+
}
317
+
318
+
commit, err := commitIter.Next()
319
+
if err != nil {
320
+
return nil, fmt.Errorf("no commit found for %s", filePath)
321
+
}
322
+
323
+
cacheMu.Lock()
324
+
commitCache.Set(filePath, commit, 1)
325
+
cacheMu.Unlock()
326
+
327
+
return commit, nil
280
328
}
281
329
282
330
func newInfoWrapper(
+15
-7
knotserver/git/tree.go
+15
-7
knotserver/git/tree.go
···
20
20
}
21
21
22
22
if path == "" {
23
-
files = makeNiceTree(tree)
23
+
files = g.makeNiceTree(tree)
24
24
} else {
25
25
o, err := tree.FindEntry(path)
26
26
if err != nil {
···
33
33
return nil, err
34
34
}
35
35
36
-
files = makeNiceTree(subtree)
36
+
files = g.makeNiceTree(subtree)
37
37
}
38
38
}
39
39
40
40
return files, nil
41
41
}
42
42
43
-
func makeNiceTree(t *object.Tree) []types.NiceTree {
43
+
func (g *GitRepo) makeNiceTree(t *object.Tree) []types.NiceTree {
44
44
nts := []types.NiceTree{}
45
45
46
46
for _, e := range t.Entries {
47
47
mode, _ := e.Mode.ToOSFileMode()
48
48
sz, _ := t.Size(e.Name)
49
+
50
+
lastCommit, err := g.LastCommitTime(e.Name)
51
+
if err != nil {
52
+
continue
53
+
}
54
+
49
55
nts = append(nts, types.NiceTree{
50
-
Name: e.Name,
51
-
Mode: mode.String(),
52
-
IsFile: e.Mode.IsFile(),
53
-
Size: sz,
56
+
Name: e.Name,
57
+
Mode: mode.String(),
58
+
IsFile: e.Mode.IsFile(),
59
+
Size: sz,
60
+
LastCommit: lastCommit,
54
61
})
62
+
55
63
}
56
64
57
65
return nts
+6
types/tree.go
+6
types/tree.go
···
1
1
package types
2
2
3
+
import (
4
+
"github.com/go-git/go-git/v5/plumbing/object"
5
+
)
6
+
3
7
// A nicer git tree representation.
4
8
type NiceTree struct {
5
9
Name string `json:"name"`
···
7
11
Size int64 `json:"size"`
8
12
IsFile bool `json:"is_file"`
9
13
IsSubtree bool `json:"is_subtree"`
14
+
15
+
LastCommit *object.Commit `json:"last_commit,omitempty"`
10
16
}