forked from tangled.org/core
Monorepo for Tangled

knotserver: last commit for each file

This is a super trivial implementation last commit for each file. It's
slow and inefficient despite using ristretto for in-memory caching.

We should move this to storing in sqlite in the future.

authored by oppi.li and committed by anirudh.fi 75dedd41 82f42852

Changed files
+75 -7
knotserver
types
+2
go.mod
··· 11 github.com/bluesky-social/jetstream v0.0.0-20241210005130-ea96859b93d1 12 github.com/casbin/casbin/v2 v2.103.0 13 github.com/cyphar/filepath-securejoin v0.3.3 14 github.com/dustin/go-humanize v1.0.1 15 github.com/gliderlabs/ssh v0.3.5 16 github.com/go-chi/chi/v5 v5.2.0 ··· 81 github.com/multiformats/go-varint v0.0.7 // indirect 82 github.com/opentracing/opentracing-go v1.2.0 // indirect 83 github.com/pjbgf/sha1cd v0.3.0 // indirect 84 github.com/pmezard/go-difflib v1.0.0 // indirect 85 github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f // indirect 86 github.com/prometheus/client_golang v1.19.1 // indirect
··· 11 github.com/bluesky-social/jetstream v0.0.0-20241210005130-ea96859b93d1 12 github.com/casbin/casbin/v2 v2.103.0 13 github.com/cyphar/filepath-securejoin v0.3.3 14 + github.com/dgraph-io/ristretto v0.2.0 15 github.com/dustin/go-humanize v1.0.1 16 github.com/gliderlabs/ssh v0.3.5 17 github.com/go-chi/chi/v5 v5.2.0 ··· 82 github.com/multiformats/go-varint v0.0.7 // indirect 83 github.com/opentracing/opentracing-go v1.2.0 // indirect 84 github.com/pjbgf/sha1cd v0.3.0 // indirect 85 + github.com/pkg/errors v0.9.1 // indirect 86 github.com/pmezard/go-difflib v1.0.0 // indirect 87 github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f // indirect 88 github.com/prometheus/client_golang v1.19.1 // indirect
+4
go.sum
··· 50 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 51 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 52 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 53 github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= 54 github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= 55 github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
··· 50 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 51 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 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= 57 github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= 58 github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= 59 github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
+48
knotserver/git/git.go
··· 7 "io/fs" 8 "path" 9 "sort" 10 "time" 11 12 "github.com/go-git/go-git/v5" 13 "github.com/go-git/go-git/v5/plumbing" 14 "github.com/go-git/go-git/v5/plumbing/object" 15 ) 16 17 var ( 18 ErrBinaryFile = fmt.Errorf("binary file") ··· 277 } 278 279 return nil 280 } 281 282 func newInfoWrapper(
··· 7 "io/fs" 8 "path" 9 "sort" 10 + "sync" 11 "time" 12 13 + "github.com/dgraph-io/ristretto" 14 "github.com/go-git/go-git/v5" 15 "github.com/go-git/go-git/v5/plumbing" 16 "github.com/go-git/go-git/v5/plumbing/object" 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 + } 32 33 var ( 34 ErrBinaryFile = fmt.Errorf("binary file") ··· 293 } 294 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 328 } 329 330 func newInfoWrapper(
+15 -7
knotserver/git/tree.go
··· 20 } 21 22 if path == "" { 23 - files = makeNiceTree(tree) 24 } else { 25 o, err := tree.FindEntry(path) 26 if err != nil { ··· 33 return nil, err 34 } 35 36 - files = makeNiceTree(subtree) 37 } 38 } 39 40 return files, nil 41 } 42 43 - func makeNiceTree(t *object.Tree) []types.NiceTree { 44 nts := []types.NiceTree{} 45 46 for _, e := range t.Entries { 47 mode, _ := e.Mode.ToOSFileMode() 48 sz, _ := t.Size(e.Name) 49 nts = append(nts, types.NiceTree{ 50 - Name: e.Name, 51 - Mode: mode.String(), 52 - IsFile: e.Mode.IsFile(), 53 - Size: sz, 54 }) 55 } 56 57 return nts
··· 20 } 21 22 if path == "" { 23 + files = g.makeNiceTree(tree) 24 } else { 25 o, err := tree.FindEntry(path) 26 if err != nil { ··· 33 return nil, err 34 } 35 36 + files = g.makeNiceTree(subtree) 37 } 38 } 39 40 return files, nil 41 } 42 43 + func (g *GitRepo) makeNiceTree(t *object.Tree) []types.NiceTree { 44 nts := []types.NiceTree{} 45 46 for _, e := range t.Entries { 47 mode, _ := e.Mode.ToOSFileMode() 48 sz, _ := t.Size(e.Name) 49 + 50 + lastCommit, err := g.LastCommitTime(e.Name) 51 + if err != nil { 52 + continue 53 + } 54 + 55 nts = append(nts, types.NiceTree{ 56 + Name: e.Name, 57 + Mode: mode.String(), 58 + IsFile: e.Mode.IsFile(), 59 + Size: sz, 60 + LastCommit: lastCommit, 61 }) 62 + 63 } 64 65 return nts
+6
types/tree.go
··· 1 package types 2 3 // A nicer git tree representation. 4 type NiceTree struct { 5 Name string `json:"name"` ··· 7 Size int64 `json:"size"` 8 IsFile bool `json:"is_file"` 9 IsSubtree bool `json:"is_subtree"` 10 }
··· 1 package types 2 3 + import ( 4 + "github.com/go-git/go-git/v5/plumbing/object" 5 + ) 6 + 7 // A nicer git tree representation. 8 type NiceTree struct { 9 Name string `json:"name"` ··· 11 Size int64 `json:"size"` 12 IsFile bool `json:"is_file"` 13 IsSubtree bool `json:"is_subtree"` 14 + 15 + LastCommit *object.Commit `json:"last_commit,omitempty"` 16 }