code.gitea.io/gitea@v1.22.3/modules/git/pipeline/lfs_gogit.go (about) 1 // Copyright 2020 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 //go:build gogit 5 6 package pipeline 7 8 import ( 9 "bufio" 10 "io" 11 "sort" 12 "strings" 13 "sync" 14 15 "code.gitea.io/gitea/modules/git" 16 17 gogit "github.com/go-git/go-git/v5" 18 "github.com/go-git/go-git/v5/plumbing" 19 "github.com/go-git/go-git/v5/plumbing/object" 20 ) 21 22 // FindLFSFile finds commits that contain a provided pointer file hash 23 func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, error) { 24 resultsMap := map[string]*LFSResult{} 25 results := make([]*LFSResult, 0) 26 27 basePath := repo.Path 28 gogitRepo := repo.GoGitRepo() 29 30 commitsIter, err := gogitRepo.Log(&gogit.LogOptions{ 31 Order: gogit.LogOrderCommitterTime, 32 All: true, 33 }) 34 if err != nil { 35 return nil, lfsError("failed to get GoGit CommitsIter", err) 36 } 37 38 err = commitsIter.ForEach(func(gitCommit *object.Commit) error { 39 tree, err := gitCommit.Tree() 40 if err != nil { 41 return err 42 } 43 treeWalker := object.NewTreeWalker(tree, true, nil) 44 defer treeWalker.Close() 45 for { 46 name, entry, err := treeWalker.Next() 47 if err == io.EOF { 48 break 49 } 50 if entry.Hash == plumbing.Hash(objectID.RawValue()) { 51 parents := make([]git.ObjectID, len(gitCommit.ParentHashes)) 52 for i, parentCommitID := range gitCommit.ParentHashes { 53 parents[i] = git.ParseGogitHash(parentCommitID) 54 } 55 56 result := LFSResult{ 57 Name: name, 58 SHA: gitCommit.Hash.String(), 59 Summary: strings.Split(strings.TrimSpace(gitCommit.Message), "\n")[0], 60 When: gitCommit.Author.When, 61 ParentHashes: parents, 62 } 63 resultsMap[gitCommit.Hash.String()+":"+name] = &result 64 } 65 } 66 return nil 67 }) 68 if err != nil && err != io.EOF { 69 return nil, lfsError("failure in CommitIter.ForEach", err) 70 } 71 72 for _, result := range resultsMap { 73 hasParent := false 74 for _, parentHash := range result.ParentHashes { 75 if _, hasParent = resultsMap[parentHash.String()+":"+result.Name]; hasParent { 76 break 77 } 78 } 79 if !hasParent { 80 results = append(results, result) 81 } 82 } 83 84 sort.Sort(lfsResultSlice(results)) 85 86 // Should really use a go-git function here but name-rev is not completed and recapitulating it is not simple 87 shasToNameReader, shasToNameWriter := io.Pipe() 88 nameRevStdinReader, nameRevStdinWriter := io.Pipe() 89 errChan := make(chan error, 1) 90 wg := sync.WaitGroup{} 91 wg.Add(3) 92 93 go func() { 94 defer wg.Done() 95 scanner := bufio.NewScanner(nameRevStdinReader) 96 i := 0 97 for scanner.Scan() { 98 line := scanner.Text() 99 if len(line) == 0 { 100 continue 101 } 102 result := results[i] 103 result.FullCommitName = line 104 result.BranchName = strings.Split(line, "~")[0] 105 i++ 106 } 107 }() 108 go NameRevStdin(repo.Ctx, shasToNameReader, nameRevStdinWriter, &wg, basePath) 109 go func() { 110 defer wg.Done() 111 defer shasToNameWriter.Close() 112 for _, result := range results { 113 i := 0 114 if i < len(result.SHA) { 115 n, err := shasToNameWriter.Write([]byte(result.SHA)[i:]) 116 if err != nil { 117 errChan <- err 118 break 119 } 120 i += n 121 } 122 n := 0 123 for n < 1 { 124 n, err = shasToNameWriter.Write([]byte{'\n'}) 125 if err != nil { 126 errChan <- err 127 break 128 } 129 130 } 131 132 } 133 }() 134 135 wg.Wait() 136 137 select { 138 case err, has := <-errChan: 139 if has { 140 return nil, lfsError("unable to obtain name for LFS files", err) 141 } 142 default: 143 } 144 145 return results, nil 146 }