mirror of
https://codeberg.org/forgejo/forgejo
synced 2024-11-24 02:36:10 +01:00
0ca13c5eae
When opening a repository, it will call `ensureValidRepository` and also `CatFileBatch`. But sometimes these will not be used until repository closed. So it's a waste of CPU to invoke 3 times git command for every open repository. This PR removed all of these from `OpenRepository` but only kept checking whether the folder exists. When a batch is necessary, the necessary functions will be invoked. --- Conflict resolution: Because of the removal of go-git in (#4941) `_nogogit.go` files were either renamed or merged into the 'common' file. Git does handle the renames correctly, but for those that were merged has to be manually copied pasted over. The patch looks the same, 201 additions 90 deletions as the original patch. (cherry picked from commit c03baab678ba5b2e9d974aea147e660417f5d3f7)
179 lines
4.6 KiB
Go
179 lines
4.6 KiB
Go
// Copyright 2017 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package git
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"path"
|
|
"sort"
|
|
|
|
"code.gitea.io/gitea/modules/log"
|
|
)
|
|
|
|
// CommitInfo describes the first commit with the provided entry
|
|
type CommitInfo struct {
|
|
Entry *TreeEntry
|
|
Commit *Commit
|
|
SubModuleFile *SubModuleFile
|
|
}
|
|
|
|
// GetCommitsInfo gets information of all commits that are corresponding to these entries
|
|
func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath string) ([]CommitInfo, *Commit, error) {
|
|
entryPaths := make([]string, len(tes)+1)
|
|
// Get the commit for the treePath itself
|
|
entryPaths[0] = ""
|
|
for i, entry := range tes {
|
|
entryPaths[i+1] = entry.Name()
|
|
}
|
|
|
|
var err error
|
|
|
|
var revs map[string]*Commit
|
|
if commit.repo.LastCommitCache != nil {
|
|
var unHitPaths []string
|
|
revs, unHitPaths, err = getLastCommitForPathsByCache(commit.ID.String(), treePath, entryPaths, commit.repo.LastCommitCache)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
if len(unHitPaths) > 0 {
|
|
sort.Strings(unHitPaths)
|
|
commits, err := GetLastCommitForPaths(ctx, commit, treePath, unHitPaths)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
for pth, found := range commits {
|
|
revs[pth] = found
|
|
}
|
|
}
|
|
} else {
|
|
sort.Strings(entryPaths)
|
|
revs, err = GetLastCommitForPaths(ctx, commit, treePath, entryPaths)
|
|
}
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
commitsInfo := make([]CommitInfo, len(tes))
|
|
for i, entry := range tes {
|
|
commitsInfo[i] = CommitInfo{
|
|
Entry: entry,
|
|
}
|
|
|
|
// Check if we have found a commit for this entry in time
|
|
if entryCommit, ok := revs[entry.Name()]; ok {
|
|
commitsInfo[i].Commit = entryCommit
|
|
} else {
|
|
log.Debug("missing commit for %s", entry.Name())
|
|
}
|
|
|
|
// If the entry if a submodule add a submodule file for this
|
|
if entry.IsSubModule() {
|
|
subModuleURL := ""
|
|
var fullPath string
|
|
if len(treePath) > 0 {
|
|
fullPath = treePath + "/" + entry.Name()
|
|
} else {
|
|
fullPath = entry.Name()
|
|
}
|
|
if subModule, err := commit.GetSubModule(fullPath); err != nil {
|
|
return nil, nil, err
|
|
} else if subModule != nil {
|
|
subModuleURL = subModule.URL
|
|
}
|
|
subModuleFile := NewSubModuleFile(commitsInfo[i].Commit, subModuleURL, entry.ID.String())
|
|
commitsInfo[i].SubModuleFile = subModuleFile
|
|
}
|
|
}
|
|
|
|
// Retrieve the commit for the treePath itself (see above). We basically
|
|
// get it for free during the tree traversal and it's used for listing
|
|
// pages to display information about newest commit for a given path.
|
|
var treeCommit *Commit
|
|
var ok bool
|
|
if treePath == "" {
|
|
treeCommit = commit
|
|
} else if treeCommit, ok = revs[""]; ok {
|
|
treeCommit.repo = commit.repo
|
|
}
|
|
return commitsInfo, treeCommit, nil
|
|
}
|
|
|
|
func getLastCommitForPathsByCache(commitID, treePath string, paths []string, cache *LastCommitCache) (map[string]*Commit, []string, error) {
|
|
var unHitEntryPaths []string
|
|
results := make(map[string]*Commit)
|
|
for _, p := range paths {
|
|
lastCommit, err := cache.Get(commitID, path.Join(treePath, p))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
if lastCommit != nil {
|
|
results[p] = lastCommit
|
|
continue
|
|
}
|
|
|
|
unHitEntryPaths = append(unHitEntryPaths, p)
|
|
}
|
|
|
|
return results, unHitEntryPaths, nil
|
|
}
|
|
|
|
// GetLastCommitForPaths returns last commit information
|
|
func GetLastCommitForPaths(ctx context.Context, commit *Commit, treePath string, paths []string) (map[string]*Commit, error) {
|
|
// We read backwards from the commit to obtain all of the commits
|
|
revs, err := WalkGitLog(ctx, commit.repo, commit, treePath, paths...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
batchStdinWriter, batchReader, cancel, err := commit.repo.CatFileBatch(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer cancel()
|
|
|
|
commitsMap := map[string]*Commit{}
|
|
commitsMap[commit.ID.String()] = commit
|
|
|
|
commitCommits := map[string]*Commit{}
|
|
for path, commitID := range revs {
|
|
c, ok := commitsMap[commitID]
|
|
if ok {
|
|
commitCommits[path] = c
|
|
continue
|
|
}
|
|
|
|
if len(commitID) == 0 {
|
|
continue
|
|
}
|
|
|
|
_, err := batchStdinWriter.Write([]byte(commitID + "\n"))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
_, typ, size, err := ReadBatchLine(batchReader)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if typ != "commit" {
|
|
if err := DiscardFull(batchReader, size+1); err != nil {
|
|
return nil, err
|
|
}
|
|
return nil, fmt.Errorf("unexpected type: %s for commit id: %s", typ, commitID)
|
|
}
|
|
c, err = CommitFromReader(commit.repo, MustIDFromString(commitID), io.LimitReader(batchReader, size))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err := batchReader.Discard(1); err != nil {
|
|
return nil, err
|
|
}
|
|
commitCommits[path] = c
|
|
}
|
|
|
|
return commitCommits, nil
|
|
}
|