mirror of
https://github.com/go-gitea/gitea.git
synced 2026-10-04 21:54:08 +00:00
ad38b60983
Removes the go-git backend so every build uses the git CLI backend. Its Windows performance advantage is gone, it lacks SHA-256 support, and it breaks repositories on Windows. The performance changes moved to https://github.com/go-gitea/gitea/pull/39526. Fixes https://github.com/go-gitea/gitea/issues/38359 Fixes https://github.com/go-gitea/gitea/issues/34694 --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
149 lines
4.9 KiB
Go
149 lines
4.9 KiB
Go
// Copyright 2017 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package git
|
|
|
|
import (
|
|
"context"
|
|
"maps"
|
|
"path"
|
|
"time"
|
|
|
|
"gitea.dev/modules/log"
|
|
"gitea.dev/modules/util"
|
|
)
|
|
|
|
// CommitInfo describes the first commit with the provided entry
|
|
type CommitInfo struct {
|
|
Entry *TreeEntry
|
|
Commit *Commit
|
|
SubmoduleFile *CommitSubmoduleFile
|
|
}
|
|
|
|
func GetCommitInfoSubmoduleFile(ctx context.Context, repoLink, fullPath string, gitRepo *Repository, commit *Commit, refCommitID ObjectID) (*CommitSubmoduleFile, error) {
|
|
submodule, err := commit.GetSubModule(ctx, gitRepo, fullPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if submodule == nil {
|
|
// unable to find submodule from ".gitmodules" file
|
|
return NewCommitSubmoduleFile(repoLink, fullPath, "", refCommitID.String()), nil
|
|
}
|
|
return NewCommitSubmoduleFile(repoLink, fullPath, submodule.URL, refCommitID.String()), nil
|
|
}
|
|
|
|
func getLastCommitForPathsWithTimeout(ctx context.Context, timeout time.Duration, gitRepo *Repository, commit *Commit, treePath string, entryNames []string) (map[string]*Commit, error) {
|
|
// Actually using "timeout ctx" here is not ideal: it creates a new git cat-file batch process
|
|
// (the other one uses the request's ctx and is cached by the gitRepo).
|
|
// Ideally, the underlying functions only need to return in predictable time,
|
|
// so it should use the shared (cached) git cat-file batcher from the request context and just use "time.After" for deadline.
|
|
// However, it's difficult to make it right at the moment.
|
|
ctxInner := ctx
|
|
if timeout > 0 {
|
|
var cancel context.CancelFunc
|
|
ctxInner, cancel = context.WithTimeout(ctx, timeout)
|
|
defer cancel()
|
|
}
|
|
revs, err := GetLastCommitForPaths(ctxInner, gitRepo, commit, treePath, entryNames)
|
|
if err != nil && ctxInner.Err() == nil {
|
|
// only return the err if context is not canceled
|
|
return nil, err
|
|
}
|
|
return revs, nil
|
|
}
|
|
|
|
// GetCommitsInfo gets information of all commits that are corresponding to these entries
|
|
func (tes Entries) GetCommitsInfo(ctx context.Context, timeout time.Duration, repoLink string, gitRepo *Repository, commit *Commit, treePath string) (commitsInfo []CommitInfo, treeLastCommit *Commit, err error) {
|
|
entryNames := make([]string, 0, len(tes)+1)
|
|
entryNames = append(entryNames, "") // include the commit for the treePath itself
|
|
for _, entry := range tes {
|
|
entryNames = append(entryNames, entry.Name())
|
|
}
|
|
|
|
revs, remainingEntryNames, err := getLastCommitForPathsByCache(ctx, commit.ID.String(), treePath, entryNames, gitRepo.LastCommitCache)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
if len(remainingEntryNames) > 0 {
|
|
remainingRevs, err := getLastCommitForPathsWithTimeout(ctx, timeout, gitRepo, commit, treePath, remainingEntryNames)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
maps.Copy(revs, remainingRevs)
|
|
}
|
|
|
|
for _, entry := range tes {
|
|
ci := CommitInfo{Entry: entry, Commit: revs[entry.Name()]}
|
|
if ci.Commit == nil {
|
|
log.Debug("missing commit for %s", entry.Name())
|
|
}
|
|
// If the entry is a submodule, add a submodule file for this
|
|
if entry.IsSubModule() {
|
|
ci.SubmoduleFile, err = GetCommitInfoSubmoduleFile(ctx, repoLink, path.Join(treePath, entry.Name()), gitRepo, commit, entry.ID)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
}
|
|
commitsInfo = append(commitsInfo, ci)
|
|
}
|
|
|
|
// 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 the newest commit for a given path.
|
|
treeLastCommit = util.Iif(treePath == "", commit, revs[""])
|
|
return commitsInfo, treeLastCommit, nil
|
|
}
|
|
|
|
func getLastCommitForPathsByCache(ctx context.Context, 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(ctx, 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, gitRepo *Repository, 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, gitRepo, commit, treePath, paths...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
commitsMap := map[string]*Commit{}
|
|
commitsMap[commit.ID.String()] = commit
|
|
|
|
commitCommits := map[string]*Commit{}
|
|
for path, commitID := range revs {
|
|
if len(commitID) == 0 {
|
|
continue
|
|
}
|
|
|
|
c, ok := commitsMap[commitID]
|
|
if ok {
|
|
commitCommits[path] = c
|
|
continue
|
|
}
|
|
|
|
c, err := gitRepo.GetCommit(ctx, commitID) // Ensure the commit exists in the repository
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
commitCommits[path] = c
|
|
}
|
|
|
|
return commitCommits, nil
|
|
}
|