175 lines · 4.4 KB · Go
RawDownload
// Copyright 2017 The Gitea Authors. All rights reserved.// SPDX-License-Identifier: MIT package git import (	"context"	"fmt"	"io"	"path"	"sort" 	"forgejo.org/modules/log") // CommitInfo describes the first commit with the provided entrytype CommitInfo struct {	Entry     *TreeEntry	Commit    *Commit	Submodule Submodule} // GetCommitsInfo gets information of all commits that are corresponding to these entriesfunc (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() {			var fullPath string			if len(treePath) > 0 {				fullPath = treePath + "/" + entry.Name()			} else {				fullPath = entry.Name()			}			submodule, err := commit.GetSubmodule(fullPath, entry)			if err != nil {				return nil, nil, err			}			commitsInfo[i].Submodule = submodule		}	} 	// 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 informationfunc 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}