comparison src/org/tmatesoft/hg/repo/HgIgnore.java @ 414:bb278ccf9866

Pull changes from smartgit3 branch
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Wed, 21 Mar 2012 20:51:12 +0100
parents 0f5696623512
children 7f136a3fa671
comparison
equal deleted inserted replaced
413:7f27122011c3 414:bb278ccf9866
1 /* 1 /*
2 * Copyright (c) 2010-2011 TMate Software Ltd 2 * Copyright (c) 2010-2012 TMate Software Ltd
3 * 3 *
4 * This program is free software; you can redistribute it and/or modify 4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by 5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 2 of the License. 6 * the Free Software Foundation; version 2 of the License.
7 * 7 *
25 import java.util.List; 25 import java.util.List;
26 import java.util.regex.Pattern; 26 import java.util.regex.Pattern;
27 import java.util.regex.PatternSyntaxException; 27 import java.util.regex.PatternSyntaxException;
28 28
29 import org.tmatesoft.hg.util.Path; 29 import org.tmatesoft.hg.util.Path;
30 import org.tmatesoft.hg.util.PathRewrite;
30 31
31 /** 32 /**
32 * Handling of ignored paths according to .hgignore configuration 33 * Handling of ignored paths according to .hgignore configuration
33 * 34 *
34 * @author Artem Tikhomirov 35 * @author Artem Tikhomirov
35 * @author TMate Software Ltd. 36 * @author TMate Software Ltd.
36 */ 37 */
37 public class HgIgnore implements Path.Matcher { 38 public class HgIgnore implements Path.Matcher {
38 39
39 private List<Pattern> entries; 40 private List<Pattern> entries;
40 41 private final PathRewrite globPathHelper;
41 HgIgnore() { 42
43 HgIgnore(PathRewrite globPathRewrite) {
42 entries = Collections.emptyList(); 44 entries = Collections.emptyList();
43 } 45 globPathHelper = globPathRewrite;
44 46 }
45 /* package-local */List<String> read(File hgignoreFile) throws IOException { 47
48 /* package-local */ List<String> read(File hgignoreFile) throws IOException {
46 if (!hgignoreFile.exists()) { 49 if (!hgignoreFile.exists()) {
47 return null; 50 return null;
48 } 51 }
49 BufferedReader fr = new BufferedReader(new FileReader(hgignoreFile)); 52 BufferedReader fr = new BufferedReader(new FileReader(hgignoreFile));
50 try { 53 try {
52 } finally { 55 } finally {
53 fr.close(); 56 fr.close();
54 } 57 }
55 } 58 }
56 59
57 /* package-local */List<String> read(BufferedReader content) throws IOException { 60 /* package-local */ List<String> read(BufferedReader content) throws IOException {
61 final String REGEXP = "regexp", GLOB = "glob";
62 final String REGEXP_PREFIX = REGEXP + ":", GLOB_PREFIX = GLOB + ":";
58 ArrayList<String> errors = new ArrayList<String>(); 63 ArrayList<String> errors = new ArrayList<String>();
59 ArrayList<Pattern> result = new ArrayList<Pattern>(entries); // start with existing 64 ArrayList<Pattern> result = new ArrayList<Pattern>(entries); // start with existing
60 String syntax = "regexp"; // or "glob" 65 String syntax = REGEXP;
61 String line; 66 String line;
62 while ((line = content.readLine()) != null) { 67 while ((line = content.readLine()) != null) {
63 line = line.trim(); 68 line = line.trim();
64 if (line.startsWith("syntax:")) { 69 if (line.startsWith("syntax:")) {
65 syntax = line.substring("syntax:".length()).trim(); 70 syntax = line.substring("syntax:".length()).trim();
66 if (!"regexp".equals(syntax) && !"glob".equals(syntax)) { 71 if (!REGEXP.equals(syntax) && !GLOB.equals(syntax)) {
67 errors.add(line); 72 errors.add(line);
68 continue; 73 continue;
69 //throw new IllegalStateException(line); 74 //throw new IllegalStateException(line);
70 } 75 }
71 } else if (line.length() > 0) { 76 } else if (line.length() > 0) {
79 s = x; // with exclusion of char at [x], s now points to what used to be at [x+1] 84 s = x; // with exclusion of char at [x], s now points to what used to be at [x+1]
80 } else { 85 } else {
81 line = line.substring(0, x).trim(); 86 line = line.substring(0, x).trim();
82 } 87 }
83 } 88 }
89 // due to the nature of Mercurial implementation, lines prefixed with syntax kind
90 // are processed correctly (despite the fact hgignore(5) suggest "syntax:<kind>" as the
91 // only way to specify it). lineSyntax below leaves a chance for the line to switch
92 // syntax in use without affecting default kind.
93 String lineSyntax;
94 if (line.startsWith(GLOB_PREFIX)) {
95 line = line.substring(GLOB_PREFIX.length()).trim();
96 lineSyntax = GLOB;
97 } else if (line.startsWith(REGEXP_PREFIX)) {
98 line = line.substring(REGEXP_PREFIX.length()).trim();
99 lineSyntax = REGEXP;
100 } else {
101 lineSyntax = syntax;
102 }
84 if (line.length() == 0) { 103 if (line.length() == 0) {
85 continue; 104 continue;
86 } 105 }
87 if ("glob".equals(syntax)) { 106 if (GLOB.equals(lineSyntax)) {
88 // hgignore(5) 107 // hgignore(5) says slashes '\' are escape characters,
89 // (http://www.selenic.com/mercurial/hgignore.5.html) says slashes '\' are escape characters, 108 // however, for glob patterns on Windows first get backslashes converted to slashes
90 // hence no special treatment of Windows path 109 if (globPathHelper != null) {
91 // however, own attempts make me think '\' on Windows are not treated as escapes 110 line = globPathHelper.rewrite(line).toString();
111 }
92 line = glob2regex(line); 112 line = glob2regex(line);
93 } else { 113 } else {
94 assert "regexp".equals(syntax); 114 assert REGEXP.equals(lineSyntax);
95 // regular expression patterns need not match start of the line unless demanded explicitly 115 // regular expression patterns need not match start of the line unless demanded explicitly
96 line = line.charAt(0) == '^' ? line : ".*" + line; 116 line = line.charAt(0) == '^' ? line : ".*" + line;
97 } 117 }
98 try { 118 try {
99 result.add(Pattern.compile(line)); // case-sensitive 119 result.add(Pattern.compile(line)); // case-sensitive
158 sb.append('|'); 178 sb.append('|');
159 continue; 179 continue;
160 } 180 }
161 sb.append(ch); 181 sb.append(ch);
162 } 182 }
163 sb.append("(?:/|$)"); 183 // Python impl doesn't keep empty segments in directory names (ntpath.normpath and posixpath.normpath),
184 // effectively removing trailing separators, thus patterns like "bin/" get translated into "bin$"
185 // Our glob rewriter doesn't strip last empty segment, and "bin/$" would be incorrect pattern,
186 // (e.g. isIgnored("bin/file") performs two matches, against "bin/file" and "bin") hence the check.
187 if (sb.charAt(sb.length() - 1) != '/') {
188 sb.append('$');
189 }
164 return sb.toString(); 190 return sb.toString();
165 } 191 }
166 192
167 /** 193 /**
168 * @param path file or directory name in question 194 * @param path file or directory name in question