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

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