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.

IndexDiffFilter.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. /*
  2. * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com>
  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.treewalk.filter;
  44. import java.io.IOException;
  45. import java.util.HashSet;
  46. import java.util.LinkedList;
  47. import java.util.List;
  48. import java.util.Set;
  49. import org.eclipse.jgit.dircache.DirCacheEntry;
  50. import org.eclipse.jgit.dircache.DirCacheIterator;
  51. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  52. import org.eclipse.jgit.errors.MissingObjectException;
  53. import org.eclipse.jgit.lib.FileMode;
  54. import org.eclipse.jgit.lib.ObjectReader;
  55. import org.eclipse.jgit.treewalk.TreeWalk;
  56. import org.eclipse.jgit.treewalk.WorkingTreeIterator;
  57. /**
  58. * A performance optimized variant of {@link TreeFilter#ANY_DIFF} which should
  59. * be used when among the walked trees there is a {@link DirCacheIterator} and a
  60. * {@link WorkingTreeIterator}. Please see the documentation of
  61. * {@link TreeFilter#ANY_DIFF} for a basic description of the semantics.
  62. * <p>
  63. * This filter tries to avoid computing content ids of the files in the
  64. * working-tree. In contrast to {@link TreeFilter#ANY_DIFF} this filter takes
  65. * care to first compare the entry from the {@link DirCacheIterator} with the
  66. * entries from all other iterators besides the {@link WorkingTreeIterator}.
  67. * Since all those entries have fast access to content ids that is very fast. If
  68. * a difference is detected in this step this filter decides to include that
  69. * path before even looking at the working-tree entry.
  70. * <p>
  71. * If no difference is found then we have to compare index and working-tree as
  72. * the last step. By making use of
  73. * {@link WorkingTreeIterator#isModified(org.eclipse.jgit.dircache.DirCacheEntry, boolean, ObjectReader)}
  74. * we can avoid the computation of the content id if the file is not dirty.
  75. * <p>
  76. * Instances of this filter should not be used for multiple {@link TreeWalk}s.
  77. * Always construct a new instance of this filter for each TreeWalk.
  78. */
  79. public class IndexDiffFilter extends TreeFilter {
  80. private final int dirCache;
  81. private final int workingTree;
  82. private final boolean honorIgnores;
  83. private final Set<String> ignoredPaths = new HashSet<String>();
  84. private final LinkedList<String> untrackedParentFolders = new LinkedList<String>();
  85. private final LinkedList<String> untrackedFolders = new LinkedList<String>();
  86. /**
  87. * Creates a new instance of this filter. Do not use an instance of this
  88. * filter in multiple treewalks.
  89. *
  90. * @param dirCacheIndex
  91. * the index of the {@link DirCacheIterator} in the associated
  92. * treewalk
  93. * @param workingTreeIndex
  94. * the index of the {@link WorkingTreeIterator} in the associated
  95. * treewalk
  96. */
  97. public IndexDiffFilter(int dirCacheIndex, int workingTreeIndex) {
  98. this(dirCacheIndex, workingTreeIndex, true /* honor ignores */);
  99. }
  100. /**
  101. * Creates a new instance of this filter. Do not use an instance of this
  102. * filter in multiple treewalks.
  103. *
  104. * @param dirCacheIndex
  105. * the index of the {@link DirCacheIterator} in the associated
  106. * treewalk
  107. * @param workingTreeIndex
  108. * the index of the {@link WorkingTreeIterator} in the associated
  109. * treewalk
  110. * @param honorIgnores
  111. * true if the filter should skip working tree files that are
  112. * declared as ignored by the standard exclude mechanisms..
  113. */
  114. public IndexDiffFilter(int dirCacheIndex, int workingTreeIndex,
  115. boolean honorIgnores) {
  116. this.dirCache = dirCacheIndex;
  117. this.workingTree = workingTreeIndex;
  118. this.honorIgnores = honorIgnores;
  119. }
  120. @Override
  121. public boolean include(TreeWalk tw) throws MissingObjectException,
  122. IncorrectObjectTypeException, IOException {
  123. final int cnt = tw.getTreeCount();
  124. final int wm = tw.getRawMode(workingTree);
  125. WorkingTreeIterator wi = workingTree(tw);
  126. String path = tw.getPathString();
  127. DirCacheIterator di = tw.getTree(dirCache, DirCacheIterator.class);
  128. if (di != null) {
  129. DirCacheEntry dce = di.getDirCacheEntry();
  130. if (dce != null) {
  131. if (dce.isAssumeValid())
  132. return false;
  133. // Never filter index entries with a stage different from 0
  134. if (dce.getStage() != 0)
  135. return true;
  136. }
  137. }
  138. if (!tw.isPostOrderTraversal()) {
  139. // detect untracked Folders
  140. // Whenever we enter a folder in the workingtree assume it will
  141. // contain only untracked files and add it to
  142. // untrackedParentFolders. If we later find tracked files we will
  143. // remove it from this list
  144. if (FileMode.TREE.equals(wm)
  145. && !(honorIgnores && wi.isEntryIgnored())) {
  146. // Clean untrackedParentFolders. This potentially moves entries
  147. // from untrackedParentFolders to untrackedFolders
  148. copyUntrackedFolders(path);
  149. // add the folder we just entered to untrackedParentFolders
  150. untrackedParentFolders.addFirst(path);
  151. }
  152. // detect untracked Folders
  153. // Whenever we see a tracked file we know that all of its parent
  154. // folders do not belong into untrackedParentFolders anymore. Clean
  155. // it.
  156. for (int i = 0; i < cnt; i++) {
  157. int rmode = tw.getRawMode(i);
  158. if (i != workingTree && rmode != FileMode.TYPE_MISSING
  159. && FileMode.TREE.equals(rmode)) {
  160. untrackedParentFolders.clear();
  161. break;
  162. }
  163. }
  164. }
  165. // If the working tree file doesn't exist, it does exist for at least
  166. // one other so include this difference.
  167. if (wm == 0)
  168. return true;
  169. // If the path does not appear in the DirCache and its ignored
  170. // we can avoid returning a result here, but only if its not in any
  171. // other tree.
  172. final int dm = tw.getRawMode(dirCache);
  173. if (dm == FileMode.TYPE_MISSING) {
  174. if (honorIgnores && wi.isEntryIgnored()) {
  175. ignoredPaths.add(wi.getEntryPathString());
  176. int i = 0;
  177. for (; i < cnt; i++) {
  178. if (i == dirCache || i == workingTree)
  179. continue;
  180. if (tw.getRawMode(i) != FileMode.TYPE_MISSING)
  181. break;
  182. }
  183. // If i is cnt then the path does not appear in any other tree,
  184. // and this working tree entry can be safely ignored.
  185. return i == cnt ? false : true;
  186. } else {
  187. // In working tree and not ignored, and not in DirCache.
  188. return true;
  189. }
  190. }
  191. // Always include subtrees as WorkingTreeIterator cannot provide
  192. // efficient elimination of unmodified subtrees.
  193. if (tw.isSubtree())
  194. return true;
  195. // Try the inexpensive comparisons between index and all real trees
  196. // first. Only if we don't find a diff here we have to bother with
  197. // the working tree
  198. for (int i = 0; i < cnt; i++) {
  199. if (i == dirCache || i == workingTree)
  200. continue;
  201. if (tw.getRawMode(i) != dm || !tw.idEqual(i, dirCache))
  202. return true;
  203. }
  204. // Only one chance left to detect a diff: between index and working
  205. // tree. Make use of the WorkingTreeIterator#isModified() method to
  206. // avoid computing SHA1 on filesystem content if not really needed.
  207. return wi.isModified(di.getDirCacheEntry(), true, tw.getObjectReader());
  208. }
  209. /**
  210. * Copy all entries which are still in untrackedParentFolders and which
  211. * belong to a path this treewalk has left into untrackedFolders. It is sure
  212. * that we will not find any tracked files underneath these paths. Therefore
  213. * these paths definitely belong to untracked folders.
  214. *
  215. * @param currentPath
  216. * the current path of the treewalk
  217. */
  218. private void copyUntrackedFolders(String currentPath) {
  219. String pathToBeSaved = null;
  220. while (!untrackedParentFolders.isEmpty()
  221. && !currentPath.startsWith(untrackedParentFolders.getFirst()
  222. + "/")) //$NON-NLS-1$
  223. pathToBeSaved = untrackedParentFolders.removeFirst();
  224. if (pathToBeSaved != null) {
  225. while (!untrackedFolders.isEmpty()
  226. && untrackedFolders.getLast().startsWith(pathToBeSaved))
  227. untrackedFolders.removeLast();
  228. untrackedFolders.addLast(pathToBeSaved);
  229. }
  230. }
  231. private WorkingTreeIterator workingTree(TreeWalk tw) {
  232. return tw.getTree(workingTree, WorkingTreeIterator.class);
  233. }
  234. @Override
  235. public boolean shouldBeRecursive() {
  236. // We cannot compare subtrees in the working tree, so encourage
  237. // use of recursive walks where the subtrees are always dived into.
  238. return true;
  239. }
  240. @Override
  241. public TreeFilter clone() {
  242. return this;
  243. }
  244. @Override
  245. public String toString() {
  246. return "INDEX_DIFF_FILTER"; //$NON-NLS-1$
  247. }
  248. /**
  249. * The method returns the list of ignored files and folders. Only the root
  250. * folder of an ignored folder hierarchy is reported. If a/b/c is listed in
  251. * the .gitignore then you should not expect a/b/c/d/e/f to be reported
  252. * here. Only a/b/c will be reported. Furthermore only ignored files /
  253. * folders are returned that are NOT in the index.
  254. *
  255. * @return ignored paths
  256. */
  257. public Set<String> getIgnoredPaths() {
  258. return ignoredPaths;
  259. }
  260. /**
  261. * @return all paths of folders which contain only untracked files/folders.
  262. * If on the associated treewalk postorder traversal was turned on
  263. * (see {@link TreeWalk#setPostOrderTraversal(boolean)}) then an
  264. * empty list will be returned.
  265. */
  266. public List<String> getUntrackedFolders() {
  267. LinkedList<String> ret = new LinkedList<String>(untrackedFolders);
  268. if (!untrackedParentFolders.isEmpty()) {
  269. String toBeAdded = untrackedParentFolders.getLast();
  270. while (!ret.isEmpty() && ret.getLast().startsWith(toBeAdded))
  271. ret.removeLast();
  272. ret.addLast(toBeAdded);
  273. }
  274. return ret;
  275. }
  276. }