blob_cache.go 4.5 KB

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