You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

TreeWalk.java 48KB

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