CleanCommand: fix prefix matching
String.startsWith() is not a valid test for file path prefixes: directory "a" is _not_ a prefix of a file "ab", only of "a/b". Add a proper Paths.isEqualOrPrefix() method and use it in CleanCommand. Bug: 580478 Change-Id: I6863e6ba94a8ffba6561835cc57044a0945d2770 Signed-off-by: Thomas Wolf <twolf@apache.org>
This commit is contained in:
parent
59e8bec6e7
commit
8184683f7e
|
@ -301,4 +301,25 @@ public void testFilesShouldBeCleanedInSubSubFolders()
|
||||||
writeTrashFile("this_is/not_ok/more/subdirs/file.txt", "2");
|
writeTrashFile("this_is/not_ok/more/subdirs/file.txt", "2");
|
||||||
git.clean().setCleanDirectories(true).setIgnore(false).call();
|
git.clean().setCleanDirectories(true).setIgnore(false).call();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPrefix() throws Exception {
|
||||||
|
File a = writeTrashFile("a.txt", "a");
|
||||||
|
File b = writeTrashFile("a/a.txt", "sub a");
|
||||||
|
File dir = b.getParentFile();
|
||||||
|
git.clean().call();
|
||||||
|
assertFalse(a.exists());
|
||||||
|
assertTrue(dir.exists());
|
||||||
|
assertTrue(b.exists());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPrefixWithDir() throws Exception {
|
||||||
|
File a = writeTrashFile("a.txt", "a");
|
||||||
|
File b = writeTrashFile("a/a.txt", "sub a");
|
||||||
|
File dir = b.getParentFile();
|
||||||
|
git.clean().setCleanDirectories(true).call();
|
||||||
|
assertFalse(a.exists());
|
||||||
|
assertFalse(dir.exists());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,9 @@
|
||||||
import static org.eclipse.jgit.util.Paths.compare;
|
import static org.eclipse.jgit.util.Paths.compare;
|
||||||
import static org.eclipse.jgit.util.Paths.compareSameName;
|
import static org.eclipse.jgit.util.Paths.compareSameName;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import org.eclipse.jgit.lib.Constants;
|
import org.eclipse.jgit.lib.Constants;
|
||||||
import org.eclipse.jgit.lib.FileMode;
|
import org.eclipse.jgit.lib.FileMode;
|
||||||
|
@ -31,6 +33,23 @@ public void testStripTrailingSeparator() {
|
||||||
assertEquals("a/boo", Paths.stripTrailingSeparator("a/boo///"));
|
assertEquals("a/boo", Paths.stripTrailingSeparator("a/boo///"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPrefix() {
|
||||||
|
assertTrue(Paths.isEqualOrPrefix("a", "a"));
|
||||||
|
assertTrue(Paths.isEqualOrPrefix("a", "a/b"));
|
||||||
|
assertTrue(Paths.isEqualOrPrefix("a", "a/a.txt"));
|
||||||
|
assertFalse(Paths.isEqualOrPrefix("a", "ab"));
|
||||||
|
assertFalse(Paths.isEqualOrPrefix("a", "a.txt"));
|
||||||
|
assertFalse(Paths.isEqualOrPrefix("a", "b/a.txt"));
|
||||||
|
assertFalse(Paths.isEqualOrPrefix("a", "b/a"));
|
||||||
|
assertFalse(Paths.isEqualOrPrefix("a", "ab/a.txt"));
|
||||||
|
assertFalse(Paths.isEqualOrPrefix("", "a"));
|
||||||
|
assertTrue(Paths.isEqualOrPrefix("", ""));
|
||||||
|
assertTrue(Paths.isEqualOrPrefix("a/b", "a/b"));
|
||||||
|
assertTrue(Paths.isEqualOrPrefix("a/b", "a/b/c"));
|
||||||
|
assertFalse(Paths.isEqualOrPrefix("a/b", "a/bc"));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPathCompare() {
|
public void testPathCompare() {
|
||||||
byte[] a = Constants.encode("afoo/bar.c");
|
byte[] a = Constants.encode("afoo/bar.c");
|
||||||
|
|
|
@ -83,4 +83,11 @@
|
||||||
</message_arguments>
|
</message_arguments>
|
||||||
</filter>
|
</filter>
|
||||||
</resource>
|
</resource>
|
||||||
|
<resource path="src/org/eclipse/jgit/util/Paths.java" type="org.eclipse.jgit.util.Paths">
|
||||||
|
<filter id="337768515">
|
||||||
|
<message_arguments>
|
||||||
|
<message_argument value="org.eclipse.jgit.util.Paths"/>
|
||||||
|
</message_arguments>
|
||||||
|
</filter>
|
||||||
|
</resource>
|
||||||
</component>
|
</component>
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
import org.eclipse.jgit.util.FS;
|
import org.eclipse.jgit.util.FS;
|
||||||
import org.eclipse.jgit.util.FileUtils;
|
import org.eclipse.jgit.util.FileUtils;
|
||||||
|
import org.eclipse.jgit.util.Paths;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove untracked files from the working tree
|
* Remove untracked files from the working tree
|
||||||
|
@ -91,15 +92,16 @@ public Set<String> call() throws NoWorkTreeException, GitAPIException {
|
||||||
Set<String> notIgnoredDirs = filterIgnorePaths(untrackedDirs,
|
Set<String> notIgnoredDirs = filterIgnorePaths(untrackedDirs,
|
||||||
status.getIgnoredNotInIndex(), false);
|
status.getIgnoredNotInIndex(), false);
|
||||||
|
|
||||||
for (String file : notIgnoredFiles)
|
for (String file : notIgnoredFiles) {
|
||||||
if (paths.isEmpty() || paths.contains(file)) {
|
if (paths.isEmpty() || paths.contains(file)) {
|
||||||
files = cleanPath(file, files);
|
files = cleanPath(file, files);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
for (String dir : notIgnoredDirs)
|
for (String dir : notIgnoredDirs) {
|
||||||
if (paths.isEmpty() || paths.contains(dir)) {
|
if (paths.isEmpty() || paths.contains(dir)) {
|
||||||
files = cleanPath(dir, files);
|
files = cleanPath(dir, files);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new JGitInternalException(e.getMessage(), e);
|
throw new JGitInternalException(e.getMessage(), e);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -142,14 +144,14 @@ private Set<String> cleanPath(String path, Set<String> inFiles)
|
||||||
FileUtils.delete(curFile, FileUtils.RECURSIVE
|
FileUtils.delete(curFile, FileUtils.RECURSIVE
|
||||||
| FileUtils.SKIP_MISSING);
|
| FileUtils.SKIP_MISSING);
|
||||||
}
|
}
|
||||||
inFiles.add(path + "/"); //$NON-NLS-1$
|
inFiles.add(path + '/');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!dryRun) {
|
if (!dryRun) {
|
||||||
FileUtils.delete(curFile,
|
FileUtils.delete(curFile,
|
||||||
FileUtils.RECURSIVE | FileUtils.SKIP_MISSING);
|
FileUtils.RECURSIVE | FileUtils.SKIP_MISSING);
|
||||||
}
|
}
|
||||||
inFiles.add(path + "/"); //$NON-NLS-1$
|
inFiles.add(path + '/');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -166,14 +168,16 @@ private Set<String> filterIgnorePaths(Set<String> inputPaths,
|
||||||
Set<String> ignoredNotInIndex, boolean exact) {
|
Set<String> ignoredNotInIndex, boolean exact) {
|
||||||
if (ignore) {
|
if (ignore) {
|
||||||
Set<String> filtered = new TreeSet<>(inputPaths);
|
Set<String> filtered = new TreeSet<>(inputPaths);
|
||||||
for (String path : inputPaths)
|
for (String path : inputPaths) {
|
||||||
for (String ignored : ignoredNotInIndex)
|
for (String ignored : ignoredNotInIndex) {
|
||||||
if ((exact && path.equals(ignored))
|
if ((exact && path.equals(ignored))
|
||||||
|| (!exact && path.startsWith(ignored))) {
|
|| (!exact
|
||||||
|
&& Paths.isEqualOrPrefix(ignored, path))) {
|
||||||
filtered.remove(path);
|
filtered.remove(path);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return filtered;
|
return filtered;
|
||||||
}
|
}
|
||||||
return inputPaths;
|
return inputPaths;
|
||||||
|
@ -182,14 +186,14 @@ private Set<String> filterIgnorePaths(Set<String> inputPaths,
|
||||||
private Set<String> filterFolders(Set<String> untracked,
|
private Set<String> filterFolders(Set<String> untracked,
|
||||||
Set<String> untrackedFolders) {
|
Set<String> untrackedFolders) {
|
||||||
Set<String> filtered = new TreeSet<>(untracked);
|
Set<String> filtered = new TreeSet<>(untracked);
|
||||||
for (String file : untracked)
|
for (String file : untracked) {
|
||||||
for (String folder : untrackedFolders)
|
for (String folder : untrackedFolders) {
|
||||||
if (file.startsWith(folder)) {
|
if (Paths.isEqualOrPrefix(folder, file)) {
|
||||||
filtered.remove(file);
|
filtered.remove(file);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return filtered;
|
return filtered;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,8 @@
|
||||||
*
|
*
|
||||||
* @since 4.2
|
* @since 4.2
|
||||||
*/
|
*/
|
||||||
public class Paths {
|
public final class Paths {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove trailing {@code '/'} if present.
|
* Remove trailing {@code '/'} if present.
|
||||||
*
|
*
|
||||||
|
@ -42,6 +43,33 @@ public static String stripTrailingSeparator(String path) {
|
||||||
return path.substring(0, i);
|
return path.substring(0, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether a git path {@code folder} is a prefix of another git
|
||||||
|
* path {@code path}, or the same as {@code path}. An empty {@code folder}
|
||||||
|
* is <em>not</em> not considered a prefix and matches only if {@code path}
|
||||||
|
* is also empty.
|
||||||
|
*
|
||||||
|
* @param folder
|
||||||
|
* a git path for a directory, without trailing slash
|
||||||
|
* @param path
|
||||||
|
* a git path
|
||||||
|
* @return {@code true} if {@code folder} is a directory prefix of
|
||||||
|
* {@code path}, or is equal to {@code path}, {@code false}
|
||||||
|
* otherwise
|
||||||
|
* @since 6.3
|
||||||
|
*/
|
||||||
|
public static boolean isEqualOrPrefix(String folder, String path) {
|
||||||
|
if (folder.isEmpty()) {
|
||||||
|
return path.isEmpty();
|
||||||
|
}
|
||||||
|
boolean isPrefix = path.startsWith(folder);
|
||||||
|
if (isPrefix) {
|
||||||
|
int length = folder.length();
|
||||||
|
return path.length() == length || path.charAt(length) == '/';
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compare two paths according to Git path sort ordering rules.
|
* Compare two paths according to Git path sort ordering rules.
|
||||||
*
|
*
|
||||||
|
@ -63,9 +91,8 @@ public static String stripTrailingSeparator(String path) {
|
||||||
* @param bMode
|
* @param bMode
|
||||||
* mode of the second file. Trees are sorted as though
|
* mode of the second file. Trees are sorted as though
|
||||||
* {@code bPath[bEnd] == '/'}, even if bEnd does not exist.
|
* {@code bPath[bEnd] == '/'}, even if bEnd does not exist.
|
||||||
* @return <0 if {@code aPath} sorts before {@code bPath};
|
* @return <0 if {@code aPath} sorts before {@code bPath}; 0 if the paths
|
||||||
* 0 if the paths are the same;
|
* are the same; >0 if {@code aPath} sorts after {@code bPath}.
|
||||||
* >0 if {@code aPath} sorts after {@code bPath}.
|
|
||||||
*/
|
*/
|
||||||
public static int compare(byte[] aPath, int aPos, int aEnd, int aMode,
|
public static int compare(byte[] aPath, int aPos, int aEnd, int aMode,
|
||||||
byte[] bPath, int bPos, int bEnd, int bMode) {
|
byte[] bPath, int bPos, int bEnd, int bMode) {
|
||||||
|
|
Loading…
Reference in New Issue