# HG changeset patch # User Artem Tikhomirov # Date 1321479741 -3600 # Node ID 863356c2847e251a815a0e2c3b92f91d0461dfdd # Parent 3cfa4d908fc9c8d8f4155d95d4edb9b30dc10759 Issue 16: respect glob patterns in HgIgnore for sub-directories diff -r 3cfa4d908fc9 -r 863356c2847e cmdline/org/tmatesoft/hg/console/Main.java --- a/cmdline/org/tmatesoft/hg/console/Main.java Tue Nov 15 04:47:03 2011 +0100 +++ b/cmdline/org/tmatesoft/hg/console/Main.java Wed Nov 16 22:42:21 2011 +0100 @@ -49,6 +49,7 @@ import org.tmatesoft.hg.repo.HgDirstate; import org.tmatesoft.hg.repo.HgDirstate.EntryKind; import org.tmatesoft.hg.repo.HgDirstate.Record; +import org.tmatesoft.hg.repo.HgIgnore; import org.tmatesoft.hg.repo.HgInternals; import org.tmatesoft.hg.repo.HgManifest; import org.tmatesoft.hg.repo.HgManifest.Flags; @@ -89,7 +90,7 @@ public static void main(String[] args) throws Exception { Main m = new Main(args); - m.buildFileLog(); +// m.buildFileLog(); // m.testConsoleLog(); // m.testTreeTraversal(); // m.testRevisionMap(); @@ -98,7 +99,7 @@ // m.testParents(); // m.testEffectiveFileLog(); // m.testCatAtCsetRevision(); -// m.testMergeState(); + m.testMergeState(); // m.testFileStatus(); // m.dumpBranches(); // m.inflaterLengthException(); @@ -413,11 +414,10 @@ } private void dumpIgnored() { - HgInternals debug = new HgInternals(hgRepo); String[] toCheck = new String[] {"design.txt", "src/com/tmate/hgkit/ll/Changelog.java", "src/Extras.java", "bin/com/tmate/hgkit/ll/Changelog.class"}; - boolean[] checkResult = debug.checkIgnored(toCheck); + HgIgnore ignore = hgRepo.getIgnore(); for (int i = 0; i < toCheck.length; i++) { - System.out.println("Ignored " + toCheck[i] + ": " + checkResult[i]); + System.out.println("Ignored " + toCheck[i] + ": " + ignore.isIgnored(Path.create(toCheck[i]))); } } diff -r 3cfa4d908fc9 -r 863356c2847e src/org/tmatesoft/hg/repo/HgIgnore.java --- a/src/org/tmatesoft/hg/repo/HgIgnore.java Tue Nov 15 04:47:03 2011 +0100 +++ b/src/org/tmatesoft/hg/repo/HgIgnore.java Wed Nov 16 22:42:21 2011 +0100 @@ -118,14 +118,7 @@ private static String glob2regex(String line) { assert line.length() > 0; StringBuilder sb = new StringBuilder(line.length() + 10); - if (line.charAt(0) != '*') { - sb.append('^'); // help avoid matcher.find() to match 'bin' pattern in the middle of the filename - } int start = 0, end = line.length() - 1; - // '*' at the beginning and end of a line are useless for Pattern - // XXX although how about **.txt - such globs can be seen in a config, are they valid for HgIgnore? - while (start <= end && line.charAt(start) == '*') start++; - while (end > start && line.charAt(end) == '*') end--; int inCurly = 0; for (int i = start; i <= end; i++) { @@ -170,10 +163,18 @@ * @return true if matches repository configuration of ignored files. */ public boolean isIgnored(Path path) { + boolean isDeep = path.toString().indexOf('/') != -1; for (Pattern p : entries) { - if (p.matcher(path).find()) { + if (p.matcher(path).matches()) { return true; } + if (isDeep) { + for (String segment : path.segments()) { + if (p.matcher(segment).matches()) { + return true; + } + } + } } return false; } diff -r 3cfa4d908fc9 -r 863356c2847e src/org/tmatesoft/hg/repo/HgInternals.java --- a/src/org/tmatesoft/hg/repo/HgInternals.java Tue Nov 15 04:47:03 2011 +0100 +++ b/src/org/tmatesoft/hg/repo/HgInternals.java Wed Nov 16 22:42:21 2011 +0100 @@ -83,15 +83,6 @@ return rv; } - public boolean[] checkIgnored(String... toCheck) { - HgIgnore ignore = repo.getIgnore(); - boolean[] rv = new boolean[toCheck.length]; - for (int i = 0; i < toCheck.length; i++) { - rv[i] = ignore.isIgnored(Path.create(toCheck[i])); - } - return rv; - } - public File getRepositoryDir() { return repo.getRepositoryRoot(); } diff -r 3cfa4d908fc9 -r 863356c2847e src/org/tmatesoft/hg/util/Path.java --- a/src/org/tmatesoft/hg/util/Path.java Tue Nov 15 04:47:03 2011 +0100 +++ b/src/org/tmatesoft/hg/util/Path.java Wed Nov 16 22:42:21 2011 +0100 @@ -17,6 +17,8 @@ package org.tmatesoft.hg.util; import java.util.Collection; +import java.util.Iterator; +import java.util.NoSuchElementException; /** * Identify repository files (not String nor io.File). Convenient for pattern matching. Memory-friendly. @@ -61,6 +63,43 @@ public String toString() { return path; // CharSequence demands toString() impl } + + public Iterable segments() { + class SegSeq implements Iterable, Iterator { + private int pos; // first char to return + + public Iterator iterator() { + reset(); + return this; + } + public boolean hasNext() { + return pos < path.length(); + } + public String next() { + if (pos >= path.length()) { + throw new NoSuchElementException(); + } + int x = path.indexOf('/', pos); + if (x == -1) { + String rv = path.substring(pos); + pos = path.length(); + return rv; + } else { + String rv = path.substring(pos, x); + pos = x+1; + return rv; + } + } + public void remove() { + throw new UnsupportedOperationException(); + } + + private void reset() { + pos = 0; + } + }; + return new SegSeq(); + } public int compareTo(Path o) { return path.compareTo(o.path); diff -r 3cfa4d908fc9 -r 863356c2847e test/org/tmatesoft/hg/test/TestIgnore.java --- a/test/org/tmatesoft/hg/test/TestIgnore.java Tue Nov 15 04:47:03 2011 +0100 +++ b/test/org/tmatesoft/hg/test/TestIgnore.java Wed Nov 16 22:42:21 2011 +0100 @@ -41,6 +41,8 @@ TestIgnore test = new TestIgnore(); test.testGlobWithAlternatives(); test.testComplexFileParse(); + test.testSegmentsMatch(); + test.testWildcardsDoNotMatchDirectorySeparator(); test.errorCollector.verify(); } @@ -71,4 +73,55 @@ } } + @Test + public void testSegmentsMatch() throws Exception { + String s = "syntax:glob\nbin\n.*\nTEST-*.xml"; + HgIgnore hgIgnore = HgInternals.newHgIgnore(new StringReader(s)); + Path[] toCheck = new Path[] { + Path.create("bin/org/sample/First.class"), + Path.create(".ignored-file"), + Path.create("dir/.ignored-file"), + Path.create("dir/.ignored-dir/file"), + Path.create("TEST-a.xml"), + Path.create("dir/TEST-b.xml"), + }; + for (Path p : toCheck) { + errorCollector.assertTrue(p.toString(), hgIgnore.isIgnored(p)); + } + } + + @Test + public void testWildcardsDoNotMatchDirectorySeparator() throws Exception { + String s = "syntax:glob\na?b\nc*d"; + HgIgnore hgIgnore = HgInternals.newHgIgnore(new StringReader(s)); + // shall not be ignored + Path[] toPass = new Path[] { + Path.create("a/b"), + Path.create("a/b/x"), + Path.create("x/a/b"), + Path.create("axyb"), + Path.create("c/d"), + Path.create("c/d/x"), + Path.create("x/c/d"), + }; + // shall be ignored + Path[] toIgnore = new Path[] { + Path.create("axb"), + Path.create("a3b"), + Path.create("a_b"), + Path.create("cd"), + Path.create("cxd"), + Path.create("cxyd"), + Path.create("x/cd"), + Path.create("x/cxyd"), + Path.create("cd/x"), + Path.create("cxyd/x"), + }; + for (Path p : toIgnore) { + errorCollector.assertTrue(p.toString(), hgIgnore.isIgnored(p)); + } + for (Path p : toPass) { + errorCollector.assertTrue(p.toString(), !hgIgnore.isIgnored(p)); + } + } }