You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

lfs.go 3.6KB

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