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