Mercurial > hg
changeset 49473:f59e49f6bee4
branching: merge stable into default
author | Raphaël Gomès <rgomes@octobus.net> |
---|---|
date | Thu, 01 Sep 2022 16:51:26 +0200 |
parents | 5acbc550d987 (diff) eb800db3d9cf (current diff) |
children | a7a5740b322a |
files | mercurial/debugcommands.py mercurial/localrepo.py |
diffstat | 43 files changed, 877 insertions(+), 155 deletions(-) [+] |
line wrap: on
line diff
--- a/Makefile Thu Sep 01 16:44:00 2022 +0200 +++ b/Makefile Thu Sep 01 16:51:26 2022 +0200 @@ -203,9 +203,11 @@ packaging_targets := \ rhel7 \ rhel8 \ + rhel9 \ deb \ docker-rhel7 \ docker-rhel8 \ + docker-rhel9 \ docker-debian-bullseye \ docker-debian-buster \ docker-debian-stretch \
--- a/contrib/heptapod-ci.yml Thu Sep 01 16:44:00 2022 +0200 +++ b/contrib/heptapod-ci.yml Thu Sep 01 16:51:26 2022 +0200 @@ -89,7 +89,7 @@ - hg -R /tmp/mercurial-ci/ update `hg log --rev '.' --template '{node}'` - cd /tmp/mercurial-ci/ - make local PYTHON=$PYTHON - - $PYTHON -m pip install --user -U pytype==2021.04.15 + - $PYTHON -m pip install --user -U libcst==0.3.20 pytype==2022.03.29 variables: RUNTEST_ARGS: " --allow-slow-tests tests/test-check-pytype.t" HGTEST_SLOWTIMEOUT: "3600"
--- a/contrib/packaging/Makefile Thu Sep 01 16:44:00 2022 +0200 +++ b/contrib/packaging/Makefile Thu Sep 01 16:51:26 2022 +0200 @@ -15,7 +15,8 @@ RHEL_RELEASES := \ 7 \ - 8 + 8 \ + 9 # Build a Python for these RHEL (and derivatives) releases. RHEL_WITH_PYTHON_RELEASES :=
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/packaging/docker/rhel9 Thu Sep 01 16:51:26 2022 +0200 @@ -0,0 +1,25 @@ +FROM rockylinux/rockylinux:9 + +RUN groupadd -g %GID% build && \ + useradd -u %UID% -g %GID% -s /bin/bash -d /build -m build + +RUN dnf install 'dnf-command(config-manager)' -y +# crb repository is necessary for docutils +RUN dnf config-manager --set-enabled crb + +RUN yum install -y \ + gcc \ + gettext \ + make \ + python3-devel \ + python3-docutils \ + rpm-build + +# For creating repo meta data +RUN yum install -y createrepo + +# For rust extensions +RUN yum install -y cargo + +# avoid incorrect docker image permissions on /tmp preventing writes by non-root users +RUN chmod 1777 /tmp
--- a/contrib/packaging/packagelib.sh Thu Sep 01 16:44:00 2022 +0200 +++ b/contrib/packaging/packagelib.sh Thu Sep 01 16:51:26 2022 +0200 @@ -9,7 +9,7 @@ # node: the node|short hg was built from, or empty if built from a tag gethgversion() { if [ -z "${1+x}" ]; then - python="python" + python="python3" else python="$1" fi
--- a/contrib/perf-utils/compare-discovery-case Thu Sep 01 16:44:00 2022 +0200 +++ b/contrib/perf-utils/compare-discovery-case Thu Sep 01 16:51:26 2022 +0200 @@ -97,6 +97,16 @@ assert set(VARIANTS.keys()) == set(VARIANTS_KEYS) +def parse_case(case): + case_type, case_args = case.split('-', 1) + if case_type == 'file': + case_args = (case_args,) + else: + case_args = tuple(int(x) for x in case_args.split('-')) + case = (case_type,) + case_args + return case + + def format_case(case): return '-'.join(str(s) for s in case) @@ -109,12 +119,41 @@ return '::randomantichain(all(), "%d")' % case[1] elif t == 'rev': return '::%d' % case[1] + elif t == 'file': + return '::nodefromfile("%s")' % case[1] else: assert False -def compare(repo, local_case, remote_case): +def compare( + repo, + local_case, + remote_case, + display_header=True, + display_case=True, +): case = (repo, local_case, remote_case) + if display_header: + pieces = ['#'] + if display_case: + pieces += [ + "repo", + "local-subset", + "remote-subset", + ] + + pieces += [ + "discovery-variant", + "roundtrips", + "queries", + "revs", + "local-heads", + "common-heads", + "undecided-initial", + "undecided-common", + "undecided-missing", + ] + print(*pieces) for variant in VARIANTS_KEYS: res = process(case, VARIANTS[variant]) revs = res["nb-revs"] @@ -122,36 +161,31 @@ common_heads = res["nb-common-heads"] roundtrips = res["total-roundtrips"] queries = res["total-queries"] - if 'tree-discovery' in variant: - print( + pieces = [] + if display_case: + pieces += [ repo, format_case(local_case), format_case(remote_case), - variant, - roundtrips, - queries, - revs, - local_heads, - common_heads, - ) - else: + ] + pieces += [ + variant, + roundtrips, + queries, + revs, + local_heads, + common_heads, + ] + if 'tree-discovery' not in variant: undecided_common = res["nb-ini_und-common"] undecided_missing = res["nb-ini_und-missing"] undecided = undecided_common + undecided_missing - print( - repo, - format_case(local_case), - format_case(remote_case), - variant, - roundtrips, - queries, - revs, - local_heads, - common_heads, + pieces += [ undecided, undecided_common, undecided_missing, - ) + ] + print(*pieces) return 0 @@ -171,13 +205,23 @@ if __name__ == '__main__': - if len(sys.argv) != 4: + + argv = sys.argv[:] + + kwargs = {} + # primitive arg parsing + if '--no-header' in argv: + kwargs['display_header'] = False + argv = [a for a in argv if a != '--no-header'] + if '--no-case' in argv: + kwargs['display_case'] = False + argv = [a for a in argv if a != '--no-case'] + + if len(argv) != 4: usage = f'USAGE: {script_name} REPO LOCAL_CASE REMOTE_CASE' print(usage, file=sys.stderr) sys.exit(128) - repo = sys.argv[1] - local_case = sys.argv[2].split('-') - local_case = (local_case[0],) + tuple(int(x) for x in local_case[1:]) - remote_case = sys.argv[3].split('-') - remote_case = (remote_case[0],) + tuple(int(x) for x in remote_case[1:]) - sys.exit(compare(repo, local_case, remote_case)) + repo = argv[1] + local_case = parse_case(argv[2]) + remote_case = parse_case(argv[3]) + sys.exit(compare(repo, local_case, remote_case, **kwargs))
--- a/contrib/perf.py Thu Sep 01 16:44:00 2022 +0200 +++ b/contrib/perf.py Thu Sep 01 16:51:26 2022 +0200 @@ -925,6 +925,71 @@ fm.end() +@command( + b'perf::delta-find', + revlogopts + formatteropts, + b'-c|-m|FILE REV', +) +def perf_delta_find(ui, repo, arg_1, arg_2=None, **opts): + """benchmark the process of finding a valid delta for a revlog revision + + When a revlog receives a new revision (e.g. from a commit, or from an + incoming bundle), it searches for a suitable delta-base to produce a delta. + This perf command measures how much time we spend in this process. It + operates on an already stored revision. + + See `hg help debug-delta-find` for another related command. + """ + from mercurial import revlogutils + import mercurial.revlogutils.deltas as deltautil + + opts = _byteskwargs(opts) + if arg_2 is None: + file_ = None + rev = arg_1 + else: + file_ = arg_1 + rev = arg_2 + + repo = repo.unfiltered() + + timer, fm = gettimer(ui, opts) + + rev = int(rev) + + revlog = cmdutil.openrevlog(repo, b'perf::delta-find', file_, opts) + + deltacomputer = deltautil.deltacomputer(revlog) + + node = revlog.node(rev) + p1r, p2r = revlog.parentrevs(rev) + p1 = revlog.node(p1r) + p2 = revlog.node(p2r) + full_text = revlog.revision(rev) + textlen = len(full_text) + cachedelta = None + flags = revlog.flags(rev) + + revinfo = revlogutils.revisioninfo( + node, + p1, + p2, + [full_text], # btext + textlen, + cachedelta, + flags, + ) + + # Note: we should probably purge the potential caches (like the full + # manifest cache) between runs. + def find_one(): + with revlog._datafp() as fh: + deltacomputer.finddeltainfo(revinfo, fh, target_rev=rev) + + timer(find_one) + fm.end() + + @command(b'perf::discovery|perfdiscovery', formatteropts, b'PATH') def perfdiscovery(ui, repo, path, **opts): """benchmark discovery between local repo and the peer at given path""" @@ -974,6 +1039,98 @@ fm.end() +@command( + b'perf::bundle', + [ + ( + b'r', + b'rev', + [], + b'changesets to bundle', + b'REV', + ), + ( + b't', + b'type', + b'none', + b'bundlespec to use (see `hg help bundlespec`)', + b'TYPE', + ), + ] + + formatteropts, + b'REVS', +) +def perfbundle(ui, repo, *revs, **opts): + """benchmark the creation of a bundle from a repository + + For now, this only supports "none" compression. + """ + from mercurial import bundlecaches + from mercurial import discovery + from mercurial import bundle2 + + opts = _byteskwargs(opts) + timer, fm = gettimer(ui, opts) + + cl = repo.changelog + revs = list(revs) + revs.extend(opts.get(b'rev', ())) + revs = scmutil.revrange(repo, revs) + if not revs: + raise error.Abort(b"not revision specified") + # make it a consistent set (ie: without topological gaps) + old_len = len(revs) + revs = list(repo.revs(b"%ld::%ld", revs, revs)) + if old_len != len(revs): + new_count = len(revs) - old_len + msg = b"add %d new revisions to make it a consistent set\n" + ui.write_err(msg % new_count) + + targets = [cl.node(r) for r in repo.revs(b"heads(::%ld)", revs)] + bases = [cl.node(r) for r in repo.revs(b"heads(::%ld - %ld)", revs, revs)] + outgoing = discovery.outgoing(repo, bases, targets) + + bundle_spec = opts.get(b'type') + + bundle_spec = bundlecaches.parsebundlespec(repo, bundle_spec, strict=False) + + cgversion = bundle_spec.params[b"cg.version"] + if cgversion not in changegroup.supportedoutgoingversions(repo): + err = b"repository does not support bundle version %s" + raise error.Abort(err % cgversion) + + if cgversion == b'01': # bundle1 + bversion = b'HG10' + bundle_spec.wirecompression + bcompression = None + elif cgversion in (b'02', b'03'): + bversion = b'HG20' + bcompression = bundle_spec.wirecompression + else: + err = b'perf::bundle: unexpected changegroup version %s' + raise error.ProgrammingError(err % cgversion) + + if bcompression is None: + bcompression = b'UN' + + if bcompression != b'UN': + err = b'perf::bundle: compression currently unsupported: %s' + raise error.ProgrammingError(err % bcompression) + + def do_bundle(): + bundle2.writenewbundle( + ui, + repo, + b'perf::bundle', + os.devnull, + bversion, + outgoing, + bundle_spec.params, + ) + + timer(do_bundle) + fm.end() + + @command(b'perf::bundleread|perfbundleread', formatteropts, b'BUNDLE') def perfbundleread(ui, repo, bundlepath, **opts): """Benchmark reading of bundle files. @@ -2498,6 +2655,60 @@ @command( + b'perf::unbundle', + formatteropts, + b'BUNDLE_FILE', +) +def perf_unbundle(ui, repo, fname, **opts): + """benchmark application of a bundle in a repository. + + This does not include the final transaction processing""" + from mercurial import exchange + from mercurial import bundle2 + + opts = _byteskwargs(opts) + + with repo.lock(): + bundle = [None, None] + orig_quiet = repo.ui.quiet + try: + repo.ui.quiet = True + with open(fname, mode="rb") as f: + + def noop_report(*args, **kwargs): + pass + + def setup(): + gen, tr = bundle + if tr is not None: + tr.abort() + bundle[:] = [None, None] + f.seek(0) + bundle[0] = exchange.readbundle(ui, f, fname) + bundle[1] = repo.transaction(b'perf::unbundle') + bundle[1]._report = noop_report # silence the transaction + + def apply(): + gen, tr = bundle + bundle2.applybundle( + repo, + gen, + tr, + source=b'perf::unbundle', + url=fname, + ) + + timer, fm = gettimer(ui, opts) + timer(apply, setup=setup) + fm.end() + finally: + repo.ui.quiet == orig_quiet + gen, tr = bundle + if tr is not None: + tr.abort() + + +@command( b'perf::unidiff|perfunidiff', revlogopts + formatteropts
--- a/hgext/rebase.py Thu Sep 01 16:44:00 2022 +0200 +++ b/hgext/rebase.py Thu Sep 01 16:51:26 2022 +0200 @@ -546,7 +546,9 @@ date = self.date if date is None: date = ctx.date() - extra = {b'rebase_source': ctx.hex()} + extra = {} + if repo.ui.configbool(b'rebase', b'store-source'): + extra = {b'rebase_source': ctx.hex()} for c in self.extrafns: c(ctx, extra) destphase = max(ctx.phase(), phases.draft)
--- a/mercurial/cmdutil.py Thu Sep 01 16:44:00 2022 +0200 +++ b/mercurial/cmdutil.py Thu Sep 01 16:51:26 2022 +0200 @@ -832,7 +832,7 @@ @attr.s(frozen=True) class morestatus: - reporoot = attr.ib() + repo = attr.ib() unfinishedop = attr.ib() unfinishedmsg = attr.ib() activemerge = attr.ib() @@ -876,7 +876,7 @@ mergeliststr = b'\n'.join( [ b' %s' - % util.pathto(self.reporoot, encoding.getcwd(), path) + % util.pathto(self.repo.root, encoding.getcwd(), path) for path in self.unresolvedpaths ] ) @@ -898,6 +898,7 @@ # Already output. continue fm.startitem() + fm.context(repo=self.repo) # We can't claim to know the status of the file - it may just # have been in one of the states that were not requested for # display, so it could be anything. @@ -923,7 +924,7 @@ if activemerge: unresolved = sorted(mergestate.unresolved()) return morestatus( - repo.root, unfinishedop, unfinishedmsg, activemerge, unresolved + repo, unfinishedop, unfinishedmsg, activemerge, unresolved )
--- a/mercurial/commands.py Thu Sep 01 16:44:00 2022 +0200 +++ b/mercurial/commands.py Thu Sep 01 16:51:26 2022 +0200 @@ -1035,7 +1035,14 @@ state = hbisect.load_state(repo) if rev: - nodes = [repo[i].node() for i in logcmdutil.revrange(repo, rev)] + revs = logcmdutil.revrange(repo, rev) + goodnodes = state[b'good'] + badnodes = state[b'bad'] + if goodnodes and badnodes: + candidates = repo.revs(b'(%ln)::(%ln)', goodnodes, badnodes) + candidates += repo.revs(b'(%ln)::(%ln)', badnodes, goodnodes) + revs = candidates & revs + nodes = [repo.changelog.node(i) for i in revs] else: nodes = [repo.lookup(b'.')] @@ -1485,6 +1492,12 @@ b'bundle', [ ( + b'', + b'exact', + None, + _(b'compute the base from the revision specified'), + ), + ( b'f', b'force', None, @@ -1553,6 +1566,7 @@ Returns 0 on success, 1 if no changes found. """ opts = pycompat.byteskwargs(opts) + revs = None if b'rev' in opts: revstrings = opts[b'rev'] @@ -1586,7 +1600,19 @@ ) if opts.get(b'base'): ui.warn(_(b"ignoring --base because --all was specified\n")) + if opts.get(b'exact'): + ui.warn(_(b"ignoring --exact because --all was specified\n")) base = [nullrev] + elif opts.get(b'exact'): + if dests: + raise error.InputError( + _(b"--exact is incompatible with specifying destinations") + ) + if opts.get(b'base'): + ui.warn(_(b"ignoring --base because --exact was specified\n")) + base = repo.revs(b'parents(%ld) - %ld', revs, revs) + if not base: + base = [nullrev] else: base = logcmdutil.revrange(repo, opts.get(b'base')) if cgversion not in changegroup.supportedoutgoingversions(repo):
--- a/mercurial/configitems.py Thu Sep 01 16:44:00 2022 +0200 +++ b/mercurial/configitems.py Thu Sep 01 16:51:26 2022 +0200 @@ -1425,12 +1425,38 @@ default=False, experimental=True, ) + +# Moving this on by default means we are confident about the scaling of phases. +# This is not garanteed to be the case at the time this message is written. coreconfigitem( b'format', - b'internal-phase', + b'use-internal-phase', default=False, experimental=True, ) +# The interaction between the archived phase and obsolescence markers needs to +# be sorted out before wider usage of this are to be considered. +# +# At the time this message is written, behavior when archiving obsolete +# changeset differ significantly from stripping. As part of stripping, we also +# remove the obsolescence marker associated to the stripped changesets, +# revealing the precedecessors changesets when applicable. When archiving, we +# don't touch the obsolescence markers, keeping everything hidden. This can +# result in quite confusing situation for people combining exchanging draft +# with the archived phases. As some markers needed by others may be skipped +# during exchange. +coreconfigitem( + b'format', + b'exp-archived-phase', + default=False, + experimental=True, +) +coreconfigitem( + b'shelve', + b'store', + default=b'internal', + experimental=True, +) coreconfigitem( b'fsmonitor', b'warn_when_unused', @@ -2835,3 +2861,17 @@ b'experimental.inmemory', default=False, ) + +# This setting controls creation of a rebase_source extra field +# during rebase. When False, no such field is created. This is +# useful eg for incrementally converting changesets and then +# rebasing them onto an existing repo. +# WARNING: this is an advanced setting reserved for people who know +# exactly what they are doing. Misuse of this setting can easily +# result in obsmarker cycles and a vivid headache. +coreconfigitem( + b'rebase', + b'store-source', + default=True, + experimental=True, +)
--- a/mercurial/debugcommands.py Thu Sep 01 16:44:00 2022 +0200 +++ b/mercurial/debugcommands.py Thu Sep 01 16:51:26 2022 +0200 @@ -1021,7 +1021,7 @@ deltacomputer = deltautil.deltacomputer( revlog, write_debug=ui.write, - debug_search=True, + debug_search=not ui.quiet, ) node = revlog.node(rev)
--- a/mercurial/dispatch.py Thu Sep 01 16:44:00 2022 +0200 +++ b/mercurial/dispatch.py Thu Sep 01 16:51:26 2022 +0200 @@ -952,14 +952,22 @@ Takes paths in [cwd]/.hg/hgrc into account." """ + try: + cwd = encoding.getcwd() + except OSError as e: + raise error.Abort( + _(b"error getting current working directory: %s") + % encoding.strtolocal(e.strerror) + ) + + # If using an alternate wd, temporarily switch to it so that relative + # paths are resolved correctly during config loading. + oldcwd = None if wd is None: - try: - wd = encoding.getcwd() - except OSError as e: - raise error.Abort( - _(b"error getting current working directory: %s") - % encoding.strtolocal(e.strerror) - ) + wd = cwd + else: + oldcwd = cwd + os.chdir(wd) path = cmdutil.findrepo(wd) or b"" if not path: @@ -979,6 +987,9 @@ lui.readconfig(os.path.join(path, b".hg", b"hgrc"), path) lui.readconfig(os.path.join(path, b".hg", b"hgrc-not-shared"), path) + if oldcwd: + os.chdir(oldcwd) + return path, lui
--- a/mercurial/hbisect.py Thu Sep 01 16:44:00 2022 +0200 +++ b/mercurial/hbisect.py Thu Sep 01 16:51:26 2022 +0200 @@ -39,7 +39,7 @@ def buildancestors(bad, good): badrev = min([changelog.rev(n) for n in bad]) ancestors = collections.defaultdict(lambda: None) - for rev in repo.revs(b"descendants(%ln) - ancestors(%ln)", good, good): + for rev in repo.revs(b"(%ln::%d) - (::%ln)", good, badrev, good): ancestors[rev] = [] if ancestors[badrev] is None: return badrev, None
--- a/mercurial/helptext/bundlespec.txt Thu Sep 01 16:44:00 2022 +0200 +++ b/mercurial/helptext/bundlespec.txt Thu Sep 01 16:51:26 2022 +0200 @@ -67,6 +67,33 @@ .. bundlecompressionmarker +Available Options +================= + +The following options exist: + +changegroup + Include the changegroup data in the bundle (default to True). + +cg.version + Select the version of the changegroup to use. Available options are : 01, 02 + or 03. By default it will be automatically selected according to the current + repository format. + +obsolescence + Include obsolescence-markers relevant to the bundled changesets. + +phases + Include phase information relevant to the bundled changesets. + +revbranchcache + Include the "tags-fnodes" cache inside the bundle. + + +tagsfnodescache + Include the "tags-fnodes" cache inside the bundle. + + Examples ========
--- a/mercurial/hgweb/__init__.py Thu Sep 01 16:44:00 2022 +0200 +++ b/mercurial/hgweb/__init__.py Thu Sep 01 16:51:26 2022 +0200 @@ -18,12 +18,15 @@ from ..utils import procutil +# pytype: disable=pyi-error from . import ( hgweb_mod, hgwebdir_mod, server, ) +# pytype: enable=pyi-error + def hgweb(config, name=None, baseui=None): """create an hgweb wsgi object
--- a/mercurial/localrepo.py Thu Sep 01 16:44:00 2022 +0200 +++ b/mercurial/localrepo.py Thu Sep 01 16:51:26 2022 +0200 @@ -1281,6 +1281,7 @@ """ _basesupported = { + requirementsmod.ARCHIVED_PHASE_REQUIREMENT, requirementsmod.BOOKMARKS_IN_STORE_REQUIREMENT, requirementsmod.CHANGELOGV2_REQUIREMENT, requirementsmod.COPIESSDC_REQUIREMENT, @@ -3668,9 +3669,13 @@ requirements.discard(requirementsmod.REVLOGV1_REQUIREMENT) requirements.add(requirementsmod.REVLOGV2_REQUIREMENT) # experimental config: format.internal-phase - if ui.configbool(b'format', b'internal-phase'): + if ui.configbool(b'format', b'use-internal-phase'): requirements.add(requirementsmod.INTERNAL_PHASE_REQUIREMENT) + # experimental config: format.exp-archived-phase + if ui.configbool(b'format', b'exp-archived-phase'): + requirements.add(requirementsmod.ARCHIVED_PHASE_REQUIREMENT) + if createopts.get(b'narrowfiles'): requirements.add(requirementsmod.NARROW_REQUIREMENT)
--- a/mercurial/obsolete.py Thu Sep 01 16:44:00 2022 +0200 +++ b/mercurial/obsolete.py Thu Sep 01 16:51:26 2022 +0200 @@ -70,6 +70,7 @@ import binascii import struct +import weakref from .i18n import _ from .pycompat import getattr @@ -561,10 +562,18 @@ # caches for various obsolescence related cache self.caches = {} self.svfs = svfs - self.repo = repo + self._repo = weakref.ref(repo) self._defaultformat = defaultformat self._readonly = readonly + @property + def repo(self): + r = self._repo() + if r is None: + msg = "using the obsstore of a deallocated repo" + raise error.ProgrammingError(msg) + return r + def __iter__(self): return iter(self._all)
--- a/mercurial/phases.py Thu Sep 01 16:44:00 2022 +0200 +++ b/mercurial/phases.py Thu Sep 01 16:51:26 2022 +0200 @@ -178,6 +178,12 @@ return requirements.INTERNAL_PHASE_REQUIREMENT in repo.requirements +def supportarchived(repo): + # type: (localrepo.localrepository) -> bool + """True if the archived phase can be used on a repository""" + return requirements.ARCHIVED_PHASE_REQUIREMENT in repo.requirements + + def _readroots(repo, phasedefaults=None): # type: (localrepo.localrepository, Optional[Phasedefaults]) -> Tuple[Phaseroots, bool] """Read phase roots from disk @@ -642,7 +648,12 @@ # phaseroots values, replace them. if revs is None: revs = [] - if targetphase in (archived, internal) and not supportinternal(repo): + if ( + targetphase == internal + and not supportinternal(repo) + or targetphase == archived + and not supportarchived(repo) + ): name = phasenames[targetphase] msg = b'this repository does not support the %s phase' % name raise error.ProgrammingError(msg)
--- a/mercurial/requirements.py Thu Sep 01 16:44:00 2022 +0200 +++ b/mercurial/requirements.py Thu Sep 01 16:51:26 2022 +0200 @@ -29,7 +29,11 @@ # Enables the internal phase which is used to hide changesets instead # of stripping them -INTERNAL_PHASE_REQUIREMENT = b'internal-phase' +INTERNAL_PHASE_REQUIREMENT = b'internal-phase-2' + +# Enables the internal phase which is used to hide changesets instead +# of stripping them +ARCHIVED_PHASE_REQUIREMENT = b'exp-archived-phase' # Stores manifest in Tree structure TREEMANIFEST_REQUIREMENT = b'treemanifest' @@ -107,6 +111,7 @@ # # note: the list is currently inherited from previous code and miss some relevant requirement while containing some irrelevant ones. STREAM_FIXED_REQUIREMENTS = { + ARCHIVED_PHASE_REQUIREMENT, BOOKMARKS_IN_STORE_REQUIREMENT, CHANGELOGV2_REQUIREMENT, COPIESSDC_REQUIREMENT,
--- a/mercurial/revlog.py Thu Sep 01 16:44:00 2022 +0200 +++ b/mercurial/revlog.py Thu Sep 01 16:51:26 2022 +0200 @@ -235,6 +235,8 @@ b' expected %d bytes from offset %d, data size is %d' ) +hexdigits = b'0123456789abcdefABCDEF' + class revlog: """ @@ -1509,7 +1511,7 @@ ambiguous = True # fall through to slow path that filters hidden revisions except (AttributeError, ValueError): - # we are pure python, or key was too short to search radix tree + # we are pure python, or key is not hex pass if ambiguous: raise error.AmbiguousPrefixLookupError( @@ -1523,6 +1525,11 @@ # hex(node)[:...] l = len(id) // 2 * 2 # grab an even number of digits try: + # we're dropping the last digit, so let's check that it's hex, + # to avoid the expensive computation below if it's not + if len(id) % 2 > 0: + if not (id[-1] in hexdigits): + return None prefix = bin(id[:l]) except binascii.Error: pass
--- a/mercurial/revset.py Thu Sep 01 16:44:00 2022 +0200 +++ b/mercurial/revset.py Thu Sep 01 16:51:26 2022 +0200 @@ -7,7 +7,10 @@ import binascii +import functools +import random import re +import sys from .i18n import _ from .pycompat import getattr @@ -2347,6 +2350,15 @@ return subset & s.filter(filter, condrepr=b'<roots>') +MAXINT = sys.maxsize +MININT = -MAXINT - 1 + + +def pick_random(c, gen=random): + # exists as its own function to make it possible to overwrite the seed + return gen.randint(MININT, MAXINT) + + _sortkeyfuncs = { b'rev': scmutil.intrev, b'branch': lambda c: c.branch(), @@ -2355,12 +2367,17 @@ b'author': lambda c: c.user(), b'date': lambda c: c.date()[0], b'node': scmutil.binnode, + b'random': pick_random, } def _getsortargs(x): """Parse sort options into (set, [(key, reverse)], opts)""" - args = getargsdict(x, b'sort', b'set keys topo.firstbranch') + args = getargsdict( + x, + b'sort', + b'set keys topo.firstbranch random.seed', + ) if b'set' not in args: # i18n: "sort" is a keyword raise error.ParseError(_(b'sort requires one or two arguments')) @@ -2400,6 +2417,20 @@ ) ) + if b'random.seed' in args: + if any(k == b'random' for k, reverse in keyflags): + s = args[b'random.seed'] + seed = getstring(s, _(b"random.seed must be a string")) + opts[b'random.seed'] = seed + else: + # i18n: "random" and "random.seed" are keywords + raise error.ParseError( + _( + b'random.seed can only be used ' + b'when using the random sort key' + ) + ) + return args[b'set'], keyflags, opts @@ -2419,11 +2450,14 @@ - ``date`` for the commit date - ``topo`` for a reverse topographical sort - ``node`` the nodeid of the revision + - ``random`` randomly shuffle revisions The ``topo`` sort order cannot be combined with other sort keys. This sort takes one optional argument, ``topo.firstbranch``, which takes a revset that specifies what topographical branches to prioritize in the sort. + The ``random`` sort takes one optional ``random.seed`` argument to control + the pseudo-randomness of the result. """ s, keyflags, opts = _getsortargs(x) revs = getset(repo, subset, s, order) @@ -2448,7 +2482,12 @@ # sort() is guaranteed to be stable ctxs = [repo[r] for r in revs] for k, reverse in reversed(keyflags): - ctxs.sort(key=_sortkeyfuncs[k], reverse=reverse) + func = _sortkeyfuncs[k] + if k == b'random' and b'random.seed' in opts: + seed = opts[b'random.seed'] + r = random.Random(seed) + func = functools.partial(func, gen=r) + ctxs.sort(key=func, reverse=reverse) return baseset([c.rev() for c in ctxs])
--- a/mercurial/scmutil.py Thu Sep 01 16:44:00 2022 +0200 +++ b/mercurial/scmutil.py Thu Sep 01 16:51:26 2022 +0200 @@ -1191,7 +1191,7 @@ obsolete.createmarkers( repo, rels, operation=operation, metadata=metadata ) - elif phases.supportinternal(repo) and mayusearchived: + elif phases.supportarchived(repo) and mayusearchived: # this assume we do not have "unstable" nodes above the cleaned ones allreplaced = set() for ns in replacements.keys():
--- a/mercurial/shelve.py Thu Sep 01 16:44:00 2022 +0200 +++ b/mercurial/shelve.py Thu Sep 01 16:51:26 2022 +0200 @@ -22,6 +22,7 @@ """ import collections +import io import itertools import stat @@ -98,6 +99,17 @@ return sorted(info, reverse=True) +def _use_internal_phase(repo): + return ( + phases.supportinternal(repo) + and repo.ui.config(b'shelve', b'store') == b'internal' + ) + + +def _target_phase(repo): + return phases.internal if _use_internal_phase(repo) else phases.secret + + class Shelf: """Represents a shelf, including possibly multiple files storing it. @@ -111,12 +123,19 @@ self.name = name def exists(self): - return self.vfs.exists(self.name + b'.patch') and self.vfs.exists( - self.name + b'.hg' - ) + return self._exists(b'.shelve') or self._exists(b'.patch', b'.hg') + + def _exists(self, *exts): + return all(self.vfs.exists(self.name + ext) for ext in exts) def mtime(self): - return self.vfs.stat(self.name + b'.patch')[stat.ST_MTIME] + try: + return self._stat(b'.shelve')[stat.ST_MTIME] + except FileNotFoundError: + return self._stat(b'.patch')[stat.ST_MTIME] + + def _stat(self, ext): + return self.vfs.stat(self.name + ext) def writeinfo(self, info): scmutil.simplekeyvaluefile(self.vfs, self.name + b'.shelve').write(info) @@ -159,9 +178,7 @@ filename = self.name + b'.hg' fp = self.vfs(filename) try: - targetphase = phases.internal - if not phases.supportinternal(repo): - targetphase = phases.secret + targetphase = _target_phase(repo) gen = exchange.readbundle(repo.ui, fp, filename, self.vfs) pretip = repo[b'tip'] bundle2.applybundle( @@ -183,6 +200,27 @@ def open_patch(self, mode=b'rb'): return self.vfs(self.name + b'.patch', mode) + def patch_from_node(self, repo, node): + repo = repo.unfiltered() + match = _optimized_match(repo, node) + fp = io.BytesIO() + cmdutil.exportfile( + repo, + [node], + fp, + opts=mdiff.diffopts(git=True), + match=match, + ) + fp.seek(0) + return fp + + def load_patch(self, repo): + try: + # prefer node-based shelf + return self.patch_from_node(repo, self.readinfo()[b'node']) + except (FileNotFoundError, error.RepoLookupError): + return self.open_patch() + def _backupfilename(self, backupvfs, filename): def gennames(base): yield base @@ -210,6 +248,15 @@ self.vfs.tryunlink(self.name + b'.' + ext) +def _optimized_match(repo, node): + """ + Create a matcher so that prefetch doesn't attempt to fetch + the entire repository pointlessly, and as an optimisation + for movedirstate, if needed. + """ + return scmutil.matchfiles(repo, repo[node].files()) + + class shelvedstate: """Handle persistence during unshelving operations. @@ -447,9 +494,7 @@ if hasmq: saved, repo.mq.checkapplied = repo.mq.checkapplied, False - targetphase = phases.internal - if not phases.supportinternal(repo): - targetphase = phases.secret + targetphase = _target_phase(repo) overrides = {(b'phases', b'new-commit'): targetphase} try: editor_ = False @@ -510,7 +555,7 @@ def _finishshelve(repo, tr): - if phases.supportinternal(repo): + if _use_internal_phase(repo): tr.close() else: _aborttransaction(repo, tr) @@ -579,10 +624,7 @@ _nothingtoshelvemessaging(ui, repo, pats, opts) return 1 - # Create a matcher so that prefetch doesn't attempt to fetch - # the entire repository pointlessly, and as an optimisation - # for movedirstate, if needed. - match = scmutil.matchfiles(repo, repo[node].files()) + match = _optimized_match(repo, node) _shelvecreatedcommit(repo, node, name, match) ui.status(_(b'shelved as %s\n') % name) @@ -668,7 +710,7 @@ ui.write(age, label=b'shelve.age') ui.write(b' ' * (12 - len(age))) used += 12 - with shelf_dir.get(name).open_patch() as fp: + with shelf_dir.get(name).load_patch(repo) as fp: while True: line = fp.readline() if not line: @@ -754,7 +796,7 @@ if state.activebookmark and state.activebookmark in repo._bookmarks: bookmarks.activate(repo, state.activebookmark) mergefiles(ui, repo, state.wctx, state.pendingctx) - if not phases.supportinternal(repo): + if not _use_internal_phase(repo): repair.strip( ui, repo, state.nodestoremove, backup=False, topic=b'shelve' ) @@ -816,9 +858,7 @@ repo.setparents(state.pendingctx.node(), repo.nullid) repo.dirstate.write(repo.currenttransaction()) - targetphase = phases.internal - if not phases.supportinternal(repo): - targetphase = phases.secret + targetphase = _target_phase(repo) overrides = {(b'phases', b'new-commit'): targetphase} with repo.ui.configoverride(overrides, b'unshelve'): with repo.dirstate.parentchange(): @@ -843,7 +883,7 @@ mergefiles(ui, repo, state.wctx, shelvectx) restorebranch(ui, repo, state.branchtorestore) - if not phases.supportinternal(repo): + if not _use_internal_phase(repo): repair.strip( ui, repo, state.nodestoremove, backup=False, topic=b'shelve' ) @@ -957,7 +997,7 @@ user=shelvectx.user(), ) if snode: - m = scmutil.matchfiles(repo, repo[snode].files()) + m = _optimized_match(repo, snode) _shelvecreatedcommit(repo, snode, basename, m) return newnode, bool(snode)
--- a/mercurial/subrepo.py Thu Sep 01 16:44:00 2022 +0200 +++ b/mercurial/subrepo.py Thu Sep 01 16:51:26 2022 +0200 @@ -1099,6 +1099,10 @@ # --non-interactive. if commands[0] in (b'update', b'checkout', b'commit'): cmd.append(b'--non-interactive') + if util.safehasattr(subprocess, 'CREATE_NO_WINDOW'): + # On Windows, prevent command prompts windows from popping up when + # running in pythonw. + extrakw['creationflags'] = getattr(subprocess, 'CREATE_NO_WINDOW') cmd.extend(commands) if filename is not None: path = self.wvfs.reljoin( @@ -1150,7 +1154,7 @@ # commit revision so we can compare the subrepo state with # both. We used to store the working directory one. output, err = self._svncommand([b'info', b'--xml']) - doc = xml.dom.minidom.parseString(output) + doc = xml.dom.minidom.parseString(output) # pytype: disable=pyi-error entries = doc.getElementsByTagName('entry') lastrev, rev = b'0', b'0' if entries: @@ -1174,7 +1178,7 @@ """ output, err = self._svncommand([b'status', b'--xml']) externals, changes, missing = [], [], [] - doc = xml.dom.minidom.parseString(output) + doc = xml.dom.minidom.parseString(output) # pytype: disable=pyi-error for e in doc.getElementsByTagName('entry'): s = e.getElementsByTagName('wc-status') if not s: @@ -1319,7 +1323,7 @@ @annotatesubrepoerror def files(self): output = self._svncommand([b'list', b'--recursive', b'--xml'])[0] - doc = xml.dom.minidom.parseString(output) + doc = xml.dom.minidom.parseString(output) # pytype: disable=pyi-error paths = [] for e in doc.getElementsByTagName('entry'): kind = pycompat.bytestr(e.getAttribute('kind')) @@ -1469,6 +1473,11 @@ # insert the argument in the front, # the end of git diff arguments is used for paths commands.insert(1, b'--color') + extrakw = {} + if util.safehasattr(subprocess, 'CREATE_NO_WINDOW'): + # On Windows, prevent command prompts windows from popping up when + # running in pythonw. + extrakw['creationflags'] = getattr(subprocess, 'CREATE_NO_WINDOW') p = subprocess.Popen( pycompat.rapply( procutil.tonativestr, [self._gitexecutable] + commands @@ -1479,6 +1488,7 @@ close_fds=procutil.closefds, stdout=subprocess.PIPE, stderr=errpipe, + **extrakw ) if stream: return p.stdout, None
--- a/mercurial/url.py Thu Sep 01 16:44:00 2022 +0200 +++ b/mercurial/url.py Thu Sep 01 16:51:26 2022 +0200 @@ -222,13 +222,16 @@ h.headers = None -def _generic_proxytunnel(self): +def _generic_proxytunnel(self: "httpsconnection"): + headers = self.headers # pytype: disable=attribute-error proxyheaders = { - pycompat.bytestr(x): pycompat.bytestr(self.headers[x]) - for x in self.headers + pycompat.bytestr(x): pycompat.bytestr(headers[x]) + for x in headers if x.lower().startswith('proxy-') } - self.send(b'CONNECT %s HTTP/1.0\r\n' % self.realhostport) + realhostport = self.realhostport # pytype: disable=attribute-error + self.send(b'CONNECT %s HTTP/1.0\r\n' % realhostport) + for header in proxyheaders.items(): self.send(b'%s: %s\r\n' % header) self.send(b'\r\n') @@ -237,10 +240,14 @@ # httplib.HTTPConnection as there are no adequate places to # override functions to provide the needed functionality. + # pytype: disable=attribute-error res = self.response_class(self.sock, method=self._method) + # pytype: enable=attribute-error while True: + # pytype: disable=attribute-error version, status, reason = res._read_status() + # pytype: enable=attribute-error if status != httplib.CONTINUE: break # skip lines that are all whitespace @@ -323,14 +330,15 @@ self.sock = socket.create_connection((self.host, self.port)) host = self.host - if self.realhostport: # use CONNECT proxy + realhostport = self.realhostport # pytype: disable=attribute-error + if realhostport: # use CONNECT proxy _generic_proxytunnel(self) - host = self.realhostport.rsplit(b':', 1)[0] + host = realhostport.rsplit(b':', 1)[0] self.sock = sslutil.wrapsocket( self.sock, self.key_file, self.cert_file, - ui=self.ui, + ui=self.ui, # pytype: disable=attribute-error serverhostname=host, ) sslutil.validatesocket(self.sock)
--- a/relnotes/next Thu Sep 01 16:44:00 2022 +0200 +++ b/relnotes/next Thu Sep 01 16:51:26 2022 +0200 @@ -13,6 +13,12 @@ == Backwards Compatibility Changes == + * chg worker processes will now correctly load per-repository configuration + when given a both a relative `--repository` path and an alternate working + directory via `--cwd`. A side-effect of this change is that these workers + will now return an error if hg cannot find the current working directory, + even when a different directory is specified via `--cwd`. + == Internal API Changes == == Miscellaneous ==
--- a/rust/hg-core/src/dirstate.rs Thu Sep 01 16:44:00 2022 +0200 +++ b/rust/hg-core/src/dirstate.rs Thu Sep 01 16:51:26 2022 +0200 @@ -30,6 +30,10 @@ p1: NULL_NODE, p2: NULL_NODE, }; + + pub fn is_merge(&self) -> bool { + return !(self.p2 == NULL_NODE); + } } pub type StateMapIter<'a> = Box<
--- a/rust/rhg/src/commands/status.rs Thu Sep 01 16:44:00 2022 +0200 +++ b/rust/rhg/src/commands/status.rs Thu Sep 01 16:51:26 2022 +0200 @@ -104,6 +104,12 @@ .short("-n") .long("--no-status"), ) + .arg( + Arg::with_name("verbose") + .help("enable additional output") + .short("-v") + .long("--verbose"), + ) } /// Pure data type allowing the caller to specify file states to display @@ -150,6 +156,33 @@ } } +fn has_unfinished_merge(repo: &Repo) -> Result<bool, CommandError> { + return Ok(repo.dirstate_parents()?.is_merge()); +} + +fn has_unfinished_state(repo: &Repo) -> Result<bool, CommandError> { + // These are all the known values for the [fname] argument of + // [addunfinished] function in [state.py] + let known_state_files: &[&str] = &[ + "bisect.state", + "graftstate", + "histedit-state", + "rebasestate", + "shelvedstate", + "transplant/journal", + "updatestate", + ]; + if has_unfinished_merge(repo)? { + return Ok(true); + }; + for f in known_state_files { + if repo.hg_vfs().join(f).exists() { + return Ok(true); + } + } + return Ok(false); +} + pub fn run(invocation: &crate::CliInvocation) -> Result<(), CommandError> { // TODO: lift these limitations if invocation.config.get_bool(b"ui", b"tweakdefaults")? { @@ -178,13 +211,9 @@ let verbose = !ui.plain(None) && !args.is_present("print0") - && (config.get_bool(b"ui", b"verbose")? + && (args.is_present("verbose") + || config.get_bool(b"ui", b"verbose")? || config.get_bool(b"commands", b"status.verbose")?); - if verbose { - return Err(CommandError::unsupported( - "verbose status is not supported yet", - )); - } let all = args.is_present("all"); let display_states = if all { @@ -214,6 +243,14 @@ let repo = invocation.repo?; + if verbose { + if has_unfinished_state(repo)? { + return Err(CommandError::unsupported( + "verbose status output is not supported by rhg (and is needed because we're in an unfinished operation)", + )); + }; + } + if repo.has_sparse() || repo.has_narrow() { return Err(CommandError::unsupported( "rhg status is not supported for sparse checkouts or narrow clones yet"
--- a/setup.py Thu Sep 01 16:44:00 2022 +0200 +++ b/setup.py Thu Sep 01 16:51:26 2022 +0200 @@ -666,30 +666,55 @@ class buildhgexe(build_ext): description = 'compile hg.exe from mercurial/exewrapper.c' - user_options = build_ext.user_options + [ - ( - 'long-paths-support', - None, - 'enable support for long paths on ' - 'Windows (off by default and ' - 'experimental)', - ), - ] - LONG_PATHS_MANIFEST = """ - <?xml version="1.0" encoding="UTF-8" standalone="yes"?> - <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> - <application> - <windowsSettings - xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings"> - <ws2:longPathAware>true</ws2:longPathAware> - </windowsSettings> - </application> - </assembly>""" + LONG_PATHS_MANIFEST = """\ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> + <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> + <security> + <requestedPrivileges> + <requestedExecutionLevel + level="asInvoker" + uiAccess="false" + /> + </requestedPrivileges> + </security> + </trustInfo> + <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> + <application> + <!-- Windows Vista --> + <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/> + <!-- Windows 7 --> + <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/> + <!-- Windows 8 --> + <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/> + <!-- Windows 8.1 --> + <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> + <!-- Windows 10 and Windows 11 --> + <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/> + </application> + </compatibility> + <application xmlns="urn:schemas-microsoft-com:asm.v3"> + <windowsSettings + xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings"> + <ws2:longPathAware>true</ws2:longPathAware> + </windowsSettings> + </application> + <dependency> + <dependentAssembly> + <assemblyIdentity type="win32" + name="Microsoft.Windows.Common-Controls" + version="6.0.0.0" + processorArchitecture="*" + publicKeyToken="6595b64144ccf1df" + language="*" /> + </dependentAssembly> + </dependency> +</assembly> +""" def initialize_options(self): build_ext.initialize_options(self) - self.long_paths_support = False def build_extensions(self): if os.name != 'nt': @@ -700,8 +725,8 @@ pythonlib = None - dir = os.path.dirname(self.get_ext_fullpath('dummy')) - self.hgtarget = os.path.join(dir, 'hg') + dirname = os.path.dirname(self.get_ext_fullpath('dummy')) + self.hgtarget = os.path.join(dirname, 'hg') if getattr(sys, 'dllhandle', None): # Different Python installs can have different Python library @@ -774,22 +799,11 @@ self.compiler.link_executable( objects, self.hgtarget, libraries=[], output_dir=self.build_temp ) - if self.long_paths_support: - self.addlongpathsmanifest() + + self.addlongpathsmanifest() def addlongpathsmanifest(self): - r"""Add manifest pieces so that hg.exe understands long paths - - This is an EXPERIMENTAL feature, use with care. - To enable long paths support, one needs to do two things: - - build Mercurial with --long-paths-support option - - change HKLM\SYSTEM\CurrentControlSet\Control\FileSystem\ - LongPathsEnabled to have value 1. - - Please ignore 'warning 81010002: Unrecognized Element "longPathAware"'; - it happens because Mercurial uses mt.exe circa 2008, which is not - yet aware of long paths support in the manifest (I think so at least). - This does not stop mt.exe from embedding/merging the XML properly. + """Add manifest pieces so that hg.exe understands long paths Why resource #1 should be used for .exe manifests? I don't know and wasn't able to find an explanation for mortals. But it seems to work. @@ -797,21 +811,18 @@ exefname = self.compiler.executable_filename(self.hgtarget) fdauto, manfname = tempfile.mkstemp(suffix='.hg.exe.manifest') os.close(fdauto) - with open(manfname, 'w') as f: + with open(manfname, 'w', encoding="UTF-8") as f: f.write(self.LONG_PATHS_MANIFEST) log.info("long paths manifest is written to '%s'" % manfname) - inputresource = '-inputresource:%s;#1' % exefname outputresource = '-outputresource:%s;#1' % exefname log.info("running mt.exe to update hg.exe's manifest in-place") - # supplying both -manifest and -inputresource to mt.exe makes - # it merge the embedded and supplied manifests in the -outputresource + self.spawn( [ - 'mt.exe', + self.compiler.mt, '-nologo', '-manifest', manfname, - inputresource, outputresource, ] )
--- a/tests/test-bisect2.t Thu Sep 01 16:44:00 2022 +0200 +++ b/tests/test-bisect2.t Thu Sep 01 16:51:26 2022 +0200 @@ -784,7 +784,6 @@ $ hg log -q -r 'bisect(pruned)' 0:33b1f9bc8bc5 1:4ca5088da217 - 2:051e12f87bf1 8:dab8161ac8fc 11:82ca6f06eccd 12:9f259202bbe7
--- a/tests/test-bundle.t Thu Sep 01 16:44:00 2022 +0200 +++ b/tests/test-bundle.t Thu Sep 01 16:51:26 2022 +0200 @@ -718,7 +718,7 @@ $ hg init empty $ hg -R test bundle --base null -r 0 ../0.hg 1 changesets found - $ hg -R test bundle --base 0 -r 1 ../1.hg + $ hg -R test bundle --exact -r 1 ../1.hg 1 changesets found $ hg -R empty unbundle -u ../0.hg ../1.hg adding changesets
--- a/tests/test-chg.t Thu Sep 01 16:44:00 2022 +0200 +++ b/tests/test-chg.t Thu Sep 01 16:51:26 2022 +0200 @@ -432,6 +432,20 @@ YYYY/MM/DD HH:MM:SS (PID)> log -R cached YYYY/MM/DD HH:MM:SS (PID)> loaded repo into cache: $TESTTMP/cached (in ...s) +Test that -R is interpreted relative to --cwd. + + $ hg init repo1 + $ mkdir -p a/b + $ hg init a/b/repo2 + $ printf "[alias]\ntest=repo1\n" >> repo1/.hg/hgrc + $ printf "[alias]\ntest=repo2\n" >> a/b/repo2/.hg/hgrc + $ cd a + $ chg --cwd .. -R repo1 show alias.test + repo1 + $ chg --cwd . -R b/repo2 show alias.test + repo2 + $ cd .. + Test that chg works (sets to the user's actual LC_CTYPE) even when python "coerces" the locale (py3.7+)
--- a/tests/test-completion.t Thu Sep 01 16:44:00 2022 +0200 +++ b/tests/test-completion.t Thu Sep 01 16:51:26 2022 +0200 @@ -261,7 +261,7 @@ bookmarks: force, rev, delete, rename, inactive, list, template branch: force, clean, rev branches: active, closed, rev, template - bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure + bundle: exact, force, rev, branch, base, all, type, ssh, remotecmd, insecure cat: output, rev, decode, include, exclude, template clone: noupdate, updaterev, rev, branch, pull, uncompressed, stream, ssh, remotecmd, insecure commit: addremove, close-branch, amend, secret, edit, force-close-branch, interactive, include, exclude, message, logfile, date, user, subrepos
--- a/tests/test-contrib-perf.t Thu Sep 01 16:44:00 2022 +0200 +++ b/tests/test-contrib-perf.t Thu Sep 01 16:51:26 2022 +0200 @@ -96,6 +96,7 @@ perf::branchmapupdate benchmark branchmap update from for <base> revs to <target> revs + perf::bundle benchmark the creation of a bundle from a repository perf::bundleread Benchmark reading of bundle files. perf::cca (no help text available) @@ -105,6 +106,9 @@ (no help text available) perf::ctxfiles (no help text available) + perf::delta-find + benchmark the process of finding a valid delta for a revlog + revision perf::diffwd Profile diff of working directory changes perf::dirfoldmap benchmap a 'dirstate._map.dirfoldmap.get()' request @@ -187,6 +191,8 @@ perf::tags (no help text available) perf::templating test the rendering time of a given template + perf::unbundle + benchmark application of a bundle in a repository. perf::unidiff benchmark a unified diff between revisions perf::volatilesets @@ -385,6 +391,11 @@ searching for changes searching for changes searching for changes + $ hg perf::bundle 'last(all(), 5)' + $ hg bundle --exact --rev 'last(all(), 5)' last-5.hg + 4 changesets found + $ hg perf::unbundle last-5.hg + test profile-benchmark option ------------------------------
--- a/tests/test-mq-subrepo-svn.t Thu Sep 01 16:44:00 2022 +0200 +++ b/tests/test-mq-subrepo-svn.t Thu Sep 01 16:51:26 2022 +0200 @@ -38,7 +38,7 @@ A .hgsub $ hg qnew -m0 0.diff $ cd sub - $ echo a > a + $ echo foo > a $ svn add a A a $ svn st
--- a/tests/test-phase-archived.t Thu Sep 01 16:44:00 2022 +0200 +++ b/tests/test-phase-archived.t Thu Sep 01 16:51:26 2022 +0200 @@ -4,7 +4,7 @@ $ cat << EOF >> $HGRCPATH > [format] - > internal-phase=yes + > exp-archived-phase=yes > [extensions] > strip= > [experimental]
--- a/tests/test-phases.t Thu Sep 01 16:44:00 2022 +0200 +++ b/tests/test-phases.t Thu Sep 01 16:51:26 2022 +0200 @@ -879,7 +879,7 @@ Check we deny its usage on older repository - $ hg init no-internal-phase --config format.internal-phase=no + $ hg init no-internal-phase --config format.use-internal-phase=no $ cd no-internal-phase $ hg debugrequires | grep internal-phase [1] @@ -900,10 +900,10 @@ Check it works fine with repository that supports it. - $ hg init internal-phase --config format.internal-phase=yes + $ hg init internal-phase --config format.use-internal-phase=yes $ cd internal-phase $ hg debugrequires | grep internal-phase - internal-phase + internal-phase-2 $ mkcommit A test-debug-phase: new rev 0: x -> 1 test-hook-close-phase: 4a2df7238c3b48766b5e22fafbb8a2f506ec8256: -> draft @@ -951,21 +951,28 @@ Commit an archived changesets + $ cd .. + $ hg clone --quiet --pull internal-phase archived-phase \ + > --config format.exp-archived-phase=yes \ + > --config extensions.phasereport='!' \ + > --config hooks.txnclose-phase.test= + + $ cd archived-phase + $ echo B > B $ hg add B $ hg status A B $ hg --config "phases.new-commit=archived" commit -m "my test archived commit" - test-debug-phase: new rev 2: x -> 32 + test-debug-phase: new rev 1: x -> 32 test-hook-close-phase: 8df5997c3361518f733d1ae67cd3adb9b0eaf125: -> archived The changeset is a working parent descendant. Per the usual visibility rules, it is made visible. $ hg log -G -l 3 - @ changeset: 2:8df5997c3361 + @ changeset: 1:8df5997c3361 | tag: tip - | parent: 0:4a2df7238c3b | user: test | date: Thu Jan 01 00:00:00 1970 +0000 | summary: my test archived commit
--- a/tests/test-revset.t Thu Sep 01 16:44:00 2022 +0200 +++ b/tests/test-revset.t Thu Sep 01 16:51:26 2022 +0200 @@ -2974,6 +2974,25 @@ 1 b11 m12 u111 112 7200 0 b12 m111 u112 111 10800 +random sort + + $ hg log --rev 'sort(all(), "random")' | wc -l + \s*8 (re) + $ hg log --rev 'sort(all(), "-random")' | wc -l + \s*8 (re) + $ hg log --rev 'sort(all(), "random", random.seed=celeste)' + 6 b111 t2 tu 130 0 + 7 b111 t3 tu 130 0 + 4 b111 m112 u111 110 14400 + 3 b112 m111 u11 120 0 + 5 b111 t1 tu 130 0 + 0 b12 m111 u112 111 10800 + 1 b11 m12 u111 112 7200 + 2 b111 m11 u12 111 3600 + $ hg log --rev 'first(sort(all(), "random", random.seed=celeste))' + 6 b111 t2 tu 130 0 + + topographical sorting can't be combined with other sort keys, and you can't use the topo.firstbranch option when topo sort is not active:
--- a/tests/test-shelve.t Thu Sep 01 16:44:00 2022 +0200 +++ b/tests/test-shelve.t Thu Sep 01 16:51:26 2022 +0200 @@ -14,7 +14,7 @@ $ cat <<EOF >> $HGRCPATH > [format] - > internal-phase = yes + > use-internal-phase = yes > EOF #endif @@ -243,12 +243,12 @@ (this also tests that same timestamp prevents backups from being removed, even though there are more than 'maxbackups' backups) - $ f -t .hg/shelve-backup/default.patch - .hg/shelve-backup/default.patch: file - $ touch -t 200001010000 .hg/shelve-backup/default.patch - $ f -t .hg/shelve-backup/default-1.patch - .hg/shelve-backup/default-1.patch: file - $ touch -t 200001010000 .hg/shelve-backup/default-1.patch + $ f -t .hg/shelve-backup/default.shelve + .hg/shelve-backup/default.shelve: file + $ touch -t 200001010000 .hg/shelve-backup/default.shelve + $ f -t .hg/shelve-backup/default-1.shelve + .hg/shelve-backup/default-1.shelve: file + $ touch -t 200001010000 .hg/shelve-backup/default-1.shelve $ hg unshelve unshelving change 'default-01' @@ -1534,4 +1534,87 @@ $ hg update -q --clean . $ hg patch -p1 test_patch.patch applying test_patch.patch + + $ hg strip -q -r . #endif + +Check the comment of the last commit for consistency + + $ hg log -r . --template '{desc}\n' + add C to bars + +-- if phasebased, shelve works without patch and bundle + + $ hg update -q --clean . + $ rm -r .hg/shelve* + $ echo import antigravity >> somefile.py + $ hg add somefile.py + $ hg shelve -q +#if phasebased + $ rm .hg/shelved/default.hg + $ rm .hg/shelved/default.patch +#endif + +shelve --list --patch should work even with no patch file. + + $ hg shelve --list --patch + default (*s ago) * changes to: add C to bars (glob) + + diff --git a/somefile.py b/somefile.py + new file mode 100644 + --- /dev/null + +++ b/somefile.py + @@ -0,0 +1,1 @@ + +import antigravity + + $ hg unshelve + unshelving change 'default' + +#if phasebased + $ ls .hg/shelve-backup + default.shelve +#endif + +#if stripbased + $ ls .hg/shelve-backup + default.hg + default.patch + default.shelve +#endif + + +-- allow for phase-based shelves to be disabled + + $ hg update -q --clean . + $ hg strip -q --hidden -r 0 + $ rm -r .hg/shelve* + +#if phasebased + $ cat <<EOF >> $HGRCPATH + > [shelve] + > store = strip + > EOF +#endif + + $ echo import this >> somefile.py + $ hg add somefile.py + $ hg shelve -q + $ hg log --hidden + $ ls .hg/shelved + default.hg + default.patch + default.shelve + $ hg unshelve -q + +Override the disabling, re-enabling phase-based shelves + + $ hg shelve --config shelve.store=internal -q + +#if phasebased + $ hg log --hidden --template '{user}\n' + shelve@localhost +#endif + +#if stripbased + $ hg log --hidden --template '{user}\n' +#endif
--- a/tests/test-shelve2.t Thu Sep 01 16:44:00 2022 +0200 +++ b/tests/test-shelve2.t Thu Sep 01 16:51:26 2022 +0200 @@ -16,7 +16,7 @@ $ cat <<EOF >> $HGRCPATH > [format] - > internal-phase = yes + > use-internal-phase = yes > EOF #endif
--- a/tests/test-subrepo-svn.t Thu Sep 01 16:44:00 2022 +0200 +++ b/tests/test-subrepo-svn.t Thu Sep 01 16:51:26 2022 +0200 @@ -591,7 +591,7 @@ $ cd "$WCROOT" $ svn up > /dev/null $ mkdir trunk/subdir branches - $ echo a > trunk/subdir/a + $ echo foo > trunk/subdir/a $ svn add trunk/subdir branches A trunk/subdir A trunk/subdir/a
--- a/tests/test-update-branches.t Thu Sep 01 16:44:00 2022 +0200 +++ b/tests/test-update-branches.t Thu Sep 01 16:51:26 2022 +0200 @@ -633,6 +633,10 @@ # # To mark files as resolved: hg resolve --mark FILE + $ hg status -T '{status} {path} - {relpath(path)}\n' + M foo - foo + a - a + $ hg status -Tjson [ {