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