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.

RewriteGenerator.java 4.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  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 org.eclipse.jgit.errors.IncorrectObjectTypeException;
  13. import org.eclipse.jgit.errors.MissingObjectException;
  14. /**
  15. * Replaces a RevCommit's parents until not colored with REWRITE.
  16. * <p>
  17. * Before a RevCommit is returned to the caller its parents are updated to
  18. * create a dense DAG. Instead of reporting the actual parents as recorded when
  19. * the commit was created the returned commit will reflect the next closest
  20. * commit that matched the revision walker's filters.
  21. * <p>
  22. * This generator is the second phase of a path limited revision walk and
  23. * assumes it is receiving RevCommits from {@link TreeRevFilter},
  24. * after they have been fully buffered by {@link AbstractRevQueue}. The full
  25. * buffering is necessary to allow the simple loop used within our own
  26. * {@link #rewrite(RevCommit)} to pull completely through a strand of
  27. * {@link RevWalk#REWRITE} colored commits and come up with a simplification
  28. * that makes the DAG dense. Not fully buffering the commits first would cause
  29. * this loop to abort early, due to commits not being parsed and colored
  30. * correctly.
  31. *
  32. * @see TreeRevFilter
  33. */
  34. class RewriteGenerator extends Generator {
  35. private static final int REWRITE = RevWalk.REWRITE;
  36. /** For {@link #cleanup(RevCommit[])} to remove duplicate parents. */
  37. private static final int DUPLICATE = RevWalk.TEMP_MARK;
  38. private final Generator source;
  39. RewriteGenerator(Generator s) {
  40. super(s.firstParent);
  41. source = s;
  42. }
  43. @Override
  44. void shareFreeList(BlockRevQueue q) {
  45. source.shareFreeList(q);
  46. }
  47. @Override
  48. int outputType() {
  49. return source.outputType() & ~NEEDS_REWRITE;
  50. }
  51. @Override
  52. RevCommit next() throws MissingObjectException,
  53. IncorrectObjectTypeException, IOException {
  54. final RevCommit c = source.next();
  55. if (c == null) {
  56. return null;
  57. }
  58. boolean rewrote = false;
  59. final RevCommit[] pList = c.parents;
  60. final int nParents = pList.length;
  61. for (int i = 0; i < nParents; i++) {
  62. final RevCommit oldp = pList[i];
  63. final RevCommit newp = rewrite(oldp);
  64. if (firstParent) {
  65. if (newp == null) {
  66. c.parents = RevCommit.NO_PARENTS;
  67. } else {
  68. c.parents = new RevCommit[] { newp };
  69. }
  70. return c;
  71. }
  72. if (oldp != newp) {
  73. pList[i] = newp;
  74. rewrote = true;
  75. }
  76. }
  77. if (rewrote) {
  78. c.parents = cleanup(pList);
  79. }
  80. return c;
  81. }
  82. private RevCommit rewrite(RevCommit p) {
  83. for (;;) {
  84. final RevCommit[] pList = p.parents;
  85. if (pList.length > 1) {
  86. // This parent is a merge, so keep it.
  87. //
  88. return p;
  89. }
  90. if ((p.flags & RevWalk.UNINTERESTING) != 0) {
  91. // Retain uninteresting parents. They show where the
  92. // DAG was cut off because it wasn't interesting.
  93. //
  94. return p;
  95. }
  96. if ((p.flags & REWRITE) == 0) {
  97. // This parent was not eligible for rewriting. We
  98. // need to keep it in the DAG.
  99. //
  100. return p;
  101. }
  102. if (pList.length == 0) {
  103. // We can't go back any further, other than to
  104. // just delete the parent entirely.
  105. //
  106. return null;
  107. }
  108. p = pList[0];
  109. }
  110. }
  111. private RevCommit[] cleanup(RevCommit[] oldList) {
  112. // Remove any duplicate parents caused due to rewrites (e.g. a merge
  113. // with two sides that both simplified back into the merge base).
  114. // We also may have deleted a parent by marking it null.
  115. //
  116. int newCnt = 0;
  117. for (int o = 0; o < oldList.length; o++) {
  118. final RevCommit p = oldList[o];
  119. if (p == null)
  120. continue;
  121. if ((p.flags & DUPLICATE) != 0) {
  122. oldList[o] = null;
  123. continue;
  124. }
  125. p.flags |= DUPLICATE;
  126. newCnt++;
  127. }
  128. if (newCnt == oldList.length) {
  129. for (RevCommit p : oldList)
  130. p.flags &= ~DUPLICATE;
  131. return oldList;
  132. }
  133. final RevCommit[] newList = new RevCommit[newCnt];
  134. newCnt = 0;
  135. for (RevCommit p : oldList) {
  136. if (p != null) {
  137. newList[newCnt++] = p;
  138. p.flags &= ~DUPLICATE;
  139. }
  140. }
  141. return newList;
  142. }
  143. }