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