blob_cache.go 4.1 KB

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