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