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.

DepthGenerator.java 6.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. /*
  2. * Copyright (C) 2010, Garmin International
  3. * Copyright (C) 2010, Matt Fischer <matt.fischer@garmin.com> and others
  4. *
  5. * This program and the accompanying materials are made available under the
  6. * terms of the Eclipse Distribution License v. 1.0 which is available at
  7. * https://www.eclipse.org/org/documents/edl-v10.php.
  8. *
  9. * SPDX-License-Identifier: BSD-3-Clause
  10. */
  11. package org.eclipse.jgit.revwalk;
  12. import java.io.IOException;
  13. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  14. import org.eclipse.jgit.errors.MissingObjectException;
  15. import org.eclipse.jgit.lib.ObjectId;
  16. /**
  17. * Only produce commits which are below a specified depth.
  18. *
  19. * @see DepthWalk
  20. */
  21. class DepthGenerator extends Generator {
  22. private final FIFORevQueue pending;
  23. private final int depth;
  24. private final int deepenSince;
  25. private final RevWalk walk;
  26. /**
  27. * Commits which used to be shallow in the client, but which are
  28. * being extended as part of this fetch. These commits should be
  29. * returned to the caller as UNINTERESTING so that their blobs/trees
  30. * can be marked appropriately in the pack writer.
  31. */
  32. private final RevFlag UNSHALLOW;
  33. /**
  34. * Commits which the normal framework has marked as UNINTERESTING,
  35. * but which we now care about again. This happens if a client is
  36. * extending a shallow checkout to become deeper--the new commits at
  37. * the bottom of the graph need to be sent, even though they are
  38. * below other commits which the client already has.
  39. */
  40. private final RevFlag REINTERESTING;
  41. /**
  42. * Commits reachable from commits that the client specified using --shallow-exclude.
  43. */
  44. private final RevFlag DEEPEN_NOT;
  45. /**
  46. * @param w
  47. * @param s Parent generator
  48. * @throws MissingObjectException
  49. * @throws IncorrectObjectTypeException
  50. * @throws IOException
  51. */
  52. DepthGenerator(DepthWalk w, Generator s) throws MissingObjectException,
  53. IncorrectObjectTypeException, IOException {
  54. super(s.firstParent);
  55. pending = new FIFORevQueue(firstParent);
  56. walk = (RevWalk)w;
  57. this.depth = w.getDepth();
  58. this.deepenSince = w.getDeepenSince();
  59. this.UNSHALLOW = w.getUnshallowFlag();
  60. this.REINTERESTING = w.getReinterestingFlag();
  61. this.DEEPEN_NOT = w.getDeepenNotFlag();
  62. s.shareFreeList(pending);
  63. // Begin by sucking out all of the source's commits, and
  64. // adding them to the pending queue
  65. FIFORevQueue unshallowCommits = new FIFORevQueue();
  66. for (;;) {
  67. RevCommit c = s.next();
  68. if (c == null)
  69. break;
  70. if (c.has(UNSHALLOW)) {
  71. unshallowCommits.add(c);
  72. } else if (((DepthWalk.Commit) c).getDepth() == 0) {
  73. pending.add(c);
  74. }
  75. }
  76. // Move unshallow commits to the front so that the REINTERESTING flag
  77. // carry over code is executed first.
  78. for (;;) {
  79. RevCommit c = unshallowCommits.next();
  80. if (c == null) {
  81. break;
  82. }
  83. pending.unpop(c);
  84. }
  85. // Mark DEEPEN_NOT on all deepen-not commits and their ancestors.
  86. // TODO(jonathantanmy): This implementation is somewhat
  87. // inefficient in that any "deepen-not <ref>" in the request
  88. // results in all commits reachable from that ref being parsed
  89. // and marked, even if the commit topology is such that it is
  90. // not necessary.
  91. for (ObjectId oid : w.getDeepenNots()) {
  92. RevCommit c;
  93. try {
  94. c = walk.parseCommit(oid);
  95. } catch (IncorrectObjectTypeException notCommit) {
  96. // The C Git implementation silently tolerates
  97. // non-commits, so do the same here.
  98. continue;
  99. }
  100. FIFORevQueue queue = new FIFORevQueue();
  101. queue.add(c);
  102. while ((c = queue.next()) != null) {
  103. if (c.has(DEEPEN_NOT)) {
  104. continue;
  105. }
  106. walk.parseHeaders(c);
  107. c.add(DEEPEN_NOT);
  108. for (RevCommit p : c.getParents()) {
  109. queue.add(p);
  110. }
  111. }
  112. }
  113. }
  114. @Override
  115. int outputType() {
  116. return pending.outputType() | HAS_UNINTERESTING;
  117. }
  118. @Override
  119. void shareFreeList(BlockRevQueue q) {
  120. pending.shareFreeList(q);
  121. }
  122. @Override
  123. RevCommit next() throws MissingObjectException,
  124. IncorrectObjectTypeException, IOException {
  125. // Perform a breadth-first descent into the commit graph,
  126. // marking depths as we go. This means that if a commit is
  127. // reachable by more than one route, we are guaranteed to
  128. // arrive by the shortest route first.
  129. for (;;) {
  130. final DepthWalk.Commit c = (DepthWalk.Commit) pending.next();
  131. if (c == null)
  132. return null;
  133. if ((c.flags & RevWalk.PARSED) == 0)
  134. c.parseHeaders(walk);
  135. if (c.getCommitTime() < deepenSince) {
  136. continue;
  137. }
  138. if (c.has(DEEPEN_NOT)) {
  139. continue;
  140. }
  141. int newDepth = c.depth + 1;
  142. for (int i = 0; i < c.parents.length; i++) {
  143. if (firstParent && i > 0) {
  144. break;
  145. }
  146. RevCommit p = c.parents[i];
  147. DepthWalk.Commit dp = (DepthWalk.Commit) p;
  148. // If no depth has been assigned to this commit, assign
  149. // it now. Since we arrive by the shortest route first,
  150. // this depth is guaranteed to be the smallest value that
  151. // any path could produce.
  152. if (dp.depth == -1) {
  153. boolean failsDeepenSince = false;
  154. if (deepenSince != 0) {
  155. if ((p.flags & RevWalk.PARSED) == 0) {
  156. p.parseHeaders(walk);
  157. }
  158. failsDeepenSince =
  159. p.getCommitTime() < deepenSince;
  160. }
  161. dp.depth = newDepth;
  162. // If the parent is not too deep and was not excluded, add
  163. // it to the queue so that we can produce it later
  164. if (newDepth <= depth && !failsDeepenSince &&
  165. !p.has(DEEPEN_NOT)) {
  166. pending.add(p);
  167. } else {
  168. dp.makesChildBoundary = true;
  169. }
  170. }
  171. if (dp.makesChildBoundary) {
  172. c.isBoundary = true;
  173. }
  174. // If the current commit has become unshallowed, everything
  175. // below us is new to the client. Mark its parent as
  176. // re-interesting, and carry that flag downward to all
  177. // of its ancestors.
  178. if(c.has(UNSHALLOW) || c.has(REINTERESTING)) {
  179. p.add(REINTERESTING);
  180. p.flags &= ~RevWalk.UNINTERESTING;
  181. }
  182. }
  183. boolean produce = true;
  184. // Unshallow commits are uninteresting, but still need to be sent
  185. // up to the PackWriter so that it will exclude objects correctly.
  186. // All other uninteresting commits should be omitted.
  187. if ((c.flags & RevWalk.UNINTERESTING) != 0 && !c.has(UNSHALLOW))
  188. produce = false;
  189. if (c.getCommitTime() < deepenSince) {
  190. produce = false;
  191. }
  192. if (produce)
  193. return c;
  194. }
  195. }
  196. }