From 75a0dcac1883414f5f99eec37ffa9cb305940718 Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Mon, 14 Dec 2015 19:57:24 -0800 Subject: [PATCH] Do not let PathFilter.create("a/b") match 'a' unless 'a' is a subtree PathFilter and PathFilterGroup form JGit's implementation of git's path-limiting feature in commands like log and diff. To save time when traversing trees, a path specification foo/bar/baz tells the tree walker not to traverse unrelated trees like qux/. It does that by returning false from include when the tree walker is visiting qux and true when it is visiting foo. Unfortunately that test was implemented to be slightly over-eager: it doesn't only return true when asked whether to visit a subtree "foo" but when asked about a plain file "foo" as well. As a result, diffs and logs restricted to some-file/non-existing-suffix unexpectedly match against some-file: $ jgit log -- LICENSE/no-such-file commit 629fd0d594d242eab26161b0dac34f7576fd4d3d Author: Shawn O. Pearce Date: Fri Jul 02 14:52:49 2010 -0700 Clean up LICENSE file [...] Fix it by checking against the entry's mode. Gitiles +log has the same bug and benefits from the same fix. Callers know not to worry about what subtrees are included in the tree walk because shouldBeRecursive() returns true in this case, so this behavior change should be safe. This also better matches the behavior of C git: $ empty=$(git mktree * This method tests that the supplied path is exactly equal to the current - * entry, or is one of its parent directories. It is faster to use this + * entry or is one of its parent directories. It is faster to use this * method then to use {@link #getPathString()} to first create a String * object, then test startsWith or some other type of string * match function. + *

+ * If the current entry is a subtree, then all paths within the subtree + * are considered to match it. * * @param p * path buffer to test. Callers should ensure the path does not @@ -900,7 +903,7 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { // If p[ci] == '/' then pattern matches this subtree, // otherwise we cannot be certain so we return -1. // - return p[ci] == '/' ? 0 : -1; + return p[ci] == '/' && FileMode.TREE.equals(t.mode) ? 0 : -1; } // Both strings are identical. diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilterGroup.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilterGroup.java index bdfde0bfcd..7601956c43 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilterGroup.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/filter/PathFilterGroup.java @@ -245,9 +245,9 @@ public class PathFilterGroup { int hash = hasher.nextHash(); if (fullpaths.contains(rp, hasher.length(), hash)) return true; - if (!hasher.hasNext()) - if (prefixes.contains(rp, hasher.length(), hash)) - return true; + if (!hasher.hasNext() && walker.isSubtree() + && prefixes.contains(rp, hasher.length(), hash)) + return true; } final int cmp = walker.isPathPrefix(max, max.length); -- 2.39.5