TreeWalk.java

  1. /*
  2.  * Copyright (C) 2008, 2009 Google Inc.
  3.  * Copyright (C) 2008, 2022 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.Arrays;
  15. import java.util.HashMap;
  16. import java.util.Map;
  17. import java.util.Set;
  18. import java.util.regex.Matcher;

  19. import org.eclipse.jgit.annotations.Nullable;
  20. import org.eclipse.jgit.api.errors.JGitInternalException;
  21. import org.eclipse.jgit.attributes.Attribute;
  22. import org.eclipse.jgit.attributes.Attributes;
  23. import org.eclipse.jgit.attributes.AttributesHandler;
  24. import org.eclipse.jgit.attributes.AttributesNodeProvider;
  25. import org.eclipse.jgit.attributes.AttributesProvider;
  26. import org.eclipse.jgit.attributes.FilterCommandRegistry;
  27. import org.eclipse.jgit.dircache.DirCacheBuildIterator;
  28. import org.eclipse.jgit.dircache.DirCacheIterator;
  29. import org.eclipse.jgit.errors.CorruptObjectException;
  30. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  31. import org.eclipse.jgit.errors.MissingObjectException;
  32. import org.eclipse.jgit.errors.StopWalkException;
  33. import org.eclipse.jgit.lib.AnyObjectId;
  34. import org.eclipse.jgit.lib.Config;
  35. import org.eclipse.jgit.lib.ConfigConstants;
  36. import org.eclipse.jgit.lib.Constants;
  37. import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
  38. import org.eclipse.jgit.lib.FileMode;
  39. import org.eclipse.jgit.lib.MutableObjectId;
  40. import org.eclipse.jgit.lib.ObjectId;
  41. import org.eclipse.jgit.lib.ObjectReader;
  42. import org.eclipse.jgit.lib.Repository;
  43. import org.eclipse.jgit.revwalk.RevTree;
  44. import org.eclipse.jgit.treewalk.filter.PathFilter;
  45. import org.eclipse.jgit.treewalk.filter.TreeFilter;
  46. import org.eclipse.jgit.util.QuotedString;
  47. import org.eclipse.jgit.util.RawParseUtils;
  48. import org.eclipse.jgit.util.io.EolStreamTypeUtil;

  49. /**
  50.  * Walks one or more {@link org.eclipse.jgit.treewalk.AbstractTreeIterator}s in
  51.  * parallel.
  52.  * <p>
  53.  * This class can perform n-way differences across as many trees as necessary.
  54.  * <p>
  55.  * Each tree added must have the same root as existing trees in the walk.
  56.  * <p>
  57.  * A TreeWalk instance can only be used once to generate results. Running a
  58.  * second time requires creating a new TreeWalk instance, or invoking
  59.  * {@link #reset()} and adding new trees before starting again. Resetting an
  60.  * existing instance may be faster for some applications as some internal
  61.  * buffers may be recycled.
  62.  * <p>
  63.  * TreeWalk instances are not thread-safe. Applications must either restrict
  64.  * usage of a TreeWalk instance to a single thread, or implement their own
  65.  * synchronization at a higher level.
  66.  * <p>
  67.  * Multiple simultaneous TreeWalk instances per
  68.  * {@link org.eclipse.jgit.lib.Repository} are permitted, even from concurrent
  69.  * threads.
  70.  */
  71. public class TreeWalk implements AutoCloseable, AttributesProvider {

  72.     private static final AbstractTreeIterator[] NO_TREES = {};

  73.     /**
  74.      * @since 4.2
  75.      */
  76.     public enum OperationType {
  77.         /**
  78.          * Represents a checkout operation (for example a checkout or reset
  79.          * operation).
  80.          */
  81.         CHECKOUT_OP,

  82.         /**
  83.          * Represents a checkin operation (for example an add operation)
  84.          */
  85.         CHECKIN_OP
  86.     }

  87.     /**
  88.      * Type of operation you want to retrieve the git attributes for.
  89.      */
  90.     private OperationType operationType = OperationType.CHECKOUT_OP;

  91.     /**
  92.      * The filter command as defined in gitattributes. The keys are
  93.      * filterName+"."+filterCommandType. E.g. "lfs.clean"
  94.      */
  95.     private Map<String, String> filterCommandsByNameDotType = new HashMap<>();

  96.     /**
  97.      * Set the operation type of this walk
  98.      *
  99.      * @param operationType
  100.      *            a {@link org.eclipse.jgit.treewalk.TreeWalk.OperationType}
  101.      *            object.
  102.      * @since 4.2
  103.      */
  104.     public void setOperationType(OperationType operationType) {
  105.         this.operationType = operationType;
  106.     }

  107.     /**
  108.      * Open a tree walk and filter to exactly one path.
  109.      * <p>
  110.      * The returned tree walk is already positioned on the requested path, so
  111.      * the caller should not need to invoke {@link #next()} unless they are
  112.      * looking for a possible directory/file name conflict.
  113.      *
  114.      * @param reader
  115.      *            the reader the walker will obtain tree data from.
  116.      * @param path
  117.      *            single path to advance the tree walk instance into.
  118.      * @param trees
  119.      *            one or more trees to walk through, all with the same root.
  120.      * @return a new tree walk configured for exactly this one path; null if no
  121.      *         path was found in any of the trees.
  122.      * @throws java.io.IOException
  123.      *             reading a pack file or loose object failed.
  124.      * @throws org.eclipse.jgit.errors.CorruptObjectException
  125.      *             an tree object could not be read as its data stream did not
  126.      *             appear to be a tree, or could not be inflated.
  127.      * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
  128.      *             an object we expected to be a tree was not a tree.
  129.      * @throws org.eclipse.jgit.errors.MissingObjectException
  130.      *             a tree object was not found.
  131.      */
  132.     public static TreeWalk forPath(final ObjectReader reader, final String path,
  133.             final AnyObjectId... trees) throws MissingObjectException,
  134.             IncorrectObjectTypeException, CorruptObjectException, IOException {
  135.         return forPath(null, reader, path, trees);
  136.     }

  137.     /**
  138.      * Open a tree walk and filter to exactly one path.
  139.      * <p>
  140.      * The returned tree walk is already positioned on the requested path, so
  141.      * the caller should not need to invoke {@link #next()} unless they are
  142.      * looking for a possible directory/file name conflict.
  143.      *
  144.      * @param repo
  145.      *            repository to read config data and
  146.      *            {@link org.eclipse.jgit.attributes.AttributesNodeProvider}
  147.      *            from.
  148.      * @param reader
  149.      *            the reader the walker will obtain tree data from.
  150.      * @param path
  151.      *            single path to advance the tree walk instance into.
  152.      * @param trees
  153.      *            one or more trees to walk through, all with the same root.
  154.      * @return a new tree walk configured for exactly this one path; null if no
  155.      *         path was found in any of the trees.
  156.      * @throws java.io.IOException
  157.      *             reading a pack file or loose object failed.
  158.      * @throws org.eclipse.jgit.errors.CorruptObjectException
  159.      *             an tree object could not be read as its data stream did not
  160.      *             appear to be a tree, or could not be inflated.
  161.      * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
  162.      *             an object we expected to be a tree was not a tree.
  163.      * @throws org.eclipse.jgit.errors.MissingObjectException
  164.      *             a tree object was not found.
  165.      * @since 4.3
  166.      */
  167.     public static TreeWalk forPath(final @Nullable Repository repo,
  168.             final ObjectReader reader, final String path,
  169.             final AnyObjectId... trees)
  170.                     throws MissingObjectException, IncorrectObjectTypeException,
  171.                     CorruptObjectException, IOException {
  172.         TreeWalk tw = new TreeWalk(repo, reader);
  173.         PathFilter f = PathFilter.create(path);
  174.         tw.setFilter(f);
  175.         tw.reset(trees);
  176.         tw.setRecursive(false);

  177.         while (tw.next()) {
  178.             if (f.isDone(tw)) {
  179.                 return tw;
  180.             } else if (tw.isSubtree()) {
  181.                 tw.enterSubtree();
  182.             }
  183.         }
  184.         return null;
  185.     }

  186.     /**
  187.      * Open a tree walk and filter to exactly one path.
  188.      * <p>
  189.      * The returned tree walk is already positioned on the requested path, so
  190.      * the caller should not need to invoke {@link #next()} unless they are
  191.      * looking for a possible directory/file name conflict.
  192.      *
  193.      * @param db
  194.      *            repository to read tree object data from.
  195.      * @param path
  196.      *            single path to advance the tree walk instance into.
  197.      * @param trees
  198.      *            one or more trees to walk through, all with the same root.
  199.      * @return a new tree walk configured for exactly this one path; null if no
  200.      *         path was found in any of the trees.
  201.      * @throws java.io.IOException
  202.      *             reading a pack file or loose object failed.
  203.      * @throws org.eclipse.jgit.errors.CorruptObjectException
  204.      *             an tree object could not be read as its data stream did not
  205.      *             appear to be a tree, or could not be inflated.
  206.      * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
  207.      *             an object we expected to be a tree was not a tree.
  208.      * @throws org.eclipse.jgit.errors.MissingObjectException
  209.      *             a tree object was not found.
  210.      */
  211.     public static TreeWalk forPath(final Repository db, final String path,
  212.             final AnyObjectId... trees) throws MissingObjectException,
  213.             IncorrectObjectTypeException, CorruptObjectException, IOException {
  214.         try (ObjectReader reader = db.newObjectReader()) {
  215.             return forPath(db, reader, path, trees);
  216.         }
  217.     }

  218.     /**
  219.      * Open a tree walk and filter to exactly one path.
  220.      * <p>
  221.      * The returned tree walk is already positioned on the requested path, so
  222.      * the caller should not need to invoke {@link #next()} unless they are
  223.      * looking for a possible directory/file name conflict.
  224.      *
  225.      * @param db
  226.      *            repository to read tree object data from.
  227.      * @param path
  228.      *            single path to advance the tree walk instance into.
  229.      * @param tree
  230.      *            the single tree to walk through.
  231.      * @return a new tree walk configured for exactly this one path; null if no
  232.      *         path was found in any of the trees.
  233.      * @throws java.io.IOException
  234.      *             reading a pack file or loose object failed.
  235.      * @throws org.eclipse.jgit.errors.CorruptObjectException
  236.      *             an tree object could not be read as its data stream did not
  237.      *             appear to be a tree, or could not be inflated.
  238.      * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
  239.      *             an object we expected to be a tree was not a tree.
  240.      * @throws org.eclipse.jgit.errors.MissingObjectException
  241.      *             a tree object was not found.
  242.      */
  243.     public static TreeWalk forPath(final Repository db, final String path,
  244.             final RevTree tree) throws MissingObjectException,
  245.             IncorrectObjectTypeException, CorruptObjectException, IOException {
  246.         return forPath(db, path, new ObjectId[] { tree });
  247.     }

  248.     private final ObjectReader reader;

  249.     private final boolean closeReader;

  250.     private final MutableObjectId idBuffer = new MutableObjectId();

  251.     private TreeFilter filter;

  252.     AbstractTreeIterator[] trees;

  253.     private boolean recursive;

  254.     private boolean postOrderTraversal;

  255.     int depth;

  256.     private boolean advance;

  257.     private boolean postChildren;

  258.     private AttributesNodeProvider attributesNodeProvider;

  259.     AbstractTreeIterator currentHead;

  260.     /**
  261.      * Cached attributes for the current entry; per tree. Index i+1 is for tree
  262.      * i; index 0 is for the deprecated legacy behavior.
  263.      */
  264.     private Attributes[] attrs;

  265.     /**
  266.      * Cached attributes handler; per tree. Index i+1 is for tree i; index 0 is
  267.      * for the deprecated legacy behavior.
  268.      */
  269.     private AttributesHandler[] attributesHandlers;

  270.     /** Can be set to identify the tree to use for {@link #getAttributes()}. */
  271.     private int headIndex = -1;

  272.     private Config config;

  273.     private Set<String> filterCommands;

  274.     /**
  275.      * Create a new tree walker for a given repository.
  276.      *
  277.      * @param repo
  278.      *            the repository the walker will obtain data from. An
  279.      *            ObjectReader will be created by the walker, and will be closed
  280.      *            when the walker is closed.
  281.      */
  282.     public TreeWalk(Repository repo) {
  283.         this(repo, repo.newObjectReader(), true);
  284.     }

  285.     /**
  286.      * Create a new tree walker for a given repository.
  287.      *
  288.      * @param repo
  289.      *            the repository the walker will obtain data from. An
  290.      *            ObjectReader will be created by the walker, and will be closed
  291.      *            when the walker is closed.
  292.      * @param or
  293.      *            the reader the walker will obtain tree data from. The reader
  294.      *            is not closed when the walker is closed.
  295.      * @since 4.3
  296.      */
  297.     public TreeWalk(@Nullable Repository repo, ObjectReader or) {
  298.         this(repo, or, false);
  299.     }

  300.     /**
  301.      * Create a new tree walker for a given repository.
  302.      *
  303.      * @param or
  304.      *            the reader the walker will obtain tree data from. The reader
  305.      *            is not closed when the walker is closed.
  306.      */
  307.     public TreeWalk(ObjectReader or) {
  308.         this(null, or, false);
  309.     }

  310.     private TreeWalk(final @Nullable Repository repo, final ObjectReader or,
  311.             final boolean closeReader) {
  312.         if (repo != null) {
  313.             config = repo.getConfig();
  314.             attributesNodeProvider = repo.createAttributesNodeProvider();
  315.             filterCommands = FilterCommandRegistry
  316.                     .getRegisteredFilterCommands();
  317.         } else {
  318.             config = null;
  319.             attributesNodeProvider = null;
  320.         }
  321.         reader = or;
  322.         filter = TreeFilter.ALL;
  323.         trees = NO_TREES;
  324.         this.closeReader = closeReader;
  325.     }

  326.     /**
  327.      * Get the reader this walker is using to load objects.
  328.      *
  329.      * @return the reader this walker is using to load objects.
  330.      */
  331.     public ObjectReader getObjectReader() {
  332.         return reader;
  333.     }

  334.     /**
  335.      * Get the operation type
  336.      *
  337.      * @return the {@link org.eclipse.jgit.treewalk.TreeWalk.OperationType}
  338.      * @since 4.3
  339.      */
  340.     public OperationType getOperationType() {
  341.         return operationType;
  342.     }

  343.     /**
  344.      * {@inheritDoc}
  345.      * <p>
  346.      * Release any resources used by this walker's reader.
  347.      * <p>
  348.      * A walker that has been released can be used again, but may need to be
  349.      * released after the subsequent usage.
  350.      *
  351.      * @since 4.0
  352.      */
  353.     @Override
  354.     public void close() {
  355.         if (closeReader) {
  356.             reader.close();
  357.         }
  358.     }

  359.     /**
  360.      * Get the currently configured filter.
  361.      *
  362.      * @return the current filter. Never null as a filter is always needed.
  363.      */
  364.     public TreeFilter getFilter() {
  365.         return filter;
  366.     }

  367.     /**
  368.      * Set the tree entry filter for this walker.
  369.      * <p>
  370.      * Multiple filters may be combined by constructing an arbitrary tree of
  371.      * <code>AndTreeFilter</code> or <code>OrTreeFilter</code> instances to
  372.      * describe the boolean expression required by the application. Custom
  373.      * filter implementations may also be constructed by applications.
  374.      * <p>
  375.      * Note that filters are not thread-safe and may not be shared by concurrent
  376.      * TreeWalk instances. Every TreeWalk must be supplied its own unique
  377.      * filter, unless the filter implementation specifically states it is (and
  378.      * always will be) thread-safe. Callers may use
  379.      * {@link org.eclipse.jgit.treewalk.filter.TreeFilter#clone()} to create a
  380.      * unique filter tree for this TreeWalk instance.
  381.      *
  382.      * @param newFilter
  383.      *            the new filter. If null the special
  384.      *            {@link org.eclipse.jgit.treewalk.filter.TreeFilter#ALL} filter
  385.      *            will be used instead, as it matches every entry.
  386.      * @see org.eclipse.jgit.treewalk.filter.AndTreeFilter
  387.      * @see org.eclipse.jgit.treewalk.filter.OrTreeFilter
  388.      */
  389.     public void setFilter(TreeFilter newFilter) {
  390.         filter = newFilter != null ? newFilter : TreeFilter.ALL;
  391.     }

  392.     /**
  393.      * Is this walker automatically entering into subtrees?
  394.      * <p>
  395.      * If the walker is recursive then the caller will not see a subtree node
  396.      * and instead will only receive file nodes in all relevant subtrees.
  397.      *
  398.      * @return true if automatically entering subtrees is enabled.
  399.      */
  400.     public boolean isRecursive() {
  401.         return recursive;
  402.     }

  403.     /**
  404.      * Set the walker to enter (or not enter) subtrees automatically.
  405.      * <p>
  406.      * If recursive mode is enabled the walker will hide subtree nodes from the
  407.      * calling application and will produce only file level nodes. If a tree
  408.      * (directory) is deleted then all of the file level nodes will appear to be
  409.      * deleted, recursively, through as many levels as necessary to account for
  410.      * all entries.
  411.      *
  412.      * @param b
  413.      *            true to skip subtree nodes and only obtain files nodes.
  414.      */
  415.     public void setRecursive(boolean b) {
  416.         recursive = b;
  417.     }

  418.     /**
  419.      * Does this walker return a tree entry after it exits the subtree?
  420.      * <p>
  421.      * If post order traversal is enabled then the walker will return a subtree
  422.      * after it has returned the last entry within that subtree. This may cause
  423.      * a subtree to be seen by the application twice if {@link #isRecursive()}
  424.      * is false, as the application will see it once, call
  425.      * {@link #enterSubtree()}, and then see it again as it leaves the subtree.
  426.      * <p>
  427.      * If an application does not enable {@link #isRecursive()} and it does not
  428.      * call {@link #enterSubtree()} then the tree is returned only once as none
  429.      * of the children were processed.
  430.      *
  431.      * @return true if subtrees are returned after entries within the subtree.
  432.      */
  433.     public boolean isPostOrderTraversal() {
  434.         return postOrderTraversal;
  435.     }

  436.     /**
  437.      * Set the walker to return trees after their children.
  438.      *
  439.      * @param b
  440.      *            true to get trees after their children.
  441.      * @see #isPostOrderTraversal()
  442.      */
  443.     public void setPostOrderTraversal(boolean b) {
  444.         postOrderTraversal = b;
  445.     }

  446.     /**
  447.      * Sets the {@link org.eclipse.jgit.attributes.AttributesNodeProvider} for
  448.      * this {@link org.eclipse.jgit.treewalk.TreeWalk}.
  449.      * <p>
  450.      * This is a requirement for a correct computation of the git attributes. If
  451.      * this {@link org.eclipse.jgit.treewalk.TreeWalk} has been built using
  452.      * {@link #TreeWalk(Repository)} constructor, the
  453.      * {@link org.eclipse.jgit.attributes.AttributesNodeProvider} has already
  454.      * been set. Indeed,the {@link org.eclipse.jgit.lib.Repository} can provide
  455.      * an {@link org.eclipse.jgit.attributes.AttributesNodeProvider} using
  456.      * {@link org.eclipse.jgit.lib.Repository#createAttributesNodeProvider()}
  457.      * method. Otherwise you should provide one.
  458.      * </p>
  459.      *
  460.      * @see Repository#createAttributesNodeProvider()
  461.      * @param provider
  462.      *            a {@link org.eclipse.jgit.attributes.AttributesNodeProvider}
  463.      *            object.
  464.      * @since 4.2
  465.      */
  466.     public void setAttributesNodeProvider(AttributesNodeProvider provider) {
  467.         attributesNodeProvider = provider;
  468.     }

  469.     /**
  470.      * Get the attributes node provider
  471.      *
  472.      * @return the {@link org.eclipse.jgit.attributes.AttributesNodeProvider}
  473.      *         for this {@link org.eclipse.jgit.treewalk.TreeWalk}.
  474.      * @since 4.3
  475.      */
  476.     public AttributesNodeProvider getAttributesNodeProvider() {
  477.         return attributesNodeProvider;
  478.     }

  479.     /**
  480.      * Identifies the tree at the given index as the head tree. This is the tree
  481.      * use by default to determine attributes and EOL modes.
  482.      *
  483.      * @param index
  484.      *            of the tree to use as head
  485.      * @throws IllegalArgumentException
  486.      *             if the index is out of range
  487.      * @since 6.1
  488.      */
  489.     public void setHead(int index) {
  490.         if (index < 0 || index >= trees.length) {
  491.             throw new IllegalArgumentException("Head index " + index //$NON-NLS-1$
  492.                     + " out of range [0," + trees.length + ')'); //$NON-NLS-1$
  493.         }
  494.         headIndex = index;
  495.     }

  496.     /**
  497.      * {@inheritDoc}
  498.      * <p>
  499.      * Retrieve the git attributes for the current entry.
  500.      *
  501.      * <h3>Git attribute computation</h3>
  502.      *
  503.      * <ul>
  504.      * <li>Get the attributes matching the current path entry from the info file
  505.      * (see {@link AttributesNodeProvider#getInfoAttributesNode()}).</li>
  506.      * <li>Completes the list of attributes using the .gitattributes files
  507.      * located on the current path (the further the directory that contains
  508.      * .gitattributes is from the path in question, the lower its precedence).
  509.      * For a checkin operation, it will look first on the working tree (if any).
  510.      * If there is no attributes file, it will fallback on the index. For a
  511.      * checkout operation, it will first use the index entry and then fallback
  512.      * on the working tree if none.</li>
  513.      * <li>In the end, completes the list of matching attributes using the
  514.      * global attribute file define in the configuration (see
  515.      * {@link AttributesNodeProvider#getGlobalAttributesNode()})</li>
  516.      *
  517.      * </ul>
  518.      *
  519.      *
  520.      * <h3>Iterator constraints</h3>
  521.      *
  522.      * <p>
  523.      * In order to have a correct list of attributes for the current entry, this
  524.      * {@link TreeWalk} requires to have at least one
  525.      * {@link AttributesNodeProvider} and a {@link DirCacheIterator} set up. An
  526.      * {@link AttributesNodeProvider} is used to retrieve the attributes from
  527.      * the info attributes file and the global attributes file. The
  528.      * {@link DirCacheIterator} is used to retrieve the .gitattributes files
  529.      * stored in the index. A {@link WorkingTreeIterator} can also be provided
  530.      * to access the local version of the .gitattributes files. If none is
  531.      * provided it will fallback on the {@link DirCacheIterator}.
  532.      * </p>
  533.      *
  534.      * @since 4.2
  535.      */
  536.     @Override
  537.     public Attributes getAttributes() {
  538.         return getAttributes(headIndex);
  539.     }

  540.     /**
  541.      * Retrieves the git attributes based on the given tree.
  542.      *
  543.      * @param index
  544.      *            of the tree to use as base for the attributes
  545.      * @return the attributes
  546.      * @since 6.1
  547.      */
  548.     public Attributes getAttributes(int index) {
  549.         int attrIndex = index + 1;
  550.         Attributes result = attrs[attrIndex];
  551.         if (result != null) {
  552.             return result;
  553.         }
  554.         if (attributesNodeProvider == null) {
  555.             throw new IllegalStateException(
  556.                     "The tree walk should have one AttributesNodeProvider set in order to compute the git attributes."); //$NON-NLS-1$
  557.         }

  558.         try {
  559.             AttributesHandler handler = attributesHandlers[attrIndex];
  560.             if (handler == null) {
  561.                 if (index < 0) {
  562.                     // Legacy behavior (headIndex not set, getAttributes() above
  563.                     // called)
  564.                     handler = new AttributesHandler(this, () -> {
  565.                         return getTree(CanonicalTreeParser.class);
  566.                     });
  567.                 } else {
  568.                     handler = new AttributesHandler(this, () -> {
  569.                         AbstractTreeIterator tree = trees[index];
  570.                         if (tree instanceof CanonicalTreeParser) {
  571.                             return (CanonicalTreeParser) tree;
  572.                         }
  573.                         return null;
  574.                     });
  575.                 }
  576.                 attributesHandlers[attrIndex] = handler;
  577.             }
  578.             result = handler.getAttributes();
  579.             attrs[attrIndex] = result;
  580.             return result;
  581.         } catch (IOException e) {
  582.             throw new JGitInternalException("Error while parsing attributes", //$NON-NLS-1$
  583.                     e);
  584.         }
  585.     }

  586.     /**
  587.      * Get the EOL stream type of the current entry using the config and
  588.      * {@link #getAttributes()}.
  589.      *
  590.      * @param opType
  591.      *            the operationtype (checkin/checkout) which should be used
  592.      * @return the EOL stream type of the current entry using the config and
  593.      *         {@link #getAttributes()}. Note that this method may return null
  594.      *         if the {@link org.eclipse.jgit.treewalk.TreeWalk} is not based on
  595.      *         a working tree
  596.      * @since 4.10
  597.      */
  598.     @Nullable
  599.     public EolStreamType getEolStreamType(OperationType opType) {
  600.         if (attributesNodeProvider == null || config == null) {
  601.             return null;
  602.         }
  603.         OperationType op = opType != null ? opType : operationType;
  604.         return EolStreamTypeUtil.detectStreamType(op,
  605.                 config.get(WorkingTreeOptions.KEY), getAttributes());
  606.     }

  607.     /**
  608.      * Get the EOL stream type of the current entry for checking out using the
  609.      * config and {@link #getAttributes()}.
  610.      *
  611.      * @param tree
  612.      *            index of the tree the check-out is to be from
  613.      * @return the EOL stream type of the current entry using the config and
  614.      *         {@link #getAttributes()}. Note that this method may return null
  615.      *         if the {@link org.eclipse.jgit.treewalk.TreeWalk} is not based on
  616.      *         a working tree
  617.      * @since 6.1
  618.      */
  619.     @Nullable
  620.     public EolStreamType getCheckoutEolStreamType(int tree) {
  621.         if (attributesNodeProvider == null || config == null) {
  622.             return null;
  623.         }
  624.         Attributes attr = getAttributes(tree);
  625.         return EolStreamTypeUtil.detectStreamType(OperationType.CHECKOUT_OP,
  626.                 config.get(WorkingTreeOptions.KEY), attr);
  627.     }

  628.     /**
  629.      * Reset this walker so new tree iterators can be added to it.
  630.      */
  631.     public void reset() {
  632.         attrs = null;
  633.         attributesHandlers = null;
  634.         headIndex = -1;
  635.         trees = NO_TREES;
  636.         advance = false;
  637.         depth = 0;
  638.     }

  639.     /**
  640.      * Reset this walker to run over a single existing tree.
  641.      *
  642.      * @param id
  643.      *            the tree we need to parse. The walker will execute over this
  644.      *            single tree if the reset is successful.
  645.      * @throws org.eclipse.jgit.errors.MissingObjectException
  646.      *             the given tree object does not exist in this repository.
  647.      * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
  648.      *             the given object id does not denote a tree, but instead names
  649.      *             some other non-tree type of object. Note that commits are not
  650.      *             trees, even if they are sometimes called a "tree-ish".
  651.      * @throws org.eclipse.jgit.errors.CorruptObjectException
  652.      *             the object claimed to be a tree, but its contents did not
  653.      *             appear to be a tree. The repository may have data corruption.
  654.      * @throws java.io.IOException
  655.      *             a loose object or pack file could not be read.
  656.      */
  657.     public void reset(AnyObjectId id) throws MissingObjectException,
  658.             IncorrectObjectTypeException, CorruptObjectException, IOException {
  659.         if (trees.length == 1) {
  660.             AbstractTreeIterator o = trees[0];
  661.             while (o.parent != null)
  662.                 o = o.parent;
  663.             if (o instanceof CanonicalTreeParser) {
  664.                 o.matches = null;
  665.                 o.matchShift = 0;
  666.                 ((CanonicalTreeParser) o).reset(reader, id);
  667.                 trees[0] = o;
  668.             } else {
  669.                 trees[0] = parserFor(id);
  670.             }
  671.         } else {
  672.             trees = new AbstractTreeIterator[] { parserFor(id) };
  673.         }

  674.         advance = false;
  675.         depth = 0;
  676.         attrs = new Attributes[2];
  677.         attributesHandlers = new AttributesHandler[2];
  678.         headIndex = -1;
  679.     }

  680.     /**
  681.      * Reset this walker to run over a set of existing trees.
  682.      *
  683.      * @param ids
  684.      *            the trees we need to parse. The walker will execute over this
  685.      *            many parallel trees if the reset is successful.
  686.      * @throws org.eclipse.jgit.errors.MissingObjectException
  687.      *             the given tree object does not exist in this repository.
  688.      * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
  689.      *             the given object id does not denote a tree, but instead names
  690.      *             some other non-tree type of object. Note that commits are not
  691.      *             trees, even if they are sometimes called a "tree-ish".
  692.      * @throws org.eclipse.jgit.errors.CorruptObjectException
  693.      *             the object claimed to be a tree, but its contents did not
  694.      *             appear to be a tree. The repository may have data corruption.
  695.      * @throws java.io.IOException
  696.      *             a loose object or pack file could not be read.
  697.      */
  698.     public void reset(AnyObjectId... ids) throws MissingObjectException,
  699.             IncorrectObjectTypeException, CorruptObjectException, IOException {
  700.         final int oldLen = trees.length;
  701.         final int newLen = ids.length;
  702.         final AbstractTreeIterator[] r = newLen == oldLen ? trees
  703.                 : new AbstractTreeIterator[newLen];
  704.         for (int i = 0; i < newLen; i++) {
  705.             AbstractTreeIterator o;

  706.             if (i < oldLen) {
  707.                 o = trees[i];
  708.                 while (o.parent != null)
  709.                     o = o.parent;
  710.                 if (o instanceof CanonicalTreeParser && o.pathOffset == 0) {
  711.                     o.matches = null;
  712.                     o.matchShift = 0;
  713.                     ((CanonicalTreeParser) o).reset(reader, ids[i]);
  714.                     r[i] = o;
  715.                     continue;
  716.                 }
  717.             }

  718.             o = parserFor(ids[i]);
  719.             r[i] = o;
  720.         }

  721.         trees = r;
  722.         advance = false;
  723.         depth = 0;
  724.         if (oldLen == newLen) {
  725.             Arrays.fill(attrs, null);
  726.             Arrays.fill(attributesHandlers, null);
  727.         } else {
  728.             attrs = new Attributes[newLen + 1];
  729.             attributesHandlers = new AttributesHandler[newLen + 1];
  730.         }
  731.         headIndex = -1;
  732.     }

  733.     /**
  734.      * Add an already existing tree object for walking.
  735.      * <p>
  736.      * The position of this tree is returned to the caller, in case the caller
  737.      * has lost track of the order they added the trees into the walker.
  738.      * <p>
  739.      * The tree must have the same root as existing trees in the walk.
  740.      *
  741.      * @param id
  742.      *            identity of the tree object the caller wants walked.
  743.      * @return position of this tree within the walker.
  744.      * @throws org.eclipse.jgit.errors.MissingObjectException
  745.      *             the given tree object does not exist in this repository.
  746.      * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
  747.      *             the given object id does not denote a tree, but instead names
  748.      *             some other non-tree type of object. Note that commits are not
  749.      *             trees, even if they are sometimes called a "tree-ish".
  750.      * @throws org.eclipse.jgit.errors.CorruptObjectException
  751.      *             the object claimed to be a tree, but its contents did not
  752.      *             appear to be a tree. The repository may have data corruption.
  753.      * @throws java.io.IOException
  754.      *             a loose object or pack file could not be read.
  755.      */
  756.     public int addTree(AnyObjectId id) throws MissingObjectException,
  757.             IncorrectObjectTypeException, CorruptObjectException, IOException {
  758.         return addTree(parserFor(id));
  759.     }

  760.     /**
  761.      * Add an already created tree iterator for walking.
  762.      * <p>
  763.      * The position of this tree is returned to the caller, in case the caller
  764.      * has lost track of the order they added the trees into the walker.
  765.      * <p>
  766.      * The tree which the iterator operates on must have the same root as
  767.      * existing trees in the walk.
  768.      *
  769.      * @param p
  770.      *            an iterator to walk over. The iterator should be new, with no
  771.      *            parent, and should still be positioned before the first entry.
  772.      *            The tree which the iterator operates on must have the same
  773.      *            root as other trees in the walk.
  774.      * @return position of this tree within the walker.
  775.      */
  776.     public int addTree(AbstractTreeIterator p) {
  777.         int n = trees.length;
  778.         AbstractTreeIterator[] newTrees = new AbstractTreeIterator[n + 1];

  779.         System.arraycopy(trees, 0, newTrees, 0, n);
  780.         newTrees[n] = p;
  781.         p.matches = null;
  782.         p.matchShift = 0;

  783.         trees = newTrees;
  784.         if (attrs == null) {
  785.             attrs = new Attributes[n + 2];
  786.         } else {
  787.             attrs = Arrays.copyOf(attrs, n + 2);
  788.         }
  789.         if (attributesHandlers == null) {
  790.             attributesHandlers = new AttributesHandler[n + 2];
  791.         } else {
  792.             attributesHandlers = Arrays.copyOf(attributesHandlers, n + 2);
  793.         }
  794.         return n;
  795.     }

  796.     /**
  797.      * Get the number of trees known to this walker.
  798.      *
  799.      * @return the total number of trees this walker is iterating over.
  800.      */
  801.     public int getTreeCount() {
  802.         return trees.length;
  803.     }

  804.     /**
  805.      * Advance this walker to the next relevant entry.
  806.      *
  807.      * @return true if there is an entry available; false if all entries have
  808.      *         been walked and the walk of this set of tree iterators is over.
  809.      * @throws org.eclipse.jgit.errors.MissingObjectException
  810.      *             {@link #isRecursive()} was enabled, a subtree was found, but
  811.      *             the subtree object does not exist in this repository. The
  812.      *             repository may be missing objects.
  813.      * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
  814.      *             {@link #isRecursive()} was enabled, a subtree was found, and
  815.      *             the subtree id does not denote a tree, but instead names some
  816.      *             other non-tree type of object. The repository may have data
  817.      *             corruption.
  818.      * @throws org.eclipse.jgit.errors.CorruptObjectException
  819.      *             the contents of a tree did not appear to be a tree. The
  820.      *             repository may have data corruption.
  821.      * @throws java.io.IOException
  822.      *             a loose object or pack file could not be read.
  823.      */
  824.     public boolean next() throws MissingObjectException,
  825.             IncorrectObjectTypeException, CorruptObjectException, IOException {
  826.         try {
  827.             if (advance) {
  828.                 advance = false;
  829.                 postChildren = false;
  830.                 popEntriesEqual();
  831.             }

  832.             for (;;) {
  833.                 Arrays.fill(attrs, null);
  834.                 final AbstractTreeIterator t = min();
  835.                 if (t.eof()) {
  836.                     if (depth > 0) {
  837.                         exitSubtree();
  838.                         if (postOrderTraversal) {
  839.                             advance = true;
  840.                             postChildren = true;
  841.                             return true;
  842.                         }
  843.                         popEntriesEqual();
  844.                         continue;
  845.                     }
  846.                     return false;
  847.                 }

  848.                 currentHead = t;
  849.                 if (filter.matchFilter(this) == 1) {
  850.                     skipEntriesEqual();
  851.                     continue;
  852.                 }

  853.                 if (recursive && FileMode.TREE.equals(t.mode)) {
  854.                     enterSubtree();
  855.                     continue;
  856.                 }

  857.                 advance = true;
  858.                 return true;
  859.             }
  860.         } catch (StopWalkException stop) {
  861.             stopWalk();
  862.             return false;
  863.         }
  864.     }

  865.     /**
  866.      * Notify iterators the walk is aborting.
  867.      * <p>
  868.      * Primarily to notify {@link DirCacheBuildIterator} the walk is aborting so
  869.      * that it can copy any remaining entries.
  870.      *
  871.      * @throws IOException
  872.      *             if traversal of remaining entries throws an exception during
  873.      *             object access. This should never occur as remaining trees
  874.      *             should already be in memory, however the methods used to
  875.      *             finish traversal are declared to throw IOException.
  876.      */
  877.     void stopWalk() throws IOException {
  878.         for (AbstractTreeIterator t : trees) {
  879.             t.stopWalk();
  880.         }
  881.     }

  882.     /**
  883.      * Obtain the tree iterator for the current entry.
  884.      * <p>
  885.      * Entering into (or exiting out of) a subtree causes the current tree
  886.      * iterator instance to be changed for the nth tree. This allows the tree
  887.      * iterators to manage only one list of items, with the diving handled by
  888.      * recursive trees.
  889.      *
  890.      * @param nth
  891.      *            tree to obtain the current iterator of.
  892.      * @param clazz
  893.      *            type of the tree iterator expected by the caller.
  894.      * @return r the current iterator of the requested type; null if the tree
  895.      *         has no entry to match the current path.
  896.      */
  897.     @SuppressWarnings("unchecked")
  898.     public <T extends AbstractTreeIterator> T getTree(final int nth,
  899.             final Class<T> clazz) {
  900.         final AbstractTreeIterator t = trees[nth];
  901.         return t.matches == currentHead ? (T) t : null;
  902.     }

  903.     /**
  904.      * Obtain the raw {@link org.eclipse.jgit.lib.FileMode} bits for the current
  905.      * entry.
  906.      * <p>
  907.      * Every added tree supplies mode bits, even if the tree does not contain
  908.      * the current entry. In the latter case
  909.      * {@link org.eclipse.jgit.lib.FileMode#MISSING}'s mode bits (0) are
  910.      * returned.
  911.      *
  912.      * @param nth
  913.      *            tree to obtain the mode bits from.
  914.      * @return mode bits for the current entry of the nth tree.
  915.      * @see FileMode#fromBits(int)
  916.      */
  917.     public int getRawMode(int nth) {
  918.         final AbstractTreeIterator t = trees[nth];
  919.         return t.matches == currentHead ? t.mode : 0;
  920.     }

  921.     /**
  922.      * Obtain the {@link org.eclipse.jgit.lib.FileMode} for the current entry.
  923.      * <p>
  924.      * Every added tree supplies a mode, even if the tree does not contain the
  925.      * current entry. In the latter case
  926.      * {@link org.eclipse.jgit.lib.FileMode#MISSING} is returned.
  927.      *
  928.      * @param nth
  929.      *            tree to obtain the mode from.
  930.      * @return mode for the current entry of the nth tree.
  931.      */
  932.     public FileMode getFileMode(int nth) {
  933.         return FileMode.fromBits(getRawMode(nth));
  934.     }

  935.     /**
  936.      * Obtain the {@link org.eclipse.jgit.lib.FileMode} for the current entry on
  937.      * the currentHead tree
  938.      *
  939.      * @return mode for the current entry of the currentHead tree.
  940.      * @since 4.3
  941.      */
  942.     public FileMode getFileMode() {
  943.         return FileMode.fromBits(currentHead.mode);
  944.     }

  945.     /**
  946.      * Obtain the ObjectId for the current entry.
  947.      * <p>
  948.      * Using this method to compare ObjectId values between trees of this walker
  949.      * is very inefficient. Applications should try to use
  950.      * {@link #idEqual(int, int)} or {@link #getObjectId(MutableObjectId, int)}
  951.      * whenever possible.
  952.      * <p>
  953.      * Every tree supplies an object id, even if the tree does not contain the
  954.      * current entry. In the latter case
  955.      * {@link org.eclipse.jgit.lib.ObjectId#zeroId()} is returned.
  956.      *
  957.      * @param nth
  958.      *            tree to obtain the object identifier from.
  959.      * @return object identifier for the current tree entry.
  960.      * @see #getObjectId(MutableObjectId, int)
  961.      * @see #idEqual(int, int)
  962.      * @see #getObjectId(MutableObjectId, int)
  963.      * @see #idEqual(int, int)
  964.      */
  965.     public ObjectId getObjectId(int nth) {
  966.         final AbstractTreeIterator t = trees[nth];
  967.         return t.matches == currentHead ? t.getEntryObjectId() : ObjectId
  968.                 .zeroId();
  969.     }

  970.     /**
  971.      * Obtain the ObjectId for the current entry.
  972.      * <p>
  973.      * Every tree supplies an object id, even if the tree does not contain the
  974.      * current entry. In the latter case
  975.      * {@link org.eclipse.jgit.lib.ObjectId#zeroId()} is supplied.
  976.      * <p>
  977.      * Applications should try to use {@link #idEqual(int, int)} when possible
  978.      * as it avoids conversion overheads.
  979.      *
  980.      * @param out
  981.      *            buffer to copy the object id into.
  982.      * @param nth
  983.      *            tree to obtain the object identifier from.
  984.      * @see #idEqual(int, int)
  985.      */
  986.     public void getObjectId(MutableObjectId out, int nth) {
  987.         final AbstractTreeIterator t = trees[nth];
  988.         if (t.matches == currentHead)
  989.             t.getEntryObjectId(out);
  990.         else
  991.             out.clear();
  992.     }

  993.     /**
  994.      * Compare two tree's current ObjectId values for equality.
  995.      *
  996.      * @param nthA
  997.      *            first tree to compare the object id from.
  998.      * @param nthB
  999.      *            second tree to compare the object id from.
  1000.      * @return result of
  1001.      *         <code>getObjectId(nthA).equals(getObjectId(nthB))</code>.
  1002.      * @see #getObjectId(int)
  1003.      */
  1004.     public boolean idEqual(int nthA, int nthB) {
  1005.         final AbstractTreeIterator ch = currentHead;
  1006.         final AbstractTreeIterator a = trees[nthA];
  1007.         final AbstractTreeIterator b = trees[nthB];
  1008.         if (a.matches != ch && b.matches != ch) {
  1009.             // If neither tree matches the current path node then neither
  1010.             // tree has this entry. In such case the ObjectId is zero(),
  1011.             // and zero() is always equal to zero().
  1012.             //
  1013.             return true;
  1014.         }
  1015.         if (!a.hasId() || !b.hasId())
  1016.             return false;
  1017.         if (a.matches == ch && b.matches == ch)
  1018.             return a.idEqual(b);
  1019.         return false;
  1020.     }

  1021.     /**
  1022.      * Get the current entry's name within its parent tree.
  1023.      * <p>
  1024.      * This method is not very efficient and is primarily meant for debugging
  1025.      * and final output generation. Applications should try to avoid calling it,
  1026.      * and if invoked do so only once per interesting entry, where the name is
  1027.      * absolutely required for correct function.
  1028.      *
  1029.      * @return name of the current entry within the parent tree (or directory).
  1030.      *         The name never includes a '/'.
  1031.      */
  1032.     public String getNameString() {
  1033.         final AbstractTreeIterator t = currentHead;
  1034.         final int off = t.pathOffset;
  1035.         final int end = t.pathLen;
  1036.         return RawParseUtils.decode(UTF_8, t.path, off, end);
  1037.     }

  1038.     /**
  1039.      * Get the current entry's complete path.
  1040.      * <p>
  1041.      * This method is not very efficient and is primarily meant for debugging
  1042.      * and final output generation. Applications should try to avoid calling it,
  1043.      * and if invoked do so only once per interesting entry, where the name is
  1044.      * absolutely required for correct function.
  1045.      *
  1046.      * @return complete path of the current entry, from the root of the
  1047.      *         repository. If the current entry is in a subtree there will be at
  1048.      *         least one '/' in the returned string.
  1049.      */
  1050.     public String getPathString() {
  1051.         return pathOf(currentHead);
  1052.     }

  1053.     /**
  1054.      * Get the current entry's complete path as a UTF-8 byte array.
  1055.      *
  1056.      * @return complete path of the current entry, from the root of the
  1057.      *         repository. If the current entry is in a subtree there will be at
  1058.      *         least one '/' in the returned string.
  1059.      */
  1060.     public byte[] getRawPath() {
  1061.         final AbstractTreeIterator t = currentHead;
  1062.         final int n = t.pathLen;
  1063.         final byte[] r = new byte[n];
  1064.         System.arraycopy(t.path, 0, r, 0, n);
  1065.         return r;
  1066.     }

  1067.     /**
  1068.      * Get the path length of the current entry.
  1069.      *
  1070.      * @return The path length of the current entry.
  1071.      */
  1072.     public int getPathLength() {
  1073.         return currentHead.pathLen;
  1074.     }

  1075.     /**
  1076.      * Test if the supplied path matches the current entry's path.
  1077.      * <p>
  1078.      * This method detects if the supplied path is equal to, a subtree of, or
  1079.      * not similar at all to the current entry. It is faster to use this
  1080.      * method than to use {@link #getPathString()} to first create a String
  1081.      * object, then test <code>startsWith</code> or some other type of string
  1082.      * match function.
  1083.      * <p>
  1084.      * If the current entry is a subtree, then all paths within the subtree
  1085.      * are considered to match it.
  1086.      *
  1087.      * @param p
  1088.      *            path buffer to test. Callers should ensure the path does not
  1089.      *            end with '/' prior to invocation.
  1090.      * @param pLen
  1091.      *            number of bytes from <code>buf</code> to test.
  1092.      * @return -1 if the current path is a parent to p; 0 if p matches the current
  1093.      *         path; 1 if the current path is different and will never match
  1094.      *         again on this tree walk.
  1095.      * @since 4.7
  1096.      */
  1097.     public int isPathMatch(byte[] p, int pLen) {
  1098.         final AbstractTreeIterator t = currentHead;
  1099.         final byte[] c = t.path;
  1100.         final int cLen = t.pathLen;
  1101.         int ci;

  1102.         for (ci = 0; ci < cLen && ci < pLen; ci++) {
  1103.             final int c_value = (c[ci] & 0xff) - (p[ci] & 0xff);
  1104.             if (c_value != 0) {
  1105.                 // Paths do not and will never match
  1106.                 return 1;
  1107.             }
  1108.         }

  1109.         if (ci < cLen) {
  1110.             // Ran out of pattern but we still had current data.
  1111.             // If c[ci] == '/' then pattern matches the subtree.
  1112.             // Otherwise it is a partial match == miss
  1113.             return c[ci] == '/' ? 0 : 1;
  1114.         }

  1115.         if (ci < pLen) {
  1116.             // Ran out of current, but we still have pattern data.
  1117.             // If p[ci] == '/' then this subtree is a parent in the pattern,
  1118.             // otherwise it's a miss.
  1119.             return p[ci] == '/' && FileMode.TREE.equals(t.mode) ? -1 : 1;
  1120.         }

  1121.         // Both strings are identical.
  1122.         return 0;
  1123.     }

  1124.     /**
  1125.      * Test if the supplied path matches the current entry's path.
  1126.      * <p>
  1127.      * This method tests that the supplied path is exactly equal to the current
  1128.      * entry or is one of its parent directories. It is faster to use this
  1129.      * method then to use {@link #getPathString()} to first create a String
  1130.      * object, then test <code>startsWith</code> or some other type of string
  1131.      * match function.
  1132.      * <p>
  1133.      * If the current entry is a subtree, then all paths within the subtree
  1134.      * are considered to match it.
  1135.      *
  1136.      * @param p
  1137.      *            path buffer to test. Callers should ensure the path does not
  1138.      *            end with '/' prior to invocation.
  1139.      * @param pLen
  1140.      *            number of bytes from <code>buf</code> to test.
  1141.      * @return &lt; 0 if p is before the current path; 0 if p matches the current
  1142.      *         path; 1 if the current path is past p and p will never match
  1143.      *         again on this tree walk.
  1144.      */
  1145.     public int isPathPrefix(byte[] p, int pLen) {
  1146.         final AbstractTreeIterator t = currentHead;
  1147.         final byte[] c = t.path;
  1148.         final int cLen = t.pathLen;
  1149.         int ci;

  1150.         for (ci = 0; ci < cLen && ci < pLen; ci++) {
  1151.             final int c_value = (c[ci] & 0xff) - (p[ci] & 0xff);
  1152.             if (c_value != 0)
  1153.                 return c_value;
  1154.         }

  1155.         if (ci < cLen) {
  1156.             // Ran out of pattern but we still had current data.
  1157.             // If c[ci] == '/' then pattern matches the subtree.
  1158.             // Otherwise we cannot be certain so we return -1.
  1159.             //
  1160.             return c[ci] == '/' ? 0 : -1;
  1161.         }

  1162.         if (ci < pLen) {
  1163.             // Ran out of current, but we still have pattern data.
  1164.             // If p[ci] == '/' then pattern matches this subtree,
  1165.             // otherwise we cannot be certain so we return -1.
  1166.             //
  1167.             return p[ci] == '/' && FileMode.TREE.equals(t.mode) ? 0 : -1;
  1168.         }

  1169.         // Both strings are identical.
  1170.         //
  1171.         return 0;
  1172.     }

  1173.     /**
  1174.      * Test if the supplied path matches (being suffix of) the current entry's
  1175.      * path.
  1176.      * <p>
  1177.      * This method tests that the supplied path is exactly equal to the current
  1178.      * entry, or is relative to one of entry's parent directories. It is faster
  1179.      * to use this method then to use {@link #getPathString()} to first create
  1180.      * a String object, then test <code>endsWith</code> or some other type of
  1181.      * string match function.
  1182.      *
  1183.      * @param p
  1184.      *            path buffer to test.
  1185.      * @param pLen
  1186.      *            number of bytes from <code>buf</code> to test.
  1187.      * @return true if p is suffix of the current path;
  1188.      *         false if otherwise
  1189.      */
  1190.     public boolean isPathSuffix(byte[] p, int pLen) {
  1191.         final AbstractTreeIterator t = currentHead;
  1192.         final byte[] c = t.path;
  1193.         final int cLen = t.pathLen;

  1194.         for (int i = 1; i <= pLen; i++) {
  1195.             // Pattern longer than current path
  1196.             if (i > cLen)
  1197.                 return false;
  1198.             // Current path doesn't match pattern
  1199.             if (c[cLen - i] != p[pLen - i])
  1200.                 return false;
  1201.         }

  1202.         // Whole pattern tested -> matches
  1203.         return true;
  1204.     }

  1205.     /**
  1206.      * Get the current subtree depth of this walker.
  1207.      *
  1208.      * @return the current subtree depth of this walker.
  1209.      */
  1210.     public int getDepth() {
  1211.         return depth;
  1212.     }

  1213.     /**
  1214.      * Is the current entry a subtree?
  1215.      * <p>
  1216.      * This method is faster then testing the raw mode bits of all trees to see
  1217.      * if any of them are a subtree. If at least one is a subtree then this
  1218.      * method will return true.
  1219.      *
  1220.      * @return true if {@link #enterSubtree()} will work on the current node.
  1221.      */
  1222.     public boolean isSubtree() {
  1223.         return FileMode.TREE.equals(currentHead.mode);
  1224.     }

  1225.     /**
  1226.      * Is the current entry a subtree returned after its children?
  1227.      *
  1228.      * @return true if the current node is a tree that has been returned after
  1229.      *         its children were already processed.
  1230.      * @see #isPostOrderTraversal()
  1231.      */
  1232.     public boolean isPostChildren() {
  1233.         return postChildren && isSubtree();
  1234.     }

  1235.     /**
  1236.      * Enter into the current subtree.
  1237.      * <p>
  1238.      * If the current entry is a subtree this method arranges for its children
  1239.      * to be returned before the next sibling following the subtree is returned.
  1240.      *
  1241.      * @throws org.eclipse.jgit.errors.MissingObjectException
  1242.      *             a subtree was found, but the subtree object does not exist in
  1243.      *             this repository. The repository may be missing objects.
  1244.      * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
  1245.      *             a subtree was found, and the subtree id does not denote a
  1246.      *             tree, but instead names some other non-tree type of object.
  1247.      *             The repository may have data corruption.
  1248.      * @throws org.eclipse.jgit.errors.CorruptObjectException
  1249.      *             the contents of a tree did not appear to be a tree. The
  1250.      *             repository may have data corruption.
  1251.      * @throws java.io.IOException
  1252.      *             a loose object or pack file could not be read.
  1253.      */
  1254.     public void enterSubtree() throws MissingObjectException,
  1255.             IncorrectObjectTypeException, CorruptObjectException, IOException {
  1256.         Arrays.fill(attrs, null);
  1257.         final AbstractTreeIterator ch = currentHead;
  1258.         final AbstractTreeIterator[] tmp = new AbstractTreeIterator[trees.length];
  1259.         for (int i = 0; i < trees.length; i++) {
  1260.             final AbstractTreeIterator t = trees[i];
  1261.             final AbstractTreeIterator n;
  1262.             // If we find a GITLINK when attempting to enter a subtree, then the
  1263.             // GITLINK must exist as a TREE in the index, meaning the working tree
  1264.             // entry should be treated as a TREE
  1265.             if (t.matches == ch && !t.eof() &&
  1266.                     (FileMode.TREE.equals(t.mode)
  1267.                             || (FileMode.GITLINK.equals(t.mode) && t.isWorkTree())))
  1268.                 n = t.createSubtreeIterator(reader, idBuffer);
  1269.             else
  1270.                 n = t.createEmptyTreeIterator();
  1271.             tmp[i] = n;
  1272.         }
  1273.         depth++;
  1274.         advance = false;
  1275.         System.arraycopy(tmp, 0, trees, 0, trees.length);
  1276.     }

  1277.     /**
  1278.      * Returns an AbstractTreeIterator from {@code trees} with the smallest name, and sets its
  1279.      * {@code matches} field. This may clobber {@code matches} in other {@code tree}s. Other iterators
  1280.      * at the same name will have their {@code matches} pointing to the same {@code min()} value.
  1281.      *
  1282.      * @return the smallest tree iterator available.
  1283.      * @throws CorruptObjectException
  1284.      */
  1285.     @SuppressWarnings("unused")
  1286.     AbstractTreeIterator min() throws CorruptObjectException {
  1287.         int i = 0;
  1288.         AbstractTreeIterator minRef = trees[i];
  1289.         while (minRef.eof() && ++i < trees.length)
  1290.             minRef = trees[i];
  1291.         if (minRef.eof())
  1292.             return minRef;

  1293.         minRef.matches = minRef;
  1294.         while (++i < trees.length) {
  1295.             final AbstractTreeIterator t = trees[i];
  1296.             if (t.eof())
  1297.                 continue;
  1298.             final int cmp = t.pathCompare(minRef);
  1299.             if (cmp < 0) {
  1300.                 t.matches = t;
  1301.                 minRef = t;
  1302.             } else if (cmp == 0) {
  1303.                 t.matches = minRef;
  1304.             }
  1305.         }

  1306.         return minRef;
  1307.     }

  1308.     void popEntriesEqual() throws CorruptObjectException {
  1309.         final AbstractTreeIterator ch = currentHead;
  1310.         for (AbstractTreeIterator t : trees) {
  1311.             if (t.matches == ch) {
  1312.                 t.next(1);
  1313.                 t.matches = null;
  1314.             }
  1315.         }
  1316.     }

  1317.     void skipEntriesEqual() throws CorruptObjectException {
  1318.         final AbstractTreeIterator ch = currentHead;
  1319.         for (AbstractTreeIterator t : trees) {
  1320.             if (t.matches == ch) {
  1321.                 t.skip();
  1322.                 t.matches = null;
  1323.             }
  1324.         }
  1325.     }

  1326.     void exitSubtree() {
  1327.         depth--;
  1328.         for (int i = 0; i < trees.length; i++)
  1329.             trees[i] = trees[i].parent;

  1330.         AbstractTreeIterator minRef = null;
  1331.         for (AbstractTreeIterator t : trees) {
  1332.             if (t.matches != t)
  1333.                 continue;
  1334.             if (minRef == null || t.pathCompare(minRef) < 0)
  1335.                 minRef = t;
  1336.         }
  1337.         currentHead = minRef;
  1338.     }

  1339.     private CanonicalTreeParser parserFor(AnyObjectId id)
  1340.             throws IncorrectObjectTypeException, IOException {
  1341.         final CanonicalTreeParser p = new CanonicalTreeParser();
  1342.         p.reset(reader, id);
  1343.         return p;
  1344.     }

  1345.     static String pathOf(AbstractTreeIterator t) {
  1346.         return RawParseUtils.decode(UTF_8, t.path, 0, t.pathLen);
  1347.     }

  1348.     static String pathOf(byte[] buf, int pos, int end) {
  1349.         return RawParseUtils.decode(UTF_8, buf, pos, end);
  1350.     }

  1351.     /**
  1352.      * Get the tree of that type.
  1353.      *
  1354.      * @param type
  1355.      *            of the tree to be queried
  1356.      * @return the tree of that type or null if none is present.
  1357.      * @since 4.3
  1358.      * @param <T>
  1359.      *            a tree type.
  1360.      */
  1361.     public <T extends AbstractTreeIterator> T getTree(Class<T> type) {
  1362.         for (AbstractTreeIterator tree : trees) {
  1363.             if (type.isInstance(tree)) {
  1364.                 return type.cast(tree);
  1365.             }
  1366.         }
  1367.         return null;
  1368.     }

  1369.     /**
  1370.      * Inspect config and attributes to return a filtercommand applicable for
  1371.      * the current path.
  1372.      *
  1373.      * @param filterCommandType
  1374.      *            which type of filterCommand should be executed. E.g. "clean",
  1375.      *            "smudge". For "smudge" consider using
  1376.      *            {{@link #getSmudgeCommand(int)} instead.
  1377.      * @return a filter command
  1378.      * @throws java.io.IOException
  1379.      * @since 4.2
  1380.      */
  1381.     public String getFilterCommand(String filterCommandType)
  1382.             throws IOException {
  1383.         Attributes attributes = getAttributes();

  1384.         Attribute f = attributes.get(Constants.ATTR_FILTER);
  1385.         if (f == null) {
  1386.             return null;
  1387.         }
  1388.         String filterValue = f.getValue();
  1389.         if (filterValue == null) {
  1390.             return null;
  1391.         }

  1392.         String filterCommand = getFilterCommandDefinition(filterValue,
  1393.                 filterCommandType);
  1394.         if (filterCommand == null) {
  1395.             return null;
  1396.         }
  1397.         return filterCommand.replaceAll("%f", //$NON-NLS-1$
  1398.                 Matcher.quoteReplacement(
  1399.                         QuotedString.BOURNE.quote((getPathString()))));
  1400.     }

  1401.     /**
  1402.      * Inspect config and attributes to return a filtercommand applicable for
  1403.      * the current path.
  1404.      *
  1405.      * @param index
  1406.      *            of the tree the item to be smudged is in
  1407.      * @return a filter command
  1408.      * @throws java.io.IOException
  1409.      * @since 6.1
  1410.      */
  1411.     public String getSmudgeCommand(int index)
  1412.             throws IOException {
  1413.         return getSmudgeCommand(getAttributes(index));
  1414.     }

  1415.     /**
  1416.      * Inspect config and attributes to return a filtercommand applicable for
  1417.      * the current path.
  1418.      *
  1419.      * @param attributes
  1420.      *            to use
  1421.      * @return a filter command
  1422.      * @throws java.io.IOException
  1423.      * @since 6.1
  1424.      */
  1425.     public String getSmudgeCommand(Attributes attributes) throws IOException {
  1426.         if (attributes == null) {
  1427.             return null;
  1428.         }
  1429.         Attribute f = attributes.get(Constants.ATTR_FILTER);
  1430.         if (f == null) {
  1431.             return null;
  1432.         }
  1433.         String filterValue = f.getValue();
  1434.         if (filterValue == null) {
  1435.             return null;
  1436.         }

  1437.         String filterCommand = getFilterCommandDefinition(filterValue,
  1438.                 Constants.ATTR_FILTER_TYPE_SMUDGE);
  1439.         if (filterCommand == null) {
  1440.             return null;
  1441.         }
  1442.         return filterCommand.replaceAll("%f", //$NON-NLS-1$
  1443.                 Matcher.quoteReplacement(
  1444.                         QuotedString.BOURNE.quote((getPathString()))));
  1445.     }

  1446.     /**
  1447.      * Get the filter command how it is defined in gitconfig. The returned
  1448.      * string may contain "%f" which needs to be replaced by the current path
  1449.      * before executing the filter command. These filter definitions are cached
  1450.      * for better performance.
  1451.      *
  1452.      * @param filterDriverName
  1453.      *            The name of the filter driver as it is referenced in the
  1454.      *            gitattributes file. E.g. "lfs". For each filter driver there
  1455.      *            may be many commands defined in the .gitconfig
  1456.      * @param filterCommandType
  1457.      *            The type of the filter command for a specific filter driver.
  1458.      *            May be "clean" or "smudge".
  1459.      * @return the definition of the command to be executed for this filter
  1460.      *         driver and filter command
  1461.      */
  1462.     private String getFilterCommandDefinition(String filterDriverName,
  1463.             String filterCommandType) {
  1464.         String key = filterDriverName + "." + filterCommandType; //$NON-NLS-1$
  1465.         String filterCommand = filterCommandsByNameDotType.get(key);
  1466.         if (filterCommand != null)
  1467.             return filterCommand;
  1468.         filterCommand = config.getString(ConfigConstants.CONFIG_FILTER_SECTION,
  1469.                 filterDriverName, filterCommandType);
  1470.         boolean useBuiltin = config.getBoolean(
  1471.                 ConfigConstants.CONFIG_FILTER_SECTION,
  1472.                 filterDriverName, ConfigConstants.CONFIG_KEY_USEJGITBUILTIN, false);
  1473.         if (useBuiltin) {
  1474.             String builtinFilterCommand = Constants.BUILTIN_FILTER_PREFIX
  1475.                     + filterDriverName + '/' + filterCommandType;
  1476.             if (filterCommands != null
  1477.                     && filterCommands.contains(builtinFilterCommand)) {
  1478.                 filterCommand = builtinFilterCommand;
  1479.             }
  1480.         }
  1481.         if (filterCommand != null) {
  1482.             filterCommandsByNameDotType.put(key, filterCommand);
  1483.         }
  1484.         return filterCommand;
  1485.     }
  1486. }