選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

TreeWalk.java 42KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282
  1. /*
  2. * Copyright (C) 2008-2009, Google Inc.
  3. * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
  4. * and other copyright owners as documented in the project's IP log.
  5. *
  6. * This program and the accompanying materials are made available
  7. * under the terms of the Eclipse Distribution License v1.0 which
  8. * accompanies this distribution, is reproduced below, and is
  9. * available at http://www.eclipse.org/org/documents/edl-v10.php
  10. *
  11. * All rights reserved.
  12. *
  13. * Redistribution and use in source and binary forms, with or
  14. * without modification, are permitted provided that the following
  15. * conditions are met:
  16. *
  17. * - Redistributions of source code must retain the above copyright
  18. * notice, this list of conditions and the following disclaimer.
  19. *
  20. * - Redistributions in binary form must reproduce the above
  21. * copyright notice, this list of conditions and the following
  22. * disclaimer in the documentation and/or other materials provided
  23. * with the distribution.
  24. *
  25. * - Neither the name of the Eclipse Foundation, Inc. nor the
  26. * names of its contributors may be used to endorse or promote
  27. * products derived from this software without specific prior
  28. * written permission.
  29. *
  30. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  31. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  32. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  33. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  34. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  35. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  36. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  37. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  38. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  39. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  40. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  41. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  42. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  43. */
  44. package org.eclipse.jgit.treewalk;
  45. import java.io.IOException;
  46. import java.util.HashMap;
  47. import java.util.Map;
  48. import java.util.Set;
  49. import org.eclipse.jgit.api.errors.JGitInternalException;
  50. import org.eclipse.jgit.attributes.Attribute;
  51. import org.eclipse.jgit.attributes.Attributes;
  52. import org.eclipse.jgit.attributes.AttributesNodeProvider;
  53. import org.eclipse.jgit.attributes.AttributesProvider;
  54. import org.eclipse.jgit.dircache.DirCacheBuildIterator;
  55. import org.eclipse.jgit.attributes.AttributesHandler;
  56. import org.eclipse.jgit.dircache.DirCacheIterator;
  57. import org.eclipse.jgit.errors.CorruptObjectException;
  58. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  59. import org.eclipse.jgit.errors.MissingObjectException;
  60. import org.eclipse.jgit.errors.StopWalkException;
  61. import org.eclipse.jgit.lib.AnyObjectId;
  62. import org.eclipse.jgit.lib.Config;
  63. import org.eclipse.jgit.lib.Constants;
  64. import org.eclipse.jgit.lib.FileMode;
  65. import org.eclipse.jgit.lib.MutableObjectId;
  66. import org.eclipse.jgit.lib.ObjectId;
  67. import org.eclipse.jgit.lib.ObjectReader;
  68. import org.eclipse.jgit.lib.Repository;
  69. import org.eclipse.jgit.revwalk.RevTree;
  70. import org.eclipse.jgit.treewalk.filter.PathFilter;
  71. import org.eclipse.jgit.treewalk.filter.TreeFilter;
  72. import org.eclipse.jgit.util.QuotedString;
  73. import org.eclipse.jgit.util.RawParseUtils;
  74. /**
  75. * Walks one or more {@link AbstractTreeIterator}s in parallel.
  76. * <p>
  77. * This class can perform n-way differences across as many trees as necessary.
  78. * <p>
  79. * Each tree added must have the same root as existing trees in the walk.
  80. * <p>
  81. * A TreeWalk instance can only be used once to generate results. Running a
  82. * second time requires creating a new TreeWalk instance, or invoking
  83. * {@link #reset()} and adding new trees before starting again. Resetting an
  84. * existing instance may be faster for some applications as some internal
  85. * buffers may be recycled.
  86. * <p>
  87. * TreeWalk instances are not thread-safe. Applications must either restrict
  88. * usage of a TreeWalk instance to a single thread, or implement their own
  89. * synchronization at a higher level.
  90. * <p>
  91. * Multiple simultaneous TreeWalk instances per {@link Repository} are
  92. * permitted, even from concurrent threads.
  93. */
  94. public class TreeWalk implements AutoCloseable, AttributesProvider {
  95. private static final AbstractTreeIterator[] NO_TREES = {};
  96. /**
  97. * @since 4.2
  98. */
  99. public static enum OperationType {
  100. /**
  101. * Represents a checkout operation (for example a checkout or reset
  102. * operation).
  103. */
  104. CHECKOUT_OP,
  105. /**
  106. * Represents a checkin operation (for example an add operation)
  107. */
  108. CHECKIN_OP
  109. }
  110. /**
  111. * Type of operation you want to retrieve the git attributes for.
  112. */
  113. private OperationType operationType = OperationType.CHECKOUT_OP;
  114. /**
  115. * The filter command as defined in gitattributes. The keys are
  116. * filterName+"."+filterCommandType. E.g. "lfs.clean"
  117. */
  118. private Map<String, String> filterCommandsByNameDotType = new HashMap<String, String>();
  119. /**
  120. * @param operationType
  121. * @since 4.2
  122. */
  123. public void setOperationType(OperationType operationType) {
  124. this.operationType = operationType;
  125. }
  126. /**
  127. * Open a tree walk and filter to exactly one path.
  128. * <p>
  129. * The returned tree walk is already positioned on the requested path, so
  130. * the caller should not need to invoke {@link #next()} unless they are
  131. * looking for a possible directory/file name conflict.
  132. *
  133. * @param reader
  134. * the reader the walker will obtain tree data from.
  135. * @param path
  136. * single path to advance the tree walk instance into.
  137. * @param trees
  138. * one or more trees to walk through, all with the same root.
  139. * @return a new tree walk configured for exactly this one path; null if no
  140. * path was found in any of the trees.
  141. * @throws IOException
  142. * reading a pack file or loose object failed.
  143. * @throws CorruptObjectException
  144. * an tree object could not be read as its data stream did not
  145. * appear to be a tree, or could not be inflated.
  146. * @throws IncorrectObjectTypeException
  147. * an object we expected to be a tree was not a tree.
  148. * @throws MissingObjectException
  149. * a tree object was not found.
  150. */
  151. public static TreeWalk forPath(final ObjectReader reader, final String path,
  152. final AnyObjectId... trees) throws MissingObjectException,
  153. IncorrectObjectTypeException, CorruptObjectException, IOException {
  154. TreeWalk tw = new TreeWalk(reader);
  155. PathFilter f = PathFilter.create(path);
  156. tw.setFilter(f);
  157. tw.reset(trees);
  158. tw.setRecursive(false);
  159. while (tw.next()) {
  160. if (f.isDone(tw)) {
  161. return tw;
  162. } else if (tw.isSubtree()) {
  163. tw.enterSubtree();
  164. }
  165. }
  166. return null;
  167. }
  168. /**
  169. * Open a tree walk and filter to exactly one path.
  170. * <p>
  171. * The returned tree walk is already positioned on the requested path, so
  172. * the caller should not need to invoke {@link #next()} unless they are
  173. * looking for a possible directory/file name conflict.
  174. *
  175. * @param db
  176. * repository to read tree object data from.
  177. * @param path
  178. * single path to advance the tree walk instance into.
  179. * @param trees
  180. * one or more trees to walk through, all with the same root.
  181. * @return a new tree walk configured for exactly this one path; null if no
  182. * path was found in any of the trees.
  183. * @throws IOException
  184. * reading a pack file or loose object failed.
  185. * @throws CorruptObjectException
  186. * an tree object could not be read as its data stream did not
  187. * appear to be a tree, or could not be inflated.
  188. * @throws IncorrectObjectTypeException
  189. * an object we expected to be a tree was not a tree.
  190. * @throws MissingObjectException
  191. * a tree object was not found.
  192. */
  193. public static TreeWalk forPath(final Repository db, final String path,
  194. final AnyObjectId... trees) throws MissingObjectException,
  195. IncorrectObjectTypeException, CorruptObjectException, IOException {
  196. try (ObjectReader reader = db.newObjectReader()) {
  197. return forPath(reader, path, trees);
  198. }
  199. }
  200. /**
  201. * Open a tree walk and filter to exactly one path.
  202. * <p>
  203. * The returned tree walk is already positioned on the requested path, so
  204. * the caller should not need to invoke {@link #next()} unless they are
  205. * looking for a possible directory/file name conflict.
  206. *
  207. * @param db
  208. * repository to read tree object data from.
  209. * @param path
  210. * single path to advance the tree walk instance into.
  211. * @param tree
  212. * the single tree to walk through.
  213. * @return a new tree walk configured for exactly this one path; null if no
  214. * path was found in any of the trees.
  215. * @throws IOException
  216. * reading a pack file or loose object failed.
  217. * @throws CorruptObjectException
  218. * an tree object could not be read as its data stream did not
  219. * appear to be a tree, or could not be inflated.
  220. * @throws IncorrectObjectTypeException
  221. * an object we expected to be a tree was not a tree.
  222. * @throws MissingObjectException
  223. * a tree object was not found.
  224. */
  225. public static TreeWalk forPath(final Repository db, final String path,
  226. final RevTree tree) throws MissingObjectException,
  227. IncorrectObjectTypeException, CorruptObjectException, IOException {
  228. return forPath(db, path, new ObjectId[] { tree });
  229. }
  230. private final ObjectReader reader;
  231. private final boolean closeReader;
  232. private final MutableObjectId idBuffer = new MutableObjectId();
  233. private TreeFilter filter;
  234. AbstractTreeIterator[] trees;
  235. private boolean recursive;
  236. private boolean postOrderTraversal;
  237. int depth;
  238. private boolean advance;
  239. private boolean postChildren;
  240. private AttributesNodeProvider attributesNodeProvider;
  241. AbstractTreeIterator currentHead;
  242. /** Cached attribute for the current entry */
  243. private Attributes attrs = null;
  244. /** Cached attributes handler */
  245. private AttributesHandler attributesHandler;
  246. private Config config;
  247. /**
  248. * Create a new tree walker for a given repository.
  249. *
  250. * @param repo
  251. * the repository the walker will obtain data from. An
  252. * ObjectReader will be created by the walker, and will be closed
  253. * when the walker is closed.
  254. */
  255. public TreeWalk(final Repository repo) {
  256. this(repo.newObjectReader(), true);
  257. config = repo.getConfig();
  258. attributesNodeProvider = repo.createAttributesNodeProvider();
  259. }
  260. /**
  261. * Create a new tree walker for a given repository.
  262. *
  263. * @param or
  264. * the reader the walker will obtain tree data from. The reader
  265. * is not closed when the walker is closed.
  266. */
  267. public TreeWalk(final ObjectReader or) {
  268. this(or, false);
  269. }
  270. private TreeWalk(final ObjectReader or, final boolean closeReader) {
  271. reader = or;
  272. filter = TreeFilter.ALL;
  273. trees = NO_TREES;
  274. this.closeReader = closeReader;
  275. }
  276. /** @return the reader this walker is using to load objects. */
  277. public ObjectReader getObjectReader() {
  278. return reader;
  279. }
  280. /**
  281. * @return the {@link OperationType}
  282. * @since 4.3
  283. */
  284. public OperationType getOperationType() {
  285. return operationType;
  286. }
  287. /**
  288. * Release any resources used by this walker's reader.
  289. * <p>
  290. * A walker that has been released can be used again, but may need to be
  291. * released after the subsequent usage.
  292. *
  293. * @since 4.0
  294. */
  295. @Override
  296. public void close() {
  297. if (closeReader) {
  298. reader.close();
  299. }
  300. }
  301. /**
  302. * Get the currently configured filter.
  303. *
  304. * @return the current filter. Never null as a filter is always needed.
  305. */
  306. public TreeFilter getFilter() {
  307. return filter;
  308. }
  309. /**
  310. * Set the tree entry filter for this walker.
  311. * <p>
  312. * Multiple filters may be combined by constructing an arbitrary tree of
  313. * <code>AndTreeFilter</code> or <code>OrTreeFilter</code> instances to
  314. * describe the boolean expression required by the application. Custom
  315. * filter implementations may also be constructed by applications.
  316. * <p>
  317. * Note that filters are not thread-safe and may not be shared by concurrent
  318. * TreeWalk instances. Every TreeWalk must be supplied its own unique
  319. * filter, unless the filter implementation specifically states it is (and
  320. * always will be) thread-safe. Callers may use {@link TreeFilter#clone()}
  321. * to create a unique filter tree for this TreeWalk instance.
  322. *
  323. * @param newFilter
  324. * the new filter. If null the special {@link TreeFilter#ALL}
  325. * filter will be used instead, as it matches every entry.
  326. * @see org.eclipse.jgit.treewalk.filter.AndTreeFilter
  327. * @see org.eclipse.jgit.treewalk.filter.OrTreeFilter
  328. */
  329. public void setFilter(final TreeFilter newFilter) {
  330. filter = newFilter != null ? newFilter : TreeFilter.ALL;
  331. }
  332. /**
  333. * Is this walker automatically entering into subtrees?
  334. * <p>
  335. * If the walker is recursive then the caller will not see a subtree node
  336. * and instead will only receive file nodes in all relevant subtrees.
  337. *
  338. * @return true if automatically entering subtrees is enabled.
  339. */
  340. public boolean isRecursive() {
  341. return recursive;
  342. }
  343. /**
  344. * Set the walker to enter (or not enter) subtrees automatically.
  345. * <p>
  346. * If recursive mode is enabled the walker will hide subtree nodes from the
  347. * calling application and will produce only file level nodes. If a tree
  348. * (directory) is deleted then all of the file level nodes will appear to be
  349. * deleted, recursively, through as many levels as necessary to account for
  350. * all entries.
  351. *
  352. * @param b
  353. * true to skip subtree nodes and only obtain files nodes.
  354. */
  355. public void setRecursive(final boolean b) {
  356. recursive = b;
  357. }
  358. /**
  359. * Does this walker return a tree entry after it exits the subtree?
  360. * <p>
  361. * If post order traversal is enabled then the walker will return a subtree
  362. * after it has returned the last entry within that subtree. This may cause
  363. * a subtree to be seen by the application twice if {@link #isRecursive()}
  364. * is false, as the application will see it once, call
  365. * {@link #enterSubtree()}, and then see it again as it leaves the subtree.
  366. * <p>
  367. * If an application does not enable {@link #isRecursive()} and it does not
  368. * call {@link #enterSubtree()} then the tree is returned only once as none
  369. * of the children were processed.
  370. *
  371. * @return true if subtrees are returned after entries within the subtree.
  372. */
  373. public boolean isPostOrderTraversal() {
  374. return postOrderTraversal;
  375. }
  376. /**
  377. * Set the walker to return trees after their children.
  378. *
  379. * @param b
  380. * true to get trees after their children.
  381. * @see #isPostOrderTraversal()
  382. */
  383. public void setPostOrderTraversal(final boolean b) {
  384. postOrderTraversal = b;
  385. }
  386. /**
  387. * Sets the {@link AttributesNodeProvider} for this {@link TreeWalk}.
  388. * <p>
  389. * This is a requirement for a correct computation of the git attributes.
  390. * If this {@link TreeWalk} has been built using
  391. * {@link #TreeWalk(Repository)} constructor, the
  392. * {@link AttributesNodeProvider} has already been set. Indeed,the
  393. * {@link Repository} can provide an {@link AttributesNodeProvider} using
  394. * {@link Repository#createAttributesNodeProvider()} method. Otherwise you
  395. * should provide one.
  396. * </p>
  397. *
  398. * @see Repository#createAttributesNodeProvider()
  399. * @param provider
  400. * @since 4.2
  401. */
  402. public void setAttributesNodeProvider(AttributesNodeProvider provider) {
  403. attributesNodeProvider = provider;
  404. }
  405. /**
  406. * @return the {@link AttributesNodeProvider} for this {@link TreeWalk}.
  407. * @since 4.3
  408. */
  409. public AttributesNodeProvider getAttributesNodeProvider() {
  410. return attributesNodeProvider;
  411. }
  412. /**
  413. * Retrieve the git attributes for the current entry.
  414. *
  415. * <h4>Git attribute computation</h4>
  416. *
  417. * <ul>
  418. * <li>Get the attributes matching the current path entry from the info file
  419. * (see {@link AttributesNodeProvider#getInfoAttributesNode()}).</li>
  420. * <li>Completes the list of attributes using the .gitattributes files
  421. * located on the current path (the further the directory that contains
  422. * .gitattributes is from the path in question, the lower its precedence).
  423. * For a checkin operation, it will look first on the working tree (if any).
  424. * If there is no attributes file, it will fallback on the index. For a
  425. * checkout operation, it will first use the index entry and then fallback
  426. * on the working tree if none.</li>
  427. * <li>In the end, completes the list of matching attributes using the
  428. * global attribute file define in the configuration (see
  429. * {@link AttributesNodeProvider#getGlobalAttributesNode()})</li>
  430. *
  431. * </ul>
  432. *
  433. *
  434. * <h4>Iterator constraints</h4>
  435. *
  436. * <p>
  437. * In order to have a correct list of attributes for the current entry, this
  438. * {@link TreeWalk} requires to have at least one
  439. * {@link AttributesNodeProvider} and a {@link DirCacheIterator} set up. An
  440. * {@link AttributesNodeProvider} is used to retrieve the attributes from
  441. * the info attributes file and the global attributes file. The
  442. * {@link DirCacheIterator} is used to retrieve the .gitattributes files
  443. * stored in the index. A {@link WorkingTreeIterator} can also be provided
  444. * to access the local version of the .gitattributes files. If none is
  445. * provided it will fallback on the {@link DirCacheIterator}.
  446. * </p>
  447. *
  448. * @return a {@link Set} of {@link Attribute}s that match the current entry.
  449. * @since 4.2
  450. */
  451. public Attributes getAttributes() {
  452. if (attrs != null)
  453. return attrs;
  454. if (attributesNodeProvider == null) {
  455. // The work tree should have a AttributesNodeProvider to be able to
  456. // retrieve the info and global attributes node
  457. throw new IllegalStateException(
  458. "The tree walk should have one AttributesNodeProvider set in order to compute the git attributes."); //$NON-NLS-1$
  459. }
  460. try {
  461. // Lazy create the attributesHandler on the first access of
  462. // attributes. This requires the info, global and root
  463. // attributes nodes
  464. if (attributesHandler == null) {
  465. attributesHandler = new AttributesHandler(this);
  466. }
  467. attrs = attributesHandler.getAttributes();
  468. return attrs;
  469. } catch (IOException e) {
  470. throw new JGitInternalException("Error while parsing attributes", //$NON-NLS-1$
  471. e);
  472. }
  473. }
  474. /** Reset this walker so new tree iterators can be added to it. */
  475. public void reset() {
  476. attrs = null;
  477. attributesHandler = null;
  478. trees = NO_TREES;
  479. advance = false;
  480. depth = 0;
  481. }
  482. /**
  483. * Reset this walker to run over a single existing tree.
  484. *
  485. * @param id
  486. * the tree we need to parse. The walker will execute over this
  487. * single tree if the reset is successful.
  488. * @throws MissingObjectException
  489. * the given tree object does not exist in this repository.
  490. * @throws IncorrectObjectTypeException
  491. * the given object id does not denote a tree, but instead names
  492. * some other non-tree type of object. Note that commits are not
  493. * trees, even if they are sometimes called a "tree-ish".
  494. * @throws CorruptObjectException
  495. * the object claimed to be a tree, but its contents did not
  496. * appear to be a tree. The repository may have data corruption.
  497. * @throws IOException
  498. * a loose object or pack file could not be read.
  499. */
  500. public void reset(final AnyObjectId id) throws MissingObjectException,
  501. IncorrectObjectTypeException, CorruptObjectException, IOException {
  502. if (trees.length == 1) {
  503. AbstractTreeIterator o = trees[0];
  504. while (o.parent != null)
  505. o = o.parent;
  506. if (o instanceof CanonicalTreeParser) {
  507. o.matches = null;
  508. o.matchShift = 0;
  509. ((CanonicalTreeParser) o).reset(reader, id);
  510. trees[0] = o;
  511. } else {
  512. trees[0] = parserFor(id);
  513. }
  514. } else {
  515. trees = new AbstractTreeIterator[] { parserFor(id) };
  516. }
  517. advance = false;
  518. depth = 0;
  519. attrs = null;
  520. }
  521. /**
  522. * Reset this walker to run over a set of existing trees.
  523. *
  524. * @param ids
  525. * the trees we need to parse. The walker will execute over this
  526. * many parallel trees if the reset is successful.
  527. * @throws MissingObjectException
  528. * the given tree object does not exist in this repository.
  529. * @throws IncorrectObjectTypeException
  530. * the given object id does not denote a tree, but instead names
  531. * some other non-tree type of object. Note that commits are not
  532. * trees, even if they are sometimes called a "tree-ish".
  533. * @throws CorruptObjectException
  534. * the object claimed to be a tree, but its contents did not
  535. * appear to be a tree. The repository may have data corruption.
  536. * @throws IOException
  537. * a loose object or pack file could not be read.
  538. */
  539. public void reset(final AnyObjectId... ids) throws MissingObjectException,
  540. IncorrectObjectTypeException, CorruptObjectException, IOException {
  541. final int oldLen = trees.length;
  542. final int newLen = ids.length;
  543. final AbstractTreeIterator[] r = newLen == oldLen ? trees
  544. : new AbstractTreeIterator[newLen];
  545. for (int i = 0; i < newLen; i++) {
  546. AbstractTreeIterator o;
  547. if (i < oldLen) {
  548. o = trees[i];
  549. while (o.parent != null)
  550. o = o.parent;
  551. if (o instanceof CanonicalTreeParser && o.pathOffset == 0) {
  552. o.matches = null;
  553. o.matchShift = 0;
  554. ((CanonicalTreeParser) o).reset(reader, ids[i]);
  555. r[i] = o;
  556. continue;
  557. }
  558. }
  559. o = parserFor(ids[i]);
  560. r[i] = o;
  561. }
  562. trees = r;
  563. advance = false;
  564. depth = 0;
  565. attrs = null;
  566. }
  567. /**
  568. * Add an already existing tree object for walking.
  569. * <p>
  570. * The position of this tree is returned to the caller, in case the caller
  571. * has lost track of the order they added the trees into the walker.
  572. * <p>
  573. * The tree must have the same root as existing trees in the walk.
  574. *
  575. * @param id
  576. * identity of the tree object the caller wants walked.
  577. * @return position of this tree within the walker.
  578. * @throws MissingObjectException
  579. * the given tree object does not exist in this repository.
  580. * @throws IncorrectObjectTypeException
  581. * the given object id does not denote a tree, but instead names
  582. * some other non-tree type of object. Note that commits are not
  583. * trees, even if they are sometimes called a "tree-ish".
  584. * @throws CorruptObjectException
  585. * the object claimed to be a tree, but its contents did not
  586. * appear to be a tree. The repository may have data corruption.
  587. * @throws IOException
  588. * a loose object or pack file could not be read.
  589. */
  590. public int addTree(final AnyObjectId id) throws MissingObjectException,
  591. IncorrectObjectTypeException, CorruptObjectException, IOException {
  592. return addTree(parserFor(id));
  593. }
  594. /**
  595. * Add an already created tree iterator for walking.
  596. * <p>
  597. * The position of this tree is returned to the caller, in case the caller
  598. * has lost track of the order they added the trees into the walker.
  599. * <p>
  600. * The tree which the iterator operates on must have the same root as
  601. * existing trees in the walk.
  602. *
  603. * @param p
  604. * an iterator to walk over. The iterator should be new, with no
  605. * parent, and should still be positioned before the first entry.
  606. * The tree which the iterator operates on must have the same
  607. * root as other trees in the walk.
  608. * @return position of this tree within the walker.
  609. */
  610. public int addTree(AbstractTreeIterator p) {
  611. int n = trees.length;
  612. AbstractTreeIterator[] newTrees = new AbstractTreeIterator[n + 1];
  613. System.arraycopy(trees, 0, newTrees, 0, n);
  614. newTrees[n] = p;
  615. p.matches = null;
  616. p.matchShift = 0;
  617. trees = newTrees;
  618. return n;
  619. }
  620. /**
  621. * Get the number of trees known to this walker.
  622. *
  623. * @return the total number of trees this walker is iterating over.
  624. */
  625. public int getTreeCount() {
  626. return trees.length;
  627. }
  628. /**
  629. * Advance this walker to the next relevant entry.
  630. *
  631. * @return true if there is an entry available; false if all entries have
  632. * been walked and the walk of this set of tree iterators is over.
  633. * @throws MissingObjectException
  634. * {@link #isRecursive()} was enabled, a subtree was found, but
  635. * the subtree object does not exist in this repository. The
  636. * repository may be missing objects.
  637. * @throws IncorrectObjectTypeException
  638. * {@link #isRecursive()} was enabled, a subtree was found, and
  639. * the subtree id does not denote a tree, but instead names some
  640. * other non-tree type of object. The repository may have data
  641. * corruption.
  642. * @throws CorruptObjectException
  643. * the contents of a tree did not appear to be a tree. The
  644. * repository may have data corruption.
  645. * @throws IOException
  646. * a loose object or pack file could not be read.
  647. */
  648. public boolean next() throws MissingObjectException,
  649. IncorrectObjectTypeException, CorruptObjectException, IOException {
  650. try {
  651. attrs = null;
  652. if (advance) {
  653. advance = false;
  654. postChildren = false;
  655. popEntriesEqual();
  656. }
  657. for (;;) {
  658. final AbstractTreeIterator t = min();
  659. if (t.eof()) {
  660. if (depth > 0) {
  661. exitSubtree();
  662. if (postOrderTraversal) {
  663. advance = true;
  664. postChildren = true;
  665. return true;
  666. }
  667. popEntriesEqual();
  668. continue;
  669. }
  670. return false;
  671. }
  672. currentHead = t;
  673. if (!filter.include(this)) {
  674. skipEntriesEqual();
  675. continue;
  676. }
  677. if (recursive && FileMode.TREE.equals(t.mode)) {
  678. enterSubtree();
  679. continue;
  680. }
  681. advance = true;
  682. return true;
  683. }
  684. } catch (StopWalkException stop) {
  685. stopWalk();
  686. return false;
  687. }
  688. }
  689. /**
  690. * Notify iterators the walk is aborting.
  691. * <p>
  692. * Primarily to notify {@link DirCacheBuildIterator} the walk is aborting so
  693. * that it can copy any remaining entries.
  694. *
  695. * @throws IOException
  696. * if traversal of remaining entries throws an exception during
  697. * object access. This should never occur as remaining trees
  698. * should already be in memory, however the methods used to
  699. * finish traversal are declared to throw IOException.
  700. */
  701. void stopWalk() throws IOException {
  702. for (AbstractTreeIterator t : trees) {
  703. t.stopWalk();
  704. }
  705. }
  706. /**
  707. * Obtain the tree iterator for the current entry.
  708. * <p>
  709. * Entering into (or exiting out of) a subtree causes the current tree
  710. * iterator instance to be changed for the nth tree. This allows the tree
  711. * iterators to manage only one list of items, with the diving handled by
  712. * recursive trees.
  713. *
  714. * @param <T>
  715. * type of the tree iterator expected by the caller.
  716. * @param nth
  717. * tree to obtain the current iterator of.
  718. * @param clazz
  719. * type of the tree iterator expected by the caller.
  720. * @return r the current iterator of the requested type; null if the tree
  721. * has no entry to match the current path.
  722. */
  723. @SuppressWarnings("unchecked")
  724. public <T extends AbstractTreeIterator> T getTree(final int nth,
  725. final Class<T> clazz) {
  726. final AbstractTreeIterator t = trees[nth];
  727. return t.matches == currentHead ? (T) t : null;
  728. }
  729. /**
  730. * Obtain the raw {@link FileMode} bits for the current entry.
  731. * <p>
  732. * Every added tree supplies mode bits, even if the tree does not contain
  733. * the current entry. In the latter case {@link FileMode#MISSING}'s mode
  734. * bits (0) are returned.
  735. *
  736. * @param nth
  737. * tree to obtain the mode bits from.
  738. * @return mode bits for the current entry of the nth tree.
  739. * @see FileMode#fromBits(int)
  740. */
  741. public int getRawMode(final int nth) {
  742. final AbstractTreeIterator t = trees[nth];
  743. return t.matches == currentHead ? t.mode : 0;
  744. }
  745. /**
  746. * Obtain the {@link FileMode} for the current entry.
  747. * <p>
  748. * Every added tree supplies a mode, even if the tree does not contain the
  749. * current entry. In the latter case {@link FileMode#MISSING} is returned.
  750. *
  751. * @param nth
  752. * tree to obtain the mode from.
  753. * @return mode for the current entry of the nth tree.
  754. */
  755. public FileMode getFileMode(final int nth) {
  756. return FileMode.fromBits(getRawMode(nth));
  757. }
  758. /**
  759. * Obtain the {@link FileMode} for the current entry on the currentHead tree
  760. *
  761. * @return mode for the current entry of the currentHead tree.
  762. * @since 4.3
  763. */
  764. public FileMode getFileMode() {
  765. return FileMode.fromBits(currentHead.mode);
  766. }
  767. /**
  768. * Obtain the ObjectId for the current entry.
  769. * <p>
  770. * Using this method to compare ObjectId values between trees of this walker
  771. * is very inefficient. Applications should try to use
  772. * {@link #idEqual(int, int)} or {@link #getObjectId(MutableObjectId, int)}
  773. * whenever possible.
  774. * <p>
  775. * Every tree supplies an object id, even if the tree does not contain the
  776. * current entry. In the latter case {@link ObjectId#zeroId()} is returned.
  777. *
  778. * @param nth
  779. * tree to obtain the object identifier from.
  780. * @return object identifier for the current tree entry.
  781. * @see #getObjectId(MutableObjectId, int)
  782. * @see #idEqual(int, int)
  783. */
  784. public ObjectId getObjectId(final int nth) {
  785. final AbstractTreeIterator t = trees[nth];
  786. return t.matches == currentHead ? t.getEntryObjectId() : ObjectId
  787. .zeroId();
  788. }
  789. /**
  790. * Obtain the ObjectId for the current entry.
  791. * <p>
  792. * Every tree supplies an object id, even if the tree does not contain the
  793. * current entry. In the latter case {@link ObjectId#zeroId()} is supplied.
  794. * <p>
  795. * Applications should try to use {@link #idEqual(int, int)} when possible
  796. * as it avoids conversion overheads.
  797. *
  798. * @param out
  799. * buffer to copy the object id into.
  800. * @param nth
  801. * tree to obtain the object identifier from.
  802. * @see #idEqual(int, int)
  803. */
  804. public void getObjectId(final MutableObjectId out, final int nth) {
  805. final AbstractTreeIterator t = trees[nth];
  806. if (t.matches == currentHead)
  807. t.getEntryObjectId(out);
  808. else
  809. out.clear();
  810. }
  811. /**
  812. * Compare two tree's current ObjectId values for equality.
  813. *
  814. * @param nthA
  815. * first tree to compare the object id from.
  816. * @param nthB
  817. * second tree to compare the object id from.
  818. * @return result of
  819. * <code>getObjectId(nthA).equals(getObjectId(nthB))</code>.
  820. * @see #getObjectId(int)
  821. */
  822. public boolean idEqual(final int nthA, final int nthB) {
  823. final AbstractTreeIterator ch = currentHead;
  824. final AbstractTreeIterator a = trees[nthA];
  825. final AbstractTreeIterator b = trees[nthB];
  826. if (a.matches != ch && b.matches != ch) {
  827. // If neither tree matches the current path node then neither
  828. // tree has this entry. In such case the ObjectId is zero(),
  829. // and zero() is always equal to zero().
  830. //
  831. return true;
  832. }
  833. if (!a.hasId() || !b.hasId())
  834. return false;
  835. if (a.matches == ch && b.matches == ch)
  836. return a.idEqual(b);
  837. return false;
  838. }
  839. /**
  840. * Get the current entry's name within its parent tree.
  841. * <p>
  842. * This method is not very efficient and is primarily meant for debugging
  843. * and final output generation. Applications should try to avoid calling it,
  844. * and if invoked do so only once per interesting entry, where the name is
  845. * absolutely required for correct function.
  846. *
  847. * @return name of the current entry within the parent tree (or directory).
  848. * The name never includes a '/'.
  849. */
  850. public String getNameString() {
  851. final AbstractTreeIterator t = currentHead;
  852. final int off = t.pathOffset;
  853. final int end = t.pathLen;
  854. return RawParseUtils.decode(Constants.CHARSET, t.path, off, end);
  855. }
  856. /**
  857. * Get the current entry's complete path.
  858. * <p>
  859. * This method is not very efficient and is primarily meant for debugging
  860. * and final output generation. Applications should try to avoid calling it,
  861. * and if invoked do so only once per interesting entry, where the name is
  862. * absolutely required for correct function.
  863. *
  864. * @return complete path of the current entry, from the root of the
  865. * repository. If the current entry is in a subtree there will be at
  866. * least one '/' in the returned string.
  867. */
  868. public String getPathString() {
  869. return pathOf(currentHead);
  870. }
  871. /**
  872. * Get the current entry's complete path as a UTF-8 byte array.
  873. *
  874. * @return complete path of the current entry, from the root of the
  875. * repository. If the current entry is in a subtree there will be at
  876. * least one '/' in the returned string.
  877. */
  878. public byte[] getRawPath() {
  879. final AbstractTreeIterator t = currentHead;
  880. final int n = t.pathLen;
  881. final byte[] r = new byte[n];
  882. System.arraycopy(t.path, 0, r, 0, n);
  883. return r;
  884. }
  885. /**
  886. * @return The path length of the current entry.
  887. */
  888. public int getPathLength() {
  889. return currentHead.pathLen;
  890. }
  891. /**
  892. * Test if the supplied path matches the current entry's path.
  893. * <p>
  894. * This method tests that the supplied path is exactly equal to the current
  895. * entry or is one of its parent directories. It is faster to use this
  896. * method then to use {@link #getPathString()} to first create a String
  897. * object, then test <code>startsWith</code> or some other type of string
  898. * match function.
  899. * <p>
  900. * If the current entry is a subtree, then all paths within the subtree
  901. * are considered to match it.
  902. *
  903. * @param p
  904. * path buffer to test. Callers should ensure the path does not
  905. * end with '/' prior to invocation.
  906. * @param pLen
  907. * number of bytes from <code>buf</code> to test.
  908. * @return &lt; 0 if p is before the current path; 0 if p matches the current
  909. * path; 1 if the current path is past p and p will never match
  910. * again on this tree walk.
  911. */
  912. public int isPathPrefix(final byte[] p, final int pLen) {
  913. final AbstractTreeIterator t = currentHead;
  914. final byte[] c = t.path;
  915. final int cLen = t.pathLen;
  916. int ci;
  917. for (ci = 0; ci < cLen && ci < pLen; ci++) {
  918. final int c_value = (c[ci] & 0xff) - (p[ci] & 0xff);
  919. if (c_value != 0)
  920. return c_value;
  921. }
  922. if (ci < cLen) {
  923. // Ran out of pattern but we still had current data.
  924. // If c[ci] == '/' then pattern matches the subtree.
  925. // Otherwise we cannot be certain so we return -1.
  926. //
  927. return c[ci] == '/' ? 0 : -1;
  928. }
  929. if (ci < pLen) {
  930. // Ran out of current, but we still have pattern data.
  931. // If p[ci] == '/' then pattern matches this subtree,
  932. // otherwise we cannot be certain so we return -1.
  933. //
  934. return p[ci] == '/' && FileMode.TREE.equals(t.mode) ? 0 : -1;
  935. }
  936. // Both strings are identical.
  937. //
  938. return 0;
  939. }
  940. /**
  941. * Test if the supplied path matches (being suffix of) the current entry's
  942. * path.
  943. * <p>
  944. * This method tests that the supplied path is exactly equal to the current
  945. * entry, or is relative to one of entry's parent directories. It is faster
  946. * to use this method then to use {@link #getPathString()} to first create
  947. * a String object, then test <code>endsWith</code> or some other type of
  948. * string match function.
  949. *
  950. * @param p
  951. * path buffer to test.
  952. * @param pLen
  953. * number of bytes from <code>buf</code> to test.
  954. * @return true if p is suffix of the current path;
  955. * false if otherwise
  956. */
  957. public boolean isPathSuffix(final byte[] p, final int pLen) {
  958. final AbstractTreeIterator t = currentHead;
  959. final byte[] c = t.path;
  960. final int cLen = t.pathLen;
  961. for (int i = 1; i <= pLen; i++) {
  962. // Pattern longer than current path
  963. if (i > cLen)
  964. return false;
  965. // Current path doesn't match pattern
  966. if (c[cLen - i] != p[pLen - i])
  967. return false;
  968. }
  969. // Whole pattern tested -> matches
  970. return true;
  971. }
  972. /**
  973. * Get the current subtree depth of this walker.
  974. *
  975. * @return the current subtree depth of this walker.
  976. */
  977. public int getDepth() {
  978. return depth;
  979. }
  980. /**
  981. * Is the current entry a subtree?
  982. * <p>
  983. * This method is faster then testing the raw mode bits of all trees to see
  984. * if any of them are a subtree. If at least one is a subtree then this
  985. * method will return true.
  986. *
  987. * @return true if {@link #enterSubtree()} will work on the current node.
  988. */
  989. public boolean isSubtree() {
  990. return FileMode.TREE.equals(currentHead.mode);
  991. }
  992. /**
  993. * Is the current entry a subtree returned after its children?
  994. *
  995. * @return true if the current node is a tree that has been returned after
  996. * its children were already processed.
  997. * @see #isPostOrderTraversal()
  998. */
  999. public boolean isPostChildren() {
  1000. return postChildren && isSubtree();
  1001. }
  1002. /**
  1003. * Enter into the current subtree.
  1004. * <p>
  1005. * If the current entry is a subtree this method arranges for its children
  1006. * to be returned before the next sibling following the subtree is returned.
  1007. *
  1008. * @throws MissingObjectException
  1009. * a subtree was found, but the subtree object does not exist in
  1010. * this repository. The repository may be missing objects.
  1011. * @throws IncorrectObjectTypeException
  1012. * a subtree was found, and the subtree id does not denote a
  1013. * tree, but instead names some other non-tree type of object.
  1014. * The repository may have data corruption.
  1015. * @throws CorruptObjectException
  1016. * the contents of a tree did not appear to be a tree. The
  1017. * repository may have data corruption.
  1018. * @throws IOException
  1019. * a loose object or pack file could not be read.
  1020. */
  1021. public void enterSubtree() throws MissingObjectException,
  1022. IncorrectObjectTypeException, CorruptObjectException, IOException {
  1023. attrs = null;
  1024. final AbstractTreeIterator ch = currentHead;
  1025. final AbstractTreeIterator[] tmp = new AbstractTreeIterator[trees.length];
  1026. for (int i = 0; i < trees.length; i++) {
  1027. final AbstractTreeIterator t = trees[i];
  1028. final AbstractTreeIterator n;
  1029. if (t.matches == ch && !t.eof() && FileMode.TREE.equals(t.mode))
  1030. n = t.createSubtreeIterator(reader, idBuffer);
  1031. else
  1032. n = t.createEmptyTreeIterator();
  1033. tmp[i] = n;
  1034. }
  1035. depth++;
  1036. advance = false;
  1037. System.arraycopy(tmp, 0, trees, 0, trees.length);
  1038. }
  1039. @SuppressWarnings("unused")
  1040. AbstractTreeIterator min() throws CorruptObjectException {
  1041. int i = 0;
  1042. AbstractTreeIterator minRef = trees[i];
  1043. while (minRef.eof() && ++i < trees.length)
  1044. minRef = trees[i];
  1045. if (minRef.eof())
  1046. return minRef;
  1047. minRef.matches = minRef;
  1048. while (++i < trees.length) {
  1049. final AbstractTreeIterator t = trees[i];
  1050. if (t.eof())
  1051. continue;
  1052. final int cmp = t.pathCompare(minRef);
  1053. if (cmp < 0) {
  1054. t.matches = t;
  1055. minRef = t;
  1056. } else if (cmp == 0) {
  1057. t.matches = minRef;
  1058. }
  1059. }
  1060. return minRef;
  1061. }
  1062. void popEntriesEqual() throws CorruptObjectException {
  1063. final AbstractTreeIterator ch = currentHead;
  1064. for (int i = 0; i < trees.length; i++) {
  1065. final AbstractTreeIterator t = trees[i];
  1066. if (t.matches == ch) {
  1067. t.next(1);
  1068. t.matches = null;
  1069. }
  1070. }
  1071. }
  1072. void skipEntriesEqual() throws CorruptObjectException {
  1073. final AbstractTreeIterator ch = currentHead;
  1074. for (int i = 0; i < trees.length; i++) {
  1075. final AbstractTreeIterator t = trees[i];
  1076. if (t.matches == ch) {
  1077. t.skip();
  1078. t.matches = null;
  1079. }
  1080. }
  1081. }
  1082. void exitSubtree() {
  1083. depth--;
  1084. for (int i = 0; i < trees.length; i++)
  1085. trees[i] = trees[i].parent;
  1086. AbstractTreeIterator minRef = null;
  1087. for (final AbstractTreeIterator t : trees) {
  1088. if (t.matches != t)
  1089. continue;
  1090. if (minRef == null || t.pathCompare(minRef) < 0)
  1091. minRef = t;
  1092. }
  1093. currentHead = minRef;
  1094. }
  1095. private CanonicalTreeParser parserFor(final AnyObjectId id)
  1096. throws IncorrectObjectTypeException, IOException {
  1097. final CanonicalTreeParser p = new CanonicalTreeParser();
  1098. p.reset(reader, id);
  1099. return p;
  1100. }
  1101. static String pathOf(final AbstractTreeIterator t) {
  1102. return RawParseUtils.decode(Constants.CHARSET, t.path, 0, t.pathLen);
  1103. }
  1104. static String pathOf(final byte[] buf, int pos, int end) {
  1105. return RawParseUtils.decode(Constants.CHARSET, buf, pos, end);
  1106. }
  1107. /**
  1108. * @param type
  1109. * of the tree to be queried
  1110. * @return the tree of that type or null if none is present
  1111. * @since 4.3
  1112. */
  1113. public <T extends AbstractTreeIterator> T getTree(
  1114. Class<T> type) {
  1115. for (int i = 0; i < trees.length; i++) {
  1116. AbstractTreeIterator tree = trees[i];
  1117. if (type.isInstance(tree)) {
  1118. return type.cast(tree);
  1119. }
  1120. }
  1121. return null;
  1122. }
  1123. /**
  1124. * Inspect config and attributes to return a filtercommand applicable for
  1125. * the current path
  1126. *
  1127. * @param filterCommandType
  1128. * which type of filterCommand should be executed. E.g. "clean",
  1129. * "smudge"
  1130. * @return a filter command
  1131. * @throws IOException
  1132. * @since 4.2
  1133. */
  1134. public String getFilterCommand(String filterCommandType)
  1135. throws IOException {
  1136. Attributes attributes = getAttributes();
  1137. Attribute f = attributes.get(Constants.ATTR_FILTER);
  1138. if (f == null) {
  1139. return null;
  1140. }
  1141. String filterValue = f.getValue();
  1142. if (filterValue == null) {
  1143. return null;
  1144. }
  1145. String filterCommand = getFilterCommandDefinition(filterValue,
  1146. filterCommandType);
  1147. if (filterCommand == null) {
  1148. return null;
  1149. }
  1150. return filterCommand.replaceAll("%f", //$NON-NLS-1$
  1151. QuotedString.BOURNE.quote((getPathString())));
  1152. }
  1153. /**
  1154. * Get the filter command how it is defined in gitconfig. The returned
  1155. * string may contain "%f" which needs to be replaced by the current path
  1156. * before executing the filter command. These filter definitions are cached
  1157. * for better performance.
  1158. *
  1159. * @param filterDriverName
  1160. * The name of the filter driver as it is referenced in the
  1161. * gitattributes file. E.g. "lfs". For each filter driver there
  1162. * may be many commands defined in the .gitconfig
  1163. * @param filterCommandType
  1164. * The type of the filter command for a specific filter driver.
  1165. * May be "clean" or "smudge".
  1166. * @return the definition of the command to be executed for this filter
  1167. * driver and filter command
  1168. */
  1169. private String getFilterCommandDefinition(String filterDriverName,
  1170. String filterCommandType) {
  1171. String key = filterDriverName + "." + filterCommandType; //$NON-NLS-1$
  1172. String filterCommand = filterCommandsByNameDotType.get(key);
  1173. if (filterCommand != null)
  1174. return filterCommand;
  1175. filterCommand = config.getString(Constants.ATTR_FILTER,
  1176. filterDriverName, filterCommandType);
  1177. if (filterCommand != null)
  1178. filterCommandsByNameDotType.put(key, filterCommand);
  1179. return filterCommand;
  1180. }
  1181. }