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

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

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

167 168

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

201 202

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

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

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

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

    .. container:: verbose

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

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

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

258 259

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

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

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

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

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

336 337

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

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

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

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

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

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

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

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

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

431 432 433 434 435 436 437
    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'))
438

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

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

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

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

466
        else:
467

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

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

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

484
    opmap = [
485 486 487 488 489 490
        (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),
491 492
    ]
    opnamemap = {
493 494 495 496
        b'rev': b'number',
        b'node': b'changeset',
        b'path': b'file',
        b'lineno': b'line_number',
497
    }
498

499
    if rootfm.isplain():
500

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

504
    else:
505

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

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

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

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

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

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

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

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

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

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

578
    rootfm.end()
579

580 581

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

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

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

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

621
    Valid types are:
622

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

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

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

    Returns 0 on success.
640 641
    '''

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

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

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

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


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

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

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

721
    .. note::
722

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

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

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

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

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

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

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

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

766

767
def _dobackout(ui, repo, node=None, rev=None, **opts):
768
    opts = pycompat.byteskwargs(opts)
769 770 771 772
    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"))
773

Daniel Holth's avatar
Daniel Holth committed
774
    if rev and node:
775
        raise error.Abort(_(b"please specify just one revision"))
Daniel Holth's avatar
Daniel Holth committed
776 777 778

    if not rev:
        rev = node