159 lines · 4.0 KB · Go
RawDownload
// Copyright 2020 The Gitea Authors. All rights reserved.// SPDX-License-Identifier: MIT package git import (	"context"	"crypto/sha256"	"fmt" 	"code.gitea.io/gitea/modules/log"	"code.gitea.io/gitea/modules/setting") // Cache represents a caching interfacetype Cache interface {	// Put puts value into cache with key and expire time.	Put(key string, val any, timeout int64) error	// Get gets cached value by given key.	Get(key string) any} func getCacheKey(repoPath, commitID, entryPath string) string {	hashBytes := sha256.Sum256([]byte(fmt.Sprintf("%s:%s:%s", repoPath, commitID, entryPath)))	return fmt.Sprintf("last_commit:%x", hashBytes)} // LastCommitCache represents a cache to store last committype LastCommitCache struct {	repoPath    string	ttl         func() int64	repo        *Repository	commitCache map[string]*Commit	cache       Cache} // NewLastCommitCache creates a new last commit cache for repofunc NewLastCommitCache(count int64, repoPath string, gitRepo *Repository, cache Cache) *LastCommitCache {	if cache == nil {		return nil	}	if count < setting.CacheService.LastCommit.CommitsCount {		return nil	} 	return &LastCommitCache{		repoPath: repoPath,		repo:     gitRepo,		ttl:      setting.LastCommitCacheTTLSeconds,		cache:    cache,	}} // Put put the last commit id with commit and entry pathfunc (c *LastCommitCache) Put(ref, entryPath, commitID string) error {	if c == nil || c.cache == nil {		return nil	}	log.Debug("LastCommitCache save: [%s:%s:%s]", ref, entryPath, commitID)	return c.cache.Put(getCacheKey(c.repoPath, ref, entryPath), commitID, c.ttl())} // Get gets the last commit information by commit id and entry pathfunc (c *LastCommitCache) Get(ref, entryPath string) (*Commit, error) {	if c == nil || c.cache == nil {		return nil, nil	} 	commitID, ok := c.cache.Get(getCacheKey(c.repoPath, ref, entryPath)).(string)	if !ok || commitID == "" {		return nil, nil	} 	log.Debug("LastCommitCache hit level 1: [%s:%s:%s]", ref, entryPath, commitID)	if c.commitCache != nil {		if commit, ok := c.commitCache[commitID]; ok {			log.Debug("LastCommitCache hit level 2: [%s:%s:%s]", ref, entryPath, commitID)			return commit, nil		}	} 	commit, err := c.repo.GetCommit(commitID)	if err != nil {		return nil, err	}	if c.commitCache == nil {		c.commitCache = make(map[string]*Commit)	}	c.commitCache[commitID] = commit	return commit, nil} // GetCommitByPath gets the last commit for the entry in the provided commitfunc (c *LastCommitCache) GetCommitByPath(commitID, entryPath string) (*Commit, error) {	sha, err := NewIDFromString(commitID)	if err != nil {		return nil, err	} 	lastCommit, err := c.Get(sha.String(), entryPath)	if err != nil || lastCommit != nil {		return lastCommit, err	} 	lastCommit, err = c.repo.getCommitByPathWithID(sha, entryPath)	if err != nil {		return nil, err	} 	if err := c.Put(commitID, entryPath, lastCommit.ID.String()); err != nil {		log.Error("Unable to cache %s as the last commit for %q in %s %s. Error %v", lastCommit.ID.String(), entryPath, commitID, c.repoPath, err)	} 	return lastCommit, nil} // CacheCommit will cache the commit from the gitRepositoryfunc (c *Commit) CacheCommit(ctx context.Context) error {	if c.repo.LastCommitCache == nil {		return nil	}	return c.recursiveCache(ctx, &c.Tree, "", 1)} func (c *Commit) recursiveCache(ctx context.Context, tree *Tree, treePath string, level int) error {	if level == 0 {		return nil	} 	entries, err := tree.ListEntries()	if err != nil {		return err	} 	entryPaths := make([]string, len(entries))	for i, entry := range entries {		entryPaths[i] = entry.Name()	} 	_, err = WalkGitLog(ctx, c.repo, c, treePath, entryPaths...)	if err != nil {		return err	} 	for _, treeEntry := range entries {		// entryMap won't contain "" therefore skip this.		if treeEntry.IsDir() {			subTree, err := tree.SubTree(treeEntry.Name())			if err != nil {				return err			}			if err := c.recursiveCache(ctx, subTree, treeEntry.Name(), level-1); err != nil {				return err			}		}	} 	return nil}