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