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

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

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

# common command options

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

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

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

169 170

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

203 204

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

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

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

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

    .. container:: verbose

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

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

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

260 261

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

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

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

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

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

338 339

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

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

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

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

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

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

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

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

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

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

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

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

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

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

468
        else:
469

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

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

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

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

501
    if rootfm.isplain():
502

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

506
    else:
507

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

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

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

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

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

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

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

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

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

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

580
    rootfm.end()
581

582 583

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

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

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

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

623
    Valid types are:
624

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

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

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

    Returns 0 on success.
642 643
    '''

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

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

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

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


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

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

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

723
    .. note::
724

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

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

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

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

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

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

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

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

768

769
def _dobackout(ui, repo, node=None, rev=None, **opts):
770
    opts = pycompat.byteskwargs(opts)
771 772 773 774 </