commands.py 247 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

Gregory Szorc's avatar
Gregory Szorc committed
8 9 10 11 12 13
from __future__ import absolute_import

import difflib
import errno
import os
import re
14
import sys
Gregory Szorc's avatar
Gregory Szorc committed
15 16 17 18 19 20 21

from .i18n import _
from .node import (
    hex,
    nullid,
    nullrev,
    short,
22 23
    wdirhex,
    wdirrev,
Gregory Szorc's avatar
Gregory Szorc committed
24 25 26 27 28 29 30 31
)
from . import (
    archival,
    bookmarks,
    bundle2,
    changegroup,
    cmdutil,
    copies,
32
    debugcommands as debugcommandsmod,
Gregory Szorc's avatar
Gregory Szorc committed
33
    destutil,
34
    dirstateguard,
Gregory Szorc's avatar
Gregory Szorc committed
35 36 37 38 39
    discovery,
    encoding,
    error,
    exchange,
    extensions,
40
    filemerge,
Yuya Nishihara's avatar
Yuya Nishihara committed
41
    formatter,
Gregory Szorc's avatar
Gregory Szorc committed
42 43 44 45
    graphmod,
    hbisect,
    help,
    hg,
46
    logcmdutil,
Gregory Szorc's avatar
Gregory Szorc committed
47
    merge as mergemod,
48
    narrowspec,
Gregory Szorc's avatar
Gregory Szorc committed
49
    obsolete,
50
    obsutil,
Gregory Szorc's avatar
Gregory Szorc committed
51 52
    patch,
    phases,
53
    pycompat,
54
    rcutil,
55
    registrar,
56
    revsetlang,
57
    rewriteutil,
Gregory Szorc's avatar
Gregory Szorc committed
58
    scmutil,
59
    server,
60
    shelve as shelvemod,
61
    state as statemod,
Gregory Szorc's avatar
Gregory Szorc committed
62
    streamclone,
63
    tags as tagsmod,
Gregory Szorc's avatar
Gregory Szorc committed
64 65
    ui as uimod,
    util,
66
    verify as verifymod,
67
    wireprotoserver,
Gregory Szorc's avatar
Gregory Szorc committed
68
)
69 70 71 72
from .utils import (
    dateutil,
    stringutil,
)
Gregory Szorc's avatar
Gregory Szorc committed
73

74
table = {}
75
table.update(debugcommandsmod.command._table)
76

77
command = registrar.command(table)
78
INTENT_READONLY = registrar.INTENT_READONLY
79 80 81 82

# common command options

globalopts = [
83 84 85 86 87 88 89 90 91 92 93 94 95 96
    (
        'R',
        'repository',
        '',
        _('repository root directory or name of overlay bundle file'),
        _('REPO'),
    ),
    ('', 'cwd', '', _('change working directory'), _('DIR')),
    (
        'y',
        'noninteractive',
        None,
        _('do not prompt, automatically pick the first choice for all prompts'),
    ),
97 98
    ('q', 'quiet', None, _('suppress output')),
    ('v', 'verbose', None, _('enable additional output')),
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
    (
        '',
        'color',
        '',
        # i18n: 'always', 'auto', 'never', and 'debug' are keywords
        # and should not be translated
        _("when to colorize (boolean, always, auto, never, or debug)"),
        _('TYPE'),
    ),
    (
        '',
        'config',
        [],
        _('set/override config option (use \'section.name=value\')'),
        _('CONFIG'),
    ),
115 116
    ('', 'debug', None, _('enable debugging output')),
    ('', 'debugger', None, _('start debugger')),
117 118 119 120 121 122 123 124 125 126 127 128 129 130
    (
        '',
        'encoding',
        encoding.encoding,
        _('set the charset encoding'),
        _('ENCODE'),
    ),
    (
        '',
        'encodingmode',
        encoding.encodingmode,
        _('set the charset encoding mode'),
        _('MODE'),
    ),
131 132 133 134 135
    ('', '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')),
136
    ('', 'hidden', False, _('consider hidden changesets')),
137 138 139 140 141 142 143
    (
        '',
        'pager',
        'auto',
        _("when to paginate (boolean, always, auto, or never)"),
        _('TYPE'),
    ),
144 145
]

146 147 148 149 150
dryrunopts = cmdutil.dryrunopts
remoteopts = cmdutil.remoteopts
walkopts = cmdutil.walkopts
commitopts = cmdutil.commitopts
commitopts2 = cmdutil.commitopts2
151
commitopts3 = cmdutil.commitopts3
152 153 154 155 156 157 158 159 160 161
formatteropts = cmdutil.formatteropts
templateopts = cmdutil.templateopts
logopts = cmdutil.logopts
diffopts = cmdutil.diffopts
diffwsopts = cmdutil.diffwsopts
diffopts2 = cmdutil.diffopts2
mergetoolopts = cmdutil.mergetoolopts
similarityopts = cmdutil.similarityopts
subrepoopts = cmdutil.subrepoopts
debugrevlogopts = cmdutil.debugrevlogopts
162

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

165 166 167 168 169 170 171

@command(
    'abort',
    dryrunopts,
    helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
    helpbasic=True,
)
172 173 174 175 176 177 178 179 180 181 182 183 184
def abort(ui, repo, **opts):
    """abort an unfinished operation (EXPERIMENTAL)

    Aborts a multistep operation like graft, histedit, rebase, merge,
    and unshelve if they are in an unfinished state.

    use --dry-run/-n to dry run the command.
    """
    dryrun = opts.get(r'dry_run')
    abortstate = cmdutil.getunfinishedstate(repo)
    if not abortstate:
        raise error.Abort(_('no operation in progress'))
    if not abortstate.abortfunc:
185 186 187 188 189 190 191
        raise error.Abort(
            (
                _("%s in progress but does not support 'hg abort'")
                % (abortstate._opname)
            ),
            hint=abortstate.hint(),
        )
192 193 194 195 196
    if dryrun:
        ui.status(_('%s in progress, will be aborted\n') % (abortstate._opname))
        return
    return abortstate.abortfunc(ui, repo)

197 198 199

@command(
    'add',
200
    walkopts + subrepoopts + dryrunopts,
201
    _('[OPTION]... [FILE]...'),
202
    helpcategory=command.CATEGORY_WORKING_DIRECTORY,
203 204 205
    helpbasic=True,
    inferrepo=True,
)
206
def add(ui, repo, *pats, **opts):
207 208
    """add the specified files on the next commit

209 210
    Schedule files to be version controlled and added to the
    repository.
211

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

timeless's avatar
timeless committed
215 216
    If no names are given, add all files to the repository (except
    files matching ``.hgignore``).
217 218 219

    .. container:: verbose

220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
       Examples:

         - New (unknown) files are added
           automatically by :hg:`add`::

             $ ls
             foo.c
             $ hg status
             ? foo.c
             $ hg add
             adding foo.c
             $ hg status
             A foo.c

         - Specific files to be added can be specified::

             $ ls
             bar.c  foo.c
             $ hg status
             ? bar.c
             ? foo.c
             $ hg add bar.c
             $ hg status
             A bar.c
             ? foo.c
245 246

    Returns 0 if all files are successfully added.
247 248
    """

249
    m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
250
    uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
251
    rejected = cmdutil.add(ui, repo, m, "", uipathfn, False, **opts)
252
    return rejected and 1 or 0
253

254 255 256

@command(
    'addremove',
257
    similarityopts + subrepoopts + walkopts + dryrunopts,
258
    _('[OPTION]... [FILE]...'),
259
    helpcategory=command.CATEGORY_WORKING_DIRECTORY,
260 261
    inferrepo=True,
)
262
def addremove(ui, repo, *pats, **opts):
263
    """add all new files, delete all missing files
264

265 266
    Add all new files and remove all missing files from the
    repository.
267

timeless's avatar
timeless committed
268 269 270
    Unless names are given, new files are ignored if they match any of
    the patterns in ``.hgignore``. As with add, these changes take
    effect at the next commit.
271

272
    Use the -s/--similarity option to detect renamed files. This
273
    option takes a percentage between 0 (disabled) and 100 (files must
274 275 276
    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
277
    can be expensive. After using this option, :hg:`status -C` can be
278 279 280
    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.
281

282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
    .. container:: verbose

       Examples:

         - A number of files (bar.c and foo.c) are new,
           while foobar.c has been removed (without using :hg:`remove`)
           from the repository::

             $ ls
             bar.c foo.c
             $ hg status
             ! foobar.c
             ? bar.c
             ? foo.c
             $ hg addremove
             adding bar.c
             adding foo.c
             removing foobar.c
             $ hg status
             A bar.c
             A foo.c
             R foobar.c

         - A file foobar.c was moved to foo.c without using :hg:`rename`.
           Afterwards, it was edited slightly::

             $ ls
             foo.c
             $ hg status
             ! foobar.c
             ? foo.c
             $ hg addremove --similarity 90
             removing foobar.c
             adding foo.c
             recording removal of foobar.c as rename to foo.c (94% similar)
             $ hg status -C
             A foo.c
               foobar.c
             R foobar.c

322
    Returns 0 if all files are successfully added.
323
    """
324
    opts = pycompat.byteskwargs(opts)
325 326
    if not opts.get('similarity'):
        opts['similarity'] = '100'
327
    matcher = scmutil.match(repo[None], pats, opts)
328
    relative = scmutil.anypats(pats, opts)
329
    uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
330
    return scmutil.addremove(repo, matcher, "", uipathfn, opts)
331

332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360

@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'),
        ),
        ('', 'skip', [], _('revision to not display (EXPERIMENTAL)'), _('REV')),
    ]
    + diffwsopts
    + walkopts
    + formatteropts,
361
    _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
362
    helpcategory=command.CATEGORY_FILE_CONTENTS,
363 364 365
    helpbasic=True,
    inferrepo=True,
)
366
def annotate(ui, repo, *pats, **opts):
367
    """show changeset information by line for each file
368

369
    List changes in files, showing the revision id responsible for
370
    each line.
371

372 373
    This command is useful for discovering when a change was made and
    by whom.
374

375 376 377
    If you include --file, --user, or --date, the revision number is
    suppressed unless you also include --number.

378 379 380 381
    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.
382

383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401
    .. container:: verbose

      Template:

      The following keywords are supported in addition to the common template
      keywords and functions. See also :hg:`help templates`.

      :lines:   List of lines with annotation data.
      :path:    String. Repository-absolute path of the specified file.

      And each entry of ``{lines}`` provides the following sub-keywords in
      addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.

      :line:    String. Line content.
      :lineno:  Integer. Line number at that revision.
      :path:    String. Repository-absolute path of the file at that revision.

      See :hg:`help templates.operators` for the list expansion syntax.

402
    Returns 0 on success.
403
    """
404
    opts = pycompat.byteskwargs(opts)
405
    if not pats:
406
        raise error.Abort(_('at least one filename or pattern is required'))
407

408 409 410
    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
411
        opts['file'] = True
412

413 414 415 416 417 418
    if (
        not opts.get('user')
        and not opts.get('changeset')
        and not opts.get('date')
        and not opts.get('file')
    ):
419 420 421 422 423 424
        opts['number'] = True

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

425 426 427 428
    rev = opts.get('rev')
    if rev:
        repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
    ctx = scmutil.revsingle(repo, rev)
429

430
    ui.pager('annotate')
431
    rootfm = ui.formatter('annotate', opts)
432 433 434
    if ui.debugflag:
        shorthex = pycompat.identity
    else:
435

436 437
        def shorthex(h):
            return h[:12]
438

439
    if ui.quiet:
440
        datefunc = dateutil.shortdate
441
    else:
442
        datefunc = dateutil.datestr
443 444 445 446
    if ctx.rev() is None:
        if opts.get('changeset'):
            # omit "+" suffix which is appended to node hex
            def formatrev(rev):
447
                if rev == wdirrev:
448 449 450
                    return '%d' % ctx.p1().rev()
                else:
                    return '%d' % rev
451

452
        else:
453

454
            def formatrev(rev):
455
                if rev == wdirrev:
456 457 458
                    return '%d+' % ctx.p1().rev()
                else:
                    return '%d ' % rev
459

460
        def formathex(h):
461
            if h == wdirhex:
462
                return '%s+' % shorthex(hex(ctx.p1().node()))
463
            else:
464
                return '%s ' % shorthex(h)
465

466
    else:
467 468
        formatrev = b'%d'.__mod__
        formathex = shorthex
469

470 471 472 473 474 475 476 477 478 479 480 481 482 483
    opmap = [
        ('user', ' ', lambda x: x.fctx.user(), ui.shortuser),
        ('rev', ' ', lambda x: scmutil.intrev(x.fctx), formatrev),
        ('node', ' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
        ('date', ' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
        ('path', ' ', lambda x: x.fctx.path(), pycompat.bytestr),
        ('lineno', ':', lambda x: x.lineno, pycompat.bytestr),
    ]
    opnamemap = {
        'rev': 'number',
        'node': 'changeset',
        'path': 'file',
        'lineno': 'line_number',
    }
484

485
    if rootfm.isplain():
486

487
        def makefunc(get, fmt):
488
            return lambda x: fmt(get(x))
489

490
    else:
491

492
        def makefunc(get, fmt):
493
            return get
494

495
    datahint = rootfm.datahint()
496 497 498 499 500 501 502 503 504 505 506
    funcmap = [
        (makefunc(get, fmt), sep)
        for fn, sep, get, fmt in opmap
        if opts.get(opnamemap.get(fn, fn)) or fn in datahint
    ]
    funcmap[0] = (funcmap[0][0], '')  # no separator in front of first column
    fields = ' '.join(
        fn
        for fn, sep, get, fmt in opmap
        if opts.get(opnamemap.get(fn, fn)) or fn in datahint
    )
507

508
    def bad(x, y):
509
        raise error.Abort("%s: %s" % (x, y))
510

511 512
    m = scmutil.match(ctx, pats, opts, badfn=bad)

513
    follow = not opts.get('no_follow')
514 515 516
    diffopts = patch.difffeatureopts(
        ui, opts, section='annotate', whitespace=True
    )
517 518 519 520
    skiprevs = opts.get('skip')
    if skiprevs:
        skiprevs = scmutil.revrange(repo, skiprevs)

521
    uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
Matt Mackall's avatar
Matt Mackall committed
522 523
    for abs in ctx.walk(m):
        fctx = ctx[abs]
524
        rootfm.startitem()
525
        rootfm.data(path=abs)
Jun Wu's avatar
Jun Wu committed
526
        if not opts.get('text') and fctx.isbinary():
527
            rootfm.plain(_("%s: binary file\n") % uipathfn(abs))
528 529
            continue

530
        fm = rootfm.nested('lines', tmpl='{rev}: {line}')
531 532 533
        lines = fctx.annotate(
            follow=follow, skiprevs=skiprevs, diffopts=diffopts
        )
534
        if not lines:
535
            fm.end()
536
            continue
537
        formats = []
538 539
        pieces = []

540
        for f, sep in funcmap:
541
            l = [f(n) for n in lines]
542
            if fm.isplain():
543 544 545
                sizes = [encoding.colwidth(x) for x in l]
                ml = max(sizes)
                formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
546 547
            else:
                formats.append(['%s' for x in l])
548
            pieces.append(l)
549

550
        for f, p, n in zip(zip(*formats), zip(*pieces), lines):
551
            fm.startitem()
552
            fm.context(fctx=n.fctx)
553
            fm.write(fields, "".join(f), *p)
554
            if n.skip:
555 556 557
                fmt = "* %s"
            else:
                fmt = ": %s"
558
            fm.write('line', fmt, n.text)
559

560
        if not lines[-1].text.endswith('\n'):
561
            fm.plain('\n')
562
        fm.end()
563

564
    rootfm.end()
565

566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582

@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,
583
    _('[OPTION]... DEST'),
584 585
    helpcategory=command.CATEGORY_IMPORT_EXPORT,
)
586
def archive(ui, repo, dest, **opts):
587
    '''create an unversioned archive of a repository revision
588

589 590
    By default, the revision used is the parent of the working
    directory; use -r/--rev to specify a different revision.
591

592
    The archive type is automatically detected based on file
timeless's avatar
timeless committed
593
    extension (to override, use -t/--type).
594

Matt Mackall's avatar
Matt Mackall committed
595 596 597 598 599 600 601 602 603 604 605 606
    .. 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*"

607
    Valid types are:
608

609 610 611 612
    :``files``: a directory full of files (default)
    :``tar``:   tar archive, uncompressed
    :``tbz2``:  tar archive, compressed using bzip2
    :``tgz``:   tar archive, compressed using gzip
613
    :``txz``:   tar archive, compressed using lzma (only in Python 3)
614 615
    :``uzip``:  zip archive, uncompressed
    :``zip``:   zip archive, compressed using deflate
616

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

620 621 622 623
    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.
624 625

    Returns 0 on success.
626 627
    '''

628
    opts = pycompat.byteskwargs(opts)
629 630 631 632
    rev = opts.get('rev')
    if rev:
        repo = scmutil.unhidehashlikerevs(repo, [rev], 'nowarn')
    ctx = scmutil.revsingle(repo, rev)
633
    if not ctx:
634
        raise error.Abort(_('no working directory: please specify a revision'))
635
    node = ctx.node()
636
    dest = cmdutil.makefilename(ctx, dest)
637
    if os.path.realpath(dest) == repo.root:
638
        raise error.Abort(_('repository root cannot be destination'))
639

640
    kind = opts.get('type') or archival.guesskind(dest) or 'files'
641
    prefix = opts.get('prefix')
642

643 644
    if dest == '-':
        if kind == 'files':
645
            raise error.Abort(_('cannot archive plain files to stdout'))
646
        dest = cmdutil.makefileobj(ctx, dest)
647 648
        if not prefix:
            prefix = os.path.basename(repo.root) + '-%h'
649

650
    prefix = cmdutil.makefilename(ctx, prefix)
651
    match = scmutil.match(ctx, [], opts)
652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688
    archival.archive(
        repo,
        dest,
        node,
        kind,
        not opts.get('no_decode'),
        match,
        prefix,
        subrepos=opts.get('subrepos'),
    )


@command(
    'backout',
    [
        ('', 'merge', None, _('merge with old dirstate parent after backout')),
        (
            '',
            'commit',
            None,
            _('commit if no conflicts were encountered (DEPRECATED)'),
        ),
        ('', 'no-commit', None, _('do not commit')),
        (
            '',
            'parent',
            '',
            _('parent to choose when backing out merge (DEPRECATED)'),
            _('REV'),
        ),
        ('r', 'rev', '', _('revision to backout'), _('REV')),
        ('e', 'edit', False, _('invoke editor on commit messages')),
    ]
    + mergetoolopts
    + walkopts
    + commitopts
    + commitopts2,
689
    _('[OPTION]... [-r] REV'),
690 691
    helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
)
692
def backout(ui, repo, node=None, rev=None, **opts):
Vadim Gelfer's avatar
Vadim Gelfer committed
693 694
    '''reverse effect of earlier changeset

695
    Prepare a new changeset with the effect of REV undone in the
696 697
    current working directory. If no conflicts were encountered,
    it will be committed immediately.
698

699
    If REV is the parent of the working directory, then this new changeset
700
    is committed automatically (unless --no-commit is specified).
701

702
    .. note::
703

704
       :hg:`backout` cannot be used to fix either an unwanted or
705
       incorrect merge.
706

Matt Mackall's avatar
Matt Mackall committed
707 708
    .. container:: verbose

709 710 711 712 713 714 715 716 717 718 719 720
      Examples:

      - Reverse the effect of the parent of the working directory.
        This backout will be committed immediately::

          hg backout -r .

      - Reverse the effect of previous bad revision 23::

          hg backout -r 23

      - Reverse the effect of previous bad revision 23 and
721
        leave changes uncommitted::
722

723 724
          hg backout -r 23 --no-commit
          hg commit -m "Backout revision 23"
725

Matt Mackall's avatar
Matt Mackall committed
726 727 728 729 730 731 732 733 734
      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.
735

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

738 739 740
    See :hg:`help revert` for a way to restore files to the state
    of another revision.

741 742
    Returns 0 on success, 1 if nothing to backout or there are unresolved
    files.
743
    '''
744
    with repo.wlock(), repo.lock():
745
        return _dobackout(ui, repo, node, rev, **opts)
746

747

748
def _dobackout(ui, repo, node=None, rev=None, **opts):
749
    opts = pycompat.byteskwargs(opts)
750 751
    if opts.get('commit') and opts.get('no_commit'):
        raise error.Abort(_("cannot use --commit with --no-commit"))
752 753
    if opts.get('merge') and opts.get('no_commit'):
        raise error.Abort(_("cannot use --merge with --no-commit"))
754

Daniel Holth's avatar
Daniel Holth committed
755
    if rev and node:
756
        raise error.Abort(_("please specify just one revision"))
Daniel Holth's avatar
Daniel Holth committed
757 758 759

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

761
    if not rev:
762
        raise error.Abort(_("please specify a revision to backout"))
763

764 765
    date = opts.get('date')
    if date:
766
        opts['date'] = dateutil.parsedate(date)
767

768
    cmdutil.checkunfinished(repo)
769
    cmdutil.bailifchanged(repo)
770
    node = scmutil.revsingle(repo, rev).node()
771

772
    op1, op2 = repo.dirstate.parents()
773
    if not repo.changelog.isancestor(node, op1):
774
        raise error.Abort(_('cannot backout change that is not an ancestor'))
775

776 777
    p1, p2 = repo.changelog.parents(node)
    if p1 == nullid:
778
        raise error.Abort(_('cannot backout a change with no parents'))
Vadim Gelfer's avatar
Vadim Gelfer committed
779
    if p2 != nullid:
780
        if not opts.get('parent'):
781
            raise error.Abort(_('cannot backout a merge changeset'))
782 783
        p = repo.lookup(opts['parent'])
        if p not in (p1, p2):
784 785 786
            raise error.Abort(
                _('%s is not a parent of %s') % (short(p), short(node))
            )
787 788
        parent = p
    else:
789
        if opts.get('parent'):
790
            raise error.Abort(_('cannot use --parent on non-merge changeset'))
791
        parent = p1
792

793
    # the backout should appear on the same branch
794 795 796 797
    branch = repo.dirstate.branch()
    bheads = repo.branchheads(branch)
    rctx = scmutil.revsingle(repo, hex(parent))
    if not opts.get('merge') and op1 != node:
798
        with dirstateguard.dirstateguard(repo, 'backout'):
799 800
            overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
            with ui.configoverride(overrides, 'backout'):
801 802 803 804 805 806 807 808
                stats = mergemod.update(
                    repo,
                    parent,
                    branchmerge=True,
                    force=True,
                    ancestor=node,
                    mergeancestor=False,
                )
809
            repo.setparents(op1, op2)
810 811
        hg._showstats(repo, stats)
        if stats.unresolvedcount:
812 813 814
            repo.ui.status(
                _("use 'hg resolve' to retry unresolved " "file merges\n")
            )
815
            return 1
816 817 818 819 820
    else:
        hg.clean(repo, node, show_stats=False)
        repo.dirstate.setbranch(branch)
        cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())

821
    if opts.get('no_commit'):
822
        msg = _("changeset %s backed out, " "don't forget to commit.\n")
823 824
        ui.status(msg % short(node))
        return 0
825 826 827

    def commitfunc(ui, repo, message, match, opts):
        editform = 'backout'
828 829 830
        e = cmdutil.getcommiteditor(
            editform=editform, **pycompat.strkwargs(opts)
        )
831 832 833 834
        if not message:
            # we don't translate commit messages
            message = "Backed out changeset %s" % short(node)
            e = cmdutil.getcommiteditor(edit=True, editform=editform)
835 836 837 838
        return repo.commit(
            message, opts.get('user'), opts.get('date'), match, editor=e
        )

839 840 841 842 843 844 845 846
    newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
    if not newnode:
        ui.status(_("nothing changed\n"))
        return 1
    cmdutil.commitstatus(repo, newnode, branch, bheads)

    def nice(node):
        return '%d:%s' % (repo.changelog.rev(node), short(node))
847 848 849 850 851

    ui.status(
        _('changeset %s backs out changeset %s\n')
        % (nice(repo.changelog.tip()), nice(node))
    )
852 853
    if opts.get('merge') and op1 != node:
        hg.clean(repo, op1, show_stats=False)
854
        ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
855 856
        overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
        with ui.configoverride(overrides, 'backout'):
857
            return hg.merge(repo, hex(repo.changelog.tip()))
858
    return 0
Vadim Gelfer's avatar
Vadim Gelfer committed
859

860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877

@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')),
    ],
878
    _("[-gbsr] [-U] [-c CMD] [REV]"),
879 880 881 882 883 884 885 886 887 888 889 890 891 892 893
    helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
)
def bisect(
    ui,
    repo,
    rev=None,
    extra=None,
    command=None,
    reset=None,
    good=None,
    bad=None,
    skip=None,
    extend=None,
    noupdate=None,
):
894 895
    """subdivision search of changesets

896 897 898 899 900 901 902 903 904 905 906 907 908
    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.
909