From dd136858f1ea40ad3c94191d647487fa4f31926c Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 18 Oct 2024 20:33:49 +0200 Subject: Adding upstream version 9.0.0. Signed-off-by: Daniel Baumann --- modules/git/last_commit_cache.go | 159 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 modules/git/last_commit_cache.go (limited to 'modules/git/last_commit_cache.go') diff --git a/modules/git/last_commit_cache.go b/modules/git/last_commit_cache.go new file mode 100644 index 0000000..8c7ee5a --- /dev/null +++ b/modules/git/last_commit_cache.go @@ -0,0 +1,159 @@ +// 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 interface +type 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 commit +type LastCommitCache struct { + repoPath string + ttl func() int64 + repo *Repository + commitCache map[string]*Commit + cache Cache +} + +// NewLastCommitCache creates a new last commit cache for repo +func 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 path +func (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 path +func (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 commit +func (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 gitRepository +func (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 +} -- cgit v1.2.3