mirror of
https://github.com/go-gitea/gitea.git
synced 2026-07-25 10:11:52 +00:00
before: gitrepo vs git packages after: git package fully handle all git operations by the way, use `WithRepo(repo)` instead of `WithDir(repo.Path)` to hide path details. benefits: 1. remove all unnecessary wrappers, developers no need to struggle with "which package should be used" 2. simplify code, RepositoryFacade can (will) be used everywhere, all "path" details are (will be) hidden
150 lines
4.1 KiB
Go
150 lines
4.1 KiB
Go
// Copyright 2020 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package pipeline
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"context"
|
|
"io"
|
|
"sort"
|
|
|
|
"gitea.dev/modules/git"
|
|
"gitea.dev/modules/git/gitcmd"
|
|
)
|
|
|
|
// FindLFSFile finds commits that contain a provided pointer file hash
|
|
func FindLFSFile(ctx context.Context, repo *git.Repository, objectID git.ObjectID) (results []*LFSResult, _ error) {
|
|
cmd := gitcmd.NewCommand("rev-list", "--all")
|
|
revListReader, revListReaderClose := cmd.MakeStdoutPipe()
|
|
defer revListReaderClose()
|
|
err := cmd.WithRepo(repo).
|
|
WithPipelineFunc(func(context gitcmd.Context) (err error) {
|
|
results, err = findLFSFileFunc(ctx, repo, objectID, revListReader)
|
|
return err
|
|
}).RunWithStderr(ctx)
|
|
return results, err
|
|
}
|
|
|
|
func findLFSFileFunc(ctx context.Context, repo *git.Repository, objectID git.ObjectID, revListReader io.Reader) ([]*LFSResult, error) {
|
|
resultsMap := map[string]*LFSResult{}
|
|
results := make([]*LFSResult, 0)
|
|
// Next feed the commits in order into cat-file --batch, followed by their trees and sub trees as necessary.
|
|
// so let's create a batch stdin and stdout
|
|
batch, cancel, err := repo.CatFileBatch(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer cancel()
|
|
|
|
// We'll use a scanner for the revList because it's simpler than a bufio.Reader
|
|
scan := bufio.NewScanner(revListReader)
|
|
trees := []string{}
|
|
paths := []string{}
|
|
|
|
for scan.Scan() {
|
|
// Get the next commit ID
|
|
commitID := scan.Text()
|
|
|
|
// push the commit to the cat-file --batch process
|
|
info, batchReader, err := batch.QueryContent(commitID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var curCommit *git.Commit
|
|
curPath := ""
|
|
|
|
commitReadingLoop:
|
|
for {
|
|
switch info.Type {
|
|
case "tag":
|
|
// This shouldn't happen but if it does well just get the commit and try again
|
|
id, err := git.ReadTagObjectID(batchReader, info.Size)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if info, batchReader, err = batch.QueryContent(id); err != nil {
|
|
return nil, err
|
|
}
|
|
continue
|
|
case "commit":
|
|
// Read in the commit to get its tree and in case this is one of the last used commits
|
|
curCommit, err = git.CommitFromReader(git.MustIDFromString(commitID), io.LimitReader(batchReader, info.Size))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err := batchReader.Discard(1); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if info, _, err = batch.QueryContent(curCommit.TreeID.String()); err != nil {
|
|
return nil, err
|
|
}
|
|
curPath = ""
|
|
case "tree":
|
|
var n int64
|
|
for n < info.Size {
|
|
mode, fname, shaID, count, err := git.ParseCatFileTreeLine(objectID.Type(), batchReader)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
n += int64(count)
|
|
if bytes.Equal(shaID.RawValue(), objectID.RawValue()) {
|
|
result := LFSResult{
|
|
Name: curPath + fname,
|
|
SHA: curCommit.ID.String(),
|
|
Summary: curCommit.MessageTitle(),
|
|
When: curCommit.Author.When,
|
|
ParentHashes: curCommit.Parents,
|
|
}
|
|
resultsMap[curCommit.ID.String()+":"+curPath+fname] = &result
|
|
} else if mode == git.EntryModeTree {
|
|
trees = append(trees, shaID.String())
|
|
paths = append(paths, curPath+fname+"/")
|
|
}
|
|
}
|
|
if _, err := batchReader.Discard(1); err != nil {
|
|
return nil, err
|
|
}
|
|
if len(trees) > 0 {
|
|
info, _, err = batch.QueryContent(trees[len(trees)-1])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
curPath = paths[len(paths)-1]
|
|
trees = trees[:len(trees)-1]
|
|
paths = paths[:len(paths)-1]
|
|
} else {
|
|
break commitReadingLoop
|
|
}
|
|
default:
|
|
if err := git.DiscardFull(batchReader, info.Size+1); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if err := scan.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, result := range resultsMap {
|
|
hasParent := false
|
|
for _, parentID := range result.ParentHashes {
|
|
if _, hasParent = resultsMap[parentID.String()+":"+result.Name]; hasParent {
|
|
break
|
|
}
|
|
}
|
|
if !hasParent {
|
|
results = append(results, result)
|
|
}
|
|
}
|
|
|
|
sort.Sort(lfsResultSlice(results))
|
|
err = fillResultNameRev(ctx, repo, results)
|
|
return results, err
|
|
}
|