本站源代码
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.

328 lines
6.8KB

  1. package object
  2. import (
  3. "container/list"
  4. "io"
  5. "gopkg.in/src-d/go-git.v4/plumbing"
  6. "gopkg.in/src-d/go-git.v4/plumbing/storer"
  7. "gopkg.in/src-d/go-git.v4/storage"
  8. )
  9. type commitPreIterator struct {
  10. seenExternal map[plumbing.Hash]bool
  11. seen map[plumbing.Hash]bool
  12. stack []CommitIter
  13. start *Commit
  14. }
  15. // NewCommitPreorderIter returns a CommitIter that walks the commit history,
  16. // starting at the given commit and visiting its parents in pre-order.
  17. // The given callback will be called for each visited commit. Each commit will
  18. // be visited only once. If the callback returns an error, walking will stop
  19. // and will return the error. Other errors might be returned if the history
  20. // cannot be traversed (e.g. missing objects). Ignore allows to skip some
  21. // commits from being iterated.
  22. func NewCommitPreorderIter(
  23. c *Commit,
  24. seenExternal map[plumbing.Hash]bool,
  25. ignore []plumbing.Hash,
  26. ) CommitIter {
  27. seen := make(map[plumbing.Hash]bool)
  28. for _, h := range ignore {
  29. seen[h] = true
  30. }
  31. return &commitPreIterator{
  32. seenExternal: seenExternal,
  33. seen: seen,
  34. stack: make([]CommitIter, 0),
  35. start: c,
  36. }
  37. }
  38. func (w *commitPreIterator) Next() (*Commit, error) {
  39. var c *Commit
  40. for {
  41. if w.start != nil {
  42. c = w.start
  43. w.start = nil
  44. } else {
  45. current := len(w.stack) - 1
  46. if current < 0 {
  47. return nil, io.EOF
  48. }
  49. var err error
  50. c, err = w.stack[current].Next()
  51. if err == io.EOF {
  52. w.stack = w.stack[:current]
  53. continue
  54. }
  55. if err != nil {
  56. return nil, err
  57. }
  58. }
  59. if w.seen[c.Hash] || w.seenExternal[c.Hash] {
  60. continue
  61. }
  62. w.seen[c.Hash] = true
  63. if c.NumParents() > 0 {
  64. w.stack = append(w.stack, filteredParentIter(c, w.seen))
  65. }
  66. return c, nil
  67. }
  68. }
  69. func filteredParentIter(c *Commit, seen map[plumbing.Hash]bool) CommitIter {
  70. var hashes []plumbing.Hash
  71. for _, h := range c.ParentHashes {
  72. if !seen[h] {
  73. hashes = append(hashes, h)
  74. }
  75. }
  76. return NewCommitIter(c.s,
  77. storer.NewEncodedObjectLookupIter(c.s, plumbing.CommitObject, hashes),
  78. )
  79. }
  80. func (w *commitPreIterator) ForEach(cb func(*Commit) error) error {
  81. for {
  82. c, err := w.Next()
  83. if err == io.EOF {
  84. break
  85. }
  86. if err != nil {
  87. return err
  88. }
  89. err = cb(c)
  90. if err == storer.ErrStop {
  91. break
  92. }
  93. if err != nil {
  94. return err
  95. }
  96. }
  97. return nil
  98. }
  99. func (w *commitPreIterator) Close() {}
  100. type commitPostIterator struct {
  101. stack []*Commit
  102. seen map[plumbing.Hash]bool
  103. }
  104. // NewCommitPostorderIter returns a CommitIter that walks the commit
  105. // history like WalkCommitHistory but in post-order. This means that after
  106. // walking a merge commit, the merged commit will be walked before the base
  107. // it was merged on. This can be useful if you wish to see the history in
  108. // chronological order. Ignore allows to skip some commits from being iterated.
  109. func NewCommitPostorderIter(c *Commit, ignore []plumbing.Hash) CommitIter {
  110. seen := make(map[plumbing.Hash]bool)
  111. for _, h := range ignore {
  112. seen[h] = true
  113. }
  114. return &commitPostIterator{
  115. stack: []*Commit{c},
  116. seen: seen,
  117. }
  118. }
  119. func (w *commitPostIterator) Next() (*Commit, error) {
  120. for {
  121. if len(w.stack) == 0 {
  122. return nil, io.EOF
  123. }
  124. c := w.stack[len(w.stack)-1]
  125. w.stack = w.stack[:len(w.stack)-1]
  126. if w.seen[c.Hash] {
  127. continue
  128. }
  129. w.seen[c.Hash] = true
  130. return c, c.Parents().ForEach(func(p *Commit) error {
  131. w.stack = append(w.stack, p)
  132. return nil
  133. })
  134. }
  135. }
  136. func (w *commitPostIterator) ForEach(cb func(*Commit) error) error {
  137. for {
  138. c, err := w.Next()
  139. if err == io.EOF {
  140. break
  141. }
  142. if err != nil {
  143. return err
  144. }
  145. err = cb(c)
  146. if err == storer.ErrStop {
  147. break
  148. }
  149. if err != nil {
  150. return err
  151. }
  152. }
  153. return nil
  154. }
  155. func (w *commitPostIterator) Close() {}
  156. // commitAllIterator stands for commit iterator for all refs.
  157. type commitAllIterator struct {
  158. // currCommit points to the current commit.
  159. currCommit *list.Element
  160. }
  161. // NewCommitAllIter returns a new commit iterator for all refs.
  162. // repoStorer is a repo Storer used to get commits and references.
  163. // commitIterFunc is a commit iterator function, used to iterate through ref commits in chosen order
  164. func NewCommitAllIter(repoStorer storage.Storer, commitIterFunc func(*Commit) CommitIter) (CommitIter, error) {
  165. commitsPath := list.New()
  166. commitsLookup := make(map[plumbing.Hash]*list.Element)
  167. head, err := storer.ResolveReference(repoStorer, plumbing.HEAD)
  168. if err == nil {
  169. err = addReference(repoStorer, commitIterFunc, head, commitsPath, commitsLookup)
  170. }
  171. if err != nil && err != plumbing.ErrReferenceNotFound {
  172. return nil, err
  173. }
  174. // add all references along with the HEAD
  175. refIter, err := repoStorer.IterReferences()
  176. if err != nil {
  177. return nil, err
  178. }
  179. defer refIter.Close()
  180. for {
  181. ref, err := refIter.Next()
  182. if err == io.EOF {
  183. break
  184. }
  185. if err == plumbing.ErrReferenceNotFound {
  186. continue
  187. }
  188. if err != nil {
  189. return nil, err
  190. }
  191. if err = addReference(repoStorer, commitIterFunc, ref, commitsPath, commitsLookup); err != nil {
  192. return nil, err
  193. }
  194. }
  195. return &commitAllIterator{commitsPath.Front()}, nil
  196. }
  197. func addReference(
  198. repoStorer storage.Storer,
  199. commitIterFunc func(*Commit) CommitIter,
  200. ref *plumbing.Reference,
  201. commitsPath *list.List,
  202. commitsLookup map[plumbing.Hash]*list.Element) error {
  203. _, exists := commitsLookup[ref.Hash()]
  204. if exists {
  205. // we already have it - skip the reference.
  206. return nil
  207. }
  208. refCommit, _ := GetCommit(repoStorer, ref.Hash())
  209. if refCommit == nil {
  210. // if it's not a commit - skip it.
  211. return nil
  212. }
  213. var (
  214. refCommits []*Commit
  215. parent *list.Element
  216. )
  217. // collect all ref commits to add
  218. commitIter := commitIterFunc(refCommit)
  219. for c, e := commitIter.Next(); e == nil; {
  220. parent, exists = commitsLookup[c.Hash]
  221. if exists {
  222. break
  223. }
  224. refCommits = append(refCommits, c)
  225. c, e = commitIter.Next()
  226. }
  227. commitIter.Close()
  228. if parent == nil {
  229. // common parent - not found
  230. // add all commits to the path from this ref (maybe it's a HEAD and we don't have anything, yet)
  231. for _, c := range refCommits {
  232. parent = commitsPath.PushBack(c)
  233. commitsLookup[c.Hash] = parent
  234. }
  235. } else {
  236. // add ref's commits to the path in reverse order (from the latest)
  237. for i := len(refCommits) - 1; i >= 0; i-- {
  238. c := refCommits[i]
  239. // insert before found common parent
  240. parent = commitsPath.InsertBefore(c, parent)
  241. commitsLookup[c.Hash] = parent
  242. }
  243. }
  244. return nil
  245. }
  246. func (it *commitAllIterator) Next() (*Commit, error) {
  247. if it.currCommit == nil {
  248. return nil, io.EOF
  249. }
  250. c := it.currCommit.Value.(*Commit)
  251. it.currCommit = it.currCommit.Next()
  252. return c, nil
  253. }
  254. func (it *commitAllIterator) ForEach(cb func(*Commit) error) error {
  255. for {
  256. c, err := it.Next()
  257. if err == io.EOF {
  258. break
  259. }
  260. if err != nil {
  261. return err
  262. }
  263. err = cb(c)
  264. if err == storer.ErrStop {
  265. break
  266. }
  267. if err != nil {
  268. return err
  269. }
  270. }
  271. return nil
  272. }
  273. func (it *commitAllIterator) Close() {
  274. it.currCommit = nil
  275. }
上海开阖软件有限公司 沪ICP备12045867号-1