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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  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. String path = tw.getPathString();
  126. DirCacheIterator di = tw.getTree(dirCache, DirCacheIterator.class);
  127. if (di != null) {
  128. DirCacheEntry dce = di.getDirCacheEntry();
  129. if (dce != null)
  130. if (dce.isAssumeValid())
  131. return false;
  132. }
  133. if (!tw.isPostOrderTraversal()) {
  134. // detect untracked Folders
  135. // Whenever we enter a folder in the workingtree assume it will
  136. // contain only untracked files and add it to
  137. // untrackedParentFolders. If we later find tracked files we will
  138. // remove it from this list
  139. if (FileMode.TREE.equals(wm)) {
  140. // Clean untrackedParentFolders. This potentially moves entries
  141. // from untrackedParentFolders to untrackedFolders
  142. copyUntrackedFolders(path);
  143. // add the folder we just entered to untrackedParentFolders
  144. untrackedParentFolders.addFirst(path);
  145. }
  146. // detect untracked Folders
  147. // Whenever we see a tracked file we know that all of its parent
  148. // folders do not belong into untrackedParentFolders anymore. Clean
  149. // it.
  150. for (int i = 0; i < cnt; i++) {
  151. int rmode = tw.getRawMode(i);
  152. if (i != workingTree && rmode != FileMode.TYPE_MISSING
  153. && FileMode.TREE.equals(rmode)) {
  154. untrackedParentFolders.clear();
  155. break;
  156. }
  157. }
  158. }
  159. // If the working tree file doesn't exist, it does exist for at least
  160. // one other so include this difference.
  161. if (wm == 0)
  162. return true;
  163. // If the path does not appear in the DirCache and its ignored
  164. // we can avoid returning a result here, but only if its not in any
  165. // other tree.
  166. final int dm = tw.getRawMode(dirCache);
  167. WorkingTreeIterator wi = workingTree(tw);
  168. if (dm == FileMode.TYPE_MISSING) {
  169. if (honorIgnores && wi.isEntryIgnored()) {
  170. ignoredPaths.add(wi.getEntryPathString());
  171. int i = 0;
  172. for (; i < cnt; i++) {
  173. if (i == dirCache || i == workingTree)
  174. continue;
  175. if (tw.getRawMode(i) != FileMode.TYPE_MISSING)
  176. break;
  177. }
  178. // If i is cnt then the path does not appear in any other tree,
  179. // and this working tree entry can be safely ignored.
  180. return i == cnt ? false : true;
  181. } else {
  182. // In working tree and not ignored, and not in DirCache.
  183. return true;
  184. }
  185. }
  186. // Always include subtrees as WorkingTreeIterator cannot provide
  187. // efficient elimination of unmodified subtrees.
  188. if (tw.isSubtree())
  189. return true;
  190. // Try the inexpensive comparisons between index and all real trees
  191. // first. Only if we don't find a diff here we have to bother with
  192. // the working tree
  193. for (int i = 0; i < cnt; i++) {
  194. if (i == dirCache || i == workingTree)
  195. continue;
  196. if (tw.getRawMode(i) != dm || !tw.idEqual(i, dirCache))
  197. return true;
  198. }
  199. // Only one chance left to detect a diff: between index and working
  200. // tree. Make use of the WorkingTreeIterator#isModified() method to
  201. // avoid computing SHA1 on filesystem content if not really needed.
  202. return wi.isModified(di.getDirCacheEntry(), true, tw.getObjectReader());
  203. }
  204. /**
  205. * Copy all entries which are still in untrackedParentFolders and which
  206. * belong to a path this treewalk has left into untrackedFolders. It is sure
  207. * that we will not find any tracked files underneath these paths. Therefore
  208. * these paths definitely belong to untracked folders.
  209. *
  210. * @param currentPath
  211. * the current path of the treewalk
  212. */
  213. private void copyUntrackedFolders(String currentPath) {
  214. String pathToBeSaved = null;
  215. while (!untrackedParentFolders.isEmpty()
  216. && !currentPath.startsWith(untrackedParentFolders.getFirst()
  217. + "/")) //$NON-NLS-1$
  218. pathToBeSaved = untrackedParentFolders.removeFirst();
  219. if (pathToBeSaved != null) {
  220. while (!untrackedFolders.isEmpty()
  221. && untrackedFolders.getLast().startsWith(pathToBeSaved))
  222. untrackedFolders.removeLast();
  223. untrackedFolders.addLast(pathToBeSaved);
  224. }
  225. }
  226. private WorkingTreeIterator workingTree(TreeWalk tw) {
  227. return tw.getTree(workingTree, WorkingTreeIterator.class);
  228. }
  229. @Override
  230. public boolean shouldBeRecursive() {
  231. // We cannot compare subtrees in the working tree, so encourage
  232. // use of recursive walks where the subtrees are always dived into.
  233. return true;
  234. }
  235. @Override
  236. public TreeFilter clone() {
  237. return this;
  238. }
  239. @Override
  240. public String toString() {
  241. return "INDEX_DIFF_FILTER"; //$NON-NLS-1$
  242. }
  243. /**
  244. * The method returns the list of ignored files and folders. Only the root
  245. * folder of an ignored folder hierarchy is reported. If a/b/c is listed in
  246. * the .gitignore then you should not expect a/b/c/d/e/f to be reported
  247. * here. Only a/b/c will be reported. Furthermore only ignored files /
  248. * folders are returned that are NOT in the index.
  249. *
  250. * @return ignored paths
  251. */
  252. public Set<String> getIgnoredPaths() {
  253. return ignoredPaths;
  254. }
  255. /**
  256. * @return all paths of folders which contain only untracked files/folders.
  257. * If on the associated treewalk postorder traversal was turned on
  258. * (see {@link TreeWalk#setPostOrderTraversal(boolean)}) then an
  259. * empty list will be returned.
  260. */
  261. public List<String> getUntrackedFolders() {
  262. LinkedList<String> ret = new LinkedList<String>(untrackedFolders);
  263. if (!untrackedParentFolders.isEmpty()) {
  264. String toBeAdded = untrackedParentFolders.getLast();
  265. while (!ret.isEmpty() && ret.getLast().startsWith(toBeAdded))
  266. ret.removeLast();
  267. ret.addLast(toBeAdded);
  268. }
  269. return ret;
  270. }
  271. }