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
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,
48
    match as matchmod,
Gregory Szorc's avatar
Gregory Szorc committed
49
    merge as mergemod,
50
    mergestate as mergestatemod,
51
    narrowspec,
Gregory Szorc's avatar
Gregory Szorc committed
52
    obsolete,
53
    obsutil,
Gregory Szorc's avatar
Gregory Szorc committed
54 55
    patch,
    phases,
56
    pycompat,
57
    rcutil,
58
    registrar,
59
    requirements,
60
    revsetlang,
61
    rewriteutil,
Gregory Szorc's avatar
Gregory Szorc committed
62
    scmutil,
63
    server,
64
    shelve as shelvemod,
65
    state as statemod,
Gregory Szorc's avatar
Gregory Szorc committed
66
    streamclone,
67
    tags as tagsmod,
Gregory Szorc's avatar
Gregory Szorc committed
68 69
    ui as uimod,
    util,
70
    verify as verifymod,
71
    vfs as vfsmod,
72
    wireprotoserver,
Gregory Szorc's avatar
Gregory Szorc committed
73
)
74 75 76 77
from .utils import (
    dateutil,
    stringutil,
)
Gregory Szorc's avatar
Gregory Szorc committed
78

79
table = {}
80
table.update(debugcommandsmod.command._table)
81

82
command = registrar.command(table)
83
INTENT_READONLY = registrar.INTENT_READONLY
84 85 86 87

# common command options

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

153 154 155 156 157
dryrunopts = cmdutil.dryrunopts
remoteopts = cmdutil.remoteopts
walkopts = cmdutil.walkopts
commitopts = cmdutil.commitopts
commitopts2 = cmdutil.commitopts2
158
commitopts3 = cmdutil.commitopts3
159 160 161 162 163 164 165 166 167 168
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
169

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

172 173

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

206 207

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

218 219
    Schedule files to be version controlled and added to the
    repository.
220

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

timeless's avatar
timeless committed
224 225
    If no names are given, add all files to the repository (except
    files matching ``.hgignore``).
226 227 228

    .. container:: verbose

229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
       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
254 255

    Returns 0 if all files are successfully added.
256 257
    """

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

263 264

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

274 275
    Add all new files and remove all missing files from the
    repository.
276

timeless's avatar
timeless committed
277 278 279
    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.
280

281
    Use the -s/--similarity option to detect renamed files. This
282
    option takes a percentage between 0 (disabled) and 100 (files must
283 284 285
    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
286
    can be expensive. After using this option, :hg:`status -C` can be
287 288 289
    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.
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 327 328 329 330
    .. 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

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

341 342

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

384
    List changes in files, showing the revision id responsible for
385
    each line.
386

387 388
    This command is useful for discovering when a change was made and
    by whom.
389

390 391 392
    If you include --file, --user, or --date, the revision number is
    suppressed unless you also include --number.

393 394 395 396
    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.
397

398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416
    .. 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.

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

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

428
    if (
429 430 431 432
        not opts.get(b'user')
        and not opts.get(b'changeset')
        and not opts.get(b'date')
        and not opts.get(b'file')
433
    ):
434
        opts[b'number'] = True
435

436 437 438 439 440 441 442
    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'))
443

444
    rev = opts.get(b'rev')
445
    if rev:
446
        repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
447
    ctx = scmutil.revsingle(repo, rev)
448

449 450
    ui.pager(b'annotate')
    rootfm = ui.formatter(b'annotate', opts)
451 452 453
    if ui.debugflag:
        shorthex = pycompat.identity
    else:
454

455 456
        def shorthex(h):
            return h[:12]
457

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

471
        else:
472

473
            def formatrev(rev):
474
                if rev == wdirrev:
475
                    return b'%d+' % ctx.p1().rev()
476
                else:
477
                    return b'%d ' % rev
478

479
        def formathex(h):
480
            if h == wdirhex:
481
                return b'%s+' % shorthex(hex(ctx.p1().node()))
482
            else:
483
                return b'%s ' % shorthex(h)
484

485
    else:
486 487
        formatrev = b'%d'.__mod__
        formathex = shorthex
488

489
    opmap = [
490 491 492 493 494 495
        (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),
496 497
    ]
    opnamemap = {
498 499 500 501
        b'rev': b'number',
        b'node': b'changeset',
        b'path': b'file',
        b'lineno': b'line_number',
502
    }
503

504
    if rootfm.isplain():
505

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

509
    else:
510

511
        def makefunc(get, fmt):
512
            return get
513

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

527
    def bad(x, y):
528
        raise error.Abort(b"%s: %s" % (x, y))
529

530 531
    m = scmutil.match(ctx, pats, opts, badfn=bad)

532
    follow = not opts.get(b'no_follow')
533
    diffopts = patch.difffeatureopts(
534
        ui, opts, section=b'annotate', whitespace=True
535
    )
536
    skiprevs = opts.get(b'skip')
537 538 539
    if skiprevs:
        skiprevs = scmutil.revrange(repo, skiprevs)

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

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

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

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

579 580
        if not lines[-1].text.endswith(b'\n'):
            fm.plain(b'\n')
581
        fm.end()
582

583
    rootfm.end()
584

585 586

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

608 609
    By default, the revision used is the parent of the working
    directory; use -r/--rev to specify a different revision.
610

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

Matt Mackall's avatar
Matt Mackall committed
614 615 616 617 618 619 620 621 622 623 624 625
    .. 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*"

626
    Valid types are:
627

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

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

639 640 641 642
    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.
643 644

    Returns 0 on success.
645 646
    '''

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

659 660
    kind = opts.get(b'type') or archival.guesskind(dest) or b'files'
    prefix = opts.get(b'prefix')
661

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

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


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

719
    Prepare a new changeset with the effect of REV undone in the
720 721
    current working directory. If no conflicts were encountered,
    it will be committed immediately.
722

723
    If REV is the parent of the working directory, then this new changeset
724
    is committed automatically (unless --no-commit is specified).
725

726
    .. note::
727

728
       :hg:`backout` cannot be used to fix either an unwanted or
729
       incorrect merge.
730

Matt Mackall's avatar
Matt Mackall committed
731 732
    .. container:: verbose

733 734 735 736 737 738 739 740 741 742 743 744
      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
745
        leave changes uncommitted::
746

747 748
          hg backout -r 23 --no-commit
          hg commit -m "Backout revision 23"
749

Matt Mackall's avatar
Matt Mackall committed
750 751 752 753 754 755 756 757 758
      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.
759

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

762 763 764
    See :hg:`help revert` for a way to restore files to the state
    of another revision.

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

771

772
def _dobackout(ui, repo, node=None, rev=None, **opts):
Pulkit Goyal's avatar