this repo has no description

knotserver/git: cache just the commit hash and time

This should help alleviate the memory leakyness that was exhibited when
we stored the entire object.Commit in memory.

Changed files
+40 -17
appview
pages
templates
knotserver
git
types
+2 -2
appview/pages/templates/repo/index.html
··· 79 </a> 80 81 <time class="text-xs text-gray-500" 82 - >{{ timeFmt .LastCommit.Author.When }}</time 83 > 84 </div> 85 </div> ··· 104 </a> 105 106 <time class="text-xs text-gray-500" 107 - >{{ timeFmt .LastCommit.Author.When }}</time 108 > 109 </div> 110 </div>
··· 79 </a> 80 81 <time class="text-xs text-gray-500" 82 + >{{ timeFmt .LastCommit.When }}</time 83 > 84 </div> 85 </div> ··· 104 </a> 105 106 <time class="text-xs text-gray-500" 107 + >{{ timeFmt .LastCommit.When }}</time 108 > 109 </div> 110 </div>
+2 -2
appview/pages/templates/repo/tree.html
··· 44 <i class="w-3 h-3 fill-current" data-lucide="folder"></i>{{ .Name }} 45 </div> 46 </a> 47 - <time class="text-xs text-gray-500">{{ timeFmt .LastCommit.Author.When }}</time> 48 </div> 49 </div> 50 {{ end }} ··· 59 <i class="w-3 h-3" data-lucide="file"></i>{{ .Name }} 60 </div> 61 </a> 62 - <time class="text-xs text-gray-500">{{ timeFmt .LastCommit.Author.When }}</time> 63 </div> 64 </div> 65 {{ end }}
··· 44 <i class="w-3 h-3 fill-current" data-lucide="folder"></i>{{ .Name }} 45 </div> 46 </a> 47 + <time class="text-xs text-gray-500">{{ timeFmt .LastCommit.When }}</time> 48 </div> 49 </div> 50 {{ end }} ··· 59 <i class="w-3 h-3" data-lucide="file"></i>{{ .Name }} 60 </div> 61 </a> 62 + <time class="text-xs text-gray-500">{{ timeFmt .LastCommit.When }}</time> 63 </div> 64 </div> 65 {{ end }}
+26 -11
knotserver/git/git.go
··· 9 "os/exec" 10 "path" 11 "sort" 12 "strings" 13 "sync" 14 "time" ··· 17 "github.com/go-git/go-git/v5" 18 "github.com/go-git/go-git/v5/plumbing" 19 "github.com/go-git/go-git/v5/plumbing/object" 20 ) 21 22 var ( ··· 297 return nil 298 } 299 300 - func (g *GitRepo) LastCommitForPath(path string) (*object.Commit, error) { 301 cacheKey := fmt.Sprintf("%s:%s", g.h.String(), path) 302 cacheMu.RLock() 303 - if commit, found := commitCache.Get(cacheKey); found { 304 cacheMu.RUnlock() 305 - return commit.(*object.Commit), nil 306 } 307 cacheMu.RUnlock() 308 309 - cmd := exec.Command("git", "-C", g.path, "log", "-1", "--format=%H", "--", path) 310 311 var out bytes.Buffer 312 cmd.Stdout = &out ··· 316 return nil, fmt.Errorf("failed to get commit hash: %w", err) 317 } 318 319 - commitHash := strings.TrimSpace(out.String()) 320 - if commitHash == "" { 321 return nil, fmt.Errorf("no commits found for path: %s", path) 322 } 323 324 hash := plumbing.NewHash(commitHash) 325 326 - commit, err := g.r.CommitObject(hash) 327 - if err != nil { 328 - return nil, err 329 } 330 331 cacheMu.Lock() 332 - commitCache.Set(cacheKey, commit, 1) 333 cacheMu.Unlock() 334 335 - return commit, nil 336 } 337 338 func newInfoWrapper(
··· 9 "os/exec" 10 "path" 11 "sort" 12 + "strconv" 13 "strings" 14 "sync" 15 "time" ··· 18 "github.com/go-git/go-git/v5" 19 "github.com/go-git/go-git/v5/plumbing" 20 "github.com/go-git/go-git/v5/plumbing/object" 21 + "github.com/sotangled/tangled/types" 22 ) 23 24 var ( ··· 299 return nil 300 } 301 302 + func (g *GitRepo) LastCommitForPath(path string) (*types.LastCommitInfo, error) { 303 cacheKey := fmt.Sprintf("%s:%s", g.h.String(), path) 304 cacheMu.RLock() 305 + if commitInfo, found := commitCache.Get(cacheKey); found { 306 cacheMu.RUnlock() 307 + return commitInfo.(*types.LastCommitInfo), nil 308 } 309 cacheMu.RUnlock() 310 311 + cmd := exec.Command("git", "-C", g.path, "log", "-1", "--format=%H %ct", "--", path) 312 313 var out bytes.Buffer 314 cmd.Stdout = &out ··· 318 return nil, fmt.Errorf("failed to get commit hash: %w", err) 319 } 320 321 + output := strings.TrimSpace(out.String()) 322 + if output == "" { 323 return nil, fmt.Errorf("no commits found for path: %s", path) 324 } 325 326 + parts := strings.SplitN(output, " ", 2) 327 + if len(parts) < 2 { 328 + return nil, fmt.Errorf("unexpected commit log format") 329 + } 330 + 331 + commitHash := parts[0] 332 + commitTimeUnix, err := strconv.ParseInt(parts[1], 10, 64) 333 + if err != nil { 334 + return nil, fmt.Errorf("parsing commit time: %w", err) 335 + } 336 + commitTime := time.Unix(commitTimeUnix, 0) 337 + 338 hash := plumbing.NewHash(commitHash) 339 340 + commitInfo := &types.LastCommitInfo{ 341 + Hash: hash, 342 + Message: "", 343 + When: commitTime, 344 } 345 346 cacheMu.Lock() 347 + commitCache.Set(cacheKey, commitInfo, 1) 348 cacheMu.Unlock() 349 350 + return commitInfo, nil 351 } 352 353 func newInfoWrapper(
+10 -2
types/tree.go
··· 1 package types 2 3 import ( 4 - "github.com/go-git/go-git/v5/plumbing/object" 5 ) 6 7 // A nicer git tree representation. ··· 12 IsFile bool `json:"is_file"` 13 IsSubtree bool `json:"is_subtree"` 14 15 - LastCommit *object.Commit `json:"last_commit,omitempty"` 16 }
··· 1 package types 2 3 import ( 4 + "time" 5 + 6 + "github.com/go-git/go-git/v5/plumbing" 7 ) 8 9 // A nicer git tree representation. ··· 14 IsFile bool `json:"is_file"` 15 IsSubtree bool `json:"is_subtree"` 16 17 + LastCommit *LastCommitInfo `json:"last_commit,omitempty"` 18 + } 19 + 20 + type LastCommitInfo struct { 21 + Hash plumbing.Hash 22 + Message string 23 + When time.Time 24 }