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 49KB

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