Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

TreeWalk.java 46KB

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