##// END OF EJS Templates
check-code: check that '>' is used for continued lines...
Mads Kiilerich -
r19873:b3de50b0 default
parent child Browse files
Show More
@@ -1,528 +1,528
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 #
2 #
3 # check-code - a style and portability checker for Mercurial
3 # check-code - a style and portability checker for Mercurial
4 #
4 #
5 # Copyright 2010 Matt Mackall <mpm@selenic.com>
5 # Copyright 2010 Matt Mackall <mpm@selenic.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 import re, glob, os, sys
10 import re, glob, os, sys
11 import keyword
11 import keyword
12 import optparse
12 import optparse
13 try:
13 try:
14 import re2
14 import re2
15 except ImportError:
15 except ImportError:
16 re2 = None
16 re2 = None
17
17
18 def compilere(pat, multiline=False):
18 def compilere(pat, multiline=False):
19 if multiline:
19 if multiline:
20 pat = '(?m)' + pat
20 pat = '(?m)' + pat
21 if re2:
21 if re2:
22 try:
22 try:
23 return re2.compile(pat)
23 return re2.compile(pat)
24 except re2.error:
24 except re2.error:
25 pass
25 pass
26 return re.compile(pat)
26 return re.compile(pat)
27
27
28 def repquote(m):
28 def repquote(m):
29 t = re.sub(r"\w", "x", m.group('text'))
29 t = re.sub(r"\w", "x", m.group('text'))
30 t = re.sub(r"[^\s\nx]", "o", t)
30 t = re.sub(r"[^\s\nx]", "o", t)
31 return m.group('quote') + t + m.group('quote')
31 return m.group('quote') + t + m.group('quote')
32
32
33 def reppython(m):
33 def reppython(m):
34 comment = m.group('comment')
34 comment = m.group('comment')
35 if comment:
35 if comment:
36 l = len(comment.rstrip())
36 l = len(comment.rstrip())
37 return "#" * l + comment[l:]
37 return "#" * l + comment[l:]
38 return repquote(m)
38 return repquote(m)
39
39
40 def repcomment(m):
40 def repcomment(m):
41 return m.group(1) + "#" * len(m.group(2))
41 return m.group(1) + "#" * len(m.group(2))
42
42
43 def repccomment(m):
43 def repccomment(m):
44 t = re.sub(r"((?<=\n) )|\S", "x", m.group(2))
44 t = re.sub(r"((?<=\n) )|\S", "x", m.group(2))
45 return m.group(1) + t + "*/"
45 return m.group(1) + t + "*/"
46
46
47 def repcallspaces(m):
47 def repcallspaces(m):
48 t = re.sub(r"\n\s+", "\n", m.group(2))
48 t = re.sub(r"\n\s+", "\n", m.group(2))
49 return m.group(1) + t
49 return m.group(1) + t
50
50
51 def repinclude(m):
51 def repinclude(m):
52 return m.group(1) + "<foo>"
52 return m.group(1) + "<foo>"
53
53
54 def rephere(m):
54 def rephere(m):
55 t = re.sub(r"\S", "x", m.group(2))
55 t = re.sub(r"\S", "x", m.group(2))
56 return m.group(1) + t
56 return m.group(1) + t
57
57
58
58
59 testpats = [
59 testpats = [
60 [
60 [
61 (r'pushd|popd', "don't use 'pushd' or 'popd', use 'cd'"),
61 (r'pushd|popd', "don't use 'pushd' or 'popd', use 'cd'"),
62 (r'\W\$?\(\([^\)\n]*\)\)', "don't use (()) or $(()), use 'expr'"),
62 (r'\W\$?\(\([^\)\n]*\)\)', "don't use (()) or $(()), use 'expr'"),
63 (r'grep.*-q', "don't use 'grep -q', redirect to /dev/null"),
63 (r'grep.*-q', "don't use 'grep -q', redirect to /dev/null"),
64 (r'(?<!hg )grep.*-a', "don't use 'grep -a', use in-line python"),
64 (r'(?<!hg )grep.*-a', "don't use 'grep -a', use in-line python"),
65 (r'sed.*-i', "don't use 'sed -i', use a temporary file"),
65 (r'sed.*-i', "don't use 'sed -i', use a temporary file"),
66 (r'\becho\b.*\\n', "don't use 'echo \\n', use printf"),
66 (r'\becho\b.*\\n', "don't use 'echo \\n', use printf"),
67 (r'echo -n', "don't use 'echo -n', use printf"),
67 (r'echo -n', "don't use 'echo -n', use printf"),
68 (r'(^| )wc[^|]*$\n(?!.*\(re\))', "filter wc output"),
68 (r'(^| )wc[^|]*$\n(?!.*\(re\))', "filter wc output"),
69 (r'head -c', "don't use 'head -c', use 'dd'"),
69 (r'head -c', "don't use 'head -c', use 'dd'"),
70 (r'tail -n', "don't use the '-n' option to tail, just use '-<num>'"),
70 (r'tail -n', "don't use the '-n' option to tail, just use '-<num>'"),
71 (r'sha1sum', "don't use sha1sum, use $TESTDIR/md5sum.py"),
71 (r'sha1sum', "don't use sha1sum, use $TESTDIR/md5sum.py"),
72 (r'ls.*-\w*R', "don't use 'ls -R', use 'find'"),
72 (r'ls.*-\w*R', "don't use 'ls -R', use 'find'"),
73 (r'printf.*[^\\]\\([1-9]|0\d)', "don't use 'printf \NNN', use Python"),
73 (r'printf.*[^\\]\\([1-9]|0\d)', "don't use 'printf \NNN', use Python"),
74 (r'printf.*[^\\]\\x', "don't use printf \\x, use Python"),
74 (r'printf.*[^\\]\\x', "don't use printf \\x, use Python"),
75 (r'\$\(.*\)', "don't use $(expr), use `expr`"),
75 (r'\$\(.*\)', "don't use $(expr), use `expr`"),
76 (r'rm -rf \*', "don't use naked rm -rf, target a directory"),
76 (r'rm -rf \*', "don't use naked rm -rf, target a directory"),
77 (r'(^|\|\s*)grep (-\w\s+)*[^|]*[(|]\w',
77 (r'(^|\|\s*)grep (-\w\s+)*[^|]*[(|]\w',
78 "use egrep for extended grep syntax"),
78 "use egrep for extended grep syntax"),
79 (r'/bin/', "don't use explicit paths for tools"),
79 (r'/bin/', "don't use explicit paths for tools"),
80 (r'[^\n]\Z', "no trailing newline"),
80 (r'[^\n]\Z', "no trailing newline"),
81 (r'export.*=', "don't export and assign at once"),
81 (r'export.*=', "don't export and assign at once"),
82 (r'^source\b', "don't use 'source', use '.'"),
82 (r'^source\b', "don't use 'source', use '.'"),
83 (r'touch -d', "don't use 'touch -d', use 'touch -t' instead"),
83 (r'touch -d', "don't use 'touch -d', use 'touch -t' instead"),
84 (r'ls +[^|\n-]+ +-', "options to 'ls' must come before filenames"),
84 (r'ls +[^|\n-]+ +-', "options to 'ls' must come before filenames"),
85 (r'[^>\n]>\s*\$HGRCPATH', "don't overwrite $HGRCPATH, append to it"),
85 (r'[^>\n]>\s*\$HGRCPATH', "don't overwrite $HGRCPATH, append to it"),
86 (r'^stop\(\)', "don't use 'stop' as a shell function name"),
86 (r'^stop\(\)', "don't use 'stop' as a shell function name"),
87 (r'(\[|\btest\b).*-e ', "don't use 'test -e', use 'test -f'"),
87 (r'(\[|\btest\b).*-e ', "don't use 'test -e', use 'test -f'"),
88 (r'^alias\b.*=', "don't use alias, use a function"),
88 (r'^alias\b.*=', "don't use alias, use a function"),
89 (r'if\s*!', "don't use '!' to negate exit status"),
89 (r'if\s*!', "don't use '!' to negate exit status"),
90 (r'/dev/u?random', "don't use entropy, use /dev/zero"),
90 (r'/dev/u?random', "don't use entropy, use /dev/zero"),
91 (r'do\s*true;\s*done', "don't use true as loop body, use sleep 0"),
91 (r'do\s*true;\s*done', "don't use true as loop body, use sleep 0"),
92 (r'^( *)\t', "don't use tabs to indent"),
92 (r'^( *)\t', "don't use tabs to indent"),
93 (r'sed (-e )?\'(\d+|/[^/]*/)i(?!\\\n)',
93 (r'sed (-e )?\'(\d+|/[^/]*/)i(?!\\\n)',
94 "put a backslash-escaped newline after sed 'i' command"),
94 "put a backslash-escaped newline after sed 'i' command"),
95 ],
95 ],
96 # warnings
96 # warnings
97 [
97 [
98 (r'^function', "don't use 'function', use old style"),
98 (r'^function', "don't use 'function', use old style"),
99 (r'^diff.*-\w*N', "don't use 'diff -N'"),
99 (r'^diff.*-\w*N', "don't use 'diff -N'"),
100 (r'\$PWD|\${PWD}', "don't use $PWD, use `pwd`"),
100 (r'\$PWD|\${PWD}', "don't use $PWD, use `pwd`"),
101 (r'^([^"\'\n]|("[^"\n]*")|(\'[^\'\n]*\'))*\^', "^ must be quoted"),
101 (r'^([^"\'\n]|("[^"\n]*")|(\'[^\'\n]*\'))*\^', "^ must be quoted"),
102 (r'kill (`|\$\()', "don't use kill, use killdaemons.py")
102 (r'kill (`|\$\()', "don't use kill, use killdaemons.py")
103 ]
103 ]
104 ]
104 ]
105
105
106 testfilters = [
106 testfilters = [
107 (r"( *)(#([^\n]*\S)?)", repcomment),
107 (r"( *)(#([^\n]*\S)?)", repcomment),
108 (r"<<(\S+)((.|\n)*?\n\1)", rephere),
108 (r"<<(\S+)((.|\n)*?\n\1)", rephere),
109 ]
109 ]
110
110
111 winglobmsg = "use (glob) to match Windows paths too"
111 winglobmsg = "use (glob) to match Windows paths too"
112 uprefix = r"^ \$ "
112 uprefix = r"^ \$ "
113 utestpats = [
113 utestpats = [
114 [
114 [
115 (r'^(\S.*|| [$>] .*)[ \t]\n', "trailing whitespace on non-output"),
115 (r'^(\S.*|| [$>] .*)[ \t]\n', "trailing whitespace on non-output"),
116 (uprefix + r'.*\|\s*sed[^|>\n]*\n',
116 (uprefix + r'.*\|\s*sed[^|>\n]*\n',
117 "use regex test output patterns instead of sed"),
117 "use regex test output patterns instead of sed"),
118 (uprefix + r'(true|exit 0)', "explicit zero exit unnecessary"),
118 (uprefix + r'(true|exit 0)', "explicit zero exit unnecessary"),
119 (uprefix + r'.*(?<!\[)\$\?', "explicit exit code checks unnecessary"),
119 (uprefix + r'.*(?<!\[)\$\?', "explicit exit code checks unnecessary"),
120 (uprefix + r'.*\|\| echo.*(fail|error)',
120 (uprefix + r'.*\|\| echo.*(fail|error)',
121 "explicit exit code checks unnecessary"),
121 "explicit exit code checks unnecessary"),
122 (uprefix + r'set -e', "don't use set -e"),
122 (uprefix + r'set -e', "don't use set -e"),
123 (uprefix + r'\s', "don't indent commands, use > for continued lines"),
123 (uprefix + r'(\s|fi\b|done\b)', "use > for continued lines"),
124 (r'^ saved backup bundle to \$TESTTMP.*\.hg$', winglobmsg),
124 (r'^ saved backup bundle to \$TESTTMP.*\.hg$', winglobmsg),
125 (r'^ changeset .* references (corrupted|missing) \$TESTTMP/.*[^)]$',
125 (r'^ changeset .* references (corrupted|missing) \$TESTTMP/.*[^)]$',
126 winglobmsg),
126 winglobmsg),
127 (r'^ pulling from \$TESTTMP/.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'),
127 (r'^ pulling from \$TESTTMP/.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'),
128 (r'^ reverting .*/.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'),
128 (r'^ reverting .*/.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'),
129 (r'^ cloning subrepo \S+/.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'),
129 (r'^ cloning subrepo \S+/.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'),
130 (r'^ pushing to \$TESTTMP/.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'),
130 (r'^ pushing to \$TESTTMP/.*[^)]$', winglobmsg, '\$TESTTMP/unix-repo$'),
131 (r'^ pushing subrepo \S+/\S+ to.*[^)]$', winglobmsg,
131 (r'^ pushing subrepo \S+/\S+ to.*[^)]$', winglobmsg,
132 '\$TESTTMP/unix-repo$'),
132 '\$TESTTMP/unix-repo$'),
133 (r'^ moving \S+/.*[^)]$', winglobmsg),
133 (r'^ moving \S+/.*[^)]$', winglobmsg),
134 (r'^ no changes made to subrepo since.*/.*[^)]$',
134 (r'^ no changes made to subrepo since.*/.*[^)]$',
135 winglobmsg, '\$TESTTMP/unix-repo$'),
135 winglobmsg, '\$TESTTMP/unix-repo$'),
136 (r'^ .*: largefile \S+ not available from file:.*/.*[^)]$',
136 (r'^ .*: largefile \S+ not available from file:.*/.*[^)]$',
137 winglobmsg, '\$TESTTMP/unix-repo$'),
137 winglobmsg, '\$TESTTMP/unix-repo$'),
138 ],
138 ],
139 # warnings
139 # warnings
140 [
140 [
141 (r'^ [^*?/\n]* \(glob\)$',
141 (r'^ [^*?/\n]* \(glob\)$',
142 "glob match with no glob character (?*/)"),
142 "glob match with no glob character (?*/)"),
143 ]
143 ]
144 ]
144 ]
145
145
146 for i in [0, 1]:
146 for i in [0, 1]:
147 for p, m in testpats[i]:
147 for p, m in testpats[i]:
148 if p.startswith(r'^'):
148 if p.startswith(r'^'):
149 p = r"^ [$>] (%s)" % p[1:]
149 p = r"^ [$>] (%s)" % p[1:]
150 else:
150 else:
151 p = r"^ [$>] .*(%s)" % p
151 p = r"^ [$>] .*(%s)" % p
152 utestpats[i].append((p, m))
152 utestpats[i].append((p, m))
153
153
154 utestfilters = [
154 utestfilters = [
155 (r"<<(\S+)((.|\n)*?\n > \1)", rephere),
155 (r"<<(\S+)((.|\n)*?\n > \1)", rephere),
156 (r"( *)(#([^\n]*\S)?)", repcomment),
156 (r"( *)(#([^\n]*\S)?)", repcomment),
157 ]
157 ]
158
158
159 pypats = [
159 pypats = [
160 [
160 [
161 (r'^\s*def\s*\w+\s*\(.*,\s*\(',
161 (r'^\s*def\s*\w+\s*\(.*,\s*\(',
162 "tuple parameter unpacking not available in Python 3+"),
162 "tuple parameter unpacking not available in Python 3+"),
163 (r'lambda\s*\(.*,.*\)',
163 (r'lambda\s*\(.*,.*\)',
164 "tuple parameter unpacking not available in Python 3+"),
164 "tuple parameter unpacking not available in Python 3+"),
165 (r'import (.+,[^.]+\.[^.]+|[^.]+\.[^.]+,)',
165 (r'import (.+,[^.]+\.[^.]+|[^.]+\.[^.]+,)',
166 '2to3 can\'t always rewrite "import qux, foo.bar", '
166 '2to3 can\'t always rewrite "import qux, foo.bar", '
167 'use "import foo.bar" on its own line instead.'),
167 'use "import foo.bar" on its own line instead.'),
168 (r'(?<!def)\s+(cmp)\(', "cmp is not available in Python 3+"),
168 (r'(?<!def)\s+(cmp)\(', "cmp is not available in Python 3+"),
169 (r'\breduce\s*\(.*', "reduce is not available in Python 3+"),
169 (r'\breduce\s*\(.*', "reduce is not available in Python 3+"),
170 (r'\.has_key\b', "dict.has_key is not available in Python 3+"),
170 (r'\.has_key\b', "dict.has_key is not available in Python 3+"),
171 (r'\s<>\s', '<> operator is not available in Python 3+, use !='),
171 (r'\s<>\s', '<> operator is not available in Python 3+, use !='),
172 (r'^\s*\t', "don't use tabs"),
172 (r'^\s*\t', "don't use tabs"),
173 (r'\S;\s*\n', "semicolon"),
173 (r'\S;\s*\n', "semicolon"),
174 (r'[^_]_\("[^"]+"\s*%', "don't use % inside _()"),
174 (r'[^_]_\("[^"]+"\s*%', "don't use % inside _()"),
175 (r"[^_]_\('[^']+'\s*%", "don't use % inside _()"),
175 (r"[^_]_\('[^']+'\s*%", "don't use % inside _()"),
176 (r'(\w|\)),\w', "missing whitespace after ,"),
176 (r'(\w|\)),\w', "missing whitespace after ,"),
177 (r'(\w|\))[+/*\-<>]\w', "missing whitespace in expression"),
177 (r'(\w|\))[+/*\-<>]\w', "missing whitespace in expression"),
178 (r'^\s+(\w|\.)+=\w[^,()\n]*$', "missing whitespace in assignment"),
178 (r'^\s+(\w|\.)+=\w[^,()\n]*$', "missing whitespace in assignment"),
179 (r'(\s+)try:\n((?:\n|\1\s.*\n)+?)\1except.*?:\n'
179 (r'(\s+)try:\n((?:\n|\1\s.*\n)+?)\1except.*?:\n'
180 r'((?:\n|\1\s.*\n)+?)\1finally:', 'no try/except/finally in Python 2.4'),
180 r'((?:\n|\1\s.*\n)+?)\1finally:', 'no try/except/finally in Python 2.4'),
181 (r'(?<!def)(\s+|^|\()next\(.+\)',
181 (r'(?<!def)(\s+|^|\()next\(.+\)',
182 'no next(foo) in Python 2.4 and 2.5, use foo.next() instead'),
182 'no next(foo) in Python 2.4 and 2.5, use foo.next() instead'),
183 (r'(\s+)try:\n((?:\n|\1\s.*\n)*?)\1\s*yield\b.*?'
183 (r'(\s+)try:\n((?:\n|\1\s.*\n)*?)\1\s*yield\b.*?'
184 r'((?:\n|\1\s.*\n)+?)\1finally:',
184 r'((?:\n|\1\s.*\n)+?)\1finally:',
185 'no yield inside try/finally in Python 2.4'),
185 'no yield inside try/finally in Python 2.4'),
186 (r'.{81}', "line too long"),
186 (r'.{81}', "line too long"),
187 (r' x+[xo][\'"]\n\s+[\'"]x', 'string join across lines with no space'),
187 (r' x+[xo][\'"]\n\s+[\'"]x', 'string join across lines with no space'),
188 (r'[^\n]\Z', "no trailing newline"),
188 (r'[^\n]\Z', "no trailing newline"),
189 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"),
189 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"),
190 # (r'^\s+[^_ \n][^_. \n]+_[^_\n]+\s*=',
190 # (r'^\s+[^_ \n][^_. \n]+_[^_\n]+\s*=',
191 # "don't use underbars in identifiers"),
191 # "don't use underbars in identifiers"),
192 (r'^\s+(self\.)?[A-za-z][a-z0-9]+[A-Z]\w* = ',
192 (r'^\s+(self\.)?[A-za-z][a-z0-9]+[A-Z]\w* = ',
193 "don't use camelcase in identifiers"),
193 "don't use camelcase in identifiers"),
194 (r'^\s*(if|while|def|class|except|try)\s[^[\n]*:\s*[^\\n]#\s]+',
194 (r'^\s*(if|while|def|class|except|try)\s[^[\n]*:\s*[^\\n]#\s]+',
195 "linebreak after :"),
195 "linebreak after :"),
196 (r'class\s[^( \n]+:', "old-style class, use class foo(object)"),
196 (r'class\s[^( \n]+:', "old-style class, use class foo(object)"),
197 (r'class\s[^( \n]+\(\):',
197 (r'class\s[^( \n]+\(\):',
198 "class foo() not available in Python 2.4, use class foo(object)"),
198 "class foo() not available in Python 2.4, use class foo(object)"),
199 (r'\b(%s)\(' % '|'.join(keyword.kwlist),
199 (r'\b(%s)\(' % '|'.join(keyword.kwlist),
200 "Python keyword is not a function"),
200 "Python keyword is not a function"),
201 (r',]', "unneeded trailing ',' in list"),
201 (r',]', "unneeded trailing ',' in list"),
202 # (r'class\s[A-Z][^\(]*\((?!Exception)',
202 # (r'class\s[A-Z][^\(]*\((?!Exception)',
203 # "don't capitalize non-exception classes"),
203 # "don't capitalize non-exception classes"),
204 # (r'in range\(', "use xrange"),
204 # (r'in range\(', "use xrange"),
205 # (r'^\s*print\s+', "avoid using print in core and extensions"),
205 # (r'^\s*print\s+', "avoid using print in core and extensions"),
206 (r'[\x80-\xff]', "non-ASCII character literal"),
206 (r'[\x80-\xff]', "non-ASCII character literal"),
207 (r'("\')\.format\(', "str.format() not available in Python 2.4"),
207 (r'("\')\.format\(', "str.format() not available in Python 2.4"),
208 (r'^\s*with\s+', "with not available in Python 2.4"),
208 (r'^\s*with\s+', "with not available in Python 2.4"),
209 (r'\.isdisjoint\(', "set.isdisjoint not available in Python 2.4"),
209 (r'\.isdisjoint\(', "set.isdisjoint not available in Python 2.4"),
210 (r'^\s*except.* as .*:', "except as not available in Python 2.4"),
210 (r'^\s*except.* as .*:', "except as not available in Python 2.4"),
211 (r'^\s*os\.path\.relpath', "relpath not available in Python 2.4"),
211 (r'^\s*os\.path\.relpath', "relpath not available in Python 2.4"),
212 (r'(?<!def)\s+(any|all|format)\(',
212 (r'(?<!def)\s+(any|all|format)\(',
213 "any/all/format not available in Python 2.4"),
213 "any/all/format not available in Python 2.4"),
214 (r'(?<!def)\s+(callable)\(',
214 (r'(?<!def)\s+(callable)\(',
215 "callable not available in Python 3, use getattr(f, '__call__', None)"),
215 "callable not available in Python 3, use getattr(f, '__call__', None)"),
216 (r'if\s.*\selse', "if ... else form not available in Python 2.4"),
216 (r'if\s.*\selse', "if ... else form not available in Python 2.4"),
217 (r'^\s*(%s)\s\s' % '|'.join(keyword.kwlist),
217 (r'^\s*(%s)\s\s' % '|'.join(keyword.kwlist),
218 "gratuitous whitespace after Python keyword"),
218 "gratuitous whitespace after Python keyword"),
219 (r'([\(\[][ \t]\S)|(\S[ \t][\)\]])', "gratuitous whitespace in () or []"),
219 (r'([\(\[][ \t]\S)|(\S[ \t][\)\]])', "gratuitous whitespace in () or []"),
220 # (r'\s\s=', "gratuitous whitespace before ="),
220 # (r'\s\s=', "gratuitous whitespace before ="),
221 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\S',
221 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\S',
222 "missing whitespace around operator"),
222 "missing whitespace around operator"),
223 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\s',
223 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\s',
224 "missing whitespace around operator"),
224 "missing whitespace around operator"),
225 (r'\s(\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\S',
225 (r'\s(\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\S',
226 "missing whitespace around operator"),
226 "missing whitespace around operator"),
227 (r'[^^+=*/!<>&| %-](\s=|=\s)[^= ]',
227 (r'[^^+=*/!<>&| %-](\s=|=\s)[^= ]',
228 "wrong whitespace around ="),
228 "wrong whitespace around ="),
229 (r'\([^()]*( =[^=]|[^<>!=]= )',
229 (r'\([^()]*( =[^=]|[^<>!=]= )',
230 "no whitespace around = for named parameters"),
230 "no whitespace around = for named parameters"),
231 (r'raise Exception', "don't raise generic exceptions"),
231 (r'raise Exception', "don't raise generic exceptions"),
232 (r'raise [^,(]+, (\([^\)]+\)|[^,\(\)]+)$',
232 (r'raise [^,(]+, (\([^\)]+\)|[^,\(\)]+)$',
233 "don't use old-style two-argument raise, use Exception(message)"),
233 "don't use old-style two-argument raise, use Exception(message)"),
234 (r' is\s+(not\s+)?["\'0-9-]', "object comparison with literal"),
234 (r' is\s+(not\s+)?["\'0-9-]', "object comparison with literal"),
235 (r' [=!]=\s+(True|False|None)',
235 (r' [=!]=\s+(True|False|None)',
236 "comparison with singleton, use 'is' or 'is not' instead"),
236 "comparison with singleton, use 'is' or 'is not' instead"),
237 (r'^\s*(while|if) [01]:',
237 (r'^\s*(while|if) [01]:',
238 "use True/False for constant Boolean expression"),
238 "use True/False for constant Boolean expression"),
239 (r'(?:(?<!def)\s+|\()hasattr',
239 (r'(?:(?<!def)\s+|\()hasattr',
240 'hasattr(foo, bar) is broken, use util.safehasattr(foo, bar) instead'),
240 'hasattr(foo, bar) is broken, use util.safehasattr(foo, bar) instead'),
241 (r'opener\([^)]*\).read\(',
241 (r'opener\([^)]*\).read\(',
242 "use opener.read() instead"),
242 "use opener.read() instead"),
243 (r'BaseException', 'not in Python 2.4, use Exception'),
243 (r'BaseException', 'not in Python 2.4, use Exception'),
244 (r'os\.path\.relpath', 'os.path.relpath is not in Python 2.5'),
244 (r'os\.path\.relpath', 'os.path.relpath is not in Python 2.5'),
245 (r'opener\([^)]*\).write\(',
245 (r'opener\([^)]*\).write\(',
246 "use opener.write() instead"),
246 "use opener.write() instead"),
247 (r'[\s\(](open|file)\([^)]*\)\.read\(',
247 (r'[\s\(](open|file)\([^)]*\)\.read\(',
248 "use util.readfile() instead"),
248 "use util.readfile() instead"),
249 (r'[\s\(](open|file)\([^)]*\)\.write\(',
249 (r'[\s\(](open|file)\([^)]*\)\.write\(',
250 "use util.readfile() instead"),
250 "use util.readfile() instead"),
251 (r'^[\s\(]*(open(er)?|file)\([^)]*\)',
251 (r'^[\s\(]*(open(er)?|file)\([^)]*\)',
252 "always assign an opened file to a variable, and close it afterwards"),
252 "always assign an opened file to a variable, and close it afterwards"),
253 (r'[\s\(](open|file)\([^)]*\)\.',
253 (r'[\s\(](open|file)\([^)]*\)\.',
254 "always assign an opened file to a variable, and close it afterwards"),
254 "always assign an opened file to a variable, and close it afterwards"),
255 (r'(?i)descendent', "the proper spelling is descendAnt"),
255 (r'(?i)descendent', "the proper spelling is descendAnt"),
256 (r'\.debug\(\_', "don't mark debug messages for translation"),
256 (r'\.debug\(\_', "don't mark debug messages for translation"),
257 (r'\.strip\(\)\.split\(\)', "no need to strip before splitting"),
257 (r'\.strip\(\)\.split\(\)', "no need to strip before splitting"),
258 (r'^\s*except\s*:', "naked except clause", r'#.*re-raises'),
258 (r'^\s*except\s*:', "naked except clause", r'#.*re-raises'),
259 (r':\n( )*( ){1,3}[^ ]', "must indent 4 spaces"),
259 (r':\n( )*( ){1,3}[^ ]', "must indent 4 spaces"),
260 (r'ui\.(status|progress|write|note|warn)\([\'\"]x',
260 (r'ui\.(status|progress|write|note|warn)\([\'\"]x',
261 "missing _() in ui message (use () to hide false-positives)"),
261 "missing _() in ui message (use () to hide false-positives)"),
262 (r'release\(.*wlock, .*lock\)', "wrong lock release order"),
262 (r'release\(.*wlock, .*lock\)', "wrong lock release order"),
263 ],
263 ],
264 # warnings
264 # warnings
265 [
265 [
266 ]
266 ]
267 ]
267 ]
268
268
269 pyfilters = [
269 pyfilters = [
270 (r"""(?msx)(?P<comment>\#.*?$)|
270 (r"""(?msx)(?P<comment>\#.*?$)|
271 ((?P<quote>('''|\"\"\"|(?<!')'(?!')|(?<!")"(?!")))
271 ((?P<quote>('''|\"\"\"|(?<!')'(?!')|(?<!")"(?!")))
272 (?P<text>(([^\\]|\\.)*?))
272 (?P<text>(([^\\]|\\.)*?))
273 (?P=quote))""", reppython),
273 (?P=quote))""", reppython),
274 ]
274 ]
275
275
276 txtfilters = []
276 txtfilters = []
277
277
278 txtpats = [
278 txtpats = [
279 [
279 [
280 ('\s$', 'trailing whitespace'),
280 ('\s$', 'trailing whitespace'),
281 ],
281 ],
282 []
282 []
283 ]
283 ]
284
284
285 cpats = [
285 cpats = [
286 [
286 [
287 (r'//', "don't use //-style comments"),
287 (r'//', "don't use //-style comments"),
288 (r'^ ', "don't use spaces to indent"),
288 (r'^ ', "don't use spaces to indent"),
289 (r'\S\t', "don't use tabs except for indent"),
289 (r'\S\t', "don't use tabs except for indent"),
290 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"),
290 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"),
291 (r'.{81}', "line too long"),
291 (r'.{81}', "line too long"),
292 (r'(while|if|do|for)\(', "use space after while/if/do/for"),
292 (r'(while|if|do|for)\(', "use space after while/if/do/for"),
293 (r'return\(', "return is not a function"),
293 (r'return\(', "return is not a function"),
294 (r' ;', "no space before ;"),
294 (r' ;', "no space before ;"),
295 (r'[)][{]', "space between ) and {"),
295 (r'[)][{]', "space between ) and {"),
296 (r'\w+\* \w+', "use int *foo, not int* foo"),
296 (r'\w+\* \w+', "use int *foo, not int* foo"),
297 (r'\W\([^\)]+\) \w+', "use (int)foo, not (int) foo"),
297 (r'\W\([^\)]+\) \w+', "use (int)foo, not (int) foo"),
298 (r'\w+ (\+\+|--)', "use foo++, not foo ++"),
298 (r'\w+ (\+\+|--)', "use foo++, not foo ++"),
299 (r'\w,\w', "missing whitespace after ,"),
299 (r'\w,\w', "missing whitespace after ,"),
300 (r'^[^#]\w[+/*]\w', "missing whitespace in expression"),
300 (r'^[^#]\w[+/*]\w', "missing whitespace in expression"),
301 (r'^#\s+\w', "use #foo, not # foo"),
301 (r'^#\s+\w', "use #foo, not # foo"),
302 (r'[^\n]\Z', "no trailing newline"),
302 (r'[^\n]\Z', "no trailing newline"),
303 (r'^\s*#import\b', "use only #include in standard C code"),
303 (r'^\s*#import\b', "use only #include in standard C code"),
304 ],
304 ],
305 # warnings
305 # warnings
306 []
306 []
307 ]
307 ]
308
308
309 cfilters = [
309 cfilters = [
310 (r'(/\*)(((\*(?!/))|[^*])*)\*/', repccomment),
310 (r'(/\*)(((\*(?!/))|[^*])*)\*/', repccomment),
311 (r'''(?P<quote>(?<!")")(?P<text>([^"]|\\")+)"(?!")''', repquote),
311 (r'''(?P<quote>(?<!")")(?P<text>([^"]|\\")+)"(?!")''', repquote),
312 (r'''(#\s*include\s+<)([^>]+)>''', repinclude),
312 (r'''(#\s*include\s+<)([^>]+)>''', repinclude),
313 (r'(\()([^)]+\))', repcallspaces),
313 (r'(\()([^)]+\))', repcallspaces),
314 ]
314 ]
315
315
316 inutilpats = [
316 inutilpats = [
317 [
317 [
318 (r'\bui\.', "don't use ui in util"),
318 (r'\bui\.', "don't use ui in util"),
319 ],
319 ],
320 # warnings
320 # warnings
321 []
321 []
322 ]
322 ]
323
323
324 inrevlogpats = [
324 inrevlogpats = [
325 [
325 [
326 (r'\brepo\.', "don't use repo in revlog"),
326 (r'\brepo\.', "don't use repo in revlog"),
327 ],
327 ],
328 # warnings
328 # warnings
329 []
329 []
330 ]
330 ]
331
331
332 checks = [
332 checks = [
333 ('python', r'.*\.(py|cgi)$', pyfilters, pypats),
333 ('python', r'.*\.(py|cgi)$', pyfilters, pypats),
334 ('test script', r'(.*/)?test-[^.~]*$', testfilters, testpats),
334 ('test script', r'(.*/)?test-[^.~]*$', testfilters, testpats),
335 ('c', r'.*\.[ch]$', cfilters, cpats),
335 ('c', r'.*\.[ch]$', cfilters, cpats),
336 ('unified test', r'.*\.t$', utestfilters, utestpats),
336 ('unified test', r'.*\.t$', utestfilters, utestpats),
337 ('layering violation repo in revlog', r'mercurial/revlog\.py', pyfilters,
337 ('layering violation repo in revlog', r'mercurial/revlog\.py', pyfilters,
338 inrevlogpats),
338 inrevlogpats),
339 ('layering violation ui in util', r'mercurial/util\.py', pyfilters,
339 ('layering violation ui in util', r'mercurial/util\.py', pyfilters,
340 inutilpats),
340 inutilpats),
341 ('txt', r'.*\.txt$', txtfilters, txtpats),
341 ('txt', r'.*\.txt$', txtfilters, txtpats),
342 ]
342 ]
343
343
344 def _preparepats():
344 def _preparepats():
345 for c in checks:
345 for c in checks:
346 failandwarn = c[-1]
346 failandwarn = c[-1]
347 for pats in failandwarn:
347 for pats in failandwarn:
348 for i, pseq in enumerate(pats):
348 for i, pseq in enumerate(pats):
349 # fix-up regexes for multi-line searches
349 # fix-up regexes for multi-line searches
350 p = pseq[0]
350 p = pseq[0]
351 # \s doesn't match \n
351 # \s doesn't match \n
352 p = re.sub(r'(?<!\\)\\s', r'[ \\t]', p)
352 p = re.sub(r'(?<!\\)\\s', r'[ \\t]', p)
353 # [^...] doesn't match newline
353 # [^...] doesn't match newline
354 p = re.sub(r'(?<!\\)\[\^', r'[^\\n', p)
354 p = re.sub(r'(?<!\\)\[\^', r'[^\\n', p)
355
355
356 pats[i] = (re.compile(p, re.MULTILINE),) + pseq[1:]
356 pats[i] = (re.compile(p, re.MULTILINE),) + pseq[1:]
357 filters = c[2]
357 filters = c[2]
358 for i, flt in enumerate(filters):
358 for i, flt in enumerate(filters):
359 filters[i] = re.compile(flt[0]), flt[1]
359 filters[i] = re.compile(flt[0]), flt[1]
360 _preparepats()
360 _preparepats()
361
361
362 class norepeatlogger(object):
362 class norepeatlogger(object):
363 def __init__(self):
363 def __init__(self):
364 self._lastseen = None
364 self._lastseen = None
365
365
366 def log(self, fname, lineno, line, msg, blame):
366 def log(self, fname, lineno, line, msg, blame):
367 """print error related a to given line of a given file.
367 """print error related a to given line of a given file.
368
368
369 The faulty line will also be printed but only once in the case
369 The faulty line will also be printed but only once in the case
370 of multiple errors.
370 of multiple errors.
371
371
372 :fname: filename
372 :fname: filename
373 :lineno: line number
373 :lineno: line number
374 :line: actual content of the line
374 :line: actual content of the line
375 :msg: error message
375 :msg: error message
376 """
376 """
377 msgid = fname, lineno, line
377 msgid = fname, lineno, line
378 if msgid != self._lastseen:
378 if msgid != self._lastseen:
379 if blame:
379 if blame:
380 print "%s:%d (%s):" % (fname, lineno, blame)
380 print "%s:%d (%s):" % (fname, lineno, blame)
381 else:
381 else:
382 print "%s:%d:" % (fname, lineno)
382 print "%s:%d:" % (fname, lineno)
383 print " > %s" % line
383 print " > %s" % line
384 self._lastseen = msgid
384 self._lastseen = msgid
385 print " " + msg
385 print " " + msg
386
386
387 _defaultlogger = norepeatlogger()
387 _defaultlogger = norepeatlogger()
388
388
389 def getblame(f):
389 def getblame(f):
390 lines = []
390 lines = []
391 for l in os.popen('hg annotate -un %s' % f):
391 for l in os.popen('hg annotate -un %s' % f):
392 start, line = l.split(':', 1)
392 start, line = l.split(':', 1)
393 user, rev = start.split()
393 user, rev = start.split()
394 lines.append((line[1:-1], user, rev))
394 lines.append((line[1:-1], user, rev))
395 return lines
395 return lines
396
396
397 def checkfile(f, logfunc=_defaultlogger.log, maxerr=None, warnings=False,
397 def checkfile(f, logfunc=_defaultlogger.log, maxerr=None, warnings=False,
398 blame=False, debug=False, lineno=True):
398 blame=False, debug=False, lineno=True):
399 """checks style and portability of a given file
399 """checks style and portability of a given file
400
400
401 :f: filepath
401 :f: filepath
402 :logfunc: function used to report error
402 :logfunc: function used to report error
403 logfunc(filename, linenumber, linecontent, errormessage)
403 logfunc(filename, linenumber, linecontent, errormessage)
404 :maxerr: number of error to display before aborting.
404 :maxerr: number of error to display before aborting.
405 Set to false (default) to report all errors
405 Set to false (default) to report all errors
406
406
407 return True if no error is found, False otherwise.
407 return True if no error is found, False otherwise.
408 """
408 """
409 blamecache = None
409 blamecache = None
410 result = True
410 result = True
411 for name, match, filters, pats in checks:
411 for name, match, filters, pats in checks:
412 if debug:
412 if debug:
413 print name, f
413 print name, f
414 fc = 0
414 fc = 0
415 if not re.match(match, f):
415 if not re.match(match, f):
416 if debug:
416 if debug:
417 print "Skipping %s for %s it doesn't match %s" % (
417 print "Skipping %s for %s it doesn't match %s" % (
418 name, match, f)
418 name, match, f)
419 continue
419 continue
420 try:
420 try:
421 fp = open(f)
421 fp = open(f)
422 except IOError, e:
422 except IOError, e:
423 print "Skipping %s, %s" % (f, str(e).split(':', 1)[0])
423 print "Skipping %s, %s" % (f, str(e).split(':', 1)[0])
424 continue
424 continue
425 pre = post = fp.read()
425 pre = post = fp.read()
426 fp.close()
426 fp.close()
427 if "no-" "check-code" in pre:
427 if "no-" "check-code" in pre:
428 if debug:
428 if debug:
429 print "Skipping %s for %s it has no-" " check-code" % (
429 print "Skipping %s for %s it has no-" " check-code" % (
430 name, f)
430 name, f)
431 break
431 break
432 for p, r in filters:
432 for p, r in filters:
433 post = re.sub(p, r, post)
433 post = re.sub(p, r, post)
434 nerrs = len(pats[0]) # nerr elements are errors
434 nerrs = len(pats[0]) # nerr elements are errors
435 if warnings:
435 if warnings:
436 pats = pats[0] + pats[1]
436 pats = pats[0] + pats[1]
437 else:
437 else:
438 pats = pats[0]
438 pats = pats[0]
439 # print post # uncomment to show filtered version
439 # print post # uncomment to show filtered version
440
440
441 if debug:
441 if debug:
442 print "Checking %s for %s" % (name, f)
442 print "Checking %s for %s" % (name, f)
443
443
444 prelines = None
444 prelines = None
445 errors = []
445 errors = []
446 for i, pat in enumerate(pats):
446 for i, pat in enumerate(pats):
447 if len(pat) == 3:
447 if len(pat) == 3:
448 p, msg, ignore = pat
448 p, msg, ignore = pat
449 else:
449 else:
450 p, msg = pat
450 p, msg = pat
451 ignore = None
451 ignore = None
452
452
453 pos = 0
453 pos = 0
454 n = 0
454 n = 0
455 for m in p.finditer(post):
455 for m in p.finditer(post):
456 if prelines is None:
456 if prelines is None:
457 prelines = pre.splitlines()
457 prelines = pre.splitlines()
458 postlines = post.splitlines(True)
458 postlines = post.splitlines(True)
459
459
460 start = m.start()
460 start = m.start()
461 while n < len(postlines):
461 while n < len(postlines):
462 step = len(postlines[n])
462 step = len(postlines[n])
463 if pos + step > start:
463 if pos + step > start:
464 break
464 break
465 pos += step
465 pos += step
466 n += 1
466 n += 1
467 l = prelines[n]
467 l = prelines[n]
468
468
469 if "check-code" "-ignore" in l:
469 if "check-code" "-ignore" in l:
470 if debug:
470 if debug:
471 print "Skipping %s for %s:%s (check-code" "-ignore)" % (
471 print "Skipping %s for %s:%s (check-code" "-ignore)" % (
472 name, f, n)
472 name, f, n)
473 continue
473 continue
474 elif ignore and re.search(ignore, l, re.MULTILINE):
474 elif ignore and re.search(ignore, l, re.MULTILINE):
475 continue
475 continue
476 bd = ""
476 bd = ""
477 if blame:
477 if blame:
478 bd = 'working directory'
478 bd = 'working directory'
479 if not blamecache:
479 if not blamecache:
480 blamecache = getblame(f)
480 blamecache = getblame(f)
481 if n < len(blamecache):
481 if n < len(blamecache):
482 bl, bu, br = blamecache[n]
482 bl, bu, br = blamecache[n]
483 if bl == l:
483 if bl == l:
484 bd = '%s@%s' % (bu, br)
484 bd = '%s@%s' % (bu, br)
485 if i >= nerrs:
485 if i >= nerrs:
486 msg = "warning: " + msg
486 msg = "warning: " + msg
487 errors.append((f, lineno and n + 1, l, msg, bd))
487 errors.append((f, lineno and n + 1, l, msg, bd))
488 result = False
488 result = False
489
489
490 errors.sort()
490 errors.sort()
491 for e in errors:
491 for e in errors:
492 logfunc(*e)
492 logfunc(*e)
493 fc += 1
493 fc += 1
494 if maxerr and fc >= maxerr:
494 if maxerr and fc >= maxerr:
495 print " (too many errors, giving up)"
495 print " (too many errors, giving up)"
496 break
496 break
497
497
498 return result
498 return result
499
499
500 if __name__ == "__main__":
500 if __name__ == "__main__":
501 parser = optparse.OptionParser("%prog [options] [files]")
501 parser = optparse.OptionParser("%prog [options] [files]")
502 parser.add_option("-w", "--warnings", action="store_true",
502 parser.add_option("-w", "--warnings", action="store_true",
503 help="include warning-level checks")
503 help="include warning-level checks")
504 parser.add_option("-p", "--per-file", type="int",
504 parser.add_option("-p", "--per-file", type="int",
505 help="max warnings per file")
505 help="max warnings per file")
506 parser.add_option("-b", "--blame", action="store_true",
506 parser.add_option("-b", "--blame", action="store_true",
507 help="use annotate to generate blame info")
507 help="use annotate to generate blame info")
508 parser.add_option("", "--debug", action="store_true",
508 parser.add_option("", "--debug", action="store_true",
509 help="show debug information")
509 help="show debug information")
510 parser.add_option("", "--nolineno", action="store_false",
510 parser.add_option("", "--nolineno", action="store_false",
511 dest='lineno', help="don't show line numbers")
511 dest='lineno', help="don't show line numbers")
512
512
513 parser.set_defaults(per_file=15, warnings=False, blame=False, debug=False,
513 parser.set_defaults(per_file=15, warnings=False, blame=False, debug=False,
514 lineno=True)
514 lineno=True)
515 (options, args) = parser.parse_args()
515 (options, args) = parser.parse_args()
516
516
517 if len(args) == 0:
517 if len(args) == 0:
518 check = glob.glob("*")
518 check = glob.glob("*")
519 else:
519 else:
520 check = args
520 check = args
521
521
522 ret = 0
522 ret = 0
523 for f in check:
523 for f in check:
524 if not checkfile(f, maxerr=options.per_file, warnings=options.warnings,
524 if not checkfile(f, maxerr=options.per_file, warnings=options.warnings,
525 blame=options.blame, debug=options.debug,
525 blame=options.blame, debug=options.debug,
526 lineno=options.lineno):
526 lineno=options.lineno):
527 ret = 1
527 ret = 1
528 sys.exit(ret)
528 sys.exit(ret)
@@ -1,333 +1,333
1 $ hg init t
1 $ hg init t
2 $ cd t
2 $ cd t
3 $ mkdir -p beans
3 $ mkdir -p beans
4 $ for b in kidney navy turtle borlotti black pinto; do
4 $ for b in kidney navy turtle borlotti black pinto; do
5 > echo $b > beans/$b
5 > echo $b > beans/$b
6 $ done
6 > done
7 $ mkdir -p mammals/Procyonidae
7 $ mkdir -p mammals/Procyonidae
8 $ for m in cacomistle coatimundi raccoon; do
8 $ for m in cacomistle coatimundi raccoon; do
9 > echo $m > mammals/Procyonidae/$m
9 > echo $m > mammals/Procyonidae/$m
10 $ done
10 > done
11 $ echo skunk > mammals/skunk
11 $ echo skunk > mammals/skunk
12 $ echo fennel > fennel
12 $ echo fennel > fennel
13 $ echo fenugreek > fenugreek
13 $ echo fenugreek > fenugreek
14 $ echo fiddlehead > fiddlehead
14 $ echo fiddlehead > fiddlehead
15 $ hg addremove
15 $ hg addremove
16 adding beans/black
16 adding beans/black
17 adding beans/borlotti
17 adding beans/borlotti
18 adding beans/kidney
18 adding beans/kidney
19 adding beans/navy
19 adding beans/navy
20 adding beans/pinto
20 adding beans/pinto
21 adding beans/turtle
21 adding beans/turtle
22 adding fennel
22 adding fennel
23 adding fenugreek
23 adding fenugreek
24 adding fiddlehead
24 adding fiddlehead
25 adding mammals/Procyonidae/cacomistle
25 adding mammals/Procyonidae/cacomistle
26 adding mammals/Procyonidae/coatimundi
26 adding mammals/Procyonidae/coatimundi
27 adding mammals/Procyonidae/raccoon
27 adding mammals/Procyonidae/raccoon
28 adding mammals/skunk
28 adding mammals/skunk
29 $ hg commit -m "commit #0"
29 $ hg commit -m "commit #0"
30
30
31 $ hg debugwalk
31 $ hg debugwalk
32 f beans/black beans/black
32 f beans/black beans/black
33 f beans/borlotti beans/borlotti
33 f beans/borlotti beans/borlotti
34 f beans/kidney beans/kidney
34 f beans/kidney beans/kidney
35 f beans/navy beans/navy
35 f beans/navy beans/navy
36 f beans/pinto beans/pinto
36 f beans/pinto beans/pinto
37 f beans/turtle beans/turtle
37 f beans/turtle beans/turtle
38 f fennel fennel
38 f fennel fennel
39 f fenugreek fenugreek
39 f fenugreek fenugreek
40 f fiddlehead fiddlehead
40 f fiddlehead fiddlehead
41 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
41 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
42 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
42 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
43 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
43 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
44 f mammals/skunk mammals/skunk
44 f mammals/skunk mammals/skunk
45 $ hg debugwalk -I.
45 $ hg debugwalk -I.
46 f beans/black beans/black
46 f beans/black beans/black
47 f beans/borlotti beans/borlotti
47 f beans/borlotti beans/borlotti
48 f beans/kidney beans/kidney
48 f beans/kidney beans/kidney
49 f beans/navy beans/navy
49 f beans/navy beans/navy
50 f beans/pinto beans/pinto
50 f beans/pinto beans/pinto
51 f beans/turtle beans/turtle
51 f beans/turtle beans/turtle
52 f fennel fennel
52 f fennel fennel
53 f fenugreek fenugreek
53 f fenugreek fenugreek
54 f fiddlehead fiddlehead
54 f fiddlehead fiddlehead
55 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
55 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
56 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
56 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
57 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
57 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
58 f mammals/skunk mammals/skunk
58 f mammals/skunk mammals/skunk
59
59
60 $ cd mammals
60 $ cd mammals
61 $ hg debugwalk
61 $ hg debugwalk
62 f beans/black ../beans/black
62 f beans/black ../beans/black
63 f beans/borlotti ../beans/borlotti
63 f beans/borlotti ../beans/borlotti
64 f beans/kidney ../beans/kidney
64 f beans/kidney ../beans/kidney
65 f beans/navy ../beans/navy
65 f beans/navy ../beans/navy
66 f beans/pinto ../beans/pinto
66 f beans/pinto ../beans/pinto
67 f beans/turtle ../beans/turtle
67 f beans/turtle ../beans/turtle
68 f fennel ../fennel
68 f fennel ../fennel
69 f fenugreek ../fenugreek
69 f fenugreek ../fenugreek
70 f fiddlehead ../fiddlehead
70 f fiddlehead ../fiddlehead
71 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
71 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
72 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
72 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
73 f mammals/Procyonidae/raccoon Procyonidae/raccoon
73 f mammals/Procyonidae/raccoon Procyonidae/raccoon
74 f mammals/skunk skunk
74 f mammals/skunk skunk
75 $ hg debugwalk -X ../beans
75 $ hg debugwalk -X ../beans
76 f fennel ../fennel
76 f fennel ../fennel
77 f fenugreek ../fenugreek
77 f fenugreek ../fenugreek
78 f fiddlehead ../fiddlehead
78 f fiddlehead ../fiddlehead
79 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
79 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
80 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
80 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
81 f mammals/Procyonidae/raccoon Procyonidae/raccoon
81 f mammals/Procyonidae/raccoon Procyonidae/raccoon
82 f mammals/skunk skunk
82 f mammals/skunk skunk
83 $ hg debugwalk -I '*k'
83 $ hg debugwalk -I '*k'
84 f mammals/skunk skunk
84 f mammals/skunk skunk
85 $ hg debugwalk -I 'glob:*k'
85 $ hg debugwalk -I 'glob:*k'
86 f mammals/skunk skunk
86 f mammals/skunk skunk
87 $ hg debugwalk -I 'relglob:*k'
87 $ hg debugwalk -I 'relglob:*k'
88 f beans/black ../beans/black
88 f beans/black ../beans/black
89 f fenugreek ../fenugreek
89 f fenugreek ../fenugreek
90 f mammals/skunk skunk
90 f mammals/skunk skunk
91 $ hg debugwalk -I 'relglob:*k' .
91 $ hg debugwalk -I 'relglob:*k' .
92 f mammals/skunk skunk
92 f mammals/skunk skunk
93 $ hg debugwalk -I 're:.*k$'
93 $ hg debugwalk -I 're:.*k$'
94 f beans/black ../beans/black
94 f beans/black ../beans/black
95 f fenugreek ../fenugreek
95 f fenugreek ../fenugreek
96 f mammals/skunk skunk
96 f mammals/skunk skunk
97 $ hg debugwalk -I 'relre:.*k$'
97 $ hg debugwalk -I 'relre:.*k$'
98 f beans/black ../beans/black
98 f beans/black ../beans/black
99 f fenugreek ../fenugreek
99 f fenugreek ../fenugreek
100 f mammals/skunk skunk
100 f mammals/skunk skunk
101 $ hg debugwalk -I 'path:beans'
101 $ hg debugwalk -I 'path:beans'
102 f beans/black ../beans/black
102 f beans/black ../beans/black
103 f beans/borlotti ../beans/borlotti
103 f beans/borlotti ../beans/borlotti
104 f beans/kidney ../beans/kidney
104 f beans/kidney ../beans/kidney
105 f beans/navy ../beans/navy
105 f beans/navy ../beans/navy
106 f beans/pinto ../beans/pinto
106 f beans/pinto ../beans/pinto
107 f beans/turtle ../beans/turtle
107 f beans/turtle ../beans/turtle
108 $ hg debugwalk -I 'relpath:detour/../../beans'
108 $ hg debugwalk -I 'relpath:detour/../../beans'
109 f beans/black ../beans/black
109 f beans/black ../beans/black
110 f beans/borlotti ../beans/borlotti
110 f beans/borlotti ../beans/borlotti
111 f beans/kidney ../beans/kidney
111 f beans/kidney ../beans/kidney
112 f beans/navy ../beans/navy
112 f beans/navy ../beans/navy
113 f beans/pinto ../beans/pinto
113 f beans/pinto ../beans/pinto
114 f beans/turtle ../beans/turtle
114 f beans/turtle ../beans/turtle
115 $ hg debugwalk .
115 $ hg debugwalk .
116 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
116 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
117 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
117 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
118 f mammals/Procyonidae/raccoon Procyonidae/raccoon
118 f mammals/Procyonidae/raccoon Procyonidae/raccoon
119 f mammals/skunk skunk
119 f mammals/skunk skunk
120 $ hg debugwalk -I.
120 $ hg debugwalk -I.
121 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
121 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
122 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
122 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
123 f mammals/Procyonidae/raccoon Procyonidae/raccoon
123 f mammals/Procyonidae/raccoon Procyonidae/raccoon
124 f mammals/skunk skunk
124 f mammals/skunk skunk
125 $ hg debugwalk Procyonidae
125 $ hg debugwalk Procyonidae
126 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
126 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
127 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
127 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
128 f mammals/Procyonidae/raccoon Procyonidae/raccoon
128 f mammals/Procyonidae/raccoon Procyonidae/raccoon
129
129
130 $ cd Procyonidae
130 $ cd Procyonidae
131 $ hg debugwalk .
131 $ hg debugwalk .
132 f mammals/Procyonidae/cacomistle cacomistle
132 f mammals/Procyonidae/cacomistle cacomistle
133 f mammals/Procyonidae/coatimundi coatimundi
133 f mammals/Procyonidae/coatimundi coatimundi
134 f mammals/Procyonidae/raccoon raccoon
134 f mammals/Procyonidae/raccoon raccoon
135 $ hg debugwalk ..
135 $ hg debugwalk ..
136 f mammals/Procyonidae/cacomistle cacomistle
136 f mammals/Procyonidae/cacomistle cacomistle
137 f mammals/Procyonidae/coatimundi coatimundi
137 f mammals/Procyonidae/coatimundi coatimundi
138 f mammals/Procyonidae/raccoon raccoon
138 f mammals/Procyonidae/raccoon raccoon
139 f mammals/skunk ../skunk
139 f mammals/skunk ../skunk
140 $ cd ..
140 $ cd ..
141
141
142 $ hg debugwalk ../beans
142 $ hg debugwalk ../beans
143 f beans/black ../beans/black
143 f beans/black ../beans/black
144 f beans/borlotti ../beans/borlotti
144 f beans/borlotti ../beans/borlotti
145 f beans/kidney ../beans/kidney
145 f beans/kidney ../beans/kidney
146 f beans/navy ../beans/navy
146 f beans/navy ../beans/navy
147 f beans/pinto ../beans/pinto
147 f beans/pinto ../beans/pinto
148 f beans/turtle ../beans/turtle
148 f beans/turtle ../beans/turtle
149 $ hg debugwalk .
149 $ hg debugwalk .
150 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
150 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
151 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
151 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
152 f mammals/Procyonidae/raccoon Procyonidae/raccoon
152 f mammals/Procyonidae/raccoon Procyonidae/raccoon
153 f mammals/skunk skunk
153 f mammals/skunk skunk
154 $ hg debugwalk .hg
154 $ hg debugwalk .hg
155 abort: path 'mammals/.hg' is inside nested repo 'mammals' (glob)
155 abort: path 'mammals/.hg' is inside nested repo 'mammals' (glob)
156 [255]
156 [255]
157 $ hg debugwalk ../.hg
157 $ hg debugwalk ../.hg
158 abort: path contains illegal component: .hg
158 abort: path contains illegal component: .hg
159 [255]
159 [255]
160 $ cd ..
160 $ cd ..
161
161
162 $ hg debugwalk -Ibeans
162 $ hg debugwalk -Ibeans
163 f beans/black beans/black
163 f beans/black beans/black
164 f beans/borlotti beans/borlotti
164 f beans/borlotti beans/borlotti
165 f beans/kidney beans/kidney
165 f beans/kidney beans/kidney
166 f beans/navy beans/navy
166 f beans/navy beans/navy
167 f beans/pinto beans/pinto
167 f beans/pinto beans/pinto
168 f beans/turtle beans/turtle
168 f beans/turtle beans/turtle
169 $ hg debugwalk -I '{*,{b,m}*/*}k'
169 $ hg debugwalk -I '{*,{b,m}*/*}k'
170 f beans/black beans/black
170 f beans/black beans/black
171 f fenugreek fenugreek
171 f fenugreek fenugreek
172 f mammals/skunk mammals/skunk
172 f mammals/skunk mammals/skunk
173 $ hg debugwalk 'glob:mammals/../beans/b*'
173 $ hg debugwalk 'glob:mammals/../beans/b*'
174 f beans/black beans/black
174 f beans/black beans/black
175 f beans/borlotti beans/borlotti
175 f beans/borlotti beans/borlotti
176 $ hg debugwalk '-X*/Procyonidae' mammals
176 $ hg debugwalk '-X*/Procyonidae' mammals
177 f mammals/skunk mammals/skunk
177 f mammals/skunk mammals/skunk
178 $ hg debugwalk path:mammals
178 $ hg debugwalk path:mammals
179 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
179 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
180 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
180 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
181 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
181 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
182 f mammals/skunk mammals/skunk
182 f mammals/skunk mammals/skunk
183 $ hg debugwalk ..
183 $ hg debugwalk ..
184 abort: .. not under root '$TESTTMP/t' (glob)
184 abort: .. not under root '$TESTTMP/t' (glob)
185 [255]
185 [255]
186 $ hg debugwalk beans/../..
186 $ hg debugwalk beans/../..
187 abort: beans/../.. not under root '$TESTTMP/t' (glob)
187 abort: beans/../.. not under root '$TESTTMP/t' (glob)
188 [255]
188 [255]
189 $ hg debugwalk .hg
189 $ hg debugwalk .hg
190 abort: path contains illegal component: .hg
190 abort: path contains illegal component: .hg
191 [255]
191 [255]
192 $ hg debugwalk beans/../.hg
192 $ hg debugwalk beans/../.hg
193 abort: path contains illegal component: .hg
193 abort: path contains illegal component: .hg
194 [255]
194 [255]
195 $ hg debugwalk beans/../.hg/data
195 $ hg debugwalk beans/../.hg/data
196 abort: path contains illegal component: .hg/data (glob)
196 abort: path contains illegal component: .hg/data (glob)
197 [255]
197 [255]
198 $ hg debugwalk beans/.hg
198 $ hg debugwalk beans/.hg
199 abort: path 'beans/.hg' is inside nested repo 'beans' (glob)
199 abort: path 'beans/.hg' is inside nested repo 'beans' (glob)
200 [255]
200 [255]
201
201
202 Test absolute paths:
202 Test absolute paths:
203
203
204 $ hg debugwalk `pwd`/beans
204 $ hg debugwalk `pwd`/beans
205 f beans/black beans/black
205 f beans/black beans/black
206 f beans/borlotti beans/borlotti
206 f beans/borlotti beans/borlotti
207 f beans/kidney beans/kidney
207 f beans/kidney beans/kidney
208 f beans/navy beans/navy
208 f beans/navy beans/navy
209 f beans/pinto beans/pinto
209 f beans/pinto beans/pinto
210 f beans/turtle beans/turtle
210 f beans/turtle beans/turtle
211 $ hg debugwalk `pwd`/..
211 $ hg debugwalk `pwd`/..
212 abort: $TESTTMP/t/.. not under root '$TESTTMP/t' (glob)
212 abort: $TESTTMP/t/.. not under root '$TESTTMP/t' (glob)
213 [255]
213 [255]
214
214
215 Test patterns:
215 Test patterns:
216
216
217 $ hg debugwalk glob:\*
217 $ hg debugwalk glob:\*
218 f fennel fennel
218 f fennel fennel
219 f fenugreek fenugreek
219 f fenugreek fenugreek
220 f fiddlehead fiddlehead
220 f fiddlehead fiddlehead
221 #if eol-in-paths
221 #if eol-in-paths
222 $ echo glob:glob > glob:glob
222 $ echo glob:glob > glob:glob
223 $ hg addremove
223 $ hg addremove
224 adding glob:glob
224 adding glob:glob
225 warning: filename contains ':', which is reserved on Windows: 'glob:glob'
225 warning: filename contains ':', which is reserved on Windows: 'glob:glob'
226 $ hg debugwalk glob:\*
226 $ hg debugwalk glob:\*
227 f fennel fennel
227 f fennel fennel
228 f fenugreek fenugreek
228 f fenugreek fenugreek
229 f fiddlehead fiddlehead
229 f fiddlehead fiddlehead
230 f glob:glob glob:glob
230 f glob:glob glob:glob
231 $ hg debugwalk glob:glob
231 $ hg debugwalk glob:glob
232 glob: No such file or directory
232 glob: No such file or directory
233 $ hg debugwalk glob:glob:glob
233 $ hg debugwalk glob:glob:glob
234 f glob:glob glob:glob exact
234 f glob:glob glob:glob exact
235 $ hg debugwalk path:glob:glob
235 $ hg debugwalk path:glob:glob
236 f glob:glob glob:glob exact
236 f glob:glob glob:glob exact
237 $ rm glob:glob
237 $ rm glob:glob
238 $ hg addremove
238 $ hg addremove
239 removing glob:glob
239 removing glob:glob
240 #endif
240 #endif
241
241
242 $ hg debugwalk 'glob:**e'
242 $ hg debugwalk 'glob:**e'
243 f beans/turtle beans/turtle
243 f beans/turtle beans/turtle
244 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
244 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
245
245
246 $ hg debugwalk 're:.*[kb]$'
246 $ hg debugwalk 're:.*[kb]$'
247 f beans/black beans/black
247 f beans/black beans/black
248 f fenugreek fenugreek
248 f fenugreek fenugreek
249 f mammals/skunk mammals/skunk
249 f mammals/skunk mammals/skunk
250
250
251 $ hg debugwalk path:beans/black
251 $ hg debugwalk path:beans/black
252 f beans/black beans/black exact
252 f beans/black beans/black exact
253 $ hg debugwalk path:beans//black
253 $ hg debugwalk path:beans//black
254 f beans/black beans/black exact
254 f beans/black beans/black exact
255
255
256 $ hg debugwalk relglob:Procyonidae
256 $ hg debugwalk relglob:Procyonidae
257 $ hg debugwalk 'relglob:Procyonidae/**'
257 $ hg debugwalk 'relglob:Procyonidae/**'
258 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
258 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
259 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
259 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
260 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
260 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
261 $ hg debugwalk 'relglob:Procyonidae/**' fennel
261 $ hg debugwalk 'relglob:Procyonidae/**' fennel
262 f fennel fennel exact
262 f fennel fennel exact
263 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
263 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
264 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
264 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
265 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
265 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
266 $ hg debugwalk beans 'glob:beans/*'
266 $ hg debugwalk beans 'glob:beans/*'
267 f beans/black beans/black
267 f beans/black beans/black
268 f beans/borlotti beans/borlotti
268 f beans/borlotti beans/borlotti
269 f beans/kidney beans/kidney
269 f beans/kidney beans/kidney
270 f beans/navy beans/navy
270 f beans/navy beans/navy
271 f beans/pinto beans/pinto
271 f beans/pinto beans/pinto
272 f beans/turtle beans/turtle
272 f beans/turtle beans/turtle
273 $ hg debugwalk 'glob:mamm**'
273 $ hg debugwalk 'glob:mamm**'
274 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
274 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
275 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
275 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
276 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
276 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
277 f mammals/skunk mammals/skunk
277 f mammals/skunk mammals/skunk
278 $ hg debugwalk 'glob:mamm**' fennel
278 $ hg debugwalk 'glob:mamm**' fennel
279 f fennel fennel exact
279 f fennel fennel exact
280 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
280 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
281 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
281 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
282 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
282 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
283 f mammals/skunk mammals/skunk
283 f mammals/skunk mammals/skunk
284 $ hg debugwalk 'glob:j*'
284 $ hg debugwalk 'glob:j*'
285 $ hg debugwalk NOEXIST
285 $ hg debugwalk NOEXIST
286 NOEXIST: * (glob)
286 NOEXIST: * (glob)
287
287
288 #if fifo
288 #if fifo
289 $ mkfifo fifo
289 $ mkfifo fifo
290 $ hg debugwalk fifo
290 $ hg debugwalk fifo
291 fifo: unsupported file type (type is fifo)
291 fifo: unsupported file type (type is fifo)
292 #endif
292 #endif
293
293
294 $ rm fenugreek
294 $ rm fenugreek
295 $ hg debugwalk fenugreek
295 $ hg debugwalk fenugreek
296 f fenugreek fenugreek exact
296 f fenugreek fenugreek exact
297 $ hg rm fenugreek
297 $ hg rm fenugreek
298 $ hg debugwalk fenugreek
298 $ hg debugwalk fenugreek
299 f fenugreek fenugreek exact
299 f fenugreek fenugreek exact
300 $ touch new
300 $ touch new
301 $ hg debugwalk new
301 $ hg debugwalk new
302 f new new exact
302 f new new exact
303
303
304 $ mkdir ignored
304 $ mkdir ignored
305 $ touch ignored/file
305 $ touch ignored/file
306 $ echo '^ignored$' > .hgignore
306 $ echo '^ignored$' > .hgignore
307 $ hg debugwalk ignored
307 $ hg debugwalk ignored
308 $ hg debugwalk ignored/file
308 $ hg debugwalk ignored/file
309 f ignored/file ignored/file exact
309 f ignored/file ignored/file exact
310
310
311 Test listfile and listfile0
311 Test listfile and listfile0
312
312
313 $ python -c "file('listfile0', 'wb').write('fenugreek\0new\0')"
313 $ python -c "file('listfile0', 'wb').write('fenugreek\0new\0')"
314 $ hg debugwalk -I 'listfile0:listfile0'
314 $ hg debugwalk -I 'listfile0:listfile0'
315 f fenugreek fenugreek
315 f fenugreek fenugreek
316 f new new
316 f new new
317 $ python -c "file('listfile', 'wb').write('fenugreek\nnew\r\nmammals/skunk\n')"
317 $ python -c "file('listfile', 'wb').write('fenugreek\nnew\r\nmammals/skunk\n')"
318 $ hg debugwalk -I 'listfile:listfile'
318 $ hg debugwalk -I 'listfile:listfile'
319 f fenugreek fenugreek
319 f fenugreek fenugreek
320 f mammals/skunk mammals/skunk
320 f mammals/skunk mammals/skunk
321 f new new
321 f new new
322
322
323 $ cd ..
323 $ cd ..
324 $ hg debugwalk -R t t/mammals/skunk
324 $ hg debugwalk -R t t/mammals/skunk
325 f mammals/skunk t/mammals/skunk exact
325 f mammals/skunk t/mammals/skunk exact
326 $ mkdir t2
326 $ mkdir t2
327 $ cd t2
327 $ cd t2
328 $ hg debugwalk -R ../t ../t/mammals/skunk
328 $ hg debugwalk -R ../t ../t/mammals/skunk
329 f mammals/skunk ../t/mammals/skunk exact
329 f mammals/skunk ../t/mammals/skunk exact
330 $ hg debugwalk --cwd ../t mammals/skunk
330 $ hg debugwalk --cwd ../t mammals/skunk
331 f mammals/skunk mammals/skunk exact
331 f mammals/skunk mammals/skunk exact
332
332
333 $ cd ..
333 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now