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

TreeWalk.java 48KB

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