changeset 339:863356c2847e

Issue 16: respect glob patterns in HgIgnore for sub-directories
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Wed, 16 Nov 2011 22:42:21 +0100
parents 3cfa4d908fc9
children a54bfe0db959
files cmdline/org/tmatesoft/hg/console/Main.java src/org/tmatesoft/hg/repo/HgIgnore.java src/org/tmatesoft/hg/repo/HgInternals.java src/org/tmatesoft/hg/util/Path.java test/org/tmatesoft/hg/test/TestIgnore.java
diffstat 5 files changed, 106 insertions(+), 22 deletions(-) [+]
line wrap: on
line diff
--- 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])));
 		}
 	}
 	
--- 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 <code>true</code> 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;
 	}
--- 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();
 	}
--- 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<String> segments() {
+		class SegSeq implements Iterable<String>, Iterator<String> {
+			private int pos; // first char to return
+
+			public Iterator<String> 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);
--- 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));
+		}
+	}
 }