commands.py 203 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 match as matchmod
17
import merge as mergemod
18
import minirst, revset, fileset
19
import dagparser, context, simplemerge
20
import random, setdiscovery, treediscovery, dagutil
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 101 102 103 104 105 106 107 108
    ('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')),
] + 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'))
]

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

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

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

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

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

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

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

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

    .. container:: verbose

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

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

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

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

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

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

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

192 193 194 195 196
    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
197 198
    can be expensive. After using this option, :hg:`status -C` can be
    used to check which files were identified as moved or renamed.
199 200

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

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

227 228
    List changes in files, showing the revision id responsible for
    each line
229

230 231
    This command is useful for discovering when a change was made and
    by whom.
232

233 234 235 236
    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.
237 238

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

245 246
    datefunc = ui.quiet and util.shortdate or util.datestr
    getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
247

248
    if not pats:
249
        raise util.Abort(_('at least one filename or pattern is required'))
250

251 252 253 254 255 256
    opmap = [('user', ' ', lambda x: ui.shortuser(x[0].user())),
             ('number', ' ', lambda x: str(x[0].rev())),
             ('changeset', ' ', lambda x: short(x[0].node())),
             ('date', ' ', getdate),
             ('file', ' ', lambda x: x[0].path()),
             ('line_number', ':', lambda x: str(x[1])),
257 258
            ]

259
    if (not opts.get('user') and not opts.get('changeset')
260
        and not opts.get('date') and not opts.get('file')):
261
        opts['number'] = True
262

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

267 268
    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
269

270 271 272
    def bad(x, y):
        raise util.Abort("%s: %s" % (x, y))

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

284 285
        lines = fctx.annotate(follow=follow, linenumber=linenumber,
                              diffopts=diffopts)
286 287
        pieces = []

288
        for f, sep in funcmap:
289 290
            l = [f(n) for n, dummy in lines]
            if l:
291 292
                sized = [(x, encoding.colwidth(x)) for x in l]
                ml = max([w for x, w in sized])
293 294
                pieces.append(["%s%s%s" % (sep, ' ' * (ml - w), x)
                               for x, w in sized])
295

mpm@selenic.com's avatar
mpm@selenic.com committed
296 297
        if pieces:
            for p, l in zip(zip(*pieces), lines):
298
                ui.write("%s: %s" % ("".join(p), l[1]))
299

300 301 302 303 304 305 306 307
@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'))
308
def archive(ui, repo, dest, **opts):
309
    '''create an unversioned archive of a repository revision
310

311 312
    By default, the revision used is the parent of the working
    directory; use -r/--rev to specify a different revision.
313

314 315 316
    The archive type is automatically detected based on file
    extension (or override using -t/--type).

Matt Mackall's avatar
Matt Mackall committed
317 318 319 320 321 322 323 324 325 326 327 328
    .. 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*"

329
    Valid types are:
330

331 332 333 334 335 336
    :``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
337

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

341 342 343 344
    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.
345 346

    Returns 0 on success.
347 348
    '''

349
    ctx = scmutil.revsingle(repo, opts.get('rev'))
350
    if not ctx:
351
        raise util.Abort(_('no working directory: please specify a revision'))
352
    node = ctx.node()
353
    dest = cmdutil.makefilename(repo, dest, node)
354
    if os.path.realpath(dest) == repo.root:
355
        raise util.Abort(_('repository root cannot be destination'))
356

357
    kind = opts.get('type') or archival.guesskind(dest) or 'files'
358
    prefix = opts.get('prefix')
359

360 361 362
    if dest == '-':
        if kind == 'files':
            raise util.Abort(_('cannot archive plain files to stdout'))
363
        dest = cmdutil.makefileobj(repo, dest)
364 365
        if not prefix:
            prefix = os.path.basename(repo.root) + '-%h'
366

367
    prefix = cmdutil.makefilename(repo, prefix, node)
368
    matchfn = scmutil.match(ctx, [], opts)
369
    archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
370
                     matchfn, prefix, subrepos=opts.get('subrepos'))
371

372 373
@command('backout',
    [('', 'merge', None, _('merge with old dirstate parent after backout')),
374 375
    ('', 'parent', '',
     _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
376
    ('r', 'rev', '', _('revision to backout'), _('REV')),
377
    ] + mergetoolopts + walkopts + commitopts + commitopts2,
378
    _('[OPTION]... [-r] REV'))
Daniel Holth's avatar
Daniel Holth committed
379
def backout(ui, repo, node=None, rev=None, **opts):
Vadim Gelfer's avatar
Vadim Gelfer committed
380 381
    '''reverse effect of earlier changeset

382 383 384
    Prepare a new changeset with the effect of REV undone in the
    current working directory.

385
    If REV is the parent of the working directory, then this new changeset
386 387 388
    is committed automatically. Otherwise, hg needs to merge the
    changes and the merged result is left uncommitted.

389 390 391 392
    .. note::
      backout cannot be used to fix either an unwanted or
      incorrect merge.

Matt Mackall's avatar
Matt Mackall committed
393 394 395 396 397 398 399 400 401 402 403
    .. 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.
404

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

    Returns 0 on success.
408
    '''
Daniel Holth's avatar
Daniel Holth committed
409 410 411 412 413
    if rev and node:
        raise util.Abort(_("please specify just one revision"))

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

415 416 417
    if not rev:
        raise util.Abort(_("please specify a revision to backout"))

418 419 420 421
    date = opts.get('date')
    if date:
        opts['date'] = util.parsedate(date)

422
    cmdutil.bailifchanged(repo)
423
    node = scmutil.revsingle(repo, rev).node()
424

425
    op1, op2 = repo.dirstate.parents()
426 427
    a = repo.changelog.ancestor(op1, node)
    if a != node:
428
        raise util.Abort(_('cannot backout change on a different branch'))
429

430 431
    p1, p2 = repo.changelog.parents(node)
    if p1 == nullid:
432
        raise util.Abort(_('cannot backout a change with no parents'))
Vadim Gelfer's avatar
Vadim Gelfer committed
433
    if p2 != nullid:
434
        if not opts.get('parent'):
435
            raise util.Abort(_('cannot backout a merge changeset'))
436 437
        p = repo.lookup(opts['parent'])
        if p not in (p1, p2):
438
            raise util.Abort(_('%s is not a parent of %s') %
439
                             (short(p), short(node)))
440 441
        parent = p
    else:
442
        if opts.get('parent'):
443 444
            raise util.Abort(_('cannot use --parent on non-merge changeset'))
        parent = p1
445

446 447
    # the backout should appear on the same branch
    branch = repo.dirstate.branch()
448
    hg.clean(repo, node, show_stats=False)
449
    repo.dirstate.setbranch(branch)
Vadim Gelfer's avatar
Vadim Gelfer committed
450
    revert_opts = opts.copy()
451
    revert_opts['date'] = None
452
    revert_opts['all'] = True
Vadim Gelfer's avatar
Vadim Gelfer committed
453
    revert_opts['rev'] = hex(parent)
454
    revert_opts['no_backup'] = None
Vadim Gelfer's avatar
Vadim Gelfer committed
455
    revert(ui, repo, **revert_opts)
456
    if not opts.get('merge') and op1 != node:
457 458 459 460 461
        try:
            ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
            return hg.update(repo, op1)
        finally:
            ui.setconfig('ui', 'forcemerge', '')
462

Vadim Gelfer's avatar
Vadim Gelfer committed
463 464
    commit_opts = opts.copy()
    commit_opts['addremove'] = False
465
    if not commit_opts['message'] and not commit_opts['logfile']:
466
        # we don't translate commit messages
467
        commit_opts['message'] = "Backed out changeset %s" % short(node)
468
        commit_opts['force_editor'] = True
Vadim Gelfer's avatar
Vadim Gelfer committed
469 470 471 472 473
    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)))
474
    if opts.get('merge') and op1 != node:
475
        hg.clean(repo, op1, show_stats=False)
476 477
        ui.status(_('merging with changeset %s\n')
                  % nice(repo.changelog.tip()))
478 479 480 481 482
        try:
            ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
            return hg.merge(repo, hex(repo.changelog.tip()))
        finally:
            ui.setconfig('ui', 'forcemerge', '')
483
    return 0
Vadim Gelfer's avatar
Vadim Gelfer committed
484

485 486 487 488 489 490 491 492 493
@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]"))
494
def bisect(ui, repo, rev=None, extra=None, command=None,
495 496
               reset=None, good=None, bad=None, skip=None, extend=None,
               noupdate=None):
497 498
    """subdivision search of changesets

499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515
    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.
    Its exit status 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.
516

517 518 519 520 521 522 523 524 525 526 527 528 529 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
    .. 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)"

      - 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)"

563 564
      See :hg:`help revsets` for more about the `bisect()` keyword.

565
    Returns 0 on success.
566
    """
567 568 569 570 571 572 573 574
    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
575
                return parents[0].ancestor(parents[1])
576 577
        return None

578 579 580 581
    def print_result(nodes, good):
        displayer = cmdutil.show_changeset(ui, repo, {})
        if len(nodes) == 1:
            # narrowed it down to a single revision
582 583 584 585
            if good:
                ui.write(_("The first good revision is:\n"))
            else:
                ui.write(_("The first bad revision is:\n"))
586
            displayer.show(repo[nodes[0]])
587 588 589 590 591
            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')
592
                         % extendnode)
593 594
        else:
            # multiple possible revisions
595 596 597 598 599 600
            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"))
601
            for n in nodes:
602
                displayer.show(repo[n])
603
        displayer.close()
604 605 606 607 608 609 610 611 612 613 614

    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

615 616 617 618 619 620 621 622 623 624
    # 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
625
    elif extra or good + bad + skip + reset + extend + bool(command) > 1:
626
        raise util.Abort(_('incompatible arguments'))
627 628 629 630 631 632 633

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

634 635 636 637
    state = hbisect.load_state(repo)

    if command:
        changesets = 1
638 639 640
        try:
            while changesets:
                # update state
641
                status = util.system(command, out=ui.fout)
642 643 644 645 646 647 648 649 650 651 652
                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"
653
                ctx = scmutil.revsingle(repo, rev)
654
                rev = None # clear for future iterations
655 656
                state[transition].append(ctx.node())
                ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
657 658 659 660
                check_state(state, interactive=False)
                # bisect
                nodes, changesets, good = hbisect.bisect(repo.changelog, state)
                # update to next check
661
                cmdutil.bailifchanged(repo)
662 663 664
                hg.clean(repo, nodes[0], show_stats=False)
        finally:
            hbisect.save_state(repo, state)
665 666
        print_result(nodes, good)
        return
667 668

    # update state
669 670

    if rev:
671
        nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
672 673 674
    else:
        nodes = [repo.lookup('.')]

675 676
    if good or bad or skip:
        if good:
677
            state['good'] += nodes
678
        elif bad:
679
            state['bad'] += nodes
680
        elif skip:
681
            state['skip'] += nodes
682
        hbisect.save_state(repo, state)
683

684 685
    if not check_state(state):
        return
686 687

    # actually bisect
688
    nodes, changesets, good = hbisect.bisect(repo.changelog, state)
689 690 691 692 693
    if extend:
        if not changesets:
            extendnode = extendbisectrange(nodes, good)
            if extendnode is not None:
                ui.write(_("Extending search to changeset %d:%s\n"
694
                         % (extendnode.rev(), extendnode)))
695 696
                if noupdate:
                    return
697
                cmdutil.bailifchanged(repo)
698 699 700
                return hg.clean(repo, extendnode.node())
        raise util.Abort(_("nothing to extend"))

701
    if changesets == 0:
702
        print_result(nodes, good)
703 704 705
    else:
        assert len(nodes) == 1 # only a single node can be tested next
        node = nodes[0]
706 707 708 709 710
        # 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)
711 712
        ui.write(_("Testing changeset %d:%s "
                   "(%d changesets remaining, ~%d tests)\n")
713
                 % (rev, short(node), changesets, tests))
714
        if not noupdate:
715
            cmdutil.bailifchanged(repo)
716 717
            return hg.clean(repo, node)

718 719 720 721 722 723 724
@command('bookmarks',
    [('f', 'force', False, _('force')),
    ('r', 'rev', '', _('revision'), _('REV')),
    ('d', 'delete', False, _('delete a given bookmark')),
    ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
    ('i', 'inactive', False, _('do not mark a new bookmark active'))],
    _('hg bookmarks [-f] [-d] [-i] [-m NAME] [-r REV] [NAME]'))
725 726
def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False,
             rename=None, inactive=False):
727 728
    '''track a line of development with movable markers

729 730 731 732
    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.
733 734 735 736 737 738 739

    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
740 741 742
    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.
743 744 745 746 747
    '''
    hexfn = ui.debugflag and hex or short
    marks = repo._bookmarks
    cur   = repo.changectx('.').node()

748 749 750 751 752 753 754 755 756 757 758
    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

759 760
    if rename:
        if rename not in marks:
761
            raise util.Abort(_("bookmark '%s' does not exist") % rename)
762
        if mark in marks and not force:
763 764
            raise util.Abort(_("bookmark '%s' already exists "
                               "(use -f to force)") % mark)
765 766 767
        if mark is None:
            raise util.Abort(_("new bookmark name required"))
        marks[mark] = marks[rename]
768
        if repo._bookmarkcurrent == rename and not inactive:
769
            bookmarks.setcurrent(repo, mark)
770
        del marks[rename]
771 772 773 774 775 776 777 778 779 780
        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"))
781 782 783
        if inactive and mark == repo._bookmarkcurrent:
            bookmarks.setcurrent(repo, None)
            return
784
        if mark in marks and not force:
785 786
            raise util.Abort(_("bookmark '%s' already exists "
                               "(use -f to force)") % mark)
787 788 789 790 791 792 793
        if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
            and not force):
            raise util.Abort(
                _("a bookmark cannot have the name of an existing branch"))
        if rev:
            marks[mark] = repo.lookup(rev)
        else:
794 795
            marks[mark] = cur
        if not inactive and cur == marks[mark]:
796
            bookmarks.setcurrent(repo, mark)
797 798 799 800 801 802 803 804 805
        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:
806
            for bmark, n in sorted(marks.iteritems()):
807 808 809
                current = repo._bookmarkcurrent
                if bmark == current and n == cur:
                    prefix, label = '*', 'bookmarks.current'
810
                else:
811
                    prefix, label = ' ', ''
812 813 814 815 816 817 818 819 820

                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

821 822 823 824 825
@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]'))
826
def branch(ui, repo, label=None, **opts):
827 828
    """set or show the current branch name

829 830 831 832 833
    .. 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.

834 835 836 837 838
    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.
839

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

843 844 845
    Use -C/--clean to reset the working directory branch to that of
    the parent of the working directory, negating a previous branch
    change.
846

Martin Geisler's avatar
Martin Geisler committed
847 848
    Use the command :hg:`update` to switch to an existing branch. Use
    :hg:`commit --close-branch` to mark this branch as closed.
849 850

    Returns 0 on success.
851 852
    """

853
    if opts.get('clean'):
854
        label = repo[None].p1().branch()
855 856 857
        repo.dirstate.setbranch(label)
        ui.status(_('reset working directory to branch %s\n') % label)
    elif label:
858
        if not opts.get('force') and label in repo.branchtags():
859
            if label not in [p.branch() for p in repo.parents()]:
860
                raise util.Abort(_('a branch of the same name already exists'),
861
                                 # i18n: "it" refers to an existing branch
862
                                 hint=_("use 'hg update' to switch to it"))
863
        repo.dirstate.setbranch(label)
864
        ui.status(_('marked working directory as branch %s\n') % label)
Matt Mackall's avatar
Matt Mackall committed
865 866
        ui.status(_('(branches are permanent and global, '
                    'did you want a bookmark?)\n'))
867
    else:
868
        ui.write("%s\n" % repo.dirstate.branch())
869

870 871 872 873
@command('branches',
    [('a', 'active', False, _('show only branches that have unmerged heads')),
    ('c', 'closed', False, _('show normal and closed branches'))],
    _('[-ac]'))
874
def branches(ui, repo, active=False, closed=False):
875 876
    """list repository named branches

877 878
    List the repository's named branches, indicating which ones are
    inactive. If -c/--closed is specified, also list branches which have
879
    been marked closed (see :hg:`commit --close-branch`).
880

881 882
    If -a/--active is specified, only show active branches. A branch
    is considered active if it contains repository heads.
883

Martin Geisler's avatar
Martin Geisler committed
884
    Use the command :hg:`update` to switch to an existing branch.
885 886

    Returns 0.
887
    """