##// END OF EJS Templates
run-tests.py: add a default --date "0 0" argument to commit et al...
Alexis S. L. Carvalho -
r5524:453acf64 default
parent child Browse files
Show More
@@ -1,576 +1,581 b''
1 1 #!/usr/bin/env python
2 2 #
3 3 # run-tests.py - Run a set of tests on Mercurial
4 4 #
5 5 # Copyright 2006 Matt Mackall <mpm@selenic.com>
6 6 #
7 7 # This software may be used and distributed according to the terms
8 8 # of the GNU General Public License, incorporated herein by reference.
9 9
10 10 import difflib
11 11 import errno
12 12 import optparse
13 13 import os
14 14 import popen2
15 15 import re
16 16 import shutil
17 17 import signal
18 18 import sys
19 19 import tempfile
20 20 import time
21 21
22 22 # hghave reserved exit code to skip test
23 23 SKIPPED_STATUS = 80
24 24
25 25 required_tools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"]
26 26
27 27 parser = optparse.OptionParser("%prog [options] [tests]")
28 28 parser.add_option("-C", "--annotate", action="store_true",
29 29 help="output files annotated with coverage")
30 30 parser.add_option("--child", type="int",
31 31 help="run as child process, summary to given fd")
32 32 parser.add_option("-c", "--cover", action="store_true",
33 33 help="print a test coverage report")
34 34 parser.add_option("-f", "--first", action="store_true",
35 35 help="exit on the first test failure")
36 36 parser.add_option("-i", "--interactive", action="store_true",
37 37 help="prompt to accept changed output")
38 38 parser.add_option("-j", "--jobs", type="int",
39 39 help="number of jobs to run in parallel")
40 40 parser.add_option("-R", "--restart", action="store_true",
41 41 help="restart at last error")
42 42 parser.add_option("-p", "--port", type="int",
43 43 help="port on which servers should listen")
44 44 parser.add_option("-r", "--retest", action="store_true",
45 45 help="retest failed tests")
46 46 parser.add_option("-s", "--cover_stdlib", action="store_true",
47 47 help="print a test coverage report inc. standard libraries")
48 48 parser.add_option("-t", "--timeout", type="int",
49 49 help="kill errant tests after TIMEOUT seconds")
50 50 parser.add_option("--tmpdir", type="string",
51 51 help="run tests in the given temporary directory")
52 52 parser.add_option("-v", "--verbose", action="store_true",
53 53 help="output verbose messages")
54 54 parser.add_option("--with-hg", type="string",
55 55 help="test existing install at given location")
56 56
57 57 parser.set_defaults(jobs=1, port=20059, timeout=180)
58 58 (options, args) = parser.parse_args()
59 59 verbose = options.verbose
60 60 coverage = options.cover or options.cover_stdlib or options.annotate
61 61 python = sys.executable
62 62
63 63 if options.jobs < 1:
64 64 print >> sys.stderr, 'ERROR: -j/--jobs must be positive'
65 65 sys.exit(1)
66 66 if options.interactive and options.jobs > 1:
67 67 print >> sys.stderr, 'ERROR: cannot mix -interactive and --jobs > 1'
68 68 sys.exit(1)
69 69
70 70 def vlog(*msg):
71 71 if verbose:
72 72 for m in msg:
73 73 print m,
74 74 print
75 75
76 76 def splitnewlines(text):
77 77 '''like str.splitlines, but only split on newlines.
78 78 keep line endings.'''
79 79 i = 0
80 80 lines = []
81 81 while True:
82 82 n = text.find('\n', i)
83 83 if n == -1:
84 84 last = text[i:]
85 85 if last:
86 86 lines.append(last)
87 87 return lines
88 88 lines.append(text[i:n+1])
89 89 i = n + 1
90 90
91 91 def extract_missing_features(lines):
92 92 '''Extract missing/unknown features log lines as a list'''
93 93 missing = []
94 94 for line in lines:
95 95 if not line.startswith('hghave: '):
96 96 continue
97 97 line = line.splitlines()[0]
98 98 missing.append(line[8:])
99 99
100 100 return missing
101 101
102 102 def show_diff(expected, output):
103 103 for line in difflib.unified_diff(expected, output,
104 104 "Expected output", "Test output"):
105 105 sys.stdout.write(line)
106 106
107 107 def find_program(program):
108 108 """Search PATH for a executable program"""
109 109 for p in os.environ.get('PATH', os.defpath).split(os.pathsep):
110 110 name = os.path.join(p, program)
111 111 if os.access(name, os.X_OK):
112 112 return name
113 113 return None
114 114
115 115 def check_required_tools():
116 116 # Before we go any further, check for pre-requisite tools
117 117 # stuff from coreutils (cat, rm, etc) are not tested
118 118 for p in required_tools:
119 119 if os.name == 'nt':
120 120 p += '.exe'
121 121 found = find_program(p)
122 122 if found:
123 123 vlog("# Found prerequisite", p, "at", found)
124 124 else:
125 125 print "WARNING: Did not find prerequisite tool: "+p
126 126
127 127 def cleanup_exit():
128 128 if verbose:
129 129 print "# Cleaning up HGTMP", HGTMP
130 130 shutil.rmtree(HGTMP, True)
131 131
132 132 def use_correct_python():
133 133 # some tests run python interpreter. they must use same
134 134 # interpreter we use or bad things will happen.
135 135 exedir, exename = os.path.split(sys.executable)
136 136 if exename == 'python':
137 137 path = find_program('python')
138 138 if os.path.dirname(path) == exedir:
139 139 return
140 140 vlog('# Making python executable in test path use correct Python')
141 141 my_python = os.path.join(BINDIR, 'python')
142 142 try:
143 143 os.symlink(sys.executable, my_python)
144 144 except AttributeError:
145 145 # windows fallback
146 146 shutil.copyfile(sys.executable, my_python)
147 147 shutil.copymode(sys.executable, my_python)
148 148
149 149 def install_hg():
150 150 global python
151 151 vlog("# Performing temporary installation of HG")
152 152 installerrs = os.path.join("tests", "install.err")
153 153
154 154 # Run installer in hg root
155 155 os.chdir(os.path.join(os.path.dirname(sys.argv[0]), '..'))
156 156 cmd = ('%s setup.py clean --all'
157 157 ' install --force --home="%s" --install-lib="%s"'
158 158 ' --install-scripts="%s" >%s 2>&1'
159 159 % (sys.executable, INST, PYTHONDIR, BINDIR, installerrs))
160 160 vlog("# Running", cmd)
161 161 if os.system(cmd) == 0:
162 162 if not verbose:
163 163 os.remove(installerrs)
164 164 else:
165 165 f = open(installerrs)
166 166 for line in f:
167 167 print line,
168 168 f.close()
169 169 sys.exit(1)
170 170 os.chdir(TESTDIR)
171 171
172 172 os.environ["PATH"] = "%s%s%s" % (BINDIR, os.pathsep, os.environ["PATH"])
173 173
174 174 pydir = os.pathsep.join([PYTHONDIR, TESTDIR])
175 175 pythonpath = os.environ.get("PYTHONPATH")
176 176 if pythonpath:
177 177 pythonpath = pydir + os.pathsep + pythonpath
178 178 else:
179 179 pythonpath = pydir
180 180 os.environ["PYTHONPATH"] = pythonpath
181 181
182 182 use_correct_python()
183 183
184 184 if coverage:
185 185 vlog("# Installing coverage wrapper")
186 186 os.environ['COVERAGE_FILE'] = COVERAGE_FILE
187 187 if os.path.exists(COVERAGE_FILE):
188 188 os.unlink(COVERAGE_FILE)
189 189 # Create a wrapper script to invoke hg via coverage.py
190 190 os.rename(os.path.join(BINDIR, "hg"), os.path.join(BINDIR, "_hg.py"))
191 191 f = open(os.path.join(BINDIR, 'hg'), 'w')
192 192 f.write('#!' + sys.executable + '\n')
193 193 f.write('import sys, os; os.execv(sys.executable, [sys.executable, '
194 194 '"%s", "-x", "%s"] + sys.argv[1:])\n' %
195 195 (os.path.join(TESTDIR, 'coverage.py'),
196 196 os.path.join(BINDIR, '_hg.py')))
197 197 f.close()
198 198 os.chmod(os.path.join(BINDIR, 'hg'), 0700)
199 199 python = '"%s" "%s" -x' % (sys.executable,
200 200 os.path.join(TESTDIR,'coverage.py'))
201 201
202 202 def output_coverage():
203 203 vlog("# Producing coverage report")
204 204 omit = [BINDIR, TESTDIR, PYTHONDIR]
205 205 if not options.cover_stdlib:
206 206 # Exclude as system paths (ignoring empty strings seen on win)
207 207 omit += [x for x in sys.path if x != '']
208 208 omit = ','.join(omit)
209 209 os.chdir(PYTHONDIR)
210 210 cmd = '"%s" "%s" -i -r "--omit=%s"' % (
211 211 sys.executable, os.path.join(TESTDIR, 'coverage.py'), omit)
212 212 vlog("# Running: "+cmd)
213 213 os.system(cmd)
214 214 if options.annotate:
215 215 adir = os.path.join(TESTDIR, 'annotated')
216 216 if not os.path.isdir(adir):
217 217 os.mkdir(adir)
218 218 cmd = '"%s" "%s" -i -a "--directory=%s" "--omit=%s"' % (
219 219 sys.executable, os.path.join(TESTDIR, 'coverage.py'),
220 220 adir, omit)
221 221 vlog("# Running: "+cmd)
222 222 os.system(cmd)
223 223
224 224 class Timeout(Exception):
225 225 pass
226 226
227 227 def alarmed(signum, frame):
228 228 raise Timeout
229 229
230 230 def run(cmd):
231 231 """Run command in a sub-process, capturing the output (stdout and stderr).
232 232 Return the exist code, and output."""
233 233 # TODO: Use subprocess.Popen if we're running on Python 2.4
234 234 if os.name == 'nt':
235 235 tochild, fromchild = os.popen4(cmd)
236 236 tochild.close()
237 237 output = fromchild.read()
238 238 ret = fromchild.close()
239 239 if ret == None:
240 240 ret = 0
241 241 else:
242 242 proc = popen2.Popen4(cmd)
243 243 try:
244 244 output = ''
245 245 proc.tochild.close()
246 246 output = proc.fromchild.read()
247 247 ret = proc.wait()
248 248 if os.WIFEXITED(ret):
249 249 ret = os.WEXITSTATUS(ret)
250 250 except Timeout:
251 251 vlog('# Process %d timed out - killing it' % proc.pid)
252 252 os.kill(proc.pid, signal.SIGTERM)
253 253 ret = proc.wait()
254 254 if ret == 0:
255 255 ret = signal.SIGTERM << 8
256 256 output += ("\n### Abort: timeout after %d seconds.\n"
257 257 % options.timeout)
258 258 return ret, splitnewlines(output)
259 259
260 260 def run_one(test, skips):
261 261 '''tristate output:
262 262 None -> skipped
263 263 True -> passed
264 264 False -> failed'''
265 265
266 266 def skip(msg):
267 267 if not verbose:
268 268 skips.append((test, msg))
269 269 else:
270 270 print "\nSkipping %s: %s" % (test, msg)
271 271 return None
272 272
273 273 vlog("# Test", test)
274 274
275 275 # create a fresh hgrc
276 276 hgrc = file(HGRCPATH, 'w+')
277 277 hgrc.write('[ui]\n')
278 278 hgrc.write('slash = True\n')
279 hgrc.write('[defaults]\n')
280 hgrc.write('backout = -d "0 0"\n')
281 hgrc.write('commit = -d "0 0"\n')
282 hgrc.write('debugrawcommit = -d "0 0"\n')
283 hgrc.write('tag = -d "0 0"\n')
279 284 hgrc.close()
280 285
281 286 err = os.path.join(TESTDIR, test+".err")
282 287 ref = os.path.join(TESTDIR, test+".out")
283 288 testpath = os.path.join(TESTDIR, test)
284 289
285 290 if os.path.exists(err):
286 291 os.remove(err) # Remove any previous output files
287 292
288 293 # Make a tmp subdirectory to work in
289 294 tmpd = os.path.join(HGTMP, test)
290 295 os.mkdir(tmpd)
291 296 os.chdir(tmpd)
292 297
293 298 try:
294 299 tf = open(testpath)
295 300 firstline = tf.readline().rstrip()
296 301 tf.close()
297 302 except:
298 303 firstline = ''
299 304 lctest = test.lower()
300 305
301 306 if lctest.endswith('.py') or firstline == '#!/usr/bin/env python':
302 307 cmd = '%s "%s"' % (python, testpath)
303 308 elif lctest.endswith('.bat'):
304 309 # do not run batch scripts on non-windows
305 310 if os.name != 'nt':
306 311 return skip("batch script")
307 312 # To reliably get the error code from batch files on WinXP,
308 313 # the "cmd /c call" prefix is needed. Grrr
309 314 cmd = 'cmd /c call "%s"' % testpath
310 315 else:
311 316 # do not run shell scripts on windows
312 317 if os.name == 'nt':
313 318 return skip("shell script")
314 319 # do not try to run non-executable programs
315 320 if not os.access(testpath, os.X_OK):
316 321 return skip("not executable")
317 322 cmd = '"%s"' % testpath
318 323
319 324 if options.timeout > 0:
320 325 signal.alarm(options.timeout)
321 326
322 327 vlog("# Running", cmd)
323 328 ret, out = run(cmd)
324 329 vlog("# Ret was:", ret)
325 330
326 331 if options.timeout > 0:
327 332 signal.alarm(0)
328 333
329 334 skipped = (ret == SKIPPED_STATUS)
330 335 diffret = 0
331 336 # If reference output file exists, check test output against it
332 337 if os.path.exists(ref):
333 338 f = open(ref, "r")
334 339 ref_out = splitnewlines(f.read())
335 340 f.close()
336 341 else:
337 342 ref_out = []
338 343 if not skipped and out != ref_out:
339 344 diffret = 1
340 345 print "\nERROR: %s output changed" % (test)
341 346 show_diff(ref_out, out)
342 347 if skipped:
343 348 missing = extract_missing_features(out)
344 349 if not missing:
345 350 missing = ['irrelevant']
346 351 skip(missing[-1])
347 352 elif ret:
348 353 print "\nERROR: %s failed with error code %d" % (test, ret)
349 354 elif diffret:
350 355 ret = diffret
351 356
352 357 if not verbose:
353 358 sys.stdout.write(skipped and 's' or '.')
354 359 sys.stdout.flush()
355 360
356 361 if ret != 0 and not skipped:
357 362 # Save errors to a file for diagnosis
358 363 f = open(err, "wb")
359 364 for line in out:
360 365 f.write(line)
361 366 f.close()
362 367
363 368 # Kill off any leftover daemon processes
364 369 try:
365 370 fp = file(DAEMON_PIDS)
366 371 for line in fp:
367 372 try:
368 373 pid = int(line)
369 374 except ValueError:
370 375 continue
371 376 try:
372 377 os.kill(pid, 0)
373 378 vlog('# Killing daemon process %d' % pid)
374 379 os.kill(pid, signal.SIGTERM)
375 380 time.sleep(0.25)
376 381 os.kill(pid, 0)
377 382 vlog('# Daemon process %d is stuck - really killing it' % pid)
378 383 os.kill(pid, signal.SIGKILL)
379 384 except OSError, err:
380 385 if err.errno != errno.ESRCH:
381 386 raise
382 387 fp.close()
383 388 os.unlink(DAEMON_PIDS)
384 389 except IOError:
385 390 pass
386 391
387 392 os.chdir(TESTDIR)
388 393 shutil.rmtree(tmpd, True)
389 394 if skipped:
390 395 return None
391 396 return ret == 0
392 397
393 398 if not options.child:
394 399 os.umask(022)
395 400
396 401 check_required_tools()
397 402
398 403 # Reset some environment variables to well-known values so that
399 404 # the tests produce repeatable output.
400 405 os.environ['LANG'] = os.environ['LC_ALL'] = 'C'
401 406 os.environ['TZ'] = 'GMT'
402 407
403 408 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
404 409 HGTMP = os.environ['HGTMP'] = tempfile.mkdtemp('', 'hgtests.', options.tmpdir)
405 410 DAEMON_PIDS = None
406 411 HGRCPATH = None
407 412
408 413 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
409 414 os.environ["HGMERGE"] = ('python "%s" -L my -L other'
410 415 % os.path.join(TESTDIR, os.path.pardir,
411 416 'contrib', 'simplemerge'))
412 417 os.environ["HGUSER"] = "test"
413 418 os.environ["HGENCODING"] = "ascii"
414 419 os.environ["HGENCODINGMODE"] = "strict"
415 420 os.environ["HGPORT"] = str(options.port)
416 421 os.environ["HGPORT1"] = str(options.port + 1)
417 422 os.environ["HGPORT2"] = str(options.port + 2)
418 423
419 424 if options.with_hg:
420 425 INST = options.with_hg
421 426 else:
422 427 INST = os.path.join(HGTMP, "install")
423 428 BINDIR = os.path.join(INST, "bin")
424 429 PYTHONDIR = os.path.join(INST, "lib", "python")
425 430 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
426 431
427 432 def run_children(tests):
428 433 if not options.with_hg:
429 434 install_hg()
430 435
431 436 optcopy = dict(options.__dict__)
432 437 optcopy['jobs'] = 1
433 438 optcopy['with_hg'] = INST
434 439 opts = []
435 440 for opt, value in optcopy.iteritems():
436 441 name = '--' + opt.replace('_', '-')
437 442 if value is True:
438 443 opts.append(name)
439 444 elif value is not None:
440 445 opts.append(name + '=' + str(value))
441 446
442 447 tests.reverse()
443 448 jobs = [[] for j in xrange(options.jobs)]
444 449 while tests:
445 450 for j in xrange(options.jobs):
446 451 if not tests: break
447 452 jobs[j].append(tests.pop())
448 453 fps = {}
449 454 for j in xrange(len(jobs)):
450 455 job = jobs[j]
451 456 if not job:
452 457 continue
453 458 rfd, wfd = os.pipe()
454 459 childopts = ['--child=%d' % wfd, '--port=%d' % (options.port + j * 3)]
455 460 cmdline = [python, sys.argv[0]] + opts + childopts + job
456 461 vlog(' '.join(cmdline))
457 462 fps[os.spawnvp(os.P_NOWAIT, cmdline[0], cmdline)] = os.fdopen(rfd, 'r')
458 463 os.close(wfd)
459 464 failures = 0
460 465 tested, skipped, failed = 0, 0, 0
461 466 skips = []
462 467 while fps:
463 468 pid, status = os.wait()
464 469 fp = fps.pop(pid)
465 470 l = fp.read().splitlines()
466 471 test, skip, fail = map(int, l[:3])
467 472 for s in l[3:]:
468 473 skips.append(s.split(" ", 1))
469 474 tested += test
470 475 skipped += skip
471 476 failed += fail
472 477 vlog('pid %d exited, status %d' % (pid, status))
473 478 failures |= status
474 479 print
475 480 for s in skips:
476 481 print "Skipped %s: %s" % (s[0], s[1])
477 482 print "# Ran %d tests, %d skipped, %d failed." % (
478 483 tested, skipped, failed)
479 484 sys.exit(failures != 0)
480 485
481 486 def run_tests(tests):
482 487 global DAEMON_PIDS, HGRCPATH
483 488 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
484 489 HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
485 490
486 491 try:
487 492 if not options.with_hg:
488 493 install_hg()
489 494
490 495 if options.timeout > 0:
491 496 try:
492 497 signal.signal(signal.SIGALRM, alarmed)
493 498 vlog('# Running tests with %d-second timeout' %
494 499 options.timeout)
495 500 except AttributeError:
496 501 print 'WARNING: cannot run tests with timeouts'
497 502 options.timeout = 0
498 503
499 504 tested = 0
500 505 failed = 0
501 506 skipped = 0
502 507
503 508 if options.restart:
504 509 orig = list(tests)
505 510 while tests:
506 511 if os.path.exists(tests[0] + ".err"):
507 512 break
508 513 tests.pop(0)
509 514 if not tests:
510 515 print "running all tests"
511 516 tests = orig
512 517
513 518 skips = []
514 519 for test in tests:
515 520 if options.retest and not os.path.exists(test + ".err"):
516 521 skipped += 1
517 522 continue
518 523 ret = run_one(test, skips)
519 524 if ret is None:
520 525 skipped += 1
521 526 elif not ret:
522 527 if options.interactive:
523 528 print "Accept this change? [n] ",
524 529 answer = sys.stdin.readline().strip()
525 530 if answer.lower() in "y yes".split():
526 531 os.rename(test + ".err", test + ".out")
527 532 tested += 1
528 533 continue
529 534 failed += 1
530 535 if options.first:
531 536 break
532 537 tested += 1
533 538
534 539 if options.child:
535 540 fp = os.fdopen(options.child, 'w')
536 541 fp.write('%d\n%d\n%d\n' % (tested, skipped, failed))
537 542 for s in skips:
538 543 fp.write("%s %s\n" % s)
539 544 fp.close()
540 545 else:
541 546 print
542 547 for s in skips:
543 548 print "Skipped %s: %s" % s
544 549 print "# Ran %d tests, %d skipped, %d failed." % (
545 550 tested, skipped, failed)
546 551
547 552 if coverage:
548 553 output_coverage()
549 554 except KeyboardInterrupt:
550 555 failed = True
551 556 print "\ninterrupted!"
552 557
553 558 if failed:
554 559 sys.exit(1)
555 560
556 561 if len(args) == 0:
557 562 args = os.listdir(".")
558 563 args.sort()
559 564
560 565 tests = []
561 566 for test in args:
562 567 if (test.startswith("test-") and '~' not in test and
563 568 ('.' not in test or test.endswith('.py') or
564 569 test.endswith('.bat'))):
565 570 tests.append(test)
566 571
567 572 vlog("# Using TESTDIR", TESTDIR)
568 573 vlog("# Using HGTMP", HGTMP)
569 574
570 575 try:
571 576 if len(tests) > 1 and options.jobs > 1:
572 577 run_children(tests)
573 578 else:
574 579 run_tests(tests)
575 580 finally:
576 581 cleanup_exit()
@@ -1,128 +1,128 b''
1 1 #!/bin/sh
2 2
3 3 mkdir t
4 4 cd t
5 5 hg init
6 6 hg id
7 7 echo a > a
8 8 hg add a
9 9 hg commit -m "test" -d "1000000 0"
10 10 hg co
11 11 hg identify
12 12 T=`hg tip --debug | head -n 1 | cut -d : -f 3`
13 13 hg tag -l "This is a local tag with a really long name!"
14 14 hg tags
15 15 rm .hg/localtags
16 16 echo "$T first" > .hgtags
17 17 cat .hgtags
18 18 hg add .hgtags
19 19 hg commit -m "add tags" -d "1000000 0"
20 20 hg tags
21 21 hg identify
22 22 echo bb > a
23 23 hg status
24 24 hg identify
25 25 hg co first
26 26 hg id
27 27 hg -v id
28 28 hg status
29 29 echo 1 > b
30 30 hg add b
31 31 hg commit -m "branch" -d "1000000 0"
32 32 hg id
33 33 hg merge 1
34 34 hg id
35 35 hg status
36 36
37 37 hg commit -m "merge" -d "1000000 0"
38 38
39 39 # create fake head, make sure tag not visible afterwards
40 40 cp .hgtags tags
41 41 hg tag -d "1000000 0" last
42 42 hg rm .hgtags
43 43 hg commit -m "remove" -d "1000000 0"
44 44
45 45 mv tags .hgtags
46 46 hg add .hgtags
47 47 hg commit -m "readd" -d "1000000 0"
48 48
49 49 hg tags
50 50
51 51 # invalid tags
52 52 echo "spam" >> .hgtags
53 53 echo >> .hgtags
54 54 echo "foo bar" >> .hgtags
55 55 echo "$T invalid" | sed "s/..../a5a5/" >> .hg/localtags
56 56 hg commit -m "tags" -d "1000000 0"
57 57
58 58 # report tag parse error on other head
59 59 hg up 3
60 60 echo 'x y' >> .hgtags
61 61 hg commit -m "head" -d "1000000 0"
62 62
63 63 hg tags
64 64 hg tip
65 65
66 66 # test tag precedence rules
67 67 cd ..
68 68 hg init t2
69 69 cd t2
70 70 echo foo > foo
71 71 hg add foo
72 72 hg ci -m 'add foo' -d '1000000 0' # rev 0
73 73 hg tag -d '1000000 0' bar # rev 1
74 74 echo >> foo
75 75 hg ci -m 'change foo 1' -d '1000000 0' # rev 2
76 76 hg up -C 1
77 77 hg tag -r 1 -d '1000000 0' -f bar # rev 3
78 78 hg up -C 1
79 79 echo >> foo
80 80 hg ci -m 'change foo 2' -d '1000000 0' # rev 4
81 81 hg tags
82 82
83 83 # test tag removal
84 84 hg tag --remove -d '1000000 0' bar
85 85 hg tip
86 86 hg tags
87 87
88 88 echo '% remove nonexistent tag'
89 89 hg tag --remove -d '1000000 0' foobar
90 90 hg tip
91 91
92 92 # test tag rank
93 93 cd ..
94 94 hg init t3
95 95 cd t3
96 96 echo foo > foo
97 97 hg add foo
98 98 hg ci -m 'add foo' -d '1000000 0' # rev 0
99 99 hg tag -d '1000000 0' -f bar # rev 1 bar -> 0
100 100 hg tag -d '1000000 0' -f bar # rev 2 bar -> 1
101 101 hg tag -d '1000000 0' -fr 0 bar # rev 3 bar -> 0
102 102 hg tag -d '1000000 0' -fr 1 bar # rev 3 bar -> 1
103 103 hg tag -d '1000000 0' -fr 0 bar # rev 4 bar -> 0
104 104 hg tags
105 105 hg co 3
106 106 echo barbar > foo
107 107 hg ci -m 'change foo' -d '1000000 0' # rev 0
108 108 hg tags
109 109
110 110 hg tag -d '1000000 0' -r 3 bar # should complain
111 111 hg tags
112 112
113 113 # test tag rank with 3 heads
114 114 cd ..
115 115 hg init t4
116 116 cd t4
117 117 echo foo > foo
118 118 hg add
119 hg ci -m 'add foo' -d '0 0' # rev 0
120 hg tag -d '0 0' bar # rev 1 bar -> 0
121 hg tag -d '0 0' -f bar # rev 2 bar -> 1
119 hg ci -m 'add foo' # rev 0
120 hg tag bar # rev 1 bar -> 0
121 hg tag -f bar # rev 2 bar -> 1
122 122 hg up -qC 0
123 hg tag -d '0 0' -fr 2 bar # rev 3 bar -> 2
123 hg tag -fr 2 bar # rev 3 bar -> 2
124 124 hg tags
125 125 hg up -qC 0
126 hg tag -d '0 0' -m 'retag rev 0' -fr 0 bar # rev 4 bar -> 0, but bar stays at 2
126 hg tag -m 'retag rev 0' -fr 0 bar # rev 4 bar -> 0, but bar stays at 2
127 127 echo % bar should still point to rev 2
128 128 hg tags
General Comments 0
You need to be logged in to leave comments. Login now