commands.py 223 KB
Newer Older
mpm@selenic.com's avatar
mpm@selenic.com committed
1 2
# commands.py - command processing for mercurial
#
3
# Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
mpm@selenic.com's avatar
mpm@selenic.com committed
4
#
5
# This software may be used and distributed according to the terms of the
Matt Mackall's avatar
Matt Mackall committed
6
# GNU General Public License version 2 or any later version.
mpm@selenic.com's avatar
mpm@selenic.com committed
7

8
from node import hex, bin, nullid, nullrev, short
9
from lock import release
10
from i18n import _
11
import os, re, difflib, time, tempfile, errno, shlex
12
import sys
13
import hg, scmutil, util, revlog, copies, error, bookmarks
14
import patch, help, encoding, templatekw, discovery
15
import archival, changegroup, cmdutil, hbisect
16
import sshserver, hgweb, commandserver
17
import extensions
18
from hgweb import server as hgweb_server
19
import merge as mergemod
20
import minirst, revset, fileset
21
import dagparser, context, simplemerge, graphmod
22 23
import random
import setdiscovery, treediscovery, dagutil, pvec, localrepo
24
import phases, obsolete, exchange
25

26 27 28 29
table = {}

command = cmdutil.command(table)

30 31 32
# Space delimited list of commands that don't require local repositories.
# This should be populated by passing norepo=True into the @command decorator.
norepo = ''
33 34 35 36
# Space delimited list of commands that optionally require local repositories.
# This should be populated by passing optionalrepo=True into the @command
# decorator.
optionalrepo = ''
37 38 39 40 41
# Space delimited list of commands that will examine arguments looking for
# a repository. This should be populated by passing inferrepo=True into the
# @command decorator.
inferrepo = ''

42 43 44 45 46 47 48 49 50
# common command options

globalopts = [
    ('R', 'repository', '',
     _('repository root directory or name of overlay bundle file'),
     _('REPO')),
    ('', 'cwd', '',
     _('change working directory'), _('DIR')),
    ('y', 'noninteractive', None,
51
     _('do not prompt, automatically pick the first choice for all prompts')),
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
    ('q', 'quiet', None, _('suppress output')),
    ('v', 'verbose', None, _('enable additional output')),
    ('', 'config', [],
     _('set/override config option (use \'section.name=value\')'),
     _('CONFIG')),
    ('', 'debug', None, _('enable debugging output')),
    ('', 'debugger', None, _('start debugger')),
    ('', 'encoding', encoding.encoding, _('set the charset encoding'),
     _('ENCODE')),
    ('', 'encodingmode', encoding.encodingmode,
     _('set the charset encoding mode'), _('MODE')),
    ('', 'traceback', None, _('always print a traceback on exception')),
    ('', 'time', None, _('time how long the command takes')),
    ('', 'profile', None, _('print command execution profile')),
    ('', 'version', None, _('output version information and exit')),
    ('h', 'help', None, _('display help and exit')),
68
    ('', 'hidden', False, _('consider hidden changesets')),
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
]

dryrunopts = [('n', 'dry-run', None,
               _('do not perform actions, just print output'))]

remoteopts = [
    ('e', 'ssh', '',
     _('specify ssh command to use'), _('CMD')),
    ('', 'remotecmd', '',
     _('specify hg command to run on the remote side'), _('CMD')),
    ('', 'insecure', None,
     _('do not verify server certificate (ignoring web.cacerts config)')),
]

walkopts = [
    ('I', 'include', [],
     _('include names matching the given patterns'), _('PATTERN')),
    ('X', 'exclude', [],
     _('exclude names matching the given patterns'), _('PATTERN')),
]

commitopts = [
    ('m', 'message', '',
     _('use text as commit message'), _('TEXT')),
    ('l', 'logfile', '',
     _('read commit message from file'), _('FILE')),
]

commitopts2 = [
    ('d', 'date', '',
     _('record the specified date as commit date'), _('DATE')),
    ('u', 'user', '',
     _('record the specified user as committer'), _('USER')),
]

104 105 106 107 108 109
# hidden for now
formatteropts = [
    ('T', 'template', '',
     _('display with template (DEPRECATED)'), _('TEMPLATE')),
]

110 111
templateopts = [
    ('', 'style', '',
112
     _('display using template map file (DEPRECATED)'), _('STYLE')),
113
    ('T', 'template', '',
114 115 116 117 118 119 120 121 122 123
     _('display with template'), _('TEMPLATE')),
]

logopts = [
    ('p', 'patch', None, _('show patch')),
    ('g', 'git', None, _('use git extended diff format')),
    ('l', 'limit', '',
     _('limit number of changes displayed'), _('NUM')),
    ('M', 'no-merges', None, _('do not show merges')),
    ('', 'stat', None, _('output diffstat-style summary of changes')),
124
    ('G', 'graph', None, _("show the revision DAG")),
125 126 127 128 129 130 131 132
] + templateopts

diffopts = [
    ('a', 'text', None, _('treat all files as text')),
    ('g', 'git', None, _('use git extended diff format')),
    ('', 'nodates', None, _('omit dates from diff headers'))
]

133
diffwsopts = [
134 135 136 137 138 139
    ('w', 'ignore-all-space', None,
     _('ignore white space when comparing lines')),
    ('b', 'ignore-space-change', None,
     _('ignore changes in the amount of white space')),
    ('B', 'ignore-blank-lines', None,
     _('ignore changes whose lines are all blank')),
140 141 142 143 144 145
    ]

diffopts2 = [
    ('p', 'show-function', None, _('show which function each change is in')),
    ('', 'reverse', None, _('produce a diff that undoes the changes')),
    ] + diffwsopts + [
146 147 148 149 150
    ('U', 'unified', '',
     _('number of lines of context to show'), _('NUM')),
    ('', 'stat', None, _('output diffstat-style summary of changes')),
]

151 152 153 154
mergetoolopts = [
    ('t', 'tool', '', _('specify merge tool')),
]

155 156 157 158 159 160 161 162 163 164
similarityopts = [
    ('s', 'similarity', '',
     _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
]

subrepoopts = [
    ('S', 'subrepos', None,
     _('recurse into subrepositories'))
]

mpm@selenic.com's avatar
mpm@selenic.com committed
165
# Commands start here, listed alphabetically
166

167 168
@command('^add',
    walkopts + subrepoopts + dryrunopts,
169 170
    _('[OPTION]... [FILE]...'),
    inferrepo=True)
171
def add(ui, repo, *pats, **opts):
172 173
    """add the specified files on the next commit

174 175
    Schedule files to be version controlled and added to the
    repository.
176

177
    The files will be added to the repository at the next commit. To
178
    undo an add before that, see :hg:`forget`.
179

180
    If no names are given, add all files to the repository.
181 182 183 184

    .. container:: verbose

       An example showing how new (unknown) files are added
Martin Geisler's avatar
Martin Geisler committed
185
       automatically by :hg:`add`::
186 187 188 189 190 191 192 193

         $ ls
         foo.c
         $ hg status
         ? foo.c
         $ hg add
         adding foo.c
         $ hg status
194
         A foo.c
195 196

    Returns 0 if all files are successfully added.
197 198
    """

199
    m = scmutil.match(repo[None], pats, opts)
200
    rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
201
                           opts.get('subrepos'), prefix="", explicitonly=False)
202
    return rejected and 1 or 0
203

204 205
@command('addremove',
    similarityopts + walkopts + dryrunopts,
206 207
    _('[OPTION]... [FILE]...'),
    inferrepo=True)
208
def addremove(ui, repo, *pats, **opts):
209
    """add all new files, delete all missing files
210

211 212
    Add all new files and remove all missing files from the
    repository.
213

214
    New files are ignored if they match any of the patterns in
215
    ``.hgignore``. As with add, these changes take effect at the next
216
    commit.
217

218
    Use the -s/--similarity option to detect renamed files. This
219
    option takes a percentage between 0 (disabled) and 100 (files must
220 221 222
    be identical) as its parameter. With a parameter greater than 0,
    this compares every removed file with every added file and records
    those similar enough as renames. Detecting renamed files this way
223
    can be expensive. After using this option, :hg:`status -C` can be
224 225 226
    used to check which files were identified as moved or renamed. If
    not specified, -s/--similarity defaults to 100 and only renames of
    identical files are detected.
227 228

    Returns 0 if all files are successfully added.
229
    """
230
    try:
231
        sim = float(opts.get('similarity') or 100)
232 233
    except ValueError:
        raise util.Abort(_('similarity must be a number'))
234 235
    if sim < 0 or sim > 100:
        raise util.Abort(_('similarity must be between 0 and 100'))
236
    return scmutil.addremove(repo, pats, opts, similarity=sim / 100.0)
237

238 239 240 241 242 243 244 245 246 247 248 249
@command('^annotate|blame',
    [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
    ('', 'follow', None,
     _('follow copies/renames and list the filename (DEPRECATED)')),
    ('', 'no-follow', None, _("don't follow copies and renames")),
    ('a', 'text', None, _('treat all files as text')),
    ('u', 'user', None, _('list the author (long with -v)')),
    ('f', 'file', None, _('list the filename')),
    ('d', 'date', None, _('list the date (short with -q)')),
    ('n', 'number', None, _('list the revision number (default)')),
    ('c', 'changeset', None, _('list the changeset')),
    ('l', 'line-number', None, _('show line number at the first appearance'))
250
    ] + diffwsopts + walkopts,
251 252
    _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
    inferrepo=True)
253
def annotate(ui, repo, *pats, **opts):
254
    """show changeset information by line for each file
255

256 257
    List changes in files, showing the revision id responsible for
    each line
258

259 260
    This command is useful for discovering when a change was made and
    by whom.
261

262 263 264 265
    Without the -a/--text option, annotate will avoid processing files
    it detects as binary. With -a, annotate will annotate the file
    anyway, although the results will probably be neither useful
    nor desirable.
266 267

    Returns 0 on success.
268
    """
269 270 271
    if not pats:
        raise util.Abort(_('at least one filename or pattern is required'))

272 273 274
    if opts.get('follow'):
        # --follow is deprecated and now just an alias for -f/--file
        # to mimic the behavior of Mercurial before version 1.5
275
        opts['file'] = True
276

277 278
    datefunc = ui.quiet and util.shortdate or util.datestr
    getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
279 280
    hexfn = ui.debugflag and hex or short

281 282
    opmap = [('user', ' ', lambda x: ui.shortuser(x[0].user())),
             ('number', ' ', lambda x: str(x[0].rev())),
283
             ('changeset', ' ', lambda x: hexfn(x[0].node())),
284 285 286
             ('date', ' ', getdate),
             ('file', ' ', lambda x: x[0].path()),
             ('line_number', ':', lambda x: str(x[1])),
287 288
            ]

289
    if (not opts.get('user') and not opts.get('changeset')
290
        and not opts.get('date') and not opts.get('file')):
291
        opts['number'] = True
292

293
    linenumber = opts.get('line_number') is not None
294
    if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
295 296
        raise util.Abort(_('at least one of -n/-c is required for -l'))

297 298
    funcmap = [(func, sep) for op, sep, func in opmap if opts.get(op)]
    funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
299

300 301 302
    def bad(x, y):
        raise util.Abort("%s: %s" % (x, y))

303
    ctx = scmutil.revsingle(repo, opts.get('rev'))
304
    m = scmutil.match(ctx, pats, opts)
305
    m.bad = bad
306
    follow = not opts.get('no_follow')
307
    diffopts = patch.diffopts(ui, opts, section='annotate')
Matt Mackall's avatar
Matt Mackall committed
308 309
    for abs in ctx.walk(m):
        fctx = ctx[abs]
310
        if not opts.get('text') and util.binary(fctx.data()):
311
            ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
312 313
            continue

314 315
        lines = fctx.annotate(follow=follow, linenumber=linenumber,
                              diffopts=diffopts)
316 317
        pieces = []

318
        for f, sep in funcmap:
319 320
            l = [f(n) for n, dummy in lines]
            if l:
321 322
                sized = [(x, encoding.colwidth(x)) for x in l]
                ml = max([w for x, w in sized])
323 324
                pieces.append(["%s%s%s" % (sep, ' ' * (ml - w), x)
                               for x, w in sized])
325

326 327
        for p, l in zip(zip(*pieces), lines):
            ui.write("%s: %s" % ("".join(p), l[1]))
328

329 330
        if lines and not lines[-1][1].endswith('\n'):
            ui.write('\n')
331

332 333 334 335 336 337 338 339
@command('archive',
    [('', 'no-decode', None, _('do not pass files through decoders')),
    ('p', 'prefix', '', _('directory prefix for files in archive'),
     _('PREFIX')),
    ('r', 'rev', '', _('revision to distribute'), _('REV')),
    ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
    ] + subrepoopts + walkopts,
    _('[OPTION]... DEST'))
340
def archive(ui, repo, dest, **opts):
341
    '''create an unversioned archive of a repository revision
342

343 344
    By default, the revision used is the parent of the working
    directory; use -r/--rev to specify a different revision.
345

346 347 348
    The archive type is automatically detected based on file
    extension (or override using -t/--type).

Matt Mackall's avatar
Matt Mackall committed
349 350 351 352 353 354 355 356 357 358 359 360
    .. container:: verbose

      Examples:

      - create a zip file containing the 1.0 release::

          hg archive -r 1.0 project-1.0.zip

      - create a tarball excluding .hg files::

          hg archive project.tar.gz -X ".hg*"

361
    Valid types are:
362

363 364 365 366 367 368
    :``files``: a directory full of files (default)
    :``tar``:   tar archive, uncompressed
    :``tbz2``:  tar archive, compressed using bzip2
    :``tgz``:   tar archive, compressed using gzip
    :``uzip``:  zip archive, uncompressed
    :``zip``:   zip archive, compressed using deflate
369

370
    The exact name of the destination archive or directory is given
Martin Geisler's avatar
Martin Geisler committed
371
    using a format string; see :hg:`help export` for details.
372

373 374 375 376
    Each member added to an archive file has a directory prefix
    prepended. Use -p/--prefix to specify a format string for the
    prefix. The default is the basename of the archive, with suffixes
    removed.
377 378

    Returns 0 on success.
379 380
    '''

381
    ctx = scmutil.revsingle(repo, opts.get('rev'))
382
    if not ctx:
383
        raise util.Abort(_('no working directory: please specify a revision'))
384
    node = ctx.node()
385
    dest = cmdutil.makefilename(repo, dest, node)
386
    if os.path.realpath(dest) == repo.root:
387
        raise util.Abort(_('repository root cannot be destination'))
388

389
    kind = opts.get('type') or archival.guesskind(dest) or 'files'
390
    prefix = opts.get('prefix')
391

392 393 394
    if dest == '-':
        if kind == 'files':
            raise util.Abort(_('cannot archive plain files to stdout'))
395
        dest = cmdutil.makefileobj(repo, dest)
396 397
        if not prefix:
            prefix = os.path.basename(repo.root) + '-%h'
398

399
    prefix = cmdutil.makefilename(repo, prefix, node)
400
    matchfn = scmutil.match(ctx, [], opts)
401
    archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
402
                     matchfn, prefix, subrepos=opts.get('subrepos'))
403

404 405
@command('backout',
    [('', 'merge', None, _('merge with old dirstate parent after backout')),
406 407
    ('', 'parent', '',
     _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
408
    ('r', 'rev', '', _('revision to backout'), _('REV')),
409
    ('e', 'edit', False, _('invoke editor on commit messages')),
410
    ] + mergetoolopts + walkopts + commitopts + commitopts2,
411
    _('[OPTION]... [-r] REV'))
Daniel Holth's avatar
Daniel Holth committed
412
def backout(ui, repo, node=None, rev=None, **opts):
Vadim Gelfer's avatar
Vadim Gelfer committed
413 414
    '''reverse effect of earlier changeset

415 416 417
    Prepare a new changeset with the effect of REV undone in the
    current working directory.

418
    If REV is the parent of the working directory, then this new changeset
419 420 421
    is committed automatically. Otherwise, hg needs to merge the
    changes and the merged result is left uncommitted.

422
    .. note::
423

424 425 426
      backout cannot be used to fix either an unwanted or
      incorrect merge.

Matt Mackall's avatar
Matt Mackall committed
427 428 429 430 431 432 433 434 435 436 437
    .. container:: verbose

      By default, the pending changeset will have one parent,
      maintaining a linear history. With --merge, the pending
      changeset will instead have two parents: the old parent of the
      working directory and a new child of REV that simply undoes REV.

      Before version 1.7, the behavior without --merge was equivalent
      to specifying --merge followed by :hg:`update --clean .` to
      cancel the merge and leave the child of REV as a head to be
      merged separately.
438

Martin Geisler's avatar
Martin Geisler committed
439
    See :hg:`help dates` for a list of formats valid for -d/--date.
440

441 442
    Returns 0 on success, 1 if nothing to backout or there are unresolved
    files.
443
    '''
Daniel Holth's avatar
Daniel Holth committed
444 445 446 447 448
    if rev and node:
        raise util.Abort(_("please specify just one revision"))

    if not rev:
        rev = node
Vadim Gelfer's avatar
Vadim Gelfer committed
449

450 451 452
    if not rev:
        raise util.Abort(_("please specify a revision to backout"))

453 454 455 456
    date = opts.get('date')
    if date:
        opts['date'] = util.parsedate(date)

457
    cmdutil.checkunfinished(repo)
458
    cmdutil.bailifchanged(repo)
459
    node = scmutil.revsingle(repo, rev).node()
460

461
    op1, op2 = repo.dirstate.parents()
462
    if not repo.changelog.isancestor(node, op1):
463
        raise util.Abort(_('cannot backout change that is not an ancestor'))
464

465 466
    p1, p2 = repo.changelog.parents(node)
    if p1 == nullid:
467
        raise util.Abort(_('cannot backout a change with no parents'))
Vadim Gelfer's avatar
Vadim Gelfer committed
468
    if p2 != nullid:
469
        if not opts.get('parent'):
470
            raise util.Abort(_('cannot backout a merge changeset'))
471 472
        p = repo.lookup(opts['parent'])
        if p not in (p1, p2):
473
            raise util.Abort(_('%s is not a parent of %s') %
474
                             (short(p), short(node)))
475 476
        parent = p
    else:
477
        if opts.get('parent'):
478 479
            raise util.Abort(_('cannot use --parent on non-merge changeset'))
        parent = p1
480

481
    # the backout should appear on the same branch
482 483 484
    wlock = repo.wlock()
    try:
        branch = repo.dirstate.branch()
485
        bheads = repo.branchheads(branch)
486
        rctx = scmutil.revsingle(repo, hex(parent))
487 488
        if not opts.get('merge') and op1 != node:
            try:
489 490
                ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
                             'backout')
491
                repo.dirstate.beginparentchange()
492 493
                stats = mergemod.update(repo, parent, True, True, False,
                                        node, False)
494
                repo.setparents(op1, op2)
495
                repo.dirstate.endparentchange()
496 497
                hg._showstats(repo, stats)
                if stats[3]:
498 499 500 501 502 503
                    repo.ui.status(_("use 'hg resolve' to retry unresolved "
                                     "file merges\n"))
                else:
                    msg = _("changeset %s backed out, "
                            "don't forget to commit.\n")
                    ui.status(msg % short(node))
504
                return stats[3] > 0
505
            finally:
506
                ui.setconfig('ui', 'forcemerge', '', '')
507 508 509 510 511
        else:
            hg.clean(repo, node, show_stats=False)
            repo.dirstate.setbranch(branch)
            cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())

512

513
        def commitfunc(ui, repo, message, match, opts):
514 515
            editform = 'backout'
            e = cmdutil.getcommiteditor(editform=editform, **opts)
516 517 518
            if not message:
                # we don't translate commit messages
                message = "Backed out changeset %s" % short(node)
519
                e = cmdutil.getcommiteditor(edit=True, editform=editform)
520 521 522
            return repo.commit(message, opts.get('user'), opts.get('date'),
                               match, editor=e)
        newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
523 524 525
        if not newnode:
            ui.status(_("nothing changed\n"))
            return 1
526
        cmdutil.commitstatus(repo, newnode, branch, bheads)
527

528 529 530 531 532 533 534 535 536
        def nice(node):
            return '%d:%s' % (repo.changelog.rev(node), short(node))
        ui.status(_('changeset %s backs out changeset %s\n') %
                  (nice(repo.changelog.tip()), nice(node)))
        if opts.get('merge') and op1 != node:
            hg.clean(repo, op1, show_stats=False)
            ui.status(_('merging with changeset %s\n')
                      % nice(repo.changelog.tip()))
            try:
537 538
                ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
                             'backout')
539 540
                return hg.merge(repo, hex(repo.changelog.tip()))
            finally:
541
                ui.setconfig('ui', 'forcemerge', '', '')
542 543
    finally:
        wlock.release()
544
    return 0
Vadim Gelfer's avatar
Vadim Gelfer committed
545

546 547 548 549 550 551 552 553 554
@command('bisect',
    [('r', 'reset', False, _('reset bisect state')),
    ('g', 'good', False, _('mark changeset good')),
    ('b', 'bad', False, _('mark changeset bad')),
    ('s', 'skip', False, _('skip testing changeset')),
    ('e', 'extend', False, _('extend the bisect range')),
    ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
    ('U', 'noupdate', False, _('do not update to target'))],
    _("[-gbsr] [-U] [-c CMD] [REV]"))
555
def bisect(ui, repo, rev=None, extra=None, command=None,
556 557
               reset=None, good=None, bad=None, skip=None, extend=None,
               noupdate=None):
558 559
    """subdivision search of changesets

560 561 562 563 564 565 566 567 568 569 570 571 572
    This command helps to find changesets which introduce problems. To
    use, mark the earliest changeset you know exhibits the problem as
    bad, then mark the latest changeset which is free from the problem
    as good. Bisect will update your working directory to a revision
    for testing (unless the -U/--noupdate option is specified). Once
    you have performed tests, mark the working directory as good or
    bad, and bisect will either update to another candidate changeset
    or announce that it has found the bad revision.

    As a shortcut, you can also use the revision argument to mark a
    revision as good or bad without checking it out first.

    If you supply a command, it will be used for automatic bisection.
573 574 575 576 577 578
    The environment variable HG_NODE will contain the ID of the
    changeset being tested. The exit status of the command will be
    used to mark revisions as good or bad: status 0 means good, 125
    means to skip the revision, 127 (command not found) will abort the
    bisection, and any other non-zero exit status means the revision
    is bad.
579

580 581 582 583
    .. container:: verbose

      Some examples:

584
      - start a bisection with known bad revision 34, and good revision 12::
585 586 587 588 589 590 591 592 593 594

          hg bisect --bad 34
          hg bisect --good 12

      - advance the current bisection by marking current revision as good or
        bad::

          hg bisect --good
          hg bisect --bad

kiilerix's avatar
kiilerix committed
595
      - mark the current revision, or a known revision, to be skipped (e.g. if
596 597 598 599 600
        that revision is not usable because of another issue)::

          hg bisect --skip
          hg bisect --skip 23

601
      - skip all revisions that do not touch directories ``foo`` or ``bar``::
602

603
          hg bisect --skip "!( file('path:foo') & file('path:bar') )"
604

605 606 607 608 609 610 611 612 613 614
      - forget the current bisection::

          hg bisect --reset

      - use 'make && make tests' to automatically find the first broken
        revision::

          hg bisect --reset
          hg bisect --bad 34
          hg bisect --good 12
615
          hg bisect --command "make && make tests"
616 617 618 619 620 621

      - see all changesets whose states are already known in the current
        bisection::

          hg log -r "bisect(pruned)"

622 623 624 625 626
      - see the changeset currently being bisected (especially useful
        if running with -U/--noupdate)::

          hg log -r "bisect(current)"

627 628 629 630
      - see all changesets that took part in the current bisection::

          hg log -r "bisect(range)"

631
      - you can even get a nice graph::
632 633 634

          hg log --graph -r "bisect(range)"

635 636
      See :hg:`help revsets` for more about the `bisect()` keyword.

637
    Returns 0 on success.
638
    """
639 640 641 642 643 644 645 646
    def extendbisectrange(nodes, good):
        # bisect is incomplete when it ends on a merge node and
        # one of the parent was not checked.
        parents = repo[nodes[0]].parents()
        if len(parents) > 1:
            side = good and state['bad'] or state['good']
            num = len(set(i.node() for i in parents) & set(side))
            if num == 1:
Yuya Nishihara's avatar
Yuya Nishihara committed
647
                return parents[0].ancestor(parents[1])
648 649
        return None

650 651 652 653
    def print_result(nodes, good):
        displayer = cmdutil.show_changeset(ui, repo, {})
        if len(nodes) == 1:
            # narrowed it down to a single revision
654 655 656 657
            if good:
                ui.write(_("The first good revision is:\n"))
            else:
                ui.write(_("The first bad revision is:\n"))
658
            displayer.show(repo[nodes[0]])
659 660 661 662 663
            extendnode = extendbisectrange(nodes, good)
            if extendnode is not None:
                ui.write(_('Not all ancestors of this changeset have been'
                           ' checked.\nUse bisect --extend to continue the '
                           'bisection from\nthe common ancestor, %s.\n')
664
                         % extendnode)
665 666
        else:
            # multiple possible revisions
667 668 669 670 671 672
            if good:
                ui.write(_("Due to skipped revisions, the first "
                        "good revision could be any of:\n"))
            else:
                ui.write(_("Due to skipped revisions, the first "
                        "bad revision could be any of:\n"))
673
            for n in nodes:
674
                displayer.show(repo[n])
675
        displayer.close()
676 677 678 679 680 681 682 683 684 685 686

    def check_state(state, interactive=True):
        if not state['good'] or not state['bad']:
            if (good or bad or skip or reset) and interactive:
                return
            if not state['good']:
                raise util.Abort(_('cannot bisect (no known good revisions)'))
            else:
                raise util.Abort(_('cannot bisect (no known bad revisions)'))
        return True

687 688 689 690 691 692 693 694 695 696
    # backward compatibility
    if rev in "good bad reset init".split():
        ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
        cmd, rev, extra = rev, extra, None
        if cmd == "good":
            good = True
        elif cmd == "bad":
            bad = True
        else:
            reset = True
697
    elif extra or good + bad + skip + reset + extend + bool(command) > 1:
698
        raise util.Abort(_('incompatible arguments'))
699

700 701
    cmdutil.checkunfinished(repo)

702 703 704 705 706 707
    if reset:
        p = repo.join("bisect.state")
        if os.path.exists(p):
            os.unlink(p)
        return

708 709 710 711
    state = hbisect.load_state(repo)

    if command:
        changesets = 1
712 713 714 715
        if noupdate:
            try:
                node = state['current'][0]
            except LookupError:
716 717
                raise util.Abort(_('current bisect revision is unknown - '
                                   'start a new bisect to fix'))
718
        else:
719 720 721
            node, p2 = repo.dirstate.parents()
            if p2 != nullid:
                raise util.Abort(_('current bisect revision is a merge'))
722 723 724
        try:
            while changesets:
                # update state
725
                state['current'] = [node]
726
                hbisect.save_state(repo, state)
727 728 729
                status = util.system(command,
                                     environ={'HG_NODE': hex(node)},
                                     out=ui.fout)
730 731 732 733 734 735 736 737 738 739 740
                if status == 125:
                    transition = "skip"
                elif status == 0:
                    transition = "good"
                # status < 0 means process was killed
                elif status == 127:
                    raise util.Abort(_("failed to execute %s") % command)
                elif status < 0:
                    raise util.Abort(_("%s killed") % command)
                else:
                    transition = "bad"
741
                ctx = scmutil.revsingle(repo, rev, node)
742
                rev = None # clear for future iterations
743
                state[transition].append(ctx.node())
744
                ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
745 746
                check_state(state, interactive=False)
                # bisect
747
                nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
748
                # update to next check
749 750 751 752
                node = nodes[0]
                if not noupdate:
                    cmdutil.bailifchanged(repo)
                    hg.clean(repo, node, show_stats=False)
753
        finally:
754
            state['current'] = [node]
755
            hbisect.save_state(repo, state)
756
        print_result(nodes, bgood)
757
        return
758 759

    # update state
760 761

    if rev:
762
        nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
763 764 765
    else:
        nodes = [repo.lookup('.')]

766 767
    if good or bad or skip:
        if good:
768
            state['good'] += nodes
769
        elif bad:
770
            state['bad'] += nodes
771
        elif skip:
772
            state['skip'] += nodes
773
        hbisect.save_state(repo, state)
774

775 776
    if not check_state(state):
        return
777 778

    # actually bisect
779
    nodes, changesets, good = hbisect.bisect(repo.changelog, state)
780 781 782 783
    if extend:
        if not changesets:
            extendnode = extendbisectrange(nodes, good)
            if extendnode is not None:
784 785
                ui.write(_("Extending search to changeset %d:%s\n")
                         % (extendnode.rev(), extendnode))
786 787
                state['current'] = [extendnode.node()]
                hbisect.save_state(repo, state)
788 789
                if noupdate:
                    return
790
                cmdutil.bailifchanged(repo)
791 792 793
                return hg.clean(repo, extendnode.node())
        raise util.Abort(_("nothing to extend"))

794
    if changesets == 0:
795
        print_result(nodes, good)
796 797 798
    else:
        assert len(nodes) == 1 # only a single node can be tested next
        node = nodes[0]
799 800 801 802 803
        # compute the approximate number of remaining tests
        tests, size = 0, 2
        while size <= changesets:
            tests, size = tests + 1, size * 2
        rev = repo.changelog.rev(node)
804 805
        ui.write(_("Testing changeset %d:%s "
                   "(%d changesets remaining, ~%d tests)\n")
806
                 % (rev, short(node), changesets, tests))
807 808
        state['current'] = [node]
        hbisect.save_state(repo, state)
809
        if not noupdate:
810
            cmdutil.bailifchanged(repo)
811 812
            return hg.clean(repo, node)

813
@command('bookmarks|bookmark',
814 815 816 817
    [('f', 'force', False, _('force')),
    ('r', 'rev', '', _('revision'), _('REV')),
    ('d', 'delete', False, _('delete a given bookmark')),
    ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
818
    ('i', 'inactive', False, _('mark a bookmark inactive'))],
819 820
    _('hg bookmarks [OPTIONS]... [NAME]...'))
def bookmark(ui, repo, *names, **opts):
821 822 823 824 825 826 827
    '''create a new bookmark or list existing bookmarks

    Bookmarks are labels on changesets to help track lines of development.
    Bookmarks are unversioned and can be moved, renamed and deleted.
    Deleting or moving a bookmark has no effect on the associated changesets.

    Creating or updating to a bookmark causes it to be marked as 'active'.
828 829
    The active bookmark is indicated with a '*'.
    When a commit is made, the active bookmark will advance to the new commit.
830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859
    A plain :hg:`update` will also advance an active bookmark, if possible.
    Updating away from a bookmark will cause it to be deactivated.

    Bookmarks can be pushed and pulled between repositories (see
    :hg:`help push` and :hg:`help pull`). If a shared bookmark has
    diverged, a new 'divergent bookmark' of the form 'name@path' will
    be created. Using :hg:'merge' will resolve the divergence.

    A bookmark named '@' has the special property that :hg:`clone` will
    check it out by default if it exists.

    .. container:: verbose

      Examples:

      - create an active bookmark for a new line of development::

          hg book new-feature

      - create an inactive bookmark as a place marker::

          hg book -i reviewed

      - create an inactive bookmark on another changeset::

          hg book -r .^ tested

      - move the '@' bookmark from another branch::

          hg book -f @
860
    '''</