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.

object_walker.go 2.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. package git
  2. import (
  3. "fmt"
  4. "github.com/go-git/go-git/v5/plumbing"
  5. "github.com/go-git/go-git/v5/plumbing/filemode"
  6. "github.com/go-git/go-git/v5/plumbing/object"
  7. "github.com/go-git/go-git/v5/storage"
  8. )
  9. type objectWalker struct {
  10. Storer storage.Storer
  11. // seen is the set of objects seen in the repo.
  12. // seen map can become huge if walking over large
  13. // repos. Thus using struct{} as the value type.
  14. seen map[plumbing.Hash]struct{}
  15. }
  16. func newObjectWalker(s storage.Storer) *objectWalker {
  17. return &objectWalker{s, map[plumbing.Hash]struct{}{}}
  18. }
  19. // walkAllRefs walks all (hash) references from the repo.
  20. func (p *objectWalker) walkAllRefs() error {
  21. // Walk over all the references in the repo.
  22. it, err := p.Storer.IterReferences()
  23. if err != nil {
  24. return err
  25. }
  26. defer it.Close()
  27. err = it.ForEach(func(ref *plumbing.Reference) error {
  28. // Exit this iteration early for non-hash references.
  29. if ref.Type() != plumbing.HashReference {
  30. return nil
  31. }
  32. return p.walkObjectTree(ref.Hash())
  33. })
  34. return err
  35. }
  36. func (p *objectWalker) isSeen(hash plumbing.Hash) bool {
  37. _, seen := p.seen[hash]
  38. return seen
  39. }
  40. func (p *objectWalker) add(hash plumbing.Hash) {
  41. p.seen[hash] = struct{}{}
  42. }
  43. // walkObjectTree walks over all objects and remembers references
  44. // to them in the objectWalker. This is used instead of the revlist
  45. // walks because memory usage is tight with huge repos.
  46. func (p *objectWalker) walkObjectTree(hash plumbing.Hash) error {
  47. // Check if we have already seen, and mark this object
  48. if p.isSeen(hash) {
  49. return nil
  50. }
  51. p.add(hash)
  52. // Fetch the object.
  53. obj, err := object.GetObject(p.Storer, hash)
  54. if err != nil {
  55. return fmt.Errorf("Getting object %s failed: %v", hash, err)
  56. }
  57. // Walk all children depending on object type.
  58. switch obj := obj.(type) {
  59. case *object.Commit:
  60. err = p.walkObjectTree(obj.TreeHash)
  61. if err != nil {
  62. return err
  63. }
  64. for _, h := range obj.ParentHashes {
  65. err = p.walkObjectTree(h)
  66. if err != nil {
  67. return err
  68. }
  69. }
  70. case *object.Tree:
  71. for i := range obj.Entries {
  72. // Shortcut for blob objects:
  73. // 'or' the lower bits of a mode and check that it
  74. // it matches a filemode.Executable. The type information
  75. // is in the higher bits, but this is the cleanest way
  76. // to handle plain files with different modes.
  77. // Other non-tree objects are somewhat rare, so they
  78. // are not special-cased.
  79. if obj.Entries[i].Mode|0755 == filemode.Executable {
  80. p.add(obj.Entries[i].Hash)
  81. continue
  82. }
  83. // Normal walk for sub-trees (and symlinks etc).
  84. err = p.walkObjectTree(obj.Entries[i].Hash)
  85. if err != nil {
  86. return err
  87. }
  88. }
  89. case *object.Tag:
  90. return p.walkObjectTree(obj.Target)
  91. default:
  92. // Error out on unhandled object types.
  93. return fmt.Errorf("Unknown object %X %s %T\n", obj.ID(), obj.Type(), obj)
  94. }
  95. return nil
  96. }