1# commands.py - command processing for mercurial
2#
3# Copyright 2005-2007 Olivia Mackall <olivia@selenic.com>
4#
5# This software may be used and distributed according to the terms of the
6# GNU General Public License version 2 or any later version.
7
8from __future__ import absolute_import
9
10import errno
11import os
12import re
13import sys
14
15from .i18n import _
16from .node import (
17    hex,
18    nullrev,
19    short,
20    wdirrev,
21)
22from .pycompat import open
23from . import (
24    archival,
25    bookmarks,
26    bundle2,
27    bundlecaches,
28    changegroup,
29    cmdutil,
30    copies,
31    debugcommands as debugcommandsmod,
32    destutil,
33    dirstateguard,
34    discovery,
35    encoding,
36    error,
37    exchange,
38    extensions,
39    filemerge,
40    formatter,
41    graphmod,
42    grep as grepmod,
43    hbisect,
44    help,
45    hg,
46    logcmdutil,
47    merge as mergemod,
48    mergestate as mergestatemod,
49    narrowspec,
50    obsolete,
51    obsutil,
52    patch,
53    phases,
54    pycompat,
55    rcutil,
56    registrar,
57    requirements,
58    revsetlang,
59    rewriteutil,
60    scmutil,
61    server,
62    shelve as shelvemod,
63    state as statemod,
64    streamclone,
65    tags as tagsmod,
66    ui as uimod,
67    util,
68    verify as verifymod,
69    vfs as vfsmod,
70    wireprotoserver,
71)
72from .utils import (
73    dateutil,
74    stringutil,
75    urlutil,
76)
77
78table = {}
79table.update(debugcommandsmod.command._table)
80
81command = registrar.command(table)
82INTENT_READONLY = registrar.INTENT_READONLY
83
84# common command options
85
86globalopts = [
87    (
88        b'R',
89        b'repository',
90        b'',
91        _(b'repository root directory or name of overlay bundle file'),
92        _(b'REPO'),
93    ),
94    (b'', b'cwd', b'', _(b'change working directory'), _(b'DIR')),
95    (
96        b'y',
97        b'noninteractive',
98        None,
99        _(
100            b'do not prompt, automatically pick the first choice for all prompts'
101        ),
102    ),
103    (b'q', b'quiet', None, _(b'suppress output')),
104    (b'v', b'verbose', None, _(b'enable additional output')),
105    (
106        b'',
107        b'color',
108        b'',
109        # i18n: 'always', 'auto', 'never', and 'debug' are keywords
110        # and should not be translated
111        _(b"when to colorize (boolean, always, auto, never, or debug)"),
112        _(b'TYPE'),
113    ),
114    (
115        b'',
116        b'config',
117        [],
118        _(b'set/override config option (use \'section.name=value\')'),
119        _(b'CONFIG'),
120    ),
121    (b'', b'debug', None, _(b'enable debugging output')),
122    (b'', b'debugger', None, _(b'start debugger')),
123    (
124        b'',
125        b'encoding',
126        encoding.encoding,
127        _(b'set the charset encoding'),
128        _(b'ENCODE'),
129    ),
130    (
131        b'',
132        b'encodingmode',
133        encoding.encodingmode,
134        _(b'set the charset encoding mode'),
135        _(b'MODE'),
136    ),
137    (b'', b'traceback', None, _(b'always print a traceback on exception')),
138    (b'', b'time', None, _(b'time how long the command takes')),
139    (b'', b'profile', None, _(b'print command execution profile')),
140    (b'', b'version', None, _(b'output version information and exit')),
141    (b'h', b'help', None, _(b'display help and exit')),
142    (b'', b'hidden', False, _(b'consider hidden changesets')),
143    (
144        b'',
145        b'pager',
146        b'auto',
147        _(b"when to paginate (boolean, always, auto, or never)"),
148        _(b'TYPE'),
149    ),
150]
151
152dryrunopts = cmdutil.dryrunopts
153remoteopts = cmdutil.remoteopts
154walkopts = cmdutil.walkopts
155commitopts = cmdutil.commitopts
156commitopts2 = cmdutil.commitopts2
157commitopts3 = cmdutil.commitopts3
158formatteropts = cmdutil.formatteropts
159templateopts = cmdutil.templateopts
160logopts = cmdutil.logopts
161diffopts = cmdutil.diffopts
162diffwsopts = cmdutil.diffwsopts
163diffopts2 = cmdutil.diffopts2
164mergetoolopts = cmdutil.mergetoolopts
165similarityopts = cmdutil.similarityopts
166subrepoopts = cmdutil.subrepoopts
167debugrevlogopts = cmdutil.debugrevlogopts
168
169# Commands start here, listed alphabetically
170
171
172@command(
173    b'abort',
174    dryrunopts,
175    helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
176    helpbasic=True,
177)
178def abort(ui, repo, **opts):
179    """abort an unfinished operation (EXPERIMENTAL)
180
181    Aborts a multistep operation like graft, histedit, rebase, merge,
182    and unshelve if they are in an unfinished state.
183
184    use --dry-run/-n to dry run the command.
185    """
186    dryrun = opts.get('dry_run')
187    abortstate = cmdutil.getunfinishedstate(repo)
188    if not abortstate:
189        raise error.StateError(_(b'no operation in progress'))
190    if not abortstate.abortfunc:
191        raise error.InputError(
192            (
193                _(b"%s in progress but does not support 'hg abort'")
194                % (abortstate._opname)
195            ),
196            hint=abortstate.hint(),
197        )
198    if dryrun:
199        ui.status(
200            _(b'%s in progress, will be aborted\n') % (abortstate._opname)
201        )
202        return
203    return abortstate.abortfunc(ui, repo)
204
205
206@command(
207    b'add',
208    walkopts + subrepoopts + dryrunopts,
209    _(b'[OPTION]... [FILE]...'),
210    helpcategory=command.CATEGORY_WORKING_DIRECTORY,
211    helpbasic=True,
212    inferrepo=True,
213)
214def add(ui, repo, *pats, **opts):
215    """add the specified files on the next commit
216
217    Schedule files to be version controlled and added to the
218    repository.
219
220    The files will be added to the repository at the next commit. To
221    undo an add before that, see :hg:`forget`.
222
223    If no names are given, add all files to the repository (except
224    files matching ``.hgignore``).
225
226    .. container:: verbose
227
228       Examples:
229
230         - New (unknown) files are added
231           automatically by :hg:`add`::
232
233             $ ls
234             foo.c
235             $ hg status
236             ? foo.c
237             $ hg add
238             adding foo.c
239             $ hg status
240             A foo.c
241
242         - Specific files to be added can be specified::
243
244             $ ls
245             bar.c  foo.c
246             $ hg status
247             ? bar.c
248             ? foo.c
249             $ hg add bar.c
250             $ hg status
251             A bar.c
252             ? foo.c
253
254    Returns 0 if all files are successfully added.
255    """
256
257    m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
258    uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
259    rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
260    return rejected and 1 or 0
261
262
263@command(
264    b'addremove',
265    similarityopts + subrepoopts + walkopts + dryrunopts,
266    _(b'[OPTION]... [FILE]...'),
267    helpcategory=command.CATEGORY_WORKING_DIRECTORY,
268    inferrepo=True,
269)
270def addremove(ui, repo, *pats, **opts):
271    """add all new files, delete all missing files
272
273    Add all new files and remove all missing files from the
274    repository.
275
276    Unless names are given, new files are ignored if they match any of
277    the patterns in ``.hgignore``. As with add, these changes take
278    effect at the next commit.
279
280    Use the -s/--similarity option to detect renamed files. This
281    option takes a percentage between 0 (disabled) and 100 (files must
282    be identical) as its parameter. With a parameter greater than 0,
283    this compares every removed file with every added file and records
284    those similar enough as renames. Detecting renamed files this way
285    can be expensive. After using this option, :hg:`status -C` can be
286    used to check which files were identified as moved or renamed. If
287    not specified, -s/--similarity defaults to 100 and only renames of
288    identical files are detected.
289
290    .. container:: verbose
291
292       Examples:
293
294         - A number of files (bar.c and foo.c) are new,
295           while foobar.c has been removed (without using :hg:`remove`)
296           from the repository::
297
298             $ ls
299             bar.c foo.c
300             $ hg status
301             ! foobar.c
302             ? bar.c
303             ? foo.c
304             $ hg addremove
305             adding bar.c
306             adding foo.c
307             removing foobar.c
308             $ hg status
309             A bar.c
310             A foo.c
311             R foobar.c
312
313         - A file foobar.c was moved to foo.c without using :hg:`rename`.
314           Afterwards, it was edited slightly::
315
316             $ ls
317             foo.c
318             $ hg status
319             ! foobar.c
320             ? foo.c
321             $ hg addremove --similarity 90
322             removing foobar.c
323             adding foo.c
324             recording removal of foobar.c as rename to foo.c (94% similar)
325             $ hg status -C
326             A foo.c
327               foobar.c
328             R foobar.c
329
330    Returns 0 if all files are successfully added.
331    """
332    opts = pycompat.byteskwargs(opts)
333    if not opts.get(b'similarity'):
334        opts[b'similarity'] = b'100'
335    matcher = scmutil.match(repo[None], pats, opts)
336    relative = scmutil.anypats(pats, opts)
337    uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
338    return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
339
340
341@command(
342    b'annotate|blame',
343    [
344        (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
345        (
346            b'',
347            b'follow',
348            None,
349            _(b'follow copies/renames and list the filename (DEPRECATED)'),
350        ),
351        (b'', b'no-follow', None, _(b"don't follow copies and renames")),
352        (b'a', b'text', None, _(b'treat all files as text')),
353        (b'u', b'user', None, _(b'list the author (long with -v)')),
354        (b'f', b'file', None, _(b'list the filename')),
355        (b'd', b'date', None, _(b'list the date (short with -q)')),
356        (b'n', b'number', None, _(b'list the revision number (default)')),
357        (b'c', b'changeset', None, _(b'list the changeset')),
358        (
359            b'l',
360            b'line-number',
361            None,
362            _(b'show line number at the first appearance'),
363        ),
364        (
365            b'',
366            b'skip',
367            [],
368            _(b'revset to not display (EXPERIMENTAL)'),
369            _(b'REV'),
370        ),
371    ]
372    + diffwsopts
373    + walkopts
374    + formatteropts,
375    _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
376    helpcategory=command.CATEGORY_FILE_CONTENTS,
377    helpbasic=True,
378    inferrepo=True,
379)
380def annotate(ui, repo, *pats, **opts):
381    """show changeset information by line for each file
382
383    List changes in files, showing the revision id responsible for
384    each line.
385
386    This command is useful for discovering when a change was made and
387    by whom.
388
389    If you include --file, --user, or --date, the revision number is
390    suppressed unless you also include --number.
391
392    Without the -a/--text option, annotate will avoid processing files
393    it detects as binary. With -a, annotate will annotate the file
394    anyway, although the results will probably be neither useful
395    nor desirable.
396
397    .. container:: verbose
398
399      Template:
400
401      The following keywords are supported in addition to the common template
402      keywords and functions. See also :hg:`help templates`.
403
404      :lines:   List of lines with annotation data.
405      :path:    String. Repository-absolute path of the specified file.
406
407      And each entry of ``{lines}`` provides the following sub-keywords in
408      addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
409
410      :line:    String. Line content.
411      :lineno:  Integer. Line number at that revision.
412      :path:    String. Repository-absolute path of the file at that revision.
413
414      See :hg:`help templates.operators` for the list expansion syntax.
415
416    Returns 0 on success.
417    """
418    opts = pycompat.byteskwargs(opts)
419    if not pats:
420        raise error.InputError(
421            _(b'at least one filename or pattern is required')
422        )
423
424    if opts.get(b'follow'):
425        # --follow is deprecated and now just an alias for -f/--file
426        # to mimic the behavior of Mercurial before version 1.5
427        opts[b'file'] = True
428
429    if (
430        not opts.get(b'user')
431        and not opts.get(b'changeset')
432        and not opts.get(b'date')
433        and not opts.get(b'file')
434    ):
435        opts[b'number'] = True
436
437    linenumber = opts.get(b'line_number') is not None
438    if (
439        linenumber
440        and (not opts.get(b'changeset'))
441        and (not opts.get(b'number'))
442    ):
443        raise error.InputError(_(b'at least one of -n/-c is required for -l'))
444
445    rev = opts.get(b'rev')
446    if rev:
447        repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
448    ctx = logcmdutil.revsingle(repo, rev)
449
450    ui.pager(b'annotate')
451    rootfm = ui.formatter(b'annotate', opts)
452    if ui.debugflag:
453        shorthex = pycompat.identity
454    else:
455
456        def shorthex(h):
457            return h[:12]
458
459    if ui.quiet:
460        datefunc = dateutil.shortdate
461    else:
462        datefunc = dateutil.datestr
463    if ctx.rev() is None:
464        if opts.get(b'changeset'):
465            # omit "+" suffix which is appended to node hex
466            def formatrev(rev):
467                if rev == wdirrev:
468                    return b'%d' % ctx.p1().rev()
469                else:
470                    return b'%d' % rev
471
472        else:
473
474            def formatrev(rev):
475                if rev == wdirrev:
476                    return b'%d+' % ctx.p1().rev()
477                else:
478                    return b'%d ' % rev
479
480        def formathex(h):
481            if h == repo.nodeconstants.wdirhex:
482                return b'%s+' % shorthex(hex(ctx.p1().node()))
483            else:
484                return b'%s ' % shorthex(h)
485
486    else:
487        formatrev = b'%d'.__mod__
488        formathex = shorthex
489
490    opmap = [
491        (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
492        (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
493        (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
494        (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
495        (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
496        (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
497    ]
498    opnamemap = {
499        b'rev': b'number',
500        b'node': b'changeset',
501        b'path': b'file',
502        b'lineno': b'line_number',
503    }
504
505    if rootfm.isplain():
506
507        def makefunc(get, fmt):
508            return lambda x: fmt(get(x))
509
510    else:
511
512        def makefunc(get, fmt):
513            return get
514
515    datahint = rootfm.datahint()
516    funcmap = [
517        (makefunc(get, fmt), sep)
518        for fn, sep, get, fmt in opmap
519        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
522    fields = b' '.join(
523        fn
524        for fn, sep, get, fmt in opmap
525        if opts.get(opnamemap.get(fn, fn)) or fn in datahint
526    )
527
528    def bad(x, y):
529        raise error.InputError(b"%s: %s" % (x, y))
530
531    m = scmutil.match(ctx, pats, opts, badfn=bad)
532
533    follow = not opts.get(b'no_follow')
534    diffopts = patch.difffeatureopts(
535        ui, opts, section=b'annotate', whitespace=True
536    )
537    skiprevs = opts.get(b'skip')
538    if skiprevs:
539        skiprevs = logcmdutil.revrange(repo, skiprevs)
540
541    uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
542    for abs in ctx.walk(m):
543        fctx = ctx[abs]
544        rootfm.startitem()
545        rootfm.data(path=abs)
546        if not opts.get(b'text') and fctx.isbinary():
547            rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
548            continue
549
550        fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
551        lines = fctx.annotate(
552            follow=follow, skiprevs=skiprevs, diffopts=diffopts
553        )
554        if not lines:
555            fm.end()
556            continue
557        formats = []
558        pieces = []
559
560        for f, sep in funcmap:
561            l = [f(n) for n in lines]
562            if fm.isplain():
563                sizes = [encoding.colwidth(x) for x in l]
564                ml = max(sizes)
565                formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
566            else:
567                formats.append([b'%s'] * len(l))
568            pieces.append(l)
569
570        for f, p, n in zip(zip(*formats), zip(*pieces), lines):
571            fm.startitem()
572            fm.context(fctx=n.fctx)
573            fm.write(fields, b"".join(f), *p)
574            if n.skip:
575                fmt = b"* %s"
576            else:
577                fmt = b": %s"
578            fm.write(b'line', fmt, n.text)
579
580        if not lines[-1].text.endswith(b'\n'):
581            fm.plain(b'\n')
582        fm.end()
583
584    rootfm.end()
585
586
587@command(
588    b'archive',
589    [
590        (b'', b'no-decode', None, _(b'do not pass files through decoders')),
591        (
592            b'p',
593            b'prefix',
594            b'',
595            _(b'directory prefix for files in archive'),
596            _(b'PREFIX'),
597        ),
598        (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
599        (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
600    ]
601    + subrepoopts
602    + walkopts,
603    _(b'[OPTION]... DEST'),
604    helpcategory=command.CATEGORY_IMPORT_EXPORT,
605)
606def archive(ui, repo, dest, **opts):
607    """create an unversioned archive of a repository revision
608
609    By default, the revision used is the parent of the working
610    directory; use -r/--rev to specify a different revision.
611
612    The archive type is automatically detected based on file
613    extension (to override, use -t/--type).
614
615    .. container:: verbose
616
617      Examples:
618
619      - create a zip file containing the 1.0 release::
620
621          hg archive -r 1.0 project-1.0.zip
622
623      - create a tarball excluding .hg files::
624
625          hg archive project.tar.gz -X ".hg*"
626
627    Valid types are:
628
629    :``files``: a directory full of files (default)
630    :``tar``:   tar archive, uncompressed
631    :``tbz2``:  tar archive, compressed using bzip2
632    :``tgz``:   tar archive, compressed using gzip
633    :``txz``:   tar archive, compressed using lzma (only in Python 3)
634    :``uzip``:  zip archive, uncompressed
635    :``zip``:   zip archive, compressed using deflate
636
637    The exact name of the destination archive or directory is given
638    using a format string; see :hg:`help export` for details.
639
640    Each member added to an archive file has a directory prefix
641    prepended. Use -p/--prefix to specify a format string for the
642    prefix. The default is the basename of the archive, with suffixes
643    removed.
644
645    Returns 0 on success.
646    """
647
648    opts = pycompat.byteskwargs(opts)
649    rev = opts.get(b'rev')
650    if rev:
651        repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
652    ctx = logcmdutil.revsingle(repo, rev)
653    if not ctx:
654        raise error.InputError(
655            _(b'no working directory: please specify a revision')
656        )
657    node = ctx.node()
658    dest = cmdutil.makefilename(ctx, dest)
659    if os.path.realpath(dest) == repo.root:
660        raise error.InputError(_(b'repository root cannot be destination'))
661
662    kind = opts.get(b'type') or archival.guesskind(dest) or b'files'
663    prefix = opts.get(b'prefix')
664
665    if dest == b'-':
666        if kind == b'files':
667            raise error.InputError(_(b'cannot archive plain files to stdout'))
668        dest = cmdutil.makefileobj(ctx, dest)
669        if not prefix:
670            prefix = os.path.basename(repo.root) + b'-%h'
671
672    prefix = cmdutil.makefilename(ctx, prefix)
673    match = scmutil.match(ctx, [], opts)
674    archival.archive(
675        repo,
676        dest,
677        node,
678        kind,
679        not opts.get(b'no_decode'),
680        match,
681        prefix,
682        subrepos=opts.get(b'subrepos'),
683    )
684
685
686@command(
687    b'backout',
688    [
689        (
690            b'',
691            b'merge',
692            None,
693            _(b'merge with old dirstate parent after backout'),
694        ),
695        (
696            b'',
697            b'commit',
698            None,
699            _(b'commit if no conflicts were encountered (DEPRECATED)'),
700        ),
701        (b'', b'no-commit', None, _(b'do not commit')),
702        (
703            b'',
704            b'parent',
705            b'',
706            _(b'parent to choose when backing out merge (DEPRECATED)'),
707            _(b'REV'),
708        ),
709        (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
710        (b'e', b'edit', False, _(b'invoke editor on commit messages')),
711    ]
712    + mergetoolopts
713    + walkopts
714    + commitopts
715    + commitopts2,
716    _(b'[OPTION]... [-r] REV'),
717    helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
718)
719def backout(ui, repo, node=None, rev=None, **opts):
720    """reverse effect of earlier changeset
721
722    Prepare a new changeset with the effect of REV undone in the
723    current working directory. If no conflicts were encountered,
724    it will be committed immediately.
725
726    If REV is the parent of the working directory, then this new changeset
727    is committed automatically (unless --no-commit is specified).
728
729    .. note::
730
731       :hg:`backout` cannot be used to fix either an unwanted or
732       incorrect merge.
733
734    .. container:: verbose
735
736      Examples:
737
738      - Reverse the effect of the parent of the working directory.
739        This backout will be committed immediately::
740
741          hg backout -r .
742
743      - Reverse the effect of previous bad revision 23::
744
745          hg backout -r 23
746
747      - Reverse the effect of previous bad revision 23 and
748        leave changes uncommitted::
749
750          hg backout -r 23 --no-commit
751          hg commit -m "Backout revision 23"
752
753      By default, the pending changeset will have one parent,
754      maintaining a linear history. With --merge, the pending
755      changeset will instead have two parents: the old parent of the
756      working directory and a new child of REV that simply undoes REV.
757
758      Before version 1.7, the behavior without --merge was equivalent
759      to specifying --merge followed by :hg:`update --clean .` to
760      cancel the merge and leave the child of REV as a head to be
761      merged separately.
762
763    See :hg:`help dates` for a list of formats valid for -d/--date.
764
765    See :hg:`help revert` for a way to restore files to the state
766    of another revision.
767
768    Returns 0 on success, 1 if nothing to backout or there are unresolved
769    files.
770    """
771    with repo.wlock(), repo.lock():
772        return _dobackout(ui, repo, node, rev, **opts)
773
774
775def _dobackout(ui, repo, node=None, rev=None, **opts):
776    cmdutil.check_incompatible_arguments(opts, 'no_commit', ['commit', 'merge'])
777    opts = pycompat.byteskwargs(opts)
778
779    if rev and node:
780        raise error.InputError(_(b"please specify just one revision"))
781
782    if not rev:
783        rev = node
784
785    if not rev:
786        raise error.InputError(_(b"please specify a revision to backout"))
787
788    date = opts.get(b'date')
789    if date:
790        opts[b'date'] = dateutil.parsedate(date)
791
792    cmdutil.checkunfinished(repo)
793    cmdutil.bailifchanged(repo)
794    ctx = logcmdutil.revsingle(repo, rev)
795    node = ctx.node()
796
797    op1, op2 = repo.dirstate.parents()
798    if not repo.changelog.isancestor(node, op1):
799        raise error.InputError(
800            _(b'cannot backout change that is not an ancestor')
801        )
802
803    p1, p2 = repo.changelog.parents(node)
804    if p1 == repo.nullid:
805        raise error.InputError(_(b'cannot backout a change with no parents'))
806    if p2 != repo.nullid:
807        if not opts.get(b'parent'):
808            raise error.InputError(_(b'cannot backout a merge changeset'))
809        p = repo.lookup(opts[b'parent'])
810        if p not in (p1, p2):
811            raise error.InputError(
812                _(b'%s is not a parent of %s') % (short(p), short(node))
813            )
814        parent = p
815    else:
816        if opts.get(b'parent'):
817            raise error.InputError(
818                _(b'cannot use --parent on non-merge changeset')
819            )
820        parent = p1
821
822    # the backout should appear on the same branch
823    branch = repo.dirstate.branch()
824    bheads = repo.branchheads(branch)
825    rctx = scmutil.revsingle(repo, hex(parent))
826    if not opts.get(b'merge') and op1 != node:
827        with dirstateguard.dirstateguard(repo, b'backout'):
828            overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
829            with ui.configoverride(overrides, b'backout'):
830                stats = mergemod.back_out(ctx, parent=repo[parent])
831            repo.setparents(op1, op2)
832        hg._showstats(repo, stats)
833        if stats.unresolvedcount:
834            repo.ui.status(
835                _(b"use 'hg resolve' to retry unresolved file merges\n")
836            )
837            return 1
838    else:
839        hg.clean(repo, node, show_stats=False)
840        repo.dirstate.setbranch(branch)
841        cmdutil.revert(ui, repo, rctx)
842
843    if opts.get(b'no_commit'):
844        msg = _(b"changeset %s backed out, don't forget to commit.\n")
845        ui.status(msg % short(node))
846        return 0
847
848    def commitfunc(ui, repo, message, match, opts):
849        editform = b'backout'
850        e = cmdutil.getcommiteditor(
851            editform=editform, **pycompat.strkwargs(opts)
852        )
853        if not message:
854            # we don't translate commit messages
855            message = b"Backed out changeset %s" % short(node)
856            e = cmdutil.getcommiteditor(edit=True, editform=editform)
857        return repo.commit(
858            message, opts.get(b'user'), opts.get(b'date'), match, editor=e
859        )
860
861    # save to detect changes
862    tip = repo.changelog.tip()
863
864    newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
865    if not newnode:
866        ui.status(_(b"nothing changed\n"))
867        return 1
868    cmdutil.commitstatus(repo, newnode, branch, bheads, tip)
869
870    def nice(node):
871        return b'%d:%s' % (repo.changelog.rev(node), short(node))
872
873    ui.status(
874        _(b'changeset %s backs out changeset %s\n')
875        % (nice(newnode), nice(node))
876    )
877    if opts.get(b'merge') and op1 != node:
878        hg.clean(repo, op1, show_stats=False)
879        ui.status(_(b'merging with changeset %s\n') % nice(newnode))
880        overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
881        with ui.configoverride(overrides, b'backout'):
882            return hg.merge(repo[b'tip'])
883    return 0
884
885
886@command(
887    b'bisect',
888    [
889        (b'r', b'reset', False, _(b'reset bisect state')),
890        (b'g', b'good', False, _(b'mark changeset good')),
891        (b'b', b'bad', False, _(b'mark changeset bad')),
892        (b's', b'skip', False, _(b'skip testing changeset')),
893        (b'e', b'extend', False, _(b'extend the bisect range')),
894        (
895            b'c',
896            b'command',
897            b'',
898            _(b'use command to check changeset state'),
899            _(b'CMD'),
900        ),
901        (b'U', b'noupdate', False, _(b'do not update to target')),
902    ],
903    _(b"[-gbsr] [-U] [-c CMD] [REV]"),
904    helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
905)
906def bisect(
907    ui,
908    repo,
909    positional_1=None,
910    positional_2=None,
911    command=None,
912    reset=None,
913    good=None,
914    bad=None,
915    skip=None,
916    extend=None,
917    noupdate=None,
918):
919    """subdivision search of changesets
920
921    This command helps to find changesets which introduce problems. To
922    use, mark the earliest changeset you know exhibits the problem as
923    bad, then mark the latest changeset which is free from the problem
924    as good. Bisect will update your working directory to a revision
925    for testing (unless the -U/--noupdate option is specified). Once
926    you have performed tests, mark the working directory as good or
927    bad, and bisect will either update to another candidate changeset
928    or announce that it has found the bad revision.
929
930    As a shortcut, you can also use the revision argument to mark a
931    revision as good or bad without checking it out first.
932
933    If you supply a command, it will be used for automatic bisection.
934    The environment variable HG_NODE will contain the ID of the
935    changeset being tested. The exit status of the command will be
936    used to mark revisions as good or bad: status 0 means good, 125
937    means to skip the revision, 127 (command not found) will abort the
938    bisection, and any other non-zero exit status means the revision
939    is bad.
940
941    .. container:: verbose
942
943      Some examples:
944
945      - start a bisection with known bad revision 34, and good revision 12::
946
947          hg bisect --bad 34
948          hg bisect --good 12
949
950      - advance the current bisection by marking current revision as good or
951        bad::
952
953          hg bisect --good
954          hg bisect --bad
955
956      - mark the current revision, or a known revision, to be skipped (e.g. if
957        that revision is not usable because of another issue)::
958
959          hg bisect --skip
960          hg bisect --skip 23
961
962      - skip all revisions that do not touch directories ``foo`` or ``bar``::
963
964          hg bisect --skip "!( file('path:foo') & file('path:bar') )"
965
966      - forget the current bisection::
967
968          hg bisect --reset
969
970      - use 'make && make tests' to automatically find the first broken
971        revision::
972
973          hg bisect --reset
974          hg bisect --bad 34
975          hg bisect --good 12
976          hg bisect --command "make && make tests"
977
978      - see all changesets whose states are already known in the current
979        bisection::
980
981          hg log -r "bisect(pruned)"
982
983      - see the changeset currently being bisected (especially useful
984        if running with -U/--noupdate)::
985
986          hg log -r "bisect(current)"
987
988      - see all changesets that took part in the current bisection::
989
990          hg log -r "bisect(range)"
991
992      - you can even get a nice graph::
993
994          hg log --graph -r "bisect(range)"
995
996      See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
997
998    Returns 0 on success.
999    """
1000    rev = []
1001    # backward compatibility
1002    if positional_1 in (b"good", b"bad", b"reset", b"init"):
1003        ui.warn(_(b"(use of 'hg bisect <cmd>' is deprecated)\n"))
1004        cmd = positional_1
1005        rev.append(positional_2)
1006        if cmd == b"good":
1007            good = True
1008        elif cmd == b"bad":
1009            bad = True
1010        else:
1011            reset = True
1012    elif positional_2:
1013        raise error.InputError(_(b'incompatible arguments'))
1014    elif positional_1 is not None:
1015        rev.append(positional_1)
1016
1017    incompatibles = {
1018        b'--bad': bad,
1019        b'--command': bool(command),
1020        b'--extend': extend,
1021        b'--good': good,
1022        b'--reset': reset,
1023        b'--skip': skip,
1024    }
1025
1026    enabled = [x for x in incompatibles if incompatibles[x]]
1027
1028    if len(enabled) > 1:
1029        raise error.InputError(
1030            _(b'%s and %s are incompatible') % tuple(sorted(enabled)[0:2])
1031        )
1032
1033    if reset:
1034        hbisect.resetstate(repo)
1035        return
1036
1037    state = hbisect.load_state(repo)
1038
1039    if rev:
1040        nodes = [repo[i].node() for i in logcmdutil.revrange(repo, rev)]
1041    else:
1042        nodes = [repo.lookup(b'.')]
1043
1044    # update state
1045    if good or bad or skip:
1046        if good:
1047            state[b'good'] += nodes
1048        elif bad:
1049            state[b'bad'] += nodes
1050        elif skip:
1051            state[b'skip'] += nodes
1052        hbisect.save_state(repo, state)
1053        if not (state[b'good'] and state[b'bad']):
1054            return
1055
1056    def mayupdate(repo, node, show_stats=True):
1057        """common used update sequence"""
1058        if noupdate:
1059            return
1060        cmdutil.checkunfinished(repo)
1061        cmdutil.bailifchanged(repo)
1062        return hg.clean(repo, node, show_stats=show_stats)
1063
1064    displayer = logcmdutil.changesetdisplayer(ui, repo, {})
1065
1066    if command:
1067        changesets = 1
1068        if noupdate:
1069            try:
1070                node = state[b'current'][0]
1071            except LookupError:
1072                raise error.StateError(
1073                    _(
1074                        b'current bisect revision is unknown - '
1075                        b'start a new bisect to fix'
1076                    )
1077                )
1078        else:
1079            node, p2 = repo.dirstate.parents()
1080            if p2 != repo.nullid:
1081                raise error.StateError(_(b'current bisect revision is a merge'))
1082        if rev:
1083            if not nodes:
1084                raise error.InputError(_(b'empty revision set'))
1085            node = repo[nodes[-1]].node()
1086        with hbisect.restore_state(repo, state, node):
1087            while changesets:
1088                # update state
1089                state[b'current'] = [node]
1090                hbisect.save_state(repo, state)
1091                status = ui.system(
1092                    command,
1093                    environ={b'HG_NODE': hex(node)},
1094                    blockedtag=b'bisect_check',
1095                )
1096                if status == 125:
1097                    transition = b"skip"
1098                elif status == 0:
1099                    transition = b"good"
1100                # status < 0 means process was killed
1101                elif status == 127:
1102                    raise error.Abort(_(b"failed to execute %s") % command)
1103                elif status < 0:
1104                    raise error.Abort(_(b"%s killed") % command)
1105                else:
1106                    transition = b"bad"
1107                state[transition].append(node)
1108                ctx = repo[node]
1109                summary = cmdutil.format_changeset_summary(ui, ctx, b'bisect')
1110                ui.status(_(b'changeset %s: %s\n') % (summary, transition))
1111                hbisect.checkstate(state)
1112                # bisect
1113                nodes, changesets, bgood = hbisect.bisect(repo, state)
1114                # update to next check
1115                node = nodes[0]
1116                mayupdate(repo, node, show_stats=False)
1117        hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
1118        return
1119
1120    hbisect.checkstate(state)
1121
1122    # actually bisect
1123    nodes, changesets, good = hbisect.bisect(repo, state)
1124    if extend:
1125        if not changesets:
1126            extendctx = hbisect.extendrange(repo, state, nodes, good)
1127            if extendctx is not None:
1128                ui.write(
1129                    _(b"Extending search to changeset %s\n")
1130                    % cmdutil.format_changeset_summary(ui, extendctx, b'bisect')
1131                )
1132                state[b'current'] = [extendctx.node()]
1133                hbisect.save_state(repo, state)
1134                return mayupdate(repo, extendctx.node())
1135        raise error.StateError(_(b"nothing to extend"))
1136
1137    if changesets == 0:
1138        hbisect.printresult(ui, repo, state, displayer, nodes, good)
1139    else:
1140        assert len(nodes) == 1  # only a single node can be tested next
1141        node = nodes[0]
1142        # compute the approximate number of remaining tests
1143        tests, size = 0, 2
1144        while size <= changesets:
1145            tests, size = tests + 1, size * 2
1146        rev = repo.changelog.rev(node)
1147        summary = cmdutil.format_changeset_summary(ui, repo[rev], b'bisect')
1148        ui.write(
1149            _(
1150                b"Testing changeset %s "
1151                b"(%d changesets remaining, ~%d tests)\n"
1152            )
1153            % (summary, changesets, tests)
1154        )
1155        state[b'current'] = [node]
1156        hbisect.save_state(repo, state)
1157        return mayupdate(repo, node)
1158
1159
1160@command(
1161    b'bookmarks|bookmark',
1162    [
1163        (b'f', b'force', False, _(b'force')),
1164        (b'r', b'rev', b'', _(b'revision for bookmark action'), _(b'REV')),
1165        (b'd', b'delete', False, _(b'delete a given bookmark')),
1166        (b'm', b'rename', b'', _(b'rename a given bookmark'), _(b'OLD')),
1167        (b'i', b'inactive', False, _(b'mark a bookmark inactive')),
1168        (b'l', b'list', False, _(b'list existing bookmarks')),
1169    ]
1170    + formatteropts,
1171    _(b'hg bookmarks [OPTIONS]... [NAME]...'),
1172    helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1173)
1174def bookmark(ui, repo, *names, **opts):
1175    """create a new bookmark or list existing bookmarks
1176
1177    Bookmarks are labels on changesets to help track lines of development.
1178    Bookmarks are unversioned and can be moved, renamed and deleted.
1179    Deleting or moving a bookmark has no effect on the associated changesets.
1180
1181    Creating or updating to a bookmark causes it to be marked as 'active'.
1182    The active bookmark is indicated with a '*'.
1183    When a commit is made, the active bookmark will advance to the new commit.
1184    A plain :hg:`update` will also advance an active bookmark, if possible.
1185    Updating away from a bookmark will cause it to be deactivated.
1186
1187    Bookmarks can be pushed and pulled between repositories (see
1188    :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1189    diverged, a new 'divergent bookmark' of the form 'name@path' will
1190    be created. Using :hg:`merge` will resolve the divergence.
1191
1192    Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
1193    the active bookmark's name.
1194
1195    A bookmark named '@' has the special property that :hg:`clone` will
1196    check it out by default if it exists.
1197
1198    .. container:: verbose
1199
1200      Template:
1201
1202      The following keywords are supported in addition to the common template
1203      keywords and functions such as ``{bookmark}``. See also
1204      :hg:`help templates`.
1205
1206      :active:  Boolean. True if the bookmark is active.
1207
1208      Examples:
1209
1210      - create an active bookmark for a new line of development::
1211
1212          hg book new-feature
1213
1214      - create an inactive bookmark as a place marker::
1215
1216          hg book -i reviewed
1217
1218      - create an inactive bookmark on another changeset::
1219
1220          hg book -r .^ tested
1221
1222      - rename bookmark turkey to dinner::
1223
1224          hg book -m turkey dinner
1225
1226      - move the '@' bookmark from another branch::
1227
1228          hg book -f @
1229
1230      - print only the active bookmark name::
1231
1232          hg book -ql .
1233    """
1234    opts = pycompat.byteskwargs(opts)
1235    force = opts.get(b'force')
1236    rev = opts.get(b'rev')
1237    inactive = opts.get(b'inactive')  # meaning add/rename to inactive bookmark
1238
1239    action = cmdutil.check_at_most_one_arg(opts, b'delete', b'rename', b'list')
1240    if action:
1241        cmdutil.check_incompatible_arguments(opts, action, [b'rev'])
1242    elif names or rev:
1243        action = b'add'
1244    elif inactive:
1245        action = b'inactive'  # meaning deactivate
1246    else:
1247        action = b'list'
1248
1249    cmdutil.check_incompatible_arguments(
1250        opts, b'inactive', [b'delete', b'list']
1251    )
1252    if not names and action in {b'add', b'delete'}:
1253        raise error.InputError(_(b"bookmark name required"))
1254
1255    if action in {b'add', b'delete', b'rename', b'inactive'}:
1256        with repo.wlock(), repo.lock(), repo.transaction(b'bookmark') as tr:
1257            if action == b'delete':
1258                names = pycompat.maplist(repo._bookmarks.expandname, names)
1259                bookmarks.delete(repo, tr, names)
1260            elif action == b'rename':
1261                if not names:
1262                    raise error.InputError(_(b"new bookmark name required"))
1263                elif len(names) > 1:
1264                    raise error.InputError(
1265                        _(b"only one new bookmark name allowed")
1266                    )
1267                oldname = repo._bookmarks.expandname(opts[b'rename'])
1268                bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1269            elif action == b'add':
1270                bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1271            elif action == b'inactive':
1272                if len(repo._bookmarks) == 0:
1273                    ui.status(_(b"no bookmarks set\n"))
1274                elif not repo._activebookmark:
1275                    ui.status(_(b"no active bookmark\n"))
1276                else:
1277                    bookmarks.deactivate(repo)
1278    elif action == b'list':
1279        names = pycompat.maplist(repo._bookmarks.expandname, names)
1280        with ui.formatter(b'bookmarks', opts) as fm:
1281            bookmarks.printbookmarks(ui, repo, fm, names)
1282    else:
1283        raise error.ProgrammingError(b'invalid action: %s' % action)
1284
1285
1286@command(
1287    b'branch',
1288    [
1289        (
1290            b'f',
1291            b'force',
1292            None,
1293            _(b'set branch name even if it shadows an existing branch'),
1294        ),
1295        (b'C', b'clean', None, _(b'reset branch name to parent branch name')),
1296        (
1297            b'r',
1298            b'rev',
1299            [],
1300            _(b'change branches of the given revs (EXPERIMENTAL)'),
1301        ),
1302    ],
1303    _(b'[-fC] [NAME]'),
1304    helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1305)
1306def branch(ui, repo, label=None, **opts):
1307    """set or show the current branch name
1308
1309    .. note::
1310
1311       Branch names are permanent and global. Use :hg:`bookmark` to create a
1312       light-weight bookmark instead. See :hg:`help glossary` for more
1313       information about named branches and bookmarks.
1314
1315    With no argument, show the current branch name. With one argument,
1316    set the working directory branch name (the branch will not exist
1317    in the repository until the next commit). Standard practice
1318    recommends that primary development take place on the 'default'
1319    branch.
1320
1321    Unless -f/--force is specified, branch will not let you set a
1322    branch name that already exists.
1323
1324    Use -C/--clean to reset the working directory branch to that of
1325    the parent of the working directory, negating a previous branch
1326    change.
1327
1328    Use the command :hg:`update` to switch to an existing branch. Use
1329    :hg:`commit --close-branch` to mark this branch head as closed.
1330    When all heads of a branch are closed, the branch will be
1331    considered closed.
1332
1333    Returns 0 on success.
1334    """
1335    opts = pycompat.byteskwargs(opts)
1336    revs = opts.get(b'rev')
1337    if label:
1338        label = label.strip()
1339
1340    if not opts.get(b'clean') and not label:
1341        if revs:
1342            raise error.InputError(
1343                _(b"no branch name specified for the revisions")
1344            )
1345        ui.write(b"%s\n" % repo.dirstate.branch())
1346        return
1347
1348    with repo.wlock():
1349        if opts.get(b'clean'):
1350            label = repo[b'.'].branch()
1351            repo.dirstate.setbranch(label)
1352            ui.status(_(b'reset working directory to branch %s\n') % label)
1353        elif label:
1354
1355            scmutil.checknewlabel(repo, label, b'branch')
1356            if revs:
1357                return cmdutil.changebranch(ui, repo, revs, label, opts)
1358
1359            if not opts.get(b'force') and label in repo.branchmap():
1360                if label not in [p.branch() for p in repo[None].parents()]:
1361                    raise error.InputError(
1362                        _(b'a branch of the same name already exists'),
1363                        # i18n: "it" refers to an existing branch
1364                        hint=_(b"use 'hg update' to switch to it"),
1365                    )
1366
1367            repo.dirstate.setbranch(label)
1368            ui.status(_(b'marked working directory as branch %s\n') % label)
1369
1370            # find any open named branches aside from default
1371            for n, h, t, c in repo.branchmap().iterbranches():
1372                if n != b"default" and not c:
1373                    return 0
1374            ui.status(
1375                _(
1376                    b'(branches are permanent and global, '
1377                    b'did you want a bookmark?)\n'
1378                )
1379            )
1380
1381
1382@command(
1383    b'branches',
1384    [
1385        (
1386            b'a',
1387            b'active',
1388            False,
1389            _(b'show only branches that have unmerged heads (DEPRECATED)'),
1390        ),
1391        (b'c', b'closed', False, _(b'show normal and closed branches')),
1392        (b'r', b'rev', [], _(b'show branch name(s) of the given rev')),
1393    ]
1394    + formatteropts,
1395    _(b'[-c]'),
1396    helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1397    intents={INTENT_READONLY},
1398)
1399def branches(ui, repo, active=False, closed=False, **opts):
1400    """list repository named branches
1401
1402    List the repository's named branches, indicating which ones are
1403    inactive. If -c/--closed is specified, also list branches which have
1404    been marked closed (see :hg:`commit --close-branch`).
1405
1406    Use the command :hg:`update` to switch to an existing branch.
1407
1408    .. container:: verbose
1409
1410      Template:
1411
1412      The following keywords are supported in addition to the common template
1413      keywords and functions such as ``{branch}``. See also
1414      :hg:`help templates`.
1415
1416      :active:  Boolean. True if the branch is active.
1417      :closed:  Boolean. True if the branch is closed.
1418      :current: Boolean. True if it is the current branch.
1419
1420    Returns 0.
1421    """
1422
1423    opts = pycompat.byteskwargs(opts)
1424    revs = opts.get(b'rev')
1425    selectedbranches = None
1426    if revs:
1427        revs = logcmdutil.revrange(repo, revs)
1428        getbi = repo.revbranchcache().branchinfo
1429        selectedbranches = {getbi(r)[0] for r in revs}
1430
1431    ui.pager(b'branches')
1432    fm = ui.formatter(b'branches', opts)
1433    hexfunc = fm.hexfunc
1434
1435    allheads = set(repo.heads())
1436    branches = []
1437    for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1438        if selectedbranches is not None and tag not in selectedbranches:
1439            continue
1440        isactive = False
1441        if not isclosed:
1442            openheads = set(repo.branchmap().iteropen(heads))
1443            isactive = bool(openheads & allheads)
1444        branches.append((tag, repo[tip], isactive, not isclosed))
1445    branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
1446
1447    for tag, ctx, isactive, isopen in branches:
1448        if active and not isactive:
1449            continue
1450        if isactive:
1451            label = b'branches.active'
1452            notice = b''
1453        elif not isopen:
1454            if not closed:
1455                continue
1456            label = b'branches.closed'
1457            notice = _(b' (closed)')
1458        else:
1459            label = b'branches.inactive'
1460            notice = _(b' (inactive)')
1461        current = tag == repo.dirstate.branch()
1462        if current:
1463            label = b'branches.current'
1464
1465        fm.startitem()
1466        fm.write(b'branch', b'%s', tag, label=label)
1467        rev = ctx.rev()
1468        padsize = max(31 - len(b"%d" % rev) - encoding.colwidth(tag), 0)
1469        fmt = b' ' * padsize + b' %d:%s'
1470        fm.condwrite(
1471            not ui.quiet,
1472            b'rev node',
1473            fmt,
1474            rev,
1475            hexfunc(ctx.node()),
1476            label=b'log.changeset changeset.%s' % ctx.phasestr(),
1477        )
1478        fm.context(ctx=ctx)
1479        fm.data(active=isactive, closed=not isopen, current=current)
1480        if not ui.quiet:
1481            fm.plain(notice)
1482        fm.plain(b'\n')
1483    fm.end()
1484
1485
1486@command(
1487    b'bundle',
1488    [
1489        (
1490            b'f',
1491            b'force',
1492            None,
1493            _(b'run even when the destination is unrelated'),
1494        ),
1495        (
1496            b'r',
1497            b'rev',
1498            [],
1499            _(b'a changeset intended to be added to the destination'),
1500            _(b'REV'),
1501        ),
1502        (
1503            b'b',
1504            b'branch',
1505            [],
1506            _(b'a specific branch you would like to bundle'),
1507            _(b'BRANCH'),
1508        ),
1509        (
1510            b'',
1511            b'base',
1512            [],
1513            _(b'a base changeset assumed to be available at the destination'),
1514            _(b'REV'),
1515        ),
1516        (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1517        (
1518            b't',
1519            b'type',
1520            b'bzip2',
1521            _(b'bundle compression type to use'),
1522            _(b'TYPE'),
1523        ),
1524    ]
1525    + remoteopts,
1526    _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]...'),
1527    helpcategory=command.CATEGORY_IMPORT_EXPORT,
1528)
1529def bundle(ui, repo, fname, *dests, **opts):
1530    """create a bundle file
1531
1532    Generate a bundle file containing data to be transferred to another
1533    repository.
1534
1535    To create a bundle containing all changesets, use -a/--all
1536    (or --base null). Otherwise, hg assumes the destination will have
1537    all the nodes you specify with --base parameters. Otherwise, hg
1538    will assume the repository has all the nodes in destination, or
1539    default-push/default if no destination is specified, where destination
1540    is the repositories you provide through DEST option.
1541
1542    You can change bundle format with the -t/--type option. See
1543    :hg:`help bundlespec` for documentation on this format. By default,
1544    the most appropriate format is used and compression defaults to
1545    bzip2.
1546
1547    The bundle file can then be transferred using conventional means
1548    and applied to another repository with the unbundle or pull
1549    command. This is useful when direct push and pull are not
1550    available or when exporting an entire repository is undesirable.
1551
1552    Applying bundles preserves all changeset contents including
1553    permissions, copy/rename information, and revision history.
1554
1555    Returns 0 on success, 1 if no changes found.
1556    """
1557    opts = pycompat.byteskwargs(opts)
1558    revs = None
1559    if b'rev' in opts:
1560        revstrings = opts[b'rev']
1561        revs = logcmdutil.revrange(repo, revstrings)
1562        if revstrings and not revs:
1563            raise error.InputError(_(b'no commits to bundle'))
1564
1565    bundletype = opts.get(b'type', b'bzip2').lower()
1566    try:
1567        bundlespec = bundlecaches.parsebundlespec(
1568            repo, bundletype, strict=False
1569        )
1570    except error.UnsupportedBundleSpecification as e:
1571        raise error.InputError(
1572            pycompat.bytestr(e),
1573            hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1574        )
1575    cgversion = bundlespec.contentopts[b"cg.version"]
1576
1577    # Packed bundles are a pseudo bundle format for now.
1578    if cgversion == b's1':
1579        raise error.InputError(
1580            _(b'packed bundles cannot be produced by "hg bundle"'),
1581            hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1582        )
1583
1584    if opts.get(b'all'):
1585        if dests:
1586            raise error.InputError(
1587                _(b"--all is incompatible with specifying destinations")
1588            )
1589        if opts.get(b'base'):
1590            ui.warn(_(b"ignoring --base because --all was specified\n"))
1591        base = [nullrev]
1592    else:
1593        base = logcmdutil.revrange(repo, opts.get(b'base'))
1594    if cgversion not in changegroup.supportedoutgoingversions(repo):
1595        raise error.Abort(
1596            _(b"repository does not support bundle version %s") % cgversion
1597        )
1598
1599    if base:
1600        if dests:
1601            raise error.InputError(
1602                _(b"--base is incompatible with specifying destinations")
1603            )
1604        common = [repo[rev].node() for rev in base]
1605        heads = [repo[r].node() for r in revs] if revs else None
1606        outgoing = discovery.outgoing(repo, common, heads)
1607        missing = outgoing.missing
1608        excluded = outgoing.excluded
1609    else:
1610        missing = set()
1611        excluded = set()
1612        for path in urlutil.get_push_paths(repo, ui, dests):
1613            other = hg.peer(repo, opts, path.rawloc)
1614            if revs is not None:
1615                hex_revs = [repo[r].hex() for r in revs]
1616            else:
1617                hex_revs = None
1618            branches = (path.branch, [])
1619            head_revs, checkout = hg.addbranchrevs(
1620                repo, repo, branches, hex_revs
1621            )
1622            heads = (
1623                head_revs
1624                and pycompat.maplist(repo.lookup, head_revs)
1625                or head_revs
1626            )
1627            outgoing = discovery.findcommonoutgoing(
1628                repo,
1629                other,
1630                onlyheads=heads,
1631                force=opts.get(b'force'),
1632                portable=True,
1633            )
1634            missing.update(outgoing.missing)
1635            excluded.update(outgoing.excluded)
1636
1637    if not missing:
1638        scmutil.nochangesfound(ui, repo, not base and excluded)
1639        return 1
1640
1641    if heads:
1642        outgoing = discovery.outgoing(
1643            repo, missingroots=missing, ancestorsof=heads
1644        )
1645    else:
1646        outgoing = discovery.outgoing(repo, missingroots=missing)
1647    outgoing.excluded = sorted(excluded)
1648
1649    if cgversion == b'01':  # bundle1
1650        bversion = b'HG10' + bundlespec.wirecompression
1651        bcompression = None
1652    elif cgversion in (b'02', b'03'):
1653        bversion = b'HG20'
1654        bcompression = bundlespec.wirecompression
1655    else:
1656        raise error.ProgrammingError(
1657            b'bundle: unexpected changegroup version %s' % cgversion
1658        )
1659
1660    # TODO compression options should be derived from bundlespec parsing.
1661    # This is a temporary hack to allow adjusting bundle compression
1662    # level without a) formalizing the bundlespec changes to declare it
1663    # b) introducing a command flag.
1664    compopts = {}
1665    complevel = ui.configint(
1666        b'experimental', b'bundlecomplevel.' + bundlespec.compression
1667    )
1668    if complevel is None:
1669        complevel = ui.configint(b'experimental', b'bundlecomplevel')
1670    if complevel is not None:
1671        compopts[b'level'] = complevel
1672
1673    compthreads = ui.configint(
1674        b'experimental', b'bundlecompthreads.' + bundlespec.compression
1675    )
1676    if compthreads is None:
1677        compthreads = ui.configint(b'experimental', b'bundlecompthreads')
1678    if compthreads is not None:
1679        compopts[b'threads'] = compthreads
1680
1681    # Bundling of obsmarker and phases is optional as not all clients
1682    # support the necessary features.
1683    cfg = ui.configbool
1684    contentopts = {
1685        b'obsolescence': cfg(b'experimental', b'evolution.bundle-obsmarker'),
1686        b'obsolescence-mandatory': cfg(
1687            b'experimental', b'evolution.bundle-obsmarker:mandatory'
1688        ),
1689        b'phases': cfg(b'experimental', b'bundle-phases'),
1690    }
1691    bundlespec.contentopts.update(contentopts)
1692
1693    bundle2.writenewbundle(
1694        ui,
1695        repo,
1696        b'bundle',
1697        fname,
1698        bversion,
1699        outgoing,
1700        bundlespec.contentopts,
1701        compression=bcompression,
1702        compopts=compopts,
1703    )
1704
1705
1706@command(
1707    b'cat',
1708    [
1709        (
1710            b'o',
1711            b'output',
1712            b'',
1713            _(b'print output to file with formatted name'),
1714            _(b'FORMAT'),
1715        ),
1716        (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1717        (b'', b'decode', None, _(b'apply any matching decode filter')),
1718    ]
1719    + walkopts
1720    + formatteropts,
1721    _(b'[OPTION]... FILE...'),
1722    helpcategory=command.CATEGORY_FILE_CONTENTS,
1723    inferrepo=True,
1724    intents={INTENT_READONLY},
1725)
1726def cat(ui, repo, file1, *pats, **opts):
1727    """output the current or given revision of files
1728
1729    Print the specified files as they were at the given revision. If
1730    no revision is given, the parent of the working directory is used.
1731
1732    Output may be to a file, in which case the name of the file is
1733    given using a template string. See :hg:`help templates`. In addition
1734    to the common template keywords, the following formatting rules are
1735    supported:
1736
1737    :``%%``: literal "%" character
1738    :``%s``: basename of file being printed
1739    :``%d``: dirname of file being printed, or '.' if in repository root
1740    :``%p``: root-relative path name of file being printed
1741    :``%H``: changeset hash (40 hexadecimal digits)
1742    :``%R``: changeset revision number
1743    :``%h``: short-form changeset hash (12 hexadecimal digits)
1744    :``%r``: zero-padded changeset revision number
1745    :``%b``: basename of the exporting repository
1746    :``\\``: literal "\\" character
1747
1748    .. container:: verbose
1749
1750      Template:
1751
1752      The following keywords are supported in addition to the common template
1753      keywords and functions. See also :hg:`help templates`.
1754
1755      :data:    String. File content.
1756      :path:    String. Repository-absolute path of the file.
1757
1758    Returns 0 on success.
1759    """
1760    opts = pycompat.byteskwargs(opts)
1761    rev = opts.get(b'rev')
1762    if rev:
1763        repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1764    ctx = logcmdutil.revsingle(repo, rev)
1765    m = scmutil.match(ctx, (file1,) + pats, opts)
1766    fntemplate = opts.pop(b'output', b'')
1767    if cmdutil.isstdiofilename(fntemplate):
1768        fntemplate = b''
1769
1770    if fntemplate:
1771        fm = formatter.nullformatter(ui, b'cat', opts)
1772    else:
1773        ui.pager(b'cat')
1774        fm = ui.formatter(b'cat', opts)
1775    with fm:
1776        return cmdutil.cat(
1777            ui, repo, ctx, m, fm, fntemplate, b'', **pycompat.strkwargs(opts)
1778        )
1779
1780
1781@command(
1782    b'clone',
1783    [
1784        (
1785            b'U',
1786            b'noupdate',
1787            None,
1788            _(
1789                b'the clone will include an empty working '
1790                b'directory (only a repository)'
1791            ),
1792        ),
1793        (
1794            b'u',
1795            b'updaterev',
1796            b'',
1797            _(b'revision, tag, or branch to check out'),
1798            _(b'REV'),
1799        ),
1800        (
1801            b'r',
1802            b'rev',
1803            [],
1804            _(
1805                b'do not clone everything, but include this changeset'
1806                b' and its ancestors'
1807            ),
1808            _(b'REV'),
1809        ),
1810        (
1811            b'b',
1812            b'branch',
1813            [],
1814            _(
1815                b'do not clone everything, but include this branch\'s'
1816                b' changesets and their ancestors'
1817            ),
1818            _(b'BRANCH'),
1819        ),
1820        (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1821        (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1822        (b'', b'stream', None, _(b'clone with minimal data processing')),
1823    ]
1824    + remoteopts,
1825    _(b'[OPTION]... SOURCE [DEST]'),
1826    helpcategory=command.CATEGORY_REPO_CREATION,
1827    helpbasic=True,
1828    norepo=True,
1829)
1830def clone(ui, source, dest=None, **opts):
1831    """make a copy of an existing repository
1832
1833    Create a copy of an existing repository in a new directory.
1834
1835    If no destination directory name is specified, it defaults to the
1836    basename of the source.
1837
1838    The location of the source is added to the new repository's
1839    ``.hg/hgrc`` file, as the default to be used for future pulls.
1840
1841    Only local paths and ``ssh://`` URLs are supported as
1842    destinations. For ``ssh://`` destinations, no working directory or
1843    ``.hg/hgrc`` will be created on the remote side.
1844
1845    If the source repository has a bookmark called '@' set, that
1846    revision will be checked out in the new repository by default.
1847
1848    To check out a particular version, use -u/--update, or
1849    -U/--noupdate to create a clone with no working directory.
1850
1851    To pull only a subset of changesets, specify one or more revisions
1852    identifiers with -r/--rev or branches with -b/--branch. The
1853    resulting clone will contain only the specified changesets and
1854    their ancestors. These options (or 'clone src#rev dest') imply
1855    --pull, even for local source repositories.
1856
1857    In normal clone mode, the remote normalizes repository data into a common
1858    exchange format and the receiving end translates this data into its local
1859    storage format. --stream activates a different clone mode that essentially
1860    copies repository files from the remote with minimal data processing. This
1861    significantly reduces the CPU cost of a clone both remotely and locally.
1862    However, it often increases the transferred data size by 30-40%. This can
1863    result in substantially faster clones where I/O throughput is plentiful,
1864    especially for larger repositories. A side-effect of --stream clones is
1865    that storage settings and requirements on the remote are applied locally:
1866    a modern client may inherit legacy or inefficient storage used by the
1867    remote or a legacy Mercurial client may not be able to clone from a
1868    modern Mercurial remote.
1869
1870    .. note::
1871
1872       Specifying a tag will include the tagged changeset but not the
1873       changeset containing the tag.
1874
1875    .. container:: verbose
1876
1877      For efficiency, hardlinks are used for cloning whenever the
1878      source and destination are on the same filesystem (note this
1879      applies only to the repository data, not to the working
1880      directory). Some filesystems, such as AFS, implement hardlinking
1881      incorrectly, but do not report errors. In these cases, use the
1882      --pull option to avoid hardlinking.
1883
1884      Mercurial will update the working directory to the first applicable
1885      revision from this list:
1886
1887      a) null if -U or the source repository has no changesets
1888      b) if -u . and the source repository is local, the first parent of
1889         the source repository's working directory
1890      c) the changeset specified with -u (if a branch name, this means the
1891         latest head of that branch)
1892      d) the changeset specified with -r
1893      e) the tipmost head specified with -b
1894      f) the tipmost head specified with the url#branch source syntax
1895      g) the revision marked with the '@' bookmark, if present
1896      h) the tipmost head of the default branch
1897      i) tip
1898
1899      When cloning from servers that support it, Mercurial may fetch
1900      pre-generated data from a server-advertised URL or inline from the
1901      same stream. When this is done, hooks operating on incoming changesets
1902      and changegroups may fire more than once, once for each pre-generated
1903      bundle and as well as for any additional remaining data. In addition,
1904      if an error occurs, the repository may be rolled back to a partial
1905      clone. This behavior may change in future releases.
1906      See :hg:`help -e clonebundles` for more.
1907
1908      Examples:
1909
1910      - clone a remote repository to a new directory named hg/::
1911
1912          hg clone https://www.mercurial-scm.org/repo/hg/
1913
1914      - create a lightweight local clone::
1915
1916          hg clone project/ project-feature/
1917
1918      - clone from an absolute path on an ssh server (note double-slash)::
1919
1920          hg clone ssh://user@server//home/projects/alpha/
1921
1922      - do a streaming clone while checking out a specified version::
1923
1924          hg clone --stream http://server/repo -u 1.5
1925
1926      - create a repository without changesets after a particular revision::
1927
1928          hg clone -r 04e544 experimental/ good/
1929
1930      - clone (and track) a particular named branch::
1931
1932          hg clone https://www.mercurial-scm.org/repo/hg/#stable
1933
1934    See :hg:`help urls` for details on specifying URLs.
1935
1936    Returns 0 on success.
1937    """
1938    opts = pycompat.byteskwargs(opts)
1939    cmdutil.check_at_most_one_arg(opts, b'noupdate', b'updaterev')
1940
1941    # --include/--exclude can come from narrow or sparse.
1942    includepats, excludepats = None, None
1943
1944    # hg.clone() differentiates between None and an empty set. So make sure
1945    # patterns are sets if narrow is requested without patterns.
1946    if opts.get(b'narrow'):
1947        includepats = set()
1948        excludepats = set()
1949
1950        if opts.get(b'include'):
1951            includepats = narrowspec.parsepatterns(opts.get(b'include'))
1952        if opts.get(b'exclude'):
1953            excludepats = narrowspec.parsepatterns(opts.get(b'exclude'))
1954
1955    r = hg.clone(
1956        ui,
1957        opts,
1958        source,
1959        dest,
1960        pull=opts.get(b'pull'),
1961        stream=opts.get(b'stream') or opts.get(b'uncompressed'),
1962        revs=opts.get(b'rev'),
1963        update=opts.get(b'updaterev') or not opts.get(b'noupdate'),
1964        branch=opts.get(b'branch'),
1965        shareopts=opts.get(b'shareopts'),
1966        storeincludepats=includepats,
1967        storeexcludepats=excludepats,
1968        depth=opts.get(b'depth') or None,
1969    )
1970
1971    return r is None
1972
1973
1974@command(
1975    b'commit|ci',
1976    [
1977        (
1978            b'A',
1979            b'addremove',
1980            None,
1981            _(b'mark new/missing files as added/removed before committing'),
1982        ),
1983        (b'', b'close-branch', None, _(b'mark a branch head as closed')),
1984        (b'', b'amend', None, _(b'amend the parent of the working directory')),
1985        (b's', b'secret', None, _(b'use the secret phase for committing')),
1986        (b'e', b'edit', None, _(b'invoke editor on commit messages')),
1987        (
1988            b'',
1989            b'force-close-branch',
1990            None,
1991            _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
1992        ),
1993        (b'i', b'interactive', None, _(b'use interactive mode')),
1994    ]
1995    + walkopts
1996    + commitopts
1997    + commitopts2
1998    + subrepoopts,
1999    _(b'[OPTION]... [FILE]...'),
2000    helpcategory=command.CATEGORY_COMMITTING,
2001    helpbasic=True,
2002    inferrepo=True,
2003)
2004def commit(ui, repo, *pats, **opts):
2005    """commit the specified files or all outstanding changes
2006
2007    Commit changes to the given files into the repository. Unlike a
2008    centralized SCM, this operation is a local operation. See
2009    :hg:`push` for a way to actively distribute your changes.
2010
2011    If a list of files is omitted, all changes reported by :hg:`status`
2012    will be committed.
2013
2014    If you are committing the result of a merge, do not provide any
2015    filenames or -I/-X filters.
2016
2017    If no commit message is specified, Mercurial starts your
2018    configured editor where you can enter a message. In case your
2019    commit fails, you will find a backup of your message in
2020    ``.hg/last-message.txt``.
2021
2022    The --close-branch flag can be used to mark the current branch
2023    head closed. When all heads of a branch are closed, the branch
2024    will be considered closed and no longer listed.
2025
2026    The --amend flag can be used to amend the parent of the
2027    working directory with a new commit that contains the changes
2028    in the parent in addition to those currently reported by :hg:`status`,
2029    if there are any. The old commit is stored in a backup bundle in
2030    ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
2031    on how to restore it).
2032
2033    Message, user and date are taken from the amended commit unless
2034    specified. When a message isn't specified on the command line,
2035    the editor will open with the message of the amended commit.
2036
2037    It is not possible to amend public changesets (see :hg:`help phases`)
2038    or changesets that have children.
2039
2040    See :hg:`help dates` for a list of formats valid for -d/--date.
2041
2042    Returns 0 on success, 1 if nothing changed.
2043
2044    .. container:: verbose
2045
2046      Examples:
2047
2048      - commit all files ending in .py::
2049
2050          hg commit --include "set:**.py"
2051
2052      - commit all non-binary files::
2053
2054          hg commit --exclude "set:binary()"
2055
2056      - amend the current commit and set the date to now::
2057
2058          hg commit --amend --date now
2059    """
2060    with repo.wlock(), repo.lock():
2061        return _docommit(ui, repo, *pats, **opts)
2062
2063
2064def _docommit(ui, repo, *pats, **opts):
2065    if opts.get('interactive'):
2066        opts.pop('interactive')
2067        ret = cmdutil.dorecord(
2068            ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2069        )
2070        # ret can be 0 (no changes to record) or the value returned by
2071        # commit(), 1 if nothing changed or None on success.
2072        return 1 if ret == 0 else ret
2073
2074    if opts.get('subrepos'):
2075        cmdutil.check_incompatible_arguments(opts, 'subrepos', ['amend'])
2076        # Let --subrepos on the command line override config setting.
2077        ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2078
2079    cmdutil.checkunfinished(repo, commit=True)
2080
2081    branch = repo[None].branch()
2082    bheads = repo.branchheads(branch)
2083    tip = repo.changelog.tip()
2084
2085    extra = {}
2086    if opts.get('close_branch') or opts.get('force_close_branch'):
2087        extra[b'close'] = b'1'
2088
2089        if repo[b'.'].closesbranch():
2090            raise error.InputError(
2091                _(b'current revision is already a branch closing head')
2092            )
2093        elif not bheads:
2094            raise error.InputError(
2095                _(b'branch "%s" has no heads to close') % branch
2096            )
2097        elif (
2098            branch == repo[b'.'].branch()
2099            and repo[b'.'].node() not in bheads
2100            and not opts.get('force_close_branch')
2101        ):
2102            hint = _(
2103                b'use --force-close-branch to close branch from a non-head'
2104                b' changeset'
2105            )
2106            raise error.InputError(_(b'can only close branch heads'), hint=hint)
2107        elif opts.get('amend'):
2108            if (
2109                repo[b'.'].p1().branch() != branch
2110                and repo[b'.'].p2().branch() != branch
2111            ):
2112                raise error.InputError(_(b'can only close branch heads'))
2113
2114    if opts.get('amend'):
2115        if ui.configbool(b'ui', b'commitsubrepos'):
2116            raise error.InputError(
2117                _(b'cannot amend with ui.commitsubrepos enabled')
2118            )
2119
2120        old = repo[b'.']
2121        rewriteutil.precheck(repo, [old.rev()], b'amend')
2122
2123        # Currently histedit gets confused if an amend happens while histedit
2124        # is in progress. Since we have a checkunfinished command, we are
2125        # temporarily honoring it.
2126        #
2127        # Note: eventually this guard will be removed. Please do not expect
2128        # this behavior to remain.
2129        if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2130            cmdutil.checkunfinished(repo)
2131
2132        node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2133        opts = pycompat.byteskwargs(opts)
2134        if node == old.node():
2135            ui.status(_(b"nothing changed\n"))
2136            return 1
2137    else:
2138
2139        def commitfunc(ui, repo, message, match, opts):
2140            overrides = {}
2141            if opts.get(b'secret'):
2142                overrides[(b'phases', b'new-commit')] = b'secret'
2143
2144            baseui = repo.baseui
2145            with baseui.configoverride(overrides, b'commit'):
2146                with ui.configoverride(overrides, b'commit'):
2147                    editform = cmdutil.mergeeditform(
2148                        repo[None], b'commit.normal'
2149                    )
2150                    editor = cmdutil.getcommiteditor(
2151                        editform=editform, **pycompat.strkwargs(opts)
2152                    )
2153                    return repo.commit(
2154                        message,
2155                        opts.get(b'user'),
2156                        opts.get(b'date'),
2157                        match,
2158                        editor=editor,
2159                        extra=extra,
2160                    )
2161
2162        opts = pycompat.byteskwargs(opts)
2163        node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
2164
2165        if not node:
2166            stat = cmdutil.postcommitstatus(repo, pats, opts)
2167            if stat.deleted:
2168                ui.status(
2169                    _(
2170                        b"nothing changed (%d missing files, see "
2171                        b"'hg status')\n"
2172                    )
2173                    % len(stat.deleted)
2174                )
2175            else:
2176                ui.status(_(b"nothing changed\n"))
2177            return 1
2178
2179    cmdutil.commitstatus(repo, node, branch, bheads, tip, opts)
2180
2181    if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2182        status(
2183            ui,
2184            repo,
2185            modified=True,
2186            added=True,
2187            removed=True,
2188            deleted=True,
2189            unknown=True,
2190            subrepos=opts.get(b'subrepos'),
2191        )
2192
2193
2194@command(
2195    b'config|showconfig|debugconfig',
2196    [
2197        (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2198        # This is experimental because we need
2199        # * reasonable behavior around aliases,
2200        # * decide if we display [debug] [experimental] and [devel] section par
2201        #   default
2202        # * some way to display "generic" config entry (the one matching
2203        #   regexp,
2204        # * proper display of the different value type
2205        # * a better way to handle <DYNAMIC> values (and variable types),
2206        # * maybe some type information ?
2207        (
2208            b'',
2209            b'exp-all-known',
2210            None,
2211            _(b'show all known config option (EXPERIMENTAL)'),
2212        ),
2213        (b'e', b'edit', None, _(b'edit user config')),
2214        (b'l', b'local', None, _(b'edit repository config')),
2215        (b'', b'source', None, _(b'show source of configuration value')),
2216        (
2217            b'',
2218            b'shared',
2219            None,
2220            _(b'edit shared source repository config (EXPERIMENTAL)'),
2221        ),
2222        (b'', b'non-shared', None, _(b'edit non shared config (EXPERIMENTAL)')),
2223        (b'g', b'global', None, _(b'edit global config')),
2224    ]
2225    + formatteropts,
2226    _(b'[-u] [NAME]...'),
2227    helpcategory=command.CATEGORY_HELP,
2228    optionalrepo=True,
2229    intents={INTENT_READONLY},
2230)
2231def config(ui, repo, *values, **opts):
2232    """show combined config settings from all hgrc files
2233
2234    With no arguments, print names and values of all config items.
2235
2236    With one argument of the form section.name, print just the value
2237    of that config item.
2238
2239    With multiple arguments, print names and values of all config
2240    items with matching section names or section.names.
2241
2242    With --edit, start an editor on the user-level config file. With
2243    --global, edit the system-wide config file. With --local, edit the
2244    repository-level config file.
2245
2246    With --source, the source (filename and line number) is printed
2247    for each config item.
2248
2249    See :hg:`help config` for more information about config files.
2250
2251    .. container:: verbose
2252
2253      --non-shared flag is used to edit `.hg/hgrc-not-shared` config file.
2254      This file is not shared across shares when in share-safe mode.
2255
2256      Template:
2257
2258      The following keywords are supported. See also :hg:`help templates`.
2259
2260      :name:    String. Config name.
2261      :source:  String. Filename and line number where the item is defined.
2262      :value:   String. Config value.
2263
2264      The --shared flag can be used to edit the config file of shared source
2265      repository. It only works when you have shared using the experimental
2266      share safe feature.
2267
2268    Returns 0 on success, 1 if NAME does not exist.
2269
2270    """
2271
2272    opts = pycompat.byteskwargs(opts)
2273    editopts = (b'edit', b'local', b'global', b'shared', b'non_shared')
2274    if any(opts.get(o) for o in editopts):
2275        cmdutil.check_at_most_one_arg(opts, *editopts[1:])
2276        if opts.get(b'local'):
2277            if not repo:
2278                raise error.InputError(
2279                    _(b"can't use --local outside a repository")
2280                )
2281            paths = [repo.vfs.join(b'hgrc')]
2282        elif opts.get(b'global'):
2283            paths = rcutil.systemrcpath()
2284        elif opts.get(b'shared'):
2285            if not repo.shared():
2286                raise error.InputError(
2287                    _(b"repository is not shared; can't use --shared")
2288                )
2289            if requirements.SHARESAFE_REQUIREMENT not in repo.requirements:
2290                raise error.InputError(
2291                    _(
2292                        b"share safe feature not enabled; "
2293                        b"unable to edit shared source repository config"
2294                    )
2295                )
2296            paths = [vfsmod.vfs(repo.sharedpath).join(b'hgrc')]
2297        elif opts.get(b'non_shared'):
2298            paths = [repo.vfs.join(b'hgrc-not-shared')]
2299        else:
2300            paths = rcutil.userrcpath()
2301
2302        for f in paths:
2303            if os.path.exists(f):
2304                break
2305        else:
2306            if opts.get(b'global'):
2307                samplehgrc = uimod.samplehgrcs[b'global']
2308            elif opts.get(b'local'):
2309                samplehgrc = uimod.samplehgrcs[b'local']
2310            else:
2311                samplehgrc = uimod.samplehgrcs[b'user']
2312
2313            f = paths[0]
2314            fp = open(f, b"wb")
2315            fp.write(util.tonativeeol(samplehgrc))
2316            fp.close()
2317
2318        editor = ui.geteditor()
2319        ui.system(
2320            b"%s \"%s\"" % (editor, f),
2321            onerr=error.InputError,
2322            errprefix=_(b"edit failed"),
2323            blockedtag=b'config_edit',
2324        )
2325        return
2326    ui.pager(b'config')
2327    fm = ui.formatter(b'config', opts)
2328    for t, f in rcutil.rccomponents():
2329        if t == b'path':
2330            ui.debug(b'read config from: %s\n' % f)
2331        elif t == b'resource':
2332            ui.debug(b'read config from: resource:%s.%s\n' % (f[0], f[1]))
2333        elif t == b'items':
2334            # Don't print anything for 'items'.
2335            pass
2336        else:
2337            raise error.ProgrammingError(b'unknown rctype: %s' % t)
2338    untrusted = bool(opts.get(b'untrusted'))
2339
2340    selsections = selentries = []
2341    if values:
2342        selsections = [v for v in values if b'.' not in v]
2343        selentries = [v for v in values if b'.' in v]
2344    uniquesel = len(selentries) == 1 and not selsections
2345    selsections = set(selsections)
2346    selentries = set(selentries)
2347
2348    matched = False
2349    all_known = opts[b'exp_all_known']
2350    show_source = ui.debugflag or opts.get(b'source')
2351    entries = ui.walkconfig(untrusted=untrusted, all_known=all_known)
2352    for section, name, value in entries:
2353        source = ui.configsource(section, name, untrusted)
2354        value = pycompat.bytestr(value)
2355        defaultvalue = ui.configdefault(section, name)
2356        if fm.isplain():
2357            source = source or b'none'
2358            value = value.replace(b'\n', b'\\n')
2359        entryname = section + b'.' + name
2360        if values and not (section in selsections or entryname in selentries):
2361            continue
2362        fm.startitem()
2363        fm.condwrite(show_source, b'source', b'%s: ', source)
2364        if uniquesel:
2365            fm.data(name=entryname)
2366            fm.write(b'value', b'%s\n', value)
2367        else:
2368            fm.write(b'name value', b'%s=%s\n', entryname, value)
2369        if formatter.isprintable(defaultvalue):
2370            fm.data(defaultvalue=defaultvalue)
2371        elif isinstance(defaultvalue, list) and all(
2372            formatter.isprintable(e) for e in defaultvalue
2373        ):
2374            fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
2375        # TODO: no idea how to process unsupported defaultvalue types
2376        matched = True
2377    fm.end()
2378    if matched:
2379        return 0
2380    return 1
2381
2382
2383@command(
2384    b'continue',
2385    dryrunopts,
2386    helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2387    helpbasic=True,
2388)
2389def continuecmd(ui, repo, **opts):
2390    """resumes an interrupted operation (EXPERIMENTAL)
2391
2392    Finishes a multistep operation like graft, histedit, rebase, merge,
2393    and unshelve if they are in an interrupted state.
2394
2395    use --dry-run/-n to dry run the command.
2396    """
2397    dryrun = opts.get('dry_run')
2398    contstate = cmdutil.getunfinishedstate(repo)
2399    if not contstate:
2400        raise error.StateError(_(b'no operation in progress'))
2401    if not contstate.continuefunc:
2402        raise error.StateError(
2403            (
2404                _(b"%s in progress but does not support 'hg continue'")
2405                % (contstate._opname)
2406            ),
2407            hint=contstate.continuemsg(),
2408        )
2409    if dryrun:
2410        ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2411        return
2412    return contstate.continuefunc(ui, repo)
2413
2414
2415@command(
2416    b'copy|cp',
2417    [
2418        (b'', b'forget', None, _(b'unmark a destination file as copied')),
2419        (b'A', b'after', None, _(b'record a copy that has already occurred')),
2420        (
2421            b'',
2422            b'at-rev',
2423            b'',
2424            _(b'(un)mark copies in the given revision (EXPERIMENTAL)'),
2425            _(b'REV'),
2426        ),
2427        (
2428            b'f',
2429            b'force',
2430            None,
2431            _(b'forcibly copy over an existing managed file'),
2432        ),
2433    ]
2434    + walkopts
2435    + dryrunopts,
2436    _(b'[OPTION]... (SOURCE... DEST | --forget DEST...)'),
2437    helpcategory=command.CATEGORY_FILE_CONTENTS,
2438)
2439def copy(ui, repo, *pats, **opts):
2440    """mark files as copied for the next commit
2441
2442    Mark dest as having copies of source files. If dest is a
2443    directory, copies are put in that directory. If dest is a file,
2444    the source must be a single file.
2445
2446    By default, this command copies the contents of files as they
2447    exist in the working directory. If invoked with -A/--after, the
2448    operation is recorded, but no copying is performed.
2449
2450    To undo marking a destination file as copied, use --forget. With that
2451    option, all given (positional) arguments are unmarked as copies. The
2452    destination file(s) will be left in place (still tracked). Note that
2453    :hg:`copy --forget` behaves the same way as :hg:`rename --forget`.
2454
2455    This command takes effect with the next commit by default.
2456
2457    Returns 0 on success, 1 if errors are encountered.
2458    """
2459    opts = pycompat.byteskwargs(opts)
2460    with repo.wlock():
2461        return cmdutil.copy(ui, repo, pats, opts)
2462
2463
2464@command(
2465    b'debugcommands',
2466    [],
2467    _(b'[COMMAND]'),
2468    helpcategory=command.CATEGORY_HELP,
2469    norepo=True,
2470)
2471def debugcommands(ui, cmd=b'', *args):
2472    """list all available commands and options"""
2473    for cmd, vals in sorted(pycompat.iteritems(table)):
2474        cmd = cmd.split(b'|')[0]
2475        opts = b', '.join([i[1] for i in vals[1]])
2476        ui.write(b'%s: %s\n' % (cmd, opts))
2477
2478
2479@command(
2480    b'debugcomplete',
2481    [(b'o', b'options', None, _(b'show the command options'))],
2482    _(b'[-o] CMD'),
2483    helpcategory=command.CATEGORY_HELP,
2484    norepo=True,
2485)
2486def debugcomplete(ui, cmd=b'', **opts):
2487    """returns the completion list associated with the given command"""
2488
2489    if opts.get('options'):
2490        options = []
2491        otables = [globalopts]
2492        if cmd:
2493            aliases, entry = cmdutil.findcmd(cmd, table, False)
2494            otables.append(entry[1])
2495        for t in otables:
2496            for o in t:
2497                if b"(DEPRECATED)" in o[3]:
2498                    continue
2499                if o[0]:
2500                    options.append(b'-%s' % o[0])
2501                options.append(b'--%s' % o[1])
2502        ui.write(b"%s\n" % b"\n".join(options))
2503        return
2504
2505    cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2506    if ui.verbose:
2507        cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2508    ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2509
2510
2511@command(
2512    b'diff',
2513    [
2514        (b'r', b'rev', [], _(b'revision (DEPRECATED)'), _(b'REV')),
2515        (b'', b'from', b'', _(b'revision to diff from'), _(b'REV1')),
2516        (b'', b'to', b'', _(b'revision to diff to'), _(b'REV2')),
2517        (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2518    ]
2519    + diffopts
2520    + diffopts2
2521    + walkopts
2522    + subrepoopts,
2523    _(b'[OPTION]... ([-c REV] | [--from REV1] [--to REV2]) [FILE]...'),
2524    helpcategory=command.CATEGORY_FILE_CONTENTS,
2525    helpbasic=True,
2526    inferrepo=True,
2527    intents={INTENT_READONLY},
2528)
2529def diff(ui, repo, *pats, **opts):
2530    """diff repository (or selected files)
2531
2532    Show differences between revisions for the specified files.
2533
2534    Differences between files are shown using the unified diff format.
2535
2536    .. note::
2537
2538       :hg:`diff` may generate unexpected results for merges, as it will
2539       default to comparing against the working directory's first
2540       parent changeset if no revisions are specified.
2541
2542    By default, the working directory files are compared to its first parent. To
2543    see the differences from another revision, use --from. To see the difference
2544    to another revision, use --to. For example, :hg:`diff --from .^` will show
2545    the differences from the working copy's grandparent to the working copy,
2546    :hg:`diff --to .` will show the diff from the working copy to its parent
2547    (i.e. the reverse of the default), and :hg:`diff --from 1.0 --to 1.2` will
2548    show the diff between those two revisions.
2549
2550    Alternatively you can specify -c/--change with a revision to see the changes
2551    in that changeset relative to its first parent (i.e. :hg:`diff -c 42` is
2552    equivalent to :hg:`diff --from 42^ --to 42`)
2553
2554    Without the -a/--text option, diff will avoid generating diffs of
2555    files it detects as binary. With -a, diff will generate a diff
2556    anyway, probably with undesirable results.
2557
2558    Use the -g/--git option to generate diffs in the git extended diff
2559    format. For more information, read :hg:`help diffs`.
2560
2561    .. container:: verbose
2562
2563      Examples:
2564
2565      - compare a file in the current working directory to its parent::
2566
2567          hg diff foo.c
2568
2569      - compare two historical versions of a directory, with rename info::
2570
2571          hg diff --git --from 1.0 --to 1.2 lib/
2572
2573      - get change stats relative to the last change on some date::
2574
2575          hg diff --stat --from "date('may 2')"
2576
2577      - diff all newly-added files that contain a keyword::
2578
2579          hg diff "set:added() and grep(GNU)"
2580
2581      - compare a revision and its parents::
2582
2583          hg diff -c 9353                  # compare against first parent
2584          hg diff --from 9353^ --to 9353   # same using revset syntax
2585          hg diff --from 9353^2 --to 9353  # compare against the second parent
2586
2587    Returns 0 on success.
2588    """
2589
2590    cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
2591    opts = pycompat.byteskwargs(opts)
2592    revs = opts.get(b'rev')
2593    change = opts.get(b'change')
2594    from_rev = opts.get(b'from')
2595    to_rev = opts.get(b'to')
2596    stat = opts.get(b'stat')
2597    reverse = opts.get(b'reverse')
2598
2599    cmdutil.check_incompatible_arguments(opts, b'from', [b'rev', b'change'])
2600    cmdutil.check_incompatible_arguments(opts, b'to', [b'rev', b'change'])
2601    if change:
2602        repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2603        ctx2 = logcmdutil.revsingle(repo, change, None)
2604        ctx1 = logcmdutil.diff_parent(ctx2)
2605    elif from_rev or to_rev:
2606        repo = scmutil.unhidehashlikerevs(
2607            repo, [from_rev] + [to_rev], b'nowarn'
2608        )
2609        ctx1 = logcmdutil.revsingle(repo, from_rev, None)
2610        ctx2 = logcmdutil.revsingle(repo, to_rev, None)
2611    else:
2612        repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2613        ctx1, ctx2 = logcmdutil.revpair(repo, revs)
2614
2615    if reverse:
2616        ctxleft = ctx2
2617        ctxright = ctx1
2618    else:
2619        ctxleft = ctx1
2620        ctxright = ctx2
2621
2622    diffopts = patch.diffallopts(ui, opts)
2623    m = scmutil.match(ctx2, pats, opts)
2624    m = repo.narrowmatch(m)
2625    ui.pager(b'diff')
2626    logcmdutil.diffordiffstat(
2627        ui,
2628        repo,
2629        diffopts,
2630        ctxleft,
2631        ctxright,
2632        m,
2633        stat=stat,
2634        listsubrepos=opts.get(b'subrepos'),
2635        root=opts.get(b'root'),
2636    )
2637
2638
2639@command(
2640    b'export',
2641    [
2642        (
2643            b'B',
2644            b'bookmark',
2645            b'',
2646            _(b'export changes only reachable by given bookmark'),
2647            _(b'BOOKMARK'),
2648        ),
2649        (
2650            b'o',
2651            b'output',
2652            b'',
2653            _(b'print output to file with formatted name'),
2654            _(b'FORMAT'),
2655        ),
2656        (b'', b'switch-parent', None, _(b'diff against the second parent')),
2657        (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2658    ]
2659    + diffopts
2660    + formatteropts,
2661    _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2662    helpcategory=command.CATEGORY_IMPORT_EXPORT,
2663    helpbasic=True,
2664    intents={INTENT_READONLY},
2665)
2666def export(ui, repo, *changesets, **opts):
2667    """dump the header and diffs for one or more changesets
2668
2669    Print the changeset header and diffs for one or more revisions.
2670    If no revision is given, the parent of the working directory is used.
2671
2672    The information shown in the changeset header is: author, date,
2673    branch name (if non-default), changeset hash, parent(s) and commit
2674    comment.
2675
2676    .. note::
2677
2678       :hg:`export` may generate unexpected diff output for merge
2679       changesets, as it will compare the merge changeset against its
2680       first parent only.
2681
2682    Output may be to a file, in which case the name of the file is
2683    given using a template string. See :hg:`help templates`. In addition
2684    to the common template keywords, the following formatting rules are
2685    supported:
2686
2687    :``%%``: literal "%" character
2688    :``%H``: changeset hash (40 hexadecimal digits)
2689    :``%N``: number of patches being generated
2690    :``%R``: changeset revision number
2691    :``%b``: basename of the exporting repository
2692    :``%h``: short-form changeset hash (12 hexadecimal digits)
2693    :``%m``: first line of the commit message (only alphanumeric characters)
2694    :``%n``: zero-padded sequence number, starting at 1
2695    :``%r``: zero-padded changeset revision number
2696    :``\\``: literal "\\" character
2697
2698    Without the -a/--text option, export will avoid generating diffs
2699    of files it detects as binary. With -a, export will generate a
2700    diff anyway, probably with undesirable results.
2701
2702    With -B/--bookmark changesets reachable by the given bookmark are
2703    selected.
2704
2705    Use the -g/--git option to generate diffs in the git extended diff
2706    format. See :hg:`help diffs` for more information.
2707
2708    With the --switch-parent option, the diff will be against the
2709    second parent. It can be useful to review a merge.
2710
2711    .. container:: verbose
2712
2713      Template:
2714
2715      The following keywords are supported in addition to the common template
2716      keywords and functions. See also :hg:`help templates`.
2717
2718      :diff:    String. Diff content.
2719      :parents: List of strings. Parent nodes of the changeset.
2720
2721      Examples:
2722
2723      - use export and import to transplant a bugfix to the current
2724        branch::
2725
2726          hg export -r 9353 | hg import -
2727
2728      - export all the changesets between two revisions to a file with
2729        rename information::
2730
2731          hg export --git -r 123:150 > changes.txt
2732
2733      - split outgoing changes into a series of patches with
2734        descriptive names::
2735
2736          hg export -r "outgoing()" -o "%n-%m.patch"
2737
2738    Returns 0 on success.
2739    """
2740    opts = pycompat.byteskwargs(opts)
2741    bookmark = opts.get(b'bookmark')
2742    changesets += tuple(opts.get(b'rev', []))
2743
2744    cmdutil.check_at_most_one_arg(opts, b'rev', b'bookmark')
2745
2746    if bookmark:
2747        if bookmark not in repo._bookmarks:
2748            raise error.InputError(_(b"bookmark '%s' not found") % bookmark)
2749
2750        revs = scmutil.bookmarkrevs(repo, bookmark)
2751    else:
2752        if not changesets:
2753            changesets = [b'.']
2754
2755        repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2756        revs = logcmdutil.revrange(repo, changesets)
2757
2758    if not revs:
2759        raise error.InputError(_(b"export requires at least one changeset"))
2760    if len(revs) > 1:
2761        ui.note(_(b'exporting patches:\n'))
2762    else:
2763        ui.note(_(b'exporting patch:\n'))
2764
2765    fntemplate = opts.get(b'output')
2766    if cmdutil.isstdiofilename(fntemplate):
2767        fntemplate = b''
2768
2769    if fntemplate:
2770        fm = formatter.nullformatter(ui, b'export', opts)
2771    else:
2772        ui.pager(b'export')
2773        fm = ui.formatter(b'export', opts)
2774    with fm:
2775        cmdutil.export(
2776            repo,
2777            revs,
2778            fm,
2779            fntemplate=fntemplate,
2780            switch_parent=opts.get(b'switch_parent'),
2781            opts=patch.diffallopts(ui, opts),
2782        )
2783
2784
2785@command(
2786    b'files',
2787    [
2788        (
2789            b'r',
2790            b'rev',
2791            b'',
2792            _(b'search the repository as it is in REV'),
2793            _(b'REV'),
2794        ),
2795        (
2796            b'0',
2797            b'print0',
2798            None,
2799            _(b'end filenames with NUL, for use with xargs'),
2800        ),
2801    ]
2802    + walkopts
2803    + formatteropts
2804    + subrepoopts,
2805    _(b'[OPTION]... [FILE]...'),
2806    helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2807    intents={INTENT_READONLY},
2808)
2809def files(ui, repo, *pats, **opts):
2810    """list tracked files
2811
2812    Print files under Mercurial control in the working directory or
2813    specified revision for given files (excluding removed files).
2814    Files can be specified as filenames or filesets.
2815
2816    If no files are given to match, this command prints the names
2817    of all files under Mercurial control.
2818
2819    .. container:: verbose
2820
2821      Template:
2822
2823      The following keywords are supported in addition to the common template
2824      keywords and functions. See also :hg:`help templates`.
2825
2826      :flags:   String. Character denoting file's symlink and executable bits.
2827      :path:    String. Repository-absolute path of the file.
2828      :size:    Integer. Size of the file in bytes.
2829
2830      Examples:
2831
2832      - list all files under the current directory::
2833
2834          hg files .
2835
2836      - shows sizes and flags for current revision::
2837
2838          hg files -vr .
2839
2840      - list all files named README::
2841
2842          hg files -I "**/README"
2843
2844      - list all binary files::
2845
2846          hg files "set:binary()"
2847
2848      - find files containing a regular expression::
2849
2850          hg files "set:grep('bob')"
2851
2852      - search tracked file contents with xargs and grep::
2853
2854          hg files -0 | xargs -0 grep foo
2855
2856    See :hg:`help patterns` and :hg:`help filesets` for more information
2857    on specifying file patterns.
2858
2859    Returns 0 if a match is found, 1 otherwise.
2860
2861    """
2862
2863    opts = pycompat.byteskwargs(opts)
2864    rev = opts.get(b'rev')
2865    if rev:
2866        repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2867    ctx = logcmdutil.revsingle(repo, rev, None)
2868
2869    end = b'\n'
2870    if opts.get(b'print0'):
2871        end = b'\0'
2872    fmt = b'%s' + end
2873
2874    m = scmutil.match(ctx, pats, opts)
2875    ui.pager(b'files')
2876    uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2877    with ui.formatter(b'files', opts) as fm:
2878        return cmdutil.files(
2879            ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2880        )
2881
2882
2883@command(
2884    b'forget',
2885    [
2886        (b'i', b'interactive', None, _(b'use interactive mode')),
2887    ]
2888    + walkopts
2889    + dryrunopts,
2890    _(b'[OPTION]... FILE...'),
2891    helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2892    helpbasic=True,
2893    inferrepo=True,
2894)
2895def forget(ui, repo, *pats, **opts):
2896    """forget the specified files on the next commit
2897
2898    Mark the specified files so they will no longer be tracked
2899    after the next commit.
2900
2901    This only removes files from the current branch, not from the
2902    entire project history, and it does not delete them from the
2903    working directory.
2904
2905    To delete the file from the working directory, see :hg:`remove`.
2906
2907    To undo a forget before the next commit, see :hg:`add`.
2908
2909    .. container:: verbose
2910
2911      Examples:
2912
2913      - forget newly-added binary files::
2914
2915          hg forget "set:added() and binary()"
2916
2917      - forget files that would be excluded by .hgignore::
2918
2919          hg forget "set:hgignore()"
2920
2921    Returns 0 on success.
2922    """
2923
2924    opts = pycompat.byteskwargs(opts)
2925    if not pats:
2926        raise error.InputError(_(b'no files specified'))
2927
2928    m = scmutil.match(repo[None], pats, opts)
2929    dryrun, interactive = opts.get(b'dry_run'), opts.get(b'interactive')
2930    uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2931    rejected = cmdutil.forget(
2932        ui,
2933        repo,
2934        m,
2935        prefix=b"",
2936        uipathfn=uipathfn,
2937        explicitonly=False,
2938        dryrun=dryrun,
2939        interactive=interactive,
2940    )[0]
2941    return rejected and 1 or 0
2942
2943
2944@command(
2945    b'graft',
2946    [
2947        (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
2948        (
2949            b'',
2950            b'base',
2951            b'',
2952            _(b'base revision when doing the graft merge (ADVANCED)'),
2953            _(b'REV'),
2954        ),
2955        (b'c', b'continue', False, _(b'resume interrupted graft')),
2956        (b'', b'stop', False, _(b'stop interrupted graft')),
2957        (b'', b'abort', False, _(b'abort interrupted graft')),
2958        (b'e', b'edit', False, _(b'invoke editor on commit messages')),
2959        (b'', b'log', None, _(b'append graft info to log message')),
2960        (
2961            b'',
2962            b'no-commit',
2963            None,
2964            _(b"don't commit, just apply the changes in working directory"),
2965        ),
2966        (b'f', b'force', False, _(b'force graft')),
2967        (
2968            b'D',
2969            b'currentdate',
2970            False,
2971            _(b'record the current date as commit date'),
2972        ),
2973        (
2974            b'U',
2975            b'currentuser',
2976            False,
2977            _(b'record the current user as committer'),
2978        ),
2979    ]
2980    + commitopts2
2981    + mergetoolopts
2982    + dryrunopts,
2983    _(b'[OPTION]... [-r REV]... REV...'),
2984    helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2985)
2986def graft(ui, repo, *revs, **opts):
2987    """copy changes from other branches onto the current branch
2988
2989    This command uses Mercurial's merge logic to copy individual
2990    changes from other branches without merging branches in the
2991    history graph. This is sometimes known as 'backporting' or
2992    'cherry-picking'. By default, graft will copy user, date, and
2993    description from the source changesets.
2994
2995    Changesets that are ancestors of the current revision, that have
2996    already been grafted, or that are merges will be skipped.
2997
2998    If --log is specified, log messages will have a comment appended
2999    of the form::
3000
3001      (grafted from CHANGESETHASH)
3002
3003    If --force is specified, revisions will be grafted even if they
3004    are already ancestors of, or have been grafted to, the destination.
3005    This is useful when the revisions have since been backed out.
3006
3007    If a graft merge results in conflicts, the graft process is
3008    interrupted so that the current merge can be manually resolved.
3009    Once all conflicts are addressed, the graft process can be
3010    continued with the -c/--continue option.
3011
3012    The -c/--continue option reapplies all the earlier options.
3013
3014    .. container:: verbose
3015
3016      The --base option exposes more of how graft internally uses merge with a
3017      custom base revision. --base can be used to specify another ancestor than
3018      the first and only parent.
3019
3020      The command::
3021
3022        hg graft -r 345 --base 234
3023
3024      is thus pretty much the same as::
3025
3026        hg diff --from 234 --to 345 | hg import
3027
3028      but using merge to resolve conflicts and track moved files.
3029
3030      The result of a merge can thus be backported as a single commit by
3031      specifying one of the merge parents as base, and thus effectively
3032      grafting the changes from the other side.
3033
3034      It is also possible to collapse multiple changesets and clean up history
3035      by specifying another ancestor as base, much like rebase --collapse
3036      --keep.
3037
3038      The commit message can be tweaked after the fact using commit --amend .
3039
3040      For using non-ancestors as the base to backout changes, see the backout
3041      command and the hidden --parent option.
3042
3043    .. container:: verbose
3044
3045      Examples:
3046
3047      - copy a single change to the stable branch and edit its description::
3048
3049          hg update stable
3050          hg graft --edit 9393
3051
3052      - graft a range of changesets with one exception, updating dates::
3053
3054          hg graft -D "2085::2093 and not 2091"
3055
3056      - continue a graft after resolving conflicts::
3057
3058          hg graft -c
3059
3060      - show the source of a grafted changeset::
3061
3062          hg log --debug -r .
3063
3064      - show revisions sorted by date::
3065
3066          hg log -r "sort(all(), date)"
3067
3068      - backport the result of a merge as a single commit::
3069
3070          hg graft -r 123 --base 123^
3071
3072      - land a feature branch as one changeset::
3073
3074          hg up -cr default
3075          hg graft -r featureX --base "ancestor('featureX', 'default')"
3076
3077    See :hg:`help revisions` for more about specifying revisions.
3078
3079    Returns 0 on successful completion, 1 if there are unresolved files.
3080    """
3081    with repo.wlock():
3082        return _dograft(ui, repo, *revs, **opts)
3083
3084
3085def _dograft(ui, repo, *revs, **opts):
3086    if revs and opts.get('rev'):
3087        ui.warn(
3088            _(
3089                b'warning: inconsistent use of --rev might give unexpected '
3090                b'revision ordering!\n'
3091            )
3092        )
3093
3094    revs = list(revs)
3095    revs.extend(opts.get('rev'))
3096    # a dict of data to be stored in state file
3097    statedata = {}
3098    # list of new nodes created by ongoing graft
3099    statedata[b'newnodes'] = []
3100
3101    cmdutil.resolve_commit_options(ui, opts)
3102
3103    editor = cmdutil.getcommiteditor(editform=b'graft', **opts)
3104
3105    cmdutil.check_at_most_one_arg(opts, 'abort', 'stop', 'continue')
3106
3107    cont = False
3108    if opts.get('no_commit'):
3109        cmdutil.check_incompatible_arguments(
3110            opts,
3111            'no_commit',
3112            ['edit', 'currentuser', 'currentdate', 'log'],
3113        )
3114
3115    graftstate = statemod.cmdstate(repo, b'graftstate')
3116
3117    if opts.get('stop'):
3118        cmdutil.check_incompatible_arguments(
3119            opts,
3120            'stop',
3121            [
3122                'edit',
3123                'log',
3124                'user',
3125                'date',
3126                'currentdate',
3127                'currentuser',
3128                'rev',
3129            ],
3130        )
3131        return _stopgraft(ui, repo, graftstate)
3132    elif opts.get('abort'):
3133        cmdutil.check_incompatible_arguments(
3134            opts,
3135            'abort',
3136            [
3137                'edit',
3138                'log',
3139                'user',
3140                'date',
3141                'currentdate',
3142                'currentuser',
3143                'rev',
3144            ],
3145        )
3146        return cmdutil.abortgraft(ui, repo, graftstate)
3147    elif opts.get('continue'):
3148        cont = True
3149        if revs:
3150            raise error.InputError(_(b"can't specify --continue and revisions"))
3151        # read in unfinished revisions
3152        if graftstate.exists():
3153            statedata = cmdutil.readgraftstate(repo, graftstate)
3154            if statedata.get(b'date'):
3155                opts['date'] = statedata[b'date']
3156            if statedata.get(b'user'):
3157                opts['user'] = statedata[b'user']
3158            if statedata.get(b'log'):
3159                opts['log'] = True
3160            if statedata.get(b'no_commit'):
3161                opts['no_commit'] = statedata.get(b'no_commit')
3162            if statedata.get(b'base'):
3163                opts['base'] = statedata.get(b'base')
3164            nodes = statedata[b'nodes']
3165            revs = [repo[node].rev() for node in nodes]
3166        else:
3167            cmdutil.wrongtooltocontinue(repo, _(b'graft'))
3168    else:
3169        if not revs:
3170            raise error.InputError(_(b'no revisions specified'))
3171        cmdutil.checkunfinished(repo)
3172        cmdutil.bailifchanged(repo)
3173        revs = logcmdutil.revrange(repo, revs)
3174
3175    skipped = set()
3176    basectx = None
3177    if opts.get('base'):
3178        basectx = logcmdutil.revsingle(repo, opts['base'], None)
3179    if basectx is None:
3180        # check for merges
3181        for rev in repo.revs(b'%ld and merge()', revs):
3182            ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
3183            skipped.add(rev)
3184    revs = [r for r in revs if r not in skipped]
3185    if not revs:
3186        return -1
3187    if basectx is not None and len(revs) != 1:
3188        raise error.InputError(_(b'only one revision allowed with --base '))
3189
3190    # Don't check in the --continue case, in effect retaining --force across
3191    # --continues. That's because without --force, any revisions we decided to
3192    # skip would have been filtered out here, so they wouldn't have made their
3193    # way to the graftstate. With --force, any revisions we would have otherwise
3194    # skipped would not have been filtered out, and if they hadn't been applied
3195    # already, they'd have been in the graftstate.
3196    if not (cont or opts.get('force')) and basectx is None:
3197        # check for ancestors of dest branch
3198        ancestors = repo.revs(b'%ld & (::.)', revs)
3199        for rev in ancestors:
3200            ui.warn(_(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev]))
3201
3202        revs = [r for r in revs if r not in ancestors]
3203
3204        if not revs:
3205            return -1
3206
3207        # analyze revs for earlier grafts
3208        ids = {}
3209        for ctx in repo.set(b"%ld", revs):
3210            ids[ctx.hex()] = ctx.rev()
3211            n = ctx.extra().get(b'source')
3212            if n:
3213                ids[n] = ctx.rev()
3214
3215        # check ancestors for earlier grafts
3216        ui.debug(b'scanning for duplicate grafts\n')
3217
3218        # The only changesets we can be sure doesn't contain grafts of any
3219        # revs, are the ones that are common ancestors of *all* revs:
3220        for rev in repo.revs(b'only(%d,ancestor(%ld))', repo[b'.'].rev(), revs):
3221            ctx = repo[rev]
3222            n = ctx.extra().get(b'source')
3223            if n in ids:
3224                try:
3225                    r = repo[n].rev()
3226                except error.RepoLookupError:
3227                    r = None
3228                if r in revs:
3229                    ui.warn(
3230                        _(
3231                            b'skipping revision %d:%s '
3232                            b'(already grafted to %d:%s)\n'
3233                        )
3234                        % (r, repo[r], rev, ctx)
3235                    )
3236                    revs.remove(r)
3237                elif ids[n] in revs:
3238                    if r is None:
3239                        ui.warn(
3240                            _(
3241                                b'skipping already grafted revision %d:%s '
3242                                b'(%d:%s also has unknown origin %s)\n'
3243                            )
3244                            % (ids[n], repo[ids[n]], rev, ctx, n[:12])
3245                        )
3246                    else:
3247                        ui.warn(
3248                            _(
3249                                b'skipping already grafted revision %d:%s '
3250                                b'(%d:%s also has origin %d:%s)\n'
3251                            )
3252                            % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
3253                        )
3254                    revs.remove(ids[n])
3255            elif ctx.hex() in ids:
3256                r = ids[ctx.hex()]
3257                if r in revs:
3258                    ui.warn(
3259                        _(
3260                            b'skipping already grafted revision %d:%s '
3261                            b'(was grafted from %d:%s)\n'
3262                        )
3263                        % (r, repo[r], rev, ctx)
3264                    )
3265                    revs.remove(r)
3266        if not revs:
3267            return -1
3268
3269    if opts.get('no_commit'):
3270        statedata[b'no_commit'] = True
3271    if opts.get('base'):
3272        statedata[b'base'] = opts['base']
3273    for pos, ctx in enumerate(repo.set(b"%ld", revs)):
3274        desc = b'%d:%s "%s"' % (
3275            ctx.rev(),
3276            ctx,
3277            ctx.description().split(b'\n', 1)[0],
3278        )
3279        names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3280        if names:
3281            desc += b' (%s)' % b' '.join(names)
3282        ui.status(_(b'grafting %s\n') % desc)
3283        if opts.get('dry_run'):
3284            continue
3285
3286        source = ctx.extra().get(b'source')
3287        extra = {}
3288        if source:
3289            extra[b'source'] = source
3290            extra[b'intermediate-source'] = ctx.hex()
3291        else:
3292            extra[b'source'] = ctx.hex()
3293        user = ctx.user()
3294        if opts.get('user'):
3295            user = opts['user']
3296            statedata[b'user'] = user
3297        date = ctx.date()
3298        if opts.get('date'):
3299            date = opts['date']
3300            statedata[b'date'] = date
3301        message = ctx.description()
3302        if opts.get('log'):
3303            message += b'\n(grafted from %s)' % ctx.hex()
3304            statedata[b'log'] = True
3305
3306        # we don't merge the first commit when continuing
3307        if not cont:
3308            # perform the graft merge with p1(rev) as 'ancestor'
3309            overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
3310            base = ctx.p1() if basectx is None else basectx
3311            with ui.configoverride(overrides, b'graft'):
3312                stats = mergemod.graft(repo, ctx, base, [b'local', b'graft'])
3313            # report any conflicts
3314            if stats.unresolvedcount > 0:
3315                # write out state for --continue
3316                nodes = [repo[rev].hex() for rev in revs[pos:]]
3317                statedata[b'nodes'] = nodes
3318                stateversion = 1
3319                graftstate.save(stateversion, statedata)
3320                ui.error(_(b"abort: unresolved conflicts, can't continue\n"))
3321                ui.error(_(b"(use 'hg resolve' and 'hg graft --continue')\n"))
3322                return 1
3323        else:
3324            cont = False
3325
3326        # commit if --no-commit is false
3327        if not opts.get('no_commit'):
3328            node = repo.commit(
3329                text=message, user=user, date=date, extra=extra, editor=editor
3330            )
3331            if node is None:
3332                ui.warn(
3333                    _(b'note: graft of %d:%s created no changes to commit\n')
3334                    % (ctx.rev(), ctx)
3335                )
3336            # checking that newnodes exist because old state files won't have it
3337            elif statedata.get(b'newnodes') is not None:
3338                nn = statedata[b'newnodes']
3339                assert isinstance(nn, list)  # list of bytes
3340                nn.append(node)
3341
3342    # remove state when we complete successfully
3343    if not opts.get('dry_run'):
3344        graftstate.delete()
3345
3346    return 0
3347
3348
3349def _stopgraft(ui, repo, graftstate):
3350    """stop the interrupted graft"""
3351    if not graftstate.exists():
3352        raise error.StateError(_(b"no interrupted graft found"))
3353    pctx = repo[b'.']
3354    mergemod.clean_update(pctx)
3355    graftstate.delete()
3356    ui.status(_(b"stopped the interrupted graft\n"))
3357    ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
3358    return 0
3359
3360
3361statemod.addunfinished(
3362    b'graft',
3363    fname=b'graftstate',
3364    clearable=True,
3365    stopflag=True,
3366    continueflag=True,
3367    abortfunc=cmdutil.hgabortgraft,
3368    cmdhint=_(b"use 'hg graft --continue' or 'hg graft --stop' to stop"),
3369)
3370
3371
3372@command(
3373    b'grep',
3374    [
3375        (b'0', b'print0', None, _(b'end fields with NUL')),
3376        (b'', b'all', None, _(b'an alias to --diff (DEPRECATED)')),
3377        (
3378            b'',
3379            b'diff',
3380            None,
3381            _(
3382                b'search revision differences for when the pattern was added '
3383                b'or removed'
3384            ),
3385        ),
3386        (b'a', b'text', None, _(b'treat all files as text')),
3387        (
3388            b'f',
3389            b'follow',
3390            None,
3391            _(
3392                b'follow changeset history,'
3393                b' or file history across copies and renames'
3394            ),
3395        ),
3396        (b'i', b'ignore-case', None, _(b'ignore case when matching')),
3397        (
3398            b'l',
3399            b'files-with-matches',
3400            None,
3401            _(b'print only filenames and revisions that match'),
3402        ),
3403        (b'n', b'line-number', None, _(b'print matching line numbers')),
3404        (
3405            b'r',
3406            b'rev',
3407            [],
3408            _(b'search files changed within revision range'),
3409            _(b'REV'),
3410        ),
3411        (
3412            b'',
3413            b'all-files',
3414            None,
3415            _(
3416                b'include all files in the changeset while grepping (DEPRECATED)'
3417            ),
3418        ),
3419        (b'u', b'user', None, _(b'list the author (long with -v)')),
3420        (b'd', b'date', None, _(b'list the date (short with -q)')),
3421    ]
3422    + formatteropts
3423    + walkopts,
3424    _(b'[--diff] [OPTION]... PATTERN [FILE]...'),
3425    helpcategory=command.CATEGORY_FILE_CONTENTS,
3426    inferrepo=True,
3427    intents={INTENT_READONLY},
3428)
3429def grep(ui, repo, pattern, *pats, **opts):
3430    """search for a pattern in specified files
3431
3432    Search the working directory or revision history for a regular
3433    expression in the specified files for the entire repository.
3434
3435    By default, grep searches the repository files in the working
3436    directory and prints the files where it finds a match. To specify
3437    historical revisions instead of the working directory, use the
3438    --rev flag.
3439
3440    To search instead historical revision differences that contains a
3441    change in match status ("-" for a match that becomes a non-match,
3442    or "+" for a non-match that becomes a match), use the --diff flag.
3443
3444    PATTERN can be any Python (roughly Perl-compatible) regular
3445    expression.
3446
3447    If no FILEs are specified and the --rev flag isn't supplied, all
3448    files in the working directory are searched. When using the --rev
3449    flag and specifying FILEs, use the --follow argument to also
3450    follow the specified FILEs across renames and copies.
3451
3452    .. container:: verbose
3453
3454      Template:
3455
3456      The following keywords are supported in addition to the common template
3457      keywords and functions. See also :hg:`help templates`.
3458
3459      :change:  String. Character denoting insertion ``+`` or removal ``-``.
3460                Available if ``--diff`` is specified.
3461      :lineno:  Integer. Line number of the match.
3462      :path:    String. Repository-absolute path of the file.
3463      :texts:   List of text chunks.
3464
3465      And each entry of ``{texts}`` provides the following sub-keywords.
3466
3467      :matched: Boolean. True if the chunk matches the specified pattern.
3468      :text:    String. Chunk content.
3469
3470      See :hg:`help templates.operators` for the list expansion syntax.
3471
3472    Returns 0 if a match is found, 1 otherwise.
3473
3474    """
3475    cmdutil.check_incompatible_arguments(opts, 'all_files', ['all', 'diff'])
3476    opts = pycompat.byteskwargs(opts)
3477    diff = opts.get(b'all') or opts.get(b'diff')
3478    follow = opts.get(b'follow')
3479    if opts.get(b'all_files') is None and not diff:
3480        opts[b'all_files'] = True
3481    plaingrep = (
3482        opts.get(b'all_files')
3483        and not opts.get(b'rev')
3484        and not opts.get(b'follow')
3485    )
3486    all_files = opts.get(b'all_files')
3487    if plaingrep:
3488        opts[b'rev'] = [b'wdir()']
3489
3490    reflags = re.M
3491    if opts.get(b'ignore_case'):
3492        reflags |= re.I
3493    try:
3494        regexp = util.re.compile(pattern, reflags)
3495    except re.error as inst:
3496        ui.warn(
3497            _(b"grep: invalid match pattern: %s\n")
3498            % stringutil.forcebytestr(inst)
3499        )
3500        return 1
3501    sep, eol = b':', b'\n'
3502    if opts.get(b'print0'):
3503        sep = eol = b'\0'
3504
3505    searcher = grepmod.grepsearcher(
3506        ui, repo, regexp, all_files=all_files, diff=diff, follow=follow
3507    )
3508
3509    getfile = searcher._getfile
3510
3511    uipathfn = scmutil.getuipathfn(repo)
3512
3513    def display(fm, fn, ctx, pstates, states):
3514        rev = scmutil.intrev(ctx)
3515        if fm.isplain():
3516            formatuser = ui.shortuser
3517        else:
3518            formatuser = pycompat.bytestr
3519        if ui.quiet:
3520            datefmt = b'%Y-%m-%d'
3521        else:
3522            datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3523        found = False
3524
3525        @util.cachefunc
3526        def binary():
3527            flog = getfile(fn)
3528            try:
3529                return stringutil.binary(flog.read(ctx.filenode(fn)))
3530            except error.WdirUnsupported:
3531                return ctx[fn].isbinary()
3532
3533        fieldnamemap = {b'linenumber': b'lineno'}
3534        if diff:
3535            iter = grepmod.difflinestates(pstates, states)
3536        else:
3537            iter = [(b'', l) for l in states]
3538        for change, l in iter:
3539            fm.startitem()
3540            fm.context(ctx=ctx)
3541            fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3542            fm.plain(uipathfn(fn), label=b'grep.filename')
3543
3544            cols = [
3545                (b'rev', b'%d', rev, not plaingrep, b''),
3546                (
3547                    b'linenumber',
3548                    b'%d',
3549                    l.linenum,
3550                    opts.get(b'line_number'),
3551                    b'',
3552                ),
3553            ]
3554            if diff:
3555                cols.append(
3556                    (
3557                        b'change',
3558                        b'%s',
3559                        change,
3560                        True,
3561                        b'grep.inserted '
3562                        if change == b'+'
3563                        else b'grep.deleted ',
3564                    )
3565                )
3566            cols.extend(
3567                [
3568                    (
3569                        b'user',
3570                        b'%s',
3571                        formatuser(ctx.user()),
3572                        opts.get(b'user'),
3573                        b'',
3574                    ),
3575                    (
3576                        b'date',
3577                        b'%s',
3578                        fm.formatdate(ctx.date(), datefmt),
3579                        opts.get(b'date'),
3580                        b'',
3581                    ),
3582                ]
3583            )
3584            for name, fmt, data, cond, extra_label in cols:
3585                if cond:
3586                    fm.plain(sep, label=b'grep.sep')
3587                field = fieldnamemap.get(name, name)
3588                label = extra_label + (b'grep.%s' % name)
3589                fm.condwrite(cond, field, fmt, data, label=label)
3590            if not opts.get(b'files_with_matches'):
3591                fm.plain(sep, label=b'grep.sep')
3592                if not opts.get(b'text') and binary():
3593                    fm.plain(_(b" Binary file matches"))
3594                else:
3595                    displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3596            fm.plain(eol)
3597            found = True
3598            if opts.get(b'files_with_matches'):
3599                break
3600        return found
3601
3602    def displaymatches(fm, l):
3603        p = 0
3604        for s, e in l.findpos(regexp):
3605            if p < s:
3606                fm.startitem()
3607                fm.write(b'text', b'%s', l.line[p:s])
3608                fm.data(matched=False)
3609            fm.startitem()
3610            fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3611            fm.data(matched=True)
3612            p = e
3613        if p < len(l.line):
3614            fm.startitem()
3615            fm.write(b'text', b'%s', l.line[p:])
3616            fm.data(matched=False)
3617        fm.end()
3618
3619    found = False
3620
3621    wopts = logcmdutil.walkopts(
3622        pats=pats,
3623        opts=opts,
3624        revspec=opts[b'rev'],
3625        include_pats=opts[b'include'],
3626        exclude_pats=opts[b'exclude'],
3627        follow=follow,
3628        force_changelog_traversal=all_files,
3629        filter_revisions_by_pats=not all_files,
3630    )
3631    revs, makefilematcher = logcmdutil.makewalker(repo, wopts)
3632
3633    ui.pager(b'grep')
3634    fm = ui.formatter(b'grep', opts)
3635    for fn, ctx, pstates, states in searcher.searchfiles(revs, makefilematcher):
3636        r = display(fm, fn, ctx, pstates, states)
3637        found = found or r
3638        if r and not diff and not all_files:
3639            searcher.skipfile(fn, ctx.rev())
3640    fm.end()
3641
3642    return not found
3643
3644
3645@command(
3646    b'heads',
3647    [
3648        (
3649            b'r',
3650            b'rev',
3651            b'',
3652            _(b'show only heads which are descendants of STARTREV'),
3653            _(b'STARTREV'),
3654        ),
3655        (b't', b'topo', False, _(b'show topological heads only')),
3656        (
3657            b'a',
3658            b'active',
3659            False,
3660            _(b'show active branchheads only (DEPRECATED)'),
3661        ),
3662        (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3663    ]
3664    + templateopts,
3665    _(b'[-ct] [-r STARTREV] [REV]...'),
3666    helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3667    intents={INTENT_READONLY},
3668)
3669def heads(ui, repo, *branchrevs, **opts):
3670    """show branch heads
3671
3672    With no arguments, show all open branch heads in the repository.
3673    Branch heads are changesets that have no descendants on the
3674    same branch. They are where development generally takes place and
3675    are the usual targets for update and merge operations.
3676
3677    If one or more REVs are given, only open branch heads on the
3678    branches associated with the specified changesets are shown. This
3679    means that you can use :hg:`heads .` to see the heads on the
3680    currently checked-out branch.
3681
3682    If -c/--closed is specified, also show branch heads marked closed
3683    (see :hg:`commit --close-branch`).
3684
3685    If STARTREV is specified, only those heads that are descendants of
3686    STARTREV will be displayed.
3687
3688    If -t/--topo is specified, named branch mechanics will be ignored and only
3689    topological heads (changesets with no children) will be shown.
3690
3691    Returns 0 if matching heads are found, 1 if not.
3692    """
3693
3694    opts = pycompat.byteskwargs(opts)
3695    start = None
3696    rev = opts.get(b'rev')
3697    if rev:
3698        repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3699        start = logcmdutil.revsingle(repo, rev, None).node()
3700
3701    if opts.get(b'topo'):
3702        heads = [repo[h] for h in repo.heads(start)]
3703    else:
3704        heads = []
3705        for branch in repo.branchmap():
3706            heads += repo.branchheads(branch, start, opts.get(b'closed'))
3707        heads = [repo[h] for h in heads]
3708
3709    if branchrevs:
3710        branches = {
3711            repo[r].branch() for r in logcmdutil.revrange(repo, branchrevs)
3712        }
3713        heads = [h for h in heads if h.branch() in branches]
3714
3715    if opts.get(b'active') and branchrevs:
3716        dagheads = repo.heads(start)
3717        heads = [h for h in heads if h.node() in dagheads]
3718
3719    if branchrevs:
3720        haveheads = {h.branch() for h in heads}
3721        if branches - haveheads:
3722            headless = b', '.join(b for b in branches - haveheads)
3723            msg = _(b'no open branch heads found on branches %s')
3724            if opts.get(b'rev'):
3725                msg += _(b' (started at %s)') % opts[b'rev']
3726            ui.warn((msg + b'\n') % headless)
3727
3728    if not heads:
3729        return 1
3730
3731    ui.pager(b'heads')
3732    heads = sorted(heads, key=lambda x: -(x.rev()))
3733    displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
3734    for ctx in heads:
3735        displayer.show(ctx)
3736    displayer.close()
3737
3738
3739@command(
3740    b'help',
3741    [
3742        (b'e', b'extension', None, _(b'show only help for extensions')),
3743        (b'c', b'command', None, _(b'show only help for commands')),
3744        (b'k', b'keyword', None, _(b'show topics matching keyword')),
3745        (
3746            b's',
3747            b'system',
3748            [],
3749            _(b'show help for specific platform(s)'),
3750            _(b'PLATFORM'),
3751        ),
3752    ],
3753    _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3754    helpcategory=command.CATEGORY_HELP,
3755    norepo=True,
3756    intents={INTENT_READONLY},
3757)
3758def help_(ui, name=None, **opts):
3759    """show help for a given topic or a help overview
3760
3761    With no arguments, print a list of commands with short help messages.
3762
3763    Given a topic, extension, or command name, print help for that
3764    topic.
3765
3766    Returns 0 if successful.
3767    """
3768
3769    keep = opts.get('system') or []
3770    if len(keep) == 0:
3771        if pycompat.sysplatform.startswith(b'win'):
3772            keep.append(b'windows')
3773        elif pycompat.sysplatform == b'OpenVMS':
3774            keep.append(b'vms')
3775        elif pycompat.sysplatform == b'plan9':
3776            keep.append(b'plan9')
3777        else:
3778            keep.append(b'unix')
3779            keep.append(pycompat.sysplatform.lower())
3780    if ui.verbose:
3781        keep.append(b'verbose')
3782
3783    commands = sys.modules[__name__]
3784    formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3785    ui.pager(b'help')
3786    ui.write(formatted)
3787
3788
3789@command(
3790    b'identify|id',
3791    [
3792        (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3793        (b'n', b'num', None, _(b'show local revision number')),
3794        (b'i', b'id', None, _(b'show global revision id')),
3795        (b'b', b'branch', None, _(b'show branch')),
3796        (b't', b'tags', None, _(b'show tags')),
3797        (b'B', b'bookmarks', None, _(b'show bookmarks')),
3798    ]
3799    + remoteopts
3800    + formatteropts,
3801    _(b'[-nibtB] [-r REV] [SOURCE]'),
3802    helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3803    optionalrepo=True,
3804    intents={INTENT_READONLY},
3805)
3806def identify(
3807    ui,
3808    repo,
3809    source=None,
3810    rev=None,
3811    num=None,
3812    id=None,
3813    branch=None,
3814    tags=None,
3815    bookmarks=None,
3816    **opts
3817):
3818    """identify the working directory or specified revision
3819
3820    Print a summary identifying the repository state at REV using one or
3821    two parent hash identifiers, followed by a "+" if the working
3822    directory has uncommitted changes, the branch name (if not default),
3823    a list of tags, and a list of bookmarks.
3824
3825    When REV is not given, print a summary of the current state of the
3826    repository including the working directory. Specify -r. to get information
3827    of the working directory parent without scanning uncommitted changes.
3828
3829    Specifying a path to a repository root or Mercurial bundle will
3830    cause lookup to operate on that repository/bundle.
3831
3832    .. container:: verbose
3833
3834      Template:
3835
3836      The following keywords are supported in addition to the common template
3837      keywords and functions. See also :hg:`help templates`.
3838
3839      :dirty:   String. Character ``+`` denoting if the working directory has
3840                uncommitted changes.
3841      :id:      String. One or two nodes, optionally followed by ``+``.
3842      :parents: List of strings. Parent nodes of the changeset.
3843
3844      Examples:
3845
3846      - generate a build identifier for the working directory::
3847
3848          hg id --id > build-id.dat
3849
3850      - find the revision corresponding to a tag::
3851
3852          hg id -n -r 1.3
3853
3854      - check the most recent revision of a remote repository::
3855
3856          hg id -r tip https://www.mercurial-scm.org/repo/hg/
3857
3858    See :hg:`log` for generating more information about specific revisions,
3859    including full hash identifiers.
3860
3861    Returns 0 if successful.
3862    """
3863
3864    opts = pycompat.byteskwargs(opts)
3865    if not repo and not source:
3866        raise error.InputError(
3867            _(b"there is no Mercurial repository here (.hg not found)")
3868        )
3869
3870    default = not (num or id or branch or tags or bookmarks)
3871    output = []
3872    revs = []
3873
3874    peer = None
3875    try:
3876        if source:
3877            source, branches = urlutil.get_unique_pull_path(
3878                b'identify', repo, ui, source
3879            )
3880            # only pass ui when no repo
3881            peer = hg.peer(repo or ui, opts, source)
3882            repo = peer.local()
3883            revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3884
3885        fm = ui.formatter(b'identify', opts)
3886        fm.startitem()
3887
3888        if not repo:
3889            if num or branch or tags:
3890                raise error.InputError(
3891                    _(b"can't query remote revision number, branch, or tags")
3892                )
3893            if not rev and revs:
3894                rev = revs[0]
3895            if not rev:
3896                rev = b"tip"
3897
3898            remoterev = peer.lookup(rev)
3899            hexrev = fm.hexfunc(remoterev)
3900            if default or id:
3901                output = [hexrev]
3902            fm.data(id=hexrev)
3903
3904            @util.cachefunc
3905            def getbms():
3906                bms = []
3907
3908                if b'bookmarks' in peer.listkeys(b'namespaces'):
3909                    hexremoterev = hex(remoterev)
3910                    bms = [
3911                        bm
3912                        for bm, bmr in pycompat.iteritems(
3913                            peer.listkeys(b'bookmarks')
3914                        )
3915                        if bmr == hexremoterev
3916                    ]
3917
3918                return sorted(bms)
3919
3920            if fm.isplain():
3921                if bookmarks:
3922                    output.extend(getbms())
3923                elif default and not ui.quiet:
3924                    # multiple bookmarks for a single parent separated by '/'
3925                    bm = b'/'.join(getbms())
3926                    if bm:
3927                        output.append(bm)
3928            else:
3929                fm.data(node=hex(remoterev))
3930                if bookmarks or b'bookmarks' in fm.datahint():
3931                    fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3932        else:
3933            if rev:
3934                repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3935            ctx = logcmdutil.revsingle(repo, rev, None)
3936
3937            if ctx.rev() is None:
3938                ctx = repo[None]
3939                parents = ctx.parents()
3940                taglist = []
3941                for p in parents:
3942                    taglist.extend(p.tags())
3943
3944                dirty = b""
3945                if ctx.dirty(missing=True, merge=False, branch=False):
3946                    dirty = b'+'
3947                fm.data(dirty=dirty)
3948
3949                hexoutput = [fm.hexfunc(p.node()) for p in parents]
3950                if default or id:
3951                    output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
3952                fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
3953
3954                if num:
3955                    numoutput = [b"%d" % p.rev() for p in parents]
3956                    output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
3957
3958                fm.data(
3959                    parents=fm.formatlist(
3960                        [fm.hexfunc(p.node()) for p in parents], name=b'node'
3961                    )
3962                )
3963            else:
3964                hexoutput = fm.hexfunc(ctx.node())
3965                if default or id:
3966                    output = [hexoutput]
3967                fm.data(id=hexoutput)
3968
3969                if num:
3970                    output.append(pycompat.bytestr(ctx.rev()))
3971                taglist = ctx.tags()
3972
3973            if default and not ui.quiet:
3974                b = ctx.branch()
3975                if b != b'default':
3976                    output.append(b"(%s)" % b)
3977
3978                # multiple tags for a single parent separated by '/'
3979                t = b'/'.join(taglist)
3980                if t:
3981                    output.append(t)
3982
3983                # multiple bookmarks for a single parent separated by '/'
3984                bm = b'/'.join(ctx.bookmarks())
3985                if bm:
3986                    output.append(bm)
3987            else:
3988                if branch:
3989                    output.append(ctx.branch())
3990
3991                if tags:
3992                    output.extend(taglist)
3993
3994                if bookmarks:
3995                    output.extend(ctx.bookmarks())
3996
3997            fm.data(node=ctx.hex())
3998            fm.data(branch=ctx.branch())
3999            fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
4000            fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
4001            fm.context(ctx=ctx)
4002
4003        fm.plain(b"%s\n" % b' '.join(output))
4004        fm.end()
4005    finally:
4006        if peer:
4007            peer.close()
4008
4009
4010@command(
4011    b'import|patch',
4012    [
4013        (
4014            b'p',
4015            b'strip',
4016            1,
4017            _(
4018                b'directory strip option for patch. This has the same '
4019                b'meaning as the corresponding patch option'
4020            ),
4021            _(b'NUM'),
4022        ),
4023        (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
4024        (b'', b'secret', None, _(b'use the secret phase for committing')),
4025        (b'e', b'edit', False, _(b'invoke editor on commit messages')),
4026        (
4027            b'f',
4028            b'force',
4029            None,
4030            _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
4031        ),
4032        (
4033            b'',
4034            b'no-commit',
4035            None,
4036            _(b"don't commit, just update the working directory"),
4037        ),
4038        (
4039            b'',
4040            b'bypass',
4041            None,
4042            _(b"apply patch without touching the working directory"),
4043        ),
4044        (b'', b'partial', None, _(b'commit even if some hunks fail')),
4045        (b'', b'exact', None, _(b'abort if patch would apply lossily')),
4046        (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
4047        (
4048            b'',
4049            b'import-branch',
4050            None,
4051            _(b'use any branch information in patch (implied by --exact)'),
4052        ),
4053    ]
4054    + commitopts
4055    + commitopts2
4056    + similarityopts,
4057    _(b'[OPTION]... PATCH...'),
4058    helpcategory=command.CATEGORY_IMPORT_EXPORT,
4059)
4060def import_(ui, repo, patch1=None, *patches, **opts):
4061    """import an ordered set of patches
4062
4063    Import a list of patches and commit them individually (unless
4064    --no-commit is specified).
4065
4066    To read a patch from standard input (stdin), use "-" as the patch
4067    name. If a URL is specified, the patch will be downloaded from
4068    there.
4069
4070    Import first applies changes to the working directory (unless
4071    --bypass is specified), import will abort if there are outstanding
4072    changes.
4073
4074    Use --bypass to apply and commit patches directly to the
4075    repository, without affecting the working directory. Without
4076    --exact, patches will be applied on top of the working directory
4077    parent revision.
4078
4079    You can import a patch straight from a mail message. Even patches
4080    as attachments work (to use the body part, it must have type
4081    text/plain or text/x-patch). From and Subject headers of email
4082    message are used as default committer and commit message. All
4083    text/plain body parts before first diff are added to the commit
4084    message.
4085
4086    If the imported patch was generated by :hg:`export`, user and
4087    description from patch override values from message headers and
4088    body. Values given on command line with -m/--message and -u/--user
4089    override these.
4090
4091    If --exact is specified, import will set the working directory to
4092    the parent of each patch before applying it, and will abort if the
4093    resulting changeset has a different ID than the one recorded in
4094    the patch. This will guard against various ways that portable
4095    patch formats and mail systems might fail to transfer Mercurial
4096    data or metadata. See :hg:`bundle` for lossless transmission.
4097
4098    Use --partial to ensure a changeset will be created from the patch
4099    even if some hunks fail to apply. Hunks that fail to apply will be
4100    written to a <target-file>.rej file. Conflicts can then be resolved
4101    by hand before :hg:`commit --amend` is run to update the created
4102    changeset. This flag exists to let people import patches that
4103    partially apply without losing the associated metadata (author,
4104    date, description, ...).
4105
4106    .. note::
4107
4108       When no hunks apply cleanly, :hg:`import --partial` will create
4109       an empty changeset, importing only the patch metadata.
4110
4111    With -s/--similarity, hg will attempt to discover renames and
4112    copies in the patch in the same way as :hg:`addremove`.
4113
4114    It is possible to use external patch programs to perform the patch
4115    by setting the ``ui.patch`` configuration option. For the default
4116    internal tool, the fuzz can also be configured via ``patch.fuzz``.
4117    See :hg:`help config` for more information about configuration
4118    files and how to use these options.
4119
4120    See :hg:`help dates` for a list of formats valid for -d/--date.
4121
4122    .. container:: verbose
4123
4124      Examples:
4125
4126      - import a traditional patch from a website and detect renames::
4127
4128          hg import -s 80 http://example.com/bugfix.patch
4129
4130      - import a changeset from an hgweb server::
4131
4132          hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
4133
4134      - import all the patches in an Unix-style mbox::
4135
4136          hg import incoming-patches.mbox
4137
4138      - import patches from stdin::
4139
4140          hg import -
4141
4142      - attempt to exactly restore an exported changeset (not always
4143        possible)::
4144
4145          hg import --exact proposed-fix.patch
4146
4147      - use an external tool to apply a patch which is too fuzzy for
4148        the default internal tool.
4149
4150          hg import --config ui.patch="patch --merge" fuzzy.patch
4151
4152      - change the default fuzzing from 2 to a less strict 7
4153
4154          hg import --config ui.fuzz=7 fuzz.patch
4155
4156    Returns 0 on success, 1 on partial success (see --partial).
4157    """
4158
4159    cmdutil.check_incompatible_arguments(
4160        opts, 'no_commit', ['bypass', 'secret']
4161    )
4162    cmdutil.check_incompatible_arguments(opts, 'exact', ['edit', 'prefix'])
4163    opts = pycompat.byteskwargs(opts)
4164    if not patch1:
4165        raise error.InputError(_(b'need at least one patch to import'))
4166
4167    patches = (patch1,) + patches
4168
4169    date = opts.get(b'date')
4170    if date:
4171        opts[b'date'] = dateutil.parsedate(date)
4172
4173    exact = opts.get(b'exact')
4174    update = not opts.get(b'bypass')
4175    try:
4176        sim = float(opts.get(b'similarity') or 0)
4177    except ValueError:
4178        raise error.InputError(_(b'similarity must be a number'))
4179    if sim < 0 or sim > 100:
4180        raise error.InputError(_(b'similarity must be between 0 and 100'))
4181    if sim and not update:
4182        raise error.InputError(_(b'cannot use --similarity with --bypass'))
4183
4184    base = opts[b"base"]
4185    msgs = []
4186    ret = 0
4187
4188    with repo.wlock():
4189        if update:
4190            cmdutil.checkunfinished(repo)
4191            if exact or not opts.get(b'force'):
4192                cmdutil.bailifchanged(repo)
4193
4194        if not opts.get(b'no_commit'):
4195            lock = repo.lock
4196            tr = lambda: repo.transaction(b'import')
4197            dsguard = util.nullcontextmanager
4198        else:
4199            lock = util.nullcontextmanager
4200            tr = util.nullcontextmanager
4201            dsguard = lambda: dirstateguard.dirstateguard(repo, b'import')
4202        with lock(), tr(), dsguard():
4203            parents = repo[None].parents()
4204            for patchurl in patches:
4205                if patchurl == b'-':
4206                    ui.status(_(b'applying patch from stdin\n'))
4207                    patchfile = ui.fin
4208                    patchurl = b'stdin'  # for error message
4209                else:
4210                    patchurl = os.path.join(base, patchurl)
4211                    ui.status(_(b'applying %s\n') % patchurl)
4212                    patchfile = hg.openpath(ui, patchurl, sendaccept=False)
4213
4214                haspatch = False
4215                for hunk in patch.split(patchfile):
4216                    with patch.extract(ui, hunk) as patchdata:
4217                        msg, node, rej = cmdutil.tryimportone(
4218                            ui, repo, patchdata, parents, opts, msgs, hg.clean
4219                        )
4220                    if msg:
4221                        haspatch = True
4222                        ui.note(msg + b'\n')
4223                    if update or exact:
4224                        parents = repo[None].parents()
4225                    else:
4226                        parents = [repo[node]]
4227                    if rej:
4228                        ui.write_err(_(b"patch applied partially\n"))
4229                        ui.write_err(
4230                            _(
4231                                b"(fix the .rej files and run "
4232                                b"`hg commit --amend`)\n"
4233                            )
4234                        )
4235                        ret = 1
4236                        break
4237
4238                if not haspatch:
4239                    raise error.InputError(_(b'%s: no diffs found') % patchurl)
4240
4241            if msgs:
4242                repo.savecommitmessage(b'\n* * *\n'.join(msgs))
4243        return ret
4244
4245
4246@command(
4247    b'incoming|in',
4248    [
4249        (
4250            b'f',
4251            b'force',
4252            None,
4253            _(b'run even if remote repository is unrelated'),
4254        ),
4255        (b'n', b'newest-first', None, _(b'show newest record first')),
4256        (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4257        (
4258            b'r',
4259            b'rev',
4260            [],
4261            _(b'a remote changeset intended to be added'),
4262            _(b'REV'),
4263        ),
4264        (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4265        (
4266            b'b',
4267            b'branch',
4268            [],
4269            _(b'a specific branch you would like to pull'),
4270            _(b'BRANCH'),
4271        ),
4272    ]
4273    + logopts
4274    + remoteopts
4275    + subrepoopts,
4276    _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4277    helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4278)
4279def incoming(ui, repo, source=b"default", **opts):
4280    """show new changesets found in source
4281
4282    Show new changesets found in the specified path/URL or the default
4283    pull location. These are the changesets that would have been pulled
4284    by :hg:`pull` at the time you issued this command.
4285
4286    See pull for valid source format details.
4287
4288    .. container:: verbose
4289
4290      With -B/--bookmarks, the result of bookmark comparison between
4291      local and remote repositories is displayed. With -v/--verbose,
4292      status is also displayed for each bookmark like below::
4293
4294        BM1               01234567890a added
4295        BM2               1234567890ab advanced
4296        BM3               234567890abc diverged
4297        BM4               34567890abcd changed
4298
4299      The action taken locally when pulling depends on the
4300      status of each bookmark:
4301
4302      :``added``: pull will create it
4303      :``advanced``: pull will update it
4304      :``diverged``: pull will create a divergent bookmark
4305      :``changed``: result depends on remote changesets
4306
4307      From the point of view of pulling behavior, bookmark
4308      existing only in the remote repository are treated as ``added``,
4309      even if it is in fact locally deleted.
4310
4311    .. container:: verbose
4312
4313      For remote repository, using --bundle avoids downloading the
4314      changesets twice if the incoming is followed by a pull.
4315
4316      Examples:
4317
4318      - show incoming changes with patches and full description::
4319
4320          hg incoming -vp
4321
4322      - show incoming changes excluding merges, store a bundle::
4323
4324          hg in -vpM --bundle incoming.hg
4325          hg pull incoming.hg
4326
4327      - briefly list changes inside a bundle::
4328
4329          hg in changes.hg -T "{desc|firstline}\\n"
4330
4331    Returns 0 if there are incoming changes, 1 otherwise.
4332    """
4333    opts = pycompat.byteskwargs(opts)
4334    if opts.get(b'graph'):
4335        logcmdutil.checkunsupportedgraphflags([], opts)
4336
4337        def display(other, chlist, displayer):
4338            revdag = logcmdutil.graphrevs(other, chlist, opts)
4339            logcmdutil.displaygraph(
4340                ui, repo, revdag, displayer, graphmod.asciiedges
4341            )
4342
4343        hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4344        return 0
4345
4346    cmdutil.check_incompatible_arguments(opts, b'subrepos', [b'bundle'])
4347
4348    if opts.get(b'bookmarks'):
4349        srcs = urlutil.get_pull_paths(repo, ui, [source])
4350        for path in srcs:
4351            source, branches = urlutil.parseurl(
4352                path.rawloc, opts.get(b'branch')
4353            )
4354            other = hg.peer(repo, opts, source)
4355            try:
4356                if b'bookmarks' not in other.listkeys(b'namespaces'):
4357                    ui.warn(_(b"remote doesn't support bookmarks\n"))
4358                    return 0
4359                ui.pager(b'incoming')
4360                ui.status(
4361                    _(b'comparing with %s\n') % urlutil.hidepassword(source)
4362                )
4363                return bookmarks.incoming(
4364                    ui, repo, other, mode=path.bookmarks_mode
4365                )
4366            finally:
4367                other.close()
4368
4369    return hg.incoming(ui, repo, source, opts)
4370
4371
4372@command(
4373    b'init',
4374    remoteopts,
4375    _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4376    helpcategory=command.CATEGORY_REPO_CREATION,
4377    helpbasic=True,
4378    norepo=True,
4379)
4380def init(ui, dest=b".", **opts):
4381    """create a new repository in the given directory
4382
4383    Initialize a new repository in the given directory. If the given
4384    directory does not exist, it will be created.
4385
4386    If no directory is given, the current directory is used.
4387
4388    It is possible to specify an ``ssh://`` URL as the destination.
4389    See :hg:`help urls` for more information.
4390
4391    Returns 0 on success.
4392    """
4393    opts = pycompat.byteskwargs(opts)
4394    path = urlutil.get_clone_path(ui, dest)[1]
4395    peer = hg.peer(ui, opts, path, create=True)
4396    peer.close()
4397
4398
4399@command(
4400    b'locate',
4401    [
4402        (
4403            b'r',
4404            b'rev',
4405            b'',
4406            _(b'search the repository as it is in REV'),
4407            _(b'REV'),
4408        ),
4409        (
4410            b'0',
4411            b'print0',
4412            None,
4413            _(b'end filenames with NUL, for use with xargs'),
4414        ),
4415        (
4416            b'f',
4417            b'fullpath',
4418            None,
4419            _(b'print complete paths from the filesystem root'),
4420        ),
4421    ]
4422    + walkopts,
4423    _(b'[OPTION]... [PATTERN]...'),
4424    helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4425)
4426def locate(ui, repo, *pats, **opts):
4427    """locate files matching specific patterns (DEPRECATED)
4428
4429    Print files under Mercurial control in the working directory whose
4430    names match the given patterns.
4431
4432    By default, this command searches all directories in the working
4433    directory. To search just the current directory and its
4434    subdirectories, use "--include .".
4435
4436    If no patterns are given to match, this command prints the names
4437    of all files under Mercurial control in the working directory.
4438
4439    If you want to feed the output of this command into the "xargs"
4440    command, use the -0 option to both this command and "xargs". This
4441    will avoid the problem of "xargs" treating single filenames that
4442    contain whitespace as multiple filenames.
4443
4444    See :hg:`help files` for a more versatile command.
4445
4446    Returns 0 if a match is found, 1 otherwise.
4447    """
4448    opts = pycompat.byteskwargs(opts)
4449    if opts.get(b'print0'):
4450        end = b'\0'
4451    else:
4452        end = b'\n'
4453    ctx = logcmdutil.revsingle(repo, opts.get(b'rev'), None)
4454
4455    ret = 1
4456    m = scmutil.match(
4457        ctx, pats, opts, default=b'relglob', badfn=lambda x, y: False
4458    )
4459
4460    ui.pager(b'locate')
4461    if ctx.rev() is None:
4462        # When run on the working copy, "locate" includes removed files, so
4463        # we get the list of files from the dirstate.
4464        filesgen = sorted(repo.dirstate.matches(m))
4465    else:
4466        filesgen = ctx.matches(m)
4467    uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4468    for abs in filesgen:
4469        if opts.get(b'fullpath'):
4470            ui.write(repo.wjoin(abs), end)
4471        else:
4472            ui.write(uipathfn(abs), end)
4473        ret = 0
4474
4475    return ret
4476
4477
4478@command(
4479    b'log|history',
4480    [
4481        (
4482            b'f',
4483            b'follow',
4484            None,
4485            _(
4486                b'follow changeset history, or file history across copies and renames'
4487            ),
4488        ),
4489        (
4490            b'',
4491            b'follow-first',
4492            None,
4493            _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4494        ),
4495        (
4496            b'd',
4497            b'date',
4498            b'',
4499            _(b'show revisions matching date spec'),
4500            _(b'DATE'),
4501        ),
4502        (b'C', b'copies', None, _(b'show copied files')),
4503        (
4504            b'k',
4505            b'keyword',
4506            [],
4507            _(b'do case-insensitive search for a given text'),
4508            _(b'TEXT'),
4509        ),
4510        (
4511            b'r',
4512            b'rev',
4513            [],
4514            _(b'revisions to select or follow from'),
4515            _(b'REV'),
4516        ),
4517        (
4518            b'L',
4519            b'line-range',
4520            [],
4521            _(b'follow line range of specified file (EXPERIMENTAL)'),
4522            _(b'FILE,RANGE'),
4523        ),
4524        (
4525            b'',
4526            b'removed',
4527            None,
4528            _(b'include revisions where files were removed'),
4529        ),
4530        (
4531            b'm',
4532            b'only-merges',
4533            None,
4534            _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4535        ),
4536        (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4537        (
4538            b'',
4539            b'only-branch',
4540            [],
4541            _(
4542                b'show only changesets within the given named branch (DEPRECATED)'
4543            ),
4544            _(b'BRANCH'),
4545        ),
4546        (
4547            b'b',
4548            b'branch',
4549            [],
4550            _(b'show changesets within the given named branch'),
4551            _(b'BRANCH'),
4552        ),
4553        (
4554            b'B',
4555            b'bookmark',
4556            [],
4557            _(b"show changesets within the given bookmark"),
4558            _(b'BOOKMARK'),
4559        ),
4560        (
4561            b'P',
4562            b'prune',
4563            [],
4564            _(b'do not display revision or any of its ancestors'),
4565            _(b'REV'),
4566        ),
4567    ]
4568    + logopts
4569    + walkopts,
4570    _(b'[OPTION]... [FILE]'),
4571    helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4572    helpbasic=True,
4573    inferrepo=True,
4574    intents={INTENT_READONLY},
4575)
4576def log(ui, repo, *pats, **opts):
4577    """show revision history of entire repository or files
4578
4579    Print the revision history of the specified files or the entire
4580    project.
4581
4582    If no revision range is specified, the default is ``tip:0`` unless
4583    --follow is set.
4584
4585    File history is shown without following rename or copy history of
4586    files. Use -f/--follow with a filename to follow history across
4587    renames and copies. --follow without a filename will only show
4588    ancestors of the starting revisions. The starting revisions can be
4589    specified by -r/--rev, which default to the working directory parent.
4590
4591    By default this command prints revision number and changeset id,
4592    tags, non-trivial parents, user, date and time, and a summary for
4593    each commit. When the -v/--verbose switch is used, the list of
4594    changed files and full commit message are shown.
4595
4596    With --graph the revisions are shown as an ASCII art DAG with the most
4597    recent changeset at the top.
4598    'o' is a changeset, '@' is a working directory parent, '%' is a changeset
4599    involved in an unresolved merge conflict, '_' closes a branch,
4600    'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4601    changeset from the lines below is a parent of the 'o' merge on the same
4602    line.
4603    Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4604    of a '|' indicates one or more revisions in a path are omitted.
4605
4606    .. container:: verbose
4607
4608       Use -L/--line-range FILE,M:N options to follow the history of lines
4609       from M to N in FILE. With -p/--patch only diff hunks affecting
4610       specified line range will be shown. This option requires --follow;
4611       it can be specified multiple times. Currently, this option is not
4612       compatible with --graph. This option is experimental.
4613
4614    .. note::
4615
4616       :hg:`log --patch` may generate unexpected diff output for merge
4617       changesets, as it will only compare the merge changeset against
4618       its first parent. Also, only files different from BOTH parents
4619       will appear in files:.
4620
4621    .. note::
4622
4623       For performance reasons, :hg:`log FILE` may omit duplicate changes
4624       made on branches and will not show removals or mode changes. To
4625       see all such changes, use the --removed switch.
4626
4627    .. container:: verbose
4628
4629       .. note::
4630
4631          The history resulting from -L/--line-range options depends on diff
4632          options; for instance if white-spaces are ignored, respective changes
4633          with only white-spaces in specified line range will not be listed.
4634
4635    .. container:: verbose
4636
4637      Some examples:
4638
4639      - changesets with full descriptions and file lists::
4640
4641          hg log -v
4642
4643      - changesets ancestral to the working directory::
4644
4645          hg log -f
4646
4647      - last 10 commits on the current branch::
4648
4649          hg log -l 10 -b .
4650
4651      - changesets showing all modifications of a file, including removals::
4652
4653          hg log --removed file.c
4654
4655      - all changesets that touch a directory, with diffs, excluding merges::
4656
4657          hg log -Mp lib/
4658
4659      - all revision numbers that match a keyword::
4660
4661          hg log -k bug --template "{rev}\\n"
4662
4663      - the full hash identifier of the working directory parent::
4664
4665          hg log -r . --template "{node}\\n"
4666
4667      - list available log templates::
4668
4669          hg log -T list
4670
4671      - check if a given changeset is included in a tagged release::
4672
4673          hg log -r "a21ccf and ancestor(1.9)"
4674
4675      - find all changesets by some user in a date range::
4676
4677          hg log -k alice -d "may 2008 to jul 2008"
4678
4679      - summary of all changesets after the last tag::
4680
4681          hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4682
4683      - changesets touching lines 13 to 23 for file.c::
4684
4685          hg log -L file.c,13:23
4686
4687      - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4688        main.c with patch::
4689
4690          hg log -L file.c,13:23 -L main.c,2:6 -p
4691
4692    See :hg:`help dates` for a list of formats valid for -d/--date.
4693
4694    See :hg:`help revisions` for more about specifying and ordering
4695    revisions.
4696
4697    See :hg:`help templates` for more about pre-packaged styles and
4698    specifying custom templates. The default template used by the log
4699    command can be customized via the ``command-templates.log`` configuration
4700    setting.
4701
4702    Returns 0 on success.
4703
4704    """
4705    opts = pycompat.byteskwargs(opts)
4706    linerange = opts.get(b'line_range')
4707
4708    if linerange and not opts.get(b'follow'):
4709        raise error.InputError(_(b'--line-range requires --follow'))
4710
4711    if linerange and pats:
4712        # TODO: take pats as patterns with no line-range filter
4713        raise error.InputError(
4714            _(b'FILE arguments are not compatible with --line-range option')
4715        )
4716
4717    repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4718    walk_opts = logcmdutil.parseopts(ui, pats, opts)
4719    revs, differ = logcmdutil.getrevs(repo, walk_opts)
4720    if linerange:
4721        # TODO: should follow file history from logcmdutil._initialrevs(),
4722        # then filter the result by logcmdutil._makerevset() and --limit
4723        revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4724
4725    getcopies = None
4726    if opts.get(b'copies'):
4727        endrev = None
4728        if revs:
4729            endrev = revs.max() + 1
4730        getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4731
4732    ui.pager(b'log')
4733    displayer = logcmdutil.changesetdisplayer(
4734        ui, repo, opts, differ, buffered=True
4735    )
4736    if opts.get(b'graph'):
4737        displayfn = logcmdutil.displaygraphrevs
4738    else:
4739        displayfn = logcmdutil.displayrevs
4740    displayfn(ui, repo, revs, displayer, getcopies)
4741
4742
4743@command(
4744    b'manifest',
4745    [
4746        (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4747        (b'', b'all', False, _(b"list files from all revisions")),
4748    ]
4749    + formatteropts,
4750    _(b'[-r REV]'),
4751    helpcategory=command.CATEGORY_MAINTENANCE,
4752    intents={INTENT_READONLY},
4753)
4754def manifest(ui, repo, node=None, rev=None, **opts):
4755    """output the current or given revision of the project manifest
4756
4757    Print a list of version controlled files for the given revision.
4758    If no revision is given, the first parent of the working directory
4759    is used, or the null revision if no revision is checked out.
4760
4761    With -v, print file permissions, symlink and executable bits.
4762    With --debug, print file revision hashes.
4763
4764    If option --all is specified, the list of all files from all revisions
4765    is printed. This includes deleted and renamed files.
4766
4767    Returns 0 on success.
4768    """
4769    opts = pycompat.byteskwargs(opts)
4770    fm = ui.formatter(b'manifest', opts)
4771
4772    if opts.get(b'all'):
4773        if rev or node:
4774            raise error.InputError(_(b"can't specify a revision with --all"))
4775
4776        res = set()
4777        for rev in repo:
4778            ctx = repo[rev]
4779            res |= set(ctx.files())
4780
4781        ui.pager(b'manifest')
4782        for f in sorted(res):
4783            fm.startitem()
4784            fm.write(b"path", b'%s\n', f)
4785        fm.end()
4786        return
4787
4788    if rev and node:
4789        raise error.InputError(_(b"please specify just one revision"))
4790
4791    if not node:
4792        node = rev
4793
4794    char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4795    mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4796    if node:
4797        repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4798    ctx = logcmdutil.revsingle(repo, node)
4799    mf = ctx.manifest()
4800    ui.pager(b'manifest')
4801    for f in ctx:
4802        fm.startitem()
4803        fm.context(ctx=ctx)
4804        fl = ctx[f].flags()
4805        fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4806        fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4807        fm.write(b'path', b'%s\n', f)
4808    fm.end()
4809
4810
4811@command(
4812    b'merge',
4813    [
4814        (
4815            b'f',
4816            b'force',
4817            None,
4818            _(b'force a merge including outstanding changes (DEPRECATED)'),
4819        ),
4820        (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4821        (
4822            b'P',
4823            b'preview',
4824            None,
4825            _(b'review revisions to merge (no merge is performed)'),
4826        ),
4827        (b'', b'abort', None, _(b'abort the ongoing merge')),
4828    ]
4829    + mergetoolopts,
4830    _(b'[-P] [[-r] REV]'),
4831    helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4832    helpbasic=True,
4833)
4834def merge(ui, repo, node=None, **opts):
4835    """merge another revision into working directory
4836
4837    The current working directory is updated with all changes made in
4838    the requested revision since the last common predecessor revision.
4839
4840    Files that changed between either parent are marked as changed for
4841    the next commit and a commit must be performed before any further
4842    updates to the repository are allowed. The next commit will have
4843    two parents.
4844
4845    ``--tool`` can be used to specify the merge tool used for file
4846    merges. It overrides the HGMERGE environment variable and your
4847    configuration files. See :hg:`help merge-tools` for options.
4848
4849    If no revision is specified, the working directory's parent is a
4850    head revision, and the current branch contains exactly one other
4851    head, the other head is merged with by default. Otherwise, an
4852    explicit revision with which to merge must be provided.
4853
4854    See :hg:`help resolve` for information on handling file conflicts.
4855
4856    To undo an uncommitted merge, use :hg:`merge --abort` which
4857    will check out a clean copy of the original merge parent, losing
4858    all changes.
4859
4860    Returns 0 on success, 1 if there are unresolved files.
4861    """
4862
4863    opts = pycompat.byteskwargs(opts)
4864    abort = opts.get(b'abort')
4865    if abort and repo.dirstate.p2() == repo.nullid:
4866        cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4867    cmdutil.check_incompatible_arguments(opts, b'abort', [b'rev', b'preview'])
4868    if abort:
4869        state = cmdutil.getunfinishedstate(repo)
4870        if state and state._opname != b'merge':
4871            raise error.StateError(
4872                _(b'cannot abort merge with %s in progress') % (state._opname),
4873                hint=state.hint(),
4874            )
4875        if node:
4876            raise error.InputError(_(b"cannot specify a node with --abort"))
4877        return hg.abortmerge(repo.ui, repo)
4878
4879    if opts.get(b'rev') and node:
4880        raise error.InputError(_(b"please specify just one revision"))
4881    if not node:
4882        node = opts.get(b'rev')
4883
4884    if node:
4885        ctx = logcmdutil.revsingle(repo, node)
4886    else:
4887        if ui.configbool(b'commands', b'merge.require-rev'):
4888            raise error.InputError(
4889                _(
4890                    b'configuration requires specifying revision to merge '
4891                    b'with'
4892                )
4893            )
4894        ctx = repo[destutil.destmerge(repo)]
4895
4896    if ctx.node() is None:
4897        raise error.InputError(
4898            _(b'merging with the working copy has no effect')
4899        )
4900
4901    if opts.get(b'preview'):
4902        # find nodes that are ancestors of p2 but not of p1
4903        p1 = repo[b'.'].node()
4904        p2 = ctx.node()
4905        nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4906
4907        displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4908        for node in nodes:
4909            displayer.show(repo[node])
4910        displayer.close()
4911        return 0
4912
4913    # ui.forcemerge is an internal variable, do not document
4914    overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
4915    with ui.configoverride(overrides, b'merge'):
4916        force = opts.get(b'force')
4917        labels = [b'working copy', b'merge rev']
4918        return hg.merge(ctx, force=force, labels=labels)
4919
4920
4921statemod.addunfinished(
4922    b'merge',
4923    fname=None,
4924    clearable=True,
4925    allowcommit=True,
4926    cmdmsg=_(b'outstanding uncommitted merge'),
4927    abortfunc=hg.abortmerge,
4928    statushint=_(
4929        b'To continue:    hg commit\nTo abort:       hg merge --abort'
4930    ),
4931    cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4932)
4933
4934
4935@command(
4936    b'outgoing|out',
4937    [
4938        (
4939            b'f',
4940            b'force',
4941            None,
4942            _(b'run even when the destination is unrelated'),
4943        ),
4944        (
4945            b'r',
4946            b'rev',
4947            [],
4948            _(b'a changeset intended to be included in the destination'),
4949            _(b'REV'),
4950        ),
4951        (b'n', b'newest-first', None, _(b'show newest record first')),
4952        (b'B', b'bookmarks', False, _(b'compare bookmarks')),
4953        (
4954            b'b',
4955            b'branch',
4956            [],
4957            _(b'a specific branch you would like to push'),
4958            _(b'BRANCH'),
4959        ),
4960    ]
4961    + logopts
4962    + remoteopts
4963    + subrepoopts,
4964    _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]...'),
4965    helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4966)
4967def outgoing(ui, repo, *dests, **opts):
4968    """show changesets not found in the destination
4969
4970    Show changesets not found in the specified destination repository
4971    or the default push location. These are the changesets that would
4972    be pushed if a push was requested.
4973
4974    See pull for details of valid destination formats.
4975
4976    .. container:: verbose
4977
4978      With -B/--bookmarks, the result of bookmark comparison between
4979      local and remote repositories is displayed. With -v/--verbose,
4980      status is also displayed for each bookmark like below::
4981
4982        BM1               01234567890a added
4983        BM2                            deleted
4984        BM3               234567890abc advanced
4985        BM4               34567890abcd diverged
4986        BM5               4567890abcde changed
4987
4988      The action taken when pushing depends on the
4989      status of each bookmark:
4990
4991      :``added``: push with ``-B`` will create it
4992      :``deleted``: push with ``-B`` will delete it
4993      :``advanced``: push will update it
4994      :``diverged``: push with ``-B`` will update it
4995      :``changed``: push with ``-B`` will update it
4996
4997      From the point of view of pushing behavior, bookmarks
4998      existing only in the remote repository are treated as
4999      ``deleted``, even if it is in fact added remotely.
5000
5001    Returns 0 if there are outgoing changes, 1 otherwise.
5002    """
5003    opts = pycompat.byteskwargs(opts)
5004    if opts.get(b'bookmarks'):
5005        for path in urlutil.get_push_paths(repo, ui, dests):
5006            dest = path.pushloc or path.loc
5007            other = hg.peer(repo, opts, dest)
5008            try:
5009                if b'bookmarks' not in other.listkeys(b'namespaces'):
5010                    ui.warn(_(b"remote doesn't support bookmarks\n"))
5011                    return 0
5012                ui.status(
5013                    _(b'comparing with %s\n') % urlutil.hidepassword(dest)
5014                )
5015                ui.pager(b'outgoing')
5016                return bookmarks.outgoing(ui, repo, other)
5017            finally:
5018                other.close()
5019
5020    return hg.outgoing(ui, repo, dests, opts)
5021
5022
5023@command(
5024    b'parents',
5025    [
5026        (
5027            b'r',
5028            b'rev',
5029            b'',
5030            _(b'show parents of the specified revision'),
5031            _(b'REV'),
5032        ),
5033    ]
5034    + templateopts,
5035    _(b'[-r REV] [FILE]'),
5036    helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
5037    inferrepo=True,
5038)
5039def parents(ui, repo, file_=None, **opts):
5040    """show the parents of the working directory or revision (DEPRECATED)
5041
5042    Print the working directory's parent revisions. If a revision is
5043    given via -r/--rev, the parent of that revision will be printed.
5044    If a file argument is given, the revision in which the file was
5045    last changed (before the working directory revision or the
5046    argument to --rev if given) is printed.
5047
5048    This command is equivalent to::
5049
5050        hg log -r "p1()+p2()" or
5051        hg log -r "p1(REV)+p2(REV)" or
5052        hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
5053        hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
5054
5055    See :hg:`summary` and :hg:`help revsets` for related information.
5056
5057    Returns 0 on success.
5058    """
5059
5060    opts = pycompat.byteskwargs(opts)
5061    rev = opts.get(b'rev')
5062    if rev:
5063        repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
5064    ctx = logcmdutil.revsingle(repo, rev, None)
5065
5066    if file_:
5067        m = scmutil.match(ctx, (file_,), opts)
5068        if m.anypats() or len(m.files()) != 1:
5069            raise error.InputError(_(b'can only specify an explicit filename'))
5070        file_ = m.files()[0]
5071        filenodes = []
5072        for cp in ctx.parents():
5073            if not cp:
5074                continue
5075            try:
5076                filenodes.append(cp.filenode(file_))
5077            except error.LookupError:
5078                pass
5079        if not filenodes:
5080            raise error.InputError(_(b"'%s' not found in manifest") % file_)
5081        p = []
5082        for fn in filenodes:
5083            fctx = repo.filectx(file_, fileid=fn)
5084            p.append(fctx.node())
5085    else:
5086        p = [cp.node() for cp in ctx.parents()]
5087
5088    displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
5089    for n in p:
5090        if n != repo.nullid:
5091            displayer.show(repo[n])
5092    displayer.close()
5093
5094
5095@command(
5096    b'paths',
5097    formatteropts,
5098    _(b'[NAME]'),
5099    helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5100    optionalrepo=True,
5101    intents={INTENT_READONLY},
5102)
5103def paths(ui, repo, search=None, **opts):
5104    """show aliases for remote repositories
5105
5106    Show definition of symbolic path name NAME. If no name is given,
5107    show definition of all available names.
5108
5109    Option -q/--quiet suppresses all output when searching for NAME
5110    and shows only the path names when listing all definitions.
5111
5112    Path names are defined in the [paths] section of your
5113    configuration file and in ``/etc/mercurial/hgrc``. If run inside a
5114    repository, ``.hg/hgrc`` is used, too.
5115
5116    The path names ``default`` and ``default-push`` have a special
5117    meaning.  When performing a push or pull operation, they are used
5118    as fallbacks if no location is specified on the command-line.
5119    When ``default-push`` is set, it will be used for push and
5120    ``default`` will be used for pull; otherwise ``default`` is used
5121    as the fallback for both.  When cloning a repository, the clone
5122    source is written as ``default`` in ``.hg/hgrc``.
5123
5124    .. note::
5125
5126       ``default`` and ``default-push`` apply to all inbound (e.g.
5127       :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
5128       and :hg:`bundle`) operations.
5129
5130    See :hg:`help urls` for more information.
5131
5132    .. container:: verbose
5133
5134      Template:
5135
5136      The following keywords are supported. See also :hg:`help templates`.
5137
5138      :name:    String. Symbolic name of the path alias.
5139      :pushurl: String. URL for push operations.
5140      :url:     String. URL or directory path for the other operations.
5141
5142    Returns 0 on success.
5143    """
5144
5145    opts = pycompat.byteskwargs(opts)
5146
5147    pathitems = urlutil.list_paths(ui, search)
5148    ui.pager(b'paths')
5149
5150    fm = ui.formatter(b'paths', opts)
5151    if fm.isplain():
5152        hidepassword = urlutil.hidepassword
5153    else:
5154        hidepassword = bytes
5155    if ui.quiet:
5156        namefmt = b'%s\n'
5157    else:
5158        namefmt = b'%s = '
5159    showsubopts = not search and not ui.quiet
5160
5161    for name, path in pathitems:
5162        fm.startitem()
5163        fm.condwrite(not search, b'name', namefmt, name)
5164        fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
5165        for subopt, value in sorted(path.suboptions.items()):
5166            assert subopt not in (b'name', b'url')
5167            if showsubopts:
5168                fm.plain(b'%s:%s = ' % (name, subopt))
5169            if isinstance(value, bool):
5170                if value:
5171                    value = b'yes'
5172                else:
5173                    value = b'no'
5174            fm.condwrite(showsubopts, subopt, b'%s\n', value)
5175
5176    fm.end()
5177
5178    if search and not pathitems:
5179        if not ui.quiet:
5180            ui.warn(_(b"not found!\n"))
5181        return 1
5182    else:
5183        return 0
5184
5185
5186@command(
5187    b'phase',
5188    [
5189        (b'p', b'public', False, _(b'set changeset phase to public')),
5190        (b'd', b'draft', False, _(b'set changeset phase to draft')),
5191        (b's', b'secret', False, _(b'set changeset phase to secret')),
5192        (b'f', b'force', False, _(b'allow to move boundary backward')),
5193        (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
5194    ],
5195    _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
5196    helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
5197)
5198def phase(ui, repo, *revs, **opts):
5199    """set or show the current phase name
5200
5201    With no argument, show the phase name of the current revision(s).
5202
5203    With one of -p/--public, -d/--draft or -s/--secret, change the
5204    phase value of the specified revisions.
5205
5206    Unless -f/--force is specified, :hg:`phase` won't move changesets from a
5207    lower phase to a higher phase. Phases are ordered as follows::
5208
5209        public < draft < secret
5210
5211    Returns 0 on success, 1 if some phases could not be changed.
5212
5213    (For more information about the phases concept, see :hg:`help phases`.)
5214    """
5215    opts = pycompat.byteskwargs(opts)
5216    # search for a unique phase argument
5217    targetphase = None
5218    for idx, name in enumerate(phases.cmdphasenames):
5219        if opts[name]:
5220            if targetphase is not None:
5221                raise error.InputError(_(b'only one phase can be specified'))
5222            targetphase = idx
5223
5224    # look for specified revision
5225    revs = list(revs)
5226    revs.extend(opts[b'rev'])
5227    if revs:
5228        revs = logcmdutil.revrange(repo, revs)
5229    else:
5230        # display both parents as the second parent phase can influence
5231        # the phase of a merge commit
5232        revs = [c.rev() for c in repo[None].parents()]
5233
5234    ret = 0
5235    if targetphase is None:
5236        # display
5237        for r in revs:
5238            ctx = repo[r]
5239            ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
5240    else:
5241        with repo.lock(), repo.transaction(b"phase") as tr:
5242            # set phase
5243            if not revs:
5244                raise error.InputError(_(b'empty revision set'))
5245            nodes = [repo[r].node() for r in revs]
5246            # moving revision from public to draft may hide them
5247            # We have to check result on an unfiltered repository
5248            unfi = repo.unfiltered()
5249            getphase = unfi._phasecache.phase
5250            olddata = [getphase(unfi, r) for r in unfi]
5251            phases.advanceboundary(repo, tr, targetphase, nodes)
5252            if opts[b'force']:
5253                phases.retractboundary(repo, tr, targetphase, nodes)
5254        getphase = unfi._phasecache.phase
5255        newdata = [getphase(unfi, r) for r in unfi]
5256        changes = sum(newdata[r] != olddata[r] for r in unfi)
5257        cl = unfi.changelog
5258        rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
5259        if rejected:
5260            ui.warn(
5261                _(
5262                    b'cannot move %i changesets to a higher '
5263                    b'phase, use --force\n'
5264                )
5265                % len(rejected)
5266            )
5267            ret = 1
5268        if changes:
5269            msg = _(b'phase changed for %i changesets\n') % changes
5270            if ret:
5271                ui.status(msg)
5272            else:
5273                ui.note(msg)
5274        else:
5275            ui.warn(_(b'no phases changed\n'))
5276    return ret
5277
5278
5279def postincoming(ui, repo, modheads, optupdate, checkout, brev):
5280    """Run after a changegroup has been added via pull/unbundle
5281
5282    This takes arguments below:
5283
5284    :modheads: change of heads by pull/unbundle
5285    :optupdate: updating working directory is needed or not
5286    :checkout: update destination revision (or None to default destination)
5287    :brev: a name, which might be a bookmark to be activated after updating
5288
5289    return True if update raise any conflict, False otherwise.
5290    """
5291    if modheads == 0:
5292        return False
5293    if optupdate:
5294        try:
5295            return hg.updatetotally(ui, repo, checkout, brev)
5296        except error.UpdateAbort as inst:
5297            msg = _(b"not updating: %s") % stringutil.forcebytestr(inst)
5298            hint = inst.hint
5299            raise error.UpdateAbort(msg, hint=hint)
5300    if modheads is not None and modheads > 1:
5301        currentbranchheads = len(repo.branchheads())
5302        if currentbranchheads == modheads:
5303            ui.status(
5304                _(b"(run 'hg heads' to see heads, 'hg merge' to merge)\n")
5305            )
5306        elif currentbranchheads > 1:
5307            ui.status(
5308                _(b"(run 'hg heads .' to see heads, 'hg merge' to merge)\n")
5309            )
5310        else:
5311            ui.status(_(b"(run 'hg heads' to see heads)\n"))
5312    elif not ui.configbool(b'commands', b'update.requiredest'):
5313        ui.status(_(b"(run 'hg update' to get a working copy)\n"))
5314    return False
5315
5316
5317@command(
5318    b'pull',
5319    [
5320        (
5321            b'u',
5322            b'update',
5323            None,
5324            _(b'update to new branch head if new descendants were pulled'),
5325        ),
5326        (
5327            b'f',
5328            b'force',
5329            None,
5330            _(b'run even when remote repository is unrelated'),
5331        ),
5332        (
5333            b'',
5334            b'confirm',
5335            None,
5336            _(b'confirm pull before applying changes'),
5337        ),
5338        (
5339            b'r',
5340            b'rev',
5341            [],
5342            _(b'a remote changeset intended to be added'),
5343            _(b'REV'),
5344        ),
5345        (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5346        (
5347            b'b',
5348            b'branch',
5349            [],
5350            _(b'a specific branch you would like to pull'),
5351            _(b'BRANCH'),
5352        ),
5353    ]
5354    + remoteopts,
5355    _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]...'),
5356    helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5357    helpbasic=True,
5358)
5359def pull(ui, repo, *sources, **opts):
5360    """pull changes from the specified source
5361
5362    Pull changes from a remote repository to a local one.
5363
5364    This finds all changes from the repository at the specified path
5365    or URL and adds them to a local repository (the current one unless
5366    -R is specified). By default, this does not update the copy of the
5367    project in the working directory.
5368
5369    When cloning from servers that support it, Mercurial may fetch
5370    pre-generated data. When this is done, hooks operating on incoming
5371    changesets and changegroups may fire more than once, once for each
5372    pre-generated bundle and as well as for any additional remaining
5373    data. See :hg:`help -e clonebundles` for more.
5374
5375    Use :hg:`incoming` if you want to see what would have been added
5376    by a pull at the time you issued this command. If you then decide
5377    to add those changes to the repository, you should use :hg:`pull
5378    -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5379
5380    If SOURCE is omitted, the 'default' path will be used.
5381    See :hg:`help urls` for more information.
5382
5383    If multiple sources are specified, they will be pulled sequentially as if
5384    the command was run multiple time. If --update is specify and the command
5385    will stop at the first failed --update.
5386
5387    Specifying bookmark as ``.`` is equivalent to specifying the active
5388    bookmark's name.
5389
5390    Returns 0 on success, 1 if an update had unresolved files.
5391    """
5392
5393    opts = pycompat.byteskwargs(opts)
5394    if ui.configbool(b'commands', b'update.requiredest') and opts.get(
5395        b'update'
5396    ):
5397        msg = _(b'update destination required by configuration')
5398        hint = _(b'use hg pull followed by hg update DEST')
5399        raise error.InputError(msg, hint=hint)
5400
5401    for path in urlutil.get_pull_paths(repo, ui, sources):
5402        source, branches = urlutil.parseurl(path.rawloc, opts.get(b'branch'))
5403        ui.status(_(b'pulling from %s\n') % urlutil.hidepassword(source))
5404        ui.flush()
5405        other = hg.peer(repo, opts, source)
5406        update_conflict = None
5407        try:
5408            revs, checkout = hg.addbranchrevs(
5409                repo, other, branches, opts.get(b'rev')
5410            )
5411
5412            pullopargs = {}
5413
5414            nodes = None
5415            if opts.get(b'bookmark') or revs:
5416                # The list of bookmark used here is the same used to actually update
5417                # the bookmark names, to avoid the race from issue 4689 and we do
5418                # all lookup and bookmark queries in one go so they see the same
5419                # version of the server state (issue 4700).
5420                nodes = []
5421                fnodes = []
5422                revs = revs or []
5423                if revs and not other.capable(b'lookup'):
5424                    err = _(
5425                        b"other repository doesn't support revision lookup, "
5426                        b"so a rev cannot be specified."
5427                    )
5428                    raise error.Abort(err)
5429                with other.commandexecutor() as e:
5430                    fremotebookmarks = e.callcommand(
5431                        b'listkeys', {b'namespace': b'bookmarks'}
5432                    )
5433                    for r in revs:
5434                        fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5435                remotebookmarks = fremotebookmarks.result()
5436                remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5437                pullopargs[b'remotebookmarks'] = remotebookmarks
5438                for b in opts.get(b'bookmark', []):
5439                    b = repo._bookmarks.expandname(b)
5440                    if b not in remotebookmarks:
5441                        raise error.InputError(
5442                            _(b'remote bookmark %s not found!') % b
5443                        )
5444                    nodes.append(remotebookmarks[b])
5445                for i, rev in enumerate(revs):
5446                    node = fnodes[i].result()
5447                    nodes.append(node)
5448                    if rev == checkout:
5449                        checkout = node
5450
5451            wlock = util.nullcontextmanager()
5452            if opts.get(b'update'):
5453                wlock = repo.wlock()
5454            with wlock:
5455                pullopargs.update(opts.get(b'opargs', {}))
5456                modheads = exchange.pull(
5457                    repo,
5458                    other,
5459                    path=path,
5460                    heads=nodes,
5461                    force=opts.get(b'force'),
5462                    bookmarks=opts.get(b'bookmark', ()),
5463                    opargs=pullopargs,
5464                    confirm=opts.get(b'confirm'),
5465                ).cgresult
5466
5467                # brev is a name, which might be a bookmark to be activated at
5468                # the end of the update. In other words, it is an explicit
5469                # destination of the update
5470                brev = None
5471
5472                if checkout:
5473                    checkout = repo.unfiltered().changelog.rev(checkout)
5474
5475                    # order below depends on implementation of
5476                    # hg.addbranchrevs(). opts['bookmark'] is ignored,
5477                    # because 'checkout' is determined without it.
5478                    if opts.get(b'rev'):
5479                        brev = opts[b'rev'][0]
5480                    elif opts.get(b'branch'):
5481                        brev = opts[b'branch'][0]
5482                    else:
5483                        brev = branches[0]
5484                repo._subtoppath = source
5485                try:
5486                    update_conflict = postincoming(
5487                        ui, repo, modheads, opts.get(b'update'), checkout, brev
5488                    )
5489                except error.FilteredRepoLookupError as exc:
5490                    msg = _(b'cannot update to target: %s') % exc.args[0]
5491                    exc.args = (msg,) + exc.args[1:]
5492                    raise
5493                finally:
5494                    del repo._subtoppath
5495
5496        finally:
5497            other.close()
5498        # skip the remaining pull source if they are some conflict.
5499        if update_conflict:
5500            break
5501    if update_conflict:
5502        return 1
5503    else:
5504        return 0
5505
5506
5507@command(
5508    b'purge|clean',
5509    [
5510        (b'a', b'abort-on-err', None, _(b'abort if an error occurs')),
5511        (b'', b'all', None, _(b'purge ignored files too')),
5512        (b'i', b'ignored', None, _(b'purge only ignored files')),
5513        (b'', b'dirs', None, _(b'purge empty directories')),
5514        (b'', b'files', None, _(b'purge files')),
5515        (b'p', b'print', None, _(b'print filenames instead of deleting them')),
5516        (
5517            b'0',
5518            b'print0',
5519            None,
5520            _(
5521                b'end filenames with NUL, for use with xargs'
5522                b' (implies -p/--print)'
5523            ),
5524        ),
5525        (b'', b'confirm', None, _(b'ask before permanently deleting files')),
5526    ]
5527    + cmdutil.walkopts,
5528    _(b'hg purge [OPTION]... [DIR]...'),
5529    helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5530)
5531def purge(ui, repo, *dirs, **opts):
5532    """removes files not tracked by Mercurial
5533
5534    Delete files not known to Mercurial. This is useful to test local
5535    and uncommitted changes in an otherwise-clean source tree.
5536
5537    This means that purge will delete the following by default:
5538
5539    - Unknown files: files marked with "?" by :hg:`status`
5540    - Empty directories: in fact Mercurial ignores directories unless
5541      they contain files under source control management
5542
5543    But it will leave untouched:
5544
5545    - Modified and unmodified tracked files
5546    - Ignored files (unless -i or --all is specified)
5547    - New files added to the repository (with :hg:`add`)
5548
5549    The --files and --dirs options can be used to direct purge to delete
5550    only files, only directories, or both. If neither option is given,
5551    both will be deleted.
5552
5553    If directories are given on the command line, only files in these
5554    directories are considered.
5555
5556    Be careful with purge, as you could irreversibly delete some files
5557    you forgot to add to the repository. If you only want to print the
5558    list of files that this program would delete, use the --print
5559    option.
5560    """
5561    opts = pycompat.byteskwargs(opts)
5562    cmdutil.check_at_most_one_arg(opts, b'all', b'ignored')
5563
5564    act = not opts.get(b'print')
5565    eol = b'\n'
5566    if opts.get(b'print0'):
5567        eol = b'\0'
5568        act = False  # --print0 implies --print
5569    if opts.get(b'all', False):
5570        ignored = True
5571        unknown = True
5572    else:
5573        ignored = opts.get(b'ignored', False)
5574        unknown = not ignored
5575
5576    removefiles = opts.get(b'files')
5577    removedirs = opts.get(b'dirs')
5578    confirm = opts.get(b'confirm')
5579    if confirm is None:
5580        try:
5581            extensions.find(b'purge')
5582            confirm = False
5583        except KeyError:
5584            confirm = True
5585
5586    if not removefiles and not removedirs:
5587        removefiles = True
5588        removedirs = True
5589
5590    match = scmutil.match(repo[None], dirs, opts)
5591
5592    paths = mergemod.purge(
5593        repo,
5594        match,
5595        unknown=unknown,
5596        ignored=ignored,
5597        removeemptydirs=removedirs,
5598        removefiles=removefiles,
5599        abortonerror=opts.get(b'abort_on_err'),
5600        noop=not act,
5601        confirm=confirm,
5602    )
5603
5604    for path in paths:
5605        if not act:
5606            ui.write(b'%s%s' % (path, eol))
5607
5608
5609@command(
5610    b'push',
5611    [
5612        (b'f', b'force', None, _(b'force push')),
5613        (
5614            b'r',
5615            b'rev',
5616            [],
5617            _(b'a changeset intended to be included in the destination'),
5618            _(b'REV'),
5619        ),
5620        (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5621        (b'', b'all-bookmarks', None, _(b"push all bookmarks (EXPERIMENTAL)")),
5622        (
5623            b'b',
5624            b'branch',
5625            [],
5626            _(b'a specific branch you would like to push'),
5627            _(b'BRANCH'),
5628        ),
5629        (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5630        (
5631            b'',
5632            b'pushvars',
5633            [],
5634            _(b'variables that can be sent to server (ADVANCED)'),
5635        ),
5636        (
5637            b'',
5638            b'publish',
5639            False,
5640            _(b'push the changeset as public (EXPERIMENTAL)'),
5641        ),
5642    ]
5643    + remoteopts,
5644    _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]...'),
5645    helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5646    helpbasic=True,
5647)
5648def push(ui, repo, *dests, **opts):
5649    """push changes to the specified destination
5650
5651    Push changesets from the local repository to the specified
5652    destination.
5653
5654    This operation is symmetrical to pull: it is identical to a pull
5655    in the destination repository from the current one.
5656
5657    By default, push will not allow creation of new heads at the
5658    destination, since multiple heads would make it unclear which head
5659    to use. In this situation, it is recommended to pull and merge
5660    before pushing.
5661
5662    Use --new-branch if you want to allow push to create a new named
5663    branch that is not present at the destination. This allows you to
5664    only create a new branch without forcing other changes.
5665
5666    .. note::
5667
5668       Extra care should be taken with the -f/--force option,
5669       which will push all new heads on all branches, an action which will
5670       almost always cause confusion for collaborators.
5671
5672    If -r/--rev is used, the specified revision and all its ancestors
5673    will be pushed to the remote repository.
5674
5675    If -B/--bookmark is used, the specified bookmarked revision, its
5676    ancestors, and the bookmark will be pushed to the remote
5677    repository. Specifying ``.`` is equivalent to specifying the active
5678    bookmark's name. Use the --all-bookmarks option for pushing all
5679    current bookmarks.
5680
5681    Please see :hg:`help urls` for important details about ``ssh://``
5682    URLs. If DESTINATION is omitted, a default path will be used.
5683
5684    When passed multiple destinations, push will process them one after the
5685    other, but stop should an error occur.
5686
5687    .. container:: verbose
5688
5689        The --pushvars option sends strings to the server that become
5690        environment variables prepended with ``HG_USERVAR_``. For example,
5691        ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5692        ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5693
5694        pushvars can provide for user-overridable hooks as well as set debug
5695        levels. One example is having a hook that blocks commits containing
5696        conflict markers, but enables the user to override the hook if the file
5697        is using conflict markers for testing purposes or the file format has
5698        strings that look like conflict markers.
5699
5700        By default, servers will ignore `--pushvars`. To enable it add the
5701        following to your configuration file::
5702
5703            [push]
5704            pushvars.server = true
5705
5706    Returns 0 if push was successful, 1 if nothing to push.
5707    """
5708
5709    opts = pycompat.byteskwargs(opts)
5710
5711    if opts.get(b'all_bookmarks'):
5712        cmdutil.check_incompatible_arguments(
5713            opts,
5714            b'all_bookmarks',
5715            [b'bookmark', b'rev'],
5716        )
5717        opts[b'bookmark'] = list(repo._bookmarks)
5718
5719    if opts.get(b'bookmark'):
5720        ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5721        for b in opts[b'bookmark']:
5722            # translate -B options to -r so changesets get pushed
5723            b = repo._bookmarks.expandname(b)
5724            if b in repo._bookmarks:
5725                opts.setdefault(b'rev', []).append(b)
5726            else:
5727                # if we try to push a deleted bookmark, translate it to null
5728                # this lets simultaneous -r, -b options continue working
5729                opts.setdefault(b'rev', []).append(b"null")
5730
5731    some_pushed = False
5732    result = 0
5733    for path in urlutil.get_push_paths(repo, ui, dests):
5734        dest = path.pushloc or path.loc
5735        branches = (path.branch, opts.get(b'branch') or [])
5736        ui.status(_(b'pushing to %s\n') % urlutil.hidepassword(dest))
5737        revs, checkout = hg.addbranchrevs(
5738            repo, repo, branches, opts.get(b'rev')
5739        )
5740        other = hg.peer(repo, opts, dest)
5741
5742        try:
5743            if revs:
5744                revs = [repo[r].node() for r in logcmdutil.revrange(repo, revs)]
5745                if not revs:
5746                    raise error.InputError(
5747                        _(b"specified revisions evaluate to an empty set"),
5748                        hint=_(b"use different revision arguments"),
5749                    )
5750            elif path.pushrev:
5751                # It doesn't make any sense to specify ancestor revisions. So limit
5752                # to DAG heads to make discovery simpler.
5753                expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5754                revs = scmutil.revrange(repo, [expr])
5755                revs = [repo[rev].node() for rev in revs]
5756                if not revs:
5757                    raise error.InputError(
5758                        _(
5759                            b'default push revset for path evaluates to an empty set'
5760                        )
5761                    )
5762            elif ui.configbool(b'commands', b'push.require-revs'):
5763                raise error.InputError(
5764                    _(b'no revisions specified to push'),
5765                    hint=_(b'did you mean "hg push -r ."?'),
5766                )
5767
5768            repo._subtoppath = dest
5769            try:
5770                # push subrepos depth-first for coherent ordering
5771                c = repo[b'.']
5772                subs = c.substate  # only repos that are committed
5773                for s in sorted(subs):
5774                    sub_result = c.sub(s).push(opts)
5775                    if sub_result == 0:
5776                        return 1
5777            finally:
5778                del repo._subtoppath
5779
5780            opargs = dict(
5781                opts.get(b'opargs', {})
5782            )  # copy opargs since we may mutate it
5783            opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5784
5785            pushop = exchange.push(
5786                repo,
5787                other,
5788                opts.get(b'force'),
5789                revs=revs,
5790                newbranch=opts.get(b'new_branch'),
5791                bookmarks=opts.get(b'bookmark', ()),
5792                publish=opts.get(b'publish'),
5793                opargs=opargs,
5794            )
5795
5796            if pushop.cgresult == 0:
5797                result = 1
5798            elif pushop.cgresult is not None:
5799                some_pushed = True
5800
5801            if pushop.bkresult is not None:
5802                if pushop.bkresult == 2:
5803                    result = 2
5804                elif not result and pushop.bkresult:
5805                    result = 2
5806
5807            if result:
5808                break
5809
5810        finally:
5811            other.close()
5812    if result == 0 and not some_pushed:
5813        result = 1
5814    return result
5815
5816
5817@command(
5818    b'recover',
5819    [
5820        (b'', b'verify', False, b"run `hg verify` after successful recover"),
5821    ],
5822    helpcategory=command.CATEGORY_MAINTENANCE,
5823)
5824def recover(ui, repo, **opts):
5825    """roll back an interrupted transaction
5826
5827    Recover from an interrupted commit or pull.
5828
5829    This command tries to fix the repository status after an
5830    interrupted operation. It should only be necessary when Mercurial
5831    suggests it.
5832
5833    Returns 0 if successful, 1 if nothing to recover or verify fails.
5834    """
5835    ret = repo.recover()
5836    if ret:
5837        if opts['verify']:
5838            return hg.verify(repo)
5839        else:
5840            msg = _(
5841                b"(verify step skipped, run `hg verify` to check your "
5842                b"repository content)\n"
5843            )
5844            ui.warn(msg)
5845            return 0
5846    return 1
5847
5848
5849@command(
5850    b'remove|rm',
5851    [
5852        (b'A', b'after', None, _(b'record delete for missing files')),
5853        (b'f', b'force', None, _(b'forget added files, delete modified files')),
5854    ]
5855    + subrepoopts
5856    + walkopts
5857    + dryrunopts,
5858    _(b'[OPTION]... FILE...'),
5859    helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5860    helpbasic=True,
5861    inferrepo=True,
5862)
5863def remove(ui, repo, *pats, **opts):
5864    """remove the specified files on the next commit
5865
5866    Schedule the indicated files for removal from the current branch.
5867
5868    This command schedules the files to be removed at the next commit.
5869    To undo a remove before that, see :hg:`revert`. To undo added
5870    files, see :hg:`forget`.
5871
5872    .. container:: verbose
5873
5874      -A/--after can be used to remove only files that have already
5875      been deleted, -f/--force can be used to force deletion, and -Af
5876      can be used to remove files from the next revision without
5877      deleting them from the working directory.
5878
5879      The following table details the behavior of remove for different
5880      file states (columns) and option combinations (rows). The file
5881      states are Added [A], Clean [C], Modified [M] and Missing [!]
5882      (as reported by :hg:`status`). The actions are Warn, Remove
5883      (from branch) and Delete (from disk):
5884
5885      ========= == == == ==
5886      opt/state A  C  M  !
5887      ========= == == == ==
5888      none      W  RD W  R
5889      -f        R  RD RD R
5890      -A        W  W  W  R
5891      -Af       R  R  R  R
5892      ========= == == == ==
5893
5894      .. note::
5895
5896         :hg:`remove` never deletes files in Added [A] state from the
5897         working directory, not even if ``--force`` is specified.
5898
5899    Returns 0 on success, 1 if any warnings encountered.
5900    """
5901
5902    opts = pycompat.byteskwargs(opts)
5903    after, force = opts.get(b'after'), opts.get(b'force')
5904    dryrun = opts.get(b'dry_run')
5905    if not pats and not after:
5906        raise error.InputError(_(b'no files specified'))
5907
5908    m = scmutil.match(repo[None], pats, opts)
5909    subrepos = opts.get(b'subrepos')
5910    uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5911    return cmdutil.remove(
5912        ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5913    )
5914
5915
5916@command(
5917    b'rename|move|mv',
5918    [
5919        (b'', b'forget', None, _(b'unmark a destination file as renamed')),
5920        (b'A', b'after', None, _(b'record a rename that has already occurred')),
5921        (
5922            b'',
5923            b'at-rev',
5924            b'',
5925            _(b'(un)mark renames in the given revision (EXPERIMENTAL)'),
5926            _(b'REV'),
5927        ),
5928        (
5929            b'f',
5930            b'force',
5931            None,
5932            _(b'forcibly move over an existing managed file'),
5933        ),
5934    ]
5935    + walkopts
5936    + dryrunopts,
5937    _(b'[OPTION]... SOURCE... DEST'),
5938    helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5939)
5940def rename(ui, repo, *pats, **opts):
5941    """rename files; equivalent of copy + remove
5942
5943    Mark dest as copies of sources; mark sources for deletion. If dest
5944    is a directory, copies are put in that directory. If dest is a
5945    file, there can only be one source.
5946
5947    By default, this command copies the contents of files as they
5948    exist in the working directory. If invoked with -A/--after, the
5949    operation is recorded, but no copying is performed.
5950
5951    To undo marking a destination file as renamed, use --forget. With that
5952    option, all given (positional) arguments are unmarked as renames. The
5953    destination file(s) will be left in place (still tracked). The source
5954    file(s) will not be restored. Note that :hg:`rename --forget` behaves
5955    the same way as :hg:`copy --forget`.
5956
5957    This command takes effect with the next commit by default.
5958
5959    Returns 0 on success, 1 if errors are encountered.
5960    """
5961    opts = pycompat.byteskwargs(opts)
5962    with repo.wlock():
5963        return cmdutil.copy(ui, repo, pats, opts, rename=True)
5964
5965
5966@command(
5967    b'resolve',
5968    [
5969        (b'a', b'all', None, _(b'select all unresolved files')),
5970        (b'l', b'list', None, _(b'list state of files needing merge')),
5971        (b'm', b'mark', None, _(b'mark files as resolved')),
5972        (b'u', b'unmark', None, _(b'mark files as unresolved')),
5973        (b'n', b'no-status', None, _(b'hide status prefix')),
5974        (b'', b're-merge', None, _(b're-merge files')),
5975    ]
5976    + mergetoolopts
5977    + walkopts
5978    + formatteropts,
5979    _(b'[OPTION]... [FILE]...'),
5980    helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5981    inferrepo=True,
5982)
5983def resolve(ui, repo, *pats, **opts):
5984    """redo merges or set/view the merge status of files
5985
5986    Merges with unresolved conflicts are often the result of
5987    non-interactive merging using the ``internal:merge`` configuration
5988    setting, or a command-line merge tool like ``diff3``. The resolve
5989    command is used to manage the files involved in a merge, after
5990    :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5991    working directory must have two parents). See :hg:`help
5992    merge-tools` for information on configuring merge tools.
5993
5994    The resolve command can be used in the following ways:
5995
5996    - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
5997      the specified files, discarding any previous merge attempts. Re-merging
5998      is not performed for files already marked as resolved. Use ``--all/-a``
5999      to select all unresolved files. ``--tool`` can be used to specify
6000      the merge tool used for the given files. It overrides the HGMERGE
6001      environment variable and your configuration files.  Previous file
6002      contents are saved with a ``.orig`` suffix.
6003
6004    - :hg:`resolve -m [FILE]`: mark a file as having been resolved
6005      (e.g. after having manually fixed-up the files). The default is
6006      to mark all unresolved files.
6007
6008    - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
6009      default is to mark all resolved files.
6010
6011    - :hg:`resolve -l`: list files which had or still have conflicts.
6012      In the printed list, ``U`` = unresolved and ``R`` = resolved.
6013      You can use ``set:unresolved()`` or ``set:resolved()`` to filter
6014      the list. See :hg:`help filesets` for details.
6015
6016    .. note::
6017
6018       Mercurial will not let you commit files with unresolved merge
6019       conflicts. You must use :hg:`resolve -m ...` before you can
6020       commit after a conflicting merge.
6021
6022    .. container:: verbose
6023
6024      Template:
6025
6026      The following keywords are supported in addition to the common template
6027      keywords and functions. See also :hg:`help templates`.
6028
6029      :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
6030      :path:    String. Repository-absolute path of the file.
6031
6032    Returns 0 on success, 1 if any files fail a resolve attempt.
6033    """
6034
6035    opts = pycompat.byteskwargs(opts)
6036    confirm = ui.configbool(b'commands', b'resolve.confirm')
6037    flaglist = b'all mark unmark list no_status re_merge'.split()
6038    all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
6039
6040    actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
6041    if actioncount > 1:
6042        raise error.InputError(_(b"too many actions specified"))
6043    elif actioncount == 0 and ui.configbool(
6044        b'commands', b'resolve.explicit-re-merge'
6045    ):
6046        hint = _(b'use --mark, --unmark, --list or --re-merge')
6047        raise error.InputError(_(b'no action specified'), hint=hint)
6048    if pats and all:
6049        raise error.InputError(_(b"can't specify --all and patterns"))
6050    if not (all or pats or show or mark or unmark):
6051        raise error.InputError(
6052            _(b'no files or directories specified'),
6053            hint=b'use --all to re-merge all unresolved files',
6054        )
6055
6056    if confirm:
6057        if all:
6058            if ui.promptchoice(
6059                _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
6060            ):
6061                raise error.CanceledError(_(b'user quit'))
6062        if mark and not pats:
6063            if ui.promptchoice(
6064                _(
6065                    b'mark all unresolved files as resolved (yn)?'
6066                    b'$$ &Yes $$ &No'
6067                )
6068            ):
6069                raise error.CanceledError(_(b'user quit'))
6070        if unmark and not pats:
6071            if ui.promptchoice(
6072                _(
6073                    b'mark all resolved files as unresolved (yn)?'
6074                    b'$$ &Yes $$ &No'
6075                )
6076            ):
6077                raise error.CanceledError(_(b'user quit'))
6078
6079    uipathfn = scmutil.getuipathfn(repo)
6080
6081    if show:
6082        ui.pager(b'resolve')
6083        fm = ui.formatter(b'resolve', opts)
6084        ms = mergestatemod.mergestate.read(repo)
6085        wctx = repo[None]
6086        m = scmutil.match(wctx, pats, opts)
6087
6088        # Labels and keys based on merge state.  Unresolved path conflicts show
6089        # as 'P'.  Resolved path conflicts show as 'R', the same as normal
6090        # resolved conflicts.
6091        mergestateinfo = {
6092            mergestatemod.MERGE_RECORD_UNRESOLVED: (
6093                b'resolve.unresolved',
6094                b'U',
6095            ),
6096            mergestatemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
6097            mergestatemod.MERGE_RECORD_UNRESOLVED_PATH: (
6098                b'resolve.unresolved',
6099                b'P',
6100            ),
6101            mergestatemod.MERGE_RECORD_RESOLVED_PATH: (
6102                b'resolve.resolved',
6103                b'R',
6104            ),
6105        }
6106
6107        for f in ms:
6108            if not m(f):
6109                continue
6110
6111            label, key = mergestateinfo[ms[f]]
6112            fm.startitem()
6113            fm.context(ctx=wctx)
6114            fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
6115            fm.data(path=f)
6116            fm.plain(b'%s\n' % uipathfn(f), label=label)
6117        fm.end()
6118        return 0
6119
6120    with repo.wlock():
6121        ms = mergestatemod.mergestate.read(repo)
6122
6123        if not (ms.active() or repo.dirstate.p2() != repo.nullid):
6124            raise error.StateError(
6125                _(b'resolve command not applicable when not merging')
6126            )
6127
6128        wctx = repo[None]
6129        m = scmutil.match(wctx, pats, opts)
6130        ret = 0
6131        didwork = False
6132
6133        tocomplete = []
6134        hasconflictmarkers = []
6135        if mark:
6136            markcheck = ui.config(b'commands', b'resolve.mark-check')
6137            if markcheck not in [b'warn', b'abort']:
6138                # Treat all invalid / unrecognized values as 'none'.
6139                markcheck = False
6140        for f in ms:
6141            if not m(f):
6142                continue
6143
6144            didwork = True
6145
6146            # path conflicts must be resolved manually
6147            if ms[f] in (
6148                mergestatemod.MERGE_RECORD_UNRESOLVED_PATH,
6149                mergestatemod.MERGE_RECORD_RESOLVED_PATH,
6150            ):
6151                if mark:
6152                    ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED_PATH)
6153                elif unmark:
6154                    ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED_PATH)
6155                elif ms[f] == mergestatemod.MERGE_RECORD_UNRESOLVED_PATH:
6156                    ui.warn(
6157                        _(b'%s: path conflict must be resolved manually\n')
6158                        % uipathfn(f)
6159                    )
6160                continue
6161
6162            if mark:
6163                if markcheck:
6164                    fdata = repo.wvfs.tryread(f)
6165                    if (
6166                        filemerge.hasconflictmarkers(fdata)
6167                        and ms[f] != mergestatemod.MERGE_RECORD_RESOLVED
6168                    ):
6169                        hasconflictmarkers.append(f)
6170                ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED)
6171            elif unmark:
6172                ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED)
6173            else:
6174                # backup pre-resolve (merge uses .orig for its own purposes)
6175                a = repo.wjoin(f)
6176                try:
6177                    util.copyfile(a, a + b".resolve")
6178                except (IOError, OSError) as inst:
6179                    if inst.errno != errno.ENOENT:
6180                        raise
6181
6182                try:
6183                    # preresolve file
6184                    overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6185                    with ui.configoverride(overrides, b'resolve'):
6186                        complete, r = ms.preresolve(f, wctx)
6187                    if not complete:
6188                        tocomplete.append(f)
6189                    elif r:
6190                        ret = 1
6191                finally:
6192                    ms.commit()
6193
6194                # replace filemerge's .orig file with our resolve file, but only
6195                # for merges that are complete
6196                if complete:
6197                    try:
6198                        util.rename(
6199                            a + b".resolve", scmutil.backuppath(ui, repo, f)
6200                        )
6201                    except OSError as inst:
6202                        if inst.errno != errno.ENOENT:
6203                            raise
6204
6205        if hasconflictmarkers:
6206            ui.warn(
6207                _(
6208                    b'warning: the following files still have conflict '
6209                    b'markers:\n'
6210                )
6211                + b''.join(
6212                    b'  ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
6213                )
6214            )
6215            if markcheck == b'abort' and not all and not pats:
6216                raise error.StateError(
6217                    _(b'conflict markers detected'),
6218                    hint=_(b'use --all to mark anyway'),
6219                )
6220
6221        for f in tocomplete:
6222            try:
6223                # resolve file
6224                overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
6225                with ui.configoverride(overrides, b'resolve'):
6226                    r = ms.resolve(f, wctx)
6227                if r:
6228                    ret = 1
6229            finally:
6230                ms.commit()
6231
6232            # replace filemerge's .orig file with our resolve file
6233            a = repo.wjoin(f)
6234            try:
6235                util.rename(a + b".resolve", scmutil.backuppath(ui, repo, f))
6236            except OSError as inst:
6237                if inst.errno != errno.ENOENT:
6238                    raise
6239
6240        ms.commit()
6241        branchmerge = repo.dirstate.p2() != repo.nullid
6242        # resolve is not doing a parent change here, however, `record updates`
6243        # will call some dirstate API that at intended for parent changes call.
6244        # Ideally we would not need this and could implement a lighter version
6245        # of the recordupdateslogic that will not have to deal with the part
6246        # related to parent changes. However this would requires that:
6247        # - we are sure we passed around enough information at update/merge
6248        #   time to no longer needs it at `hg resolve time`
6249        # - we are sure we store that information well enough to be able to reuse it
6250        # - we are the necessary logic to reuse it right.
6251        #
6252        # All this should eventually happens, but in the mean time, we use this
6253        # context manager slightly out of the context it should be.
6254        with repo.dirstate.parentchange():
6255            mergestatemod.recordupdates(repo, ms.actions(), branchmerge, None)
6256
6257        if not didwork and pats:
6258            hint = None
6259            if not any([p for p in pats if p.find(b':') >= 0]):
6260                pats = [b'path:%s' % p for p in pats]
6261                m = scmutil.match(wctx, pats, opts)
6262                for f in ms:
6263                    if not m(f):
6264                        continue
6265
6266                    def flag(o):
6267                        if o == b're_merge':
6268                            return b'--re-merge '
6269                        return b'-%s ' % o[0:1]
6270
6271                    flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
6272                    hint = _(b"(try: hg resolve %s%s)\n") % (
6273                        flags,
6274                        b' '.join(pats),
6275                    )
6276                    break
6277            ui.warn(_(b"arguments do not match paths that need resolving\n"))
6278            if hint:
6279                ui.warn(hint)
6280
6281    unresolvedf = ms.unresolvedcount()
6282    if not unresolvedf:
6283        ui.status(_(b'(no more unresolved files)\n'))
6284        cmdutil.checkafterresolved(repo)
6285
6286    return ret
6287
6288
6289@command(
6290    b'revert',
6291    [
6292        (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6293        (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6294        (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6295        (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6296        (b'i', b'interactive', None, _(b'interactively select the changes')),
6297    ]
6298    + walkopts
6299    + dryrunopts,
6300    _(b'[OPTION]... [-r REV] [NAME]...'),
6301    helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6302)
6303def revert(ui, repo, *pats, **opts):
6304    """restore files to their checkout state
6305
6306    .. note::
6307
6308       To check out earlier revisions, you should use :hg:`update REV`.
6309       To cancel an uncommitted merge (and lose your changes),
6310       use :hg:`merge --abort`.
6311
6312    With no revision specified, revert the specified files or directories
6313    to the contents they had in the parent of the working directory.
6314    This restores the contents of files to an unmodified
6315    state and unschedules adds, removes, copies, and renames. If the
6316    working directory has two parents, you must explicitly specify a
6317    revision.
6318
6319    Using the -r/--rev or -d/--date options, revert the given files or
6320    directories to their states as of a specific revision. Because
6321    revert does not change the working directory parents, this will
6322    cause these files to appear modified. This can be helpful to "back
6323    out" some or all of an earlier change. See :hg:`backout` for a
6324    related method.
6325
6326    Modified files are saved with a .orig suffix before reverting.
6327    To disable these backups, use --no-backup. It is possible to store
6328    the backup files in a custom directory relative to the root of the
6329    repository by setting the ``ui.origbackuppath`` configuration
6330    option.
6331
6332    See :hg:`help dates` for a list of formats valid for -d/--date.
6333
6334    See :hg:`help backout` for a way to reverse the effect of an
6335    earlier changeset.
6336
6337    Returns 0 on success.
6338    """
6339
6340    opts = pycompat.byteskwargs(opts)
6341    if opts.get(b"date"):
6342        cmdutil.check_incompatible_arguments(opts, b'date', [b'rev'])
6343        opts[b"rev"] = cmdutil.finddate(ui, repo, opts[b"date"])
6344
6345    parent, p2 = repo.dirstate.parents()
6346    if not opts.get(b'rev') and p2 != repo.nullid:
6347        # revert after merge is a trap for new users (issue2915)
6348        raise error.InputError(
6349            _(b'uncommitted merge with no revision specified'),
6350            hint=_(b"use 'hg update' or see 'hg help revert'"),
6351        )
6352
6353    rev = opts.get(b'rev')
6354    if rev:
6355        repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6356    ctx = logcmdutil.revsingle(repo, rev)
6357
6358    if not (
6359        pats
6360        or opts.get(b'include')
6361        or opts.get(b'exclude')
6362        or opts.get(b'all')
6363        or opts.get(b'interactive')
6364    ):
6365        msg = _(b"no files or directories specified")
6366        if p2 != repo.nullid:
6367            hint = _(
6368                b"uncommitted merge, use --all to discard all changes,"
6369                b" or 'hg update -C .' to abort the merge"
6370            )
6371            raise error.InputError(msg, hint=hint)
6372        dirty = any(repo.status())
6373        node = ctx.node()
6374        if node != parent:
6375            if dirty:
6376                hint = (
6377                    _(
6378                        b"uncommitted changes, use --all to discard all"
6379                        b" changes, or 'hg update %d' to update"
6380                    )
6381                    % ctx.rev()
6382                )
6383            else:
6384                hint = (
6385                    _(
6386                        b"use --all to revert all files,"
6387                        b" or 'hg update %d' to update"
6388                    )
6389                    % ctx.rev()
6390                )
6391        elif dirty:
6392            hint = _(b"uncommitted changes, use --all to discard all changes")
6393        else:
6394            hint = _(b"use --all to revert all files")
6395        raise error.InputError(msg, hint=hint)
6396
6397    return cmdutil.revert(ui, repo, ctx, *pats, **pycompat.strkwargs(opts))
6398
6399
6400@command(
6401    b'rollback',
6402    dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6403    helpcategory=command.CATEGORY_MAINTENANCE,
6404)
6405def rollback(ui, repo, **opts):
6406    """roll back the last transaction (DANGEROUS) (DEPRECATED)
6407
6408    Please use :hg:`commit --amend` instead of rollback to correct
6409    mistakes in the last commit.
6410
6411    This command should be used with care. There is only one level of
6412    rollback, and there is no way to undo a rollback. It will also
6413    restore the dirstate at the time of the last transaction, losing
6414    any dirstate changes since that time. This command does not alter
6415    the working directory.
6416
6417    Transactions are used to encapsulate the effects of all commands
6418    that create new changesets or propagate existing changesets into a
6419    repository.
6420
6421    .. container:: verbose
6422
6423      For example, the following commands are transactional, and their
6424      effects can be rolled back:
6425
6426      - commit
6427      - import
6428      - pull
6429      - push (with this repository as the destination)
6430      - unbundle
6431
6432      To avoid permanent data loss, rollback will refuse to rollback a
6433      commit transaction if it isn't checked out. Use --force to
6434      override this protection.
6435
6436      The rollback command can be entirely disabled by setting the
6437      ``ui.rollback`` configuration setting to false. If you're here
6438      because you want to use rollback and it's disabled, you can
6439      re-enable the command by setting ``ui.rollback`` to true.
6440
6441    This command is not intended for use on public repositories. Once
6442    changes are visible for pull by other users, rolling a transaction
6443    back locally is ineffective (someone else may already have pulled
6444    the changes). Furthermore, a race is possible with readers of the
6445    repository; for example an in-progress pull from the repository
6446    may fail if a rollback is performed.
6447
6448    Returns 0 on success, 1 if no rollback data is available.
6449    """
6450    if not ui.configbool(b'ui', b'rollback'):
6451        raise error.Abort(
6452            _(b'rollback is disabled because it is unsafe'),
6453            hint=b'see `hg help -v rollback` for information',
6454        )
6455    return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
6456
6457
6458@command(
6459    b'root',
6460    [] + formatteropts,
6461    intents={INTENT_READONLY},
6462    helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6463)
6464def root(ui, repo, **opts):
6465    """print the root (top) of the current working directory
6466
6467    Print the root directory of the current repository.
6468
6469    .. container:: verbose
6470
6471      Template:
6472
6473      The following keywords are supported in addition to the common template
6474      keywords and functions. See also :hg:`help templates`.
6475
6476      :hgpath:    String. Path to the .hg directory.
6477      :storepath: String. Path to the directory holding versioned data.
6478
6479    Returns 0 on success.
6480    """
6481    opts = pycompat.byteskwargs(opts)
6482    with ui.formatter(b'root', opts) as fm:
6483        fm.startitem()
6484        fm.write(b'reporoot', b'%s\n', repo.root)
6485        fm.data(hgpath=repo.path, storepath=repo.spath)
6486
6487
6488@command(
6489    b'serve',
6490    [
6491        (
6492            b'A',
6493            b'accesslog',
6494            b'',
6495            _(b'name of access log file to write to'),
6496            _(b'FILE'),
6497        ),
6498        (b'd', b'daemon', None, _(b'run server in background')),
6499        (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6500        (
6501            b'E',
6502            b'errorlog',
6503            b'',
6504            _(b'name of error log file to write to'),
6505            _(b'FILE'),
6506        ),
6507        # use string type, then we can check if something was passed
6508        (
6509            b'p',
6510            b'port',
6511            b'',
6512            _(b'port to listen on (default: 8000)'),
6513            _(b'PORT'),
6514        ),
6515        (
6516            b'a',
6517            b'address',
6518            b'',
6519            _(b'address to listen on (default: all interfaces)'),
6520            _(b'ADDR'),
6521        ),
6522        (
6523            b'',
6524            b'prefix',
6525            b'',
6526            _(b'prefix path to serve from (default: server root)'),
6527            _(b'PREFIX'),
6528        ),
6529        (
6530            b'n',
6531            b'name',
6532            b'',
6533            _(b'name to show in web pages (default: working directory)'),
6534            _(b'NAME'),
6535        ),
6536        (
6537            b'',
6538            b'web-conf',
6539            b'',
6540            _(b"name of the hgweb config file (see 'hg help hgweb')"),
6541            _(b'FILE'),
6542        ),
6543        (
6544            b'',
6545            b'webdir-conf',
6546            b'',
6547            _(b'name of the hgweb config file (DEPRECATED)'),
6548            _(b'FILE'),
6549        ),
6550        (
6551            b'',
6552            b'pid-file',
6553            b'',
6554            _(b'name of file to write process ID to'),
6555            _(b'FILE'),
6556        ),
6557        (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6558        (
6559            b'',
6560            b'cmdserver',
6561            b'',
6562            _(b'for remote clients (ADVANCED)'),
6563            _(b'MODE'),
6564        ),
6565        (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6566        (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6567        (b'6', b'ipv6', None, _(b'use IPv6 in addition to IPv4')),
6568        (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6569        (b'', b'print-url', None, _(b'start and print only the URL')),
6570    ]
6571    + subrepoopts,
6572    _(b'[OPTION]...'),
6573    helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6574    helpbasic=True,
6575    optionalrepo=True,
6576)
6577def serve(ui, repo, **opts):
6578    """start stand-alone webserver
6579
6580    Start a local HTTP repository browser and pull server. You can use
6581    this for ad-hoc sharing and browsing of repositories. It is
6582    recommended to use a real web server to serve a repository for
6583    longer periods of time.
6584
6585    Please note that the server does not implement access control.
6586    This means that, by default, anybody can read from the server and
6587    nobody can write to it by default. Set the ``web.allow-push``
6588    option to ``*`` to allow everybody to push to the server. You
6589    should use a real web server if you need to authenticate users.
6590
6591    By default, the server logs accesses to stdout and errors to
6592    stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6593    files.
6594
6595    To have the server choose a free port number to listen on, specify
6596    a port number of 0; in this case, the server will print the port
6597    number it uses.
6598
6599    Returns 0 on success.
6600    """
6601
6602    cmdutil.check_incompatible_arguments(opts, 'stdio', ['cmdserver'])
6603    opts = pycompat.byteskwargs(opts)
6604    if opts[b"print_url"] and ui.verbose:
6605        raise error.InputError(_(b"cannot use --print-url with --verbose"))
6606
6607    if opts[b"stdio"]:
6608        if repo is None:
6609            raise error.RepoError(
6610                _(b"there is no Mercurial repository here (.hg not found)")
6611            )
6612        s = wireprotoserver.sshserver(ui, repo)
6613        s.serve_forever()
6614        return
6615
6616    service = server.createservice(ui, repo, opts)
6617    return server.runservice(opts, initfn=service.init, runfn=service.run)
6618
6619
6620@command(
6621    b'shelve',
6622    [
6623        (
6624            b'A',
6625            b'addremove',
6626            None,
6627            _(b'mark new/missing files as added/removed before shelving'),
6628        ),
6629        (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6630        (b'', b'cleanup', None, _(b'delete all shelved changes')),
6631        (
6632            b'',
6633            b'date',
6634            b'',
6635            _(b'shelve with the specified commit date'),
6636            _(b'DATE'),
6637        ),
6638        (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6639        (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6640        (
6641            b'k',
6642            b'keep',
6643            False,
6644            _(b'shelve, but keep changes in the working directory'),
6645        ),
6646        (b'l', b'list', None, _(b'list current shelves')),
6647        (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6648        (
6649            b'n',
6650            b'name',
6651            b'',
6652            _(b'use the given name for the shelved commit'),
6653            _(b'NAME'),
6654        ),
6655        (
6656            b'p',
6657            b'patch',
6658            None,
6659            _(
6660                b'output patches for changes (provide the names of the shelved '
6661                b'changes as positional arguments)'
6662            ),
6663        ),
6664        (b'i', b'interactive', None, _(b'interactive mode')),
6665        (
6666            b'',
6667            b'stat',
6668            None,
6669            _(
6670                b'output diffstat-style summary of changes (provide the names of '
6671                b'the shelved changes as positional arguments)'
6672            ),
6673        ),
6674    ]
6675    + cmdutil.walkopts,
6676    _(b'hg shelve [OPTION]... [FILE]...'),
6677    helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6678)
6679def shelve(ui, repo, *pats, **opts):
6680    """save and set aside changes from the working directory
6681
6682    Shelving takes files that "hg status" reports as not clean, saves
6683    the modifications to a bundle (a shelved change), and reverts the
6684    files so that their state in the working directory becomes clean.
6685
6686    To restore these changes to the working directory, using "hg
6687    unshelve"; this will work even if you switch to a different
6688    commit.
6689
6690    When no files are specified, "hg shelve" saves all not-clean
6691    files. If specific files or directories are named, only changes to
6692    those files are shelved.
6693
6694    In bare shelve (when no files are specified, without interactive,
6695    include and exclude option), shelving remembers information if the
6696    working directory was on newly created branch, in other words working
6697    directory was on different branch than its first parent. In this
6698    situation unshelving restores branch information to the working directory.
6699
6700    Each shelved change has a name that makes it easier to find later.
6701    The name of a shelved change defaults to being based on the active
6702    bookmark, or if there is no active bookmark, the current named
6703    branch.  To specify a different name, use ``--name``.
6704
6705    To see a list of existing shelved changes, use the ``--list``
6706    option. For each shelved change, this will print its name, age,
6707    and description; use ``--patch`` or ``--stat`` for more details.
6708
6709    To delete specific shelved changes, use ``--delete``. To delete
6710    all shelved changes, use ``--cleanup``.
6711    """
6712    opts = pycompat.byteskwargs(opts)
6713    allowables = [
6714        (b'addremove', {b'create'}),  # 'create' is pseudo action
6715        (b'unknown', {b'create'}),
6716        (b'cleanup', {b'cleanup'}),
6717        #       ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6718        (b'delete', {b'delete'}),
6719        (b'edit', {b'create'}),
6720        (b'keep', {b'create'}),
6721        (b'list', {b'list'}),
6722        (b'message', {b'create'}),
6723        (b'name', {b'create'}),
6724        (b'patch', {b'patch', b'list'}),
6725        (b'stat', {b'stat', b'list'}),
6726    ]
6727
6728    def checkopt(opt):
6729        if opts.get(opt):
6730            for i, allowable in allowables:
6731                if opts[i] and opt not in allowable:
6732                    raise error.InputError(
6733                        _(
6734                            b"options '--%s' and '--%s' may not be "
6735                            b"used together"
6736                        )
6737                        % (opt, i)
6738                    )
6739            return True
6740
6741    if checkopt(b'cleanup'):
6742        if pats:
6743            raise error.InputError(
6744                _(b"cannot specify names when using '--cleanup'")
6745            )
6746        return shelvemod.cleanupcmd(ui, repo)
6747    elif checkopt(b'delete'):
6748        return shelvemod.deletecmd(ui, repo, pats)
6749    elif checkopt(b'list'):
6750        return shelvemod.listcmd(ui, repo, pats, opts)
6751    elif checkopt(b'patch') or checkopt(b'stat'):
6752        return shelvemod.patchcmds(ui, repo, pats, opts)
6753    else:
6754        return shelvemod.createcmd(ui, repo, pats, opts)
6755
6756
6757_NOTTERSE = b'nothing'
6758
6759
6760@command(
6761    b'status|st',
6762    [
6763        (b'A', b'all', None, _(b'show status of all files')),
6764        (b'm', b'modified', None, _(b'show only modified files')),
6765        (b'a', b'added', None, _(b'show only added files')),
6766        (b'r', b'removed', None, _(b'show only removed files')),
6767        (b'd', b'deleted', None, _(b'show only missing files')),
6768        (b'c', b'clean', None, _(b'show only files without changes')),
6769        (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6770        (b'i', b'ignored', None, _(b'show only ignored files')),
6771        (b'n', b'no-status', None, _(b'hide status prefix')),
6772        (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6773        (
6774            b'C',
6775            b'copies',
6776            None,
6777            _(b'show source of copied files (DEFAULT: ui.statuscopies)'),
6778        ),
6779        (
6780            b'0',
6781            b'print0',
6782            None,
6783            _(b'end filenames with NUL, for use with xargs'),
6784        ),
6785        (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6786        (
6787            b'',
6788            b'change',
6789            b'',
6790            _(b'list the changed files of a revision'),
6791            _(b'REV'),
6792        ),
6793    ]
6794    + walkopts
6795    + subrepoopts
6796    + formatteropts,
6797    _(b'[OPTION]... [FILE]...'),
6798    helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6799    helpbasic=True,
6800    inferrepo=True,
6801    intents={INTENT_READONLY},
6802)
6803def status(ui, repo, *pats, **opts):
6804    """show changed files in the working directory
6805
6806    Show status of files in the repository. If names are given, only
6807    files that match are shown. Files that are clean or ignored or
6808    the source of a copy/move operation, are not listed unless
6809    -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6810    Unless options described with "show only ..." are given, the
6811    options -mardu are used.
6812
6813    Option -q/--quiet hides untracked (unknown and ignored) files
6814    unless explicitly requested with -u/--unknown or -i/--ignored.
6815
6816    .. note::
6817
6818       :hg:`status` may appear to disagree with diff if permissions have
6819       changed or a merge has occurred. The standard diff format does
6820       not report permission changes and diff only reports changes
6821       relative to one merge parent.
6822
6823    If one revision is given, it is used as the base revision.
6824    If two revisions are given, the differences between them are
6825    shown. The --change option can also be used as a shortcut to list
6826    the changed files of a revision from its first parent.
6827
6828    The codes used to show the status of files are::
6829
6830      M = modified
6831      A = added
6832      R = removed
6833      C = clean
6834      ! = missing (deleted by non-hg command, but still tracked)
6835      ? = not tracked
6836      I = ignored
6837        = origin of the previous file (with --copies)
6838
6839    .. container:: verbose
6840
6841      The -t/--terse option abbreviates the output by showing only the directory
6842      name if all the files in it share the same status. The option takes an
6843      argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6844      for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6845      for 'ignored' and 'c' for clean.
6846
6847      It abbreviates only those statuses which are passed. Note that clean and
6848      ignored files are not displayed with '--terse ic' unless the -c/--clean
6849      and -i/--ignored options are also used.
6850
6851      The -v/--verbose option shows information when the repository is in an
6852      unfinished merge, shelve, rebase state etc. You can have this behavior
6853      turned on by default by enabling the ``commands.status.verbose`` option.
6854
6855      You can skip displaying some of these states by setting
6856      ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6857      'histedit', 'merge', 'rebase', or 'unshelve'.
6858
6859      Template:
6860
6861      The following keywords are supported in addition to the common template
6862      keywords and functions. See also :hg:`help templates`.
6863
6864      :path:    String. Repository-absolute path of the file.
6865      :source:  String. Repository-absolute path of the file originated from.
6866                Available if ``--copies`` is specified.
6867      :status:  String. Character denoting file's status.
6868
6869      Examples:
6870
6871      - show changes in the working directory relative to a
6872        changeset::
6873
6874          hg status --rev 9353
6875
6876      - show changes in the working directory relative to the
6877        current directory (see :hg:`help patterns` for more information)::
6878
6879          hg status re:
6880
6881      - show all changes including copies in an existing changeset::
6882
6883          hg status --copies --change 9353
6884
6885      - get a NUL separated list of added files, suitable for xargs::
6886
6887          hg status -an0
6888
6889      - show more information about the repository status, abbreviating
6890        added, removed, modified, deleted, and untracked paths::
6891
6892          hg status -v -t mardu
6893
6894    Returns 0 on success.
6895
6896    """
6897
6898    cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
6899    opts = pycompat.byteskwargs(opts)
6900    revs = opts.get(b'rev')
6901    change = opts.get(b'change')
6902    terse = opts.get(b'terse')
6903    if terse is _NOTTERSE:
6904        if revs:
6905            terse = b''
6906        else:
6907            terse = ui.config(b'commands', b'status.terse')
6908
6909    if revs and terse:
6910        msg = _(b'cannot use --terse with --rev')
6911        raise error.InputError(msg)
6912    elif change:
6913        repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6914        ctx2 = logcmdutil.revsingle(repo, change, None)
6915        ctx1 = ctx2.p1()
6916    else:
6917        repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6918        ctx1, ctx2 = logcmdutil.revpair(repo, revs)
6919
6920    forcerelativevalue = None
6921    if ui.hasconfig(b'commands', b'status.relative'):
6922        forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6923    uipathfn = scmutil.getuipathfn(
6924        repo,
6925        legacyrelativevalue=bool(pats),
6926        forcerelativevalue=forcerelativevalue,
6927    )
6928
6929    if opts.get(b'print0'):
6930        end = b'\0'
6931    else:
6932        end = b'\n'
6933    states = b'modified added removed deleted unknown ignored clean'.split()
6934    show = [k for k in states if opts.get(k)]
6935    if opts.get(b'all'):
6936        show += ui.quiet and (states[:4] + [b'clean']) or states
6937
6938    if not show:
6939        if ui.quiet:
6940            show = states[:4]
6941        else:
6942            show = states[:5]
6943
6944    m = scmutil.match(ctx2, pats, opts)
6945    if terse:
6946        # we need to compute clean and unknown to terse
6947        stat = repo.status(
6948            ctx1.node(),
6949            ctx2.node(),
6950            m,
6951            b'ignored' in show or b'i' in terse,
6952            clean=True,
6953            unknown=True,
6954            listsubrepos=opts.get(b'subrepos'),
6955        )
6956
6957        stat = cmdutil.tersedir(stat, terse)
6958    else:
6959        stat = repo.status(
6960            ctx1.node(),
6961            ctx2.node(),
6962            m,
6963            b'ignored' in show,
6964            b'clean' in show,
6965            b'unknown' in show,
6966            opts.get(b'subrepos'),
6967        )
6968
6969    changestates = zip(
6970        states,
6971        pycompat.iterbytestr(b'MAR!?IC'),
6972        [getattr(stat, s.decode('utf8')) for s in states],
6973    )
6974
6975    copy = {}
6976    if (
6977        opts.get(b'all')
6978        or opts.get(b'copies')
6979        or ui.configbool(b'ui', b'statuscopies')
6980    ) and not opts.get(b'no_status'):
6981        copy = copies.pathcopies(ctx1, ctx2, m)
6982
6983    morestatus = None
6984    if (
6985        (ui.verbose or ui.configbool(b'commands', b'status.verbose'))
6986        and not ui.plain()
6987        and not opts.get(b'print0')
6988    ):
6989        morestatus = cmdutil.readmorestatus(repo)
6990
6991    ui.pager(b'status')
6992    fm = ui.formatter(b'status', opts)
6993    fmt = b'%s' + end
6994    showchar = not opts.get(b'no_status')
6995
6996    for state, char, files in changestates:
6997        if state in show:
6998            label = b'status.' + state
6999            for f in files:
7000                fm.startitem()
7001                fm.context(ctx=ctx2)
7002                fm.data(itemtype=b'file', path=f)
7003                fm.condwrite(showchar, b'status', b'%s ', char, label=label)
7004                fm.plain(fmt % uipathfn(f), label=label)
7005                if f in copy:
7006                    fm.data(source=copy[f])
7007                    fm.plain(
7008                        (b'  %s' + end) % uipathfn(copy[f]),
7009                        label=b'status.copied',
7010                    )
7011                if morestatus:
7012                    morestatus.formatfile(f, fm)
7013
7014    if morestatus:
7015        morestatus.formatfooter(fm)
7016    fm.end()
7017
7018
7019@command(
7020    b'summary|sum',
7021    [(b'', b'remote', None, _(b'check for push and pull'))],
7022    b'[--remote]',
7023    helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7024    helpbasic=True,
7025    intents={INTENT_READONLY},
7026)
7027def summary(ui, repo, **opts):
7028    """summarize working directory state
7029
7030    This generates a brief summary of the working directory state,
7031    including parents, branch, commit status, phase and available updates.
7032
7033    With the --remote option, this will check the default paths for
7034    incoming and outgoing changes. This can be time-consuming.
7035
7036    Returns 0 on success.
7037    """
7038
7039    opts = pycompat.byteskwargs(opts)
7040    ui.pager(b'summary')
7041    ctx = repo[None]
7042    parents = ctx.parents()
7043    pnode = parents[0].node()
7044    marks = []
7045
7046    try:
7047        ms = mergestatemod.mergestate.read(repo)
7048    except error.UnsupportedMergeRecords as e:
7049        s = b' '.join(e.recordtypes)
7050        ui.warn(
7051            _(b'warning: merge state has unsupported record types: %s\n') % s
7052        )
7053        unresolved = []
7054    else:
7055        unresolved = list(ms.unresolved())
7056
7057    for p in parents:
7058        # label with log.changeset (instead of log.parent) since this
7059        # shows a working directory parent *changeset*:
7060        # i18n: column positioning for "hg summary"
7061        ui.write(
7062            _(b'parent: %d:%s ') % (p.rev(), p),
7063            label=logcmdutil.changesetlabels(p),
7064        )
7065        ui.write(b' '.join(p.tags()), label=b'log.tag')
7066        if p.bookmarks():
7067            marks.extend(p.bookmarks())
7068        if p.rev() == -1:
7069            if not len(repo):
7070                ui.write(_(b' (empty repository)'))
7071            else:
7072                ui.write(_(b' (no revision checked out)'))
7073        if p.obsolete():
7074            ui.write(_(b' (obsolete)'))
7075        if p.isunstable():
7076            instabilities = (
7077                ui.label(instability, b'trouble.%s' % instability)
7078                for instability in p.instabilities()
7079            )
7080            ui.write(b' (' + b', '.join(instabilities) + b')')
7081        ui.write(b'\n')
7082        if p.description():
7083            ui.status(
7084                b' ' + p.description().splitlines()[0].strip() + b'\n',
7085                label=b'log.summary',
7086            )
7087
7088    branch = ctx.branch()
7089    bheads = repo.branchheads(branch)
7090    # i18n: column positioning for "hg summary"
7091    m = _(b'branch: %s\n') % branch
7092    if branch != b'default':
7093        ui.write(m, label=b'log.branch')
7094    else:
7095        ui.status(m, label=b'log.branch')
7096
7097    if marks:
7098        active = repo._activebookmark
7099        # i18n: column positioning for "hg summary"
7100        ui.write(_(b'bookmarks:'), label=b'log.bookmark')
7101        if active is not None:
7102            if active in marks:
7103                ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
7104                marks.remove(active)
7105            else:
7106                ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
7107        for m in marks:
7108            ui.write(b' ' + m, label=b'log.bookmark')
7109        ui.write(b'\n', label=b'log.bookmark')
7110
7111    status = repo.status(unknown=True)
7112
7113    c = repo.dirstate.copies()
7114    copied, renamed = [], []
7115    for d, s in pycompat.iteritems(c):
7116        if s in status.removed:
7117            status.removed.remove(s)
7118            renamed.append(d)
7119        else:
7120            copied.append(d)
7121        if d in status.added:
7122            status.added.remove(d)
7123
7124    subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
7125
7126    labels = [
7127        (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
7128        (ui.label(_(b'%d added'), b'status.added'), status.added),
7129        (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
7130        (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
7131        (ui.label(_(b'%d copied'), b'status.copied'), copied),
7132        (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
7133        (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
7134        (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
7135        (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
7136    ]
7137    t = []
7138    for l, s in labels:
7139        if s:
7140            t.append(l % len(s))
7141
7142    t = b', '.join(t)
7143    cleanworkdir = False
7144
7145    if repo.vfs.exists(b'graftstate'):
7146        t += _(b' (graft in progress)')
7147    if repo.vfs.exists(b'updatestate'):
7148        t += _(b' (interrupted update)')
7149    elif len(parents) > 1:
7150        t += _(b' (merge)')
7151    elif branch != parents[0].branch():
7152        t += _(b' (new branch)')
7153    elif parents[0].closesbranch() and pnode in repo.branchheads(
7154        branch, closed=True
7155    ):
7156        t += _(b' (head closed)')
7157    elif not (
7158        status.modified
7159        or status.added
7160        or status.removed
7161        or renamed
7162        or copied
7163        or subs
7164    ):
7165        t += _(b' (clean)')
7166        cleanworkdir = True
7167    elif pnode not in bheads:
7168        t += _(b' (new branch head)')
7169
7170    if parents:
7171        pendingphase = max(p.phase() for p in parents)
7172    else:
7173        pendingphase = phases.public
7174
7175    if pendingphase > phases.newcommitphase(ui):
7176        t += b' (%s)' % phases.phasenames[pendingphase]
7177
7178    if cleanworkdir:
7179        # i18n: column positioning for "hg summary"
7180        ui.status(_(b'commit: %s\n') % t.strip())
7181    else:
7182        # i18n: column positioning for "hg summary"
7183        ui.write(_(b'commit: %s\n') % t.strip())
7184
7185    # all ancestors of branch heads - all ancestors of parent = new csets
7186    new = len(
7187        repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
7188    )
7189
7190    if new == 0:
7191        # i18n: column positioning for "hg summary"
7192        ui.status(_(b'update: (current)\n'))
7193    elif pnode not in bheads:
7194        # i18n: column positioning for "hg summary"
7195        ui.write(_(b'update: %d new changesets (update)\n') % new)
7196    else:
7197        # i18n: column positioning for "hg summary"
7198        ui.write(
7199            _(b'update: %d new changesets, %d branch heads (merge)\n')
7200            % (new, len(bheads))
7201        )
7202
7203    t = []
7204    draft = len(repo.revs(b'draft()'))
7205    if draft:
7206        t.append(_(b'%d draft') % draft)
7207    secret = len(repo.revs(b'secret()'))
7208    if secret:
7209        t.append(_(b'%d secret') % secret)
7210
7211    if draft or secret:
7212        ui.status(_(b'phases: %s\n') % b', '.join(t))
7213
7214    if obsolete.isenabled(repo, obsolete.createmarkersopt):
7215        for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
7216            numtrouble = len(repo.revs(trouble + b"()"))
7217            # We write all the possibilities to ease translation
7218            troublemsg = {
7219                b"orphan": _(b"orphan: %d changesets"),
7220                b"contentdivergent": _(b"content-divergent: %d changesets"),
7221                b"phasedivergent": _(b"phase-divergent: %d changesets"),
7222            }
7223            if numtrouble > 0:
7224                ui.status(troublemsg[trouble] % numtrouble + b"\n")
7225
7226    cmdutil.summaryhooks(ui, repo)
7227
7228    if opts.get(b'remote'):
7229        needsincoming, needsoutgoing = True, True
7230    else:
7231        needsincoming, needsoutgoing = False, False
7232        for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
7233            if i:
7234                needsincoming = True
7235            if o:
7236                needsoutgoing = True
7237        if not needsincoming and not needsoutgoing:
7238            return
7239
7240    def getincoming():
7241        # XXX We should actually skip this if no default is specified, instead
7242        # of passing "default" which will resolve as "./default/" if no default
7243        # path is defined.
7244        source, branches = urlutil.get_unique_pull_path(
7245            b'summary', repo, ui, b'default'
7246        )
7247        sbranch = branches[0]
7248        try:
7249            other = hg.peer(repo, {}, source)
7250        except error.RepoError:
7251            if opts.get(b'remote'):
7252                raise
7253            return source, sbranch, None, None, None
7254        revs, checkout = hg.addbranchrevs(repo, other, branches, None)
7255        if revs:
7256            revs = [other.lookup(rev) for rev in revs]
7257        ui.debug(b'comparing with %s\n' % urlutil.hidepassword(source))
7258        with repo.ui.silent():
7259            commoninc = discovery.findcommonincoming(repo, other, heads=revs)
7260        return source, sbranch, other, commoninc, commoninc[1]
7261
7262    if needsincoming:
7263        source, sbranch, sother, commoninc, incoming = getincoming()
7264    else:
7265        source = sbranch = sother = commoninc = incoming = None
7266
7267    def getoutgoing():
7268        # XXX We should actually skip this if no default is specified, instead
7269        # of passing "default" which will resolve as "./default/" if no default
7270        # path is defined.
7271        d = None
7272        if b'default-push' in ui.paths:
7273            d = b'default-push'
7274        elif b'default' in ui.paths:
7275            d = b'default'
7276        if d is not None:
7277            path = urlutil.get_unique_push_path(b'summary', repo, ui, d)
7278            dest = path.pushloc or path.loc
7279            dbranch = path.branch
7280        else:
7281            dest = b'default'
7282            dbranch = None
7283        revs, checkout = hg.addbranchrevs(repo, repo, (dbranch, []), None)
7284        if source != dest:
7285            try:
7286                dother = hg.peer(repo, {}, dest)
7287            except error.RepoError:
7288                if opts.get(b'remote'):
7289                    raise
7290                return dest, dbranch, None, None
7291            ui.debug(b'comparing with %s\n' % urlutil.hidepassword(dest))
7292        elif sother is None:
7293            # there is no explicit destination peer, but source one is invalid
7294            return dest, dbranch, None, None
7295        else:
7296            dother = sother
7297        if source != dest or (sbranch is not None and sbranch != dbranch):
7298            common = None
7299        else:
7300            common = commoninc
7301        if revs:
7302            revs = [repo.lookup(rev) for rev in revs]
7303        with repo.ui.silent():
7304            outgoing = discovery.findcommonoutgoing(
7305                repo, dother, onlyheads=revs, commoninc=common
7306            )
7307        return dest, dbranch, dother, outgoing
7308
7309    if needsoutgoing:
7310        dest, dbranch, dother, outgoing = getoutgoing()
7311    else:
7312        dest = dbranch = dother = outgoing = None
7313
7314    if opts.get(b'remote'):
7315        # Help pytype.  --remote sets both `needsincoming` and `needsoutgoing`.
7316        # The former always sets `sother` (or raises an exception if it can't);
7317        # the latter always sets `outgoing`.
7318        assert sother is not None
7319        assert outgoing is not None
7320
7321        t = []
7322        if incoming:
7323            t.append(_(b'1 or more incoming'))
7324        o = outgoing.missing
7325        if o:
7326            t.append(_(b'%d outgoing') % len(o))
7327        other = dother or sother
7328        if b'bookmarks' in other.listkeys(b'namespaces'):
7329            counts = bookmarks.summary(repo, other)
7330            if counts[0] > 0:
7331                t.append(_(b'%d incoming bookmarks') % counts[0])
7332            if counts[1] > 0:
7333                t.append(_(b'%d outgoing bookmarks') % counts[1])
7334
7335        if t:
7336            # i18n: column positioning for "hg summary"
7337            ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7338        else:
7339            # i18n: column positioning for "hg summary"
7340            ui.status(_(b'remote: (synced)\n'))
7341
7342    cmdutil.summaryremotehooks(
7343        ui,
7344        repo,
7345        opts,
7346        (
7347            (source, sbranch, sother, commoninc),
7348            (dest, dbranch, dother, outgoing),
7349        ),
7350    )
7351
7352
7353@command(
7354    b'tag',
7355    [
7356        (b'f', b'force', None, _(b'force tag')),
7357        (b'l', b'local', None, _(b'make the tag local')),
7358        (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7359        (b'', b'remove', None, _(b'remove a tag')),
7360        # -l/--local is already there, commitopts cannot be used
7361        (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7362        (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7363    ]
7364    + commitopts2,
7365    _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7366    helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7367)
7368def tag(ui, repo, name1, *names, **opts):
7369    """add one or more tags for the current or given revision
7370
7371    Name a particular revision using <name>.
7372
7373    Tags are used to name particular revisions of the repository and are
7374    very useful to compare different revisions, to go back to significant
7375    earlier versions or to mark branch points as releases, etc. Changing
7376    an existing tag is normally disallowed; use -f/--force to override.
7377
7378    If no revision is given, the parent of the working directory is
7379    used.
7380
7381    To facilitate version control, distribution, and merging of tags,
7382    they are stored as a file named ".hgtags" which is managed similarly
7383    to other project files and can be hand-edited if necessary. This
7384    also means that tagging creates a new commit. The file
7385    ".hg/localtags" is used for local tags (not shared among
7386    repositories).
7387
7388    Tag commits are usually made at the head of a branch. If the parent
7389    of the working directory is not a branch head, :hg:`tag` aborts; use
7390    -f/--force to force the tag commit to be based on a non-head
7391    changeset.
7392
7393    See :hg:`help dates` for a list of formats valid for -d/--date.
7394
7395    Since tag names have priority over branch names during revision
7396    lookup, using an existing branch name as a tag name is discouraged.
7397
7398    Returns 0 on success.
7399    """
7400    cmdutil.check_incompatible_arguments(opts, 'remove', ['rev'])
7401    opts = pycompat.byteskwargs(opts)
7402    with repo.wlock(), repo.lock():
7403        rev_ = b"."
7404        names = [t.strip() for t in (name1,) + names]
7405        if len(names) != len(set(names)):
7406            raise error.InputError(_(b'tag names must be unique'))
7407        for n in names:
7408            scmutil.checknewlabel(repo, n, b'tag')
7409            if not n:
7410                raise error.InputError(
7411                    _(b'tag names cannot consist entirely of whitespace')
7412                )
7413        if opts.get(b'rev'):
7414            rev_ = opts[b'rev']
7415        message = opts.get(b'message')
7416        if opts.get(b'remove'):
7417            if opts.get(b'local'):
7418                expectedtype = b'local'
7419            else:
7420                expectedtype = b'global'
7421
7422            for n in names:
7423                if repo.tagtype(n) == b'global':
7424                    alltags = tagsmod.findglobaltags(ui, repo)
7425                    if alltags[n][0] == repo.nullid:
7426                        raise error.InputError(
7427                            _(b"tag '%s' is already removed") % n
7428                        )
7429                if not repo.tagtype(n):
7430                    raise error.InputError(_(b"tag '%s' does not exist") % n)
7431                if repo.tagtype(n) != expectedtype:
7432                    if expectedtype == b'global':
7433                        raise error.InputError(
7434                            _(b"tag '%s' is not a global tag") % n
7435                        )
7436                    else:
7437                        raise error.InputError(
7438                            _(b"tag '%s' is not a local tag") % n
7439                        )
7440            rev_ = b'null'
7441            if not message:
7442                # we don't translate commit messages
7443                message = b'Removed tag %s' % b', '.join(names)
7444        elif not opts.get(b'force'):
7445            for n in names:
7446                if n in repo.tags():
7447                    raise error.InputError(
7448                        _(b"tag '%s' already exists (use -f to force)") % n
7449                    )
7450        if not opts.get(b'local'):
7451            p1, p2 = repo.dirstate.parents()
7452            if p2 != repo.nullid:
7453                raise error.StateError(_(b'uncommitted merge'))
7454            bheads = repo.branchheads()
7455            if not opts.get(b'force') and bheads and p1 not in bheads:
7456                raise error.InputError(
7457                    _(
7458                        b'working directory is not at a branch head '
7459                        b'(use -f to force)'
7460                    )
7461                )
7462        node = logcmdutil.revsingle(repo, rev_).node()
7463
7464        if not message:
7465            # we don't translate commit messages
7466            message = b'Added tag %s for changeset %s' % (
7467                b', '.join(names),
7468                short(node),
7469            )
7470
7471        date = opts.get(b'date')
7472        if date:
7473            date = dateutil.parsedate(date)
7474
7475        if opts.get(b'remove'):
7476            editform = b'tag.remove'
7477        else:
7478            editform = b'tag.add'
7479        editor = cmdutil.getcommiteditor(
7480            editform=editform, **pycompat.strkwargs(opts)
7481        )
7482
7483        # don't allow tagging the null rev
7484        if (
7485            not opts.get(b'remove')
7486            and logcmdutil.revsingle(repo, rev_).rev() == nullrev
7487        ):
7488            raise error.InputError(_(b"cannot tag null revision"))
7489
7490        tagsmod.tag(
7491            repo,
7492            names,
7493            node,
7494            message,
7495            opts.get(b'local'),
7496            opts.get(b'user'),
7497            date,
7498            editor=editor,
7499        )
7500
7501
7502@command(
7503    b'tags',
7504    formatteropts,
7505    b'',
7506    helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7507    intents={INTENT_READONLY},
7508)
7509def tags(ui, repo, **opts):
7510    """list repository tags
7511
7512    This lists both regular and local tags. When the -v/--verbose
7513    switch is used, a third column "local" is printed for local tags.
7514    When the -q/--quiet switch is used, only the tag name is printed.
7515
7516    .. container:: verbose
7517
7518      Template:
7519
7520      The following keywords are supported in addition to the common template
7521      keywords and functions such as ``{tag}``. See also
7522      :hg:`help templates`.
7523
7524      :type:    String. ``local`` for local tags.
7525
7526    Returns 0 on success.
7527    """
7528
7529    opts = pycompat.byteskwargs(opts)
7530    ui.pager(b'tags')
7531    fm = ui.formatter(b'tags', opts)
7532    hexfunc = fm.hexfunc
7533
7534    for t, n in reversed(repo.tagslist()):
7535        hn = hexfunc(n)
7536        label = b'tags.normal'
7537        tagtype = repo.tagtype(t)
7538        if not tagtype or tagtype == b'global':
7539            tagtype = b''
7540        else:
7541            label = b'tags.' + tagtype
7542
7543        fm.startitem()
7544        fm.context(repo=repo)
7545        fm.write(b'tag', b'%s', t, label=label)
7546        fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7547        fm.condwrite(
7548            not ui.quiet,
7549            b'rev node',
7550            fmt,
7551            repo.changelog.rev(n),
7552            hn,
7553            label=label,
7554        )
7555        fm.condwrite(
7556            ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7557        )
7558        fm.plain(b'\n')
7559    fm.end()
7560
7561
7562@command(
7563    b'tip',
7564    [
7565        (b'p', b'patch', None, _(b'show patch')),
7566        (b'g', b'git', None, _(b'use git extended diff format')),
7567    ]
7568    + templateopts,
7569    _(b'[-p] [-g]'),
7570    helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7571)
7572def tip(ui, repo, **opts):
7573    """show the tip revision (DEPRECATED)
7574
7575    The tip revision (usually just called the tip) is the changeset
7576    most recently added to the repository (and therefore the most
7577    recently changed head).
7578
7579    If you have just made a commit, that commit will be the tip. If
7580    you have just pulled changes from another repository, the tip of
7581    that repository becomes the current tip. The "tip" tag is special
7582    and cannot be renamed or assigned to a different changeset.
7583
7584    This command is deprecated, please use :hg:`heads` instead.
7585
7586    Returns 0 on success.
7587    """
7588    opts = pycompat.byteskwargs(opts)
7589    displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7590    displayer.show(repo[b'tip'])
7591    displayer.close()
7592
7593
7594@command(
7595    b'unbundle',
7596    [
7597        (
7598            b'u',
7599            b'update',
7600            None,
7601            _(b'update to new branch head if changesets were unbundled'),
7602        )
7603    ],
7604    _(b'[-u] FILE...'),
7605    helpcategory=command.CATEGORY_IMPORT_EXPORT,
7606)
7607def unbundle(ui, repo, fname1, *fnames, **opts):
7608    """apply one or more bundle files
7609
7610    Apply one or more bundle files generated by :hg:`bundle`.
7611
7612    Returns 0 on success, 1 if an update has unresolved files.
7613    """
7614    fnames = (fname1,) + fnames
7615
7616    with repo.lock():
7617        for fname in fnames:
7618            f = hg.openpath(ui, fname)
7619            gen = exchange.readbundle(ui, f, fname)
7620            if isinstance(gen, streamclone.streamcloneapplier):
7621                raise error.InputError(
7622                    _(
7623                        b'packed bundles cannot be applied with '
7624                        b'"hg unbundle"'
7625                    ),
7626                    hint=_(b'use "hg debugapplystreamclonebundle"'),
7627                )
7628            url = b'bundle:' + fname
7629            try:
7630                txnname = b'unbundle'
7631                if not isinstance(gen, bundle2.unbundle20):
7632                    txnname = b'unbundle\n%s' % urlutil.hidepassword(url)
7633                with repo.transaction(txnname) as tr:
7634                    op = bundle2.applybundle(
7635                        repo, gen, tr, source=b'unbundle', url=url
7636                    )
7637            except error.BundleUnknownFeatureError as exc:
7638                raise error.Abort(
7639                    _(b'%s: unknown bundle feature, %s') % (fname, exc),
7640                    hint=_(
7641                        b"see https://mercurial-scm.org/"
7642                        b"wiki/BundleFeature for more "
7643                        b"information"
7644                    ),
7645                )
7646            modheads = bundle2.combinechangegroupresults(op)
7647
7648    if postincoming(ui, repo, modheads, opts.get('update'), None, None):
7649        return 1
7650    else:
7651        return 0
7652
7653
7654@command(
7655    b'unshelve',
7656    [
7657        (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7658        (
7659            b'c',
7660            b'continue',
7661            None,
7662            _(b'continue an incomplete unshelve operation'),
7663        ),
7664        (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7665        (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7666        (
7667            b'n',
7668            b'name',
7669            b'',
7670            _(b'restore shelved change with given name'),
7671            _(b'NAME'),
7672        ),
7673        (b't', b'tool', b'', _(b'specify merge tool')),
7674        (
7675            b'',
7676            b'date',
7677            b'',
7678            _(b'set date for temporary commits (DEPRECATED)'),
7679            _(b'DATE'),
7680        ),
7681    ],
7682    _(b'hg unshelve [OPTION]... [[-n] SHELVED]'),
7683    helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7684)
7685def unshelve(ui, repo, *shelved, **opts):
7686    """restore a shelved change to the working directory
7687
7688    This command accepts an optional name of a shelved change to
7689    restore. If none is given, the most recent shelved change is used.
7690
7691    If a shelved change is applied successfully, the bundle that
7692    contains the shelved changes is moved to a backup location
7693    (.hg/shelve-backup).
7694
7695    Since you can restore a shelved change on top of an arbitrary
7696    commit, it is possible that unshelving will result in a conflict
7697    between your changes and the commits you are unshelving onto. If
7698    this occurs, you must resolve the conflict, then use
7699    ``--continue`` to complete the unshelve operation. (The bundle
7700    will not be moved until you successfully complete the unshelve.)
7701
7702    (Alternatively, you can use ``--abort`` to abandon an unshelve
7703    that causes a conflict. This reverts the unshelved changes, and
7704    leaves the bundle in place.)
7705
7706    If bare shelved change (without interactive, include and exclude
7707    option) was done on newly created branch it would restore branch
7708    information to the working directory.
7709
7710    After a successful unshelve, the shelved changes are stored in a
7711    backup directory. Only the N most recent backups are kept. N
7712    defaults to 10 but can be overridden using the ``shelve.maxbackups``
7713    configuration option.
7714
7715    .. container:: verbose
7716
7717       Timestamp in seconds is used to decide order of backups. More
7718       than ``maxbackups`` backups are kept, if same timestamp
7719       prevents from deciding exact order of them, for safety.
7720
7721       Selected changes can be unshelved with ``--interactive`` flag.
7722       The working directory is updated with the selected changes, and
7723       only the unselected changes remain shelved.
7724       Note: The whole shelve is applied to working directory first before
7725       running interactively. So, this will bring up all the conflicts between
7726       working directory and the shelve, irrespective of which changes will be
7727       unshelved.
7728    """
7729    with repo.wlock():
7730        return shelvemod.unshelvecmd(ui, repo, *shelved, **opts)
7731
7732
7733statemod.addunfinished(
7734    b'unshelve',
7735    fname=b'shelvedstate',
7736    continueflag=True,
7737    abortfunc=shelvemod.hgabortunshelve,
7738    continuefunc=shelvemod.hgcontinueunshelve,
7739    cmdmsg=_(b'unshelve already in progress'),
7740)
7741
7742
7743@command(
7744    b'update|up|checkout|co',
7745    [
7746        (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7747        (b'c', b'check', None, _(b'require clean working directory')),
7748        (b'm', b'merge', None, _(b'merge uncommitted changes')),
7749        (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7750        (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7751    ]
7752    + mergetoolopts,
7753    _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7754    helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7755    helpbasic=True,
7756)
7757def update(ui, repo, node=None, **opts):
7758    """update working directory (or switch revisions)
7759
7760    Update the repository's working directory to the specified
7761    changeset. If no changeset is specified, update to the tip of the
7762    current named branch and move the active bookmark (see :hg:`help
7763    bookmarks`).
7764
7765    Update sets the working directory's parent revision to the specified
7766    changeset (see :hg:`help parents`).
7767
7768    If the changeset is not a descendant or ancestor of the working
7769    directory's parent and there are uncommitted changes, the update is
7770    aborted. With the -c/--check option, the working directory is checked
7771    for uncommitted changes; if none are found, the working directory is
7772    updated to the specified changeset.
7773
7774    .. container:: verbose
7775
7776      The -C/--clean, -c/--check, and -m/--merge options control what
7777      happens if the working directory contains uncommitted changes.
7778      At most of one of them can be specified.
7779
7780      1. If no option is specified, and if
7781         the requested changeset is an ancestor or descendant of
7782         the working directory's parent, the uncommitted changes
7783         are merged into the requested changeset and the merged
7784         result is left uncommitted. If the requested changeset is
7785         not an ancestor or descendant (that is, it is on another
7786         branch), the update is aborted and the uncommitted changes
7787         are preserved.
7788
7789      2. With the -m/--merge option, the update is allowed even if the
7790         requested changeset is not an ancestor or descendant of
7791         the working directory's parent.
7792
7793      3. With the -c/--check option, the update is aborted and the
7794         uncommitted changes are preserved.
7795
7796      4. With the -C/--clean option, uncommitted changes are discarded and
7797         the working directory is updated to the requested changeset.
7798
7799    To cancel an uncommitted merge (and lose your changes), use
7800    :hg:`merge --abort`.
7801
7802    Use null as the changeset to remove the working directory (like
7803    :hg:`clone -U`).
7804
7805    If you want to revert just one file to an older revision, use
7806    :hg:`revert [-r REV] NAME`.
7807
7808    See :hg:`help dates` for a list of formats valid for -d/--date.
7809
7810    Returns 0 on success, 1 if there are unresolved files.
7811    """
7812    cmdutil.check_at_most_one_arg(opts, 'clean', 'check', 'merge')
7813    rev = opts.get('rev')
7814    date = opts.get('date')
7815    clean = opts.get('clean')
7816    check = opts.get('check')
7817    merge = opts.get('merge')
7818    if rev and node:
7819        raise error.InputError(_(b"please specify just one revision"))
7820
7821    if ui.configbool(b'commands', b'update.requiredest'):
7822        if not node and not rev and not date:
7823            raise error.InputError(
7824                _(b'you must specify a destination'),
7825                hint=_(b'for example: hg update ".::"'),
7826            )
7827
7828    if rev is None or rev == b'':
7829        rev = node
7830
7831    if date and rev is not None:
7832        raise error.InputError(_(b"you can't specify a revision and a date"))
7833
7834    updatecheck = None
7835    if check:
7836        updatecheck = b'abort'
7837    elif merge:
7838        updatecheck = b'none'
7839
7840    with repo.wlock():
7841        cmdutil.clearunfinished(repo)
7842        if date:
7843            rev = cmdutil.finddate(ui, repo, date)
7844
7845        # if we defined a bookmark, we have to remember the original name
7846        brev = rev
7847        if rev:
7848            repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7849        ctx = logcmdutil.revsingle(repo, rev, default=None)
7850        rev = ctx.rev()
7851        hidden = ctx.hidden()
7852        overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
7853        with ui.configoverride(overrides, b'update'):
7854            ret = hg.updatetotally(
7855                ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7856            )
7857        if hidden:
7858            ctxstr = ctx.hex()[:12]
7859            ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7860
7861            if ctx.obsolete():
7862                obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7863                ui.warn(b"(%s)\n" % obsfatemsg)
7864        return ret
7865
7866
7867@command(
7868    b'verify',
7869    [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7870    helpcategory=command.CATEGORY_MAINTENANCE,
7871)
7872def verify(ui, repo, **opts):
7873    """verify the integrity of the repository
7874
7875    Verify the integrity of the current repository.
7876
7877    This will perform an extensive check of the repository's
7878    integrity, validating the hashes and checksums of each entry in
7879    the changelog, manifest, and tracked files, as well as the
7880    integrity of their crosslinks and indices.
7881
7882    Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7883    for more information about recovery from corruption of the
7884    repository.
7885
7886    Returns 0 on success, 1 if errors are encountered.
7887    """
7888    opts = pycompat.byteskwargs(opts)
7889
7890    level = None
7891    if opts[b'full']:
7892        level = verifymod.VERIFY_FULL
7893    return hg.verify(repo, level)
7894
7895
7896@command(
7897    b'version',
7898    [] + formatteropts,
7899    helpcategory=command.CATEGORY_HELP,
7900    norepo=True,
7901    intents={INTENT_READONLY},
7902)
7903def version_(ui, **opts):
7904    """output version and copyright information
7905
7906    .. container:: verbose
7907
7908      Template:
7909
7910      The following keywords are supported. See also :hg:`help templates`.
7911
7912      :extensions: List of extensions.
7913      :ver:     String. Version number.
7914
7915      And each entry of ``{extensions}`` provides the following sub-keywords
7916      in addition to ``{ver}``.
7917
7918      :bundled: Boolean. True if included in the release.
7919      :name:    String. Extension name.
7920    """
7921    opts = pycompat.byteskwargs(opts)
7922    if ui.verbose:
7923        ui.pager(b'version')
7924    fm = ui.formatter(b"version", opts)
7925    fm.startitem()
7926    fm.write(
7927        b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
7928    )
7929    license = _(
7930        b"(see https://mercurial-scm.org for more information)\n"
7931        b"\nCopyright (C) 2005-2021 Olivia Mackall and others\n"
7932        b"This is free software; see the source for copying conditions. "
7933        b"There is NO\nwarranty; "
7934        b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7935    )
7936    if not ui.quiet:
7937        fm.plain(license)
7938
7939    if ui.verbose:
7940        fm.plain(_(b"\nEnabled extensions:\n\n"))
7941    # format names and versions into columns
7942    names = []
7943    vers = []
7944    isinternals = []
7945    for name, module in sorted(extensions.extensions()):
7946        names.append(name)
7947        vers.append(extensions.moduleversion(module) or None)
7948        isinternals.append(extensions.ismoduleinternal(module))
7949    fn = fm.nested(b"extensions", tmpl=b'{name}\n')
7950    if names:
7951        namefmt = b"  %%-%ds  " % max(len(n) for n in names)
7952        places = [_(b"external"), _(b"internal")]
7953        for n, v, p in zip(names, vers, isinternals):
7954            fn.startitem()
7955            fn.condwrite(ui.verbose, b"name", namefmt, n)
7956            if ui.verbose:
7957                fn.plain(b"%s  " % places[p])
7958            fn.data(bundled=p)
7959            fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
7960            if ui.verbose:
7961                fn.plain(b"\n")
7962    fn.end()
7963    fm.end()
7964
7965
7966def loadcmdtable(ui, name, cmdtable):
7967    """Load command functions from specified cmdtable"""
7968    overrides = [cmd for cmd in cmdtable if cmd in table]
7969    if overrides:
7970        ui.warn(
7971            _(b"extension '%s' overrides commands: %s\n")
7972            % (name, b" ".join(overrides))
7973        )
7974    table.update(cmdtable)
7975