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

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

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

# common command options

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

149 150 151 152 153
dryrunopts = cmdutil.dryrunopts
remoteopts = cmdutil.remoteopts
walkopts = cmdutil.walkopts
commitopts = cmdutil.commitopts
commitopts2 = cmdutil.commitopts2
154
commitopts3 = cmdutil.commitopts3
155 156 157 158 159 160 161 162 163 164
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
165

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

168 169

@command(
170
    b'abort',
171 172 173 174
    dryrunopts,
    helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
    helpbasic=True,
)
175 176 177 178 179 180 181 182
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.
    """
183
    dryrun = opts.get('dry_run')
184 185
    abortstate = cmdutil.getunfinishedstate(repo)
    if not abortstate:
186
        raise error.Abort(_(b'no operation in progress'))
187
    if not abortstate.abortfunc:
188 189
        raise error.Abort(
            (
190
                _(b"%s in progress but does not support 'hg abort'")
191 192 193 194
                % (abortstate._opname)
            ),
            hint=abortstate.hint(),
        )
195
    if dryrun:
196 197 198
        ui.status(
            _(b'%s in progress, will be aborted\n') % (abortstate._opname)
        )
199 200 201
        return
    return abortstate.abortfunc(ui, repo)

202 203

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

214 215
    Schedule files to be version controlled and added to the
    repository.
216

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

timeless's avatar
timeless committed
220 221
    If no names are given, add all files to the repository (except
    files matching ``.hgignore``).
222 223 224

    .. container:: verbose

225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
       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
250 251

    Returns 0 if all files are successfully added.
252 253
    """

254
    m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
255
    uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
256
    rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
257
    return rejected and 1 or 0
258

259 260

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

270 271
    Add all new files and remove all missing files from the
    repository.
272

timeless's avatar
timeless committed
273 274 275
    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.
276

277
    Use the -s/--similarity option to detect renamed files. This
278
    option takes a percentage between 0 (disabled) and 100 (files must
279 280 281
    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
282
    can be expensive. After using this option, :hg:`status -C` can be
283 284 285
    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.
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 322 323 324 325 326
    .. 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

327
    Returns 0 if all files are successfully added.
328
    """
329
    opts = pycompat.byteskwargs(opts)
330 331
    if not opts.get(b'similarity'):
        opts[b'similarity'] = b'100'
332
    matcher = scmutil.match(repo[None], pats, opts)
333
    relative = scmutil.anypats(pats, opts)
334
    uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
335
    return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
336

337 338

@command(
339
    b'annotate|blame',
340
    [
341
        (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
342
        (
343 344
            b'',
            b'follow',
345
            None,
346
            _(b'follow copies/renames and list the filename (DEPRECATED)'),
347
        ),
348 349 350 351 352 353 354
        (b'', b'no-follow', None, _(b"don't follow copies and renames")),
        (b'a', b'text', None, _(b'treat all files as text')),
        (b'u', b'user', None, _(b'list the author (long with -v)')),
        (b'f', b'file', None, _(b'list the filename')),
        (b'd', b'date', None, _(b'list the date (short with -q)')),
        (b'n', b'number', None, _(b'list the revision number (default)')),
        (b'c', b'changeset', None, _(b'list the changeset')),
355
        (
356 357
            b'l',
            b'line-number',
358
            None,
359 360 361 362 363 364 365 366
            _(b'show line number at the first appearance'),
        ),
        (
            b'',
            b'skip',
            [],
            _(b'revision to not display (EXPERIMENTAL)'),
            _(b'REV'),
367 368 369 370 371
        ),
    ]
    + diffwsopts
    + walkopts
    + formatteropts,
372
    _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
373
    helpcategory=command.CATEGORY_FILE_CONTENTS,
374 375 376
    helpbasic=True,
    inferrepo=True,
)
377
def annotate(ui, repo, *pats, **opts):
378
    """show changeset information by line for each file
379

380
    List changes in files, showing the revision id responsible for
381
    each line.
382

383 384
    This command is useful for discovering when a change was made and
    by whom.
385

386 387 388
    If you include --file, --user, or --date, the revision number is
    suppressed unless you also include --number.

389 390 391 392
    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.
393

394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412
    .. 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.

413
    Returns 0 on success.
414
    """
415
    opts = pycompat.byteskwargs(opts)
416
    if not pats:
417
        raise error.Abort(_(b'at least one filename or pattern is required'))
418

419
    if opts.get(b'follow'):
420 421
        # --follow is deprecated and now just an alias for -f/--file
        # to mimic the behavior of Mercurial before version 1.5
422
        opts[b'file'] = True
423

424
    if (
425 426 427 428
        not opts.get(b'user')
        and not opts.get(b'changeset')
        and not opts.get(b'date')
        and not opts.get(b'file')
429
    ):
430
        opts[b'number'] = True
431

432 433 434 435 436 437 438
    linenumber = opts.get(b'line_number') is not None
    if (
        linenumber
        and (not opts.get(b'changeset'))
        and (not opts.get(b'number'))
    ):
        raise error.Abort(_(b'at least one of -n/-c is required for -l'))
439

440
    rev = opts.get(b'rev')
441
    if rev:
442
        repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
443
    ctx = scmutil.revsingle(repo, rev)
444

445 446
    ui.pager(b'annotate')
    rootfm = ui.formatter(b'annotate', opts)
447 448 449
    if ui.debugflag:
        shorthex = pycompat.identity
    else:
450

451 452
        def shorthex(h):
            return h[:12]
453

454
    if ui.quiet:
455
        datefunc = dateutil.shortdate
456
    else:
457
        datefunc = dateutil.datestr
458
    if ctx.rev() is None:
459
        if opts.get(b'changeset'):
460 461
            # omit "+" suffix which is appended to node hex
            def formatrev(rev):
462
                if rev == wdirrev:
463
                    return b'%d' % ctx.p1().rev()
464
                else:
465
                    return b'%d' % rev
466

467
        else:
468

469
            def formatrev(rev):
470
                if rev == wdirrev:
471
                    return b'%d+' % ctx.p1().rev()
472
                else:
473
                    return b'%d ' % rev
474

475
        def formathex(h):
476
            if h == wdirhex:
477
                return b'%s+' % shorthex(hex(ctx.p1().node()))
478
            else:
479
                return b'%s ' % shorthex(h)
480

481
    else:
482 483
        formatrev = b'%d'.__mod__
        formathex = shorthex
484

485
    opmap = [
486 487 488 489 490 491
        (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
        (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
        (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
        (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
        (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
        (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
492 493
    ]
    opnamemap = {
494 495 496 497
        b'rev': b'number',
        b'node': b'changeset',
        b'path': b'file',
        b'lineno': b'line_number',
498
    }
499

500
    if rootfm.isplain():
501

502
        def makefunc(get, fmt):
503
            return lambda x: fmt(get(x))
504

505
    else:
506

507
        def makefunc(get, fmt):
508
            return get
509

510
    datahint = rootfm.datahint()
511 512 513 514 515
    funcmap = [
        (makefunc(get, fmt), sep)
        for fn, sep, get, fmt in opmap
        if opts.get(opnamemap.get(fn, fn)) or fn in datahint
    ]
516 517
    funcmap[0] = (funcmap[0][0], b'')  # no separator in front of first column
    fields = b' '.join(
518 519 520 521
        fn
        for fn, sep, get, fmt in opmap
        if opts.get(opnamemap.get(fn, fn)) or fn in datahint
    )
522

523
    def bad(x, y):
524
        raise error.Abort(b"%s: %s" % (x, y))
525

526 527
    m = scmutil.match(ctx, pats, opts, badfn=bad)

528
    follow = not opts.get(b'no_follow')
529
    diffopts = patch.difffeatureopts(
530
        ui, opts, section=b'annotate', whitespace=True
531
    )
532
    skiprevs = opts.get(b'skip')
533 534 535
    if skiprevs:
        skiprevs = scmutil.revrange(repo, skiprevs)

536
    uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
Matt Mackall's avatar
Matt Mackall committed
537 538
    for abs in ctx.walk(m):
        fctx = ctx[abs]
539
        rootfm.startitem()
540
        rootfm.data(path=abs)
541 542
        if not opts.get(b'text') and fctx.isbinary():
            rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
543 544
            continue

545
        fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
546 547 548
        lines = fctx.annotate(
            follow=follow, skiprevs=skiprevs, diffopts=diffopts
        )
549
        if not lines:
550
            fm.end()
551
            continue
552
        formats = []
553 554
        pieces = []

555
        for f, sep in funcmap:
556
            l = [f(n) for n in lines]
557
            if fm.isplain():
558 559
                sizes = [encoding.colwidth(x) for x in l]
                ml = max(sizes)
560
                formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
561
            else:
562
                formats.append([b'%s' for x in l])
563
            pieces.append(l)
564

565
        for f, p, n in zip(zip(*formats), zip(*pieces), lines):
566
            fm.startitem()
567
            fm.context(fctx=n.fctx)
568
            fm.write(fields, b"".join(f), *p)
569
            if n.skip:
570
                fmt = b"* %s"
571
            else:
572 573
                fmt = b": %s"
            fm.write(b'line', fmt, n.text)
574

575 576
        if not lines[-1].text.endswith(b'\n'):
            fm.plain(b'\n')
577
        fm.end()
578

579
    rootfm.end()
580

581 582

@command(
583
    b'archive',
584
    [
585
        (b'', b'no-decode', None, _(b'do not pass files through decoders')),
586
        (
587 588 589 590 591
            b'p',
            b'prefix',
            b'',
            _(b'directory prefix for files in archive'),
            _(b'PREFIX'),
592
        ),
593 594
        (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
        (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
595 596 597
    ]
    + subrepoopts
    + walkopts,
598
    _(b'[OPTION]... DEST'),
599 600
    helpcategory=command.CATEGORY_IMPORT_EXPORT,
)
601
def archive(ui, repo, dest, **opts):
602
    '''create an unversioned archive of a repository revision
603

604 605
    By default, the revision used is the parent of the working
    directory; use -r/--rev to specify a different revision.
606

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

Matt Mackall's avatar
Matt Mackall committed
610 611 612 613 614 615 616 617 618 619 620 621
    .. 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*"

622
    Valid types are:
623

624 625 626 627
    :``files``: a directory full of files (default)
    :``tar``:   tar archive, uncompressed
    :``tbz2``:  tar archive, compressed using bzip2
    :``tgz``:   tar archive, compressed using gzip
628
    :``txz``:   tar archive, compressed using lzma (only in Python 3)
629 630
    :``uzip``:  zip archive, uncompressed
    :``zip``:   zip archive, compressed using deflate
631

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

635 636 637 638
    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.
639 640

    Returns 0 on success.
641 642
    '''

643
    opts = pycompat.byteskwargs(opts)
644
    rev = opts.get(b'rev')
645
    if rev:
646
        repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
647
    ctx = scmutil.revsingle(repo, rev)
648
    if not ctx:
649
        raise error.Abort(_(b'no working directory: please specify a revision'))
650
    node = ctx.node()
651
    dest = cmdutil.makefilename(ctx, dest)
652
    if os.path.realpath(dest) == repo.root:
653
        raise error.Abort(_(b'repository root cannot be destination'))
654

655 656
    kind = opts.get(b'type') or archival.guesskind(dest) or b'files'
    prefix = opts.get(b'prefix')
657

658 659 660
    if dest == b'-':
        if kind == b'files':
            raise error.Abort(_(b'cannot archive plain files to stdout'))
661
        dest = cmdutil.makefileobj(ctx, dest)
662
        if not prefix:
663
            prefix = os.path.basename(repo.root) + b'-%h'
664

665
    prefix = cmdutil.makefilename(ctx, prefix)
666
    match = scmutil.match(ctx, [], opts)
667 668 669 670 671
    archival.archive(
        repo,
        dest,
        node,
        kind,
672
        not opts.get(b'no_decode'),
673 674
        match,
        prefix,
675
        subrepos=opts.get(b'subrepos'),
676 677 678 679
    )


@command(
680
    b'backout',
681 682
    [
        (
683 684 685 686 687 688 689 690
            b'',
            b'merge',
            None,
            _(b'merge with old dirstate parent after backout'),
        ),
        (
            b'',
            b'commit',
691
            None,
692
            _(b'commit if no conflicts were encountered (DEPRECATED)'),
693
        ),
694
        (b'', b'no-commit', None, _(b'do not commit')),
695
        (
696 697 698 699 700
            b'',
            b'parent',
            b'',
            _(b'parent to choose when backing out merge (DEPRECATED)'),
            _(b'REV'),
701
        ),
702 703
        (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
        (b'e', b'edit', False, _(b'invoke editor on commit messages')),
704 705 706 707 708
    ]
    + mergetoolopts
    + walkopts
    + commitopts
    + commitopts2,
709
    _(b'[OPTION]... [-r] REV'),
710 711
    helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
)
712
def backout(ui, repo, node=None, rev=None, **opts):
Vadim Gelfer's avatar
Vadim Gelfer committed
713 714
    '''reverse effect of earlier changeset

715
    Prepare a new changeset with the effect of REV undone in the
716 717
    current working directory. If no conflicts were encountered,
    it will be committed immediately.
718

719
    If REV is the parent of the working directory, then this new changeset
720
    is committed automatically (unless --no-commit is specified).
721

722
    .. note::
723

724
       :hg:`backout` cannot be used to fix either an unwanted or
725
       incorrect merge.
726

Matt Mackall's avatar
Matt Mackall committed
727 728
    .. container:: verbose

729 730 731 732 733 734 735 736 737 738 739 740
      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
741
        leave changes uncommitted::
742

743 744
          hg backout -r 23 --no-commit
          hg commit -m "Backout revision 23"
745

Matt Mackall's avatar
Matt Mackall committed
746 747 748 749 750 751 752 753 754
      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.
755

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

758 759 760
    See :hg:`help revert` for a way to restore files to the state
    of another revision.

761 762
    Returns 0 on success, 1 if nothing to backout or there are unresolved
    files.
763
    '''
764
    with repo.wlock(), repo.lock():
765
        return _dobackout(ui, repo, node, rev, **opts)
766

767

768
def _dobackout(ui, repo, node=None, rev=None, **opts):
769
    opts = pycompat.byteskwargs(opts)
770 771 772 773
    if opts.get(b'commit') and opts.get(b'no_commit'):
        raise error.Abort(_(b"cannot use --commit with --no-commit"))
    if opts.get(b'merge') and opts.get(b'no_commit'):
        raise error.Abort(_(b"cannot use --merge with --no-commit"))
774

Daniel Holth's avatar
Daniel Holth committed