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.

MergeBaseGenerator.java 6.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. /*
  2. * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
  3. *
  4. * This program and the accompanying materials are made available under the
  5. * terms of the Eclipse Distribution License v. 1.0 which is available at
  6. * https://www.eclipse.org/org/documents/edl-v10.php.
  7. *
  8. * SPDX-License-Identifier: BSD-3-Clause
  9. */
  10. package org.eclipse.jgit.revwalk;
  11. import java.io.IOException;
  12. import java.text.MessageFormat;
  13. import java.util.LinkedList;
  14. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  15. import org.eclipse.jgit.errors.MissingObjectException;
  16. import org.eclipse.jgit.internal.JGitText;
  17. /**
  18. * Computes the merge base(s) of the starting commits.
  19. * <p>
  20. * This generator is selected if the RevFilter is only
  21. * {@link org.eclipse.jgit.revwalk.filter.RevFilter#MERGE_BASE}.
  22. * <p>
  23. * To compute the merge base we assign a temporary flag to each of the starting
  24. * commits. The maximum number of starting commits is bounded by the number of
  25. * free flags available in the RevWalk when the generator is initialized. These
  26. * flags will be automatically released on the next reset of the RevWalk, but
  27. * not until then, as they are assigned to commits throughout the history.
  28. * <p>
  29. * Several internal flags are reused here for a different purpose, but this
  30. * should not have any impact as this generator should be run alone, and without
  31. * any other generators wrapped around it.
  32. */
  33. class MergeBaseGenerator extends Generator {
  34. private static final int PARSED = RevWalk.PARSED;
  35. private static final int IN_PENDING = RevWalk.SEEN;
  36. private static final int POPPED = RevWalk.TEMP_MARK;
  37. private static final int MERGE_BASE = RevWalk.REWRITE;
  38. private final RevWalk walker;
  39. private final DateRevQueue pending;
  40. private int branchMask;
  41. private int recarryTest;
  42. private int recarryMask;
  43. private int mergeBaseAncestor = -1;
  44. private LinkedList<RevCommit> ret = new LinkedList<>();
  45. private CarryStack stack;
  46. MergeBaseGenerator(RevWalk w) {
  47. super(w.isFirstParent());
  48. walker = w;
  49. pending = new DateRevQueue(firstParent);
  50. }
  51. void init(AbstractRevQueue p) throws IOException {
  52. try {
  53. for (;;) {
  54. final RevCommit c = p.next();
  55. if (c == null)
  56. break;
  57. add(c);
  58. }
  59. // Setup the condition used by carryOntoOne to detect a late
  60. // merge base and produce it on the next round.
  61. //
  62. recarryTest = branchMask | POPPED;
  63. recarryMask = branchMask | POPPED | MERGE_BASE;
  64. mergeBaseAncestor = walker.allocFlag();
  65. for (;;) {
  66. RevCommit c = _next();
  67. if (c == null) {
  68. break;
  69. }
  70. ret.add(c);
  71. }
  72. } finally {
  73. // Always free the flags immediately. This ensures the flags
  74. // will be available for reuse when the walk resets.
  75. //
  76. walker.freeFlag(branchMask | mergeBaseAncestor);
  77. }
  78. }
  79. private void add(RevCommit c) {
  80. final int flag = walker.allocFlag();
  81. branchMask |= flag;
  82. if ((c.flags & branchMask) != 0) {
  83. // This should never happen. RevWalk ensures we get a
  84. // commit admitted to the initial queue only once. If
  85. // we see this marks aren't correctly erased.
  86. //
  87. throw new IllegalStateException(MessageFormat.format(JGitText.get().staleRevFlagsOn, c.name()));
  88. }
  89. c.flags |= flag;
  90. pending.add(c);
  91. }
  92. @Override
  93. int outputType() {
  94. return 0;
  95. }
  96. private RevCommit _next() throws MissingObjectException,
  97. IncorrectObjectTypeException, IOException {
  98. for (;;) {
  99. final RevCommit c = pending.next();
  100. if (c == null) {
  101. return null;
  102. }
  103. for (RevCommit p : c.parents) {
  104. if ((p.flags & IN_PENDING) != 0)
  105. continue;
  106. if ((p.flags & PARSED) == 0)
  107. p.parseHeaders(walker);
  108. p.flags |= IN_PENDING;
  109. pending.add(p);
  110. }
  111. int carry = c.flags & branchMask;
  112. boolean mb = carry == branchMask;
  113. if (mb) {
  114. // If we are a merge base make sure our ancestors are
  115. // also flagged as being popped, so that they do not
  116. // generate to the caller.
  117. //
  118. carry |= MERGE_BASE | mergeBaseAncestor;
  119. }
  120. carryOntoHistory(c, carry);
  121. if ((c.flags & MERGE_BASE) != 0) {
  122. // This commit is an ancestor of a merge base we already
  123. // popped back to the caller. If everyone in pending is
  124. // that way we are done traversing; if not we just need
  125. // to move to the next available commit and try again.
  126. //
  127. if (pending.everbodyHasFlag(MERGE_BASE))
  128. return null;
  129. continue;
  130. }
  131. c.flags |= POPPED;
  132. if (mb) {
  133. c.flags |= MERGE_BASE;
  134. return c;
  135. }
  136. }
  137. }
  138. @Override
  139. RevCommit next() throws MissingObjectException,
  140. IncorrectObjectTypeException, IOException {
  141. while (!ret.isEmpty()) {
  142. RevCommit commit = ret.remove();
  143. if ((commit.flags & mergeBaseAncestor) == 0) {
  144. return commit;
  145. }
  146. }
  147. return null;
  148. }
  149. private void carryOntoHistory(RevCommit c, int carry) {
  150. stack = null;
  151. for (;;) {
  152. carryOntoHistoryInnerLoop(c, carry);
  153. if (stack == null) {
  154. break;
  155. }
  156. c = stack.c;
  157. carry = stack.carry;
  158. stack = stack.prev;
  159. }
  160. }
  161. private void carryOntoHistoryInnerLoop(RevCommit c, int carry) {
  162. for (;;) {
  163. RevCommit[] parents = c.parents;
  164. if (parents == null || parents.length == 0) {
  165. break;
  166. }
  167. int e = parents.length - 1;
  168. for (int i = 0; i < e; i++) {
  169. RevCommit p = parents[i];
  170. if (carryOntoOne(p, carry) == CONTINUE) {
  171. // Walking p will be required, buffer p on stack.
  172. stack = new CarryStack(stack, p, carry);
  173. }
  174. // For other results from carryOntoOne:
  175. // HAVE_ALL: p has all bits, do nothing to skip that path.
  176. // CONTINUE_ON_STACK: callee pushed StackElement for p.
  177. }
  178. c = parents[e];
  179. if (carryOntoOne(c, carry) != CONTINUE) {
  180. break;
  181. }
  182. }
  183. }
  184. private static final int CONTINUE = 0;
  185. private static final int HAVE_ALL = 1;
  186. private static final int CONTINUE_ON_STACK = 2;
  187. private int carryOntoOne(RevCommit p, int carry) {
  188. // If we already had all carried flags, our parents do too.
  189. // Return HAVE_ALL to stop caller from running down this leg
  190. // of the revision graph any further.
  191. //
  192. // Otherwise return CONTINUE to ask the caller to walk history.
  193. int rc = (p.flags & carry) == carry ? HAVE_ALL : CONTINUE;
  194. p.flags |= carry;
  195. if ((p.flags & recarryMask) == recarryTest) {
  196. // We were popped without being a merge base, but we just got
  197. // voted to be one. Inject ourselves back at the front of the
  198. // pending queue and tell all of our ancestors they are within
  199. // the merge base now.
  200. p.flags &= ~POPPED;
  201. pending.add(p);
  202. stack = new CarryStack(stack, p, branchMask | MERGE_BASE);
  203. return CONTINUE_ON_STACK;
  204. }
  205. return rc;
  206. }
  207. private static class CarryStack {
  208. final CarryStack prev;
  209. final RevCommit c;
  210. final int carry;
  211. CarryStack(CarryStack prev, RevCommit c, int carry) {
  212. this.prev = prev;
  213. this.c = c;
  214. this.carry = carry;
  215. }
  216. }
  217. }