##// END OF EJS Templates
test-revert.t: fix wc check-code false positive
Matt Mackall -
r23134:22e76e37 default
parent child Browse files
Show More
@@ -1,581 +1,581
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 """style and portability checker for Mercurial
11 11
12 12 when a rule triggers wrong, do one of the following (prefer one from top):
13 13 * do the work-around the rule suggests
14 14 * doublecheck that it is a false match
15 15 * improve the rule pattern
16 16 * add an ignore pattern to the rule (3rd arg) which matches your good line
17 17 (you can append a short comment and match this, like: #re-raises, # no-py24)
18 18 * change the pattern to a warning and list the exception in test-check-code-hg
19 19 * ONLY use no--check-code for skipping entire files from external sources
20 20 """
21 21
22 22 import re, glob, os, sys
23 23 import keyword
24 24 import optparse
25 25 try:
26 26 import re2
27 27 except ImportError:
28 28 re2 = None
29 29
30 30 def compilere(pat, multiline=False):
31 31 if multiline:
32 32 pat = '(?m)' + pat
33 33 if re2:
34 34 try:
35 35 return re2.compile(pat)
36 36 except re2.error:
37 37 pass
38 38 return re.compile(pat)
39 39
40 40 def repquote(m):
41 41 fromc = '.:'
42 42 tochr = 'pq'
43 43 def encodechr(i):
44 44 if i > 255:
45 45 return 'u'
46 46 c = chr(i)
47 47 if c in ' \n':
48 48 return c
49 49 if c.isalpha():
50 50 return 'x'
51 51 if c.isdigit():
52 52 return 'n'
53 53 try:
54 54 return tochr[fromc.find(c)]
55 55 except (ValueError, IndexError):
56 56 return 'o'
57 57 t = m.group('text')
58 58 tt = ''.join(encodechr(i) for i in xrange(256))
59 59 t = t.translate(tt)
60 60 return m.group('quote') + t + m.group('quote')
61 61
62 62 def reppython(m):
63 63 comment = m.group('comment')
64 64 if comment:
65 65 l = len(comment.rstrip())
66 66 return "#" * l + comment[l:]
67 67 return repquote(m)
68 68
69 69 def repcomment(m):
70 70 return m.group(1) + "#" * len(m.group(2))
71 71
72 72 def repccomment(m):
73 73 t = re.sub(r"((?<=\n) )|\S", "x", m.group(2))
74 74 return m.group(1) + t + "*/"
75 75
76 76 def repcallspaces(m):
77 77 t = re.sub(r"\n\s+", "\n", m.group(2))
78 78 return m.group(1) + t
79 79
80 80 def repinclude(m):
81 81 return m.group(1) + "<foo>"
82 82
83 83 def rephere(m):
84 84 t = re.sub(r"\S", "x", m.group(2))
85 85 return m.group(1) + t
86 86
87 87
88 88 testpats = [
89 89 [
90 90 (r'pushd|popd', "don't use 'pushd' or 'popd', use 'cd'"),
91 91 (r'\W\$?\(\([^\)\n]*\)\)', "don't use (()) or $(()), use 'expr'"),
92 92 (r'grep.*-q', "don't use 'grep -q', redirect to /dev/null"),
93 93 (r'(?<!hg )grep.*-a', "don't use 'grep -a', use in-line python"),
94 94 (r'sed.*-i', "don't use 'sed -i', use a temporary file"),
95 95 (r'\becho\b.*\\n', "don't use 'echo \\n', use printf"),
96 96 (r'echo -n', "don't use 'echo -n', use printf"),
97 (r'(^| )\bwc\b[^|]*$\n(?!.*\(re\))', "filter wc output"),
97 (r'(^|\|\s*)\bwc\b[^|]*$\n(?!.*\(re\))', "filter wc output"),
98 98 (r'head -c', "don't use 'head -c', use 'dd'"),
99 99 (r'tail -n', "don't use the '-n' option to tail, just use '-<num>'"),
100 100 (r'sha1sum', "don't use sha1sum, use $TESTDIR/md5sum.py"),
101 101 (r'ls.*-\w*R', "don't use 'ls -R', use 'find'"),
102 102 (r'printf.*[^\\]\\([1-9]|0\d)', "don't use 'printf \NNN', use Python"),
103 103 (r'printf.*[^\\]\\x', "don't use printf \\x, use Python"),
104 104 (r'\$\(.*\)', "don't use $(expr), use `expr`"),
105 105 (r'rm -rf \*', "don't use naked rm -rf, target a directory"),
106 106 (r'(^|\|\s*)grep (-\w\s+)*[^|]*[(|]\w',
107 107 "use egrep for extended grep syntax"),
108 108 (r'/bin/', "don't use explicit paths for tools"),
109 109 (r'[^\n]\Z', "no trailing newline"),
110 110 (r'export.*=', "don't export and assign at once"),
111 111 (r'^source\b', "don't use 'source', use '.'"),
112 112 (r'touch -d', "don't use 'touch -d', use 'touch -t' instead"),
113 113 (r'ls +[^|\n-]+ +-', "options to 'ls' must come before filenames"),
114 114 (r'[^>\n]>\s*\$HGRCPATH', "don't overwrite $HGRCPATH, append to it"),
115 115 (r'^stop\(\)', "don't use 'stop' as a shell function name"),
116 116 (r'(\[|\btest\b).*-e ', "don't use 'test -e', use 'test -f'"),
117 117 (r'^alias\b.*=', "don't use alias, use a function"),
118 118 (r'if\s*!', "don't use '!' to negate exit status"),
119 119 (r'/dev/u?random', "don't use entropy, use /dev/zero"),
120 120 (r'do\s*true;\s*done', "don't use true as loop body, use sleep 0"),
121 121 (r'^( *)\t', "don't use tabs to indent"),
122 122 (r'sed (-e )?\'(\d+|/[^/]*/)i(?!\\\n)',
123 123 "put a backslash-escaped newline after sed 'i' command"),
124 124 (r'^diff *-\w*u.*$\n(^ \$ |^$)', "prefix diff -u with cmp"),
125 125 ],
126 126 # warnings
127 127 [
128 128 (r'^function', "don't use 'function', use old style"),
129 129 (r'^diff.*-\w*N', "don't use 'diff -N'"),
130 130 (r'\$PWD|\${PWD}', "don't use $PWD, use `pwd`"),
131 131 (r'^([^"\'\n]|("[^"\n]*")|(\'[^\'\n]*\'))*\^', "^ must be quoted"),
132 132 (r'kill (`|\$\()', "don't use kill, use killdaemons.py")
133 133 ]
134 134 ]
135 135
136 136 testfilters = [
137 137 (r"( *)(#([^\n]*\S)?)", repcomment),
138 138 (r"<<(\S+)((.|\n)*?\n\1)", rephere),
139 139 ]
140 140
141 141 winglobmsg = "use (glob) to match Windows paths too"
142 142 uprefix = r"^ \$ "
143 143 utestpats = [
144 144 [
145 145 (r'^(\S.*|| [$>] .*)[ \t]\n', "trailing whitespace on non-output"),
146 146 (uprefix + r'.*\|\s*sed[^|>\n]*\n',
147 147 "use regex test output patterns instead of sed"),
148 148 (uprefix + r'(true|exit 0)', "explicit zero exit unnecessary"),
149 149 (uprefix + r'.*(?<!\[)\$\?', "explicit exit code checks unnecessary"),
150 150 (uprefix + r'.*\|\| echo.*(fail|error)',
151 151 "explicit exit code checks unnecessary"),
152 152 (uprefix + r'set -e', "don't use set -e"),
153 153 (uprefix + r'(\s|fi\b|done\b)', "use > for continued lines"),
154 154 (uprefix + r'.*:\.\S*/', "x:.y in a path does not work on msys, rewrite "
155 155 "as x://.y, or see `hg log -k msys` for alternatives", r'-\S+:\.|' #-Rxxx
156 156 'hg pull -q file:../test'), # in test-pull.t which is skipped on windows
157 157 (r'^ saved backup bundle to \$TESTTMP.*\.hg$', winglobmsg),
158 158 (r'^ changeset .* references (corrupted|missing) \$TESTTMP/.*[^)]$',
159 159 winglobmsg),
160 160 (r'^ pulling from \$TESTTMP/.*[^)]$', winglobmsg,
161 161 '\$TESTTMP/unix-repo$'), # in test-issue1802.t which skipped on windows
162 162 (r'^ reverting .*/.*[^)]$', winglobmsg),
163 163 (r'^ cloning subrepo \S+/.*[^)]$', winglobmsg),
164 164 (r'^ pushing to \$TESTTMP/.*[^)]$', winglobmsg),
165 165 (r'^ pushing subrepo \S+/\S+ to.*[^)]$', winglobmsg),
166 166 (r'^ moving \S+/.*[^)]$', winglobmsg),
167 167 (r'^ no changes made to subrepo since.*/.*[^)]$', winglobmsg),
168 168 (r'^ .*: largefile \S+ not available from file:.*/.*[^)]$', winglobmsg),
169 169 (r'^ .*file://\$TESTTMP',
170 170 'write "file:/*/$TESTTMP" + (glob) to match on windows too'),
171 171 (r'^ (cat|find): .*: No such file or directory',
172 172 'use test -f to test for file existence'),
173 173 ],
174 174 # warnings
175 175 [
176 176 (r'^ [^*?/\n]* \(glob\)$',
177 177 "glob match with no glob character (?*/)"),
178 178 ]
179 179 ]
180 180
181 181 for i in [0, 1]:
182 182 for tp in testpats[i]:
183 183 p = tp[0]
184 184 m = tp[1]
185 185 if p.startswith(r'^'):
186 186 p = r"^ [$>] (%s)" % p[1:]
187 187 else:
188 188 p = r"^ [$>] .*(%s)" % p
189 189 utestpats[i].append((p, m) + tp[2:])
190 190
191 191 utestfilters = [
192 192 (r"<<(\S+)((.|\n)*?\n > \1)", rephere),
193 193 (r"( *)(#([^\n]*\S)?)", repcomment),
194 194 ]
195 195
196 196 pypats = [
197 197 [
198 198 (r'\([^)]*\*\w[^()]+\w+=', "can't pass varargs with keyword in Py2.5"),
199 199 (r'^\s*def\s*\w+\s*\(.*,\s*\(',
200 200 "tuple parameter unpacking not available in Python 3+"),
201 201 (r'lambda\s*\(.*,.*\)',
202 202 "tuple parameter unpacking not available in Python 3+"),
203 203 (r'import (.+,[^.]+\.[^.]+|[^.]+\.[^.]+,)',
204 204 '2to3 can\'t always rewrite "import qux, foo.bar", '
205 205 'use "import foo.bar" on its own line instead.'),
206 206 (r'(?<!def)\s+(cmp)\(', "cmp is not available in Python 3+"),
207 207 (r'\breduce\s*\(.*', "reduce is not available in Python 3+"),
208 208 (r'dict\(.*=', 'dict() is different in Py2 and 3 and is slower than {}',
209 209 'dict-from-generator'),
210 210 (r'\.has_key\b', "dict.has_key is not available in Python 3+"),
211 211 (r'\s<>\s', '<> operator is not available in Python 3+, use !='),
212 212 (r'^\s*\t', "don't use tabs"),
213 213 (r'\S;\s*\n', "semicolon"),
214 214 (r'[^_]_\([ \t\n]*(?:"[^"]+"[ \t\n+]*)+%', "don't use % inside _()"),
215 215 (r"[^_]_\([ \t\n]*(?:'[^']+'[ \t\n+]*)+%", "don't use % inside _()"),
216 216 (r'(\w|\)),\w', "missing whitespace after ,"),
217 217 (r'(\w|\))[+/*\-<>]\w', "missing whitespace in expression"),
218 218 (r'^\s+(\w|\.)+=\w[^,()\n]*$', "missing whitespace in assignment"),
219 219 (r'(\s+)try:\n((?:\n|\1\s.*\n)+?)(\1except.*?:\n'
220 220 r'((?:\n|\1\s.*\n)+?))+\1finally:',
221 221 'no try/except/finally in Python 2.4'),
222 222 (r'(?<!def)(\s+|^|\()next\(.+\)',
223 223 'no next(foo) in Python 2.4 and 2.5, use foo.next() instead'),
224 224 (r'(\s+)try:\n((?:\n|\1\s.*\n)*?)\1\s*yield\b.*?'
225 225 r'((?:\n|\1\s.*\n)+?)\1finally:',
226 226 'no yield inside try/finally in Python 2.4'),
227 227 (r'.{81}', "line too long"),
228 228 (r' x+[xo][\'"]\n\s+[\'"]x', 'string join across lines with no space'),
229 229 (r'[^\n]\Z', "no trailing newline"),
230 230 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"),
231 231 # (r'^\s+[^_ \n][^_. \n]+_[^_\n]+\s*=',
232 232 # "don't use underbars in identifiers"),
233 233 (r'^\s+(self\.)?[A-za-z][a-z0-9]+[A-Z]\w* = ',
234 234 "don't use camelcase in identifiers"),
235 235 (r'^\s*(if|while|def|class|except|try)\s[^[\n]*:\s*[^\\n]#\s]+',
236 236 "linebreak after :"),
237 237 (r'class\s[^( \n]+:', "old-style class, use class foo(object)"),
238 238 (r'class\s[^( \n]+\(\):',
239 239 "class foo() not available in Python 2.4, use class foo(object)"),
240 240 (r'\b(%s)\(' % '|'.join(keyword.kwlist),
241 241 "Python keyword is not a function"),
242 242 (r',]', "unneeded trailing ',' in list"),
243 243 # (r'class\s[A-Z][^\(]*\((?!Exception)',
244 244 # "don't capitalize non-exception classes"),
245 245 # (r'in range\(', "use xrange"),
246 246 # (r'^\s*print\s+', "avoid using print in core and extensions"),
247 247 (r'[\x80-\xff]', "non-ASCII character literal"),
248 248 (r'("\')\.format\(', "str.format() not available in Python 2.4"),
249 249 (r'^\s*with\s+', "with not available in Python 2.4"),
250 250 (r'\.isdisjoint\(', "set.isdisjoint not available in Python 2.4"),
251 251 (r'^\s*except.* as .*:', "except as not available in Python 2.4"),
252 252 (r'^\s*os\.path\.relpath', "relpath not available in Python 2.4"),
253 253 (r'(?<!def)\s+(any|all|format)\(',
254 254 "any/all/format not available in Python 2.4", 'no-py24'),
255 255 (r'if\s.*\selse', "if ... else form not available in Python 2.4"),
256 256 (r'^\s*(%s)\s\s' % '|'.join(keyword.kwlist),
257 257 "gratuitous whitespace after Python keyword"),
258 258 (r'([\(\[][ \t]\S)|(\S[ \t][\)\]])', "gratuitous whitespace in () or []"),
259 259 # (r'\s\s=', "gratuitous whitespace before ="),
260 260 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\S',
261 261 "missing whitespace around operator"),
262 262 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\s',
263 263 "missing whitespace around operator"),
264 264 (r'\s(\+=|-=|!=|<>|<=|>=|<<=|>>=|%=)\S',
265 265 "missing whitespace around operator"),
266 266 (r'[^^+=*/!<>&| %-](\s=|=\s)[^= ]',
267 267 "wrong whitespace around ="),
268 268 (r'\([^()]*( =[^=]|[^<>!=]= )',
269 269 "no whitespace around = for named parameters"),
270 270 (r'raise Exception', "don't raise generic exceptions"),
271 271 (r'raise [^,(]+, (\([^\)]+\)|[^,\(\)]+)$',
272 272 "don't use old-style two-argument raise, use Exception(message)"),
273 273 (r' is\s+(not\s+)?["\'0-9-]', "object comparison with literal"),
274 274 (r' [=!]=\s+(True|False|None)',
275 275 "comparison with singleton, use 'is' or 'is not' instead"),
276 276 (r'^\s*(while|if) [01]:',
277 277 "use True/False for constant Boolean expression"),
278 278 (r'(?:(?<!def)\s+|\()hasattr',
279 279 'hasattr(foo, bar) is broken, use util.safehasattr(foo, bar) instead'),
280 280 (r'opener\([^)]*\).read\(',
281 281 "use opener.read() instead"),
282 282 (r'BaseException', 'not in Python 2.4, use Exception'),
283 283 (r'os\.path\.relpath', 'os.path.relpath is not in Python 2.5'),
284 284 (r'opener\([^)]*\).write\(',
285 285 "use opener.write() instead"),
286 286 (r'[\s\(](open|file)\([^)]*\)\.read\(',
287 287 "use util.readfile() instead"),
288 288 (r'[\s\(](open|file)\([^)]*\)\.write\(',
289 289 "use util.writefile() instead"),
290 290 (r'^[\s\(]*(open(er)?|file)\([^)]*\)',
291 291 "always assign an opened file to a variable, and close it afterwards"),
292 292 (r'[\s\(](open|file)\([^)]*\)\.',
293 293 "always assign an opened file to a variable, and close it afterwards"),
294 294 (r'(?i)descendent', "the proper spelling is descendAnt"),
295 295 (r'\.debug\(\_', "don't mark debug messages for translation"),
296 296 (r'\.strip\(\)\.split\(\)', "no need to strip before splitting"),
297 297 (r'^\s*except\s*:', "naked except clause", r'#.*re-raises'),
298 298 (r':\n( )*( ){1,3}[^ ]', "must indent 4 spaces"),
299 299 (r'ui\.(status|progress|write|note|warn)\([\'\"]x',
300 300 "missing _() in ui message (use () to hide false-positives)"),
301 301 (r'release\(.*wlock, .*lock\)', "wrong lock release order"),
302 302 (r'\b__bool__\b', "__bool__ should be __nonzero__ in Python 2"),
303 303 ],
304 304 # warnings
305 305 [
306 306 (r'(^| )pp +xxxxqq[ \n][^\n]', "add two newlines after '.. note::'"),
307 307 ]
308 308 ]
309 309
310 310 pyfilters = [
311 311 (r"""(?msx)(?P<comment>\#.*?$)|
312 312 ((?P<quote>('''|\"\"\"|(?<!')'(?!')|(?<!")"(?!")))
313 313 (?P<text>(([^\\]|\\.)*?))
314 314 (?P=quote))""", reppython),
315 315 ]
316 316
317 317 txtfilters = []
318 318
319 319 txtpats = [
320 320 [
321 321 ('\s$', 'trailing whitespace'),
322 322 ('.. note::[ \n][^\n]', 'add two newlines after note::')
323 323 ],
324 324 []
325 325 ]
326 326
327 327 cpats = [
328 328 [
329 329 (r'//', "don't use //-style comments"),
330 330 (r'^ ', "don't use spaces to indent"),
331 331 (r'\S\t', "don't use tabs except for indent"),
332 332 (r'(\S[ \t]+|^[ \t]+)\n', "trailing whitespace"),
333 333 (r'.{81}', "line too long"),
334 334 (r'(while|if|do|for)\(', "use space after while/if/do/for"),
335 335 (r'return\(', "return is not a function"),
336 336 (r' ;', "no space before ;"),
337 337 (r'[)][{]', "space between ) and {"),
338 338 (r'\w+\* \w+', "use int *foo, not int* foo"),
339 339 (r'\W\([^\)]+\) \w+', "use (int)foo, not (int) foo"),
340 340 (r'\w+ (\+\+|--)', "use foo++, not foo ++"),
341 341 (r'\w,\w', "missing whitespace after ,"),
342 342 (r'^[^#]\w[+/*]\w', "missing whitespace in expression"),
343 343 (r'^#\s+\w', "use #foo, not # foo"),
344 344 (r'[^\n]\Z', "no trailing newline"),
345 345 (r'^\s*#import\b', "use only #include in standard C code"),
346 346 ],
347 347 # warnings
348 348 []
349 349 ]
350 350
351 351 cfilters = [
352 352 (r'(/\*)(((\*(?!/))|[^*])*)\*/', repccomment),
353 353 (r'''(?P<quote>(?<!")")(?P<text>([^"]|\\")+)"(?!")''', repquote),
354 354 (r'''(#\s*include\s+<)([^>]+)>''', repinclude),
355 355 (r'(\()([^)]+\))', repcallspaces),
356 356 ]
357 357
358 358 inutilpats = [
359 359 [
360 360 (r'\bui\.', "don't use ui in util"),
361 361 ],
362 362 # warnings
363 363 []
364 364 ]
365 365
366 366 inrevlogpats = [
367 367 [
368 368 (r'\brepo\.', "don't use repo in revlog"),
369 369 ],
370 370 # warnings
371 371 []
372 372 ]
373 373
374 374 webtemplatefilters = []
375 375
376 376 webtemplatepats = [
377 377 [],
378 378 [
379 379 (r'{desc(\|(?!websub|firstline)[^\|]*)+}',
380 380 'follow desc keyword with either firstline or websub'),
381 381 ]
382 382 ]
383 383
384 384 checks = [
385 385 ('python', r'.*\.(py|cgi)$', r'^#!.*python', pyfilters, pypats),
386 386 ('test script', r'(.*/)?test-[^.~]*$', '', testfilters, testpats),
387 387 ('c', r'.*\.[ch]$', '', cfilters, cpats),
388 388 ('unified test', r'.*\.t$', '', utestfilters, utestpats),
389 389 ('layering violation repo in revlog', r'mercurial/revlog\.py', '',
390 390 pyfilters, inrevlogpats),
391 391 ('layering violation ui in util', r'mercurial/util\.py', '', pyfilters,
392 392 inutilpats),
393 393 ('txt', r'.*\.txt$', '', txtfilters, txtpats),
394 394 ('web template', r'mercurial/templates/.*\.tmpl', '',
395 395 webtemplatefilters, webtemplatepats),
396 396 ]
397 397
398 398 def _preparepats():
399 399 for c in checks:
400 400 failandwarn = c[-1]
401 401 for pats in failandwarn:
402 402 for i, pseq in enumerate(pats):
403 403 # fix-up regexes for multi-line searches
404 404 p = pseq[0]
405 405 # \s doesn't match \n
406 406 p = re.sub(r'(?<!\\)\\s', r'[ \\t]', p)
407 407 # [^...] doesn't match newline
408 408 p = re.sub(r'(?<!\\)\[\^', r'[^\\n', p)
409 409
410 410 pats[i] = (re.compile(p, re.MULTILINE),) + pseq[1:]
411 411 filters = c[3]
412 412 for i, flt in enumerate(filters):
413 413 filters[i] = re.compile(flt[0]), flt[1]
414 414 _preparepats()
415 415
416 416 class norepeatlogger(object):
417 417 def __init__(self):
418 418 self._lastseen = None
419 419
420 420 def log(self, fname, lineno, line, msg, blame):
421 421 """print error related a to given line of a given file.
422 422
423 423 The faulty line will also be printed but only once in the case
424 424 of multiple errors.
425 425
426 426 :fname: filename
427 427 :lineno: line number
428 428 :line: actual content of the line
429 429 :msg: error message
430 430 """
431 431 msgid = fname, lineno, line
432 432 if msgid != self._lastseen:
433 433 if blame:
434 434 print "%s:%d (%s):" % (fname, lineno, blame)
435 435 else:
436 436 print "%s:%d:" % (fname, lineno)
437 437 print " > %s" % line
438 438 self._lastseen = msgid
439 439 print " " + msg
440 440
441 441 _defaultlogger = norepeatlogger()
442 442
443 443 def getblame(f):
444 444 lines = []
445 445 for l in os.popen('hg annotate -un %s' % f):
446 446 start, line = l.split(':', 1)
447 447 user, rev = start.split()
448 448 lines.append((line[1:-1], user, rev))
449 449 return lines
450 450
451 451 def checkfile(f, logfunc=_defaultlogger.log, maxerr=None, warnings=False,
452 452 blame=False, debug=False, lineno=True):
453 453 """checks style and portability of a given file
454 454
455 455 :f: filepath
456 456 :logfunc: function used to report error
457 457 logfunc(filename, linenumber, linecontent, errormessage)
458 458 :maxerr: number of error to display before aborting.
459 459 Set to false (default) to report all errors
460 460
461 461 return True if no error is found, False otherwise.
462 462 """
463 463 blamecache = None
464 464 result = True
465 465
466 466 try:
467 467 fp = open(f)
468 468 except IOError, e:
469 469 print "Skipping %s, %s" % (f, str(e).split(':', 1)[0])
470 470 return result
471 471 pre = post = fp.read()
472 472 fp.close()
473 473
474 474 for name, match, magic, filters, pats in checks:
475 475 if debug:
476 476 print name, f
477 477 fc = 0
478 478 if not (re.match(match, f) or (magic and re.search(magic, f))):
479 479 if debug:
480 480 print "Skipping %s for %s it doesn't match %s" % (
481 481 name, match, f)
482 482 continue
483 483 if "no-" "check-code" in pre:
484 484 print "Skipping %s it has no-" "check-code" % f
485 485 return "Skip" # skip checking this file
486 486 for p, r in filters:
487 487 post = re.sub(p, r, post)
488 488 nerrs = len(pats[0]) # nerr elements are errors
489 489 if warnings:
490 490 pats = pats[0] + pats[1]
491 491 else:
492 492 pats = pats[0]
493 493 # print post # uncomment to show filtered version
494 494
495 495 if debug:
496 496 print "Checking %s for %s" % (name, f)
497 497
498 498 prelines = None
499 499 errors = []
500 500 for i, pat in enumerate(pats):
501 501 if len(pat) == 3:
502 502 p, msg, ignore = pat
503 503 else:
504 504 p, msg = pat
505 505 ignore = None
506 506 if i >= nerrs:
507 507 msg = "warning: " + msg
508 508
509 509 pos = 0
510 510 n = 0
511 511 for m in p.finditer(post):
512 512 if prelines is None:
513 513 prelines = pre.splitlines()
514 514 postlines = post.splitlines(True)
515 515
516 516 start = m.start()
517 517 while n < len(postlines):
518 518 step = len(postlines[n])
519 519 if pos + step > start:
520 520 break
521 521 pos += step
522 522 n += 1
523 523 l = prelines[n]
524 524
525 525 if ignore and re.search(ignore, l, re.MULTILINE):
526 526 if debug:
527 527 print "Skipping %s for %s:%s (ignore pattern)" % (
528 528 name, f, n)
529 529 continue
530 530 bd = ""
531 531 if blame:
532 532 bd = 'working directory'
533 533 if not blamecache:
534 534 blamecache = getblame(f)
535 535 if n < len(blamecache):
536 536 bl, bu, br = blamecache[n]
537 537 if bl == l:
538 538 bd = '%s@%s' % (bu, br)
539 539
540 540 errors.append((f, lineno and n + 1, l, msg, bd))
541 541 result = False
542 542
543 543 errors.sort()
544 544 for e in errors:
545 545 logfunc(*e)
546 546 fc += 1
547 547 if maxerr and fc >= maxerr:
548 548 print " (too many errors, giving up)"
549 549 break
550 550
551 551 return result
552 552
553 553 if __name__ == "__main__":
554 554 parser = optparse.OptionParser("%prog [options] [files]")
555 555 parser.add_option("-w", "--warnings", action="store_true",
556 556 help="include warning-level checks")
557 557 parser.add_option("-p", "--per-file", type="int",
558 558 help="max warnings per file")
559 559 parser.add_option("-b", "--blame", action="store_true",
560 560 help="use annotate to generate blame info")
561 561 parser.add_option("", "--debug", action="store_true",
562 562 help="show debug information")
563 563 parser.add_option("", "--nolineno", action="store_false",
564 564 dest='lineno', help="don't show line numbers")
565 565
566 566 parser.set_defaults(per_file=15, warnings=False, blame=False, debug=False,
567 567 lineno=True)
568 568 (options, args) = parser.parse_args()
569 569
570 570 if len(args) == 0:
571 571 check = glob.glob("*")
572 572 else:
573 573 check = args
574 574
575 575 ret = 0
576 576 for f in check:
577 577 if not checkfile(f, maxerr=options.per_file, warnings=options.warnings,
578 578 blame=options.blame, debug=options.debug,
579 579 lineno=options.lineno):
580 580 ret = 1
581 581 sys.exit(ret)
@@ -1,1197 +1,1197
1 1 $ hg init repo
2 2 $ cd repo
3 3 $ echo 123 > a
4 4 $ echo 123 > c
5 5 $ echo 123 > e
6 6 $ hg add a c e
7 7 $ hg commit -m "first" a c e
8 8
9 9 nothing changed
10 10
11 11 $ hg revert
12 12 abort: no files or directories specified
13 13 (use --all to revert all files)
14 14 [255]
15 15 $ hg revert --all
16 16
17 17 Introduce some changes and revert them
18 18 --------------------------------------
19 19
20 20 $ echo 123 > b
21 21
22 22 $ hg status
23 23 ? b
24 24 $ echo 12 > c
25 25
26 26 $ hg status
27 27 M c
28 28 ? b
29 29 $ hg add b
30 30
31 31 $ hg status
32 32 M c
33 33 A b
34 34 $ hg rm a
35 35
36 36 $ hg status
37 37 M c
38 38 A b
39 39 R a
40 40
41 41 revert removal of a file
42 42
43 43 $ hg revert a
44 44 $ hg status
45 45 M c
46 46 A b
47 47
48 48 revert addition of a file
49 49
50 50 $ hg revert b
51 51 $ hg status
52 52 M c
53 53 ? b
54 54
55 55 revert modification of a file (--no-backup)
56 56
57 57 $ hg revert --no-backup c
58 58 $ hg status
59 59 ? b
60 60
61 61 revert deletion (! status) of a added file
62 62 ------------------------------------------
63 63
64 64 $ hg add b
65 65
66 66 $ hg status b
67 67 A b
68 68 $ rm b
69 69 $ hg status b
70 70 ! b
71 71 $ hg revert -v b
72 72 forgetting b
73 73 $ hg status b
74 74 b: * (glob)
75 75
76 76 $ ls
77 77 a
78 78 c
79 79 e
80 80
81 81 Test creation of backup (.orig) files
82 82 -------------------------------------
83 83
84 84 $ echo z > e
85 85 $ hg revert --all -v
86 86 saving current version of e as e.orig
87 87 reverting e
88 88
89 89 revert on clean file (no change)
90 90 --------------------------------
91 91
92 92 $ hg revert a
93 93 no changes needed to a
94 94
95 95 revert on an untracked file
96 96 ---------------------------
97 97
98 98 $ echo q > q
99 99 $ hg revert q
100 100 file not managed: q
101 101 $ rm q
102 102
103 103 revert on file that does not exists
104 104 -----------------------------------
105 105
106 106 $ hg revert notfound
107 107 notfound: no such file in rev 334a9e57682c
108 108 $ touch d
109 109 $ hg add d
110 110 $ hg rm a
111 111 $ hg commit -m "second"
112 112 $ echo z > z
113 113 $ hg add z
114 114 $ hg st
115 115 A z
116 116 ? e.orig
117 117
118 118 revert to another revision (--rev)
119 119 ----------------------------------
120 120
121 121 $ hg revert --all -r0
122 122 adding a
123 123 removing d
124 124 forgetting z
125 125
126 126 revert explicitly to parent (--rev)
127 127 -----------------------------------
128 128
129 129 $ hg revert --all -rtip
130 130 forgetting a
131 131 undeleting d
132 132 $ rm a *.orig
133 133
134 134 revert to another revision (--rev) and exact match
135 135 --------------------------------------------------
136 136
137 137 exact match are more silent
138 138
139 139 $ hg revert -r0 a
140 140 $ hg st a
141 141 A a
142 142 $ hg rm d
143 143 $ hg st d
144 144 R d
145 145
146 146 should keep d removed
147 147
148 148 $ hg revert -r0 d
149 149 no changes needed to d
150 150 $ hg st d
151 151 R d
152 152
153 153 $ hg update -C
154 154 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
155 155
156 156 revert of exec bit
157 157 ------------------
158 158
159 159 #if execbit
160 160 $ chmod +x c
161 161 $ hg revert --all
162 162 reverting c
163 163
164 164 $ test -x c || echo non-executable
165 165 non-executable
166 166
167 167 $ chmod +x c
168 168 $ hg commit -m exe
169 169
170 170 $ chmod -x c
171 171 $ hg revert --all
172 172 reverting c
173 173
174 174 $ test -x c && echo executable
175 175 executable
176 176 #endif
177 177
178 178 $ cd ..
179 179
180 180
181 181 Issue241: update and revert produces inconsistent repositories
182 182 --------------------------------------------------------------
183 183
184 184 $ hg init a
185 185 $ cd a
186 186 $ echo a >> a
187 187 $ hg commit -A -d '1 0' -m a
188 188 adding a
189 189 $ echo a >> a
190 190 $ hg commit -d '2 0' -m a
191 191 $ hg update 0
192 192 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
193 193 $ mkdir b
194 194 $ echo b > b/b
195 195
196 196 call `hg revert` with no file specified
197 197 ---------------------------------------
198 198
199 199 $ hg revert -rtip
200 200 abort: no files or directories specified
201 201 (use --all to revert all files, or 'hg update 1' to update)
202 202 [255]
203 203
204 204 call `hg revert` with --all
205 205 ---------------------------
206 206
207 207 $ hg revert --all -rtip
208 208 reverting a
209 209
210 210
211 211 Issue332: confusing message when reverting directory
212 212 ----------------------------------------------------
213 213
214 214 $ hg ci -A -m b
215 215 adding b/b
216 216 created new head
217 217 $ echo foobar > b/b
218 218 $ mkdir newdir
219 219 $ echo foo > newdir/newfile
220 220 $ hg add newdir/newfile
221 221 $ hg revert b newdir
222 222 reverting b/b (glob)
223 223 forgetting newdir/newfile (glob)
224 224 $ echo foobar > b/b
225 225 $ hg revert .
226 226 reverting b/b (glob)
227 227
228 228
229 229 reverting a rename target should revert the source
230 230 --------------------------------------------------
231 231
232 232 $ hg mv a newa
233 233 $ hg revert newa
234 234 $ hg st a newa
235 235 ? newa
236 236
237 237 $ cd ..
238 238
239 239 $ hg init ignored
240 240 $ cd ignored
241 241 $ echo '^ignored$' > .hgignore
242 242 $ echo '^ignoreddir$' >> .hgignore
243 243 $ echo '^removed$' >> .hgignore
244 244
245 245 $ mkdir ignoreddir
246 246 $ touch ignoreddir/file
247 247 $ touch ignoreddir/removed
248 248 $ touch ignored
249 249 $ touch removed
250 250
251 251 4 ignored files (we will add/commit everything)
252 252
253 253 $ hg st -A -X .hgignore
254 254 I ignored
255 255 I ignoreddir/file
256 256 I ignoreddir/removed
257 257 I removed
258 258 $ hg ci -qAm 'add files' ignored ignoreddir/file ignoreddir/removed removed
259 259
260 260 $ echo >> ignored
261 261 $ echo >> ignoreddir/file
262 262 $ hg rm removed ignoreddir/removed
263 263
264 264 should revert ignored* and undelete *removed
265 265 --------------------------------------------
266 266
267 267 $ hg revert -a --no-backup
268 268 reverting ignored
269 269 reverting ignoreddir/file (glob)
270 270 undeleting ignoreddir/removed (glob)
271 271 undeleting removed
272 272 $ hg st -mardi
273 273
274 274 $ hg up -qC
275 275 $ echo >> ignored
276 276 $ hg rm removed
277 277
278 278 should silently revert the named files
279 279 --------------------------------------
280 280
281 281 $ hg revert --no-backup ignored removed
282 282 $ hg st -mardi
283 283
284 284 Reverting copy (issue3920)
285 285 --------------------------
286 286
287 287 someone set up us the copies
288 288
289 289 $ rm .hgignore
290 290 $ hg update -C
291 291 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
292 292 $ hg mv ignored allyour
293 293 $ hg copy removed base
294 294 $ hg commit -m rename
295 295
296 296 copies and renames, you have no chance to survive make your time (issue3920)
297 297
298 298 $ hg update '.^'
299 299 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
300 300 $ hg revert -rtip -a
301 301 adding allyour
302 302 adding base
303 303 removing ignored
304 304 $ hg status -C
305 305 A allyour
306 306 ignored
307 307 A base
308 308 removed
309 309 R ignored
310 310
311 311 Test revert of a file added by one side of the merge
312 312 ====================================================
313 313
314 314 remove any pending change
315 315
316 316 $ hg revert --all
317 317 forgetting allyour
318 318 forgetting base
319 319 undeleting ignored
320 320 $ hg purge --all --config extensions.purge=
321 321
322 322 Adds a new commit
323 323
324 324 $ echo foo > newadd
325 325 $ hg add newadd
326 326 $ hg commit -m 'other adds'
327 327 created new head
328 328
329 329
330 330 merge it with the other head
331 331
332 332 $ hg merge # merge 1 into 2
333 333 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
334 334 (branch merge, don't forget to commit)
335 335 $ hg summary
336 336 parent: 2:b8ec310b2d4e tip
337 337 other adds
338 338 parent: 1:f6180deb8fbe
339 339 rename
340 340 branch: default
341 341 commit: 2 modified, 1 removed (merge)
342 342 update: (current)
343 343
344 344 clarifies who added what
345 345
346 346 $ hg status
347 347 M allyour
348 348 M base
349 349 R ignored
350 350 $ hg status --change 'p1()'
351 351 A newadd
352 352 $ hg status --change 'p2()'
353 353 A allyour
354 354 A base
355 355 R ignored
356 356
357 357 revert file added by p1() to p1() state
358 358 -----------------------------------------
359 359
360 360 $ hg revert -r 'p1()' 'glob:newad?'
361 361 $ hg status
362 362 M allyour
363 363 M base
364 364 R ignored
365 365
366 366 revert file added by p1() to p2() state
367 367 ------------------------------------------
368 368
369 369 $ hg revert -r 'p2()' 'glob:newad?'
370 370 removing newadd
371 371 $ hg status
372 372 M allyour
373 373 M base
374 374 R ignored
375 375 R newadd
376 376
377 377 revert file added by p2() to p2() state
378 378 ------------------------------------------
379 379
380 380 $ hg revert -r 'p2()' 'glob:allyou?'
381 381 $ hg status
382 382 M allyour
383 383 M base
384 384 R ignored
385 385 R newadd
386 386
387 387 revert file added by p2() to p1() state
388 388 ------------------------------------------
389 389
390 390 $ hg revert -r 'p1()' 'glob:allyou?'
391 391 removing allyour
392 392 $ hg status
393 393 M base
394 394 R allyour
395 395 R ignored
396 396 R newadd
397 397
398 398 Systematic behavior validation of most possible cases
399 399 =====================================================
400 400
401 401 This section tests most of the possible combinations of working directory
402 402 changes and inter-revision changes. The number of possible cases is significant
403 403 but they all have a slighly different handling. So this section commits to
404 404 generating and testing all of them to allow safe refactoring of the revert code.
405 405
406 406 A python script is used to generate a file history for each combination of
407 407 changes between, on one side the working directory and its parent and on
408 408 the other side, changes between a revert target (--rev) and working directory
409 409 parent. The three states generated are:
410 410
411 411 - a "base" revision
412 412 - a "parent" revision
413 413 - the working directory (based on "parent")
414 414
415 415 The file generated have names of the form:
416 416
417 417 <changeset-state>_<working-copy-state>
418 418
419 419 Here, "changeset-state" conveys the state in "base" and "parent" (or the change
420 420 that happen between them), "working-copy-state" is self explanatory.
421 421
422 422 All known states are not tested yet. See inline documentation for details.
423 423 Special cases from merge and rename are not tested by this section.
424 424
425 425 There are also multiple cases where the current revert implementation is known to
426 426 slightly misbehave.
427 427
428 428 Write the python script to disk
429 429 -------------------------------
430 430
431 431 $ cat << EOF > gen-revert-cases.py
432 432 > # generate proper file state to test revert behavior
433 433 > import sys
434 434 > import os
435 435 >
436 436 > # content of the file in "base" and "parent"
437 437 > # None means no file at all
438 438 > ctxcontent = {
439 439 > # clean: no change from base to parent
440 440 > 'clean': ['base', 'base'],
441 441 > # modified: file content change from base to parent
442 442 > 'modified': ['base', 'parent'],
443 443 > # added: file is missing from base and added in parent
444 444 > 'added': [None, 'parent'],
445 445 > # removed: file exist in base but is removed from parent
446 446 > 'removed': ['base', None],
447 447 > # file exist neither in base not in parent
448 448 > 'missing': [None, None],
449 449 > }
450 450 >
451 451 > # content of file in working copy
452 452 > wccontent = {
453 453 > # clean: wc content is the same as parent
454 454 > 'clean': lambda cc: cc[1],
455 455 > # revert: wc content is the same as base
456 456 > 'revert': lambda cc: cc[0],
457 457 > # wc: file exist with a content different from base and parent
458 458 > 'wc': lambda cc: 'wc',
459 459 > # removed: file is missing and marked as untracked
460 460 > 'removed': lambda cc: None,
461 461 > # deleted: file is recorded as tracked but missing
462 462 > # rely on file deletion outside of this script
463 463 > 'deleted': lambda cc:'TOBEDELETED',
464 464 > }
465 465 > # untracked-X is a version of X where the file is not tracked (? unknown)
466 466 > wccontent['untracked-clean'] = wccontent['clean']
467 467 > wccontent['untracked-revert'] = wccontent['revert']
468 468 > wccontent['untracked-wc'] = wccontent['wc']
469 469 >
470 470 > # build the combination of possible states
471 471 > combination = []
472 472 > for ctxkey in ctxcontent:
473 473 > for wckey in wccontent:
474 474 > filename = "%s_%s" % (ctxkey, wckey)
475 475 > combination.append((filename, ctxkey, wckey))
476 476 >
477 477 > # make sure we have stable output
478 478 > combination.sort()
479 479 >
480 480 > # retrieve the state we must generate
481 481 > target = sys.argv[1]
482 482 >
483 483 > # compute file content
484 484 > content = []
485 485 > for filename, ctxkey, wckey in combination:
486 486 > cc = ctxcontent[ctxkey]
487 487 > if target == 'filelist':
488 488 > print filename
489 489 > elif target == 'base':
490 490 > content.append((filename, cc[0]))
491 491 > elif target == 'parent':
492 492 > content.append((filename, cc[1]))
493 493 > elif target == 'wc':
494 494 > content.append((filename, wccontent[wckey](cc)))
495 495 > else:
496 496 > print >> sys.stderr, "unknown target:", target
497 497 > sys.exit(1)
498 498 >
499 499 > # write actual content
500 500 > for filename, data in content:
501 501 > if data is not None:
502 502 > f = open(filename, 'w')
503 503 > f.write(data + '\n')
504 504 > f.close()
505 505 > elif os.path.exists(filename):
506 506 > os.remove(filename)
507 507 > EOF
508 508
509 509 check list of planned files
510 510
511 511 $ python gen-revert-cases.py filelist
512 512 added_clean
513 513 added_deleted
514 514 added_removed
515 515 added_revert
516 516 added_untracked-clean
517 517 added_untracked-revert
518 518 added_untracked-wc
519 519 added_wc
520 520 clean_clean
521 521 clean_deleted
522 522 clean_removed
523 523 clean_revert
524 524 clean_untracked-clean
525 525 clean_untracked-revert
526 526 clean_untracked-wc
527 527 clean_wc
528 528 missing_clean
529 529 missing_deleted
530 530 missing_removed
531 531 missing_revert
532 532 missing_untracked-clean
533 533 missing_untracked-revert
534 534 missing_untracked-wc
535 535 missing_wc
536 536 modified_clean
537 537 modified_deleted
538 538 modified_removed
539 539 modified_revert
540 540 modified_untracked-clean
541 541 modified_untracked-revert
542 542 modified_untracked-wc
543 543 modified_wc
544 544 removed_clean
545 545 removed_deleted
546 546 removed_removed
547 547 removed_revert
548 548 removed_untracked-clean
549 549 removed_untracked-revert
550 550 removed_untracked-wc
551 551 removed_wc
552 552
553 553 Script to make a simple text version of the content
554 554 ---------------------------------------------------
555 555
556 556 $ cat << EOF >> dircontent.py
557 557 > # generate a simple text view of the directory for easy comparison
558 558 > import os
559 559 > files = os.listdir('.')
560 560 > files.sort()
561 561 > for filename in files:
562 562 > if os.path.isdir(filename):
563 563 > continue
564 564 > content = open(filename).read()
565 565 > print '%-6s %s' % (content.strip(), filename)
566 566 > EOF
567 567
568 568 Generate appropriate repo state
569 569 -------------------------------
570 570
571 571 $ hg init revert-ref
572 572 $ cd revert-ref
573 573
574 574 Generate base changeset
575 575
576 576 $ python ../gen-revert-cases.py base
577 577 $ hg addremove --similarity 0
578 578 adding clean_clean
579 579 adding clean_deleted
580 580 adding clean_removed
581 581 adding clean_revert
582 582 adding clean_untracked-clean
583 583 adding clean_untracked-revert
584 584 adding clean_untracked-wc
585 585 adding clean_wc
586 586 adding modified_clean
587 587 adding modified_deleted
588 588 adding modified_removed
589 589 adding modified_revert
590 590 adding modified_untracked-clean
591 591 adding modified_untracked-revert
592 592 adding modified_untracked-wc
593 593 adding modified_wc
594 594 adding removed_clean
595 595 adding removed_deleted
596 596 adding removed_removed
597 597 adding removed_revert
598 598 adding removed_untracked-clean
599 599 adding removed_untracked-revert
600 600 adding removed_untracked-wc
601 601 adding removed_wc
602 602 $ hg status
603 603 A clean_clean
604 604 A clean_deleted
605 605 A clean_removed
606 606 A clean_revert
607 607 A clean_untracked-clean
608 608 A clean_untracked-revert
609 609 A clean_untracked-wc
610 610 A clean_wc
611 611 A modified_clean
612 612 A modified_deleted
613 613 A modified_removed
614 614 A modified_revert
615 615 A modified_untracked-clean
616 616 A modified_untracked-revert
617 617 A modified_untracked-wc
618 618 A modified_wc
619 619 A removed_clean
620 620 A removed_deleted
621 621 A removed_removed
622 622 A removed_revert
623 623 A removed_untracked-clean
624 624 A removed_untracked-revert
625 625 A removed_untracked-wc
626 626 A removed_wc
627 627 $ hg commit -m 'base'
628 628
629 629 (create a simple text version of the content)
630 630
631 631 $ python ../dircontent.py > ../content-base.txt
632 632 $ cat ../content-base.txt
633 633 base clean_clean
634 634 base clean_deleted
635 635 base clean_removed
636 636 base clean_revert
637 637 base clean_untracked-clean
638 638 base clean_untracked-revert
639 639 base clean_untracked-wc
640 640 base clean_wc
641 641 base modified_clean
642 642 base modified_deleted
643 643 base modified_removed
644 644 base modified_revert
645 645 base modified_untracked-clean
646 646 base modified_untracked-revert
647 647 base modified_untracked-wc
648 648 base modified_wc
649 649 base removed_clean
650 650 base removed_deleted
651 651 base removed_removed
652 652 base removed_revert
653 653 base removed_untracked-clean
654 654 base removed_untracked-revert
655 655 base removed_untracked-wc
656 656 base removed_wc
657 657
658 658 Create parent changeset
659 659
660 660 $ python ../gen-revert-cases.py parent
661 661 $ hg addremove --similarity 0
662 662 adding added_clean
663 663 adding added_deleted
664 664 adding added_removed
665 665 adding added_revert
666 666 adding added_untracked-clean
667 667 adding added_untracked-revert
668 668 adding added_untracked-wc
669 669 adding added_wc
670 670 removing removed_clean
671 671 removing removed_deleted
672 672 removing removed_removed
673 673 removing removed_revert
674 674 removing removed_untracked-clean
675 675 removing removed_untracked-revert
676 676 removing removed_untracked-wc
677 677 removing removed_wc
678 678 $ hg status
679 679 M modified_clean
680 680 M modified_deleted
681 681 M modified_removed
682 682 M modified_revert
683 683 M modified_untracked-clean
684 684 M modified_untracked-revert
685 685 M modified_untracked-wc
686 686 M modified_wc
687 687 A added_clean
688 688 A added_deleted
689 689 A added_removed
690 690 A added_revert
691 691 A added_untracked-clean
692 692 A added_untracked-revert
693 693 A added_untracked-wc
694 694 A added_wc
695 695 R removed_clean
696 696 R removed_deleted
697 697 R removed_removed
698 698 R removed_revert
699 699 R removed_untracked-clean
700 700 R removed_untracked-revert
701 701 R removed_untracked-wc
702 702 R removed_wc
703 703 $ hg commit -m 'parent'
704 704
705 705 (create a simple text version of the content)
706 706
707 707 $ python ../dircontent.py > ../content-parent.txt
708 708 $ cat ../content-parent.txt
709 709 parent added_clean
710 710 parent added_deleted
711 711 parent added_removed
712 712 parent added_revert
713 713 parent added_untracked-clean
714 714 parent added_untracked-revert
715 715 parent added_untracked-wc
716 716 parent added_wc
717 717 base clean_clean
718 718 base clean_deleted
719 719 base clean_removed
720 720 base clean_revert
721 721 base clean_untracked-clean
722 722 base clean_untracked-revert
723 723 base clean_untracked-wc
724 724 base clean_wc
725 725 parent modified_clean
726 726 parent modified_deleted
727 727 parent modified_removed
728 728 parent modified_revert
729 729 parent modified_untracked-clean
730 730 parent modified_untracked-revert
731 731 parent modified_untracked-wc
732 732 parent modified_wc
733 733
734 734 Setup working directory
735 735
736 $ python ../gen-revert-cases.py wc | cat
736 $ python ../gen-revert-cases.py wc
737 737 $ hg addremove --similarity 0
738 738 removing added_removed
739 739 removing added_revert
740 740 removing added_untracked-revert
741 741 removing clean_removed
742 742 adding missing_deleted
743 743 adding missing_untracked-wc
744 744 adding missing_wc
745 745 removing modified_removed
746 746 adding removed_deleted
747 747 adding removed_revert
748 748 adding removed_untracked-revert
749 749 adding removed_untracked-wc
750 750 adding removed_wc
751 751 $ hg forget *untracked*
752 752 $ rm *deleted*
753 753 $ hg status
754 754 M added_wc
755 755 M clean_wc
756 756 M modified_revert
757 757 M modified_wc
758 758 A missing_wc
759 759 A removed_revert
760 760 A removed_wc
761 761 R added_removed
762 762 R added_revert
763 763 R added_untracked-clean
764 764 R added_untracked-revert
765 765 R added_untracked-wc
766 766 R clean_removed
767 767 R clean_untracked-clean
768 768 R clean_untracked-revert
769 769 R clean_untracked-wc
770 770 R modified_removed
771 771 R modified_untracked-clean
772 772 R modified_untracked-revert
773 773 R modified_untracked-wc
774 774 ! added_deleted
775 775 ! clean_deleted
776 776 ! missing_deleted
777 777 ! modified_deleted
778 778 ! removed_deleted
779 779 ? missing_untracked-wc
780 780 ? removed_untracked-revert
781 781 ? removed_untracked-wc
782 782
783 783 $ hg status --rev 'desc("base")'
784 784 M clean_wc
785 785 M modified_clean
786 786 M modified_wc
787 787 M removed_wc
788 788 A added_clean
789 789 A added_wc
790 790 A missing_wc
791 791 R clean_removed
792 792 R clean_untracked-clean
793 793 R clean_untracked-revert
794 794 R clean_untracked-wc
795 795 R modified_removed
796 796 R modified_untracked-clean
797 797 R modified_untracked-revert
798 798 R modified_untracked-wc
799 799 R removed_clean
800 800 R removed_deleted
801 801 R removed_removed
802 802 R removed_untracked-clean
803 803 R removed_untracked-revert
804 804 R removed_untracked-wc
805 805 ! added_deleted
806 806 ! clean_deleted
807 807 ! missing_deleted
808 808 ! modified_deleted
809 809 ! removed_deleted
810 810 ? missing_untracked-wc
811 811
812 812 (create a simple text version of the content)
813 813
814 814 $ python ../dircontent.py > ../content-wc.txt
815 815 $ cat ../content-wc.txt
816 816 parent added_clean
817 817 parent added_untracked-clean
818 818 wc added_untracked-wc
819 819 wc added_wc
820 820 base clean_clean
821 821 base clean_revert
822 822 base clean_untracked-clean
823 823 base clean_untracked-revert
824 824 wc clean_untracked-wc
825 825 wc clean_wc
826 826 wc missing_untracked-wc
827 827 wc missing_wc
828 828 parent modified_clean
829 829 base modified_revert
830 830 parent modified_untracked-clean
831 831 base modified_untracked-revert
832 832 wc modified_untracked-wc
833 833 wc modified_wc
834 834 base removed_revert
835 835 base removed_untracked-revert
836 836 wc removed_untracked-wc
837 837 wc removed_wc
838 838
839 839 $ cd ..
840 840
841 841 Test revert --all to parent content
842 842 -----------------------------------
843 843
844 844 (setup from reference repo)
845 845
846 846 $ cp -r revert-ref revert-parent-all
847 847 $ cd revert-parent-all
848 848
849 849 check revert output
850 850
851 851 $ hg revert --all
852 852 reverting added_deleted
853 853 undeleting added_removed
854 854 undeleting added_revert
855 855 undeleting added_untracked-clean
856 856 undeleting added_untracked-revert
857 857 undeleting added_untracked-wc
858 858 reverting added_wc
859 859 reverting clean_deleted
860 860 undeleting clean_removed
861 861 undeleting clean_untracked-clean
862 862 undeleting clean_untracked-revert
863 863 undeleting clean_untracked-wc
864 864 reverting clean_wc
865 865 forgetting missing_deleted
866 866 forgetting missing_wc
867 867 reverting modified_deleted
868 868 undeleting modified_removed
869 869 reverting modified_revert
870 870 undeleting modified_untracked-clean
871 871 undeleting modified_untracked-revert
872 872 undeleting modified_untracked-wc
873 873 reverting modified_wc
874 874 forgetting removed_deleted
875 875 forgetting removed_revert
876 876 forgetting removed_wc
877 877
878 878 Compare resulting directory with revert target.
879 879
880 880 The diff is filtered to include change only. The only difference should be
881 881 additional `.orig` backup file when applicable.
882 882
883 883 $ python ../dircontent.py > ../content-parent-all.txt
884 884 $ cd ..
885 885 $ diff -U 0 -- content-parent.txt content-parent-all.txt | grep _
886 886 +wc added_untracked-wc.orig
887 887 +wc added_wc.orig
888 888 +wc clean_untracked-wc.orig
889 889 +wc clean_wc.orig
890 890 +wc missing_untracked-wc
891 891 +wc missing_wc
892 892 +base modified_revert.orig
893 893 +base modified_untracked-revert.orig
894 894 +wc modified_untracked-wc.orig
895 895 +wc modified_wc.orig
896 896 +base removed_revert
897 897 +base removed_untracked-revert
898 898 +wc removed_untracked-wc
899 899 +wc removed_wc
900 900
901 901 Test revert --all to "base" content
902 902 -----------------------------------
903 903
904 904 (setup from reference repo)
905 905
906 906 $ cp -r revert-ref revert-base-all
907 907 $ cd revert-base-all
908 908
909 909 check revert output
910 910
911 911 $ hg revert --all --rev 'desc(base)'
912 912 removing added_clean
913 913 removing added_deleted
914 914 removing added_wc
915 915 reverting clean_deleted
916 916 undeleting clean_removed
917 917 undeleting clean_untracked-clean
918 918 undeleting clean_untracked-revert
919 919 undeleting clean_untracked-wc
920 920 reverting clean_wc
921 921 forgetting missing_deleted
922 922 forgetting missing_wc
923 923 reverting modified_clean
924 924 reverting modified_deleted
925 925 undeleting modified_removed
926 926 undeleting modified_untracked-clean
927 927 undeleting modified_untracked-revert
928 928 undeleting modified_untracked-wc
929 929 reverting modified_wc
930 930 adding removed_clean
931 931 reverting removed_deleted
932 932 adding removed_removed
933 933 adding removed_untracked-clean
934 934 adding removed_untracked-revert
935 935 adding removed_untracked-wc
936 936 reverting removed_wc
937 937
938 938 Compare resulting directory with revert target.
939 939
940 940 The diff is filtered to include change only. The only difference should be
941 941 additional `.orig` backup file when applicable.
942 942
943 943 $ python ../dircontent.py > ../content-base-all.txt
944 944 $ cd ..
945 945 $ diff -U 0 -- content-base.txt content-base-all.txt | grep _
946 946 +parent added_untracked-clean
947 947 +wc added_untracked-wc
948 948 +wc added_wc.orig
949 949 +wc clean_untracked-wc.orig
950 950 +wc clean_wc.orig
951 951 +wc missing_untracked-wc
952 952 +wc missing_wc
953 953 +parent modified_untracked-clean.orig
954 954 +wc modified_untracked-wc.orig
955 955 +wc modified_wc.orig
956 956 +wc removed_untracked-wc.orig
957 957 +wc removed_wc.orig
958 958
959 959 Test revert to parent content with explicit file name
960 960 -----------------------------------------------------
961 961
962 962 (setup from reference repo)
963 963
964 964 $ cp -r revert-ref revert-parent-explicit
965 965 $ cd revert-parent-explicit
966 966
967 967 revert all files individually and check the output
968 968 (output is expected to be different than in the --all case)
969 969
970 970 $ for file in `python ../gen-revert-cases.py filelist`; do
971 971 > echo '### revert for:' $file;
972 972 > hg revert $file;
973 973 > echo
974 974 > done
975 975 ### revert for: added_clean
976 976 no changes needed to added_clean
977 977
978 978 ### revert for: added_deleted
979 979
980 980 ### revert for: added_removed
981 981
982 982 ### revert for: added_revert
983 983
984 984 ### revert for: added_untracked-clean
985 985
986 986 ### revert for: added_untracked-revert
987 987
988 988 ### revert for: added_untracked-wc
989 989
990 990 ### revert for: added_wc
991 991
992 992 ### revert for: clean_clean
993 993 no changes needed to clean_clean
994 994
995 995 ### revert for: clean_deleted
996 996
997 997 ### revert for: clean_removed
998 998
999 999 ### revert for: clean_revert
1000 1000 no changes needed to clean_revert
1001 1001
1002 1002 ### revert for: clean_untracked-clean
1003 1003
1004 1004 ### revert for: clean_untracked-revert
1005 1005
1006 1006 ### revert for: clean_untracked-wc
1007 1007
1008 1008 ### revert for: clean_wc
1009 1009
1010 1010 ### revert for: missing_clean
1011 1011 missing_clean: no such file in rev * (glob)
1012 1012
1013 1013 ### revert for: missing_deleted
1014 1014
1015 1015 ### revert for: missing_removed
1016 1016 missing_removed: no such file in rev * (glob)
1017 1017
1018 1018 ### revert for: missing_revert
1019 1019 missing_revert: no such file in rev * (glob)
1020 1020
1021 1021 ### revert for: missing_untracked-clean
1022 1022 missing_untracked-clean: no such file in rev * (glob)
1023 1023
1024 1024 ### revert for: missing_untracked-revert
1025 1025 missing_untracked-revert: no such file in rev * (glob)
1026 1026
1027 1027 ### revert for: missing_untracked-wc
1028 1028 file not managed: missing_untracked-wc
1029 1029
1030 1030 ### revert for: missing_wc
1031 1031
1032 1032 ### revert for: modified_clean
1033 1033 no changes needed to modified_clean
1034 1034
1035 1035 ### revert for: modified_deleted
1036 1036
1037 1037 ### revert for: modified_removed
1038 1038
1039 1039 ### revert for: modified_revert
1040 1040
1041 1041 ### revert for: modified_untracked-clean
1042 1042
1043 1043 ### revert for: modified_untracked-revert
1044 1044
1045 1045 ### revert for: modified_untracked-wc
1046 1046
1047 1047 ### revert for: modified_wc
1048 1048
1049 1049 ### revert for: removed_clean
1050 1050 removed_clean: no such file in rev * (glob)
1051 1051
1052 1052 ### revert for: removed_deleted
1053 1053
1054 1054 ### revert for: removed_removed
1055 1055 removed_removed: no such file in rev * (glob)
1056 1056
1057 1057 ### revert for: removed_revert
1058 1058
1059 1059 ### revert for: removed_untracked-clean
1060 1060 removed_untracked-clean: no such file in rev * (glob)
1061 1061
1062 1062 ### revert for: removed_untracked-revert
1063 1063 file not managed: removed_untracked-revert
1064 1064
1065 1065 ### revert for: removed_untracked-wc
1066 1066 file not managed: removed_untracked-wc
1067 1067
1068 1068 ### revert for: removed_wc
1069 1069
1070 1070
1071 1071 check resulting directory againt the --all run
1072 1072 (There should be no difference)
1073 1073
1074 1074 $ python ../dircontent.py > ../content-parent-explicit.txt
1075 1075 $ cd ..
1076 1076 $ diff -U 0 -- content-parent-all.txt content-parent-explicit.txt | grep _
1077 1077 [1]
1078 1078
1079 1079 Test revert to "base" content with explicit file name
1080 1080 -----------------------------------------------------
1081 1081
1082 1082 (setup from reference repo)
1083 1083
1084 1084 $ cp -r revert-ref revert-base-explicit
1085 1085 $ cd revert-base-explicit
1086 1086
1087 1087 revert all files individually and check the output
1088 1088 (output is expected to be different than in the --all case)
1089 1089
1090 1090 $ for file in `python ../gen-revert-cases.py filelist`; do
1091 1091 > echo '### revert for:' $file;
1092 1092 > hg revert $file --rev 'desc(base)';
1093 1093 > echo
1094 1094 > done
1095 1095 ### revert for: added_clean
1096 1096
1097 1097 ### revert for: added_deleted
1098 1098
1099 1099 ### revert for: added_removed
1100 1100 no changes needed to added_removed
1101 1101
1102 1102 ### revert for: added_revert
1103 1103 no changes needed to added_revert
1104 1104
1105 1105 ### revert for: added_untracked-clean
1106 1106 no changes needed to added_untracked-clean
1107 1107
1108 1108 ### revert for: added_untracked-revert
1109 1109 no changes needed to added_untracked-revert
1110 1110
1111 1111 ### revert for: added_untracked-wc
1112 1112 no changes needed to added_untracked-wc
1113 1113
1114 1114 ### revert for: added_wc
1115 1115
1116 1116 ### revert for: clean_clean
1117 1117 no changes needed to clean_clean
1118 1118
1119 1119 ### revert for: clean_deleted
1120 1120
1121 1121 ### revert for: clean_removed
1122 1122
1123 1123 ### revert for: clean_revert
1124 1124 no changes needed to clean_revert
1125 1125
1126 1126 ### revert for: clean_untracked-clean
1127 1127
1128 1128 ### revert for: clean_untracked-revert
1129 1129
1130 1130 ### revert for: clean_untracked-wc
1131 1131
1132 1132 ### revert for: clean_wc
1133 1133
1134 1134 ### revert for: missing_clean
1135 1135 missing_clean: no such file in rev * (glob)
1136 1136
1137 1137 ### revert for: missing_deleted
1138 1138
1139 1139 ### revert for: missing_removed
1140 1140 missing_removed: no such file in rev * (glob)
1141 1141
1142 1142 ### revert for: missing_revert
1143 1143 missing_revert: no such file in rev * (glob)
1144 1144
1145 1145 ### revert for: missing_untracked-clean
1146 1146 missing_untracked-clean: no such file in rev * (glob)
1147 1147
1148 1148 ### revert for: missing_untracked-revert
1149 1149 missing_untracked-revert: no such file in rev * (glob)
1150 1150
1151 1151 ### revert for: missing_untracked-wc
1152 1152 file not managed: missing_untracked-wc
1153 1153
1154 1154 ### revert for: missing_wc
1155 1155
1156 1156 ### revert for: modified_clean
1157 1157
1158 1158 ### revert for: modified_deleted
1159 1159
1160 1160 ### revert for: modified_removed
1161 1161
1162 1162 ### revert for: modified_revert
1163 1163 no changes needed to modified_revert
1164 1164
1165 1165 ### revert for: modified_untracked-clean
1166 1166
1167 1167 ### revert for: modified_untracked-revert
1168 1168
1169 1169 ### revert for: modified_untracked-wc
1170 1170
1171 1171 ### revert for: modified_wc
1172 1172
1173 1173 ### revert for: removed_clean
1174 1174
1175 1175 ### revert for: removed_deleted
1176 1176
1177 1177 ### revert for: removed_removed
1178 1178
1179 1179 ### revert for: removed_revert
1180 1180 no changes needed to removed_revert
1181 1181
1182 1182 ### revert for: removed_untracked-clean
1183 1183
1184 1184 ### revert for: removed_untracked-revert
1185 1185
1186 1186 ### revert for: removed_untracked-wc
1187 1187
1188 1188 ### revert for: removed_wc
1189 1189
1190 1190
1191 1191 check resulting directory againt the --all run
1192 1192 (There should be no difference)
1193 1193
1194 1194 $ python ../dircontent.py > ../content-base-explicit.txt
1195 1195 $ cd ..
1196 1196 $ diff -U 0 -- content-base-all.txt content-base-explicit.txt | grep _
1197 1197 [1]
General Comments 0
You need to be logged in to leave comments. Login now