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.

RewriteTreeFilter.java 8.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. /*
  2. * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
  3. * and other copyright owners as documented in the project's IP log.
  4. *
  5. * This program and the accompanying materials are made available
  6. * under the terms of the Eclipse Distribution License v1.0 which
  7. * accompanies this distribution, is reproduced below, and is
  8. * available at http://www.eclipse.org/org/documents/edl-v10.php
  9. *
  10. * All rights reserved.
  11. *
  12. * Redistribution and use in source and binary forms, with or
  13. * without modification, are permitted provided that the following
  14. * conditions are met:
  15. *
  16. * - Redistributions of source code must retain the above copyright
  17. * notice, this list of conditions and the following disclaimer.
  18. *
  19. * - Redistributions in binary form must reproduce the above
  20. * copyright notice, this list of conditions and the following
  21. * disclaimer in the documentation and/or other materials provided
  22. * with the distribution.
  23. *
  24. * - Neither the name of the Eclipse Foundation, Inc. nor the
  25. * names of its contributors may be used to endorse or promote
  26. * products derived from this software without specific prior
  27. * written permission.
  28. *
  29. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  30. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  31. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  32. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  33. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  34. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  35. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  36. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  37. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  38. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  39. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  40. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  41. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  42. */
  43. package org.eclipse.jgit.revwalk;
  44. import java.io.IOException;
  45. import java.util.List;
  46. import org.eclipse.jgit.diff.DiffEntry;
  47. import org.eclipse.jgit.diff.RenameDetector;
  48. import org.eclipse.jgit.diff.DiffEntry.ChangeType;
  49. import org.eclipse.jgit.errors.CorruptObjectException;
  50. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  51. import org.eclipse.jgit.errors.MissingObjectException;
  52. import org.eclipse.jgit.errors.StopWalkException;
  53. import org.eclipse.jgit.lib.ObjectId;
  54. import org.eclipse.jgit.lib.Repository;
  55. import org.eclipse.jgit.revwalk.filter.RevFilter;
  56. import org.eclipse.jgit.treewalk.TreeWalk;
  57. import org.eclipse.jgit.treewalk.filter.TreeFilter;
  58. /**
  59. * First phase of a path limited revision walk.
  60. * <p>
  61. * This filter is ANDed to evaluate after all other filters and ties the
  62. * configured {@link TreeFilter} into the revision walking process.
  63. * <p>
  64. * Each commit is differenced concurrently against all of its parents to look
  65. * for tree entries that are interesting to the TreeFilter. If none are found
  66. * the commit is colored with {@link RevWalk#REWRITE}, allowing a later pass
  67. * implemented by {@link RewriteGenerator} to remove those colored commits from
  68. * the DAG.
  69. *
  70. * @see RewriteGenerator
  71. */
  72. class RewriteTreeFilter extends RevFilter {
  73. private static final int PARSED = RevWalk.PARSED;
  74. private static final int UNINTERESTING = RevWalk.UNINTERESTING;
  75. private static final int REWRITE = RevWalk.REWRITE;
  76. private final TreeWalk pathFilter;
  77. private final Repository repository;
  78. RewriteTreeFilter(final RevWalk walker, final TreeFilter t) {
  79. repository = walker.repository;
  80. pathFilter = new TreeWalk(walker.reader);
  81. pathFilter.setFilter(t);
  82. pathFilter.setRecursive(t.shouldBeRecursive());
  83. }
  84. @Override
  85. public RevFilter clone() {
  86. throw new UnsupportedOperationException();
  87. }
  88. @Override
  89. public boolean include(final RevWalk walker, final RevCommit c)
  90. throws StopWalkException, MissingObjectException,
  91. IncorrectObjectTypeException, IOException {
  92. // Reset the tree filter to scan this commit and parents.
  93. //
  94. final RevCommit[] pList = c.parents;
  95. final int nParents = pList.length;
  96. final TreeWalk tw = pathFilter;
  97. final ObjectId[] trees = new ObjectId[nParents + 1];
  98. for (int i = 0; i < nParents; i++) {
  99. final RevCommit p = c.parents[i];
  100. if ((p.flags & PARSED) == 0)
  101. p.parseHeaders(walker);
  102. trees[i] = p.getTree();
  103. }
  104. trees[nParents] = c.getTree();
  105. tw.reset(trees);
  106. if (nParents == 1) {
  107. // We have exactly one parent. This is a very common case.
  108. //
  109. int chgs = 0, adds = 0;
  110. while (tw.next()) {
  111. chgs++;
  112. if (tw.getRawMode(0) == 0 && tw.getRawMode(1) != 0)
  113. adds++;
  114. else
  115. break; // no point in looking at this further.
  116. }
  117. if (chgs == 0) {
  118. // No changes, so our tree is effectively the same as
  119. // our parent tree. We pass the buck to our parent.
  120. //
  121. c.flags |= REWRITE;
  122. return false;
  123. } else {
  124. // We have interesting items, but neither of the special
  125. // cases denoted above.
  126. //
  127. if (adds > 0 && tw.getFilter() instanceof FollowFilter) {
  128. // One of the paths we care about was added in this
  129. // commit. We need to update our filter to its older
  130. // name, if we can discover it. Find out what that is.
  131. //
  132. updateFollowFilter(trees);
  133. }
  134. return true;
  135. }
  136. } else if (nParents == 0) {
  137. // We have no parents to compare against. Consider us to be
  138. // REWRITE only if we have no paths matching our filter.
  139. //
  140. if (tw.next())
  141. return true;
  142. c.flags |= REWRITE;
  143. return false;
  144. }
  145. // We are a merge commit. We can only be REWRITE if we are same
  146. // to _all_ parents. We may also be able to eliminate a parent if
  147. // it does not contribute changes to us. Such a parent may be an
  148. // uninteresting side branch.
  149. //
  150. final int[] chgs = new int[nParents];
  151. final int[] adds = new int[nParents];
  152. while (tw.next()) {
  153. final int myMode = tw.getRawMode(nParents);
  154. for (int i = 0; i < nParents; i++) {
  155. final int pMode = tw.getRawMode(i);
  156. if (myMode == pMode && tw.idEqual(i, nParents))
  157. continue;
  158. chgs[i]++;
  159. if (pMode == 0 && myMode != 0)
  160. adds[i]++;
  161. }
  162. }
  163. boolean same = false;
  164. boolean diff = false;
  165. for (int i = 0; i < nParents; i++) {
  166. if (chgs[i] == 0) {
  167. // No changes, so our tree is effectively the same as
  168. // this parent tree. We pass the buck to only this one
  169. // parent commit.
  170. //
  171. final RevCommit p = pList[i];
  172. if ((p.flags & UNINTERESTING) != 0) {
  173. // This parent was marked as not interesting by the
  174. // application. We should look for another parent
  175. // that is interesting.
  176. //
  177. same = true;
  178. continue;
  179. }
  180. c.flags |= REWRITE;
  181. c.parents = new RevCommit[] { p };
  182. return false;
  183. }
  184. if (chgs[i] == adds[i]) {
  185. // All of the differences from this parent were because we
  186. // added files that they did not have. This parent is our
  187. // "empty tree root" and thus their history is not relevant.
  188. // Cut our grandparents to be an empty list.
  189. //
  190. pList[i].parents = RevCommit.NO_PARENTS;
  191. }
  192. // We have an interesting difference relative to this parent.
  193. //
  194. diff = true;
  195. }
  196. if (diff && !same) {
  197. // We did not abort above, so we are different in at least one
  198. // way from all of our parents. We have to take the blame for
  199. // that difference.
  200. //
  201. return true;
  202. }
  203. // We are the same as all of our parents. We must keep them
  204. // as they are and allow those parents to flow into pending
  205. // for further scanning.
  206. //
  207. c.flags |= REWRITE;
  208. return false;
  209. }
  210. private void updateFollowFilter(ObjectId[] trees)
  211. throws MissingObjectException, IncorrectObjectTypeException,
  212. CorruptObjectException, IOException {
  213. TreeWalk tw = pathFilter;
  214. FollowFilter oldFilter = (FollowFilter) tw.getFilter();
  215. tw.setFilter(TreeFilter.ANY_DIFF);
  216. tw.reset(trees);
  217. List<DiffEntry> files = DiffEntry.scan(tw);
  218. RenameDetector rd = new RenameDetector(repository);
  219. rd.addAll(files);
  220. files = rd.compute();
  221. TreeFilter newFilter = oldFilter;
  222. for (DiffEntry ent : files) {
  223. if (isRename(ent) && ent.getNewPath().equals(oldFilter.getPath())) {
  224. newFilter = FollowFilter.create(ent.getOldPath());
  225. break;
  226. }
  227. }
  228. tw.setFilter(newFilter);
  229. }
  230. private static boolean isRename(DiffEntry ent) {
  231. return ent.getChangeType() == ChangeType.RENAME
  232. || ent.getChangeType() == ChangeType.COPY;
  233. }
  234. }