commands.py 211 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 _, gettext
11
import os, re, difflib, time, tempfile, errno
12
import hg, scmutil, util, revlog, extensions, copies, error, bookmarks
13
import patch, help, url, encoding, templatekw, discovery
14 15
import archival, changegroup, cmdutil, hbisect
import sshserver, hgweb, hgweb.server, commandserver
16
import merge as mergemod
17
import minirst, revset, fileset
18
import dagparser, context, simplemerge, graphmod
19
import random, setdiscovery, treediscovery, dagutil, pvec, localrepo
20
import phases, obsolete
21

22 23 24 25 26 27 28 29 30 31 32 33 34
table = {}

command = cmdutil.command(table)

# common command options

globalopts = [
    ('R', 'repository', '',
     _('repository root directory or name of overlay bundle file'),
     _('REPO')),
    ('', 'cwd', '',
     _('change working directory'), _('DIR')),
    ('y', 'noninteractive', None,
35
     _('do not prompt, automatically pick the first choice for all prompts')),
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 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
    ('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')),
]

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')),
]

templateopts = [
    ('', 'style', '',
     _('display using template map file'), _('STYLE')),
    ('', 'template', '',
     _('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')),
101
    ('G', 'graph', None, _("show the revision DAG")),
102 103 104 105 106 107 108 109
] + 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'))
]

110
diffwsopts = [
111 112 113 114 115 116
    ('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')),
117 118 119 120 121 122
    ]

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

128 129 130 131
mergetoolopts = [
    ('t', 'tool', '', _('specify merge tool')),
]

132 133 134 135 136 137 138 139 140 141
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
142
# Commands start here, listed alphabetically
143

144 145 146
@command('^add',
    walkopts + subrepoopts + dryrunopts,
    _('[OPTION]... [FILE]...'))
147
def add(ui, repo, *pats, **opts):
148 149
    """add the specified files on the next commit

150 151
    Schedule files to be version controlled and added to the
    repository.
152

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

156
    If no names are given, add all files to the repository.
157 158 159 160

    .. container:: verbose

       An example showing how new (unknown) files are added
Martin Geisler's avatar
Martin Geisler committed
161
       automatically by :hg:`add`::
162 163 164 165 166 167 168 169

         $ ls
         foo.c
         $ hg status
         ? foo.c
         $ hg add
         adding foo.c
         $ hg status
170
         A foo.c
171 172

    Returns 0 if all files are successfully added.
173 174
    """

175
    m = scmutil.match(repo[None], pats, opts)
176
    rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
177
                           opts.get('subrepos'), prefix="", explicitonly=False)
178
    return rejected and 1 or 0
179

180 181 182
@command('addremove',
    similarityopts + walkopts + dryrunopts,
    _('[OPTION]... [FILE]...'))
183
def addremove(ui, repo, *pats, **opts):
184
    """add all new files, delete all missing files
185

186 187
    Add all new files and remove all missing files from the
    repository.
188

189
    New files are ignored if they match any of the patterns in
190
    ``.hgignore``. As with add, these changes take effect at the next
191
    commit.
192

193 194 195 196 197
    Use the -s/--similarity option to detect renamed files. With a
    parameter greater than 0, this compares every removed file with
    every added file and records those similar enough as renames. This
    option takes a percentage between 0 (disabled) and 100 (files must
    be identical) as its parameter. Detecting renamed files this way
198 199
    can be expensive. After using this option, :hg:`status -C` can be
    used to check which files were identified as moved or renamed.
200 201
    If this option is not specified, only renames of identical files
    are detected.
202 203

    Returns 0 if all files are successfully added.
204
    """
205
    try:
206
        sim = float(opts.get('similarity') or 100)
207 208
    except ValueError:
        raise util.Abort(_('similarity must be a number'))
209 210
    if sim < 0 or sim > 100:
        raise util.Abort(_('similarity must be between 0 and 100'))
211
    return scmutil.addremove(repo, pats, opts, similarity=sim / 100.0)
212

213 214 215 216 217 218 219 220 221 222 223 224
@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'))
225
    ] + diffwsopts + walkopts,
226
    _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'))
227
def annotate(ui, repo, *pats, **opts):
228
    """show changeset information by line for each file
229

230 231
    List changes in files, showing the revision id responsible for
    each line
232

233 234
    This command is useful for discovering when a change was made and
    by whom.
235

236 237 238 239
    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.
240 241

    Returns 0 on success.
242
    """
243 244 245
    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
246
        opts['file'] = True
247

248 249
    datefunc = ui.quiet and util.shortdate or util.datestr
    getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
250

251
    if not pats:
252
        raise util.Abort(_('at least one filename or pattern is required'))
253

254 255
    hexfn = ui.debugflag and hex or short

256 257
    opmap = [('user', ' ', lambda x: ui.shortuser(x[0].user())),
             ('number', ' ', lambda x: str(x[0].rev())),
258
             ('changeset', ' ', lambda x: hexfn(x[0].node())),
259 260 261
             ('date', ' ', getdate),
             ('file', ' ', lambda x: x[0].path()),
             ('line_number', ':', lambda x: str(x[1])),
262 263
            ]

264
    if (not opts.get('user') and not opts.get('changeset')
265
        and not opts.get('date') and not opts.get('file')):
266
        opts['number'] = True
267

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

272 273
    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
274

275 276 277
    def bad(x, y):
        raise util.Abort("%s: %s" % (x, y))

278
    ctx = scmutil.revsingle(repo, opts.get('rev'))
279
    m = scmutil.match(ctx, pats, opts)
280
    m.bad = bad
281
    follow = not opts.get('no_follow')
282
    diffopts = patch.diffopts(ui, opts, section='annotate')
Matt Mackall's avatar
Matt Mackall committed
283 284
    for abs in ctx.walk(m):
        fctx = ctx[abs]
285
        if not opts.get('text') and util.binary(fctx.data()):
286
            ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
287 288
            continue

289 290
        lines = fctx.annotate(follow=follow, linenumber=linenumber,
                              diffopts=diffopts)
291 292
        pieces = []

293
        for f, sep in funcmap:
294 295
            l = [f(n) for n, dummy in lines]
            if l:
296 297
                sized = [(x, encoding.colwidth(x)) for x in l]
                ml = max([w for x, w in sized])
298 299
                pieces.append(["%s%s%s" % (sep, ' ' * (ml - w), x)
                               for x, w in sized])
300

mpm@selenic.com's avatar
mpm@selenic.com committed
301 302
        if pieces:
            for p, l in zip(zip(*pieces), lines):
303
                ui.write("%s: %s" % ("".join(p), l[1]))
304

305 306 307
            if lines and not lines[-1][1].endswith('\n'):
                ui.write('\n')

308 309 310 311 312 313 314 315
@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'))
316
def archive(ui, repo, dest, **opts):
317
    '''create an unversioned archive of a repository revision
318

319 320
    By default, the revision used is the parent of the working
    directory; use -r/--rev to specify a different revision.
321

322 323 324
    The archive type is automatically detected based on file
    extension (or override using -t/--type).

Matt Mackall's avatar
Matt Mackall committed
325 326 327 328 329 330 331 332 333 334 335 336
    .. 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*"

337
    Valid types are:
338

339 340 341 342 343 344
    :``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
345

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

349 350 351 352
    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.
353 354

    Returns 0 on success.
355 356
    '''

357
    ctx = scmutil.revsingle(repo, opts.get('rev'))
358
    if not ctx:
359
        raise util.Abort(_('no working directory: please specify a revision'))
360
    node = ctx.node()
361
    dest = cmdutil.makefilename(repo, dest, node)
362
    if os.path.realpath(dest) == repo.root:
363
        raise util.Abort(_('repository root cannot be destination'))
364

365
    kind = opts.get('type') or archival.guesskind(dest) or 'files'
366
    prefix = opts.get('prefix')
367

368 369 370
    if dest == '-':
        if kind == 'files':
            raise util.Abort(_('cannot archive plain files to stdout'))
371
        dest = cmdutil.makefileobj(repo, dest)
372 373
        if not prefix:
            prefix = os.path.basename(repo.root) + '-%h'
374

375
    prefix = cmdutil.makefilename(repo, prefix, node)
376
    matchfn = scmutil.match(ctx, [], opts)
377
    archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
378
                     matchfn, prefix, subrepos=opts.get('subrepos'))
379

380 381
@command('backout',
    [('', 'merge', None, _('merge with old dirstate parent after backout')),
382 383
    ('', 'parent', '',
     _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
384
    ('r', 'rev', '', _('revision to backout'), _('REV')),
385
    ] + mergetoolopts + walkopts + commitopts + commitopts2,
386
    _('[OPTION]... [-r] REV'))
Daniel Holth's avatar
Daniel Holth committed
387
def backout(ui, repo, node=None, rev=None, **opts):
Vadim Gelfer's avatar
Vadim Gelfer committed
388 389
    '''reverse effect of earlier changeset

390 391 392
    Prepare a new changeset with the effect of REV undone in the
    current working directory.

393
    If REV is the parent of the working directory, then this new changeset
394 395 396
    is committed automatically. Otherwise, hg needs to merge the
    changes and the merged result is left uncommitted.

397 398 399 400
    .. note::
      backout cannot be used to fix either an unwanted or
      incorrect merge.

Matt Mackall's avatar
Matt Mackall committed
401 402 403 404 405 406 407 408 409 410 411
    .. 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.
412

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

    Returns 0 on success.
416
    '''
Daniel Holth's avatar
Daniel Holth committed
417 418 419 420 421
    if rev and node:
        raise util.Abort(_("please specify just one revision"))

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

423 424 425
    if not rev:
        raise util.Abort(_("please specify a revision to backout"))

426 427 428 429
    date = opts.get('date')
    if date:
        opts['date'] = util.parsedate(date)

430
    cmdutil.bailifchanged(repo)
431
    node = scmutil.revsingle(repo, rev).node()
432

433
    op1, op2 = repo.dirstate.parents()
434 435
    a = repo.changelog.ancestor(op1, node)
    if a != node:
436
        raise util.Abort(_('cannot backout change on a different branch'))
437

438 439
    p1, p2 = repo.changelog.parents(node)
    if p1 == nullid:
440
        raise util.Abort(_('cannot backout a change with no parents'))
Vadim Gelfer's avatar
Vadim Gelfer committed
441
    if p2 != nullid:
442
        if not opts.get('parent'):
443
            raise util.Abort(_('cannot backout a merge changeset'))
444 445
        p = repo.lookup(opts['parent'])
        if p not in (p1, p2):
446
            raise util.Abort(_('%s is not a parent of %s') %
447
                             (short(p), short(node)))
448 449
        parent = p
    else:
450
        if opts.get('parent'):
451 452
            raise util.Abort(_('cannot use --parent on non-merge changeset'))
        parent = p1
453

454
    # the backout should appear on the same branch
455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
    wlock = repo.wlock()
    try:
        branch = repo.dirstate.branch()
        hg.clean(repo, node, show_stats=False)
        repo.dirstate.setbranch(branch)
        revert_opts = opts.copy()
        revert_opts['date'] = None
        revert_opts['all'] = True
        revert_opts['rev'] = hex(parent)
        revert_opts['no_backup'] = None
        revert(ui, repo, **revert_opts)
        if not opts.get('merge') and op1 != node:
            try:
                ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
                return hg.update(repo, op1)
            finally:
                ui.setconfig('ui', 'forcemerge', '')

        commit_opts = opts.copy()
        commit_opts['addremove'] = False
        if not commit_opts['message'] and not commit_opts['logfile']:
            # we don't translate commit messages
            commit_opts['message'] = "Backed out changeset %s" % short(node)
            commit_opts['force_editor'] = True
        commit(ui, repo, **commit_opts)
        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:
                ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
                return hg.merge(repo, hex(repo.changelog.tip()))
            finally:
                ui.setconfig('ui', 'forcemerge', '')
    finally:
        wlock.release()
495
    return 0
Vadim Gelfer's avatar
Vadim Gelfer committed
496

497 498 499 500 501 502 503 504 505
@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]"))
506
def bisect(ui, repo, rev=None, extra=None, command=None,
507 508
               reset=None, good=None, bad=None, skip=None, extend=None,
               noupdate=None):
509 510
    """subdivision search of changesets

511 512 513 514 515 516 517 518 519 520 521 522 523
    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.
524 525 526 527 528 529
    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.
530

531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568
    .. container:: verbose

      Some examples:

      - start a bisection with known bad revision 12, and good revision 34::

          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

      - mark the current revision, or a known revision, to be skipped (eg. if
        that revision is not usable because of another issue)::

          hg bisect --skip
          hg bisect --skip 23

      - 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
          hg bisect --command 'make && make tests'

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

          hg log -r "bisect(pruned)"

569 570 571 572 573
      - see the changeset currently being bisected (especially useful
        if running with -U/--noupdate)::

          hg log -r "bisect(current)"

574 575 576 577 578 579 580 581
      - see all changesets that took part in the current bisection::

          hg log -r "bisect(range)"

      - with the graphlog extension, you can even get a nice graph::

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

582 583
      See :hg:`help revsets` for more about the `bisect()` keyword.

584
    Returns 0 on success.
585
    """
586 587 588 589 590 591 592 593
    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
594
                return parents[0].ancestor(parents[1])
595 596
        return None

597 598 599 600
    def print_result(nodes, good):
        displayer = cmdutil.show_changeset(ui, repo, {})
        if len(nodes) == 1:
            # narrowed it down to a single revision
601 602 603 604
            if good:
                ui.write(_("The first good revision is:\n"))
            else:
                ui.write(_("The first bad revision is:\n"))
605
            displayer.show(repo[nodes[0]])
606 607 608 609 610
            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')
611
                         % extendnode)
612 613
        else:
            # multiple possible revisions
614 615 616 617 618 619
            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"))
620
            for n in nodes:
621
                displayer.show(repo[n])
622
        displayer.close()
623 624 625 626 627 628 629 630 631 632 633

    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

634 635 636 637 638 639 640 641 642 643
    # 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
644
    elif extra or good + bad + skip + reset + extend + bool(command) > 1:
645
        raise util.Abort(_('incompatible arguments'))
646 647 648 649 650 651 652

    if reset:
        p = repo.join("bisect.state")
        if os.path.exists(p):
            os.unlink(p)
        return

653 654 655 656
    state = hbisect.load_state(repo)

    if command:
        changesets = 1
657 658 659 660 661 662 663 664 665
        try:
            node = state['current'][0]
        except LookupError:
            if noupdate:
                raise util.Abort(_('current bisect revision is unknown - '
                                   'start a new bisect to fix'))
            node, p2 = repo.dirstate.parents()
            if p2 != nullid:
                raise util.Abort(_('current bisect revision is a merge'))
666 667 668
        try:
            while changesets:
                # update state
669
                state['current'] = [node]
670
                hbisect.save_state(repo, state)
671 672 673
                status = util.system(command,
                                     environ={'HG_NODE': hex(node)},
                                     out=ui.fout)
674 675 676 677 678 679 680 681 682 683 684
                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"
685
                ctx = scmutil.revsingle(repo, rev, node)
686
                rev = None # clear for future iterations
687
                state[transition].append(ctx.node())
688
                ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
689 690 691 692
                check_state(state, interactive=False)
                # bisect
                nodes, changesets, good = hbisect.bisect(repo.changelog, state)
                # update to next check
693 694 695 696
                node = nodes[0]
                if not noupdate:
                    cmdutil.bailifchanged(repo)
                    hg.clean(repo, node, show_stats=False)
697
        finally:
698
            state['current'] = [node]
699
            hbisect.save_state(repo, state)
700 701
        print_result(nodes, good)
        return
702 703

    # update state
704 705

    if rev:
706
        nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
707 708 709
    else:
        nodes = [repo.lookup('.')]

710 711
    if good or bad or skip:
        if good:
712
            state['good'] += nodes
713
        elif bad:
714
            state['bad'] += nodes
715
        elif skip:
716
            state['skip'] += nodes
717
        hbisect.save_state(repo, state)
718

719 720
    if not check_state(state):
        return
721 722

    # actually bisect
723
    nodes, changesets, good = hbisect.bisect(repo.changelog, state)
724 725 726 727 728
    if extend:
        if not changesets:
            extendnode = extendbisectrange(nodes, good)
            if extendnode is not None:
                ui.write(_("Extending search to changeset %d:%s\n"
729
                         % (extendnode.rev(), extendnode)))
730 731
                state['current'] = [extendnode.node()]
                hbisect.save_state(repo, state)
732 733
                if noupdate:
                    return
734
                cmdutil.bailifchanged(repo)
735 736 737
                return hg.clean(repo, extendnode.node())
        raise util.Abort(_("nothing to extend"))

738
    if changesets == 0:
739
        print_result(nodes, good)
740 741 742
    else:
        assert len(nodes) == 1 # only a single node can be tested next
        node = nodes[0]
743 744 745 746 747
        # 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)
748 749
        ui.write(_("Testing changeset %d:%s "
                   "(%d changesets remaining, ~%d tests)\n")
750
                 % (rev, short(node), changesets, tests))
751 752
        state['current'] = [node]
        hbisect.save_state(repo, state)
753
        if not noupdate:
754
            cmdutil.bailifchanged(repo)
755 756
            return hg.clean(repo, node)

757 758 759 760 761
@command('bookmarks',
    [('f', 'force', False, _('force')),
    ('r', 'rev', '', _('revision'), _('REV')),
    ('d', 'delete', False, _('delete a given bookmark')),
    ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
762
    ('i', 'inactive', False, _('mark a bookmark inactive'))],
763
    _('hg bookmarks [-f] [-d] [-i] [-m NAME] [-r REV] [NAME]'))
764 765
def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False,
             rename=None, inactive=False):
766 767
    '''track a line of development with movable markers

768 769 770 771
    Bookmarks are pointers to certain commits that move when committing.
    Bookmarks are local. They can be renamed, copied and deleted. It is
    possible to use :hg:`merge NAME` to merge from a given bookmark, and
    :hg:`update NAME` to update to a given bookmark.
772 773 774 775 776 777 778

    You can use :hg:`bookmark NAME` to set a bookmark on the working
    directory's parent revision with the given name. If you specify
    a revision using -r REV (where REV may be an existing bookmark),
    the bookmark is assigned to that revision.

    Bookmarks can be pushed and pulled between repositories (see :hg:`help
779 780 781
    push` and :hg:`help pull`). This requires both the local and remote
    repositories to support bookmarks. For versions prior to 1.8, this means
    the bookmarks extension must be enabled.
782 783 784 785 786

    With -i/--inactive, the new bookmark will not be made the active
    bookmark. If -r/--rev is given, the new bookmark will not be made
    active even if -i/--inactive is not given. If no NAME is given, the
    current active bookmark will be marked inactive.
787 788 789 790 791
    '''
    hexfn = ui.debugflag and hex or short
    marks = repo._bookmarks
    cur   = repo.changectx('.').node()

792 793 794 795 796 797 798 799 800 801 802
    if delete:
        if mark is None:
            raise util.Abort(_("bookmark name required"))
        if mark not in marks:
            raise util.Abort(_("bookmark '%s' does not exist") % mark)
        if mark == repo._bookmarkcurrent:
            bookmarks.setcurrent(repo, None)
        del marks[mark]
        bookmarks.write(repo)
        return

803 804
    if rename:
        if rename not in marks:
805
            raise util.Abort(_("bookmark '%s' does not exist") % rename)
806
        if mark in marks and not force:
807 808
            raise util.Abort(_("bookmark '%s' already exists "
                               "(use -f to force)") % mark)
809 810 811
        if mark is None:
            raise util.Abort(_("new bookmark name required"))
        marks[mark] = marks[rename]
812
        if repo._bookmarkcurrent == rename and not inactive:
813
            bookmarks.setcurrent(repo, mark)
814
        del marks[rename]
815 816 817 818 819 820 821 822 823 824
        bookmarks.write(repo)
        return

    if mark is not None:
        if "\n" in mark:
            raise util.Abort(_("bookmark name cannot contain newlines"))
        mark = mark.strip()
        if not mark:
            raise util.Abort(_("bookmark names cannot consist entirely of "
                               "whitespace"))
825 826 827
        if inactive and mark == repo._bookmarkcurrent:
            bookmarks.setcurrent(repo, None)
            return
828
        if mark in marks and not force:
829 830
            raise util.Abort(_("bookmark '%s' already exists "
                               "(use -f to force)") % mark)
831
        if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
832 833 834 835 836 837
            and not force):
            raise util.Abort(
                _("a bookmark cannot have the name of an existing branch"))
        if rev:
            marks[mark] = repo.lookup(rev)
        else:
838 839
            marks[mark] = cur
        if not inactive and cur == marks[mark]:
840
            bookmarks.setcurrent(repo, mark)
841 842 843 844 845 846 847 848 849
        bookmarks.write(repo)
        return

    if mark is None:
        if rev:
            raise util.Abort(_("bookmark name required"))
        if len(marks) == 0:
            ui.status(_("no bookmarks set\n"))
        else:
850
            for bmark, n in sorted(marks.iteritems()):
851 852 853
                current = repo._bookmarkcurrent
                if bmark == current and n == cur:
                    prefix, label = '*', 'bookmarks.current'
854
                else:
855
                    prefix, label = ' ', ''
856 857 858 859 860 861 862 863 864

                if ui.quiet:
                    ui.write("%s\n" % bmark, label=label)
                else:
                    ui.write(" %s %-25s %d:%s\n" % (
                        prefix, bmark, repo.changelog.rev(n), hexfn(n)),
                        label=label)
        return

865 866 867 868 869
@command('branch',
    [('f', 'force', None,
     _('set branch name even if it shadows an existing branch')),
    ('C', 'clean', None, _('reset branch name to parent branch name'))],
    _('[-fC] [NAME]'))
870
def branch(ui, repo, label=None, **opts):
871 872
    """set or show the current branch name

873 874 875 876 877
    .. note::
       Branch names are permanent and global. Use :hg:`bookmark` to create a
       light-weight bookmark instead. See :hg:`help glossary` for more
       information about named branches and bookmarks.

878 879 880 881 882
    With no argument, show the current branch name. With one argument,
    set the working directory branch name (the branch will not exist
    in the repository until the next commit). Standard practice
    recommends that primary development take place on the 'default'
    branch.
883

884 885
    Unless -f/--force is specified, branch will not let you set a
    branch name that already exists, even if it's inactive.
886

887 888 889
    Use -C/--clean to reset the working directory branch to that of
    the parent of the working directory, negating a previous branch
    change.
890

Martin Geisler's avatar
Martin Geisler committed
891 892
    Use the command :hg:`update` to switch to an existing branch. Use
    :hg:`commit --close-branch` to mark this branch as closed.
893 894

    Returns 0 on success.
895
    """
896
    if not opts.get('clean') and not label:
897
        ui.write("%s\n" % repo.dirstate.branch())
898 899 900 901 902 903 904 905 906
        return

    wlock = repo.wlock()
    try:
        if opts.get('clean'):
            label = repo[None].p1().branch()
            repo.dirstate.setbranch(label)
            ui.status(_('reset working directory to branch %s\n') % label)
        elif label:
907
            if not opts.get('force') and label in repo.branchmap():
908 909 910 911 912 913 914 915 916 917 918
                if label not in [p.branch() for p in repo.parents()]:
                    raise util.Abort(_('a branch of the same name already'
                                       ' exists'),
                                     # i18n: "it" refers to an existing branch
                                     hint=_("use 'hg update' to switch to it"))
            repo.dirstate.setbranch(label)
            ui.status(_('marked working directory as branch %s\n') % label)
            ui.status(_('(branches are permanent and global, '
                        'did you want a bookmark?)\n'))
    finally:
        wlock.release()
919

920 921 922 923
@command('branches',
    [('a', 'active', False, _('show only branches that have unmerged heads')),
    ('c', 'closed', False, _('show normal and closed branches'))],
    _('[-ac]'))
924
def branches(ui, repo, active=False, closed=False):
925 926
    """list repository named branches