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

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