blob_cache.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. package hercules
  2. import (
  3. "fmt"
  4. "os"
  5. "gopkg.in/src-d/go-git.v4"
  6. "gopkg.in/src-d/go-git.v4/config"
  7. "gopkg.in/src-d/go-git.v4/plumbing"
  8. "gopkg.in/src-d/go-git.v4/plumbing/object"
  9. "gopkg.in/src-d/go-git.v4/utils/merkletrie"
  10. )
  11. type BlobCache struct {
  12. IgnoreMissingSubmodules bool
  13. repository *git.Repository
  14. cache map[plumbing.Hash]*object.Blob
  15. }
  16. const (
  17. ConfigBlobCacheIgnoreMissingSubmodules = "BlobCache.IgnoreMissingSubmodules"
  18. DependencyBlobCache = "blob_cache"
  19. )
  20. func (cache *BlobCache) Name() string {
  21. return "BlobCache"
  22. }
  23. func (cache *BlobCache) Provides() []string {
  24. arr := [...]string{DependencyBlobCache}
  25. return arr[:]
  26. }
  27. func (cache *BlobCache) Requires() []string {
  28. arr := [...]string{DependencyTreeChanges}
  29. return arr[:]
  30. }
  31. func (cache *BlobCache) ListConfigurationOptions() []ConfigurationOption {
  32. options := [...]ConfigurationOption{{
  33. Name: ConfigBlobCacheIgnoreMissingSubmodules,
  34. Description: "Specifies whether to panic if some submodules do not exist and thus " +
  35. "the corresponding Git objects cannot be loaded.",
  36. Flag: "ignore-missing-submodules",
  37. Type: BoolConfigurationOption,
  38. Default: false}}
  39. return options[:]
  40. }
  41. func (cache *BlobCache) Configure(facts map[string]interface{}) {
  42. if val, exists := facts[ConfigBlobCacheIgnoreMissingSubmodules].(bool); exists {
  43. cache.IgnoreMissingSubmodules = val
  44. }
  45. }
  46. func (cache *BlobCache) Initialize(repository *git.Repository) {
  47. cache.repository = repository
  48. cache.cache = map[plumbing.Hash]*object.Blob{}
  49. }
  50. func (self *BlobCache) Consume(deps map[string]interface{}) (map[string]interface{}, error) {
  51. commit := deps["commit"].(*object.Commit)
  52. changes := deps[DependencyTreeChanges].(object.Changes)
  53. cache := map[plumbing.Hash]*object.Blob{}
  54. newCache := map[plumbing.Hash]*object.Blob{}
  55. for _, change := range changes {
  56. action, err := change.Action()
  57. if err != nil {
  58. fmt.Fprintf(os.Stderr, "no action in %s\n", change.To.TreeEntry.Hash)
  59. return nil, err
  60. }
  61. var exists bool
  62. var blob *object.Blob
  63. switch action {
  64. case merkletrie.Insert:
  65. blob, err = self.getBlob(&change.To, commit.File)
  66. if err != nil {
  67. fmt.Fprintf(os.Stderr, "file to %s %s\n", change.To.Name, change.To.TreeEntry.Hash)
  68. } else {
  69. cache[change.To.TreeEntry.Hash] = blob
  70. newCache[change.To.TreeEntry.Hash] = blob
  71. }
  72. case merkletrie.Delete:
  73. cache[change.From.TreeEntry.Hash], exists = self.cache[change.From.TreeEntry.Hash]
  74. if !exists {
  75. cache[change.From.TreeEntry.Hash], err = self.getBlob(&change.From, commit.File)
  76. if err != nil {
  77. if err.Error() != plumbing.ErrObjectNotFound.Error() {
  78. fmt.Fprintf(os.Stderr, "file from %s %s\n", change.From.Name,
  79. change.From.TreeEntry.Hash)
  80. } else {
  81. cache[change.From.TreeEntry.Hash], err = createDummyBlob(
  82. change.From.TreeEntry.Hash)
  83. }
  84. }
  85. }
  86. case merkletrie.Modify:
  87. blob, err = self.getBlob(&change.To, commit.File)
  88. if err != nil {
  89. fmt.Fprintf(os.Stderr, "file to %s\n", change.To.Name)
  90. } else {
  91. cache[change.To.TreeEntry.Hash] = blob
  92. newCache[change.To.TreeEntry.Hash] = blob
  93. }
  94. cache[change.From.TreeEntry.Hash], exists = self.cache[change.From.TreeEntry.Hash]
  95. if !exists {
  96. cache[change.From.TreeEntry.Hash], err = self.getBlob(&change.From, commit.File)
  97. if err != nil {
  98. fmt.Fprintf(os.Stderr, "file from %s\n", change.From.Name)
  99. }
  100. }
  101. }
  102. if err != nil {
  103. return nil, err
  104. }
  105. }
  106. self.cache = newCache
  107. return map[string]interface{}{DependencyBlobCache: cache}, nil
  108. }
  109. type FileGetter func(path string) (*object.File, error)
  110. func (cache *BlobCache) getBlob(entry *object.ChangeEntry, fileGetter FileGetter) (
  111. *object.Blob, error) {
  112. blob, err := cache.repository.BlobObject(entry.TreeEntry.Hash)
  113. if err != nil {
  114. if err.Error() != plumbing.ErrObjectNotFound.Error() {
  115. fmt.Fprintf(os.Stderr, "getBlob(%s)\n", entry.TreeEntry.Hash.String())
  116. return nil, err
  117. }
  118. if entry.TreeEntry.Mode != 0160000 {
  119. // this is not a submodule
  120. return nil, err
  121. } else if cache.IgnoreMissingSubmodules {
  122. return createDummyBlob(entry.TreeEntry.Hash)
  123. }
  124. file, err_modules := fileGetter(".gitmodules")
  125. if err_modules != nil {
  126. return nil, err_modules
  127. }
  128. contents, err_modules := file.Contents()
  129. if err_modules != nil {
  130. return nil, err_modules
  131. }
  132. modules := config.NewModules()
  133. err_modules = modules.Unmarshal([]byte(contents))
  134. if err_modules != nil {
  135. return nil, err_modules
  136. }
  137. _, exists := modules.Submodules[entry.Name]
  138. if exists {
  139. // we found that this is a submodule
  140. return createDummyBlob(entry.TreeEntry.Hash)
  141. }
  142. return nil, err
  143. }
  144. return blob, nil
  145. }
  146. func init() {
  147. Registry.Register(&BlobCache{})
  148. }