TreeWalk.java

  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. }