code.gitea.io/gitea@v1.19.3/modules/git/commit_info_nogogit.go (about)

     1  // Copyright 2017 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  //go:build !gogit
     5  
     6  package git
     7  
     8  import (
     9  	"context"
    10  	"fmt"
    11  	"io"
    12  	"path"
    13  	"sort"
    14  
    15  	"code.gitea.io/gitea/modules/log"
    16  )
    17  
    18  // GetCommitsInfo gets information of all commits that are corresponding to these entries
    19  func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath string) ([]CommitInfo, *Commit, error) {
    20  	entryPaths := make([]string, len(tes)+1)
    21  	// Get the commit for the treePath itself
    22  	entryPaths[0] = ""
    23  	for i, entry := range tes {
    24  		entryPaths[i+1] = entry.Name()
    25  	}
    26  
    27  	var err error
    28  
    29  	var revs map[string]*Commit
    30  	if commit.repo.LastCommitCache != nil {
    31  		var unHitPaths []string
    32  		revs, unHitPaths, err = getLastCommitForPathsByCache(ctx, commit.ID.String(), treePath, entryPaths, commit.repo.LastCommitCache)
    33  		if err != nil {
    34  			return nil, nil, err
    35  		}
    36  		if len(unHitPaths) > 0 {
    37  			sort.Strings(unHitPaths)
    38  			commits, err := GetLastCommitForPaths(ctx, commit, treePath, unHitPaths)
    39  			if err != nil {
    40  				return nil, nil, err
    41  			}
    42  
    43  			for pth, found := range commits {
    44  				revs[pth] = found
    45  			}
    46  		}
    47  	} else {
    48  		sort.Strings(entryPaths)
    49  		revs, err = GetLastCommitForPaths(ctx, commit, treePath, entryPaths)
    50  	}
    51  	if err != nil {
    52  		return nil, nil, err
    53  	}
    54  
    55  	commitsInfo := make([]CommitInfo, len(tes))
    56  	for i, entry := range tes {
    57  		commitsInfo[i] = CommitInfo{
    58  			Entry: entry,
    59  		}
    60  
    61  		// Check if we have found a commit for this entry in time
    62  		if entryCommit, ok := revs[entry.Name()]; ok {
    63  			commitsInfo[i].Commit = entryCommit
    64  		} else {
    65  			log.Debug("missing commit for %s", entry.Name())
    66  		}
    67  
    68  		// If the entry if a submodule add a submodule file for this
    69  		if entry.IsSubModule() {
    70  			subModuleURL := ""
    71  			var fullPath string
    72  			if len(treePath) > 0 {
    73  				fullPath = treePath + "/" + entry.Name()
    74  			} else {
    75  				fullPath = entry.Name()
    76  			}
    77  			if subModule, err := commit.GetSubModule(fullPath); err != nil {
    78  				return nil, nil, err
    79  			} else if subModule != nil {
    80  				subModuleURL = subModule.URL
    81  			}
    82  			subModuleFile := NewSubModuleFile(commitsInfo[i].Commit, subModuleURL, entry.ID.String())
    83  			commitsInfo[i].SubModuleFile = subModuleFile
    84  		}
    85  	}
    86  
    87  	// Retrieve the commit for the treePath itself (see above). We basically
    88  	// get it for free during the tree traversal and it's used for listing
    89  	// pages to display information about newest commit for a given path.
    90  	var treeCommit *Commit
    91  	var ok bool
    92  	if treePath == "" {
    93  		treeCommit = commit
    94  	} else if treeCommit, ok = revs[""]; ok {
    95  		treeCommit.repo = commit.repo
    96  	}
    97  	return commitsInfo, treeCommit, nil
    98  }
    99  
   100  func getLastCommitForPathsByCache(ctx context.Context, commitID, treePath string, paths []string, cache *LastCommitCache) (map[string]*Commit, []string, error) {
   101  	var unHitEntryPaths []string
   102  	results := make(map[string]*Commit)
   103  	for _, p := range paths {
   104  		lastCommit, err := cache.Get(commitID, path.Join(treePath, p))
   105  		if err != nil {
   106  			return nil, nil, err
   107  		}
   108  		if lastCommit != nil {
   109  			results[p] = lastCommit
   110  			continue
   111  		}
   112  
   113  		unHitEntryPaths = append(unHitEntryPaths, p)
   114  	}
   115  
   116  	return results, unHitEntryPaths, nil
   117  }
   118  
   119  // GetLastCommitForPaths returns last commit information
   120  func GetLastCommitForPaths(ctx context.Context, commit *Commit, treePath string, paths []string) (map[string]*Commit, error) {
   121  	// We read backwards from the commit to obtain all of the commits
   122  	revs, err := WalkGitLog(ctx, commit.repo, commit, treePath, paths...)
   123  	if err != nil {
   124  		return nil, err
   125  	}
   126  
   127  	batchStdinWriter, batchReader, cancel := commit.repo.CatFileBatch(ctx)
   128  	defer cancel()
   129  
   130  	commitsMap := map[string]*Commit{}
   131  	commitsMap[commit.ID.String()] = commit
   132  
   133  	commitCommits := map[string]*Commit{}
   134  	for path, commitID := range revs {
   135  		c, ok := commitsMap[commitID]
   136  		if ok {
   137  			commitCommits[path] = c
   138  			continue
   139  		}
   140  
   141  		if len(commitID) == 0 {
   142  			continue
   143  		}
   144  
   145  		_, err := batchStdinWriter.Write([]byte(commitID + "\n"))
   146  		if err != nil {
   147  			return nil, err
   148  		}
   149  		_, typ, size, err := ReadBatchLine(batchReader)
   150  		if err != nil {
   151  			return nil, err
   152  		}
   153  		if typ != "commit" {
   154  			return nil, fmt.Errorf("unexpected type: %s for commit id: %s", typ, commitID)
   155  		}
   156  		c, err = CommitFromReader(commit.repo, MustIDFromString(commitID), io.LimitReader(batchReader, size))
   157  		if err != nil {
   158  			return nil, err
   159  		}
   160  		if _, err := batchReader.Discard(1); err != nil {
   161  			return nil, err
   162  		}
   163  		commitCommits[path] = c
   164  	}
   165  
   166  	return commitCommits, nil
   167  }