##// END OF EJS Templates
merge with crew
Benoit Boissinot -
r6986:55c16f12 merge default
parent child Browse files
Show More
@@ -1,3320 +1,3322 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from node import hex, nullid, nullrev, short
9 9 from repo import RepoError, NoCapability
10 10 from i18n import _
11 11 import os, re, sys, urllib
12 12 import hg, util, revlog, bundlerepo, extensions, copies
13 13 import difflib, patch, time, help, mdiff, tempfile
14 14 import version, socket
15 15 import archival, changegroup, cmdutil, hgweb.server, sshserver, hbisect
16 16 import merge as merge_
17 17
18 18 # Commands start here, listed alphabetically
19 19
20 20 def add(ui, repo, *pats, **opts):
21 21 """add the specified files on the next commit
22 22
23 23 Schedule files to be version controlled and added to the repository.
24 24
25 25 The files will be added to the repository at the next commit. To
26 26 undo an add before that, see hg revert.
27 27
28 28 If no names are given, add all files in the repository.
29 29 """
30 30
31 31 rejected = None
32 32 exacts = {}
33 33 names = []
34 34 m = cmdutil.match(repo, pats, opts)
35 35 m.bad = lambda x,y: True
36 36 for abs in repo.walk(m):
37 37 if m.exact(abs):
38 38 if ui.verbose:
39 39 ui.status(_('adding %s\n') % m.rel(abs))
40 40 names.append(abs)
41 41 exacts[abs] = 1
42 42 elif abs not in repo.dirstate:
43 43 ui.status(_('adding %s\n') % m.rel(abs))
44 44 names.append(abs)
45 45 if not opts.get('dry_run'):
46 46 rejected = repo.add(names)
47 47 rejected = [p for p in rejected if p in exacts]
48 48 return rejected and 1 or 0
49 49
50 50 def addremove(ui, repo, *pats, **opts):
51 51 """add all new files, delete all missing files
52 52
53 53 Add all new files and remove all missing files from the repository.
54 54
55 55 New files are ignored if they match any of the patterns in .hgignore. As
56 56 with add, these changes take effect at the next commit.
57 57
58 58 Use the -s option to detect renamed files. With a parameter > 0,
59 59 this compares every removed file with every added file and records
60 60 those similar enough as renames. This option takes a percentage
61 61 between 0 (disabled) and 100 (files must be identical) as its
62 62 parameter. Detecting renamed files this way can be expensive.
63 63 """
64 64 try:
65 65 sim = float(opts.get('similarity') or 0)
66 66 except ValueError:
67 67 raise util.Abort(_('similarity must be a number'))
68 68 if sim < 0 or sim > 100:
69 69 raise util.Abort(_('similarity must be between 0 and 100'))
70 70 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
71 71
72 72 def annotate(ui, repo, *pats, **opts):
73 73 """show changeset information per file line
74 74
75 75 List changes in files, showing the revision id responsible for each line
76 76
77 77 This command is useful to discover who did a change or when a change took
78 78 place.
79 79
80 80 Without the -a option, annotate will avoid processing files it
81 81 detects as binary. With -a, annotate will generate an annotation
82 82 anyway, probably with undesirable results.
83 83 """
84 84 datefunc = ui.quiet and util.shortdate or util.datestr
85 85 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
86 86
87 87 if not pats:
88 88 raise util.Abort(_('at least one file name or pattern required'))
89 89
90 90 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
91 91 ('number', lambda x: str(x[0].rev())),
92 92 ('changeset', lambda x: short(x[0].node())),
93 93 ('date', getdate),
94 94 ('follow', lambda x: x[0].path()),
95 95 ]
96 96
97 97 if (not opts['user'] and not opts['changeset'] and not opts['date']
98 98 and not opts['follow']):
99 99 opts['number'] = 1
100 100
101 101 linenumber = opts.get('line_number') is not None
102 102 if (linenumber and (not opts['changeset']) and (not opts['number'])):
103 103 raise util.Abort(_('at least one of -n/-c is required for -l'))
104 104
105 105 funcmap = [func for op, func in opmap if opts.get(op)]
106 106 if linenumber:
107 107 lastfunc = funcmap[-1]
108 108 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
109 109
110 110 ctx = repo[opts['rev']]
111 111
112 112 m = cmdutil.match(repo, pats, opts)
113 113 for abs in ctx.walk(m):
114 114 fctx = ctx[abs]
115 115 if not opts['text'] and util.binary(fctx.data()):
116 116 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
117 117 continue
118 118
119 119 lines = fctx.annotate(follow=opts.get('follow'),
120 120 linenumber=linenumber)
121 121 pieces = []
122 122
123 123 for f in funcmap:
124 124 l = [f(n) for n, dummy in lines]
125 125 if l:
126 126 m = max(map(len, l))
127 127 pieces.append(["%*s" % (m, x) for x in l])
128 128
129 129 if pieces:
130 130 for p, l in zip(zip(*pieces), lines):
131 131 ui.write("%s: %s" % (" ".join(p), l[1]))
132 132
133 133 def archive(ui, repo, dest, **opts):
134 134 '''create unversioned archive of a repository revision
135 135
136 136 By default, the revision used is the parent of the working
137 137 directory; use "-r" to specify a different revision.
138 138
139 139 To specify the type of archive to create, use "-t". Valid
140 140 types are:
141 141
142 142 "files" (default): a directory full of files
143 143 "tar": tar archive, uncompressed
144 144 "tbz2": tar archive, compressed using bzip2
145 145 "tgz": tar archive, compressed using gzip
146 146 "uzip": zip archive, uncompressed
147 147 "zip": zip archive, compressed using deflate
148 148
149 149 The exact name of the destination archive or directory is given
150 150 using a format string; see "hg help export" for details.
151 151
152 152 Each member added to an archive file has a directory prefix
153 153 prepended. Use "-p" to specify a format string for the prefix.
154 154 The default is the basename of the archive, with suffixes removed.
155 155 '''
156 156
157 157 ctx = repo[opts['rev']]
158 158 if not ctx:
159 159 raise util.Abort(_('repository has no revisions'))
160 160 node = ctx.node()
161 161 dest = cmdutil.make_filename(repo, dest, node)
162 162 if os.path.realpath(dest) == repo.root:
163 163 raise util.Abort(_('repository root cannot be destination'))
164 164 matchfn = cmdutil.match(repo, [], opts)
165 165 kind = opts.get('type') or 'files'
166 166 prefix = opts['prefix']
167 167 if dest == '-':
168 168 if kind == 'files':
169 169 raise util.Abort(_('cannot archive plain files to stdout'))
170 170 dest = sys.stdout
171 171 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
172 172 prefix = cmdutil.make_filename(repo, prefix, node)
173 173 archival.archive(repo, dest, node, kind, not opts['no_decode'],
174 174 matchfn, prefix)
175 175
176 176 def backout(ui, repo, node=None, rev=None, **opts):
177 177 '''reverse effect of earlier changeset
178 178
179 179 Commit the backed out changes as a new changeset. The new
180 180 changeset is a child of the backed out changeset.
181 181
182 182 If you back out a changeset other than the tip, a new head is
183 183 created. This head will be the new tip and you should merge this
184 184 backout changeset with another head (current one by default).
185 185
186 186 The --merge option remembers the parent of the working directory
187 187 before starting the backout, then merges the new head with that
188 188 changeset afterwards. This saves you from doing the merge by
189 189 hand. The result of this merge is not committed, as for a normal
190 190 merge.
191 191
192 192 See \'hg help dates\' for a list of formats valid for -d/--date.
193 193 '''
194 194 if rev and node:
195 195 raise util.Abort(_("please specify just one revision"))
196 196
197 197 if not rev:
198 198 rev = node
199 199
200 200 if not rev:
201 201 raise util.Abort(_("please specify a revision to backout"))
202 202
203 203 date = opts.get('date')
204 204 if date:
205 205 opts['date'] = util.parsedate(date)
206 206
207 207 cmdutil.bail_if_changed(repo)
208 208 node = repo.lookup(rev)
209 209
210 210 op1, op2 = repo.dirstate.parents()
211 211 a = repo.changelog.ancestor(op1, node)
212 212 if a != node:
213 213 raise util.Abort(_('cannot back out change on a different branch'))
214 214
215 215 p1, p2 = repo.changelog.parents(node)
216 216 if p1 == nullid:
217 217 raise util.Abort(_('cannot back out a change with no parents'))
218 218 if p2 != nullid:
219 219 if not opts['parent']:
220 220 raise util.Abort(_('cannot back out a merge changeset without '
221 221 '--parent'))
222 222 p = repo.lookup(opts['parent'])
223 223 if p not in (p1, p2):
224 224 raise util.Abort(_('%s is not a parent of %s') %
225 225 (short(p), short(node)))
226 226 parent = p
227 227 else:
228 228 if opts['parent']:
229 229 raise util.Abort(_('cannot use --parent on non-merge changeset'))
230 230 parent = p1
231 231
232 232 # the backout should appear on the same branch
233 233 branch = repo.dirstate.branch()
234 234 hg.clean(repo, node, show_stats=False)
235 235 repo.dirstate.setbranch(branch)
236 236 revert_opts = opts.copy()
237 237 revert_opts['date'] = None
238 238 revert_opts['all'] = True
239 239 revert_opts['rev'] = hex(parent)
240 240 revert_opts['no_backup'] = None
241 241 revert(ui, repo, **revert_opts)
242 242 commit_opts = opts.copy()
243 243 commit_opts['addremove'] = False
244 244 if not commit_opts['message'] and not commit_opts['logfile']:
245 245 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
246 246 commit_opts['force_editor'] = True
247 247 commit(ui, repo, **commit_opts)
248 248 def nice(node):
249 249 return '%d:%s' % (repo.changelog.rev(node), short(node))
250 250 ui.status(_('changeset %s backs out changeset %s\n') %
251 251 (nice(repo.changelog.tip()), nice(node)))
252 252 if op1 != node:
253 253 hg.clean(repo, op1, show_stats=False)
254 254 if opts['merge']:
255 255 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
256 256 hg.merge(repo, hex(repo.changelog.tip()))
257 257 else:
258 258 ui.status(_('the backout changeset is a new head - '
259 259 'do not forget to merge\n'))
260 260 ui.status(_('(use "backout --merge" '
261 261 'if you want to auto-merge)\n'))
262 262
263 263 def bisect(ui, repo, rev=None, extra=None,
264 264 reset=None, good=None, bad=None, skip=None, noupdate=None):
265 265 """subdivision search of changesets
266 266
267 267 This command helps to find changesets which introduce problems.
268 268 To use, mark the earliest changeset you know exhibits the problem
269 269 as bad, then mark the latest changeset which is free from the
270 270 problem as good. Bisect will update your working directory to a
271 271 revision for testing (unless the --noupdate option is specified).
272 272 Once you have performed tests, mark the working directory as bad
273 273 or good and bisect will either update to another candidate changeset
274 274 or announce that it has found the bad revision.
275 275
276 276 As a shortcut, you can also use the revision argument to mark a
277 277 revision as good or bad without checking it out first.
278 278 """
279 279 # backward compatibility
280 280 if rev in "good bad reset init".split():
281 281 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
282 282 cmd, rev, extra = rev, extra, None
283 283 if cmd == "good":
284 284 good = True
285 285 elif cmd == "bad":
286 286 bad = True
287 287 else:
288 288 reset = True
289 289 elif extra or good + bad + skip + reset > 1:
290 290 raise util.Abort(_('incompatible arguments'))
291 elif not (good or bad or skip or reset):
292 ui.status(_('(no action selected)\n'))
293 return
294 291
295 292 if reset:
296 293 p = repo.join("bisect.state")
297 294 if os.path.exists(p):
298 295 os.unlink(p)
299 296 return
300 297
301 298 # load state
302 299 state = {'good': [], 'bad': [], 'skip': []}
303 300 if os.path.exists(repo.join("bisect.state")):
304 301 for l in repo.opener("bisect.state"):
305 302 kind, node = l[:-1].split()
306 303 node = repo.lookup(node)
307 304 if kind not in state:
308 305 raise util.Abort(_("unknown bisect kind %s") % kind)
309 306 state[kind].append(node)
310 307
311 308 # update state
312 309 node = repo.lookup(rev or '.')
313 310 if good:
314 311 state['good'].append(node)
315 312 elif bad:
316 313 state['bad'].append(node)
317 314 elif skip:
318 315 state['skip'].append(node)
319 316
320 317 # save state
321 318 f = repo.opener("bisect.state", "w", atomictemp=True)
322 319 wlock = repo.wlock()
323 320 try:
324 321 for kind in state:
325 322 for node in state[kind]:
326 323 f.write("%s %s\n" % (kind, hex(node)))
327 324 f.rename()
328 325 finally:
329 326 del wlock
330 327
331 328 if not state['good'] or not state['bad']:
332 return
329 if (good or bad or skip or reset):
330 return
331 if not state['good']:
332 raise util.Abort(_('cannot bisect (no known good revisions)'))
333 else:
334 raise util.Abort(_('cannot bisect (no known bad revisions)'))
333 335
334 336 # actually bisect
335 337 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
336 338 if changesets == 0:
337 339 displayer = cmdutil.show_changeset(ui, repo, {})
338 340 transition = (good and "good" or "bad")
339 341 if len(nodes) == 1:
340 342 # narrowed it down to a single revision
341 343 ui.write(_("The first %s revision is:\n") % transition)
342 344 displayer.show(changenode=nodes[0])
343 345 else:
344 346 # multiple possible revisions
345 347 ui.write(_("Due to skipped revisions, the first "
346 348 "%s revision could be any of:\n") % transition)
347 349 for n in nodes:
348 350 displayer.show(changenode=n)
349 351 else:
350 352 assert len(nodes) == 1 # only a single node can be tested next
351 353 node = nodes[0]
352 354 # compute the approximate number of remaining tests
353 355 tests, size = 0, 2
354 356 while size <= changesets:
355 357 tests, size = tests + 1, size * 2
356 358 rev = repo.changelog.rev(node)
357 359 ui.write(_("Testing changeset %s:%s "
358 360 "(%s changesets remaining, ~%s tests)\n")
359 361 % (rev, short(node), changesets, tests))
360 362 if not noupdate:
361 363 cmdutil.bail_if_changed(repo)
362 364 return hg.clean(repo, node)
363 365
364 366 def branch(ui, repo, label=None, **opts):
365 367 """set or show the current branch name
366 368
367 369 With no argument, show the current branch name. With one argument,
368 370 set the working directory branch name (the branch does not exist in
369 371 the repository until the next commit).
370 372
371 373 Unless --force is specified, branch will not let you set a
372 374 branch name that shadows an existing branch.
373 375
374 376 Use the command 'hg update' to switch to an existing branch.
375 377 """
376 378
377 379 if label:
378 380 if not opts.get('force') and label in repo.branchtags():
379 381 if label not in [p.branch() for p in repo.parents()]:
380 382 raise util.Abort(_('a branch of the same name already exists'
381 383 ' (use --force to override)'))
382 384 repo.dirstate.setbranch(util.fromlocal(label))
383 385 ui.status(_('marked working directory as branch %s\n') % label)
384 386 else:
385 387 ui.write("%s\n" % util.tolocal(repo.dirstate.branch()))
386 388
387 389 def branches(ui, repo, active=False):
388 390 """list repository named branches
389 391
390 392 List the repository's named branches, indicating which ones are
391 393 inactive. If active is specified, only show active branches.
392 394
393 395 A branch is considered active if it contains repository heads.
394 396
395 397 Use the command 'hg update' to switch to an existing branch.
396 398 """
397 399 hexfunc = ui.debugflag and hex or short
398 400 activebranches = [util.tolocal(repo[n].branch())
399 401 for n in repo.heads()]
400 402 branches = util.sort([(tag in activebranches, repo.changelog.rev(node), tag)
401 403 for tag, node in repo.branchtags().items()])
402 404 branches.reverse()
403 405
404 406 for isactive, node, tag in branches:
405 407 if (not active) or isactive:
406 408 if ui.quiet:
407 409 ui.write("%s\n" % tag)
408 410 else:
409 411 rev = str(node).rjust(31 - util.locallen(tag))
410 412 isinactive = ((not isactive) and " (inactive)") or ''
411 413 data = tag, rev, hexfunc(repo.lookup(node)), isinactive
412 414 ui.write("%s %s:%s%s\n" % data)
413 415
414 416 def bundle(ui, repo, fname, dest=None, **opts):
415 417 """create a changegroup file
416 418
417 419 Generate a compressed changegroup file collecting changesets not
418 420 found in the other repository.
419 421
420 422 If no destination repository is specified the destination is
421 423 assumed to have all the nodes specified by one or more --base
422 424 parameters. To create a bundle containing all changesets, use
423 425 --all (or --base null). To change the compression method applied,
424 426 use the -t option (by default, bundles are compressed using bz2).
425 427
426 428 The bundle file can then be transferred using conventional means and
427 429 applied to another repository with the unbundle or pull command.
428 430 This is useful when direct push and pull are not available or when
429 431 exporting an entire repository is undesirable.
430 432
431 433 Applying bundles preserves all changeset contents including
432 434 permissions, copy/rename information, and revision history.
433 435 """
434 436 revs = opts.get('rev') or None
435 437 if revs:
436 438 revs = [repo.lookup(rev) for rev in revs]
437 439 if opts.get('all'):
438 440 base = ['null']
439 441 else:
440 442 base = opts.get('base')
441 443 if base:
442 444 if dest:
443 445 raise util.Abort(_("--base is incompatible with specifiying "
444 446 "a destination"))
445 447 base = [repo.lookup(rev) for rev in base]
446 448 # create the right base
447 449 # XXX: nodesbetween / changegroup* should be "fixed" instead
448 450 o = []
449 451 has = {nullid: None}
450 452 for n in base:
451 453 has.update(repo.changelog.reachable(n))
452 454 if revs:
453 455 visit = list(revs)
454 456 else:
455 457 visit = repo.changelog.heads()
456 458 seen = {}
457 459 while visit:
458 460 n = visit.pop(0)
459 461 parents = [p for p in repo.changelog.parents(n) if p not in has]
460 462 if len(parents) == 0:
461 463 o.insert(0, n)
462 464 else:
463 465 for p in parents:
464 466 if p not in seen:
465 467 seen[p] = 1
466 468 visit.append(p)
467 469 else:
468 470 cmdutil.setremoteconfig(ui, opts)
469 471 dest, revs, checkout = hg.parseurl(
470 472 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
471 473 other = hg.repository(ui, dest)
472 474 o = repo.findoutgoing(other, force=opts['force'])
473 475
474 476 if revs:
475 477 cg = repo.changegroupsubset(o, revs, 'bundle')
476 478 else:
477 479 cg = repo.changegroup(o, 'bundle')
478 480
479 481 bundletype = opts.get('type', 'bzip2').lower()
480 482 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
481 483 bundletype = btypes.get(bundletype)
482 484 if bundletype not in changegroup.bundletypes:
483 485 raise util.Abort(_('unknown bundle type specified with --type'))
484 486
485 487 changegroup.writebundle(cg, fname, bundletype)
486 488
487 489 def cat(ui, repo, file1, *pats, **opts):
488 490 """output the current or given revision of files
489 491
490 492 Print the specified files as they were at the given revision.
491 493 If no revision is given, the parent of the working directory is used,
492 494 or tip if no revision is checked out.
493 495
494 496 Output may be to a file, in which case the name of the file is
495 497 given using a format string. The formatting rules are the same as
496 498 for the export command, with the following additions:
497 499
498 500 %s basename of file being printed
499 501 %d dirname of file being printed, or '.' if in repo root
500 502 %p root-relative path name of file being printed
501 503 """
502 504 ctx = repo[opts['rev']]
503 505 err = 1
504 506 m = cmdutil.match(repo, (file1,) + pats, opts)
505 507 for abs in ctx.walk(m):
506 508 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
507 509 data = ctx[abs].data()
508 510 if opts.get('decode'):
509 511 data = repo.wwritedata(abs, data)
510 512 fp.write(data)
511 513 err = 0
512 514 return err
513 515
514 516 def clone(ui, source, dest=None, **opts):
515 517 """make a copy of an existing repository
516 518
517 519 Create a copy of an existing repository in a new directory.
518 520
519 521 If no destination directory name is specified, it defaults to the
520 522 basename of the source.
521 523
522 524 The location of the source is added to the new repository's
523 525 .hg/hgrc file, as the default to be used for future pulls.
524 526
525 527 For efficiency, hardlinks are used for cloning whenever the source
526 528 and destination are on the same filesystem (note this applies only
527 529 to the repository data, not to the checked out files). Some
528 530 filesystems, such as AFS, implement hardlinking incorrectly, but
529 531 do not report errors. In these cases, use the --pull option to
530 532 avoid hardlinking.
531 533
532 534 In some cases, you can clone repositories and checked out files
533 535 using full hardlinks with
534 536
535 537 $ cp -al REPO REPOCLONE
536 538
537 539 This is the fastest way to clone, but it is not always safe. The
538 540 operation is not atomic (making sure REPO is not modified during
539 541 the operation is up to you) and you have to make sure your editor
540 542 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
541 543 this is not compatible with certain extensions that place their
542 544 metadata under the .hg directory, such as mq.
543 545
544 546 If you use the -r option to clone up to a specific revision, no
545 547 subsequent revisions will be present in the cloned repository.
546 548 This option implies --pull, even on local repositories.
547 549
548 550 If the -U option is used, the new clone will contain only a repository
549 551 (.hg) and no working copy (the working copy parent is the null revision).
550 552
551 553 See pull for valid source format details.
552 554
553 555 It is possible to specify an ssh:// URL as the destination, but no
554 556 .hg/hgrc and working directory will be created on the remote side.
555 557 Look at the help text for the pull command for important details
556 558 about ssh:// URLs.
557 559 """
558 560 cmdutil.setremoteconfig(ui, opts)
559 561 hg.clone(ui, source, dest,
560 562 pull=opts['pull'],
561 563 stream=opts['uncompressed'],
562 564 rev=opts['rev'],
563 565 update=not opts['noupdate'])
564 566
565 567 def commit(ui, repo, *pats, **opts):
566 568 """commit the specified files or all outstanding changes
567 569
568 570 Commit changes to the given files into the repository.
569 571
570 572 If a list of files is omitted, all changes reported by "hg status"
571 573 will be committed.
572 574
573 575 If you are committing the result of a merge, do not provide any
574 576 file names or -I/-X filters.
575 577
576 578 If no commit message is specified, the configured editor is started to
577 579 enter a message.
578 580
579 581 See 'hg help dates' for a list of formats valid for -d/--date.
580 582 """
581 583 def commitfunc(ui, repo, message, match, opts):
582 584 return repo.commit(match.files(), message, opts['user'], opts['date'],
583 585 match, force_editor=opts.get('force_editor'))
584 586
585 587 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
586 588 if not node:
587 589 return
588 590 cl = repo.changelog
589 591 rev = cl.rev(node)
590 592 parents = cl.parentrevs(rev)
591 593 if rev - 1 in parents:
592 594 # one of the parents was the old tip
593 595 pass
594 596 elif (parents == (nullrev, nullrev) or
595 597 len(cl.heads(cl.node(parents[0]))) > 1 and
596 598 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
597 599 ui.status(_('created new head\n'))
598 600
599 601 if ui.debugflag:
600 602 ui.write(_('committed changeset %d:%s\n') % (rev,hex(node)))
601 603 elif ui.verbose:
602 604 ui.write(_('committed changeset %d:%s\n') % (rev,short(node)))
603 605
604 606 def copy(ui, repo, *pats, **opts):
605 607 """mark files as copied for the next commit
606 608
607 609 Mark dest as having copies of source files. If dest is a
608 610 directory, copies are put in that directory. If dest is a file,
609 611 there can only be one source.
610 612
611 613 By default, this command copies the contents of files as they
612 614 stand in the working directory. If invoked with --after, the
613 615 operation is recorded, but no copying is performed.
614 616
615 617 This command takes effect in the next commit. To undo a copy
616 618 before that, see hg revert.
617 619 """
618 620 wlock = repo.wlock(False)
619 621 try:
620 622 return cmdutil.copy(ui, repo, pats, opts)
621 623 finally:
622 624 del wlock
623 625
624 626 def debugancestor(ui, repo, *args):
625 627 """find the ancestor revision of two revisions in a given index"""
626 628 if len(args) == 3:
627 629 index, rev1, rev2 = args
628 630 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
629 631 lookup = r.lookup
630 632 elif len(args) == 2:
631 633 if not repo:
632 634 raise util.Abort(_("There is no Mercurial repository here "
633 635 "(.hg not found)"))
634 636 rev1, rev2 = args
635 637 r = repo.changelog
636 638 lookup = repo.lookup
637 639 else:
638 640 raise util.Abort(_('either two or three arguments required'))
639 641 a = r.ancestor(lookup(rev1), lookup(rev2))
640 642 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
641 643
642 644 def debugcomplete(ui, cmd='', **opts):
643 645 """returns the completion list associated with the given command"""
644 646
645 647 if opts['options']:
646 648 options = []
647 649 otables = [globalopts]
648 650 if cmd:
649 651 aliases, entry = cmdutil.findcmd(ui, cmd, table)
650 652 otables.append(entry[1])
651 653 for t in otables:
652 654 for o in t:
653 655 if o[0]:
654 656 options.append('-%s' % o[0])
655 657 options.append('--%s' % o[1])
656 658 ui.write("%s\n" % "\n".join(options))
657 659 return
658 660
659 661 ui.write("%s\n" % "\n".join(util.sort(cmdutil.findpossible(ui, cmd, table))))
660 662
661 663 def debugfsinfo(ui, path = "."):
662 664 file('.debugfsinfo', 'w').write('')
663 665 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
664 666 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
665 667 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
666 668 and 'yes' or 'no'))
667 669 os.unlink('.debugfsinfo')
668 670
669 671 def debugrebuildstate(ui, repo, rev="tip"):
670 672 """rebuild the dirstate as it would look like for the given revision"""
671 673 ctx = repo[rev]
672 674 wlock = repo.wlock()
673 675 try:
674 676 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
675 677 finally:
676 678 del wlock
677 679
678 680 def debugcheckstate(ui, repo):
679 681 """validate the correctness of the current dirstate"""
680 682 parent1, parent2 = repo.dirstate.parents()
681 683 m1 = repo[parent1].manifest()
682 684 m2 = repo[parent2].manifest()
683 685 errors = 0
684 686 for f in repo.dirstate:
685 687 state = repo.dirstate[f]
686 688 if state in "nr" and f not in m1:
687 689 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
688 690 errors += 1
689 691 if state in "a" and f in m1:
690 692 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
691 693 errors += 1
692 694 if state in "m" and f not in m1 and f not in m2:
693 695 ui.warn(_("%s in state %s, but not in either manifest\n") %
694 696 (f, state))
695 697 errors += 1
696 698 for f in m1:
697 699 state = repo.dirstate[f]
698 700 if state not in "nrm":
699 701 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
700 702 errors += 1
701 703 if errors:
702 704 error = _(".hg/dirstate inconsistent with current parent's manifest")
703 705 raise util.Abort(error)
704 706
705 707 def showconfig(ui, repo, *values, **opts):
706 708 """show combined config settings from all hgrc files
707 709
708 710 With no args, print names and values of all config items.
709 711
710 712 With one arg of the form section.name, print just the value of
711 713 that config item.
712 714
713 715 With multiple args, print names and values of all config items
714 716 with matching section names."""
715 717
716 718 untrusted = bool(opts.get('untrusted'))
717 719 if values:
718 720 if len([v for v in values if '.' in v]) > 1:
719 721 raise util.Abort(_('only one config item permitted'))
720 722 for section, name, value in ui.walkconfig(untrusted=untrusted):
721 723 sectname = section + '.' + name
722 724 if values:
723 725 for v in values:
724 726 if v == section:
725 727 ui.write('%s=%s\n' % (sectname, value))
726 728 elif v == sectname:
727 729 ui.write(value, '\n')
728 730 else:
729 731 ui.write('%s=%s\n' % (sectname, value))
730 732
731 733 def debugsetparents(ui, repo, rev1, rev2=None):
732 734 """manually set the parents of the current working directory
733 735
734 736 This is useful for writing repository conversion tools, but should
735 737 be used with care.
736 738 """
737 739
738 740 if not rev2:
739 741 rev2 = hex(nullid)
740 742
741 743 wlock = repo.wlock()
742 744 try:
743 745 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
744 746 finally:
745 747 del wlock
746 748
747 749 def debugstate(ui, repo, nodates=None):
748 750 """show the contents of the current dirstate"""
749 751 timestr = ""
750 752 showdate = not nodates
751 753 for file_, ent in util.sort(repo.dirstate._map.items()):
752 754 if showdate:
753 755 if ent[3] == -1:
754 756 # Pad or slice to locale representation
755 757 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
756 758 timestr = 'unset'
757 759 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
758 760 else:
759 761 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
760 762 if ent[1] & 020000:
761 763 mode = 'lnk'
762 764 else:
763 765 mode = '%3o' % (ent[1] & 0777)
764 766 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
765 767 for f in repo.dirstate.copies():
766 768 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
767 769
768 770 def debugdata(ui, file_, rev):
769 771 """dump the contents of a data file revision"""
770 772 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
771 773 try:
772 774 ui.write(r.revision(r.lookup(rev)))
773 775 except KeyError:
774 776 raise util.Abort(_('invalid revision identifier %s') % rev)
775 777
776 778 def debugdate(ui, date, range=None, **opts):
777 779 """parse and display a date"""
778 780 if opts["extended"]:
779 781 d = util.parsedate(date, util.extendeddateformats)
780 782 else:
781 783 d = util.parsedate(date)
782 784 ui.write("internal: %s %s\n" % d)
783 785 ui.write("standard: %s\n" % util.datestr(d))
784 786 if range:
785 787 m = util.matchdate(range)
786 788 ui.write("match: %s\n" % m(d[0]))
787 789
788 790 def debugindex(ui, file_):
789 791 """dump the contents of an index file"""
790 792 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
791 793 ui.write(" rev offset length base linkrev" +
792 794 " nodeid p1 p2\n")
793 795 for i in r:
794 796 node = r.node(i)
795 797 try:
796 798 pp = r.parents(node)
797 799 except:
798 800 pp = [nullid, nullid]
799 801 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
800 802 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
801 803 short(node), short(pp[0]), short(pp[1])))
802 804
803 805 def debugindexdot(ui, file_):
804 806 """dump an index DAG as a .dot file"""
805 807 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
806 808 ui.write("digraph G {\n")
807 809 for i in r:
808 810 node = r.node(i)
809 811 pp = r.parents(node)
810 812 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
811 813 if pp[1] != nullid:
812 814 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
813 815 ui.write("}\n")
814 816
815 817 def debuginstall(ui):
816 818 '''test Mercurial installation'''
817 819
818 820 def writetemp(contents):
819 821 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
820 822 f = os.fdopen(fd, "wb")
821 823 f.write(contents)
822 824 f.close()
823 825 return name
824 826
825 827 problems = 0
826 828
827 829 # encoding
828 830 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
829 831 try:
830 832 util.fromlocal("test")
831 833 except util.Abort, inst:
832 834 ui.write(" %s\n" % inst)
833 835 ui.write(_(" (check that your locale is properly set)\n"))
834 836 problems += 1
835 837
836 838 # compiled modules
837 839 ui.status(_("Checking extensions...\n"))
838 840 try:
839 841 import bdiff, mpatch, base85
840 842 except Exception, inst:
841 843 ui.write(" %s\n" % inst)
842 844 ui.write(_(" One or more extensions could not be found"))
843 845 ui.write(_(" (check that you compiled the extensions)\n"))
844 846 problems += 1
845 847
846 848 # templates
847 849 ui.status(_("Checking templates...\n"))
848 850 try:
849 851 import templater
850 852 t = templater.templater(templater.templatepath("map-cmdline.default"))
851 853 except Exception, inst:
852 854 ui.write(" %s\n" % inst)
853 855 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
854 856 problems += 1
855 857
856 858 # patch
857 859 ui.status(_("Checking patch...\n"))
858 860 patchproblems = 0
859 861 a = "1\n2\n3\n4\n"
860 862 b = "1\n2\n3\ninsert\n4\n"
861 863 fa = writetemp(a)
862 864 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
863 865 os.path.basename(fa))
864 866 fd = writetemp(d)
865 867
866 868 files = {}
867 869 try:
868 870 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
869 871 except util.Abort, e:
870 872 ui.write(_(" patch call failed:\n"))
871 873 ui.write(" " + str(e) + "\n")
872 874 patchproblems += 1
873 875 else:
874 876 if list(files) != [os.path.basename(fa)]:
875 877 ui.write(_(" unexpected patch output!\n"))
876 878 patchproblems += 1
877 879 a = file(fa).read()
878 880 if a != b:
879 881 ui.write(_(" patch test failed!\n"))
880 882 patchproblems += 1
881 883
882 884 if patchproblems:
883 885 if ui.config('ui', 'patch'):
884 886 ui.write(_(" (Current patch tool may be incompatible with patch,"
885 887 " or misconfigured. Please check your .hgrc file)\n"))
886 888 else:
887 889 ui.write(_(" Internal patcher failure, please report this error"
888 890 " to http://www.selenic.com/mercurial/bts\n"))
889 891 problems += patchproblems
890 892
891 893 os.unlink(fa)
892 894 os.unlink(fd)
893 895
894 896 # editor
895 897 ui.status(_("Checking commit editor...\n"))
896 898 editor = ui.geteditor()
897 899 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
898 900 if not cmdpath:
899 901 if editor == 'vi':
900 902 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
901 903 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
902 904 else:
903 905 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
904 906 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
905 907 problems += 1
906 908
907 909 # check username
908 910 ui.status(_("Checking username...\n"))
909 911 user = os.environ.get("HGUSER")
910 912 if user is None:
911 913 user = ui.config("ui", "username")
912 914 if user is None:
913 915 user = os.environ.get("EMAIL")
914 916 if not user:
915 917 ui.warn(" ")
916 918 ui.username()
917 919 ui.write(_(" (specify a username in your .hgrc file)\n"))
918 920
919 921 if not problems:
920 922 ui.status(_("No problems detected\n"))
921 923 else:
922 924 ui.write(_("%s problems detected,"
923 925 " please check your install!\n") % problems)
924 926
925 927 return problems
926 928
927 929 def debugrename(ui, repo, file1, *pats, **opts):
928 930 """dump rename information"""
929 931
930 932 ctx = repo[opts.get('rev')]
931 933 m = cmdutil.match(repo, (file1,) + pats, opts)
932 934 for abs in ctx.walk(m):
933 935 fctx = ctx[abs]
934 936 o = fctx.filelog().renamed(fctx.filenode())
935 937 rel = m.rel(abs)
936 938 if o:
937 939 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
938 940 else:
939 941 ui.write(_("%s not renamed\n") % rel)
940 942
941 943 def debugwalk(ui, repo, *pats, **opts):
942 944 """show how files match on given patterns"""
943 945 m = cmdutil.match(repo, pats, opts)
944 946 items = list(repo.walk(m))
945 947 if not items:
946 948 return
947 949 fmt = 'f %%-%ds %%-%ds %%s' % (
948 950 max([len(abs) for abs in items]),
949 951 max([len(m.rel(abs)) for abs in items]))
950 952 for abs in items:
951 953 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
952 954 ui.write("%s\n" % line.rstrip())
953 955
954 956 def diff(ui, repo, *pats, **opts):
955 957 """diff repository (or selected files)
956 958
957 959 Show differences between revisions for the specified files.
958 960
959 961 Differences between files are shown using the unified diff format.
960 962
961 963 NOTE: diff may generate unexpected results for merges, as it will
962 964 default to comparing against the working directory's first parent
963 965 changeset if no revisions are specified.
964 966
965 967 When two revision arguments are given, then changes are shown
966 968 between those revisions. If only one revision is specified then
967 969 that revision is compared to the working directory, and, when no
968 970 revisions are specified, the working directory files are compared
969 971 to its parent.
970 972
971 973 Without the -a option, diff will avoid generating diffs of files
972 974 it detects as binary. With -a, diff will generate a diff anyway,
973 975 probably with undesirable results.
974 976 """
975 977 node1, node2 = cmdutil.revpair(repo, opts['rev'])
976 978
977 979 m = cmdutil.match(repo, pats, opts)
978 980 patch.diff(repo, node1, node2, match=m, opts=patch.diffopts(ui, opts))
979 981
980 982 def export(ui, repo, *changesets, **opts):
981 983 """dump the header and diffs for one or more changesets
982 984
983 985 Print the changeset header and diffs for one or more revisions.
984 986
985 987 The information shown in the changeset header is: author,
986 988 changeset hash, parent(s) and commit comment.
987 989
988 990 NOTE: export may generate unexpected diff output for merge changesets,
989 991 as it will compare the merge changeset against its first parent only.
990 992
991 993 Output may be to a file, in which case the name of the file is
992 994 given using a format string. The formatting rules are as follows:
993 995
994 996 %% literal "%" character
995 997 %H changeset hash (40 bytes of hexadecimal)
996 998 %N number of patches being generated
997 999 %R changeset revision number
998 1000 %b basename of the exporting repository
999 1001 %h short-form changeset hash (12 bytes of hexadecimal)
1000 1002 %n zero-padded sequence number, starting at 1
1001 1003 %r zero-padded changeset revision number
1002 1004
1003 1005 Without the -a option, export will avoid generating diffs of files
1004 1006 it detects as binary. With -a, export will generate a diff anyway,
1005 1007 probably with undesirable results.
1006 1008
1007 1009 With the --switch-parent option, the diff will be against the second
1008 1010 parent. It can be useful to review a merge.
1009 1011 """
1010 1012 if not changesets:
1011 1013 raise util.Abort(_("export requires at least one changeset"))
1012 1014 revs = cmdutil.revrange(repo, changesets)
1013 1015 if len(revs) > 1:
1014 1016 ui.note(_('exporting patches:\n'))
1015 1017 else:
1016 1018 ui.note(_('exporting patch:\n'))
1017 1019 patch.export(repo, revs, template=opts['output'],
1018 1020 switch_parent=opts['switch_parent'],
1019 1021 opts=patch.diffopts(ui, opts))
1020 1022
1021 1023 def grep(ui, repo, pattern, *pats, **opts):
1022 1024 """search for a pattern in specified files and revisions
1023 1025
1024 1026 Search revisions of files for a regular expression.
1025 1027
1026 1028 This command behaves differently than Unix grep. It only accepts
1027 1029 Python/Perl regexps. It searches repository history, not the
1028 1030 working directory. It always prints the revision number in which
1029 1031 a match appears.
1030 1032
1031 1033 By default, grep only prints output for the first revision of a
1032 1034 file in which it finds a match. To get it to print every revision
1033 1035 that contains a change in match status ("-" for a match that
1034 1036 becomes a non-match, or "+" for a non-match that becomes a match),
1035 1037 use the --all flag.
1036 1038 """
1037 1039 reflags = 0
1038 1040 if opts['ignore_case']:
1039 1041 reflags |= re.I
1040 1042 try:
1041 1043 regexp = re.compile(pattern, reflags)
1042 1044 except Exception, inst:
1043 1045 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1044 1046 return None
1045 1047 sep, eol = ':', '\n'
1046 1048 if opts['print0']:
1047 1049 sep = eol = '\0'
1048 1050
1049 1051 fcache = {}
1050 1052 def getfile(fn):
1051 1053 if fn not in fcache:
1052 1054 fcache[fn] = repo.file(fn)
1053 1055 return fcache[fn]
1054 1056
1055 1057 def matchlines(body):
1056 1058 begin = 0
1057 1059 linenum = 0
1058 1060 while True:
1059 1061 match = regexp.search(body, begin)
1060 1062 if not match:
1061 1063 break
1062 1064 mstart, mend = match.span()
1063 1065 linenum += body.count('\n', begin, mstart) + 1
1064 1066 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1065 1067 lend = body.find('\n', mend)
1066 1068 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1067 1069 begin = lend + 1
1068 1070
1069 1071 class linestate(object):
1070 1072 def __init__(self, line, linenum, colstart, colend):
1071 1073 self.line = line
1072 1074 self.linenum = linenum
1073 1075 self.colstart = colstart
1074 1076 self.colend = colend
1075 1077
1076 1078 def __hash__(self):
1077 1079 return hash((self.linenum, self.line))
1078 1080
1079 1081 def __eq__(self, other):
1080 1082 return self.line == other.line
1081 1083
1082 1084 matches = {}
1083 1085 copies = {}
1084 1086 def grepbody(fn, rev, body):
1085 1087 matches[rev].setdefault(fn, [])
1086 1088 m = matches[rev][fn]
1087 1089 for lnum, cstart, cend, line in matchlines(body):
1088 1090 s = linestate(line, lnum, cstart, cend)
1089 1091 m.append(s)
1090 1092
1091 1093 def difflinestates(a, b):
1092 1094 sm = difflib.SequenceMatcher(None, a, b)
1093 1095 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1094 1096 if tag == 'insert':
1095 1097 for i in xrange(blo, bhi):
1096 1098 yield ('+', b[i])
1097 1099 elif tag == 'delete':
1098 1100 for i in xrange(alo, ahi):
1099 1101 yield ('-', a[i])
1100 1102 elif tag == 'replace':
1101 1103 for i in xrange(alo, ahi):
1102 1104 yield ('-', a[i])
1103 1105 for i in xrange(blo, bhi):
1104 1106 yield ('+', b[i])
1105 1107
1106 1108 prev = {}
1107 1109 def display(fn, rev, states, prevstates):
1108 1110 datefunc = ui.quiet and util.shortdate or util.datestr
1109 1111 found = False
1110 1112 filerevmatches = {}
1111 1113 r = prev.get(fn, -1)
1112 1114 if opts['all']:
1113 1115 iter = difflinestates(states, prevstates)
1114 1116 else:
1115 1117 iter = [('', l) for l in prevstates]
1116 1118 for change, l in iter:
1117 1119 cols = [fn, str(r)]
1118 1120 if opts['line_number']:
1119 1121 cols.append(str(l.linenum))
1120 1122 if opts['all']:
1121 1123 cols.append(change)
1122 1124 if opts['user']:
1123 1125 cols.append(ui.shortuser(get(r)[1]))
1124 1126 if opts.get('date'):
1125 1127 cols.append(datefunc(get(r)[2]))
1126 1128 if opts['files_with_matches']:
1127 1129 c = (fn, r)
1128 1130 if c in filerevmatches:
1129 1131 continue
1130 1132 filerevmatches[c] = 1
1131 1133 else:
1132 1134 cols.append(l.line)
1133 1135 ui.write(sep.join(cols), eol)
1134 1136 found = True
1135 1137 return found
1136 1138
1137 1139 fstate = {}
1138 1140 skip = {}
1139 1141 get = util.cachefunc(lambda r: repo[r].changeset())
1140 1142 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1141 1143 found = False
1142 1144 follow = opts.get('follow')
1143 1145 for st, rev, fns in changeiter:
1144 1146 if st == 'window':
1145 1147 matches.clear()
1146 1148 elif st == 'add':
1147 1149 ctx = repo[rev]
1148 1150 matches[rev] = {}
1149 1151 for fn in fns:
1150 1152 if fn in skip:
1151 1153 continue
1152 1154 try:
1153 1155 grepbody(fn, rev, getfile(fn).read(ctx.filenode(fn)))
1154 1156 fstate.setdefault(fn, [])
1155 1157 if follow:
1156 1158 copied = getfile(fn).renamed(ctx.filenode(fn))
1157 1159 if copied:
1158 1160 copies.setdefault(rev, {})[fn] = copied[0]
1159 1161 except revlog.LookupError:
1160 1162 pass
1161 1163 elif st == 'iter':
1162 1164 for fn, m in util.sort(matches[rev].items()):
1163 1165 copy = copies.get(rev, {}).get(fn)
1164 1166 if fn in skip:
1165 1167 if copy:
1166 1168 skip[copy] = True
1167 1169 continue
1168 1170 if fn in prev or fstate[fn]:
1169 1171 r = display(fn, rev, m, fstate[fn])
1170 1172 found = found or r
1171 1173 if r and not opts['all']:
1172 1174 skip[fn] = True
1173 1175 if copy:
1174 1176 skip[copy] = True
1175 1177 fstate[fn] = m
1176 1178 if copy:
1177 1179 fstate[copy] = m
1178 1180 prev[fn] = rev
1179 1181
1180 1182 for fn, state in util.sort(fstate.items()):
1181 1183 if fn in skip:
1182 1184 continue
1183 1185 if fn not in copies.get(prev[fn], {}):
1184 1186 found = display(fn, rev, {}, state) or found
1185 1187 return (not found and 1) or 0
1186 1188
1187 1189 def heads(ui, repo, *branchrevs, **opts):
1188 1190 """show current repository heads or show branch heads
1189 1191
1190 1192 With no arguments, show all repository head changesets.
1191 1193
1192 1194 If branch or revisions names are given this will show the heads of
1193 1195 the specified branches or the branches those revisions are tagged
1194 1196 with.
1195 1197
1196 1198 Repository "heads" are changesets that don't have child
1197 1199 changesets. They are where development generally takes place and
1198 1200 are the usual targets for update and merge operations.
1199 1201
1200 1202 Branch heads are changesets that have a given branch tag, but have
1201 1203 no child changesets with that tag. They are usually where
1202 1204 development on the given branch takes place.
1203 1205 """
1204 1206 if opts['rev']:
1205 1207 start = repo.lookup(opts['rev'])
1206 1208 else:
1207 1209 start = None
1208 1210 if not branchrevs:
1209 1211 # Assume we're looking repo-wide heads if no revs were specified.
1210 1212 heads = repo.heads(start)
1211 1213 else:
1212 1214 heads = []
1213 1215 visitedset = util.set()
1214 1216 for branchrev in branchrevs:
1215 1217 branch = repo[branchrev].branch()
1216 1218 if branch in visitedset:
1217 1219 continue
1218 1220 visitedset.add(branch)
1219 1221 bheads = repo.branchheads(branch, start)
1220 1222 if not bheads:
1221 1223 if branch != branchrev:
1222 1224 ui.warn(_("no changes on branch %s containing %s are "
1223 1225 "reachable from %s\n")
1224 1226 % (branch, branchrev, opts['rev']))
1225 1227 else:
1226 1228 ui.warn(_("no changes on branch %s are reachable from %s\n")
1227 1229 % (branch, opts['rev']))
1228 1230 heads.extend(bheads)
1229 1231 if not heads:
1230 1232 return 1
1231 1233 displayer = cmdutil.show_changeset(ui, repo, opts)
1232 1234 for n in heads:
1233 1235 displayer.show(changenode=n)
1234 1236
1235 1237 def help_(ui, name=None, with_version=False):
1236 1238 """show help for a command, extension, or list of commands
1237 1239
1238 1240 With no arguments, print a list of commands and short help.
1239 1241
1240 1242 Given a command name, print help for that command.
1241 1243
1242 1244 Given an extension name, print help for that extension, and the
1243 1245 commands it provides."""
1244 1246 option_lists = []
1245 1247
1246 1248 def addglobalopts(aliases):
1247 1249 if ui.verbose:
1248 1250 option_lists.append((_("global options:"), globalopts))
1249 1251 if name == 'shortlist':
1250 1252 option_lists.append((_('use "hg help" for the full list '
1251 1253 'of commands'), ()))
1252 1254 else:
1253 1255 if name == 'shortlist':
1254 1256 msg = _('use "hg help" for the full list of commands '
1255 1257 'or "hg -v" for details')
1256 1258 elif aliases:
1257 1259 msg = _('use "hg -v help%s" to show aliases and '
1258 1260 'global options') % (name and " " + name or "")
1259 1261 else:
1260 1262 msg = _('use "hg -v help %s" to show global options') % name
1261 1263 option_lists.append((msg, ()))
1262 1264
1263 1265 def helpcmd(name):
1264 1266 if with_version:
1265 1267 version_(ui)
1266 1268 ui.write('\n')
1267 1269
1268 1270 try:
1269 1271 aliases, i = cmdutil.findcmd(ui, name, table)
1270 1272 except cmdutil.AmbiguousCommand, inst:
1271 1273 select = lambda c: c.lstrip('^').startswith(inst.args[0])
1272 1274 helplist(_('list of commands:\n\n'), select)
1273 1275 return
1274 1276
1275 1277 # synopsis
1276 1278 ui.write("%s\n" % i[2])
1277 1279
1278 1280 # aliases
1279 1281 if not ui.quiet and len(aliases) > 1:
1280 1282 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1281 1283
1282 1284 # description
1283 1285 doc = i[0].__doc__
1284 1286 if not doc:
1285 1287 doc = _("(No help text available)")
1286 1288 if ui.quiet:
1287 1289 doc = doc.splitlines(0)[0]
1288 1290 ui.write("\n%s\n" % doc.rstrip())
1289 1291
1290 1292 if not ui.quiet:
1291 1293 # options
1292 1294 if i[1]:
1293 1295 option_lists.append((_("options:\n"), i[1]))
1294 1296
1295 1297 addglobalopts(False)
1296 1298
1297 1299 def helplist(header, select=None):
1298 1300 h = {}
1299 1301 cmds = {}
1300 1302 for c, e in table.items():
1301 1303 f = c.split("|", 1)[0]
1302 1304 if select and not select(f):
1303 1305 continue
1304 1306 if name == "shortlist" and not f.startswith("^"):
1305 1307 continue
1306 1308 f = f.lstrip("^")
1307 1309 if not ui.debugflag and f.startswith("debug"):
1308 1310 continue
1309 1311 doc = e[0].__doc__
1310 1312 if not doc:
1311 1313 doc = _("(No help text available)")
1312 1314 h[f] = doc.splitlines(0)[0].rstrip()
1313 1315 cmds[f] = c.lstrip("^")
1314 1316
1315 1317 if not h:
1316 1318 ui.status(_('no commands defined\n'))
1317 1319 return
1318 1320
1319 1321 ui.status(header)
1320 1322 fns = util.sort(h)
1321 1323 m = max(map(len, fns))
1322 1324 for f in fns:
1323 1325 if ui.verbose:
1324 1326 commands = cmds[f].replace("|",", ")
1325 1327 ui.write(" %s:\n %s\n"%(commands, h[f]))
1326 1328 else:
1327 1329 ui.write(' %-*s %s\n' % (m, f, h[f]))
1328 1330
1329 1331 if not ui.quiet:
1330 1332 addglobalopts(True)
1331 1333
1332 1334 def helptopic(name):
1333 1335 v = None
1334 1336 for i, d in help.helptable:
1335 1337 l = i.split('|')
1336 1338 if name in l:
1337 1339 v = i
1338 1340 header = l[-1]
1339 1341 doc = d
1340 1342 if not v:
1341 1343 raise cmdutil.UnknownCommand(name)
1342 1344
1343 1345 # description
1344 1346 if not doc:
1345 1347 doc = _("(No help text available)")
1346 1348 if callable(doc):
1347 1349 doc = doc()
1348 1350
1349 1351 ui.write("%s\n" % header)
1350 1352 ui.write("%s\n" % doc.rstrip())
1351 1353
1352 1354 def helpext(name):
1353 1355 try:
1354 1356 mod = extensions.find(name)
1355 1357 except KeyError:
1356 1358 raise cmdutil.UnknownCommand(name)
1357 1359
1358 1360 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1359 1361 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1360 1362 for d in doc[1:]:
1361 1363 ui.write(d, '\n')
1362 1364
1363 1365 ui.status('\n')
1364 1366
1365 1367 try:
1366 1368 ct = mod.cmdtable
1367 1369 except AttributeError:
1368 1370 ct = {}
1369 1371
1370 1372 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1371 1373 helplist(_('list of commands:\n\n'), modcmds.has_key)
1372 1374
1373 1375 if name and name != 'shortlist':
1374 1376 i = None
1375 1377 for f in (helpcmd, helptopic, helpext):
1376 1378 try:
1377 1379 f(name)
1378 1380 i = None
1379 1381 break
1380 1382 except cmdutil.UnknownCommand, inst:
1381 1383 i = inst
1382 1384 if i:
1383 1385 raise i
1384 1386
1385 1387 else:
1386 1388 # program name
1387 1389 if ui.verbose or with_version:
1388 1390 version_(ui)
1389 1391 else:
1390 1392 ui.status(_("Mercurial Distributed SCM\n"))
1391 1393 ui.status('\n')
1392 1394
1393 1395 # list of commands
1394 1396 if name == "shortlist":
1395 1397 header = _('basic commands:\n\n')
1396 1398 else:
1397 1399 header = _('list of commands:\n\n')
1398 1400
1399 1401 helplist(header)
1400 1402
1401 1403 # list all option lists
1402 1404 opt_output = []
1403 1405 for title, options in option_lists:
1404 1406 opt_output.append(("\n%s" % title, None))
1405 1407 for shortopt, longopt, default, desc in options:
1406 1408 if "DEPRECATED" in desc and not ui.verbose: continue
1407 1409 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1408 1410 longopt and " --%s" % longopt),
1409 1411 "%s%s" % (desc,
1410 1412 default
1411 1413 and _(" (default: %s)") % default
1412 1414 or "")))
1413 1415
1414 1416 if ui.verbose:
1415 1417 ui.write(_("\nspecial help topics:\n"))
1416 1418 topics = []
1417 1419 for i, d in help.helptable:
1418 1420 l = i.split('|')
1419 1421 topics.append((", ".join(l[:-1]), l[-1]))
1420 1422 topics_len = max([len(s[0]) for s in topics])
1421 1423 for t, desc in topics:
1422 1424 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1423 1425
1424 1426 if opt_output:
1425 1427 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1426 1428 for first, second in opt_output:
1427 1429 if second:
1428 1430 ui.write(" %-*s %s\n" % (opts_len, first, second))
1429 1431 else:
1430 1432 ui.write("%s\n" % first)
1431 1433
1432 1434 def identify(ui, repo, source=None,
1433 1435 rev=None, num=None, id=None, branch=None, tags=None):
1434 1436 """identify the working copy or specified revision
1435 1437
1436 1438 With no revision, print a summary of the current state of the repo.
1437 1439
1438 1440 With a path, do a lookup in another repository.
1439 1441
1440 1442 This summary identifies the repository state using one or two parent
1441 1443 hash identifiers, followed by a "+" if there are uncommitted changes
1442 1444 in the working directory, a list of tags for this revision and a branch
1443 1445 name for non-default branches.
1444 1446 """
1445 1447
1446 1448 if not repo and not source:
1447 1449 raise util.Abort(_("There is no Mercurial repository here "
1448 1450 "(.hg not found)"))
1449 1451
1450 1452 hexfunc = ui.debugflag and hex or short
1451 1453 default = not (num or id or branch or tags)
1452 1454 output = []
1453 1455
1454 1456 if source:
1455 1457 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1456 1458 srepo = hg.repository(ui, source)
1457 1459 if not rev and revs:
1458 1460 rev = revs[0]
1459 1461 if not rev:
1460 1462 rev = "tip"
1461 1463 if num or branch or tags:
1462 1464 raise util.Abort(
1463 1465 "can't query remote revision number, branch, or tags")
1464 1466 output = [hexfunc(srepo.lookup(rev))]
1465 1467 elif not rev:
1466 1468 ctx = repo[None]
1467 1469 parents = ctx.parents()
1468 1470 changed = False
1469 1471 if default or id or num:
1470 1472 changed = ctx.files() + ctx.deleted()
1471 1473 if default or id:
1472 1474 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1473 1475 (changed) and "+" or "")]
1474 1476 if num:
1475 1477 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1476 1478 (changed) and "+" or ""))
1477 1479 else:
1478 1480 ctx = repo[rev]
1479 1481 if default or id:
1480 1482 output = [hexfunc(ctx.node())]
1481 1483 if num:
1482 1484 output.append(str(ctx.rev()))
1483 1485
1484 1486 if not source and default and not ui.quiet:
1485 1487 b = util.tolocal(ctx.branch())
1486 1488 if b != 'default':
1487 1489 output.append("(%s)" % b)
1488 1490
1489 1491 # multiple tags for a single parent separated by '/'
1490 1492 t = "/".join(ctx.tags())
1491 1493 if t:
1492 1494 output.append(t)
1493 1495
1494 1496 if branch:
1495 1497 output.append(util.tolocal(ctx.branch()))
1496 1498
1497 1499 if tags:
1498 1500 output.extend(ctx.tags())
1499 1501
1500 1502 ui.write("%s\n" % ' '.join(output))
1501 1503
1502 1504 def import_(ui, repo, patch1, *patches, **opts):
1503 1505 """import an ordered set of patches
1504 1506
1505 1507 Import a list of patches and commit them individually.
1506 1508
1507 1509 If there are outstanding changes in the working directory, import
1508 1510 will abort unless given the -f flag.
1509 1511
1510 1512 You can import a patch straight from a mail message. Even patches
1511 1513 as attachments work (body part must be type text/plain or
1512 1514 text/x-patch to be used). From and Subject headers of email
1513 1515 message are used as default committer and commit message. All
1514 1516 text/plain body parts before first diff are added to commit
1515 1517 message.
1516 1518
1517 1519 If the imported patch was generated by hg export, user and description
1518 1520 from patch override values from message headers and body. Values
1519 1521 given on command line with -m and -u override these.
1520 1522
1521 1523 If --exact is specified, import will set the working directory
1522 1524 to the parent of each patch before applying it, and will abort
1523 1525 if the resulting changeset has a different ID than the one
1524 1526 recorded in the patch. This may happen due to character set
1525 1527 problems or other deficiencies in the text patch format.
1526 1528
1527 1529 To read a patch from standard input, use patch name "-".
1528 1530 See 'hg help dates' for a list of formats valid for -d/--date.
1529 1531 """
1530 1532 patches = (patch1,) + patches
1531 1533
1532 1534 date = opts.get('date')
1533 1535 if date:
1534 1536 opts['date'] = util.parsedate(date)
1535 1537
1536 1538 if opts.get('exact') or not opts['force']:
1537 1539 cmdutil.bail_if_changed(repo)
1538 1540
1539 1541 d = opts["base"]
1540 1542 strip = opts["strip"]
1541 1543 wlock = lock = None
1542 1544 try:
1543 1545 wlock = repo.wlock()
1544 1546 lock = repo.lock()
1545 1547 for p in patches:
1546 1548 pf = os.path.join(d, p)
1547 1549
1548 1550 if pf == '-':
1549 1551 ui.status(_("applying patch from stdin\n"))
1550 1552 data = patch.extract(ui, sys.stdin)
1551 1553 else:
1552 1554 ui.status(_("applying %s\n") % p)
1553 1555 if os.path.exists(pf):
1554 1556 data = patch.extract(ui, file(pf, 'rb'))
1555 1557 else:
1556 1558 data = patch.extract(ui, urllib.urlopen(pf))
1557 1559 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1558 1560
1559 1561 if tmpname is None:
1560 1562 raise util.Abort(_('no diffs found'))
1561 1563
1562 1564 try:
1563 1565 cmdline_message = cmdutil.logmessage(opts)
1564 1566 if cmdline_message:
1565 1567 # pickup the cmdline msg
1566 1568 message = cmdline_message
1567 1569 elif message:
1568 1570 # pickup the patch msg
1569 1571 message = message.strip()
1570 1572 else:
1571 1573 # launch the editor
1572 1574 message = None
1573 1575 ui.debug(_('message:\n%s\n') % message)
1574 1576
1575 1577 wp = repo.parents()
1576 1578 if opts.get('exact'):
1577 1579 if not nodeid or not p1:
1578 1580 raise util.Abort(_('not a mercurial patch'))
1579 1581 p1 = repo.lookup(p1)
1580 1582 p2 = repo.lookup(p2 or hex(nullid))
1581 1583
1582 1584 if p1 != wp[0].node():
1583 1585 hg.clean(repo, p1)
1584 1586 repo.dirstate.setparents(p1, p2)
1585 1587 elif p2:
1586 1588 try:
1587 1589 p1 = repo.lookup(p1)
1588 1590 p2 = repo.lookup(p2)
1589 1591 if p1 == wp[0].node():
1590 1592 repo.dirstate.setparents(p1, p2)
1591 1593 except RepoError:
1592 1594 pass
1593 1595 if opts.get('exact') or opts.get('import_branch'):
1594 1596 repo.dirstate.setbranch(branch or 'default')
1595 1597
1596 1598 files = {}
1597 1599 try:
1598 1600 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1599 1601 files=files)
1600 1602 finally:
1601 1603 files = patch.updatedir(ui, repo, files)
1602 1604 if not opts.get('no_commit'):
1603 1605 n = repo.commit(files, message, opts.get('user') or user,
1604 1606 opts.get('date') or date)
1605 1607 if opts.get('exact'):
1606 1608 if hex(n) != nodeid:
1607 1609 repo.rollback()
1608 1610 raise util.Abort(_('patch is damaged'
1609 1611 ' or loses information'))
1610 1612 # Force a dirstate write so that the next transaction
1611 1613 # backups an up-do-date file.
1612 1614 repo.dirstate.write()
1613 1615 finally:
1614 1616 os.unlink(tmpname)
1615 1617 finally:
1616 1618 del lock, wlock
1617 1619
1618 1620 def incoming(ui, repo, source="default", **opts):
1619 1621 """show new changesets found in source
1620 1622
1621 1623 Show new changesets found in the specified path/URL or the default
1622 1624 pull location. These are the changesets that would be pulled if a pull
1623 1625 was requested.
1624 1626
1625 1627 For remote repository, using --bundle avoids downloading the changesets
1626 1628 twice if the incoming is followed by a pull.
1627 1629
1628 1630 See pull for valid source format details.
1629 1631 """
1630 1632 limit = cmdutil.loglimit(opts)
1631 1633 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
1632 1634 cmdutil.setremoteconfig(ui, opts)
1633 1635
1634 1636 other = hg.repository(ui, source)
1635 1637 ui.status(_('comparing with %s\n') % util.hidepassword(source))
1636 1638 if revs:
1637 1639 revs = [other.lookup(rev) for rev in revs]
1638 1640 incoming = repo.findincoming(other, heads=revs, force=opts["force"])
1639 1641 if not incoming:
1640 1642 try:
1641 1643 os.unlink(opts["bundle"])
1642 1644 except:
1643 1645 pass
1644 1646 ui.status(_("no changes found\n"))
1645 1647 return 1
1646 1648
1647 1649 cleanup = None
1648 1650 try:
1649 1651 fname = opts["bundle"]
1650 1652 if fname or not other.local():
1651 1653 # create a bundle (uncompressed if other repo is not local)
1652 1654 if revs is None:
1653 1655 cg = other.changegroup(incoming, "incoming")
1654 1656 else:
1655 1657 cg = other.changegroupsubset(incoming, revs, 'incoming')
1656 1658 bundletype = other.local() and "HG10BZ" or "HG10UN"
1657 1659 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1658 1660 # keep written bundle?
1659 1661 if opts["bundle"]:
1660 1662 cleanup = None
1661 1663 if not other.local():
1662 1664 # use the created uncompressed bundlerepo
1663 1665 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1664 1666
1665 1667 o = other.changelog.nodesbetween(incoming, revs)[0]
1666 1668 if opts['newest_first']:
1667 1669 o.reverse()
1668 1670 displayer = cmdutil.show_changeset(ui, other, opts)
1669 1671 count = 0
1670 1672 for n in o:
1671 1673 if count >= limit:
1672 1674 break
1673 1675 parents = [p for p in other.changelog.parents(n) if p != nullid]
1674 1676 if opts['no_merges'] and len(parents) == 2:
1675 1677 continue
1676 1678 count += 1
1677 1679 displayer.show(changenode=n)
1678 1680 finally:
1679 1681 if hasattr(other, 'close'):
1680 1682 other.close()
1681 1683 if cleanup:
1682 1684 os.unlink(cleanup)
1683 1685
1684 1686 def init(ui, dest=".", **opts):
1685 1687 """create a new repository in the given directory
1686 1688
1687 1689 Initialize a new repository in the given directory. If the given
1688 1690 directory does not exist, it is created.
1689 1691
1690 1692 If no directory is given, the current directory is used.
1691 1693
1692 1694 It is possible to specify an ssh:// URL as the destination.
1693 1695 Look at the help text for the pull command for important details
1694 1696 about ssh:// URLs.
1695 1697 """
1696 1698 cmdutil.setremoteconfig(ui, opts)
1697 1699 hg.repository(ui, dest, create=1)
1698 1700
1699 1701 def locate(ui, repo, *pats, **opts):
1700 1702 """locate files matching specific patterns
1701 1703
1702 1704 Print all files under Mercurial control whose names match the
1703 1705 given patterns.
1704 1706
1705 1707 This command searches the entire repository by default. To search
1706 1708 just the current directory and its subdirectories, use
1707 1709 "--include .".
1708 1710
1709 1711 If no patterns are given to match, this command prints all file
1710 1712 names.
1711 1713
1712 1714 If you want to feed the output of this command into the "xargs"
1713 1715 command, use the "-0" option to both this command and "xargs".
1714 1716 This will avoid the problem of "xargs" treating single filenames
1715 1717 that contain white space as multiple filenames.
1716 1718 """
1717 1719 end = opts['print0'] and '\0' or '\n'
1718 1720 rev = opts.get('rev') or None
1719 1721
1720 1722 ret = 1
1721 1723 m = cmdutil.match(repo, pats, opts, default='relglob')
1722 1724 m.bad = lambda x,y: False
1723 1725 for abs in repo[rev].walk(m):
1724 1726 if not rev and abs not in repo.dirstate:
1725 1727 continue
1726 1728 if opts['fullpath']:
1727 1729 ui.write(os.path.join(repo.root, abs), end)
1728 1730 else:
1729 1731 ui.write(((pats and m.rel(abs)) or abs), end)
1730 1732 ret = 0
1731 1733
1732 1734 return ret
1733 1735
1734 1736 def log(ui, repo, *pats, **opts):
1735 1737 """show revision history of entire repository or files
1736 1738
1737 1739 Print the revision history of the specified files or the entire
1738 1740 project.
1739 1741
1740 1742 File history is shown without following rename or copy history of
1741 1743 files. Use -f/--follow with a file name to follow history across
1742 1744 renames and copies. --follow without a file name will only show
1743 1745 ancestors or descendants of the starting revision. --follow-first
1744 1746 only follows the first parent of merge revisions.
1745 1747
1746 1748 If no revision range is specified, the default is tip:0 unless
1747 1749 --follow is set, in which case the working directory parent is
1748 1750 used as the starting revision.
1749 1751
1750 1752 See 'hg help dates' for a list of formats valid for -d/--date.
1751 1753
1752 1754 By default this command outputs: changeset id and hash, tags,
1753 1755 non-trivial parents, user, date and time, and a summary for each
1754 1756 commit. When the -v/--verbose switch is used, the list of changed
1755 1757 files and full commit message is shown.
1756 1758
1757 1759 NOTE: log -p may generate unexpected diff output for merge
1758 1760 changesets, as it will compare the merge changeset against its
1759 1761 first parent only. Also, the files: list will only reflect files
1760 1762 that are different from BOTH parents.
1761 1763
1762 1764 """
1763 1765
1764 1766 get = util.cachefunc(lambda r: repo[r].changeset())
1765 1767 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1766 1768
1767 1769 limit = cmdutil.loglimit(opts)
1768 1770 count = 0
1769 1771
1770 1772 if opts['copies'] and opts['rev']:
1771 1773 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1772 1774 else:
1773 1775 endrev = len(repo)
1774 1776 rcache = {}
1775 1777 ncache = {}
1776 1778 def getrenamed(fn, rev):
1777 1779 '''looks up all renames for a file (up to endrev) the first
1778 1780 time the file is given. It indexes on the changerev and only
1779 1781 parses the manifest if linkrev != changerev.
1780 1782 Returns rename info for fn at changerev rev.'''
1781 1783 if fn not in rcache:
1782 1784 rcache[fn] = {}
1783 1785 ncache[fn] = {}
1784 1786 fl = repo.file(fn)
1785 1787 for i in fl:
1786 1788 node = fl.node(i)
1787 1789 lr = fl.linkrev(node)
1788 1790 renamed = fl.renamed(node)
1789 1791 rcache[fn][lr] = renamed
1790 1792 if renamed:
1791 1793 ncache[fn][node] = renamed
1792 1794 if lr >= endrev:
1793 1795 break
1794 1796 if rev in rcache[fn]:
1795 1797 return rcache[fn][rev]
1796 1798
1797 1799 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1798 1800 # filectx logic.
1799 1801
1800 1802 try:
1801 1803 return repo[rev][fn].renamed()
1802 1804 except revlog.LookupError:
1803 1805 pass
1804 1806 return None
1805 1807
1806 1808 df = False
1807 1809 if opts["date"]:
1808 1810 df = util.matchdate(opts["date"])
1809 1811
1810 1812 only_branches = opts['only_branch']
1811 1813
1812 1814 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1813 1815 for st, rev, fns in changeiter:
1814 1816 if st == 'add':
1815 1817 changenode = repo.changelog.node(rev)
1816 1818 parents = [p for p in repo.changelog.parentrevs(rev)
1817 1819 if p != nullrev]
1818 1820 if opts['no_merges'] and len(parents) == 2:
1819 1821 continue
1820 1822 if opts['only_merges'] and len(parents) != 2:
1821 1823 continue
1822 1824
1823 1825 if only_branches:
1824 1826 revbranch = get(rev)[5]['branch']
1825 1827 if revbranch not in only_branches:
1826 1828 continue
1827 1829
1828 1830 if df:
1829 1831 changes = get(rev)
1830 1832 if not df(changes[2][0]):
1831 1833 continue
1832 1834
1833 1835 if opts['keyword']:
1834 1836 changes = get(rev)
1835 1837 miss = 0
1836 1838 for k in [kw.lower() for kw in opts['keyword']]:
1837 1839 if not (k in changes[1].lower() or
1838 1840 k in changes[4].lower() or
1839 1841 k in " ".join(changes[3]).lower()):
1840 1842 miss = 1
1841 1843 break
1842 1844 if miss:
1843 1845 continue
1844 1846
1845 1847 copies = []
1846 1848 if opts.get('copies') and rev:
1847 1849 for fn in get(rev)[3]:
1848 1850 rename = getrenamed(fn, rev)
1849 1851 if rename:
1850 1852 copies.append((fn, rename[0]))
1851 1853 displayer.show(rev, changenode, copies=copies)
1852 1854 elif st == 'iter':
1853 1855 if count == limit: break
1854 1856 if displayer.flush(rev):
1855 1857 count += 1
1856 1858
1857 1859 def manifest(ui, repo, node=None, rev=None):
1858 1860 """output the current or given revision of the project manifest
1859 1861
1860 1862 Print a list of version controlled files for the given revision.
1861 1863 If no revision is given, the parent of the working directory is used,
1862 1864 or tip if no revision is checked out.
1863 1865
1864 1866 The manifest is the list of files being version controlled. If no revision
1865 1867 is given then the first parent of the working directory is used.
1866 1868
1867 1869 With -v flag, print file permissions, symlink and executable bits. With
1868 1870 --debug flag, print file revision hashes.
1869 1871 """
1870 1872
1871 1873 if rev and node:
1872 1874 raise util.Abort(_("please specify just one revision"))
1873 1875
1874 1876 if not node:
1875 1877 node = rev
1876 1878
1877 1879 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
1878 1880 ctx = repo[node]
1879 1881 for f in ctx:
1880 1882 if ui.debugflag:
1881 1883 ui.write("%40s " % hex(ctx.manifest()[f]))
1882 1884 if ui.verbose:
1883 1885 ui.write(decor[ctx.flags(f)])
1884 1886 ui.write("%s\n" % f)
1885 1887
1886 1888 def merge(ui, repo, node=None, force=None, rev=None):
1887 1889 """merge working directory with another revision
1888 1890
1889 1891 Merge the contents of the current working directory and the
1890 1892 requested revision. Files that changed between either parent are
1891 1893 marked as changed for the next commit and a commit must be
1892 1894 performed before any further updates are allowed.
1893 1895
1894 1896 If no revision is specified, the working directory's parent is a
1895 1897 head revision, and the current branch contains exactly one other head,
1896 1898 the other head is merged with by default. Otherwise, an explicit
1897 1899 revision to merge with must be provided.
1898 1900 """
1899 1901
1900 1902 if rev and node:
1901 1903 raise util.Abort(_("please specify just one revision"))
1902 1904 if not node:
1903 1905 node = rev
1904 1906
1905 1907 if not node:
1906 1908 branch = repo.changectx(None).branch()
1907 1909 bheads = repo.branchheads(branch)
1908 1910 if len(bheads) > 2:
1909 1911 raise util.Abort(_("branch '%s' has %d heads - "
1910 1912 "please merge with an explicit rev") %
1911 1913 (branch, len(bheads)))
1912 1914
1913 1915 parent = repo.dirstate.parents()[0]
1914 1916 if len(bheads) == 1:
1915 1917 if len(repo.heads()) > 1:
1916 1918 raise util.Abort(_("branch '%s' has one head - "
1917 1919 "please merge with an explicit rev") %
1918 1920 branch)
1919 1921 msg = _('there is nothing to merge')
1920 1922 if parent != repo.lookup(repo[None].branch()):
1921 1923 msg = _('%s - use "hg update" instead') % msg
1922 1924 raise util.Abort(msg)
1923 1925
1924 1926 if parent not in bheads:
1925 1927 raise util.Abort(_('working dir not at a head rev - '
1926 1928 'use "hg update" or merge with an explicit rev'))
1927 1929 node = parent == bheads[0] and bheads[-1] or bheads[0]
1928 1930 return hg.merge(repo, node, force=force)
1929 1931
1930 1932 def outgoing(ui, repo, dest=None, **opts):
1931 1933 """show changesets not found in destination
1932 1934
1933 1935 Show changesets not found in the specified destination repository or
1934 1936 the default push location. These are the changesets that would be pushed
1935 1937 if a push was requested.
1936 1938
1937 1939 See pull for valid destination format details.
1938 1940 """
1939 1941 limit = cmdutil.loglimit(opts)
1940 1942 dest, revs, checkout = hg.parseurl(
1941 1943 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
1942 1944 cmdutil.setremoteconfig(ui, opts)
1943 1945 if revs:
1944 1946 revs = [repo.lookup(rev) for rev in revs]
1945 1947
1946 1948 other = hg.repository(ui, dest)
1947 1949 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
1948 1950 o = repo.findoutgoing(other, force=opts['force'])
1949 1951 if not o:
1950 1952 ui.status(_("no changes found\n"))
1951 1953 return 1
1952 1954 o = repo.changelog.nodesbetween(o, revs)[0]
1953 1955 if opts['newest_first']:
1954 1956 o.reverse()
1955 1957 displayer = cmdutil.show_changeset(ui, repo, opts)
1956 1958 count = 0
1957 1959 for n in o:
1958 1960 if count >= limit:
1959 1961 break
1960 1962 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1961 1963 if opts['no_merges'] and len(parents) == 2:
1962 1964 continue
1963 1965 count += 1
1964 1966 displayer.show(changenode=n)
1965 1967
1966 1968 def parents(ui, repo, file_=None, **opts):
1967 1969 """show the parents of the working dir or revision
1968 1970
1969 1971 Print the working directory's parent revisions. If a
1970 1972 revision is given via --rev, the parent of that revision
1971 1973 will be printed. If a file argument is given, revision in
1972 1974 which the file was last changed (before the working directory
1973 1975 revision or the argument to --rev if given) is printed.
1974 1976 """
1975 1977 rev = opts.get('rev')
1976 1978 if rev:
1977 1979 ctx = repo[rev]
1978 1980 else:
1979 1981 ctx = repo[None]
1980 1982
1981 1983 if file_:
1982 1984 m = cmdutil.match(repo, (file_,), opts)
1983 1985 if m.anypats() or len(m.files()) != 1:
1984 1986 raise util.Abort(_('can only specify an explicit file name'))
1985 1987 file_ = m.files()[0]
1986 1988 filenodes = []
1987 1989 for cp in ctx.parents():
1988 1990 if not cp:
1989 1991 continue
1990 1992 try:
1991 1993 filenodes.append(cp.filenode(file_))
1992 1994 except revlog.LookupError:
1993 1995 pass
1994 1996 if not filenodes:
1995 1997 raise util.Abort(_("'%s' not found in manifest!") % file_)
1996 1998 fl = repo.file(file_)
1997 1999 p = [repo.lookup(fl.linkrev(fn)) for fn in filenodes]
1998 2000 else:
1999 2001 p = [cp.node() for cp in ctx.parents()]
2000 2002
2001 2003 displayer = cmdutil.show_changeset(ui, repo, opts)
2002 2004 for n in p:
2003 2005 if n != nullid:
2004 2006 displayer.show(changenode=n)
2005 2007
2006 2008 def paths(ui, repo, search=None):
2007 2009 """show definition of symbolic path names
2008 2010
2009 2011 Show definition of symbolic path name NAME. If no name is given, show
2010 2012 definition of available names.
2011 2013
2012 2014 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2013 2015 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2014 2016 """
2015 2017 if search:
2016 2018 for name, path in ui.configitems("paths"):
2017 2019 if name == search:
2018 2020 ui.write("%s\n" % util.hidepassword(path))
2019 2021 return
2020 2022 ui.warn(_("not found!\n"))
2021 2023 return 1
2022 2024 else:
2023 2025 for name, path in ui.configitems("paths"):
2024 2026 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
2025 2027
2026 2028 def postincoming(ui, repo, modheads, optupdate, checkout):
2027 2029 if modheads == 0:
2028 2030 return
2029 2031 if optupdate:
2030 2032 if modheads <= 1 or checkout:
2031 2033 return hg.update(repo, checkout)
2032 2034 else:
2033 2035 ui.status(_("not updating, since new heads added\n"))
2034 2036 if modheads > 1:
2035 2037 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2036 2038 else:
2037 2039 ui.status(_("(run 'hg update' to get a working copy)\n"))
2038 2040
2039 2041 def pull(ui, repo, source="default", **opts):
2040 2042 """pull changes from the specified source
2041 2043
2042 2044 Pull changes from a remote repository to a local one.
2043 2045
2044 2046 This finds all changes from the repository at the specified path
2045 2047 or URL and adds them to the local repository. By default, this
2046 2048 does not update the copy of the project in the working directory.
2047 2049
2048 2050 Valid URLs are of the form:
2049 2051
2050 2052 local/filesystem/path (or file://local/filesystem/path)
2051 2053 http://[user[:pass]@]host[:port]/[path]
2052 2054 https://[user[:pass]@]host[:port]/[path]
2053 2055 ssh://[user[:pass]@]host[:port]/[path]
2054 2056 static-http://host[:port]/[path]
2055 2057
2056 2058 Paths in the local filesystem can either point to Mercurial
2057 2059 repositories or to bundle files (as created by 'hg bundle' or
2058 2060 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
2059 2061 allows access to a Mercurial repository where you simply use a web
2060 2062 server to publish the .hg directory as static content.
2061 2063
2062 2064 An optional identifier after # indicates a particular branch, tag,
2063 2065 or changeset to pull.
2064 2066
2065 2067 Some notes about using SSH with Mercurial:
2066 2068 - SSH requires an accessible shell account on the destination machine
2067 2069 and a copy of hg in the remote path or specified with as remotecmd.
2068 2070 - path is relative to the remote user's home directory by default.
2069 2071 Use an extra slash at the start of a path to specify an absolute path:
2070 2072 ssh://example.com//tmp/repository
2071 2073 - Mercurial doesn't use its own compression via SSH; the right thing
2072 2074 to do is to configure it in your ~/.ssh/config, e.g.:
2073 2075 Host *.mylocalnetwork.example.com
2074 2076 Compression no
2075 2077 Host *
2076 2078 Compression yes
2077 2079 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2078 2080 with the --ssh command line option.
2079 2081 """
2080 2082 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
2081 2083 cmdutil.setremoteconfig(ui, opts)
2082 2084
2083 2085 other = hg.repository(ui, source)
2084 2086 ui.status(_('pulling from %s\n') % util.hidepassword(source))
2085 2087 if revs:
2086 2088 try:
2087 2089 revs = [other.lookup(rev) for rev in revs]
2088 2090 except NoCapability:
2089 2091 error = _("Other repository doesn't support revision lookup, "
2090 2092 "so a rev cannot be specified.")
2091 2093 raise util.Abort(error)
2092 2094
2093 2095 modheads = repo.pull(other, heads=revs, force=opts['force'])
2094 2096 return postincoming(ui, repo, modheads, opts['update'], checkout)
2095 2097
2096 2098 def push(ui, repo, dest=None, **opts):
2097 2099 """push changes to the specified destination
2098 2100
2099 2101 Push changes from the local repository to the given destination.
2100 2102
2101 2103 This is the symmetrical operation for pull. It helps to move
2102 2104 changes from the current repository to a different one. If the
2103 2105 destination is local this is identical to a pull in that directory
2104 2106 from the current one.
2105 2107
2106 2108 By default, push will refuse to run if it detects the result would
2107 2109 increase the number of remote heads. This generally indicates the
2108 2110 the client has forgotten to pull and merge before pushing.
2109 2111
2110 2112 Valid URLs are of the form:
2111 2113
2112 2114 local/filesystem/path (or file://local/filesystem/path)
2113 2115 ssh://[user[:pass]@]host[:port]/[path]
2114 2116 http://[user[:pass]@]host[:port]/[path]
2115 2117 https://[user[:pass]@]host[:port]/[path]
2116 2118
2117 2119 An optional identifier after # indicates a particular branch, tag,
2118 2120 or changeset to push. If -r is used, the named changeset and all its
2119 2121 ancestors will be pushed to the remote repository.
2120 2122
2121 2123 Look at the help text for the pull command for important details
2122 2124 about ssh:// URLs.
2123 2125
2124 2126 Pushing to http:// and https:// URLs is only possible, if this
2125 2127 feature is explicitly enabled on the remote Mercurial server.
2126 2128 """
2127 2129 dest, revs, checkout = hg.parseurl(
2128 2130 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
2129 2131 cmdutil.setremoteconfig(ui, opts)
2130 2132
2131 2133 other = hg.repository(ui, dest)
2132 2134 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
2133 2135 if revs:
2134 2136 revs = [repo.lookup(rev) for rev in revs]
2135 2137 r = repo.push(other, opts['force'], revs=revs)
2136 2138 return r == 0
2137 2139
2138 2140 def rawcommit(ui, repo, *pats, **opts):
2139 2141 """raw commit interface (DEPRECATED)
2140 2142
2141 2143 (DEPRECATED)
2142 2144 Lowlevel commit, for use in helper scripts.
2143 2145
2144 2146 This command is not intended to be used by normal users, as it is
2145 2147 primarily useful for importing from other SCMs.
2146 2148
2147 2149 This command is now deprecated and will be removed in a future
2148 2150 release, please use debugsetparents and commit instead.
2149 2151 """
2150 2152
2151 2153 ui.warn(_("(the rawcommit command is deprecated)\n"))
2152 2154
2153 2155 message = cmdutil.logmessage(opts)
2154 2156
2155 2157 files = cmdutil.match(repo, pats, opts).files()
2156 2158 if opts['files']:
2157 2159 files += open(opts['files']).read().splitlines()
2158 2160
2159 2161 parents = [repo.lookup(p) for p in opts['parent']]
2160 2162
2161 2163 try:
2162 2164 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2163 2165 except ValueError, inst:
2164 2166 raise util.Abort(str(inst))
2165 2167
2166 2168 def recover(ui, repo):
2167 2169 """roll back an interrupted transaction
2168 2170
2169 2171 Recover from an interrupted commit or pull.
2170 2172
2171 2173 This command tries to fix the repository status after an interrupted
2172 2174 operation. It should only be necessary when Mercurial suggests it.
2173 2175 """
2174 2176 if repo.recover():
2175 2177 return hg.verify(repo)
2176 2178 return 1
2177 2179
2178 2180 def remove(ui, repo, *pats, **opts):
2179 2181 """remove the specified files on the next commit
2180 2182
2181 2183 Schedule the indicated files for removal from the repository.
2182 2184
2183 2185 This only removes files from the current branch, not from the entire
2184 2186 project history. -A can be used to remove only files that have already
2185 2187 been deleted, -f can be used to force deletion, and -Af can be used
2186 2188 to remove files from the next revision without deleting them.
2187 2189
2188 2190 The following table details the behavior of remove for different file
2189 2191 states (columns) and option combinations (rows). The file states are
2190 2192 Added, Clean, Modified and Missing (as reported by hg status). The
2191 2193 actions are Warn, Remove (from branch) and Delete (from disk).
2192 2194
2193 2195 A C M !
2194 2196 none W RD W R
2195 2197 -f R RD RD R
2196 2198 -A W W W R
2197 2199 -Af R R R R
2198 2200
2199 2201 This command schedules the files to be removed at the next commit.
2200 2202 To undo a remove before that, see hg revert.
2201 2203 """
2202 2204
2203 2205 after, force = opts.get('after'), opts.get('force')
2204 2206 if not pats and not after:
2205 2207 raise util.Abort(_('no files specified'))
2206 2208
2207 2209 m = cmdutil.match(repo, pats, opts)
2208 2210 s = repo.status(match=m, clean=True)
2209 2211 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2210 2212
2211 2213 def warn(files, reason):
2212 2214 for f in files:
2213 2215 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2214 2216 % (m.rel(f), reason))
2215 2217
2216 2218 if force:
2217 2219 remove, forget = modified + deleted + clean, added
2218 2220 elif after:
2219 2221 remove, forget = deleted, []
2220 2222 warn(modified + added + clean, _('still exists'))
2221 2223 else:
2222 2224 remove, forget = deleted + clean, []
2223 2225 warn(modified, _('is modified'))
2224 2226 warn(added, _('has been marked for add'))
2225 2227
2226 2228 for f in util.sort(remove + forget):
2227 2229 if ui.verbose or not m.exact(f):
2228 2230 ui.status(_('removing %s\n') % m.rel(f))
2229 2231
2230 2232 repo.forget(forget)
2231 2233 repo.remove(remove, unlink=not after)
2232 2234
2233 2235 def rename(ui, repo, *pats, **opts):
2234 2236 """rename files; equivalent of copy + remove
2235 2237
2236 2238 Mark dest as copies of sources; mark sources for deletion. If
2237 2239 dest is a directory, copies are put in that directory. If dest is
2238 2240 a file, there can only be one source.
2239 2241
2240 2242 By default, this command copies the contents of files as they
2241 2243 stand in the working directory. If invoked with --after, the
2242 2244 operation is recorded, but no copying is performed.
2243 2245
2244 2246 This command takes effect in the next commit. To undo a rename
2245 2247 before that, see hg revert.
2246 2248 """
2247 2249 wlock = repo.wlock(False)
2248 2250 try:
2249 2251 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2250 2252 finally:
2251 2253 del wlock
2252 2254
2253 2255 def resolve(ui, repo, *pats, **opts):
2254 2256 """resolve file merges from a branch merge or update
2255 2257
2256 2258 This command will attempt to resolve unresolved merges from the
2257 2259 last update or merge command. This will use the local file
2258 2260 revision preserved at the last update or merge to cleanly retry
2259 2261 the file merge attempt. With no file or options specified, this
2260 2262 command will attempt to resolve all unresolved files.
2261 2263
2262 2264 The codes used to show the status of files are:
2263 2265 U = unresolved
2264 2266 R = resolved
2265 2267 """
2266 2268
2267 2269 if len([x for x in opts if opts[x]]) > 1:
2268 2270 raise util.Abort(_("too many options specified"))
2269 2271
2270 2272 ms = merge_.mergestate(repo)
2271 2273 m = cmdutil.match(repo, pats, opts)
2272 2274
2273 2275 for f in ms:
2274 2276 if m(f):
2275 2277 if opts.get("list"):
2276 2278 ui.write("%s %s\n" % (ms[f].upper(), f))
2277 2279 elif opts.get("mark"):
2278 2280 ms.mark(f, "r")
2279 2281 elif opts.get("unmark"):
2280 2282 ms.mark(f, "u")
2281 2283 else:
2282 2284 wctx = repo[None]
2283 2285 mctx = wctx.parents()[-1]
2284 2286 ms.resolve(f, wctx, mctx)
2285 2287
2286 2288 def revert(ui, repo, *pats, **opts):
2287 2289 """restore individual files or dirs to an earlier state
2288 2290
2289 2291 (use update -r to check out earlier revisions, revert does not
2290 2292 change the working dir parents)
2291 2293
2292 2294 With no revision specified, revert the named files or directories
2293 2295 to the contents they had in the parent of the working directory.
2294 2296 This restores the contents of the affected files to an unmodified
2295 2297 state and unschedules adds, removes, copies, and renames. If the
2296 2298 working directory has two parents, you must explicitly specify the
2297 2299 revision to revert to.
2298 2300
2299 2301 Using the -r option, revert the given files or directories to their
2300 2302 contents as of a specific revision. This can be helpful to "roll
2301 2303 back" some or all of an earlier change.
2302 2304 See 'hg help dates' for a list of formats valid for -d/--date.
2303 2305
2304 2306 Revert modifies the working directory. It does not commit any
2305 2307 changes, or change the parent of the working directory. If you
2306 2308 revert to a revision other than the parent of the working
2307 2309 directory, the reverted files will thus appear modified
2308 2310 afterwards.
2309 2311
2310 2312 If a file has been deleted, it is restored. If the executable
2311 2313 mode of a file was changed, it is reset.
2312 2314
2313 2315 If names are given, all files matching the names are reverted.
2314 2316 If no arguments are given, no files are reverted.
2315 2317
2316 2318 Modified files are saved with a .orig suffix before reverting.
2317 2319 To disable these backups, use --no-backup.
2318 2320 """
2319 2321
2320 2322 if opts["date"]:
2321 2323 if opts["rev"]:
2322 2324 raise util.Abort(_("you can't specify a revision and a date"))
2323 2325 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2324 2326
2325 2327 if not pats and not opts['all']:
2326 2328 raise util.Abort(_('no files or directories specified; '
2327 2329 'use --all to revert the whole repo'))
2328 2330
2329 2331 parent, p2 = repo.dirstate.parents()
2330 2332 if not opts['rev'] and p2 != nullid:
2331 2333 raise util.Abort(_('uncommitted merge - please provide a '
2332 2334 'specific revision'))
2333 2335 ctx = repo[opts['rev']]
2334 2336 node = ctx.node()
2335 2337 mf = ctx.manifest()
2336 2338 if node == parent:
2337 2339 pmf = mf
2338 2340 else:
2339 2341 pmf = None
2340 2342
2341 2343 # need all matching names in dirstate and manifest of target rev,
2342 2344 # so have to walk both. do not print errors if files exist in one
2343 2345 # but not other.
2344 2346
2345 2347 names = {}
2346 2348
2347 2349 wlock = repo.wlock()
2348 2350 try:
2349 2351 # walk dirstate.
2350 2352 files = []
2351 2353
2352 2354 m = cmdutil.match(repo, pats, opts)
2353 2355 m.bad = lambda x,y: False
2354 2356 for abs in repo.walk(m):
2355 2357 names[abs] = m.rel(abs), m.exact(abs)
2356 2358
2357 2359 # walk target manifest.
2358 2360
2359 2361 def badfn(path, msg):
2360 2362 if path in names:
2361 2363 return False
2362 2364 path_ = path + '/'
2363 2365 for f in names:
2364 2366 if f.startswith(path_):
2365 2367 return False
2366 2368 repo.ui.warn("%s: %s\n" % (m.rel(path), msg))
2367 2369 return False
2368 2370
2369 2371 m = cmdutil.match(repo, pats, opts)
2370 2372 m.bad = badfn
2371 2373 for abs in repo[node].walk(m):
2372 2374 if abs not in names:
2373 2375 names[abs] = m.rel(abs), m.exact(abs)
2374 2376
2375 2377 m = cmdutil.matchfiles(repo, names)
2376 2378 changes = repo.status(match=m)[:4]
2377 2379 modified, added, removed, deleted = map(dict.fromkeys, changes)
2378 2380
2379 2381 # if f is a rename, also revert the source
2380 2382 cwd = repo.getcwd()
2381 2383 for f in added:
2382 2384 src = repo.dirstate.copied(f)
2383 2385 if src and src not in names and repo.dirstate[src] == 'r':
2384 2386 removed[src] = None
2385 2387 names[src] = (repo.pathto(src, cwd), True)
2386 2388
2387 2389 def removeforget(abs):
2388 2390 if repo.dirstate[abs] == 'a':
2389 2391 return _('forgetting %s\n')
2390 2392 return _('removing %s\n')
2391 2393
2392 2394 revert = ([], _('reverting %s\n'))
2393 2395 add = ([], _('adding %s\n'))
2394 2396 remove = ([], removeforget)
2395 2397 undelete = ([], _('undeleting %s\n'))
2396 2398
2397 2399 disptable = (
2398 2400 # dispatch table:
2399 2401 # file state
2400 2402 # action if in target manifest
2401 2403 # action if not in target manifest
2402 2404 # make backup if in target manifest
2403 2405 # make backup if not in target manifest
2404 2406 (modified, revert, remove, True, True),
2405 2407 (added, revert, remove, True, False),
2406 2408 (removed, undelete, None, False, False),
2407 2409 (deleted, revert, remove, False, False),
2408 2410 )
2409 2411
2410 2412 for abs, (rel, exact) in util.sort(names.items()):
2411 2413 mfentry = mf.get(abs)
2412 2414 target = repo.wjoin(abs)
2413 2415 def handle(xlist, dobackup):
2414 2416 xlist[0].append(abs)
2415 2417 if dobackup and not opts['no_backup'] and util.lexists(target):
2416 2418 bakname = "%s.orig" % rel
2417 2419 ui.note(_('saving current version of %s as %s\n') %
2418 2420 (rel, bakname))
2419 2421 if not opts.get('dry_run'):
2420 2422 util.copyfile(target, bakname)
2421 2423 if ui.verbose or not exact:
2422 2424 msg = xlist[1]
2423 2425 if not isinstance(msg, basestring):
2424 2426 msg = msg(abs)
2425 2427 ui.status(msg % rel)
2426 2428 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2427 2429 if abs not in table: continue
2428 2430 # file has changed in dirstate
2429 2431 if mfentry:
2430 2432 handle(hitlist, backuphit)
2431 2433 elif misslist is not None:
2432 2434 handle(misslist, backupmiss)
2433 2435 break
2434 2436 else:
2435 2437 if abs not in repo.dirstate:
2436 2438 if mfentry:
2437 2439 handle(add, True)
2438 2440 elif exact:
2439 2441 ui.warn(_('file not managed: %s\n') % rel)
2440 2442 continue
2441 2443 # file has not changed in dirstate
2442 2444 if node == parent:
2443 2445 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2444 2446 continue
2445 2447 if pmf is None:
2446 2448 # only need parent manifest in this unlikely case,
2447 2449 # so do not read by default
2448 2450 pmf = repo[parent].manifest()
2449 2451 if abs in pmf:
2450 2452 if mfentry:
2451 2453 # if version of file is same in parent and target
2452 2454 # manifests, do nothing
2453 2455 if (pmf[abs] != mfentry or
2454 2456 pmf.flags(abs) != mf.flags(abs)):
2455 2457 handle(revert, False)
2456 2458 else:
2457 2459 handle(remove, False)
2458 2460
2459 2461 if not opts.get('dry_run'):
2460 2462 def checkout(f):
2461 2463 fc = ctx[f]
2462 2464 repo.wwrite(f, fc.data(), fc.flags())
2463 2465
2464 2466 audit_path = util.path_auditor(repo.root)
2465 2467 for f in remove[0]:
2466 2468 if repo.dirstate[f] == 'a':
2467 2469 repo.dirstate.forget(f)
2468 2470 continue
2469 2471 audit_path(f)
2470 2472 try:
2471 2473 util.unlink(repo.wjoin(f))
2472 2474 except OSError:
2473 2475 pass
2474 2476 repo.dirstate.remove(f)
2475 2477
2476 2478 normal = None
2477 2479 if node == parent:
2478 2480 # We're reverting to our parent. If possible, we'd like status
2479 2481 # to report the file as clean. We have to use normallookup for
2480 2482 # merges to avoid losing information about merged/dirty files.
2481 2483 if p2 != nullid:
2482 2484 normal = repo.dirstate.normallookup
2483 2485 else:
2484 2486 normal = repo.dirstate.normal
2485 2487 for f in revert[0]:
2486 2488 checkout(f)
2487 2489 if normal:
2488 2490 normal(f)
2489 2491
2490 2492 for f in add[0]:
2491 2493 checkout(f)
2492 2494 repo.dirstate.add(f)
2493 2495
2494 2496 normal = repo.dirstate.normallookup
2495 2497 if node == parent and p2 == nullid:
2496 2498 normal = repo.dirstate.normal
2497 2499 for f in undelete[0]:
2498 2500 checkout(f)
2499 2501 normal(f)
2500 2502
2501 2503 finally:
2502 2504 del wlock
2503 2505
2504 2506 def rollback(ui, repo):
2505 2507 """roll back the last transaction
2506 2508
2507 2509 This command should be used with care. There is only one level of
2508 2510 rollback, and there is no way to undo a rollback. It will also
2509 2511 restore the dirstate at the time of the last transaction, losing
2510 2512 any dirstate changes since that time.
2511 2513
2512 2514 Transactions are used to encapsulate the effects of all commands
2513 2515 that create new changesets or propagate existing changesets into a
2514 2516 repository. For example, the following commands are transactional,
2515 2517 and their effects can be rolled back:
2516 2518
2517 2519 commit
2518 2520 import
2519 2521 pull
2520 2522 push (with this repository as destination)
2521 2523 unbundle
2522 2524
2523 2525 This command is not intended for use on public repositories. Once
2524 2526 changes are visible for pull by other users, rolling a transaction
2525 2527 back locally is ineffective (someone else may already have pulled
2526 2528 the changes). Furthermore, a race is possible with readers of the
2527 2529 repository; for example an in-progress pull from the repository
2528 2530 may fail if a rollback is performed.
2529 2531 """
2530 2532 repo.rollback()
2531 2533
2532 2534 def root(ui, repo):
2533 2535 """print the root (top) of the current working dir
2534 2536
2535 2537 Print the root directory of the current repository.
2536 2538 """
2537 2539 ui.write(repo.root + "\n")
2538 2540
2539 2541 def serve(ui, repo, **opts):
2540 2542 """export the repository via HTTP
2541 2543
2542 2544 Start a local HTTP repository browser and pull server.
2543 2545
2544 2546 By default, the server logs accesses to stdout and errors to
2545 2547 stderr. Use the "-A" and "-E" options to log to files.
2546 2548 """
2547 2549
2548 2550 if opts["stdio"]:
2549 2551 if repo is None:
2550 2552 raise RepoError(_("There is no Mercurial repository here"
2551 2553 " (.hg not found)"))
2552 2554 s = sshserver.sshserver(ui, repo)
2553 2555 s.serve_forever()
2554 2556
2555 2557 parentui = ui.parentui or ui
2556 2558 optlist = ("name templates style address port prefix ipv6"
2557 2559 " accesslog errorlog webdir_conf certificate")
2558 2560 for o in optlist.split():
2559 2561 if opts[o]:
2560 2562 parentui.setconfig("web", o, str(opts[o]))
2561 2563 if (repo is not None) and (repo.ui != parentui):
2562 2564 repo.ui.setconfig("web", o, str(opts[o]))
2563 2565
2564 2566 if repo is None and not ui.config("web", "webdir_conf"):
2565 2567 raise RepoError(_("There is no Mercurial repository here"
2566 2568 " (.hg not found)"))
2567 2569
2568 2570 class service:
2569 2571 def init(self):
2570 2572 util.set_signal_handler()
2571 2573 self.httpd = hgweb.server.create_server(parentui, repo)
2572 2574
2573 2575 if not ui.verbose: return
2574 2576
2575 2577 if self.httpd.prefix:
2576 2578 prefix = self.httpd.prefix.strip('/') + '/'
2577 2579 else:
2578 2580 prefix = ''
2579 2581
2580 2582 port = ':%d' % self.httpd.port
2581 2583 if port == ':80':
2582 2584 port = ''
2583 2585
2584 2586 bindaddr = self.httpd.addr
2585 2587 if bindaddr == '0.0.0.0':
2586 2588 bindaddr = '*'
2587 2589 elif ':' in bindaddr: # IPv6
2588 2590 bindaddr = '[%s]' % bindaddr
2589 2591
2590 2592 fqaddr = self.httpd.fqaddr
2591 2593 if ':' in fqaddr:
2592 2594 fqaddr = '[%s]' % fqaddr
2593 2595 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2594 2596 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2595 2597
2596 2598 def run(self):
2597 2599 self.httpd.serve_forever()
2598 2600
2599 2601 service = service()
2600 2602
2601 2603 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2602 2604
2603 2605 def status(ui, repo, *pats, **opts):
2604 2606 """show changed files in the working directory
2605 2607
2606 2608 Show status of files in the repository. If names are given, only
2607 2609 files that match are shown. Files that are clean or ignored or
2608 2610 source of a copy/move operation, are not listed unless -c (clean),
2609 2611 -i (ignored), -C (copies) or -A is given. Unless options described
2610 2612 with "show only ..." are given, the options -mardu are used.
2611 2613
2612 2614 Option -q/--quiet hides untracked (unknown and ignored) files
2613 2615 unless explicitly requested with -u/--unknown or -i/-ignored.
2614 2616
2615 2617 NOTE: status may appear to disagree with diff if permissions have
2616 2618 changed or a merge has occurred. The standard diff format does not
2617 2619 report permission changes and diff only reports changes relative
2618 2620 to one merge parent.
2619 2621
2620 2622 If one revision is given, it is used as the base revision.
2621 2623 If two revisions are given, the difference between them is shown.
2622 2624
2623 2625 The codes used to show the status of files are:
2624 2626 M = modified
2625 2627 A = added
2626 2628 R = removed
2627 2629 C = clean
2628 2630 ! = deleted, but still tracked
2629 2631 ? = not tracked
2630 2632 I = ignored
2631 2633 = the previous added file was copied from here
2632 2634 """
2633 2635
2634 2636 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2635 2637 cwd = (pats and repo.getcwd()) or ''
2636 2638 end = opts['print0'] and '\0' or '\n'
2637 2639 copy = {}
2638 2640 states = 'modified added removed deleted unknown ignored clean'.split()
2639 2641 show = [k for k in states if opts[k]]
2640 2642 if opts['all']:
2641 2643 show += ui.quiet and (states[:4] + ['clean']) or states
2642 2644 if not show:
2643 2645 show = ui.quiet and states[:4] or states[:5]
2644 2646
2645 2647 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2646 2648 'ignored' in show, 'clean' in show, 'unknown' in show)
2647 2649 changestates = zip(states, 'MAR!?IC', stat)
2648 2650
2649 2651 if (opts['all'] or opts['copies']) and not opts['no_status']:
2650 2652 ctxn = repo[nullid]
2651 2653 ctx1 = repo[node1]
2652 2654 ctx2 = repo[node2]
2653 2655 added = stat[1]
2654 2656 if node2 is None:
2655 2657 added = stat[0] + stat[1] # merged?
2656 2658
2657 2659 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].items():
2658 2660 if k in added:
2659 2661 copy[k] = v
2660 2662 elif v in added:
2661 2663 copy[v] = k
2662 2664
2663 2665 for state, char, files in changestates:
2664 2666 if state in show:
2665 2667 format = "%s %%s%s" % (char, end)
2666 2668 if opts['no_status']:
2667 2669 format = "%%s%s" % end
2668 2670
2669 2671 for f in files:
2670 2672 ui.write(format % repo.pathto(f, cwd))
2671 2673 if f in copy:
2672 2674 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2673 2675
2674 2676 def tag(ui, repo, name1, *names, **opts):
2675 2677 """add one or more tags for the current or given revision
2676 2678
2677 2679 Name a particular revision using <name>.
2678 2680
2679 2681 Tags are used to name particular revisions of the repository and are
2680 2682 very useful to compare different revisions, to go back to significant
2681 2683 earlier versions or to mark branch points as releases, etc.
2682 2684
2683 2685 If no revision is given, the parent of the working directory is used,
2684 2686 or tip if no revision is checked out.
2685 2687
2686 2688 To facilitate version control, distribution, and merging of tags,
2687 2689 they are stored as a file named ".hgtags" which is managed
2688 2690 similarly to other project files and can be hand-edited if
2689 2691 necessary. The file '.hg/localtags' is used for local tags (not
2690 2692 shared among repositories).
2691 2693
2692 2694 See 'hg help dates' for a list of formats valid for -d/--date.
2693 2695 """
2694 2696
2695 2697 rev_ = "."
2696 2698 names = (name1,) + names
2697 2699 if len(names) != len(dict.fromkeys(names)):
2698 2700 raise util.Abort(_('tag names must be unique'))
2699 2701 for n in names:
2700 2702 if n in ['tip', '.', 'null']:
2701 2703 raise util.Abort(_('the name \'%s\' is reserved') % n)
2702 2704 if opts['rev'] and opts['remove']:
2703 2705 raise util.Abort(_("--rev and --remove are incompatible"))
2704 2706 if opts['rev']:
2705 2707 rev_ = opts['rev']
2706 2708 message = opts['message']
2707 2709 if opts['remove']:
2708 2710 expectedtype = opts['local'] and 'local' or 'global'
2709 2711 for n in names:
2710 2712 if not repo.tagtype(n):
2711 2713 raise util.Abort(_('tag \'%s\' does not exist') % n)
2712 2714 if repo.tagtype(n) != expectedtype:
2713 2715 raise util.Abort(_('tag \'%s\' is not a %s tag') %
2714 2716 (n, expectedtype))
2715 2717 rev_ = nullid
2716 2718 if not message:
2717 2719 message = _('Removed tag %s') % ', '.join(names)
2718 2720 elif not opts['force']:
2719 2721 for n in names:
2720 2722 if n in repo.tags():
2721 2723 raise util.Abort(_('tag \'%s\' already exists '
2722 2724 '(use -f to force)') % n)
2723 2725 if not rev_ and repo.dirstate.parents()[1] != nullid:
2724 2726 raise util.Abort(_('uncommitted merge - please provide a '
2725 2727 'specific revision'))
2726 2728 r = repo[rev_].node()
2727 2729
2728 2730 if not message:
2729 2731 message = (_('Added tag %s for changeset %s') %
2730 2732 (', '.join(names), short(r)))
2731 2733
2732 2734 date = opts.get('date')
2733 2735 if date:
2734 2736 date = util.parsedate(date)
2735 2737
2736 2738 repo.tag(names, r, message, opts['local'], opts['user'], date)
2737 2739
2738 2740 def tags(ui, repo):
2739 2741 """list repository tags
2740 2742
2741 2743 List the repository tags.
2742 2744
2743 2745 This lists both regular and local tags. When the -v/--verbose switch
2744 2746 is used, a third column "local" is printed for local tags.
2745 2747 """
2746 2748
2747 2749 l = repo.tagslist()
2748 2750 l.reverse()
2749 2751 hexfunc = ui.debugflag and hex or short
2750 2752 tagtype = ""
2751 2753
2752 2754 for t, n in l:
2753 2755 if ui.quiet:
2754 2756 ui.write("%s\n" % t)
2755 2757 continue
2756 2758
2757 2759 try:
2758 2760 hn = hexfunc(n)
2759 2761 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2760 2762 except revlog.LookupError:
2761 2763 r = " ?:%s" % hn
2762 2764 else:
2763 2765 spaces = " " * (30 - util.locallen(t))
2764 2766 if ui.verbose:
2765 2767 if repo.tagtype(t) == 'local':
2766 2768 tagtype = " local"
2767 2769 else:
2768 2770 tagtype = ""
2769 2771 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2770 2772
2771 2773 def tip(ui, repo, **opts):
2772 2774 """show the tip revision
2773 2775
2774 2776 The tip revision (usually just called the tip) is the most
2775 2777 recently added changeset in the repository, the most recently
2776 2778 changed head.
2777 2779
2778 2780 If you have just made a commit, that commit will be the tip. If
2779 2781 you have just pulled changes from another repository, the tip of
2780 2782 that repository becomes the current tip. The "tip" tag is special
2781 2783 and cannot be renamed or assigned to a different changeset.
2782 2784 """
2783 2785 cmdutil.show_changeset(ui, repo, opts).show(len(repo) - 1)
2784 2786
2785 2787 def unbundle(ui, repo, fname1, *fnames, **opts):
2786 2788 """apply one or more changegroup files
2787 2789
2788 2790 Apply one or more compressed changegroup files generated by the
2789 2791 bundle command.
2790 2792 """
2791 2793 fnames = (fname1,) + fnames
2792 2794
2793 2795 lock = None
2794 2796 try:
2795 2797 lock = repo.lock()
2796 2798 for fname in fnames:
2797 2799 if os.path.exists(fname):
2798 2800 f = open(fname, "rb")
2799 2801 else:
2800 2802 f = urllib.urlopen(fname)
2801 2803 gen = changegroup.readbundle(f, fname)
2802 2804 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2803 2805 finally:
2804 2806 del lock
2805 2807
2806 2808 return postincoming(ui, repo, modheads, opts['update'], None)
2807 2809
2808 2810 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2809 2811 """update working directory
2810 2812
2811 2813 Update the repository's working directory to the specified revision,
2812 2814 or the tip of the current branch if none is specified.
2813 2815
2814 2816 If the requested revision is a descendant of the working
2815 2817 directory, any outstanding changes in the working directory will
2816 2818 be merged into the result. If it is not directly descended but is
2817 2819 on the same named branch, update aborts with a suggestion to use
2818 2820 merge or update -C instead.
2819 2821
2820 2822 If the requested revision is on a different named branch and the
2821 2823 working directory is clean, update quietly switches branches.
2822 2824
2823 2825 If you want to update just one file to an older revision, use revert.
2824 2826
2825 2827 See 'hg help dates' for a list of formats valid for --date.
2826 2828 """
2827 2829 if rev and node:
2828 2830 raise util.Abort(_("please specify just one revision"))
2829 2831
2830 2832 if not rev:
2831 2833 rev = node
2832 2834
2833 2835 if date:
2834 2836 if rev:
2835 2837 raise util.Abort(_("you can't specify a revision and a date"))
2836 2838 rev = cmdutil.finddate(ui, repo, date)
2837 2839
2838 2840 if clean:
2839 2841 return hg.clean(repo, rev)
2840 2842 else:
2841 2843 return hg.update(repo, rev)
2842 2844
2843 2845 def verify(ui, repo):
2844 2846 """verify the integrity of the repository
2845 2847
2846 2848 Verify the integrity of the current repository.
2847 2849
2848 2850 This will perform an extensive check of the repository's
2849 2851 integrity, validating the hashes and checksums of each entry in
2850 2852 the changelog, manifest, and tracked files, as well as the
2851 2853 integrity of their crosslinks and indices.
2852 2854 """
2853 2855 return hg.verify(repo)
2854 2856
2855 2857 def version_(ui):
2856 2858 """output version and copyright information"""
2857 2859 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2858 2860 % version.get_version())
2859 2861 ui.status(_(
2860 2862 "\nCopyright (C) 2005-2008 Matt Mackall <mpm@selenic.com> and others\n"
2861 2863 "This is free software; see the source for copying conditions. "
2862 2864 "There is NO\nwarranty; "
2863 2865 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2864 2866 ))
2865 2867
2866 2868 # Command options and aliases are listed here, alphabetically
2867 2869
2868 2870 globalopts = [
2869 2871 ('R', 'repository', '',
2870 2872 _('repository root directory or symbolic path name')),
2871 2873 ('', 'cwd', '', _('change working directory')),
2872 2874 ('y', 'noninteractive', None,
2873 2875 _('do not prompt, assume \'yes\' for any required answers')),
2874 2876 ('q', 'quiet', None, _('suppress output')),
2875 2877 ('v', 'verbose', None, _('enable additional output')),
2876 2878 ('', 'config', [], _('set/override config option')),
2877 2879 ('', 'debug', None, _('enable debugging output')),
2878 2880 ('', 'debugger', None, _('start debugger')),
2879 2881 ('', 'encoding', util._encoding, _('set the charset encoding')),
2880 2882 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2881 2883 ('', 'lsprof', None, _('print improved command execution profile')),
2882 2884 ('', 'traceback', None, _('print traceback on exception')),
2883 2885 ('', 'time', None, _('time how long the command takes')),
2884 2886 ('', 'profile', None, _('print command execution profile')),
2885 2887 ('', 'version', None, _('output version information and exit')),
2886 2888 ('h', 'help', None, _('display help and exit')),
2887 2889 ]
2888 2890
2889 2891 dryrunopts = [('n', 'dry-run', None,
2890 2892 _('do not perform actions, just print output'))]
2891 2893
2892 2894 remoteopts = [
2893 2895 ('e', 'ssh', '', _('specify ssh command to use')),
2894 2896 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2895 2897 ]
2896 2898
2897 2899 walkopts = [
2898 2900 ('I', 'include', [], _('include names matching the given patterns')),
2899 2901 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2900 2902 ]
2901 2903
2902 2904 commitopts = [
2903 2905 ('m', 'message', '', _('use <text> as commit message')),
2904 2906 ('l', 'logfile', '', _('read commit message from <file>')),
2905 2907 ]
2906 2908
2907 2909 commitopts2 = [
2908 2910 ('d', 'date', '', _('record datecode as commit date')),
2909 2911 ('u', 'user', '', _('record user as committer')),
2910 2912 ]
2911 2913
2912 2914 templateopts = [
2913 2915 ('', 'style', '', _('display using template map file')),
2914 2916 ('', 'template', '', _('display with template')),
2915 2917 ]
2916 2918
2917 2919 logopts = [
2918 2920 ('p', 'patch', None, _('show patch')),
2919 2921 ('l', 'limit', '', _('limit number of changes displayed')),
2920 2922 ('M', 'no-merges', None, _('do not show merges')),
2921 2923 ] + templateopts
2922 2924
2923 2925 diffopts = [
2924 2926 ('a', 'text', None, _('treat all files as text')),
2925 2927 ('g', 'git', None, _('use git extended diff format')),
2926 2928 ('', 'nodates', None, _("don't include dates in diff headers"))
2927 2929 ]
2928 2930
2929 2931 diffopts2 = [
2930 2932 ('p', 'show-function', None, _('show which function each change is in')),
2931 2933 ('w', 'ignore-all-space', None,
2932 2934 _('ignore white space when comparing lines')),
2933 2935 ('b', 'ignore-space-change', None,
2934 2936 _('ignore changes in the amount of white space')),
2935 2937 ('B', 'ignore-blank-lines', None,
2936 2938 _('ignore changes whose lines are all blank')),
2937 2939 ('U', 'unified', '', _('number of lines of context to show'))
2938 2940 ]
2939 2941
2940 2942 table = {
2941 2943 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2942 2944 "addremove":
2943 2945 (addremove,
2944 2946 [('s', 'similarity', '',
2945 2947 _('guess renamed files by similarity (0<=s<=100)')),
2946 2948 ] + walkopts + dryrunopts,
2947 2949 _('hg addremove [OPTION]... [FILE]...')),
2948 2950 "^annotate|blame":
2949 2951 (annotate,
2950 2952 [('r', 'rev', '', _('annotate the specified revision')),
2951 2953 ('f', 'follow', None, _('follow file copies and renames')),
2952 2954 ('a', 'text', None, _('treat all files as text')),
2953 2955 ('u', 'user', None, _('list the author (long with -v)')),
2954 2956 ('d', 'date', None, _('list the date (short with -q)')),
2955 2957 ('n', 'number', None, _('list the revision number (default)')),
2956 2958 ('c', 'changeset', None, _('list the changeset')),
2957 2959 ('l', 'line-number', None,
2958 2960 _('show line number at the first appearance'))
2959 2961 ] + walkopts,
2960 2962 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
2961 2963 "archive":
2962 2964 (archive,
2963 2965 [('', 'no-decode', None, _('do not pass files through decoders')),
2964 2966 ('p', 'prefix', '', _('directory prefix for files in archive')),
2965 2967 ('r', 'rev', '', _('revision to distribute')),
2966 2968 ('t', 'type', '', _('type of distribution to create')),
2967 2969 ] + walkopts,
2968 2970 _('hg archive [OPTION]... DEST')),
2969 2971 "backout":
2970 2972 (backout,
2971 2973 [('', 'merge', None,
2972 2974 _('merge with old dirstate parent after backout')),
2973 2975 ('', 'parent', '', _('parent to choose when backing out merge')),
2974 2976 ('r', 'rev', '', _('revision to backout')),
2975 2977 ] + walkopts + commitopts + commitopts2,
2976 2978 _('hg backout [OPTION]... [-r] REV')),
2977 2979 "bisect":
2978 2980 (bisect,
2979 2981 [('r', 'reset', False, _('reset bisect state')),
2980 2982 ('g', 'good', False, _('mark changeset good')),
2981 2983 ('b', 'bad', False, _('mark changeset bad')),
2982 2984 ('s', 'skip', False, _('skip testing changeset')),
2983 2985 ('U', 'noupdate', False, _('do not update to target'))],
2984 2986 _("hg bisect [-gbsr] [REV]")),
2985 2987 "branch":
2986 2988 (branch,
2987 2989 [('f', 'force', None,
2988 2990 _('set branch name even if it shadows an existing branch'))],
2989 2991 _('hg branch [-f] [NAME]')),
2990 2992 "branches":
2991 2993 (branches,
2992 2994 [('a', 'active', False,
2993 2995 _('show only branches that have unmerged heads'))],
2994 2996 _('hg branches [-a]')),
2995 2997 "bundle":
2996 2998 (bundle,
2997 2999 [('f', 'force', None,
2998 3000 _('run even when remote repository is unrelated')),
2999 3001 ('r', 'rev', [],
3000 3002 _('a changeset up to which you would like to bundle')),
3001 3003 ('', 'base', [],
3002 3004 _('a base changeset to specify instead of a destination')),
3003 3005 ('a', 'all', None, _('bundle all changesets in the repository')),
3004 3006 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3005 3007 ] + remoteopts,
3006 3008 _('hg bundle [-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3007 3009 "cat":
3008 3010 (cat,
3009 3011 [('o', 'output', '', _('print output to file with formatted name')),
3010 3012 ('r', 'rev', '', _('print the given revision')),
3011 3013 ('', 'decode', None, _('apply any matching decode filter')),
3012 3014 ] + walkopts,
3013 3015 _('hg cat [OPTION]... FILE...')),
3014 3016 "^clone":
3015 3017 (clone,
3016 3018 [('U', 'noupdate', None,
3017 3019 _('the clone will only contain a repository (no working copy)')),
3018 3020 ('r', 'rev', [],
3019 3021 _('a changeset you would like to have after cloning')),
3020 3022 ('', 'pull', None, _('use pull protocol to copy metadata')),
3021 3023 ('', 'uncompressed', None,
3022 3024 _('use uncompressed transfer (fast over LAN)')),
3023 3025 ] + remoteopts,
3024 3026 _('hg clone [OPTION]... SOURCE [DEST]')),
3025 3027 "^commit|ci":
3026 3028 (commit,
3027 3029 [('A', 'addremove', None,
3028 3030 _('mark new/missing files as added/removed before committing')),
3029 3031 ] + walkopts + commitopts + commitopts2,
3030 3032 _('hg commit [OPTION]... [FILE]...')),
3031 3033 "copy|cp":
3032 3034 (copy,
3033 3035 [('A', 'after', None, _('record a copy that has already occurred')),
3034 3036 ('f', 'force', None,
3035 3037 _('forcibly copy over an existing managed file')),
3036 3038 ] + walkopts + dryrunopts,
3037 3039 _('hg copy [OPTION]... [SOURCE]... DEST')),
3038 3040 "debugancestor": (debugancestor, [],
3039 3041 _('hg debugancestor [INDEX] REV1 REV2')),
3040 3042 "debugcheckstate": (debugcheckstate, [], _('hg debugcheckstate')),
3041 3043 "debugcomplete":
3042 3044 (debugcomplete,
3043 3045 [('o', 'options', None, _('show the command options'))],
3044 3046 _('hg debugcomplete [-o] CMD')),
3045 3047 "debugdate":
3046 3048 (debugdate,
3047 3049 [('e', 'extended', None, _('try extended date formats'))],
3048 3050 _('hg debugdate [-e] DATE [RANGE]')),
3049 3051 "debugdata": (debugdata, [], _('hg debugdata FILE REV')),
3050 3052 "debugfsinfo": (debugfsinfo, [], _('hg debugfsinfo [PATH]')),
3051 3053 "debugindex": (debugindex, [], _('hg debugindex FILE')),
3052 3054 "debugindexdot": (debugindexdot, [], _('hg debugindexdot FILE')),
3053 3055 "debuginstall": (debuginstall, [], _('hg debuginstall')),
3054 3056 "debugrawcommit|rawcommit":
3055 3057 (rawcommit,
3056 3058 [('p', 'parent', [], _('parent')),
3057 3059 ('F', 'files', '', _('file list'))
3058 3060 ] + commitopts + commitopts2,
3059 3061 _('hg debugrawcommit [OPTION]... [FILE]...')),
3060 3062 "debugrebuildstate":
3061 3063 (debugrebuildstate,
3062 3064 [('r', 'rev', '', _('revision to rebuild to'))],
3063 3065 _('hg debugrebuildstate [-r REV] [REV]')),
3064 3066 "debugrename":
3065 3067 (debugrename,
3066 3068 [('r', 'rev', '', _('revision to debug'))],
3067 3069 _('hg debugrename [-r REV] FILE')),
3068 3070 "debugsetparents":
3069 3071 (debugsetparents,
3070 3072 [],
3071 3073 _('hg debugsetparents REV1 [REV2]')),
3072 3074 "debugstate":
3073 3075 (debugstate,
3074 3076 [('', 'nodates', None, _('do not display the saved mtime'))],
3075 3077 _('hg debugstate [OPTS]')),
3076 3078 "debugwalk": (debugwalk, walkopts, _('hg debugwalk [OPTION]... [FILE]...')),
3077 3079 "^diff":
3078 3080 (diff,
3079 3081 [('r', 'rev', [], _('revision'))
3080 3082 ] + diffopts + diffopts2 + walkopts,
3081 3083 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3082 3084 "^export":
3083 3085 (export,
3084 3086 [('o', 'output', '', _('print output to file with formatted name')),
3085 3087 ('', 'switch-parent', None, _('diff against the second parent'))
3086 3088 ] + diffopts,
3087 3089 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
3088 3090 "grep":
3089 3091 (grep,
3090 3092 [('0', 'print0', None, _('end fields with NUL')),
3091 3093 ('', 'all', None, _('print all revisions that match')),
3092 3094 ('f', 'follow', None,
3093 3095 _('follow changeset history, or file history across copies and renames')),
3094 3096 ('i', 'ignore-case', None, _('ignore case when matching')),
3095 3097 ('l', 'files-with-matches', None,
3096 3098 _('print only filenames and revs that match')),
3097 3099 ('n', 'line-number', None, _('print matching line numbers')),
3098 3100 ('r', 'rev', [], _('search in given revision range')),
3099 3101 ('u', 'user', None, _('list the author (long with -v)')),
3100 3102 ('d', 'date', None, _('list the date (short with -q)')),
3101 3103 ] + walkopts,
3102 3104 _('hg grep [OPTION]... PATTERN [FILE]...')),
3103 3105 "heads":
3104 3106 (heads,
3105 3107 [('r', 'rev', '', _('show only heads which are descendants of rev')),
3106 3108 ] + templateopts,
3107 3109 _('hg heads [-r REV] [REV]...')),
3108 3110 "help": (help_, [], _('hg help [COMMAND]')),
3109 3111 "identify|id":
3110 3112 (identify,
3111 3113 [('r', 'rev', '', _('identify the specified rev')),
3112 3114 ('n', 'num', None, _('show local revision number')),
3113 3115 ('i', 'id', None, _('show global revision id')),
3114 3116 ('b', 'branch', None, _('show branch')),
3115 3117 ('t', 'tags', None, _('show tags'))],
3116 3118 _('hg identify [-nibt] [-r REV] [SOURCE]')),
3117 3119 "import|patch":
3118 3120 (import_,
3119 3121 [('p', 'strip', 1,
3120 3122 _('directory strip option for patch. This has the same\n'
3121 3123 'meaning as the corresponding patch option')),
3122 3124 ('b', 'base', '', _('base path')),
3123 3125 ('f', 'force', None,
3124 3126 _('skip check for outstanding uncommitted changes')),
3125 3127 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3126 3128 ('', 'exact', None,
3127 3129 _('apply patch to the nodes from which it was generated')),
3128 3130 ('', 'import-branch', None,
3129 3131 _('Use any branch information in patch (implied by --exact)'))] +
3130 3132 commitopts + commitopts2,
3131 3133 _('hg import [OPTION]... PATCH...')),
3132 3134 "incoming|in":
3133 3135 (incoming,
3134 3136 [('f', 'force', None,
3135 3137 _('run even when remote repository is unrelated')),
3136 3138 ('n', 'newest-first', None, _('show newest record first')),
3137 3139 ('', 'bundle', '', _('file to store the bundles into')),
3138 3140 ('r', 'rev', [],
3139 3141 _('a specific revision up to which you would like to pull')),
3140 3142 ] + logopts + remoteopts,
3141 3143 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
3142 3144 ' [--bundle FILENAME] [SOURCE]')),
3143 3145 "^init":
3144 3146 (init,
3145 3147 remoteopts,
3146 3148 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
3147 3149 "locate":
3148 3150 (locate,
3149 3151 [('r', 'rev', '', _('search the repository as it stood at rev')),
3150 3152 ('0', 'print0', None,
3151 3153 _('end filenames with NUL, for use with xargs')),
3152 3154 ('f', 'fullpath', None,
3153 3155 _('print complete paths from the filesystem root')),
3154 3156 ] + walkopts,
3155 3157 _('hg locate [OPTION]... [PATTERN]...')),
3156 3158 "^log|history":
3157 3159 (log,
3158 3160 [('f', 'follow', None,
3159 3161 _('follow changeset history, or file history across copies and renames')),
3160 3162 ('', 'follow-first', None,
3161 3163 _('only follow the first parent of merge changesets')),
3162 3164 ('d', 'date', '', _('show revs matching date spec')),
3163 3165 ('C', 'copies', None, _('show copied files')),
3164 3166 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3165 3167 ('r', 'rev', [], _('show the specified revision or range')),
3166 3168 ('', 'removed', None, _('include revs where files were removed')),
3167 3169 ('m', 'only-merges', None, _('show only merges')),
3168 3170 ('b', 'only-branch', [],
3169 3171 _('show only changesets within the given named branch')),
3170 3172 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3171 3173 ] + logopts + walkopts,
3172 3174 _('hg log [OPTION]... [FILE]')),
3173 3175 "manifest":
3174 3176 (manifest,
3175 3177 [('r', 'rev', '', _('revision to display'))],
3176 3178 _('hg manifest [-r REV]')),
3177 3179 "^merge":
3178 3180 (merge,
3179 3181 [('f', 'force', None, _('force a merge with outstanding changes')),
3180 3182 ('r', 'rev', '', _('revision to merge')),
3181 3183 ],
3182 3184 _('hg merge [-f] [[-r] REV]')),
3183 3185 "outgoing|out":
3184 3186 (outgoing,
3185 3187 [('f', 'force', None,
3186 3188 _('run even when remote repository is unrelated')),
3187 3189 ('r', 'rev', [],
3188 3190 _('a specific revision up to which you would like to push')),
3189 3191 ('n', 'newest-first', None, _('show newest record first')),
3190 3192 ] + logopts + remoteopts,
3191 3193 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3192 3194 "^parents":
3193 3195 (parents,
3194 3196 [('r', 'rev', '', _('show parents from the specified rev')),
3195 3197 ] + templateopts,
3196 3198 _('hg parents [-r REV] [FILE]')),
3197 3199 "paths": (paths, [], _('hg paths [NAME]')),
3198 3200 "^pull":
3199 3201 (pull,
3200 3202 [('u', 'update', None,
3201 3203 _('update to new tip if changesets were pulled')),
3202 3204 ('f', 'force', None,
3203 3205 _('run even when remote repository is unrelated')),
3204 3206 ('r', 'rev', [],
3205 3207 _('a specific revision up to which you would like to pull')),
3206 3208 ] + remoteopts,
3207 3209 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3208 3210 "^push":
3209 3211 (push,
3210 3212 [('f', 'force', None, _('force push')),
3211 3213 ('r', 'rev', [],
3212 3214 _('a specific revision up to which you would like to push')),
3213 3215 ] + remoteopts,
3214 3216 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3215 3217 "recover": (recover, [], _('hg recover')),
3216 3218 "^remove|rm":
3217 3219 (remove,
3218 3220 [('A', 'after', None, _('record delete for missing files')),
3219 3221 ('f', 'force', None,
3220 3222 _('remove (and delete) file even if added or modified')),
3221 3223 ] + walkopts,
3222 3224 _('hg remove [OPTION]... FILE...')),
3223 3225 "rename|mv":
3224 3226 (rename,
3225 3227 [('A', 'after', None, _('record a rename that has already occurred')),
3226 3228 ('f', 'force', None,
3227 3229 _('forcibly copy over an existing managed file')),
3228 3230 ] + walkopts + dryrunopts,
3229 3231 _('hg rename [OPTION]... SOURCE... DEST')),
3230 3232 "resolve":
3231 3233 (resolve,
3232 3234 [('l', 'list', None, _('list state of files needing merge')),
3233 3235 ('m', 'mark', None, _('mark files as resolved')),
3234 3236 ('u', 'unmark', None, _('unmark files as resolved'))],
3235 3237 ('hg resolve [OPTION] [FILES...]')),
3236 3238 "revert":
3237 3239 (revert,
3238 3240 [('a', 'all', None, _('revert all changes when no arguments given')),
3239 3241 ('d', 'date', '', _('tipmost revision matching date')),
3240 3242 ('r', 'rev', '', _('revision to revert to')),
3241 3243 ('', 'no-backup', None, _('do not save backup copies of files')),
3242 3244 ] + walkopts + dryrunopts,
3243 3245 _('hg revert [OPTION]... [-r REV] [NAME]...')),
3244 3246 "rollback": (rollback, [], _('hg rollback')),
3245 3247 "root": (root, [], _('hg root')),
3246 3248 "^serve":
3247 3249 (serve,
3248 3250 [('A', 'accesslog', '', _('name of access log file to write to')),
3249 3251 ('d', 'daemon', None, _('run server in background')),
3250 3252 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3251 3253 ('E', 'errorlog', '', _('name of error log file to write to')),
3252 3254 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3253 3255 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3254 3256 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3255 3257 ('n', 'name', '',
3256 3258 _('name to show in web pages (default: working dir)')),
3257 3259 ('', 'webdir-conf', '', _('name of the webdir config file'
3258 3260 ' (serve more than one repo)')),
3259 3261 ('', 'pid-file', '', _('name of file to write process ID to')),
3260 3262 ('', 'stdio', None, _('for remote clients')),
3261 3263 ('t', 'templates', '', _('web templates to use')),
3262 3264 ('', 'style', '', _('template style to use')),
3263 3265 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3264 3266 ('', 'certificate', '', _('SSL certificate file'))],
3265 3267 _('hg serve [OPTION]...')),
3266 3268 "showconfig|debugconfig":
3267 3269 (showconfig,
3268 3270 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3269 3271 _('hg showconfig [-u] [NAME]...')),
3270 3272 "^status|st":
3271 3273 (status,
3272 3274 [('A', 'all', None, _('show status of all files')),
3273 3275 ('m', 'modified', None, _('show only modified files')),
3274 3276 ('a', 'added', None, _('show only added files')),
3275 3277 ('r', 'removed', None, _('show only removed files')),
3276 3278 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3277 3279 ('c', 'clean', None, _('show only files without changes')),
3278 3280 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3279 3281 ('i', 'ignored', None, _('show only ignored files')),
3280 3282 ('n', 'no-status', None, _('hide status prefix')),
3281 3283 ('C', 'copies', None, _('show source of copied files')),
3282 3284 ('0', 'print0', None,
3283 3285 _('end filenames with NUL, for use with xargs')),
3284 3286 ('', 'rev', [], _('show difference from revision')),
3285 3287 ] + walkopts,
3286 3288 _('hg status [OPTION]... [FILE]...')),
3287 3289 "tag":
3288 3290 (tag,
3289 3291 [('f', 'force', None, _('replace existing tag')),
3290 3292 ('l', 'local', None, _('make the tag local')),
3291 3293 ('r', 'rev', '', _('revision to tag')),
3292 3294 ('', 'remove', None, _('remove a tag')),
3293 3295 # -l/--local is already there, commitopts cannot be used
3294 3296 ('m', 'message', '', _('use <text> as commit message')),
3295 3297 ] + commitopts2,
3296 3298 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3297 3299 "tags": (tags, [], _('hg tags')),
3298 3300 "tip":
3299 3301 (tip,
3300 3302 [('p', 'patch', None, _('show patch')),
3301 3303 ] + templateopts,
3302 3304 _('hg tip [-p]')),
3303 3305 "unbundle":
3304 3306 (unbundle,
3305 3307 [('u', 'update', None,
3306 3308 _('update to new tip if changesets were unbundled'))],
3307 3309 _('hg unbundle [-u] FILE...')),
3308 3310 "^update|up|checkout|co":
3309 3311 (update,
3310 3312 [('C', 'clean', None, _('overwrite locally modified files (no backup)')),
3311 3313 ('d', 'date', '', _('tipmost revision matching date')),
3312 3314 ('r', 'rev', '', _('revision'))],
3313 3315 _('hg update [-C] [-d DATE] [[-r] REV]')),
3314 3316 "verify": (verify, [], _('hg verify')),
3315 3317 "version": (version_, [], _('hg version')),
3316 3318 }
3317 3319
3318 3320 norepo = ("clone init version help debugcomplete debugdata"
3319 3321 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3320 3322 optionalrepo = ("identify paths serve showconfig debugancestor")
@@ -1,620 +1,640 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 shutil
16 16 import signal
17 17 import sys
18 18 import tempfile
19 19 import time
20 20
21 21 # reserved exit code to skip test (used by hghave)
22 22 SKIPPED_STATUS = 80
23 23 SKIPPED_PREFIX = 'skipped: '
24 24
25 25 required_tools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"]
26 26
27 27 defaults = {
28 28 'jobs': ('HGTEST_JOBS', 1),
29 29 'timeout': ('HGTEST_TIMEOUT', 180),
30 30 'port': ('HGTEST_PORT', 20059),
31 31 }
32 32
33 33 parser = optparse.OptionParser("%prog [options] [tests]")
34 34 parser.add_option("-C", "--annotate", action="store_true",
35 35 help="output files annotated with coverage")
36 36 parser.add_option("--child", type="int",
37 37 help="run as child process, summary to given fd")
38 38 parser.add_option("-c", "--cover", action="store_true",
39 39 help="print a test coverage report")
40 40 parser.add_option("-f", "--first", action="store_true",
41 41 help="exit on the first test failure")
42 42 parser.add_option("-i", "--interactive", action="store_true",
43 43 help="prompt to accept changed output")
44 44 parser.add_option("-j", "--jobs", type="int",
45 45 help="number of jobs to run in parallel"
46 46 " (default: $%s or %d)" % defaults['jobs'])
47 47 parser.add_option("--keep-tmpdir", action="store_true",
48 48 help="keep temporary directory after running tests"
49 49 " (best used with --tmpdir)")
50 50 parser.add_option("-R", "--restart", action="store_true",
51 51 help="restart at last error")
52 52 parser.add_option("-p", "--port", type="int",
53 53 help="port on which servers should listen"
54 54 " (default: $%s or %d)" % defaults['port'])
55 55 parser.add_option("-r", "--retest", action="store_true",
56 56 help="retest failed tests")
57 57 parser.add_option("-s", "--cover_stdlib", action="store_true",
58 58 help="print a test coverage report inc. standard libraries")
59 59 parser.add_option("-t", "--timeout", type="int",
60 60 help="kill errant tests after TIMEOUT seconds"
61 61 " (default: $%s or %d)" % defaults['timeout'])
62 62 parser.add_option("--tmpdir", type="string",
63 63 help="run tests in the given temporary directory")
64 64 parser.add_option("-v", "--verbose", action="store_true",
65 65 help="output verbose messages")
66 66 parser.add_option("--with-hg", type="string",
67 67 help="test existing install at given location")
68 68
69 69 for option, default in defaults.items():
70 70 defaults[option] = int(os.environ.get(*default))
71 71 parser.set_defaults(**defaults)
72 72 (options, args) = parser.parse_args()
73 73 verbose = options.verbose
74 74 coverage = options.cover or options.cover_stdlib or options.annotate
75 75 python = sys.executable
76 76
77 77 if options.jobs < 1:
78 78 print >> sys.stderr, 'ERROR: -j/--jobs must be positive'
79 79 sys.exit(1)
80 80 if options.interactive and options.jobs > 1:
81 81 print >> sys.stderr, 'ERROR: cannot mix -interactive and --jobs > 1'
82 82 sys.exit(1)
83 83
84 84 def rename(src, dst):
85 85 """Like os.rename(), trade atomicity and opened files friendliness
86 86 for existing destination support.
87 87 """
88 88 shutil.copy(src, dst)
89 89 os.remove(src)
90 90
91 91 def vlog(*msg):
92 92 if verbose:
93 93 for m in msg:
94 94 print m,
95 95 print
96 96
97 97 def splitnewlines(text):
98 98 '''like str.splitlines, but only split on newlines.
99 99 keep line endings.'''
100 100 i = 0
101 101 lines = []
102 102 while True:
103 103 n = text.find('\n', i)
104 104 if n == -1:
105 105 last = text[i:]
106 106 if last:
107 107 lines.append(last)
108 108 return lines
109 109 lines.append(text[i:n+1])
110 110 i = n + 1
111 111
112 112 def extract_missing_features(lines):
113 113 '''Extract missing/unknown features log lines as a list'''
114 114 missing = []
115 115 for line in lines:
116 116 if not line.startswith(SKIPPED_PREFIX):
117 117 continue
118 118 line = line.splitlines()[0]
119 119 missing.append(line[len(SKIPPED_PREFIX):])
120 120
121 121 return missing
122 122
123 123 def show_diff(expected, output):
124 124 for line in difflib.unified_diff(expected, output,
125 125 "Expected output", "Test output"):
126 126 sys.stdout.write(line)
127 127
128 128 def find_program(program):
129 129 """Search PATH for a executable program"""
130 130 for p in os.environ.get('PATH', os.defpath).split(os.pathsep):
131 131 name = os.path.join(p, program)
132 132 if os.access(name, os.X_OK):
133 133 return name
134 134 return None
135 135
136 136 def check_required_tools():
137 137 # Before we go any further, check for pre-requisite tools
138 138 # stuff from coreutils (cat, rm, etc) are not tested
139 139 for p in required_tools:
140 140 if os.name == 'nt':
141 141 p += '.exe'
142 142 found = find_program(p)
143 143 if found:
144 144 vlog("# Found prerequisite", p, "at", found)
145 145 else:
146 146 print "WARNING: Did not find prerequisite tool: "+p
147 147
148 148 def cleanup_exit():
149 149 if not options.keep_tmpdir:
150 150 if verbose:
151 151 print "# Cleaning up HGTMP", HGTMP
152 152 shutil.rmtree(HGTMP, True)
153 153
154 154 def use_correct_python():
155 155 # some tests run python interpreter. they must use same
156 156 # interpreter we use or bad things will happen.
157 157 exedir, exename = os.path.split(sys.executable)
158 158 if exename == 'python':
159 159 path = find_program('python')
160 160 if os.path.dirname(path) == exedir:
161 161 return
162 162 vlog('# Making python executable in test path use correct Python')
163 163 my_python = os.path.join(BINDIR, 'python')
164 164 try:
165 165 os.symlink(sys.executable, my_python)
166 166 except AttributeError:
167 167 # windows fallback
168 168 shutil.copyfile(sys.executable, my_python)
169 169 shutil.copymode(sys.executable, my_python)
170 170
171 171 def install_hg():
172 172 global python
173 173 vlog("# Performing temporary installation of HG")
174 174 installerrs = os.path.join("tests", "install.err")
175 175
176 176 # Run installer in hg root
177 177 os.chdir(os.path.join(os.path.dirname(sys.argv[0]), '..'))
178 178 cmd = ('%s setup.py clean --all'
179 179 ' install --force --home="%s" --install-lib="%s"'
180 180 ' --install-scripts="%s" >%s 2>&1'
181 181 % (sys.executable, INST, PYTHONDIR, BINDIR, installerrs))
182 182 vlog("# Running", cmd)
183 183 if os.system(cmd) == 0:
184 184 if not verbose:
185 185 os.remove(installerrs)
186 186 else:
187 187 f = open(installerrs)
188 188 for line in f:
189 189 print line,
190 190 f.close()
191 191 sys.exit(1)
192 192 os.chdir(TESTDIR)
193 193
194 194 os.environ["PATH"] = "%s%s%s" % (BINDIR, os.pathsep, os.environ["PATH"])
195 195
196 196 pydir = os.pathsep.join([PYTHONDIR, TESTDIR])
197 197 pythonpath = os.environ.get("PYTHONPATH")
198 198 if pythonpath:
199 199 pythonpath = pydir + os.pathsep + pythonpath
200 200 else:
201 201 pythonpath = pydir
202 202 os.environ["PYTHONPATH"] = pythonpath
203 203
204 204 use_correct_python()
205 global hgpkg
206 hgpkg = _hgpath()
205 207
206 208 if coverage:
207 209 vlog("# Installing coverage wrapper")
208 210 os.environ['COVERAGE_FILE'] = COVERAGE_FILE
209 211 if os.path.exists(COVERAGE_FILE):
210 212 os.unlink(COVERAGE_FILE)
211 213 # Create a wrapper script to invoke hg via coverage.py
212 214 os.rename(os.path.join(BINDIR, "hg"), os.path.join(BINDIR, "_hg.py"))
213 215 f = open(os.path.join(BINDIR, 'hg'), 'w')
214 216 f.write('#!' + sys.executable + '\n')
215 217 f.write('import sys, os; os.execv(sys.executable, [sys.executable, '
216 218 '"%s", "-x", "%s"] + sys.argv[1:])\n' %
217 219 (os.path.join(TESTDIR, 'coverage.py'),
218 220 os.path.join(BINDIR, '_hg.py')))
219 221 f.close()
220 222 os.chmod(os.path.join(BINDIR, 'hg'), 0700)
221 223 python = '"%s" "%s" -x' % (sys.executable,
222 224 os.path.join(TESTDIR,'coverage.py'))
223 225
224 226 def output_coverage():
225 227 vlog("# Producing coverage report")
226 228 omit = [BINDIR, TESTDIR, PYTHONDIR]
227 229 if not options.cover_stdlib:
228 230 # Exclude as system paths (ignoring empty strings seen on win)
229 231 omit += [x for x in sys.path if x != '']
230 232 omit = ','.join(omit)
231 233 os.chdir(PYTHONDIR)
232 234 cmd = '"%s" "%s" -i -r "--omit=%s"' % (
233 235 sys.executable, os.path.join(TESTDIR, 'coverage.py'), omit)
234 236 vlog("# Running: "+cmd)
235 237 os.system(cmd)
236 238 if options.annotate:
237 239 adir = os.path.join(TESTDIR, 'annotated')
238 240 if not os.path.isdir(adir):
239 241 os.mkdir(adir)
240 242 cmd = '"%s" "%s" -i -a "--directory=%s" "--omit=%s"' % (
241 243 sys.executable, os.path.join(TESTDIR, 'coverage.py'),
242 244 adir, omit)
243 245 vlog("# Running: "+cmd)
244 246 os.system(cmd)
245 247
246 248 class Timeout(Exception):
247 249 pass
248 250
249 251 def alarmed(signum, frame):
250 252 raise Timeout
251 253
252 254 def run(cmd):
253 255 """Run command in a sub-process, capturing the output (stdout and stderr).
254 256 Return the exist code, and output."""
255 257 # TODO: Use subprocess.Popen if we're running on Python 2.4
256 258 if os.name == 'nt':
257 259 tochild, fromchild = os.popen4(cmd)
258 260 tochild.close()
259 261 output = fromchild.read()
260 262 ret = fromchild.close()
261 263 if ret == None:
262 264 ret = 0
263 265 else:
264 266 proc = popen2.Popen4(cmd)
265 267 try:
266 268 output = ''
267 269 proc.tochild.close()
268 270 output = proc.fromchild.read()
269 271 ret = proc.wait()
270 272 if os.WIFEXITED(ret):
271 273 ret = os.WEXITSTATUS(ret)
272 274 except Timeout:
273 275 vlog('# Process %d timed out - killing it' % proc.pid)
274 276 os.kill(proc.pid, signal.SIGTERM)
275 277 ret = proc.wait()
276 278 if ret == 0:
277 279 ret = signal.SIGTERM << 8
278 280 output += ("\n### Abort: timeout after %d seconds.\n"
279 281 % options.timeout)
280 282 return ret, splitnewlines(output)
281 283
282 284 def run_one(test, skips, fails):
283 285 '''tristate output:
284 286 None -> skipped
285 287 True -> passed
286 288 False -> failed'''
287 289
288 290 def skip(msg):
289 291 if not verbose:
290 292 skips.append((test, msg))
291 293 else:
292 294 print "\nSkipping %s: %s" % (test, msg)
293 295 return None
294 296
295 297 def fail(msg):
296 298 fails.append((test, msg))
297 299 print "\nERROR: %s %s" % (test, msg)
298 300 return None
299 301
300 302 vlog("# Test", test)
301 303
302 304 # create a fresh hgrc
303 305 hgrc = file(HGRCPATH, 'w+')
304 306 hgrc.write('[ui]\n')
305 307 hgrc.write('slash = True\n')
306 308 hgrc.write('[defaults]\n')
307 309 hgrc.write('backout = -d "0 0"\n')
308 310 hgrc.write('commit = -d "0 0"\n')
309 311 hgrc.write('debugrawcommit = -d "0 0"\n')
310 312 hgrc.write('tag = -d "0 0"\n')
311 313 hgrc.close()
312 314
313 315 err = os.path.join(TESTDIR, test+".err")
314 316 ref = os.path.join(TESTDIR, test+".out")
315 317 testpath = os.path.join(TESTDIR, test)
316 318
317 319 if os.path.exists(err):
318 320 os.remove(err) # Remove any previous output files
319 321
320 322 # Make a tmp subdirectory to work in
321 323 tmpd = os.path.join(HGTMP, test)
322 324 os.mkdir(tmpd)
323 325 os.chdir(tmpd)
324 326
325 327 try:
326 328 tf = open(testpath)
327 329 firstline = tf.readline().rstrip()
328 330 tf.close()
329 331 except:
330 332 firstline = ''
331 333 lctest = test.lower()
332 334
333 335 if lctest.endswith('.py') or firstline == '#!/usr/bin/env python':
334 336 cmd = '%s "%s"' % (python, testpath)
335 337 elif lctest.endswith('.bat'):
336 338 # do not run batch scripts on non-windows
337 339 if os.name != 'nt':
338 340 return skip("batch script")
339 341 # To reliably get the error code from batch files on WinXP,
340 342 # the "cmd /c call" prefix is needed. Grrr
341 343 cmd = 'cmd /c call "%s"' % testpath
342 344 else:
343 345 # do not run shell scripts on windows
344 346 if os.name == 'nt':
345 347 return skip("shell script")
346 348 # do not try to run non-executable programs
347 349 if not os.access(testpath, os.X_OK):
348 350 return skip("not executable")
349 351 cmd = '"%s"' % testpath
350 352
351 353 if options.timeout > 0:
352 354 signal.alarm(options.timeout)
353 355
354 356 vlog("# Running", cmd)
355 357 ret, out = run(cmd)
356 358 vlog("# Ret was:", ret)
357 359
358 360 if options.timeout > 0:
359 361 signal.alarm(0)
360 362
361 363 skipped = (ret == SKIPPED_STATUS)
362 364 # If reference output file exists, check test output against it
363 365 if os.path.exists(ref):
364 366 f = open(ref, "r")
365 367 ref_out = splitnewlines(f.read())
366 368 f.close()
367 369 else:
368 370 ref_out = []
369 371 if skipped:
370 372 missing = extract_missing_features(out)
371 373 if not missing:
372 374 missing = ['irrelevant']
373 375 skip(missing[-1])
374 376 elif out != ref_out:
375 377 if ret:
376 378 fail("output changed and returned error code %d" % ret)
377 379 else:
378 380 fail("output changed")
379 381 show_diff(ref_out, out)
380 382 ret = 1
381 383 elif ret:
382 384 fail("returned error code %d" % ret)
383 385
384 386 if not verbose:
385 387 sys.stdout.write(skipped and 's' or '.')
386 388 sys.stdout.flush()
387 389
388 390 if ret != 0 and not skipped:
389 391 # Save errors to a file for diagnosis
390 392 f = open(err, "wb")
391 393 for line in out:
392 394 f.write(line)
393 395 f.close()
394 396
395 397 # Kill off any leftover daemon processes
396 398 try:
397 399 fp = file(DAEMON_PIDS)
398 400 for line in fp:
399 401 try:
400 402 pid = int(line)
401 403 except ValueError:
402 404 continue
403 405 try:
404 406 os.kill(pid, 0)
405 407 vlog('# Killing daemon process %d' % pid)
406 408 os.kill(pid, signal.SIGTERM)
407 409 time.sleep(0.25)
408 410 os.kill(pid, 0)
409 411 vlog('# Daemon process %d is stuck - really killing it' % pid)
410 412 os.kill(pid, signal.SIGKILL)
411 413 except OSError, err:
412 414 if err.errno != errno.ESRCH:
413 415 raise
414 416 fp.close()
415 417 os.unlink(DAEMON_PIDS)
416 418 except IOError:
417 419 pass
418 420
419 421 os.chdir(TESTDIR)
420 422 if not options.keep_tmpdir:
421 423 shutil.rmtree(tmpd, True)
422 424 if skipped:
423 425 return None
424 426 return ret == 0
425 427
426 428 if not options.child:
427 429 os.umask(022)
428 430
429 431 check_required_tools()
430 432
431 433 # Reset some environment variables to well-known values so that
432 434 # the tests produce repeatable output.
433 435 os.environ['LANG'] = os.environ['LC_ALL'] = 'C'
434 436 os.environ['TZ'] = 'GMT'
435 437 os.environ["EMAIL"] = "Foo Bar <foo.bar@example.com>"
436 438
437 439 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
438 440 HGTMP = os.environ['HGTMP'] = tempfile.mkdtemp('', 'hgtests.', options.tmpdir)
439 441 DAEMON_PIDS = None
440 442 HGRCPATH = None
441 443
442 444 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
443 445 os.environ["HGMERGE"] = "internal:merge"
444 446 os.environ["HGUSER"] = "test"
445 447 os.environ["HGENCODING"] = "ascii"
446 448 os.environ["HGENCODINGMODE"] = "strict"
447 449 os.environ["HGPORT"] = str(options.port)
448 450 os.environ["HGPORT1"] = str(options.port + 1)
449 451 os.environ["HGPORT2"] = str(options.port + 2)
450 452
451 453 if options.with_hg:
452 454 INST = options.with_hg
453 455 else:
454 456 INST = os.path.join(HGTMP, "install")
455 457 BINDIR = os.path.join(INST, "bin")
456 458 PYTHONDIR = os.path.join(INST, "lib", "python")
457 459 COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
458 460
461 def _hgpath():
462 cmd = '%s -c "import mercurial; print mercurial.__path__[0]"'
463 hgpath = os.popen(cmd % python)
464 path = hgpath.read().strip()
465 hgpath.close()
466 return path
467
468 expecthg = os.path.join(HGTMP, 'install', 'lib', 'python', 'mercurial')
469 hgpkg = None
470
459 471 def run_children(tests):
460 472 if not options.with_hg:
461 473 install_hg()
462 474
463 475 optcopy = dict(options.__dict__)
464 476 optcopy['jobs'] = 1
465 477 optcopy['with_hg'] = INST
466 478 opts = []
467 479 for opt, value in optcopy.iteritems():
468 480 name = '--' + opt.replace('_', '-')
469 481 if value is True:
470 482 opts.append(name)
471 483 elif value is not None:
472 484 opts.append(name + '=' + str(value))
473 485
474 486 tests.reverse()
475 487 jobs = [[] for j in xrange(options.jobs)]
476 488 while tests:
477 489 for j in xrange(options.jobs):
478 490 if not tests: break
479 491 jobs[j].append(tests.pop())
480 492 fps = {}
481 493 for j in xrange(len(jobs)):
482 494 job = jobs[j]
483 495 if not job:
484 496 continue
485 497 rfd, wfd = os.pipe()
486 498 childopts = ['--child=%d' % wfd, '--port=%d' % (options.port + j * 3)]
487 499 cmdline = [python, sys.argv[0]] + opts + childopts + job
488 500 vlog(' '.join(cmdline))
489 501 fps[os.spawnvp(os.P_NOWAIT, cmdline[0], cmdline)] = os.fdopen(rfd, 'r')
490 502 os.close(wfd)
491 503 failures = 0
492 504 tested, skipped, failed = 0, 0, 0
493 505 skips = []
494 506 fails = []
495 507 while fps:
496 508 pid, status = os.wait()
497 509 fp = fps.pop(pid)
498 510 l = fp.read().splitlines()
499 511 test, skip, fail = map(int, l[:3])
500 512 split = -fail or len(l)
501 513 for s in l[3:split]:
502 514 skips.append(s.split(" ", 1))
503 515 for s in l[split:]:
504 516 fails.append(s.split(" ", 1))
505 517 tested += test
506 518 skipped += skip
507 519 failed += fail
508 520 vlog('pid %d exited, status %d' % (pid, status))
509 521 failures |= status
510 522 print
511 523 for s in skips:
512 524 print "Skipped %s: %s" % (s[0], s[1])
513 525 for s in fails:
514 526 print "Failed %s: %s" % (s[0], s[1])
527
528 if hgpkg != expecthg:
529 print '# Tested unexpected mercurial: %s' % hgpkg
515 530 print "# Ran %d tests, %d skipped, %d failed." % (
516 531 tested, skipped, failed)
517 532 sys.exit(failures != 0)
518 533
519 534 def run_tests(tests):
520 535 global DAEMON_PIDS, HGRCPATH
521 536 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
522 537 HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
523 538
524 539 try:
525 540 if not options.with_hg:
526 541 install_hg()
527 542
543 if hgpkg != expecthg:
544 print '# Testing unexpected mercurial: %s' % hgpkg
545
528 546 if options.timeout > 0:
529 547 try:
530 548 signal.signal(signal.SIGALRM, alarmed)
531 549 vlog('# Running tests with %d-second timeout' %
532 550 options.timeout)
533 551 except AttributeError:
534 552 print 'WARNING: cannot run tests with timeouts'
535 553 options.timeout = 0
536 554
537 555 tested = 0
538 556 failed = 0
539 557 skipped = 0
540 558
541 559 if options.restart:
542 560 orig = list(tests)
543 561 while tests:
544 562 if os.path.exists(tests[0] + ".err"):
545 563 break
546 564 tests.pop(0)
547 565 if not tests:
548 566 print "running all tests"
549 567 tests = orig
550 568
551 569 skips = []
552 570 fails = []
553 571 for test in tests:
554 572 if options.retest and not os.path.exists(test + ".err"):
555 573 skipped += 1
556 574 continue
557 575 ret = run_one(test, skips, fails)
558 576 if ret is None:
559 577 skipped += 1
560 578 elif not ret:
561 579 if options.interactive:
562 580 print "Accept this change? [n] ",
563 581 answer = sys.stdin.readline().strip()
564 582 if answer.lower() in "y yes".split():
565 583 rename(test + ".err", test + ".out")
566 584 tested += 1
567 585 fails.pop()
568 586 continue
569 587 failed += 1
570 588 if options.first:
571 589 break
572 590 tested += 1
573 591
574 592 if options.child:
575 593 fp = os.fdopen(options.child, 'w')
576 594 fp.write('%d\n%d\n%d\n' % (tested, skipped, failed))
577 595 for s in skips:
578 596 fp.write("%s %s\n" % s)
579 597 for s in fails:
580 598 fp.write("%s %s\n" % s)
581 599 fp.close()
582 600 else:
583 601 print
584 602 for s in skips:
585 603 print "Skipped %s: %s" % s
586 604 for s in fails:
587 605 print "Failed %s: %s" % s
606 if hgpkg != expecthg:
607 print '# Tested unexpected mercurial: %s' % hgpkg
588 608 print "# Ran %d tests, %d skipped, %d failed." % (
589 609 tested, skipped, failed)
590 610
591 611 if coverage:
592 612 output_coverage()
593 613 except KeyboardInterrupt:
594 614 failed = True
595 615 print "\ninterrupted!"
596 616
597 617 if failed:
598 618 sys.exit(1)
599 619
600 620 if len(args) == 0:
601 621 args = os.listdir(".")
602 622 args.sort()
603 623
604 624 tests = []
605 625 for test in args:
606 626 if (test.startswith("test-") and '~' not in test and
607 627 ('.' not in test or test.endswith('.py') or
608 628 test.endswith('.bat'))):
609 629 tests.append(test)
610 630
611 631 vlog("# Using TESTDIR", TESTDIR)
612 632 vlog("# Using HGTMP", HGTMP)
613 633
614 634 try:
615 635 if len(tests) > 1 and options.jobs > 1:
616 636 run_children(tests)
617 637 else:
618 638 run_tests(tests)
619 639 finally:
620 640 cleanup_exit()
@@ -1,73 +1,74 b''
1 1 #!/bin/sh
2 2
3 3 set -e
4 4
5 5 echo % init
6 6 hg init
7 7
8 8 echo % committing changes
9 9 count=0
10 10 echo > a
11 11 while test $count -lt 32 ; do
12 12 echo 'a' >> a
13 13 test $count -eq 0 && hg add
14 14 hg ci -m "msg $count" -d "$count 0"
15 15 echo % committed changeset $count
16 16 count=`expr $count + 1`
17 17 done
18 18
19 19 echo % log
20 20 hg log
21 21
22 22 echo % hg up -C
23 23 hg up -C
24 24
25 25 echo % bisect test
26 26 hg bisect -r
27 27 hg bisect -b
28 28 hg bisect -g 1
29 29 hg bisect -g
30 30 echo skip
31 31 hg bisect -s
32 32 hg bisect -g
33 33 hg bisect -g
34 34 hg bisect -b
35 35 hg bisect -g
36 36
37 37 echo % bisect reverse test
38 38 hg bisect -r
39 39 hg bisect -b null
40 40 hg bisect -g tip
41 41 hg bisect -g
42 42 echo skip
43 43 hg bisect -s
44 44 hg bisect -g
45 45 hg bisect -g
46 46 hg bisect -b
47 47 hg bisect -g
48 48
49 49 hg bisect -r
50 50 hg bisect -g tip
51 51 hg bisect -b tip || echo error
52 52
53 53 hg bisect -r
54 54 hg bisect -g null
55 55 hg bisect -bU tip
56 56 hg id
57 57
58 echo % test no action message
59 hg bisect
60
61 # reproduce AssertionError (issue1228 and issue1182)
58 echo % reproduce AssertionError, issue1228 and issue1182
62 59 hg bisect -r
63 60 hg bisect -b 4
64 61 hg bisect -g 0
65 62 hg bisect -s
66 63 hg bisect -s
67 64 hg bisect -s
68 65
69 # reproduce non converging bisect (issue1182)
66 echo % reproduce non converging bisect, issue1182
70 67 hg bisect -r
71 68 hg bisect -g 0
72 69 hg bisect -b 2
73 70 hg bisect -s
71
72 echo % test no action
73 hg bisect -r
74 hg bisect
@@ -1,285 +1,287 b''
1 1 % init
2 2 % committing changes
3 3 adding a
4 4 % committed changeset 0
5 5 % committed changeset 1
6 6 % committed changeset 2
7 7 % committed changeset 3
8 8 % committed changeset 4
9 9 % committed changeset 5
10 10 % committed changeset 6
11 11 % committed changeset 7
12 12 % committed changeset 8
13 13 % committed changeset 9
14 14 % committed changeset 10
15 15 % committed changeset 11
16 16 % committed changeset 12
17 17 % committed changeset 13
18 18 % committed changeset 14
19 19 % committed changeset 15
20 20 % committed changeset 16
21 21 % committed changeset 17
22 22 % committed changeset 18
23 23 % committed changeset 19
24 24 % committed changeset 20
25 25 % committed changeset 21
26 26 % committed changeset 22
27 27 % committed changeset 23
28 28 % committed changeset 24
29 29 % committed changeset 25
30 30 % committed changeset 26
31 31 % committed changeset 27
32 32 % committed changeset 28
33 33 % committed changeset 29
34 34 % committed changeset 30
35 35 % committed changeset 31
36 36 % log
37 37 changeset: 31:58c80a7c8a40
38 38 tag: tip
39 39 user: test
40 40 date: Thu Jan 01 00:00:31 1970 +0000
41 41 summary: msg 31
42 42
43 43 changeset: 30:ed2d2f24b11c
44 44 user: test
45 45 date: Thu Jan 01 00:00:30 1970 +0000
46 46 summary: msg 30
47 47
48 48 changeset: 29:b5bd63375ab9
49 49 user: test
50 50 date: Thu Jan 01 00:00:29 1970 +0000
51 51 summary: msg 29
52 52
53 53 changeset: 28:8e0c2264c8af
54 54 user: test
55 55 date: Thu Jan 01 00:00:28 1970 +0000
56 56 summary: msg 28
57 57
58 58 changeset: 27:288867a866e9
59 59 user: test
60 60 date: Thu Jan 01 00:00:27 1970 +0000
61 61 summary: msg 27
62 62
63 63 changeset: 26:3efc6fd51aeb
64 64 user: test
65 65 date: Thu Jan 01 00:00:26 1970 +0000
66 66 summary: msg 26
67 67
68 68 changeset: 25:02a84173a97a
69 69 user: test
70 70 date: Thu Jan 01 00:00:25 1970 +0000
71 71 summary: msg 25
72 72
73 73 changeset: 24:10e0acd3809e
74 74 user: test
75 75 date: Thu Jan 01 00:00:24 1970 +0000
76 76 summary: msg 24
77 77
78 78 changeset: 23:5ec79163bff4
79 79 user: test
80 80 date: Thu Jan 01 00:00:23 1970 +0000
81 81 summary: msg 23
82 82
83 83 changeset: 22:06c7993750ce
84 84 user: test
85 85 date: Thu Jan 01 00:00:22 1970 +0000
86 86 summary: msg 22
87 87
88 88 changeset: 21:e5db6aa3fe2a
89 89 user: test
90 90 date: Thu Jan 01 00:00:21 1970 +0000
91 91 summary: msg 21
92 92
93 93 changeset: 20:7128fb4fdbc9
94 94 user: test
95 95 date: Thu Jan 01 00:00:20 1970 +0000
96 96 summary: msg 20
97 97
98 98 changeset: 19:52798545b482
99 99 user: test
100 100 date: Thu Jan 01 00:00:19 1970 +0000
101 101 summary: msg 19
102 102
103 103 changeset: 18:86977a90077e
104 104 user: test
105 105 date: Thu Jan 01 00:00:18 1970 +0000
106 106 summary: msg 18
107 107
108 108 changeset: 17:03515f4a9080
109 109 user: test
110 110 date: Thu Jan 01 00:00:17 1970 +0000
111 111 summary: msg 17
112 112
113 113 changeset: 16:a2e6ea4973e9
114 114 user: test
115 115 date: Thu Jan 01 00:00:16 1970 +0000
116 116 summary: msg 16
117 117
118 118 changeset: 15:e7fa0811edb0
119 119 user: test
120 120 date: Thu Jan 01 00:00:15 1970 +0000
121 121 summary: msg 15
122 122
123 123 changeset: 14:ce8f0998e922
124 124 user: test
125 125 date: Thu Jan 01 00:00:14 1970 +0000
126 126 summary: msg 14
127 127
128 128 changeset: 13:9d7d07bc967c
129 129 user: test
130 130 date: Thu Jan 01 00:00:13 1970 +0000
131 131 summary: msg 13
132 132
133 133 changeset: 12:1941b52820a5
134 134 user: test
135 135 date: Thu Jan 01 00:00:12 1970 +0000
136 136 summary: msg 12
137 137
138 138 changeset: 11:7b4cd9578619
139 139 user: test
140 140 date: Thu Jan 01 00:00:11 1970 +0000
141 141 summary: msg 11
142 142
143 143 changeset: 10:7c5eff49a6b6
144 144 user: test
145 145 date: Thu Jan 01 00:00:10 1970 +0000
146 146 summary: msg 10
147 147
148 148 changeset: 9:eb44510ef29a
149 149 user: test
150 150 date: Thu Jan 01 00:00:09 1970 +0000
151 151 summary: msg 9
152 152
153 153 changeset: 8:453eb4dba229
154 154 user: test
155 155 date: Thu Jan 01 00:00:08 1970 +0000
156 156 summary: msg 8
157 157
158 158 changeset: 7:03750880c6b5
159 159 user: test
160 160 date: Thu Jan 01 00:00:07 1970 +0000
161 161 summary: msg 7
162 162
163 163 changeset: 6:a3d5c6fdf0d3
164 164 user: test
165 165 date: Thu Jan 01 00:00:06 1970 +0000
166 166 summary: msg 6
167 167
168 168 changeset: 5:7874a09ea728
169 169 user: test
170 170 date: Thu Jan 01 00:00:05 1970 +0000
171 171 summary: msg 5
172 172
173 173 changeset: 4:9b2ba8336a65
174 174 user: test
175 175 date: Thu Jan 01 00:00:04 1970 +0000
176 176 summary: msg 4
177 177
178 178 changeset: 3:b53bea5e2fcb
179 179 user: test
180 180 date: Thu Jan 01 00:00:03 1970 +0000
181 181 summary: msg 3
182 182
183 183 changeset: 2:db07c04beaca
184 184 user: test
185 185 date: Thu Jan 01 00:00:02 1970 +0000
186 186 summary: msg 2
187 187
188 188 changeset: 1:5cd978ea5149
189 189 user: test
190 190 date: Thu Jan 01 00:00:01 1970 +0000
191 191 summary: msg 1
192 192
193 193 changeset: 0:b99c7b9c8e11
194 194 user: test
195 195 date: Thu Jan 01 00:00:00 1970 +0000
196 196 summary: msg 0
197 197
198 198 % hg up -C
199 199 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
200 200 % bisect test
201 201 Testing changeset 16:a2e6ea4973e9 (30 changesets remaining, ~4 tests)
202 202 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
203 203 Testing changeset 23:5ec79163bff4 (15 changesets remaining, ~3 tests)
204 204 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
205 205 skip
206 206 Testing changeset 24:10e0acd3809e (15 changesets remaining, ~3 tests)
207 207 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
208 208 Testing changeset 27:288867a866e9 (7 changesets remaining, ~2 tests)
209 209 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
210 210 Testing changeset 29:b5bd63375ab9 (4 changesets remaining, ~2 tests)
211 211 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
212 212 Testing changeset 28:8e0c2264c8af (2 changesets remaining, ~1 tests)
213 213 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
214 214 The first bad revision is:
215 215 changeset: 29:b5bd63375ab9
216 216 user: test
217 217 date: Thu Jan 01 00:00:29 1970 +0000
218 218 summary: msg 29
219 219
220 220 % bisect reverse test
221 221 Testing changeset 15:e7fa0811edb0 (32 changesets remaining, ~5 tests)
222 222 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
223 223 Testing changeset 7:03750880c6b5 (16 changesets remaining, ~4 tests)
224 224 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
225 225 skip
226 226 Testing changeset 6:a3d5c6fdf0d3 (16 changesets remaining, ~4 tests)
227 227 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
228 228 Testing changeset 2:db07c04beaca (7 changesets remaining, ~2 tests)
229 229 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
230 230 Testing changeset 0:b99c7b9c8e11 (3 changesets remaining, ~1 tests)
231 231 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
232 232 Testing changeset 1:5cd978ea5149 (2 changesets remaining, ~1 tests)
233 233 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
234 234 The first good revision is:
235 235 changeset: 1:5cd978ea5149
236 236 user: test
237 237 date: Thu Jan 01 00:00:01 1970 +0000
238 238 summary: msg 1
239 239
240 240 abort: Inconsistent state, 31:58c80a7c8a40 is good and bad
241 241 error
242 242 Testing changeset 15:e7fa0811edb0 (32 changesets remaining, ~5 tests)
243 243 5cd978ea5149
244 % test no action message
245 (no action selected)
244 % reproduce AssertionError, issue1228 and issue1182
246 245 Testing changeset 2:db07c04beaca (4 changesets remaining, ~2 tests)
247 246 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
248 247 Testing changeset 1:5cd978ea5149 (4 changesets remaining, ~2 tests)
249 248 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
250 249 Testing changeset 3:b53bea5e2fcb (4 changesets remaining, ~2 tests)
251 250 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
252 251 Due to skipped revisions, the first bad revision could be any of:
253 252 changeset: 1:5cd978ea5149
254 253 user: test
255 254 date: Thu Jan 01 00:00:01 1970 +0000
256 255 summary: msg 1
257 256
258 257 changeset: 2:db07c04beaca
259 258 user: test
260 259 date: Thu Jan 01 00:00:02 1970 +0000
261 260 summary: msg 2
262 261
263 262 changeset: 3:b53bea5e2fcb
264 263 user: test
265 264 date: Thu Jan 01 00:00:03 1970 +0000
266 265 summary: msg 3
267 266
268 267 changeset: 4:9b2ba8336a65
269 268 user: test
270 269 date: Thu Jan 01 00:00:04 1970 +0000
271 270 summary: msg 4
272 271
272 % reproduce non converging bisect, issue1182
273 273 Testing changeset 1:5cd978ea5149 (2 changesets remaining, ~1 tests)
274 274 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
275 275 Due to skipped revisions, the first bad revision could be any of:
276 276 changeset: 1:5cd978ea5149
277 277 user: test
278 278 date: Thu Jan 01 00:00:01 1970 +0000
279 279 summary: msg 1
280 280
281 281 changeset: 2:db07c04beaca
282 282 user: test
283 283 date: Thu Jan 01 00:00:02 1970 +0000
284 284 summary: msg 2
285 285
286 % test no action
287 abort: cannot bisect (no known good revisions)
General Comments 0
You need to be logged in to leave comments. Login now