github.com/gitbundle/modules@v0.0.0-20231025071548-85b91c5c3b01/git/last_commit_cache_gogit.go (about)

     1  // Copyright 2023 The GitBundle Inc. All rights reserved.
     2  // Copyright 2017 The Gitea Authors. All rights reserved.
     3  // Use of this source code is governed by a MIT-style
     4  // license that can be found in the LICENSE file.
     5  
     6  //go:build gogit
     7  
     8  package git
     9  
    10  import (
    11  	"context"
    12  
    13  	"github.com/gitbundle/modules/log"
    14  
    15  	"github.com/go-git/go-git/v5/plumbing/object"
    16  	cgobject "github.com/go-git/go-git/v5/plumbing/object/commitgraph"
    17  )
    18  
    19  // LastCommitCache represents a cache to store last commit
    20  type LastCommitCache struct {
    21  	repoPath    string
    22  	ttl         func() int64
    23  	repo        *Repository
    24  	commitCache map[string]*object.Commit
    25  	cache       Cache
    26  }
    27  
    28  // NewLastCommitCache creates a new last commit cache for repo
    29  func NewLastCommitCache(repoPath string, gitRepo *Repository, ttl func() int64, cache Cache) *LastCommitCache {
    30  	if cache == nil {
    31  		return nil
    32  	}
    33  	return &LastCommitCache{
    34  		repoPath:    repoPath,
    35  		repo:        gitRepo,
    36  		commitCache: make(map[string]*object.Commit),
    37  		ttl:         ttl,
    38  		cache:       cache,
    39  	}
    40  }
    41  
    42  // Get get the last commit information by commit id and entry path
    43  func (c *LastCommitCache) Get(ref, entryPath string) (interface{}, error) {
    44  	v := c.cache.Get(c.getCacheKey(c.repoPath, ref, entryPath))
    45  	if vs, ok := v.(string); ok {
    46  		log.Debug("LastCommitCache hit level 1: [%s:%s:%s]", ref, entryPath, vs)
    47  		if commit, ok := c.commitCache[vs]; ok {
    48  			log.Debug("LastCommitCache hit level 2: [%s:%s:%s]", ref, entryPath, vs)
    49  			return commit, nil
    50  		}
    51  		id, err := c.repo.ConvertToSHA1(vs)
    52  		if err != nil {
    53  			return nil, err
    54  		}
    55  		commit, err := c.repo.GoGitRepo().CommitObject(id)
    56  		if err != nil {
    57  			return nil, err
    58  		}
    59  		c.commitCache[vs] = commit
    60  		return commit, nil
    61  	}
    62  	return nil, nil
    63  }
    64  
    65  // CacheCommit will cache the commit from the gitRepository
    66  func (c *LastCommitCache) CacheCommit(ctx context.Context, commit *Commit) error {
    67  	commitNodeIndex, _ := commit.repo.CommitNodeIndex()
    68  
    69  	index, err := commitNodeIndex.Get(commit.ID)
    70  	if err != nil {
    71  		return err
    72  	}
    73  
    74  	return c.recursiveCache(ctx, index, &commit.Tree, "", 1)
    75  }
    76  
    77  func (c *LastCommitCache) recursiveCache(ctx context.Context, index cgobject.CommitNode, tree *Tree, treePath string, level int) error {
    78  	if level == 0 {
    79  		return nil
    80  	}
    81  
    82  	entries, err := tree.ListEntries()
    83  	if err != nil {
    84  		return err
    85  	}
    86  
    87  	entryPaths := make([]string, len(entries))
    88  	entryMap := make(map[string]*TreeEntry)
    89  	for i, entry := range entries {
    90  		entryPaths[i] = entry.Name()
    91  		entryMap[entry.Name()] = entry
    92  	}
    93  
    94  	commits, err := GetLastCommitForPaths(ctx, c, index, treePath, entryPaths)
    95  	if err != nil {
    96  		return err
    97  	}
    98  
    99  	for entry := range commits {
   100  		if entryMap[entry].IsDir() {
   101  			subTree, err := tree.SubTree(entry)
   102  			if err != nil {
   103  				return err
   104  			}
   105  			if err := c.recursiveCache(ctx, index, subTree, entry, level-1); err != nil {
   106  				return err
   107  			}
   108  		}
   109  	}
   110  
   111  	return nil
   112  }