debugcommands.py
4769 lines
| 151.0 KiB
| text/x-python
|
PythonLexer
/ mercurial / debugcommands.py
Gregory Szorc
|
r30401 | # debugcommands.py - command processing for debug* commands | ||
# | ||||
Raphaël Gomès
|
r47575 | # Copyright 2005-2016 Olivia Mackall <olivia@selenic.com> | ||
Gregory Szorc
|
r30401 | # | ||
# This software may be used and distributed according to the terms of the | ||||
# GNU General Public License version 2 or any later version. | ||||
Matt Harbison
|
r52756 | from __future__ import annotations | ||
Gregory Szorc
|
r30401 | |||
Simon Sapin
|
r48474 | import binascii | ||
Yuya Nishihara
|
r34131 | import codecs | ||
Augie Fackler
|
r34027 | import collections | ||
r47503 | import contextlib | |||
Pierre-Yves David
|
r30952 | import difflib | ||
Pierre-Yves David
|
r30938 | import errno | ||
Pulkit Goyal
|
r44915 | import glob | ||
Gregory Szorc
|
r30518 | import operator | ||
Gregory Szorc
|
r30525 | import os | ||
Gregory Szorc
|
r44603 | import platform | ||
Gregory Szorc
|
r30517 | import random | ||
Gregory Szorc
|
r37031 | import re | ||
Pierre-Yves David
|
r30938 | import socket | ||
Matt Harbison
|
r33493 | import ssl | ||
Augie Fackler
|
r36799 | import stat | ||
Gregory Szorc
|
r36545 | import subprocess | ||
Pierre-Yves David
|
r30918 | import sys | ||
Pierre-Yves David
|
r30938 | import time | ||
Gregory Szorc
|
r30401 | |||
from .i18n import _ | ||||
Gregory Szorc
|
r30402 | from .node import ( | ||
Gregory Szorc
|
r30526 | bin, | ||
Gregory Szorc
|
r30402 | hex, | ||
Pierre-Yves David
|
r30951 | nullrev, | ||
Gregory Szorc
|
r30517 | short, | ||
Gregory Szorc
|
r30402 | ) | ||
Gregory Szorc
|
r43359 | from .pycompat import ( | ||
open, | ||||
) | ||||
Gregory Szorc
|
r30401 | from . import ( | ||
Gregory Szorc
|
r30501 | bundle2, | ||
Pulkit Goyal
|
r44915 | bundlerepo, | ||
Gregory Szorc
|
r30501 | changegroup, | ||
Gregory Szorc
|
r30401 | cmdutil, | ||
Pierre-Yves David
|
r31120 | color, | ||
Gregory Szorc
|
r30402 | context, | ||
Martin von Zweigbergk
|
r41656 | copies, | ||
Gregory Szorc
|
r30402 | dagparser, | ||
Raphaël Gomès
|
r50046 | dirstateutils, | ||
Pierre-Yves David
|
r30918 | encoding, | ||
Gregory Szorc
|
r30401 | error, | ||
Gregory Szorc
|
r30501 | exchange, | ||
Gregory Szorc
|
r30518 | extensions, | ||
Arseniy Alekseyev
|
r51402 | filelog, | ||
FUJIWARA Katsunori
|
r32256 | filemerge, | ||
Yuya Nishihara
|
r38841 | filesetlang, | ||
Pierre-Yves David
|
r30957 | formatter, | ||
Gregory Szorc
|
r30501 | hg, | ||
Gregory Szorc
|
r37030 | httppeer, | ||
Gregory Szorc
|
r30517 | localrepo, | ||
Gregory Szorc
|
r30402 | lock as lockmod, | ||
Yuya Nishihara
|
r35906 | logcmdutil, | ||
Arseniy Alekseyev
|
r51402 | manifest, | ||
Augie Fackler
|
r45383 | mergestate as mergestatemod, | ||
r46257 | metadata, | |||
Pierre-Yves David
|
r30939 | obsolete, | ||
r33143 | obsutil, | |||
Martin von Zweigbergk
|
r44032 | pathutil, | ||
Gregory Szorc
|
r32745 | phases, | ||
Pierre-Yves David
|
r30918 | policy, | ||
Pierre-Yves David
|
r30947 | pvec, | ||
Pulkit Goyal
|
r30519 | pycompat, | ||
Yuya Nishihara
|
r32337 | registrar, | ||
Gregory Szorc
|
r30775 | repair, | ||
r47205 | repoview, | |||
Raphaël Gomès
|
r48623 | requirements, | ||
Gregory Szorc
|
r30401 | revlog, | ||
Pierre-Yves David
|
r30952 | revset, | ||
Yuya Nishihara
|
r31024 | revsetlang, | ||
Gregory Szorc
|
r30401 | scmutil, | ||
Gregory Szorc
|
r30517 | setdiscovery, | ||
Gregory Szorc
|
r30402 | simplemerge, | ||
Gregory Szorc
|
r36545 | sshpeer, | ||
Pierre-Yves David
|
r30918 | sslutil, | ||
Gregory Szorc
|
r30502 | streamclone, | ||
Valentin Gatien-Baron
|
r46477 | strip, | ||
Valentin Gatien-Baron
|
r44772 | tags as tagsmod, | ||
Pierre-Yves David
|
r30918 | templater, | ||
Gregory Szorc
|
r30517 | treediscovery, | ||
Pierre-Yves David
|
r31864 | upgrade, | ||
Boris Feld
|
r35578 | url as urlmod, | ||
Gregory Szorc
|
r30516 | util, | ||
Raphaël Gomès
|
r50721 | verify, | ||
Pierre-Yves David
|
r31239 | vfs as vfsmod, | ||
Gregory Szorc
|
r37069 | wireprotoframing, | ||
Gregory Szorc
|
r36544 | wireprotoserver, | ||
Gregory Szorc
|
r30401 | ) | ||
r48078 | from .interfaces import repository | |||
pacien
|
r51288 | from .stabletailgraph import stabletailsort | ||
Yuya Nishihara
|
r37102 | from .utils import ( | ||
Gregory Szorc
|
r39480 | cborutil, | ||
r42208 | compression, | |||
Yuya Nishihara
|
r37102 | dateutil, | ||
Yuya Nishihara
|
r37138 | procutil, | ||
Yuya Nishihara
|
r37102 | stringutil, | ||
r47669 | urlutil, | |||
Yuya Nishihara
|
r37102 | ) | ||
Gregory Szorc
|
r30401 | |||
r44788 | from .revlogutils import ( | |||
r50145 | debug as revlog_debug, | |||
r44788 | nodemap, | |||
Raphaël Gomès
|
r48623 | rewrite, | ||
r46257 | sidedata, | |||
r44788 | ) | |||
Boris Feld
|
r39366 | |||
Gregory Szorc
|
r30402 | release = lockmod.release | ||
Valentin Gatien-Baron
|
r46477 | table = {} | ||
table.update(strip.command._table) | ||||
command = registrar.command(table) | ||||
Gregory Szorc
|
r30401 | |||
Augie Fackler
|
r43346 | |||
Augie Fackler
|
r43347 | @command(b'debugancestor', [], _(b'[INDEX] REV1 REV2'), optionalrepo=True) | ||
Gregory Szorc
|
r30401 | def debugancestor(ui, repo, *args): | ||
"""find the ancestor revision of two revisions in a given index""" | ||||
if len(args) == 3: | ||||
index, rev1, rev2 = args | ||||
Matt Harbison
|
r39843 | r = revlog.revlog(vfsmod.vfs(encoding.getcwd(), audit=False), index) | ||
Gregory Szorc
|
r30401 | lookup = r.lookup | ||
elif len(args) == 2: | ||||
if not repo: | ||||
Augie Fackler
|
r43346 | raise error.Abort( | ||
Martin von Zweigbergk
|
r43387 | _(b'there is no Mercurial repository here (.hg not found)') | ||
Augie Fackler
|
r43346 | ) | ||
Gregory Szorc
|
r30401 | rev1, rev2 = args | ||
r = repo.changelog | ||||
lookup = repo.lookup | ||||
else: | ||||
Augie Fackler
|
r43347 | raise error.Abort(_(b'either two or three arguments required')) | ||
Gregory Szorc
|
r30401 | a = r.ancestor(lookup(rev1), lookup(rev2)) | ||
Augie Fackler
|
r43347 | ui.write(b'%d:%s\n' % (r.rev(a), hex(a))) | ||
Augie Fackler
|
r45709 | @command(b'debugantivirusrunning', []) | ||
def debugantivirusrunning(ui, repo): | ||||
"""attempt to trigger an antivirus scanner to see if one is active""" | ||||
with repo.cachevfs.open('eicar-test-file.com', b'wb') as f: | ||||
f.write( | ||||
util.b85decode( | ||||
# This is a base85-armored version of the EICAR test file. See | ||||
# https://en.wikipedia.org/wiki/EICAR_test_file for details. | ||||
b'ST#=}P$fV?P+K%yP+C|uG$>GBDK|qyDK~v2MM*<JQY}+dK~6+LQba95P' | ||||
b'E<)&Nm5l)EmTEQR4qnHOhq9iNGnJx' | ||||
) | ||||
) | ||||
# Give an AV engine time to scan the file. | ||||
time.sleep(2) | ||||
util.unlink(repo.cachevfs.join('eicar-test-file.com')) | ||||
Augie Fackler
|
r43347 | @command(b'debugapplystreamclonebundle', [], b'FILE') | ||
Gregory Szorc
|
r30541 | def debugapplystreamclonebundle(ui, repo, fname): | ||
"""apply a stream clone bundle file""" | ||||
f = hg.openpath(ui, fname) | ||||
gen = exchange.readbundle(ui, f, fname) | ||||
gen.apply(repo) | ||||
Augie Fackler
|
r43346 | |||
@command( | ||||
Augie Fackler
|
r43347 | b'debugbuilddag', | ||
Augie Fackler
|
r43346 | [ | ||
Augie Fackler
|
r43347 | ( | ||
b'm', | ||||
b'mergeable-file', | ||||
None, | ||||
_(b'add single file mergeable changes'), | ||||
), | ||||
Augie Fackler
|
r43346 | ( | ||
Augie Fackler
|
r43347 | b'o', | ||
b'overwritten-file', | ||||
Augie Fackler
|
r43346 | None, | ||
Augie Fackler
|
r43347 | _(b'add single file all revs overwrite'), | ||
Augie Fackler
|
r43346 | ), | ||
Augie Fackler
|
r43347 | (b'n', b'new-file', None, _(b'add new file at each rev')), | ||
r49540 | ( | |||
b'', | ||||
b'from-existing', | ||||
None, | ||||
_(b'continue from a non-empty repository'), | ||||
), | ||||
Augie Fackler
|
r43346 | ], | ||
Augie Fackler
|
r43347 | _(b'[OPTION]... [TEXT]'), | ||
Augie Fackler
|
r43346 | ) | ||
def debugbuilddag( | ||||
ui, | ||||
repo, | ||||
text=None, | ||||
mergeable_file=False, | ||||
overwritten_file=False, | ||||
new_file=False, | ||||
r49540 | from_existing=False, | |||
Augie Fackler
|
r43346 | ): | ||
Gregory Szorc
|
r30402 | """builds a repo with a given DAG from scratch in the current empty repo | ||
The description of the DAG is read from stdin if not given on the | ||||
command line. | ||||
Elements: | ||||
- "+n" is a linear run of n nodes based on the current default parent | ||||
- "." is a single node based on the current default parent | ||||
- "$" resets the default parent to null (implied at the start); | ||||
otherwise the default parent is always the last node created | ||||
- "<p" sets the default parent to the backref p | ||||
- "*p" is a fork at parent p, which is a backref | ||||
- "*p1/p2" is a merge of parents p1 and p2, which are backrefs | ||||
- "/p2" is a merge of the preceding node and p2 | ||||
- ":tag" defines a local tag for the preceding node | ||||
- "@branch" sets the named branch for subsequent nodes | ||||
- "#...\\n" is a comment up to the end of the line | ||||
Whitespace between the above elements is ignored. | ||||
A backref is either | ||||
- a number n, which references the node curr-n, where curr is the current | ||||
node, or | ||||
- the name of a local tag you placed earlier using ":tag", or | ||||
- empty to denote the default parent. | ||||
All string valued-elements are either strictly alphanumeric, or must | ||||
be enclosed in double quotes ("..."), with "\\" as escape character. | ||||
""" | ||||
if text is None: | ||||
Augie Fackler
|
r43347 | ui.status(_(b"reading DAG from stdin\n")) | ||
Gregory Szorc
|
r30402 | text = ui.fin.read() | ||
cl = repo.changelog | ||||
r49540 | if len(cl) > 0 and not from_existing: | |||
Augie Fackler
|
r43347 | raise error.Abort(_(b'repository is not empty')) | ||
Gregory Szorc
|
r30402 | |||
# determine number of revs in DAG | ||||
total = 0 | ||||
for type, data in dagparser.parsedag(text): | ||||
Augie Fackler
|
r43347 | if type == b'n': | ||
Gregory Szorc
|
r30402 | total += 1 | ||
if mergeable_file: | ||||
linesperrev = 2 | ||||
# make a file with k lines per rev | ||||
Manuel Jacob
|
r50179 | initialmergedlines = [b'%d' % i for i in range(0, total * linesperrev)] | ||
Augie Fackler
|
r43347 | initialmergedlines.append(b"") | ||
Gregory Szorc
|
r30402 | |||
tags = [] | ||||
Augie Fackler
|
r43347 | progress = ui.makeprogress( | ||
_(b'building'), unit=_(b'revisions'), total=total | ||||
) | ||||
with progress, repo.wlock(), repo.lock(), repo.transaction(b"builddag"): | ||||
Gregory Szorc
|
r30402 | at = -1 | ||
Augie Fackler
|
r43347 | atbranch = b'default' | ||
Gregory Szorc
|
r30402 | nodeids = [] | ||
id = 0 | ||||
Martin von Zweigbergk
|
r38394 | progress.update(id) | ||
Gregory Szorc
|
r30402 | for type, data in dagparser.parsedag(text): | ||
Augie Fackler
|
r43347 | if type == b'n': | ||
ui.note((b'node %s\n' % pycompat.bytestr(data))) | ||||
Gregory Szorc
|
r30402 | id, ps = data | ||
files = [] | ||||
Martin von Zweigbergk
|
r35400 | filecontent = {} | ||
Gregory Szorc
|
r30402 | |||
p2 = None | ||||
if mergeable_file: | ||||
Augie Fackler
|
r43347 | fn = b"mf" | ||
Gregory Szorc
|
r30402 | p1 = repo[ps[0]] | ||
if len(ps) > 1: | ||||
p2 = repo[ps[1]] | ||||
pa = p1.ancestor(p2) | ||||
Augie Fackler
|
r43346 | base, local, other = [ | ||
x[fn].data() for x in (pa, p1, p2) | ||||
] | ||||
Gregory Szorc
|
r30402 | m3 = simplemerge.Merge3Text(base, local, other) | ||
Martin von Zweigbergk
|
r49407 | ml = [ | ||
Martin von Zweigbergk
|
r49411 | l.strip() | ||
for l in simplemerge.render_minimized(m3)[0] | ||||
Martin von Zweigbergk
|
r49407 | ] | ||
Augie Fackler
|
r43347 | ml.append(b"") | ||
Gregory Szorc
|
r30402 | elif at > 0: | ||
Augie Fackler
|
r43347 | ml = p1[fn].data().split(b"\n") | ||
Gregory Szorc
|
r30402 | else: | ||
ml = initialmergedlines | ||||
Augie Fackler
|
r43347 | ml[id * linesperrev] += b" r%i" % id | ||
mergedtext = b"\n".join(ml) | ||||
Gregory Szorc
|
r30402 | files.append(fn) | ||
Martin von Zweigbergk
|
r35400 | filecontent[fn] = mergedtext | ||
Gregory Szorc
|
r30402 | |||
if overwritten_file: | ||||
Augie Fackler
|
r43347 | fn = b"of" | ||
Gregory Szorc
|
r30402 | files.append(fn) | ||
Augie Fackler
|
r43347 | filecontent[fn] = b"r%i\n" % id | ||
Gregory Szorc
|
r30402 | |||
if new_file: | ||||
Augie Fackler
|
r43347 | fn = b"nf%i" % id | ||
Gregory Szorc
|
r30402 | files.append(fn) | ||
Augie Fackler
|
r43347 | filecontent[fn] = b"r%i\n" % id | ||
Gregory Szorc
|
r30402 | if len(ps) > 1: | ||
if not p2: | ||||
p2 = repo[ps[1]] | ||||
for fn in p2: | ||||
Augie Fackler
|
r43347 | if fn.startswith(b"nf"): | ||
Gregory Szorc
|
r30402 | files.append(fn) | ||
Martin von Zweigbergk
|
r35400 | filecontent[fn] = p2[fn].data() | ||
Gregory Szorc
|
r30402 | |||
def fctxfn(repo, cx, path): | ||||
Martin von Zweigbergk
|
r35400 | if path in filecontent: | ||
Augie Fackler
|
r43346 | return context.memfilectx( | ||
repo, cx, path, filecontent[path] | ||||
) | ||||
Martin von Zweigbergk
|
r35400 | return None | ||
Gregory Szorc
|
r30402 | |||
if len(ps) == 0 or ps[0] < 0: | ||||
pars = [None, None] | ||||
elif len(ps) == 1: | ||||
pars = [nodeids[ps[0]], None] | ||||
else: | ||||
pars = [nodeids[p] for p in ps] | ||||
Augie Fackler
|
r43346 | cx = context.memctx( | ||
repo, | ||||
pars, | ||||
Augie Fackler
|
r43347 | b"r%i" % id, | ||
Augie Fackler
|
r43346 | files, | ||
fctxfn, | ||||
date=(id, 0), | ||||
Augie Fackler
|
r43347 | user=b"debugbuilddag", | ||
extra={b'branch': atbranch}, | ||||
Augie Fackler
|
r43346 | ) | ||
Gregory Szorc
|
r30402 | nodeid = repo.commitctx(cx) | ||
nodeids.append(nodeid) | ||||
at = id | ||||
Augie Fackler
|
r43347 | elif type == b'l': | ||
Gregory Szorc
|
r30402 | id, name = data | ||
Augie Fackler
|
r43347 | ui.note((b'tag %s\n' % name)) | ||
tags.append(b"%s %s\n" % (hex(repo.changelog.node(id)), name)) | ||||
elif type == b'a': | ||||
ui.note((b'branch %s\n' % data)) | ||||
Gregory Szorc
|
r30402 | atbranch = data | ||
Martin von Zweigbergk
|
r38394 | progress.update(id) | ||
Gregory Szorc
|
r30402 | |||
if tags: | ||||
Augie Fackler
|
r43347 | repo.vfs.write(b"localtags", b"".join(tags)) | ||
Gregory Szorc
|
r30501 | |||
Augie Fackler
|
r43346 | |||
Gregory Szorc
|
r30501 | def _debugchangegroup(ui, gen, all=None, indent=0, **opts): | ||
Augie Fackler
|
r43347 | indent_string = b' ' * indent | ||
Gregory Szorc
|
r30501 | if all: | ||
Augie Fackler
|
r43350 | ui.writenoi18n( | ||
Augie Fackler
|
r43347 | b"%sformat: id, p1, p2, cset, delta base, len(delta)\n" | ||
Augie Fackler
|
r43346 | % indent_string | ||
) | ||||
Gregory Szorc
|
r30501 | |||
def showchunks(named): | ||||
Augie Fackler
|
r43347 | ui.write(b"\n%s%s\n" % (indent_string, named)) | ||
Durham Goode
|
r34293 | for deltadata in gen.deltaiter(): | ||
Raphaël Gomès
|
r47445 | node, p1, p2, cs, deltabase, delta, flags, sidedata = deltadata | ||
Augie Fackler
|
r43346 | ui.write( | ||
Augie Fackler
|
r43347 | b"%s%s %s %s %s %s %d\n" | ||
Augie Fackler
|
r43346 | % ( | ||
indent_string, | ||||
hex(node), | ||||
hex(p1), | ||||
hex(p2), | ||||
hex(cs), | ||||
hex(deltabase), | ||||
len(delta), | ||||
) | ||||
) | ||||
Gregory Szorc
|
r30501 | |||
Matt Harbison
|
r44424 | gen.changelogheader() | ||
Augie Fackler
|
r43347 | showchunks(b"changelog") | ||
Matt Harbison
|
r44424 | gen.manifestheader() | ||
Augie Fackler
|
r43347 | showchunks(b"manifest") | ||
Gregory Szorc
|
r30501 | for chunkdata in iter(gen.filelogheader, {}): | ||
Augie Fackler
|
r43347 | fname = chunkdata[b'filename'] | ||
Gregory Szorc
|
r30501 | showchunks(fname) | ||
else: | ||||
if isinstance(gen, bundle2.unbundle20): | ||||
Augie Fackler
|
r43347 | raise error.Abort(_(b'use debugbundle2 for this file')) | ||
Matt Harbison
|
r44424 | gen.changelogheader() | ||
Durham Goode
|
r34293 | for deltadata in gen.deltaiter(): | ||
Raphaël Gomès
|
r47445 | node, p1, p2, cs, deltabase, delta, flags, sidedata = deltadata | ||
Augie Fackler
|
r43347 | ui.write(b"%s%s\n" % (indent_string, hex(node))) | ||
Gregory Szorc
|
r30501 | |||
Augie Fackler
|
r43346 | |||
Martin von Zweigbergk
|
r33029 | def _debugobsmarkers(ui, part, indent=0, **opts): | ||
r32517 | """display version and markers contained in 'data'""" | |||
Martin von Zweigbergk
|
r33029 | data = part.read() | ||
Augie Fackler
|
r43347 | indent_string = b' ' * indent | ||
r32517 | try: | |||
version, markers = obsolete._readmarkers(data) | ||||
except error.UnknownVersion as exc: | ||||
Augie Fackler
|
r43347 | msg = b"%sunsupported version: %s (%d bytes)\n" | ||
r32517 | msg %= indent_string, exc.version, len(data) | |||
ui.write(msg) | ||||
else: | ||||
Augie Fackler
|
r43347 | msg = b"%sversion: %d (%d bytes)\n" | ||
r32517 | msg %= indent_string, version, len(data) | |||
ui.write(msg) | ||||
Matt Harbison
|
r51837 | fm = ui.formatter(b'debugobsolete', pycompat.byteskwargs(opts)) | ||
r32517 | for rawmarker in sorted(markers): | |||
r33149 | m = obsutil.marker(None, rawmarker) | |||
r32517 | fm.startitem() | |||
fm.plain(indent_string) | ||||
cmdutil.showmarker(fm, m) | ||||
fm.end() | ||||
Augie Fackler
|
r43346 | |||
Martin von Zweigbergk
|
r33031 | def _debugphaseheads(ui, data, indent=0): | ||
"""display version and markers contained in 'data'""" | ||||
Augie Fackler
|
r43347 | indent_string = b' ' * indent | ||
Boris Feld
|
r34321 | headsbyphase = phases.binarydecode(data) | ||
Martin von Zweigbergk
|
r33031 | for phase in phases.allphases: | ||
for head in headsbyphase[phase]: | ||||
ui.write(indent_string) | ||||
Augie Fackler
|
r43347 | ui.write(b'%s %s\n' % (hex(head), phases.phasenames[phase])) | ||
Martin von Zweigbergk
|
r33031 | |||
Augie Fackler
|
r43346 | |||
Augie Fackler
|
r34027 | def _quasirepr(thing): | ||
if isinstance(thing, (dict, util.sortdict, collections.OrderedDict)): | ||||
Augie Fackler
|
r43347 | return b'{%s}' % ( | ||
Augie Fackler
|
r43346 | b', '.join(b'%s: %s' % (k, thing[k]) for k in sorted(thing)) | ||
) | ||||
Augie Fackler
|
r34027 | return pycompat.bytestr(repr(thing)) | ||
Augie Fackler
|
r43346 | |||
Gregory Szorc
|
r30501 | def _debugbundle2(ui, gen, all=None, **opts): | ||
"""lists the contents of a bundle2""" | ||||
if not isinstance(gen, bundle2.unbundle20): | ||||
Augie Fackler
|
r43347 | raise error.Abort(_(b'not a bundle2 file')) | ||
ui.write((b'Stream params: %s\n' % _quasirepr(gen.params))) | ||||
Augie Fackler
|
r43906 | parttypes = opts.get('part_type', []) | ||
Gregory Szorc
|
r30501 | for part in gen.iterparts(): | ||
Danek Duvall
|
r32694 | if parttypes and part.type not in parttypes: | ||
continue | ||||
Augie Fackler
|
r43347 | msg = b'%s -- %s (mandatory: %r)\n' | ||
Boris Feld
|
r37919 | ui.write((msg % (part.type, _quasirepr(part.params), part.mandatory))) | ||
Augie Fackler
|
r43347 | if part.type == b'changegroup': | ||
version = part.params.get(b'version', b'01') | ||||
cg = changegroup.getunbundler(version, part, b'UN') | ||||
Boris Feld
|
r36969 | if not ui.quiet: | ||
_debugchangegroup(ui, cg, all=all, indent=4, **opts) | ||||
Augie Fackler
|
r43347 | if part.type == b'obsmarkers': | ||
Boris Feld
|
r36969 | if not ui.quiet: | ||
_debugobsmarkers(ui, part, indent=4, **opts) | ||||
Augie Fackler
|
r43347 | if part.type == b'phase-heads': | ||
Boris Feld
|
r36969 | if not ui.quiet: | ||
_debugphaseheads(ui, part, indent=4) | ||||
Gregory Szorc
|
r30502 | |||
Augie Fackler
|
r43346 | |||
@command( | ||||
Augie Fackler
|
r43347 | b'debugbundle', | ||
Augie Fackler
|
r43346 | [ | ||
Augie Fackler
|
r43347 | (b'a', b'all', None, _(b'show all details')), | ||
(b'', b'part-type', [], _(b'show only the named part type')), | ||||
(b'', b'spec', None, _(b'print the bundlespec of the bundle')), | ||||
Augie Fackler
|
r43346 | ], | ||
Augie Fackler
|
r43347 | _(b'FILE'), | ||
Augie Fackler
|
r43346 | norepo=True, | ||
) | ||||
Gregory Szorc
|
r30541 | def debugbundle(ui, bundlepath, all=None, spec=None, **opts): | ||
"""lists the contents of a bundle""" | ||||
with hg.openpath(ui, bundlepath) as f: | ||||
if spec: | ||||
spec = exchange.getbundlespec(ui, f) | ||||
Augie Fackler
|
r43347 | ui.write(b'%s\n' % spec) | ||
Gregory Szorc
|
r30541 | return | ||
Gregory Szorc
|
r30502 | |||
Gregory Szorc
|
r30541 | gen = exchange.readbundle(ui, f, bundlepath) | ||
if isinstance(gen, bundle2.unbundle20): | ||||
return _debugbundle2(ui, gen, all=all, **opts) | ||||
_debugchangegroup(ui, gen, all=all, **opts) | ||||
Gregory Szorc
|
r30503 | |||
Augie Fackler
|
r43346 | |||
Augie Fackler
|
r43347 | @command(b'debugcapabilities', [], _(b'PATH'), norepo=True) | ||
Boris Feld
|
r34960 | def debugcapabilities(ui, path, **opts): | ||
"""lists the capabilities of a remote peer""" | ||||
Matt Harbison
|
r51838 | peer = hg.peer(ui, pycompat.byteskwargs(opts), path) | ||
Valentin Gatien-Baron
|
r47419 | try: | ||
caps = peer.capabilities() | ||||
ui.writenoi18n(b'Main capabilities:\n') | ||||
for c in sorted(caps): | ||||
ui.write(b' %s\n' % c) | ||||
b2caps = bundle2.bundle2caps(peer) | ||||
if b2caps: | ||||
ui.writenoi18n(b'Bundle2 capabilities:\n') | ||||
Gregory Szorc
|
r49768 | for key, values in sorted(b2caps.items()): | ||
Valentin Gatien-Baron
|
r47419 | ui.write(b' %s\n' % key) | ||
for v in values: | ||||
ui.write(b' %s\n' % v) | ||||
finally: | ||||
peer.close() | ||||
Augie Fackler
|
r43347 | |||
r47208 | @command( | |||
b'debugchangedfiles', | ||||
[ | ||||
( | ||||
b'', | ||||
b'compute', | ||||
False, | ||||
b"compute information instead of reading it from storage", | ||||
), | ||||
], | ||||
b'REV', | ||||
) | ||||
def debugchangedfiles(ui, repo, rev, **opts): | ||||
r46257 | """list the stored files changes for a revision""" | |||
Martin von Zweigbergk
|
r48930 | ctx = logcmdutil.revsingle(repo, rev, None) | ||
r47208 | files = None | |||
if opts['compute']: | ||||
files = metadata.compute_all_files_changes(ctx) | ||||
else: | ||||
sd = repo.changelog.sidedata(ctx.rev()) | ||||
files_block = sd.get(sidedata.SD_FILES) | ||||
if files_block is not None: | ||||
files = metadata.decode_files_sidedata(sd) | ||||
if files is not None: | ||||
r46257 | for f in sorted(files.touched): | |||
if f in files.added: | ||||
action = b"added" | ||||
elif f in files.removed: | ||||
action = b"removed" | ||||
elif f in files.merged: | ||||
action = b"merged" | ||||
elif f in files.salvaged: | ||||
action = b"salvaged" | ||||
else: | ||||
action = b"touched" | ||||
copy_parent = b"" | ||||
copy_source = b"" | ||||
if f in files.copied_from_p1: | ||||
copy_parent = b"p1" | ||||
copy_source = files.copied_from_p1[f] | ||||
elif f in files.copied_from_p2: | ||||
copy_parent = b"p2" | ||||
copy_source = files.copied_from_p2[f] | ||||
data = (action, copy_parent, f, copy_source) | ||||
template = b"%-8s %2s: %s, %s;\n" | ||||
ui.write(template % data) | ||||
Augie Fackler
|
r43347 | @command(b'debugcheckstate', [], b'') | ||
Gregory Szorc
|
r30503 | def debugcheckstate(ui, repo): | ||
"""validate the correctness of the current dirstate""" | ||||
Raphaël Gomès
|
r50721 | errors = verify.verifier(repo)._verify_dirstate() | ||
Gregory Szorc
|
r30503 | if errors: | ||
Raphaël Gomès
|
r50721 | errstr = _(b"dirstate inconsistent with current parent's manifest") | ||
Augie Fackler
|
r44034 | raise error.Abort(errstr) | ||
Gregory Szorc
|
r30504 | |||
Augie Fackler
|
r43346 | |||
@command( | ||||
Augie Fackler
|
r43347 | b'debugcolor', | ||
[(b'', b'style', None, _(b'show all configured styles'))], | ||||
b'hg debugcolor', | ||||
Augie Fackler
|
r43346 | ) | ||
Pierre-Yves David
|
r31120 | def debugcolor(ui, repo, **opts): | ||
"""show available color, effects or style""" | ||||
Augie Fackler
|
r43350 | ui.writenoi18n(b'color mode: %s\n' % stringutil.pprint(ui._colormode)) | ||
Augie Fackler
|
r43906 | if opts.get('style'): | ||
Pierre-Yves David
|
r31120 | return _debugdisplaystyle(ui) | ||
else: | ||||
return _debugdisplaycolor(ui) | ||||
Augie Fackler
|
r43346 | |||
Pierre-Yves David
|
r31120 | def _debugdisplaycolor(ui): | ||
Pierre-Yves David
|
r31121 | ui = ui.copy() | ||
ui._styles.clear() | ||||
Matt Harbison
|
r31689 | for effect in color._activeeffects(ui).keys(): | ||
Pierre-Yves David
|
r31121 | ui._styles[effect] = effect | ||
if ui._terminfoparams: | ||||
Augie Fackler
|
r43347 | for k, v in ui.configitems(b'color'): | ||
if k.startswith(b'color.'): | ||||
Pierre-Yves David
|
r31121 | ui._styles[k] = k[6:] | ||
Augie Fackler
|
r43347 | elif k.startswith(b'terminfo.'): | ||
Pierre-Yves David
|
r31121 | ui._styles[k] = k[9:] | ||
Augie Fackler
|
r43347 | ui.write(_(b'available colors:\n')) | ||
Pierre-Yves David
|
r31121 | # sort label with a '_' after the other to group '_background' entry. | ||
Augie Fackler
|
r43347 | items = sorted(ui._styles.items(), key=lambda i: (b'_' in i[0], i[0], i[1])) | ||
Pierre-Yves David
|
r31121 | for colorname, label in items: | ||
Augie Fackler
|
r43347 | ui.write(b'%s\n' % colorname, label=label) | ||
Augie Fackler
|
r43346 | |||
Pierre-Yves David
|
r31120 | |||
def _debugdisplaystyle(ui): | ||||
Augie Fackler
|
r43347 | ui.write(_(b'available style:\n')) | ||
Yuya Nishihara
|
r37841 | if not ui._styles: | ||
return | ||||
Pierre-Yves David
|
r31120 | width = max(len(s) for s in ui._styles) | ||
for label, effects in sorted(ui._styles.items()): | ||||
Augie Fackler
|
r43347 | ui.write(b'%s' % label, label=label) | ||
Pierre-Yves David
|
r31120 | if effects: | ||
# 50 | ||||
Augie Fackler
|
r43347 | ui.write(b': ') | ||
ui.write(b' ' * (max(0, width - len(label)))) | ||||
ui.write(b', '.join(ui.label(e, e) for e in effects.split())) | ||||
ui.write(b'\n') | ||||
@command(b'debugcreatestreamclonebundle', [], b'FILE') | ||||
Gregory Szorc
|
r30541 | def debugcreatestreamclonebundle(ui, repo, fname): | ||
"""create a stream clone bundle file | ||||
Stream bundles are special bundles that are essentially archives of | ||||
revlog files. They are commonly used for cloning very quickly. | ||||
r51537 | ||||
This command creates a "version 1" stream clone, which is deprecated in | ||||
favor of newer versions of the stream protocol. Bundles using such newer | ||||
versions can be generated using the `hg bundle` command. | ||||
Gregory Szorc
|
r30541 | """ | ||
Gregory Szorc
|
r32745 | # TODO we may want to turn this into an abort when this functionality | ||
# is moved into `hg bundle`. | ||||
if phases.hassecret(repo): | ||||
Augie Fackler
|
r43346 | ui.warn( | ||
_( | ||||
Augie Fackler
|
r43347 | b'(warning: stream clone bundle will contain secret ' | ||
b'revisions)\n' | ||||
Augie Fackler
|
r43346 | ) | ||
) | ||||
Gregory Szorc
|
r32745 | |||
Gregory Szorc
|
r30541 | requirements, gen = streamclone.generatebundlev1(repo) | ||
changegroup.writechunks(ui, gen, fname) | ||||
Augie Fackler
|
r43347 | ui.write(_(b'bundle requirements: %s\n') % b', '.join(sorted(requirements))) | ||
Gregory Szorc
|
r30541 | |||
Augie Fackler
|
r43346 | |||
@command( | ||||
Augie Fackler
|
r43347 | b'debugdag', | ||
Augie Fackler
|
r43346 | [ | ||
Augie Fackler
|
r43347 | (b't', b'tags', None, _(b'use tags as labels')), | ||
(b'b', b'branches', None, _(b'annotate with branch names')), | ||||
(b'', b'dots', None, _(b'use dots for runs')), | ||||
(b's', b'spaces', None, _(b'separate elements by spaces')), | ||||
Augie Fackler
|
r43346 | ], | ||
Augie Fackler
|
r43347 | _(b'[OPTION]... [FILE [REV]...]'), | ||
Augie Fackler
|
r43346 | optionalrepo=True, | ||
) | ||||
Gregory Szorc
|
r30514 | def debugdag(ui, repo, file_=None, *revs, **opts): | ||
"""format the changelog or an index DAG as a concise textual description | ||||
If you pass a revlog index, the revlog's DAG is emitted. If you list | ||||
revision numbers, they get labeled in the output as rN. | ||||
Otherwise, the changelog DAG of the current repo is emitted. | ||||
""" | ||||
Augie Fackler
|
r43906 | spaces = opts.get('spaces') | ||
dots = opts.get('dots') | ||||
Gregory Szorc
|
r30514 | if file_: | ||
Augie Fackler
|
r43346 | rlog = revlog.revlog(vfsmod.vfs(encoding.getcwd(), audit=False), file_) | ||
Augie Fackler
|
r44937 | revs = {int(r) for r in revs} | ||
Augie Fackler
|
r43346 | |||
Gregory Szorc
|
r30514 | def events(): | ||
for r in rlog: | ||||
Augie Fackler
|
r43347 | yield b'n', (r, list(p for p in rlog.parentrevs(r) if p != -1)) | ||
Gregory Szorc
|
r30514 | if r in revs: | ||
Augie Fackler
|
r43347 | yield b'l', (r, b"r%i" % r) | ||
Augie Fackler
|
r43346 | |||
Gregory Szorc
|
r30514 | elif repo: | ||
cl = repo.changelog | ||||
Augie Fackler
|
r43906 | tags = opts.get('tags') | ||
branches = opts.get('branches') | ||||
Gregory Szorc
|
r30514 | if tags: | ||
labels = {} | ||||
for l, n in repo.tags().items(): | ||||
labels.setdefault(cl.rev(n), []).append(l) | ||||
Augie Fackler
|
r43346 | |||
Gregory Szorc
|
r30514 | def events(): | ||
Augie Fackler
|
r43347 | b = b"default" | ||
Gregory Szorc
|
r30514 | for r in cl: | ||
if branches: | ||||
Augie Fackler
|
r43347 | newb = cl.read(cl.node(r))[5][b'branch'] | ||
Gregory Szorc
|
r30514 | if newb != b: | ||
Augie Fackler
|
r43347 | yield b'a', newb | ||
Gregory Szorc
|
r30514 | b = newb | ||
Augie Fackler
|
r43347 | yield b'n', (r, list(p for p in cl.parentrevs(r) if p != -1)) | ||
Gregory Szorc
|
r30514 | if tags: | ||
ls = labels.get(r) | ||||
if ls: | ||||
for l in ls: | ||||
Augie Fackler
|
r43347 | yield b'l', (r, l) | ||
Augie Fackler
|
r43346 | |||
Gregory Szorc
|
r30514 | else: | ||
Augie Fackler
|
r43347 | raise error.Abort(_(b'need repo for changelog dag')) | ||
Gregory Szorc
|
r30514 | |||
Augie Fackler
|
r43346 | for line in dagparser.dagtextlines( | ||
events(), | ||||
addspaces=spaces, | ||||
wraplabels=True, | ||||
wrapannotations=True, | ||||
wrapnonlinear=dots, | ||||
usedots=dots, | ||||
maxlinewidth=70, | ||||
): | ||||
Gregory Szorc
|
r30514 | ui.write(line) | ||
Augie Fackler
|
r43347 | ui.write(b"\n") | ||
@command(b'debugdata', cmdutil.debugrevlogopts, _(b'-c|-m|FILE REV')) | ||||
Gregory Szorc
|
r30515 | def debugdata(ui, repo, file_, rev=None, **opts): | ||
"""dump the contents of a data file revision""" | ||||
Matt Harbison
|
r51839 | if opts.get('changelog') or opts.get('manifest') or opts.get('dir'): | ||
Gregory Szorc
|
r30515 | if rev is not None: | ||
r51440 | raise error.InputError( | |||
_(b'cannot specify a revision with other arguments') | ||||
) | ||||
Gregory Szorc
|
r30515 | file_, rev = None, file_ | ||
elif rev is None: | ||||
r51440 | raise error.InputError(_(b'please specify a revision')) | |||
Matt Harbison
|
r51839 | r = cmdutil.openstorage( | ||
repo, b'debugdata', file_, pycompat.byteskwargs(opts) | ||||
) | ||||
Gregory Szorc
|
r30515 | try: | ||
r43041 | ui.write(r.rawdata(r.lookup(rev))) | |||
Gregory Szorc
|
r30515 | except KeyError: | ||
Augie Fackler
|
r43347 | raise error.Abort(_(b'invalid revision identifier %s') % rev) | ||
Gregory Szorc
|
r30516 | |||
Augie Fackler
|
r43346 | |||
@command( | ||||
Augie Fackler
|
r43347 | b'debugdate', | ||
[(b'e', b'extended', None, _(b'try extended date formats'))], | ||||
_(b'[-e] DATE [RANGE]'), | ||||
Augie Fackler
|
r43346 | norepo=True, | ||
optionalrepo=True, | ||||
) | ||||
Gregory Szorc
|
r30516 | def debugdate(ui, date, range=None, **opts): | ||
"""parse and display a date""" | ||||
Augie Fackler
|
r43809 | if opts["extended"]: | ||
Matt Harbison
|
r44330 | d = dateutil.parsedate(date, dateutil.extendeddateformats) | ||
Gregory Szorc
|
r30516 | else: | ||
Boris Feld
|
r36625 | d = dateutil.parsedate(date) | ||
Augie Fackler
|
r43350 | ui.writenoi18n(b"internal: %d %d\n" % d) | ||
ui.writenoi18n(b"standard: %s\n" % dateutil.datestr(d)) | ||||
Gregory Szorc
|
r30516 | if range: | ||
Boris Feld
|
r36625 | m = dateutil.matchdate(range) | ||
Augie Fackler
|
r43350 | ui.writenoi18n(b"match: %s\n" % m(d[0])) | ||
Augie Fackler
|
r43346 | |||
@command( | ||||
Augie Fackler
|
r43347 | b'debugdeltachain', | ||
r51965 | [ | |||
( | ||||
b'r', | ||||
b'rev', | ||||
[], | ||||
_('restrict processing to these revlog revisions'), | ||||
), | ||||
r51966 | ( | |||
b'', | ||||
r51968 | b'all-info', | |||
r51969 | False, | |||
r51968 | _('compute all information unless specified otherwise'), | |||
), | ||||
( | ||||
b'', | ||||
r51966 | b'size-info', | |||
r51968 | None, | |||
r51966 | _('compute information related to deltas size'), | |||
), | ||||
( | ||||
b'', | ||||
b'dist-info', | ||||
r51968 | None, | |||
r51966 | _('compute information related to base distance'), | |||
), | ||||
( | ||||
b'', | ||||
b'sparse-info', | ||||
r51968 | None, | |||
r51966 | _('compute information related to sparse read'), | |||
), | ||||
r51965 | ] | |||
+ cmdutil.debugrevlogopts | ||||
+ cmdutil.formatteropts, | ||||
Augie Fackler
|
r43347 | _(b'-c|-m|FILE'), | ||
Augie Fackler
|
r43346 | optionalrepo=True, | ||
) | ||||
Gregory Szorc
|
r30541 | def debugdeltachain(ui, repo, file_=None, **opts): | ||
"""dump information about delta chains in a revlog | ||||
Output can be templatized. Available template keywords are: | ||||
:``rev``: revision number | ||||
r50116 | :``p1``: parent 1 revision number (for reference) | |||
:``p2``: parent 2 revision number (for reference) | ||||
r51966 | ||||
Gregory Szorc
|
r30541 | :``chainid``: delta chain identifier (numbered by unique base) | ||
:``chainlen``: delta chain length to this revision | ||||
r51966 | ||||
Gregory Szorc
|
r30541 | :``prevrev``: previous revision in delta chain | ||
:``deltatype``: role of delta / how it was computed | ||||
r50117 | - base: a full snapshot | |||
- snap: an intermediate snapshot | ||||
- p1: a delta against the first parent | ||||
- p2: a delta against the second parent | ||||
r50118 | - skip1: a delta against the same base as p1 | |||
(when p1 has empty delta | ||||
- skip2: a delta against the same base as p2 | ||||
(when p2 has empty delta | ||||
r50117 | - prev: a delta against the previous revision | |||
- other: a delta against an arbitrary revision | ||||
r51966 | ||||
Gregory Szorc
|
r30541 | :``compsize``: compressed size of revision | ||
:``uncompsize``: uncompressed size of revision | ||||
:``chainsize``: total size of compressed revisions in chain | ||||
:``chainratio``: total chain size divided by uncompressed revision size | ||||
(new delta chains typically start at ratio 2.00) | ||||
r51966 | ||||
Gregory Szorc
|
r30541 | :``lindist``: linear distance from base revision in delta chain to end | ||
of this revision | ||||
:``extradist``: total size of revisions not part of this delta chain from | ||||
base of delta chain to end of this revision; a measurement | ||||
of how much extra data we need to read/seek across to read | ||||
the delta chain for this revision | ||||
:``extraratio``: extradist divided by chainsize; another representation of | ||||
how much unrelated data is needed to load this delta chain | ||||
Paul Morelle
|
r35050 | |||
If the repository is configured to use the sparse read, additional keywords | ||||
are available: | ||||
:``readsize``: total size of data read from the disk for a revision | ||||
(sum of the sizes of all the blocks) | ||||
:``largestblock``: size of the largest block of data read from the disk | ||||
:``readdensity``: density of useful bytes in the data read from the disk | ||||
Paul Morelle
|
r35696 | :``srchunks``: in how many data hunks the whole revision would be read | ||
Paul Morelle
|
r35050 | |||
r51966 | It is possible to select the information to be computed, this can provide a | |||
noticeable speedup to the command in some cases. | ||||
Always computed: | ||||
- ``rev`` | ||||
- ``p1`` | ||||
- ``p2`` | ||||
- ``chainid`` | ||||
- ``chainlen`` | ||||
- ``prevrev`` | ||||
- ``deltatype`` | ||||
Computed with --no-size-info | ||||
- ``compsize`` | ||||
- ``uncompsize`` | ||||
- ``chainsize`` | ||||
- ``chainratio`` | ||||
Computed with --no-dist-info | ||||
- ``lindist`` | ||||
- ``extradist`` | ||||
- ``extraratio`` | ||||
Skipped with --no-sparse-info | ||||
- ``readsize`` | ||||
- ``largestblock`` | ||||
- ``readdensity`` | ||||
- ``srchunks`` | ||||
-- | ||||
Paul Morelle
|
r35050 | The sparse read can be enabled with experimental.sparse-read = True | ||
Gregory Szorc
|
r30541 | """ | ||
r51965 | revs = None | |||
revs_opt = opts.pop('rev', []) | ||||
if revs_opt: | ||||
revs = [int(r) for r in revs_opt] | ||||
r51966 | ||||
r51969 | all_info = opts.pop('all_info', False) | |||
r51968 | size_info = opts.pop('size_info', None) | |||
if size_info is None: | ||||
size_info = all_info | ||||
dist_info = opts.pop('dist_info', None) | ||||
if dist_info is None: | ||||
dist_info = all_info | ||||
sparse_info = opts.pop('sparse_info', None) | ||||
if sparse_info is None: | ||||
sparse_info = all_info | ||||
r51966 | ||||
r51963 | revlog = cmdutil.openrevlog( | |||
Matt Harbison
|
r51840 | repo, b'debugdeltachain', file_, pycompat.byteskwargs(opts) | ||
) | ||||
fm = ui.formatter(b'debugdeltachain', pycompat.byteskwargs(opts)) | ||||
Gregory Szorc
|
r30541 | |||
r51966 | lines = revlog_debug.debug_delta_chain( | |||
revlog, | ||||
revs=revs, | ||||
size_info=size_info, | ||||
dist_info=dist_info, | ||||
sparse_info=sparse_info, | ||||
) | ||||
r51963 | # first entry is the header | |||
header = next(lines) | ||||
fm.plain(header) | ||||
for entry in lines: | ||||
label = b' '.join(e[0] for e in entry) | ||||
format = b' '.join(e[1] for e in entry) | ||||
values = [e[3] for e in entry] | ||||
data = dict((e[2], e[3]) for e in entry) | ||||
Gregory Szorc
|
r30541 | fm.startitem() | ||
r51963 | fm.write(label, format, *values, **data) | |||
Augie Fackler
|
r43347 | fm.plain(b'\n') | ||
Gregory Szorc
|
r30541 | fm.end() | ||
Augie Fackler
|
r43346 | |||
@command( | ||||
r50123 | b'debug-delta-find', | |||
r50484 | cmdutil.debugrevlogopts | |||
+ cmdutil.formatteropts | ||||
+ [ | ||||
( | ||||
b'', | ||||
b'source', | ||||
b'full', | ||||
_(b'input data feed to the process (full, storage, p1, p2, prev)'), | ||||
), | ||||
], | ||||
r50123 | _(b'-c|-m|FILE REV'), | |||
optionalrepo=True, | ||||
) | ||||
r50484 | def debugdeltafind(ui, repo, arg_1, arg_2=None, source=b'full', **opts): | |||
r50123 | """display the computation to get to a valid delta for storing REV | |||
This command will replay the process used to find the "best" delta to store | ||||
a revision and display information about all the steps used to get to that | ||||
result. | ||||
r50484 | By default, the process is fed with a the full-text for the revision. This | |||
can be controlled with the --source flag. | ||||
r50123 | The revision use the revision number of the target storage (not changelog | |||
revision number). | ||||
note: the process is initiated from a full text of the revision to store. | ||||
""" | ||||
if arg_2 is None: | ||||
file_ = None | ||||
rev = arg_1 | ||||
else: | ||||
file_ = arg_1 | ||||
rev = arg_2 | ||||
rev = int(rev) | ||||
Matt Harbison
|
r51841 | revlog = cmdutil.openrevlog( | ||
repo, b'debugdeltachain', file_, pycompat.byteskwargs(opts) | ||||
) | ||||
r50123 | p1r, p2r = revlog.parentrevs(rev) | |||
r50571 | ||||
if source == b'full': | ||||
base_rev = nullrev | ||||
elif source == b'storage': | ||||
base_rev = revlog.deltaparent(rev) | ||||
elif source == b'p1': | ||||
base_rev = p1r | ||||
elif source == b'p2': | ||||
base_rev = p2r | ||||
elif source == b'prev': | ||||
base_rev = rev - 1 | ||||
else: | ||||
raise error.InputError(b"invalid --source value: %s" % source) | ||||
revlog_debug.debug_delta_find(ui, revlog, rev, base_rev=base_rev) | ||||
r50123 | ||||
@command( | ||||
Augie Fackler
|
r43347 | b'debugdirstate|debugstate', | ||
Augie Fackler
|
r43346 | [ | ||
Augie Fackler
|
r43347 | ( | ||
b'', | ||||
b'nodates', | ||||
None, | ||||
_(b'do not display the saved mtime (DEPRECATED)'), | ||||
), | ||||
(b'', b'dates', True, _(b'display the saved mtime')), | ||||
(b'', b'datesort', None, _(b'sort by saved mtime')), | ||||
Simon Sapin
|
r48483 | ( | ||
b'', | ||||
Raphaël Gomès
|
r50046 | b'docket', | ||
False, | ||||
_(b'display the docket (metadata file) instead'), | ||||
), | ||||
( | ||||
b'', | ||||
Simon Sapin
|
r48483 | b'all', | ||
False, | ||||
_(b'display dirstate-v2 tree nodes that would not exist in v1'), | ||||
), | ||||
Augie Fackler
|
r43346 | ], | ||
Augie Fackler
|
r43347 | _(b'[OPTION]...'), | ||
Augie Fackler
|
r43346 | ) | ||
Pierre-Yves David
|
r30954 | def debugstate(ui, repo, **opts): | ||
"""show the contents of the current dirstate""" | ||||
Raphaël Gomès
|
r50046 | if opts.get("docket"): | ||
if not repo.dirstate._use_dirstate_v2: | ||||
raise error.Abort(_(b'dirstate v1 does not have a docket')) | ||||
docket = repo.dirstate._map.docket | ||||
( | ||||
start_offset, | ||||
root_nodes, | ||||
nodes_with_entry, | ||||
nodes_with_copy, | ||||
unused_bytes, | ||||
_unused, | ||||
ignore_pattern, | ||||
) = dirstateutils.v2.TREE_METADATA.unpack(docket.tree_metadata) | ||||
ui.write(_(b"size of dirstate data: %d\n") % docket.data_size) | ||||
ui.write(_(b"data file uuid: %s\n") % docket.uuid) | ||||
ui.write(_(b"start offset of root nodes: %d\n") % start_offset) | ||||
ui.write(_(b"number of root nodes: %d\n") % root_nodes) | ||||
ui.write(_(b"nodes with entries: %d\n") % nodes_with_entry) | ||||
ui.write(_(b"nodes with copies: %d\n") % nodes_with_copy) | ||||
ui.write(_(b"number of unused bytes: %d\n") % unused_bytes) | ||||
ui.write( | ||||
_(b"ignore pattern hash: %s\n") % binascii.hexlify(ignore_pattern) | ||||
) | ||||
return | ||||
Augie Fackler
|
r43906 | nodates = not opts['dates'] | ||
if opts.get('nodates') is not None: | ||||
Martin von Zweigbergk
|
r39796 | nodates = True | ||
Augie Fackler
|
r43906 | datesort = opts.get('datesort') | ||
Pierre-Yves David
|
r30954 | |||
if datesort: | ||||
Simon Sapin
|
r48836 | |||
def keyfunc(entry): | ||||
filename, _state, _mode, _size, mtime = entry | ||||
return (mtime, filename) | ||||
Pierre-Yves David
|
r30954 | else: | ||
Augie Fackler
|
r43346 | keyfunc = None # sort by filename | ||
Simon Sapin
|
r48835 | entries = list(repo.dirstate._map.debug_iter(all=opts['all'])) | ||
Simon Sapin
|
r48140 | entries.sort(key=keyfunc) | ||
Simon Sapin
|
r48836 | for entry in entries: | ||
filename, state, mode, size, mtime = entry | ||||
if mtime == -1: | ||||
Augie Fackler
|
r43347 | timestr = b'unset ' | ||
Pierre-Yves David
|
r30954 | elif nodates: | ||
Augie Fackler
|
r43347 | timestr = b'set ' | ||
Pierre-Yves David
|
r30954 | else: | ||
Simon Sapin
|
r48836 | timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(mtime)) | ||
Pulkit Goyal
|
r35205 | timestr = encoding.strtolocal(timestr) | ||
Simon Sapin
|
r48836 | if mode & 0o20000: | ||
Augie Fackler
|
r43347 | mode = b'lnk' | ||
Pierre-Yves David
|
r30954 | else: | ||
Simon Sapin
|
r48836 | mode = b'%3o' % (mode & 0o777 & ~util.umask) | ||
ui.write(b"%c %s %10d %s%s\n" % (state, mode, size, timestr, filename)) | ||||
Pierre-Yves David
|
r30954 | for f in repo.dirstate.copies(): | ||
Augie Fackler
|
r43347 | ui.write(_(b"copy: %s -> %s\n") % (repo.dirstate.copied(f), f)) | ||
Pierre-Yves David
|
r30954 | |||
Augie Fackler
|
r43346 | |||
@command( | ||||
r50078 | b'debugdirstateignorepatternshash', | |||
[], | ||||
_(b''), | ||||
) | ||||
def debugdirstateignorepatternshash(ui, repo, **opts): | ||||
"""show the hash of ignore patterns stored in dirstate if v2, | ||||
or nothing for dirstate-v2 | ||||
""" | ||||
if repo.dirstate._use_dirstate_v2: | ||||
docket = repo.dirstate._map.docket | ||||
hash_len = 20 # 160 bits for SHA-1 | ||||
hash_bytes = docket.tree_metadata[-hash_len:] | ||||
ui.write(binascii.hexlify(hash_bytes) + b'\n') | ||||
@command( | ||||
Augie Fackler
|
r43347 | b'debugdiscovery', | ||
Augie Fackler
|
r43346 | [ | ||
Augie Fackler
|
r43347 | (b'', b'old', None, _(b'use old-style discovery')), | ||
Augie Fackler
|
r43346 | ( | ||
Augie Fackler
|
r43347 | b'', | ||
b'nonheads', | ||||
Augie Fackler
|
r43346 | None, | ||
Augie Fackler
|
r43347 | _(b'use old-style discovery with non-heads included'), | ||
Augie Fackler
|
r43346 | ), | ||
Augie Fackler
|
r43347 | (b'', b'rev', [], b'restrict discovery to this set of revs'), | ||
(b'', b'seed', b'12323', b'specify the random seed use for discovery'), | ||||
r47205 | ( | |||
b'', | ||||
b'local-as-revs', | ||||
r47504 | b"", | |||
b'treat local has having these revisions only', | ||||
r47205 | ), | |||
( | ||||
b'', | ||||
b'remote-as-revs', | ||||
r47504 | b"", | |||
r49810 | b'use local as remote, with only these revisions', | |||
r47205 | ), | |||
Augie Fackler
|
r43346 | ] | ||
r47502 | + cmdutil.remoteopts | |||
+ cmdutil.formatteropts, | ||||
Augie Fackler
|
r43347 | _(b'[--rev REV] [OTHER]'), | ||
Augie Fackler
|
r43346 | ) | ||
Augie Fackler
|
r43347 | def debugdiscovery(ui, repo, remoteurl=b"default", **opts): | ||
r47205 | """runs the changeset discovery protocol in isolation | |||
The local peer can be "replaced" by a subset of the local repository by | ||||
r50301 | using the `--local-as-revs` flag. In the same way, the usual `remote` peer | |||
can be "replaced" by a subset of the local repository using the | ||||
r50294 | `--remote-as-revs` flag. This is useful to efficiently debug pathological | |||
r50301 | discovery situations. | |||
r47560 | ||||
The following developer oriented config are relevant for people playing with this command: | ||||
* devel.discovery.exchange-heads=True | ||||
If False, the discovery will not start with | ||||
remote head fetching and local head querying. | ||||
* devel.discovery.grow-sample=True | ||||
If False, the sample size used in set discovery will not be increased | ||||
through the process | ||||
r47562 | * devel.discovery.grow-sample.dynamic=True | |||
When discovery.grow-sample.dynamic is True, the default, the sample size is | ||||
adapted to the shape of the undecided set (it is set to the max of: | ||||
<target-size>, len(roots(undecided)), len(heads(undecided) | ||||
r47560 | * devel.discovery.grow-sample.rate=1.05 | |||
the rate at which the sample grow | ||||
* devel.discovery.randomize=True | ||||
If andom sampling during discovery are deterministic. It is meant for | ||||
integration tests. | ||||
* devel.discovery.sample-size=200 | ||||
Control the initial size of the discovery sample | ||||
* devel.discovery.sample-size.initial=100 | ||||
Control the initial size of the discovery for initial change | ||||
r47205 | """ | |||
unfi = repo.unfiltered() | ||||
# setup potential extra filtering | ||||
Matt Harbison
|
r51842 | local_revs = opts["local_as_revs"] | ||
remote_revs = opts["remote_as_revs"] | ||||
Gregory Szorc
|
r30517 | |||
# make sure tests are repeatable | ||||
Matt Harbison
|
r51842 | random.seed(int(opts['seed'])) | ||
Augie Fackler
|
r43347 | |||
r47205 | if not remote_revs: | |||
r50621 | path = urlutil.get_unique_pull_path_obj( | |||
b'debugdiscovery', ui, remoteurl | ||||
r47721 | ) | |||
r50621 | branches = (path.branch, []) | |||
Matt Harbison
|
r51842 | remote = hg.peer(repo, pycompat.byteskwargs(opts), path) | ||
r50621 | ui.status(_(b'comparing with %s\n') % urlutil.hidepassword(path.loc)) | |||
r47205 | else: | |||
branches = (None, []) | ||||
Martin von Zweigbergk
|
r48928 | remote_filtered_revs = logcmdutil.revrange( | ||
r47205 | unfi, [b"not (::(%s))" % remote_revs] | |||
) | ||||
remote_filtered_revs = frozenset(remote_filtered_revs) | ||||
def remote_func(x): | ||||
return remote_filtered_revs | ||||
repoview.filtertable[b'debug-discovery-remote-filter'] = remote_func | ||||
remote = repo.peer() | ||||
remote._repo = remote._repo.filtered(b'debug-discovery-remote-filter') | ||||
if local_revs: | ||||
Martin von Zweigbergk
|
r48928 | local_filtered_revs = logcmdutil.revrange( | ||
r47205 | unfi, [b"not (::(%s))" % local_revs] | |||
) | ||||
local_filtered_revs = frozenset(local_filtered_revs) | ||||
def local_func(x): | ||||
return local_filtered_revs | ||||
repoview.filtertable[b'debug-discovery-local-filter'] = local_func | ||||
repo = repo.filtered(b'debug-discovery-local-filter') | ||||
r46726 | data = {} | |||
Matt Harbison
|
r51842 | if opts.get('old'): | ||
Augie Fackler
|
r43346 | |||
r42198 | def doit(pushedrevs, remoteheads, remote=remote): | |||
r51821 | if not hasattr(remote, 'branches'): | |||
Gregory Szorc
|
r30517 | # enable in-client legacy support | ||
remote = localrepo.locallegacypeer(remote.local()) | ||||
r50295 | if remote_revs: | |||
r = remote._repo.filtered(b'debug-discovery-remote-filter') | ||||
remote._repo = r | ||||
Augie Fackler
|
r43346 | common, _in, hds = treediscovery.findcommonincoming( | ||
r46726 | repo, remote, force=True, audit=data | |||
Augie Fackler
|
r43346 | ) | ||
Gregory Szorc
|
r30517 | common = set(common) | ||
Matt Harbison
|
r51842 | if not opts.get('nonheads'): | ||
Augie Fackler
|
r43350 | ui.writenoi18n( | ||
Augie Fackler
|
r43347 | b"unpruned common: %s\n" | ||
% b" ".join(sorted(short(n) for n in common)) | ||||
Augie Fackler
|
r43346 | ) | ||
Gregory Szorc
|
r39199 | |||
clnode = repo.changelog.node | ||||
Augie Fackler
|
r43347 | common = repo.revs(b'heads(::%ln)', common) | ||
Gregory Szorc
|
r39199 | common = {clnode(r) for r in common} | ||
r42198 | return common, hds | |||
Augie Fackler
|
r43346 | |||
r42198 | else: | |||
Augie Fackler
|
r43346 | |||
r42198 | def doit(pushedrevs, remoteheads, remote=remote): | |||
Boris Feld
|
r35305 | nodes = None | ||
if pushedrevs: | ||||
Martin von Zweigbergk
|
r48928 | revs = logcmdutil.revrange(repo, pushedrevs) | ||
Boris Feld
|
r35305 | nodes = [repo[r].node() for r in revs] | ||
Augie Fackler
|
r43346 | common, any, hds = setdiscovery.findcommonheads( | ||
r50298 | ui, | |||
repo, | ||||
remote, | ||||
ancestorsof=nodes, | ||||
audit=data, | ||||
abortwhenunrelated=False, | ||||
Augie Fackler
|
r43346 | ) | ||
r42198 | return common, hds | |||
Gregory Szorc
|
r30517 | |||
Martin von Zweigbergk
|
r35420 | remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches, revs=None) | ||
Matt Harbison
|
r51842 | localrevs = opts['rev'] | ||
fm = ui.formatter(b'debugdiscovery', pycompat.byteskwargs(opts)) | ||||
r47503 | if fm.strict_format: | |||
@contextlib.contextmanager | ||||
def may_capture_output(): | ||||
ui.pushbuffer() | ||||
yield | ||||
data[b'output'] = ui.popbuffer() | ||||
else: | ||||
may_capture_output = util.nullcontextmanager | ||||
with may_capture_output(): | ||||
with util.timedcm('debug-discovery') as t: | ||||
common, hds = doit(localrevs, remoterevs) | ||||
r42198 | ||||
r42199 | # compute all statistics | |||
r50299 | if len(common) == 1 and repo.nullid in common: | |||
common = set() | ||||
r46678 | heads_common = set(common) | |||
heads_remote = set(hds) | ||||
heads_local = set(repo.heads()) | ||||
# note: they cannot be a local or remote head that is in common and not | ||||
# itself a head of common. | ||||
heads_common_local = heads_common & heads_local | ||||
heads_common_remote = heads_common & heads_remote | ||||
heads_common_both = heads_common & heads_remote & heads_local | ||||
all = repo.revs(b'all()') | ||||
common = repo.revs(b'::%ln', common) | ||||
r46692 | roots_common = repo.revs(b'roots(::%ld)', common) | |||
r46678 | missing = repo.revs(b'not ::%ld', common) | |||
r46692 | heads_missing = repo.revs(b'heads(%ld)', missing) | |||
roots_missing = repo.revs(b'roots(%ld)', missing) | ||||
r46678 | assert len(common) + len(missing) == len(all) | |||
r42199 | ||||
r46693 | initial_undecided = repo.revs( | |||
b'not (::%ln or %ln::)', heads_common_remote, heads_common_local | ||||
) | ||||
heads_initial_undecided = repo.revs(b'heads(%ld)', initial_undecided) | ||||
roots_initial_undecided = repo.revs(b'roots(%ld)', initial_undecided) | ||||
common_initial_undecided = initial_undecided & common | ||||
missing_initial_undecided = initial_undecided & missing | ||||
Augie Fackler
|
r43347 | data[b'elapsed'] = t.elapsed | ||
r46678 | data[b'nb-common-heads'] = len(heads_common) | |||
data[b'nb-common-heads-local'] = len(heads_common_local) | ||||
data[b'nb-common-heads-remote'] = len(heads_common_remote) | ||||
data[b'nb-common-heads-both'] = len(heads_common_both) | ||||
r46692 | data[b'nb-common-roots'] = len(roots_common) | |||
r46678 | data[b'nb-head-local'] = len(heads_local) | |||
data[b'nb-head-local-missing'] = len(heads_local) - len(heads_common_local) | ||||
data[b'nb-head-remote'] = len(heads_remote) | ||||
data[b'nb-head-remote-unknown'] = len(heads_remote) - len( | ||||
heads_common_remote | ||||
r46677 | ) | |||
r46678 | data[b'nb-revs'] = len(all) | |||
data[b'nb-revs-common'] = len(common) | ||||
data[b'nb-revs-missing'] = len(missing) | ||||
r46727 | data[b'nb-missing-heads'] = len(heads_missing) | |||
data[b'nb-missing-roots'] = len(roots_missing) | ||||
r46693 | data[b'nb-ini_und'] = len(initial_undecided) | |||
data[b'nb-ini_und-heads'] = len(heads_initial_undecided) | ||||
data[b'nb-ini_und-roots'] = len(roots_initial_undecided) | ||||
data[b'nb-ini_und-common'] = len(common_initial_undecided) | ||||
data[b'nb-ini_und-missing'] = len(missing_initial_undecided) | ||||
r42199 | ||||
r47502 | fm.startitem() | |||
fm.data(**pycompat.strkwargs(data)) | ||||
r42199 | # display discovery summary | |||
r47502 | fm.plain(b"elapsed time: %(elapsed)f seconds\n" % data) | |||
fm.plain(b"round-trips: %(total-roundtrips)9d\n" % data) | ||||
r50297 | if b'total-round-trips-heads' in data: | |||
fm.plain( | ||||
b" round-trips-heads: %(total-round-trips-heads)9d\n" % data | ||||
) | ||||
if b'total-round-trips-branches' in data: | ||||
fm.plain( | ||||
b" round-trips-branches: %(total-round-trips-branches)9d\n" | ||||
% data | ||||
) | ||||
if b'total-round-trips-between' in data: | ||||
fm.plain( | ||||
b" round-trips-between: %(total-round-trips-between)9d\n" % data | ||||
) | ||||
r49881 | fm.plain(b"queries: %(total-queries)9d\n" % data) | |||
r50296 | if b'total-queries-branches' in data: | |||
fm.plain(b" queries-branches: %(total-queries-branches)9d\n" % data) | ||||
if b'total-queries-between' in data: | ||||
fm.plain(b" queries-between: %(total-queries-between)9d\n" % data) | ||||
r47502 | fm.plain(b"heads summary:\n") | |||
fm.plain(b" total common heads: %(nb-common-heads)9d\n" % data) | ||||
fm.plain(b" also local heads: %(nb-common-heads-local)9d\n" % data) | ||||
fm.plain(b" also remote heads: %(nb-common-heads-remote)9d\n" % data) | ||||
fm.plain(b" both: %(nb-common-heads-both)9d\n" % data) | ||||
fm.plain(b" local heads: %(nb-head-local)9d\n" % data) | ||||
fm.plain(b" common: %(nb-common-heads-local)9d\n" % data) | ||||
fm.plain(b" missing: %(nb-head-local-missing)9d\n" % data) | ||||
fm.plain(b" remote heads: %(nb-head-remote)9d\n" % data) | ||||
fm.plain(b" common: %(nb-common-heads-remote)9d\n" % data) | ||||
fm.plain(b" unknown: %(nb-head-remote-unknown)9d\n" % data) | ||||
fm.plain(b"local changesets: %(nb-revs)9d\n" % data) | ||||
fm.plain(b" common: %(nb-revs-common)9d\n" % data) | ||||
fm.plain(b" heads: %(nb-common-heads)9d\n" % data) | ||||
fm.plain(b" roots: %(nb-common-roots)9d\n" % data) | ||||
fm.plain(b" missing: %(nb-revs-missing)9d\n" % data) | ||||
fm.plain(b" heads: %(nb-missing-heads)9d\n" % data) | ||||
fm.plain(b" roots: %(nb-missing-roots)9d\n" % data) | ||||
fm.plain(b" first undecided set: %(nb-ini_und)9d\n" % data) | ||||
fm.plain(b" heads: %(nb-ini_und-heads)9d\n" % data) | ||||
fm.plain(b" roots: %(nb-ini_und-roots)9d\n" % data) | ||||
fm.plain(b" common: %(nb-ini_und-common)9d\n" % data) | ||||
fm.plain(b" missing: %(nb-ini_und-missing)9d\n" % data) | ||||
r42199 | ||||
r42201 | if ui.verbose: | |||
r47502 | fm.plain( | |||
r46678 | b"common heads: %s\n" | |||
% b" ".join(sorted(short(n) for n in heads_common)) | ||||
Augie Fackler
|
r43346 | ) | ||
r47502 | fm.end() | |||
Augie Fackler
|
r43346 | |||
Gregory Szorc
|
r30518 | |||
Boris Feld
|
r35578 | _chunksize = 4 << 10 | ||
Augie Fackler
|
r43346 | |||
Augie Fackler
|
r43347 | @command( | ||
Augie Fackler
|
r46554 | b'debugdownload', | ||
[ | ||||
(b'o', b'output', b'', _(b'path')), | ||||
], | ||||
optionalrepo=True, | ||||
Augie Fackler
|
r43347 | ) | ||
Boris Feld
|
r35748 | def debugdownload(ui, repo, url, output=None, **opts): | ||
Augie Fackler
|
r46554 | """download a resource using Mercurial logic and config""" | ||
Boris Feld
|
r35578 | fh = urlmod.open(ui, url, output) | ||
dest = ui | ||||
if output: | ||||
Augie Fackler
|
r43347 | dest = open(output, b"wb", _chunksize) | ||
Boris Feld
|
r35578 | try: | ||
data = fh.read(_chunksize) | ||||
while data: | ||||
dest.write(data) | ||||
data = fh.read(_chunksize) | ||||
finally: | ||||
if output: | ||||
dest.close() | ||||
Augie Fackler
|
r43346 | |||
Augie Fackler
|
r43347 | @command(b'debugextensions', cmdutil.formatteropts, [], optionalrepo=True) | ||
Matt Harbison
|
r37998 | def debugextensions(ui, repo, **opts): | ||
Gregory Szorc
|
r30518 | '''show information about active extensions''' | ||
exts = extensions.extensions(ui) | ||||
hgver = util.version() | ||||
Matt Harbison
|
r51843 | fm = ui.formatter(b'debugextensions', pycompat.byteskwargs(opts)) | ||
Gregory Szorc
|
r30518 | for extname, extmod in sorted(exts, key=operator.itemgetter(0)): | ||
isinternal = extensions.ismoduleinternal(extmod) | ||||
Matt Harbison
|
r44084 | extsource = None | ||
r51821 | if hasattr(extmod, '__file__'): | |||
Matt Harbison
|
r44084 | extsource = pycompat.fsencode(extmod.__file__) | ||
elif getattr(sys, 'oxidized', False): | ||||
extsource = pycompat.sysexecutable | ||||
Gregory Szorc
|
r30518 | if isinternal: | ||
exttestedwith = [] # never expose magic string to users | ||||
else: | ||||
Augie Fackler
|
r43347 | exttestedwith = getattr(extmod, 'testedwith', b'').split() | ||
Gregory Szorc
|
r30518 | extbuglink = getattr(extmod, 'buglink', None) | ||
fm.startitem() | ||||
if ui.quiet or ui.verbose: | ||||
Augie Fackler
|
r43347 | fm.write(b'name', b'%s\n', extname) | ||
Gregory Szorc
|
r30518 | else: | ||
Augie Fackler
|
r43347 | fm.write(b'name', b'%s', extname) | ||
Gregory Szorc
|
r30518 | if isinternal or hgver in exttestedwith: | ||
Augie Fackler
|
r43347 | fm.plain(b'\n') | ||
Gregory Szorc
|
r30518 | elif not exttestedwith: | ||
Augie Fackler
|
r43347 | fm.plain(_(b' (untested!)\n')) | ||
Gregory Szorc
|
r30518 | else: | ||
lasttestedversion = exttestedwith[-1] | ||||
Augie Fackler
|
r43347 | fm.plain(b' (%s!)\n' % lasttestedversion) | ||
Gregory Szorc
|
r30518 | |||
Augie Fackler
|
r43346 | fm.condwrite( | ||
ui.verbose and extsource, | ||||
Augie Fackler
|
r43347 | b'source', | ||
_(b' location: %s\n'), | ||||
extsource or b"", | ||||
Augie Fackler
|
r43346 | ) | ||
Gregory Szorc
|
r30518 | |||
if ui.verbose: | ||||
Augie Fackler
|
r43347 | fm.plain(_(b' bundled: %s\n') % [b'no', b'yes'][isinternal]) | ||
Gregory Szorc
|
r30518 | fm.data(bundled=isinternal) | ||
Augie Fackler
|
r43346 | fm.condwrite( | ||
ui.verbose and exttestedwith, | ||||
Augie Fackler
|
r43347 | b'testedwith', | ||
_(b' tested with: %s\n'), | ||||
fm.formatlist(exttestedwith, name=b'ver'), | ||||
Augie Fackler
|
r43346 | ) | ||
fm.condwrite( | ||||
ui.verbose and extbuglink, | ||||
Augie Fackler
|
r43347 | b'buglink', | ||
_(b' bug reporting: %s\n'), | ||||
extbuglink or b"", | ||||
Augie Fackler
|
r43346 | ) | ||
Gregory Szorc
|
r30518 | |||
fm.end() | ||||
Gregory Szorc
|
r30524 | |||
Augie Fackler
|
r43346 | |||
@command( | ||||
Augie Fackler
|
r43347 | b'debugfileset', | ||
Augie Fackler
|
r43346 | [ | ||
( | ||||
Augie Fackler
|
r43347 | b'r', | ||
b'rev', | ||||
b'', | ||||
_(b'apply the filespec on this revision'), | ||||
_(b'REV'), | ||||
Augie Fackler
|
r43346 | ), | ||
( | ||||
Augie Fackler
|
r43347 | b'', | ||
b'all-files', | ||||
False, | ||||
_(b'test files from all revisions and working directory'), | ||||
Augie Fackler
|
r43346 | ), | ||
( | ||||
Augie Fackler
|
r43347 | b's', | ||
b'show-matcher', | ||||
None, | ||||
_(b'print internal representation of matcher'), | ||||
), | ||||
( | ||||
b'p', | ||||
b'show-stage', | ||||
Augie Fackler
|
r43346 | [], | ||
Augie Fackler
|
r43347 | _(b'print parsed tree at the given stage'), | ||
_(b'NAME'), | ||||
Augie Fackler
|
r43346 | ), | ||
], | ||||
Augie Fackler
|
r43347 | _(b'[-r REV] [--all-files] [OPTION]... FILESPEC'), | ||
Augie Fackler
|
r43346 | ) | ||
Gregory Szorc
|
r30524 | def debugfileset(ui, repo, expr, **opts): | ||
'''parse and apply a fileset specification''' | ||||
Augie Fackler
|
r39008 | from . import fileset | ||
Augie Fackler
|
r43346 | |||
fileset.symbols # force import of fileset so we have predicates to optimize | ||||
Matt Harbison
|
r51844 | |||
ctx = logcmdutil.revsingle(repo, opts.get('rev'), None) | ||||
Yuya Nishihara
|
r38837 | |||
stages = [ | ||||
Augie Fackler
|
r43347 | (b'parsed', pycompat.identity), | ||
(b'analyzed', filesetlang.analyze), | ||||
(b'optimized', filesetlang.optimize), | ||||
Yuya Nishihara
|
r38837 | ] | ||
Augie Fackler
|
r44937 | stagenames = {n for n, f in stages} | ||
Yuya Nishihara
|
r38837 | |||
showalways = set() | ||||
Matt Harbison
|
r51844 | if ui.verbose and not opts['show_stage']: | ||
Yuya Nishihara
|
r38837 | # show parsed tree by --verbose (deprecated) | ||
Augie Fackler
|
r43347 | showalways.add(b'parsed') | ||
Matt Harbison
|
r51844 | if opts['show_stage'] == [b'all']: | ||
Yuya Nishihara
|
r38837 | showalways.update(stagenames) | ||
else: | ||||
Matt Harbison
|
r51844 | for n in opts['show_stage']: | ||
Yuya Nishihara
|
r38837 | if n not in stagenames: | ||
Augie Fackler
|
r43347 | raise error.Abort(_(b'invalid stage name: %s') % n) | ||
Matt Harbison
|
r51844 | showalways.update(opts['show_stage']) | ||
Yuya Nishihara
|
r38837 | |||
Yuya Nishihara
|
r38841 | tree = filesetlang.parse(expr) | ||
Yuya Nishihara
|
r38837 | for n, f in stages: | ||
tree = f(tree) | ||||
if n in showalways: | ||||
Matt Harbison
|
r51844 | if opts['show_stage'] or n != b'parsed': | ||
Augie Fackler
|
r43347 | ui.write(b"* %s:\n" % n) | ||
ui.write(filesetlang.prettyformat(tree), b"\n") | ||||
Gregory Szorc
|
r30524 | |||
Yuya Nishihara
|
r38629 | files = set() | ||
Matt Harbison
|
r51844 | if opts['all_files']: | ||
Yuya Nishihara
|
r38629 | for r in repo: | ||
c = repo[r] | ||||
files.update(c.files()) | ||||
files.update(c.substate) | ||||
Matt Harbison
|
r51844 | if opts['all_files'] or ctx.rev() is None: | ||
Yuya Nishihara
|
r38629 | wctx = repo[None] | ||
Augie Fackler
|
r43346 | files.update( | ||
repo.dirstate.walk( | ||||
scmutil.matchall(repo), | ||||
subrepos=list(wctx.substate), | ||||
unknown=True, | ||||
ignored=True, | ||||
) | ||||
) | ||||
Yuya Nishihara
|
r38629 | files.update(wctx.substate) | ||
else: | ||||
files.update(ctx.files()) | ||||
files.update(ctx.substate) | ||||
Matt Harbison
|
r44461 | m = ctx.matchfileset(repo.getcwd(), expr) | ||
Matt Harbison
|
r51844 | if opts['show_matcher'] or (opts['show_matcher'] is None and ui.verbose): | ||
Augie Fackler
|
r43350 | ui.writenoi18n(b'* matcher:\n', stringutil.prettyrepr(m), b'\n') | ||
Yuya Nishihara
|
r38629 | for f in sorted(files): | ||
if not m(f): | ||||
continue | ||||
Augie Fackler
|
r43347 | ui.write(b"%s\n" % f) | ||
Raphaël Gomès
|
r48623 | @command( | ||
b"debug-repair-issue6528", | ||||
[ | ||||
( | ||||
b'', | ||||
b'to-report', | ||||
b'', | ||||
_(b'build a report of affected revisions to this file'), | ||||
_(b'FILE'), | ||||
), | ||||
( | ||||
b'', | ||||
b'from-report', | ||||
b'', | ||||
_(b'repair revisions listed in this report file'), | ||||
_(b'FILE'), | ||||
), | ||||
Raphaël Gomès
|
r48625 | ( | ||
b'', | ||||
b'paranoid', | ||||
False, | ||||
_(b'check that both detection methods do the same thing'), | ||||
), | ||||
Raphaël Gomès
|
r48623 | ] | ||
+ cmdutil.dryrunopts, | ||||
) | ||||
def debug_repair_issue6528(ui, repo, **opts): | ||||
"""find affected revisions and repair them. See issue6528 for more details. | ||||
The `--to-report` and `--from-report` flags allow you to cache and reuse the | ||||
computation of affected revisions for a given repository across clones. | ||||
The report format is line-based (with empty lines ignored): | ||||
``` | ||||
<ascii-hex of the affected revision>,... <unencoded filelog index filename> | ||||
``` | ||||
There can be multiple broken revisions per filelog, they are separated by | ||||
a comma with no spaces. The only space is between the revision(s) and the | ||||
filename. | ||||
Note that this does *not* mean that this repairs future affected revisions, | ||||
Raphaël Gomès
|
r49001 | that needs a separate fix at the exchange level that was introduced in | ||
Mercurial 5.9.1. | ||||
Raphaël Gomès
|
r48625 | |||
There is a `--paranoid` flag to test that the fast implementation is correct | ||||
by checking it against the slow implementation. Since this matter is quite | ||||
urgent and testing every edge-case is probably quite costly, we use this | ||||
method to test on large repositories as a fuzzing method of sorts. | ||||
Raphaël Gomès
|
r48623 | """ | ||
cmdutil.check_incompatible_arguments( | ||||
opts, 'to_report', ['from_report', 'dry_run'] | ||||
) | ||||
dry_run = opts.get('dry_run') | ||||
to_report = opts.get('to_report') | ||||
from_report = opts.get('from_report') | ||||
Raphaël Gomès
|
r48625 | paranoid = opts.get('paranoid') | ||
Raphaël Gomès
|
r48623 | # TODO maybe add filelog pattern and revision pattern parameters to help | ||
# narrow down the search for users that know what they're looking for? | ||||
if requirements.REVLOGV1_REQUIREMENT not in repo.requirements: | ||||
msg = b"can only repair revlogv1 repositories, v2 is not affected" | ||||
raise error.Abort(_(msg)) | ||||
rewrite.repair_issue6528( | ||||
Raphaël Gomès
|
r48625 | ui, | ||
repo, | ||||
dry_run=dry_run, | ||||
to_report=to_report, | ||||
from_report=from_report, | ||||
paranoid=paranoid, | ||||
Raphaël Gomès
|
r48623 | ) | ||
Augie Fackler
|
r43347 | @command(b'debugformat', [] + cmdutil.formatteropts) | ||
Boris Feld
|
r35337 | def debugformat(ui, repo, **opts): | ||
Boris Feld
|
r35338 | """display format information about the current repository | ||
Use --verbose to get extra information about current config value and | ||||
Mercurial default.""" | ||||
Boris Feld
|
r35337 | maxvariantlength = max(len(fv.name) for fv in upgrade.allformatvariant) | ||
Augie Fackler
|
r43347 | maxvariantlength = max(len(b'format-variant'), maxvariantlength) | ||
Boris Feld
|
r35337 | |||
def makeformatname(name): | ||||
Augie Fackler
|
r43347 | return b'%s:' + (b' ' * (maxvariantlength - len(name))) | ||
Matt Harbison
|
r51845 | fm = ui.formatter(b'debugformat', pycompat.byteskwargs(opts)) | ||
Yuya Nishihara
|
r35379 | if fm.isplain(): | ||
Augie Fackler
|
r43346 | |||
Yuya Nishihara
|
r35379 | def formatvalue(value): | ||
r51821 | if hasattr(value, 'startswith'): | |||
Yuya Nishihara
|
r35379 | return value | ||
if value: | ||||
Augie Fackler
|
r43347 | return b'yes' | ||
Yuya Nishihara
|
r35379 | else: | ||
Augie Fackler
|
r43347 | return b'no' | ||
Augie Fackler
|
r43346 | |||
Yuya Nishihara
|
r35379 | else: | ||
formatvalue = pycompat.identity | ||||
Boris Feld
|
r35337 | |||
Augie Fackler
|
r43347 | fm.plain(b'format-variant') | ||
fm.plain(b' ' * (maxvariantlength - len(b'format-variant'))) | ||||
fm.plain(b' repo') | ||||
Boris Feld
|
r35338 | if ui.verbose: | ||
Augie Fackler
|
r43347 | fm.plain(b' config default') | ||
fm.plain(b'\n') | ||||
Boris Feld
|
r35337 | for fv in upgrade.allformatvariant: | ||
Yuya Nishihara
|
r35378 | fm.startitem() | ||
Boris Feld
|
r35337 | repovalue = fv.fromrepo(repo) | ||
Boris Feld
|
r35338 | configvalue = fv.fromconfig(repo) | ||
Boris Feld
|
r35337 | |||
Boris Feld
|
r35339 | if repovalue != configvalue: | ||
Augie Fackler
|
r43347 | namelabel = b'formatvariant.name.mismatchconfig' | ||
repolabel = b'formatvariant.repo.mismatchconfig' | ||||
Boris Feld
|
r35339 | elif repovalue != fv.default: | ||
Augie Fackler
|
r43347 | namelabel = b'formatvariant.name.mismatchdefault' | ||
repolabel = b'formatvariant.repo.mismatchdefault' | ||||
Boris Feld
|
r35339 | else: | ||
Augie Fackler
|
r43347 | namelabel = b'formatvariant.name.uptodate' | ||
repolabel = b'formatvariant.repo.uptodate' | ||||
fm.write(b'name', makeformatname(fv.name), fv.name, label=namelabel) | ||||
fm.write(b'repo', b' %3s', formatvalue(repovalue), label=repolabel) | ||||
Boris Feld
|
r35339 | if fv.default != configvalue: | ||
Augie Fackler
|
r43347 | configlabel = b'formatvariant.config.special' | ||
Boris Feld
|
r35339 | else: | ||
Augie Fackler
|
r43347 | configlabel = b'formatvariant.config.default' | ||
Augie Fackler
|
r43346 | fm.condwrite( | ||
ui.verbose, | ||||
Augie Fackler
|
r43347 | b'config', | ||
b' %6s', | ||||
Augie Fackler
|
r43346 | formatvalue(configvalue), | ||
label=configlabel, | ||||
) | ||||
fm.condwrite( | ||||
ui.verbose, | ||||
Augie Fackler
|
r43347 | b'default', | ||
b' %7s', | ||||
Augie Fackler
|
r43346 | formatvalue(fv.default), | ||
Augie Fackler
|
r43347 | label=b'formatvariant.default', | ||
Augie Fackler
|
r43346 | ) | ||
Augie Fackler
|
r43347 | fm.plain(b'\n') | ||
Yuya Nishihara
|
r35378 | fm.end() | ||
Boris Feld
|
r35337 | |||
Augie Fackler
|
r43346 | |||
Augie Fackler
|
r43347 | @command(b'debugfsinfo', [], _(b'[PATH]'), norepo=True) | ||
def debugfsinfo(ui, path=b"."): | ||||
Gregory Szorc
|
r30525 | """show information detected about current filesystem""" | ||
Augie Fackler
|
r43350 | ui.writenoi18n(b'path: %s\n' % path) | ||
r43364 | ui.writenoi18n( | |||
b'mounted on: %s\n' % (util.getfsmountpoint(path) or b'(unknown)') | ||||
) | ||||
Augie Fackler
|
r43350 | ui.writenoi18n(b'exec: %s\n' % (util.checkexec(path) and b'yes' or b'no')) | ||
ui.writenoi18n(b'fstype: %s\n' % (util.getfstype(path) or b'(unknown)')) | ||||
r43364 | ui.writenoi18n( | |||
b'symlink: %s\n' % (util.checklink(path) and b'yes' or b'no') | ||||
) | ||||
ui.writenoi18n( | ||||
b'hardlink: %s\n' % (util.checknlink(path) and b'yes' or b'no') | ||||
) | ||||
Augie Fackler
|
r43347 | casesensitive = b'(unknown)' | ||
Jun Wu
|
r31634 | try: | ||
Augie Fackler
|
r43347 | with pycompat.namedtempfile(prefix=b'.debugfsinfo', dir=path) as f: | ||
casesensitive = util.fscasesensitive(f.name) and b'yes' or b'no' | ||||
Jun Wu
|
r31634 | except OSError: | ||
pass | ||||
Augie Fackler
|
r43350 | ui.writenoi18n(b'case-sensitive: %s\n' % casesensitive) | ||
Augie Fackler
|
r43346 | |||
@command( | ||||
Augie Fackler
|
r43347 | b'debuggetbundle', | ||
Augie Fackler
|
r43346 | [ | ||
Augie Fackler
|
r43347 | (b'H', b'head', [], _(b'id of head node'), _(b'ID')), | ||
(b'C', b'common', [], _(b'id of common node'), _(b'ID')), | ||||
( | ||||
b't', | ||||
b'type', | ||||
b'bzip2', | ||||
_(b'bundle compression type to use'), | ||||
_(b'TYPE'), | ||||
), | ||||
Augie Fackler
|
r43346 | ], | ||
Augie Fackler
|
r43347 | _(b'REPO FILE [-H|-C ID]...'), | ||
Augie Fackler
|
r43346 | norepo=True, | ||
) | ||||
Gregory Szorc
|
r30526 | def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts): | ||
"""retrieves a bundle from a repo | ||||
Every ID must be a full-length hex node id string. Saves the bundle to the | ||||
given file. | ||||
""" | ||||
Matt Harbison
|
r51846 | repo = hg.peer(ui, pycompat.byteskwargs(opts), repopath) | ||
Augie Fackler
|
r43347 | if not repo.capable(b'getbundle'): | ||
raise error.Abort(b"getbundle() not supported by target repository") | ||||
Gregory Szorc
|
r30526 | args = {} | ||
if common: | ||||
Augie Fackler
|
r43906 | args['common'] = [bin(s) for s in common] | ||
Gregory Szorc
|
r30526 | if head: | ||
Augie Fackler
|
r43906 | args['heads'] = [bin(s) for s in head] | ||
Gregory Szorc
|
r30526 | # TODO: get desired bundlecaps from command line. | ||
Augie Fackler
|
r43906 | args['bundlecaps'] = None | ||
Augie Fackler
|
r43347 | bundle = repo.getbundle(b'debug', **args) | ||
Matt Harbison
|
r51846 | bundletype = opts.get('type', b'bzip2').lower() | ||
Augie Fackler
|
r43346 | btypes = { | ||
Augie Fackler
|
r43347 | b'none': b'HG10UN', | ||
b'bzip2': b'HG10BZ', | ||||
b'gzip': b'HG10GZ', | ||||
b'bundle2': b'HG20', | ||||
Augie Fackler
|
r43346 | } | ||
Gregory Szorc
|
r30526 | bundletype = btypes.get(bundletype) | ||
if bundletype not in bundle2.bundletypes: | ||||
Augie Fackler
|
r43347 | raise error.Abort(_(b'unknown bundle type specified with --type')) | ||
Gregory Szorc
|
r30526 | bundle2.writebundle(ui, bundle, bundlepath, bundletype) | ||
Gregory Szorc
|
r30527 | |||
Augie Fackler
|
r43346 | |||
r51441 | @command(b'debugignore', [], b'[FILE]...') | |||
Gregory Szorc
|
r30527 | def debugignore(ui, repo, *files, **opts): | ||
"""display the combined ignore pattern and information about ignored files | ||||
With no argument display the combined ignore pattern. | ||||
Given space separated file names, shows if the given file is ignored and | ||||
if so, show the ignore rule (file and line number) that matched it. | ||||
""" | ||||
ignore = repo.dirstate._ignore | ||||
if not files: | ||||
# Show all the patterns | ||||
Augie Fackler
|
r43347 | ui.write(b"%s\n" % pycompat.byterepr(ignore)) | ||
Gregory Szorc
|
r30527 | else: | ||
Matt Harbison
|
r33507 | m = scmutil.match(repo[None], pats=files) | ||
Martin von Zweigbergk
|
r41785 | uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True) | ||
Matt Harbison
|
r33507 | for f in m.files(): | ||
Gregory Szorc
|
r30527 | nf = util.normpath(f) | ||
ignored = None | ||||
ignoredata = None | ||||
Augie Fackler
|
r43347 | if nf != b'.': | ||
Gregory Szorc
|
r30527 | if ignore(nf): | ||
ignored = nf | ||||
ignoredata = repo.dirstate._ignorefileandline(nf) | ||||
else: | ||||
Martin von Zweigbergk
|
r44032 | for p in pathutil.finddirs(nf): | ||
Gregory Szorc
|
r30527 | if ignore(p): | ||
ignored = p | ||||
ignoredata = repo.dirstate._ignorefileandline(p) | ||||
break | ||||
if ignored: | ||||
if ignored == nf: | ||||
Augie Fackler
|
r43347 | ui.write(_(b"%s is ignored\n") % uipathfn(f)) | ||
Gregory Szorc
|
r30527 | else: | ||
Augie Fackler
|
r43346 | ui.write( | ||
_( | ||||
Augie Fackler
|
r43347 | b"%s is ignored because of " | ||
b"containing directory %s\n" | ||||
Augie Fackler
|
r43346 | ) | ||
% (uipathfn(f), ignored) | ||||
) | ||||
Gregory Szorc
|
r30527 | ignorefile, lineno, line = ignoredata | ||
Augie Fackler
|
r43346 | ui.write( | ||
Augie Fackler
|
r43347 | _(b"(ignore rule in %s, line %d: '%s')\n") | ||
Augie Fackler
|
r43346 | % (ignorefile, lineno, line) | ||
) | ||||
Gregory Szorc
|
r30527 | else: | ||
Augie Fackler
|
r43347 | ui.write(_(b"%s is not ignored\n") % uipathfn(f)) | ||
Gregory Szorc
|
r30528 | |||
Augie Fackler
|
r43346 | |||
@command( | ||||
r50144 | b'debug-revlog-index|debugindex', | |||
Augie Fackler
|
r43346 | cmdutil.debugrevlogopts + cmdutil.formatteropts, | ||
Augie Fackler
|
r43347 | _(b'-c|-m|FILE'), | ||
Augie Fackler
|
r43346 | ) | ||
Gregory Szorc
|
r30528 | def debugindex(ui, repo, file_=None, **opts): | ||
r50144 | """dump index data for a revlog""" | |||
Pulkit Goyal
|
r33100 | opts = pycompat.byteskwargs(opts) | ||
Augie Fackler
|
r43347 | store = cmdutil.openstorage(repo, b'debugindex', file_, opts) | ||
Gregory Szorc
|
r30528 | |||
Augie Fackler
|
r43347 | fm = ui.formatter(b'debugindex', opts) | ||
r50145 | ||||
r51510 | revlog = getattr(store, '_revlog', store) | |||
r50148 | ||||
r50145 | return revlog_debug.debug_index( | |||
ui, | ||||
repo, | ||||
formatter=fm, | ||||
r50148 | revlog=revlog, | |||
r50145 | full_node=ui.debugflag, | |||
Augie Fackler
|
r43346 | ) | ||
Gregory Szorc
|
r39318 | |||
Augie Fackler
|
r43346 | |||
@command( | ||||
Augie Fackler
|
r43347 | b'debugindexdot', | ||
cmdutil.debugrevlogopts, | ||||
_(b'-c|-m|FILE'), | ||||
optionalrepo=True, | ||||
Augie Fackler
|
r43346 | ) | ||
Gregory Szorc
|
r30528 | def debugindexdot(ui, repo, file_=None, **opts): | ||
"""dump an index DAG as a graphviz dot file""" | ||||
Matt Harbison
|
r51847 | r = cmdutil.openstorage( | ||
repo, b'debugindexdot', file_, pycompat.byteskwargs(opts) | ||||
) | ||||
Augie Fackler
|
r43350 | ui.writenoi18n(b"digraph G {\n") | ||
Gregory Szorc
|
r30528 | for i in r: | ||
node = r.node(i) | ||||
pp = r.parents(node) | ||||
Augie Fackler
|
r43347 | ui.write(b"\t%d -> %d\n" % (r.rev(pp[0]), i)) | ||
Joerg Sonnenberger
|
r47771 | if pp[1] != repo.nullid: | ||
Augie Fackler
|
r43347 | ui.write(b"\t%d -> %d\n" % (r.rev(pp[1]), i)) | ||
ui.write(b"}\n") | ||||
@command(b'debugindexstats', []) | ||||
Martin von Zweigbergk
|
r40016 | def debugindexstats(ui, repo): | ||
"""show stats related to the changelog index""" | ||||
Joerg Sonnenberger
|
r47771 | repo.changelog.shortest(repo.nullid, 1) | ||
Martin von Zweigbergk
|
r40401 | index = repo.changelog.index | ||
r51821 | if not hasattr(index, 'stats'): | |||
Raphaël Gomès
|
r52125 | raise error.Abort(_(b'debugindexstats only works with native C code')) | ||
Martin von Zweigbergk
|
r40401 | for k, v in sorted(index.stats().items()): | ||
Augie Fackler
|
r43347 | ui.write(b'%s: %d\n' % (k, v)) | ||
@command(b'debuginstall', [] + cmdutil.formatteropts, b'', norepo=True) | ||||
Pierre-Yves David
|
r30918 | def debuginstall(ui, **opts): | ||
Augie Fackler
|
r46554 | """test Mercurial installation | ||
Pierre-Yves David
|
r30918 | |||
Returns 0 on success. | ||||
Augie Fackler
|
r46554 | """ | ||
Pierre-Yves David
|
r30918 | problems = 0 | ||
Matt Harbison
|
r51848 | fm = ui.formatter(b'debuginstall', pycompat.byteskwargs(opts)) | ||
Pierre-Yves David
|
r30918 | fm.startitem() | ||
Yuya Nishihara
|
r45660 | # encoding might be unknown or wrong. don't translate these messages. | ||
fm.write(b'encoding', b"checking encoding (%s)...\n", encoding.encoding) | ||||
Pierre-Yves David
|
r30918 | err = None | ||
try: | ||||
Yuya Nishihara
|
r34131 | codecs.lookup(pycompat.sysstr(encoding.encoding)) | ||
except LookupError as inst: | ||||
Yuya Nishihara
|
r37102 | err = stringutil.forcebytestr(inst) | ||
Pierre-Yves David
|
r30918 | problems += 1 | ||
Augie Fackler
|
r43346 | fm.condwrite( | ||
err, | ||||
Augie Fackler
|
r43347 | b'encodingerror', | ||
Yuya Nishihara
|
r45660 | b" %s\n (check that your locale is properly set)\n", | ||
Augie Fackler
|
r43346 | err, | ||
) | ||||
Pierre-Yves David
|
r30918 | |||
# Python | ||||
Matt Harbison
|
r44083 | pythonlib = None | ||
r51821 | if hasattr(os, '__file__'): | |||
Matt Harbison
|
r44083 | pythonlib = os.path.dirname(pycompat.fsencode(os.__file__)) | ||
elif getattr(sys, 'oxidized', False): | ||||
pythonlib = pycompat.sysexecutable | ||||
Augie Fackler
|
r43346 | fm.write( | ||
Augie Fackler
|
r43347 | b'pythonexe', | ||
_(b"checking Python executable (%s)\n"), | ||||
pycompat.sysexecutable or _(b"unknown"), | ||||
Augie Fackler
|
r43346 | ) | ||
fm.write( | ||||
Gregory Szorc
|
r44603 | b'pythonimplementation', | ||
_(b"checking Python implementation (%s)\n"), | ||||
pycompat.sysbytes(platform.python_implementation()), | ||||
) | ||||
fm.write( | ||||
Augie Fackler
|
r43347 | b'pythonver', | ||
_(b"checking Python version (%s)\n"), | ||||
(b"%d.%d.%d" % sys.version_info[:3]), | ||||
Augie Fackler
|
r43346 | ) | ||
fm.write( | ||||
Augie Fackler
|
r43347 | b'pythonlib', | ||
_(b"checking Python lib (%s)...\n"), | ||||
Matt Harbison
|
r44083 | pythonlib or _(b"unknown"), | ||
Augie Fackler
|
r43346 | ) | ||
Pierre-Yves David
|
r30918 | |||
Raphaël Gomès
|
r44953 | try: | ||
Matt Harbison
|
r47543 | from . import rustext # pytype: disable=import-error | ||
Raphaël Gomès
|
r44953 | |||
rustext.__doc__ # trigger lazy import | ||||
except ImportError: | ||||
rustext = None | ||||
Pierre-Yves David
|
r30918 | security = set(sslutil.supportedprotocols) | ||
if sslutil.hassni: | ||||
Augie Fackler
|
r43347 | security.add(b'sni') | ||
Pierre-Yves David
|
r30918 | |||
Augie Fackler
|
r43346 | fm.write( | ||
Augie Fackler
|
r43347 | b'pythonsecurity', | ||
_(b"checking Python security support (%s)\n"), | ||||
fm.formatlist(sorted(security), name=b'protocol', fmt=b'%s', sep=b','), | ||||
Augie Fackler
|
r43346 | ) | ||
Pierre-Yves David
|
r30918 | |||
# These are warnings, not errors. So don't increment problem count. This | ||||
# may change in the future. | ||||
Augie Fackler
|
r43347 | if b'tls1.2' not in security: | ||
Augie Fackler
|
r43346 | fm.plain( | ||
_( | ||||
Augie Fackler
|
r43347 | b' TLS 1.2 not supported by Python install; ' | ||
b'network connections lack modern security\n' | ||||
Augie Fackler
|
r43346 | ) | ||
) | ||||
Augie Fackler
|
r43347 | if b'sni' not in security: | ||
Augie Fackler
|
r43346 | fm.plain( | ||
_( | ||||
Augie Fackler
|
r43347 | b' SNI not supported by Python install; may have ' | ||
b'connectivity issues with some servers\n' | ||||
Augie Fackler
|
r43346 | ) | ||
) | ||||
Pierre-Yves David
|
r30918 | |||
Raphaël Gomès
|
r44953 | fm.plain( | ||
_( | ||||
b"checking Rust extensions (%s)\n" | ||||
% (b'missing' if rustext is None else b'installed') | ||||
), | ||||
) | ||||
Pierre-Yves David
|
r30918 | # TODO print CA cert info | ||
# hg version | ||||
hgver = util.version() | ||||
Augie Fackler
|
r43346 | fm.write( | ||
Augie Fackler
|
r43347 | b'hgver', _(b"checking Mercurial version (%s)\n"), hgver.split(b'+')[0] | ||
Augie Fackler
|
r43346 | ) | ||
fm.write( | ||||
Augie Fackler
|
r43347 | b'hgverextra', | ||
_(b"checking Mercurial custom build (%s)\n"), | ||||
b'+'.join(hgver.split(b'+')[1:]), | ||||
Augie Fackler
|
r43346 | ) | ||
Pierre-Yves David
|
r30918 | |||
# compiled modules | ||||
Matt Harbison
|
r44083 | hgmodules = None | ||
r51821 | if hasattr(sys.modules[__name__], '__file__'): | |||
Matt Harbison
|
r44083 | hgmodules = os.path.dirname(pycompat.fsencode(__file__)) | ||
elif getattr(sys, 'oxidized', False): | ||||
hgmodules = pycompat.sysexecutable | ||||
Augie Fackler
|
r43346 | fm.write( | ||
Augie Fackler
|
r43347 | b'hgmodulepolicy', _(b"checking module policy (%s)\n"), policy.policy | ||
Augie Fackler
|
r43346 | ) | ||
fm.write( | ||||
Augie Fackler
|
r43347 | b'hgmodules', | ||
_(b"checking installed modules (%s)...\n"), | ||||
Matt Harbison
|
r44083 | hgmodules or _(b"unknown"), | ||
Augie Fackler
|
r43346 | ) | ||
Pierre-Yves David
|
r30918 | |||
Augie Fackler
|
r43347 | rustandc = policy.policy in (b'rust+c', b'rust+c-allow') | ||
Georges Racinet
|
r42651 | rustext = rustandc # for now, that's the only case | ||
Augie Fackler
|
r43347 | cext = policy.policy in (b'c', b'allow') or rustandc | ||
Georges Racinet
|
r42651 | nopure = cext or rustext | ||
if nopure: | ||||
Yuya Nishihara
|
r32204 | err = None | ||
try: | ||||
Georges Racinet
|
r42651 | if cext: | ||
Augie Fackler
|
r44102 | from .cext import ( # pytype: disable=import-error | ||
Georges Racinet
|
r42651 | base85, | ||
bdiff, | ||||
mpatch, | ||||
osutil, | ||||
) | ||||
Augie Fackler
|
r43346 | |||
Georges Racinet
|
r42651 | # quiet pyflakes | ||
dir(bdiff), dir(mpatch), dir(base85), dir(osutil) | ||||
if rustext: | ||||
Augie Fackler
|
r44102 | from .rustext import ( # pytype: disable=import-error | ||
Georges Racinet
|
r42651 | ancestor, | ||
dirstate, | ||||
) | ||||
Augie Fackler
|
r43346 | |||
dir(ancestor), dir(dirstate) # quiet pyflakes | ||||
Yuya Nishihara
|
r32204 | except Exception as inst: | ||
Yuya Nishihara
|
r37102 | err = stringutil.forcebytestr(inst) | ||
Yuya Nishihara
|
r32204 | problems += 1 | ||
Augie Fackler
|
r43347 | fm.condwrite(err, b'extensionserror', b" %s\n", err) | ||
Pierre-Yves David
|
r30918 | |||
compengines = util.compengines._engines.values() | ||||
Augie Fackler
|
r43346 | fm.write( | ||
Augie Fackler
|
r43347 | b'compengines', | ||
_(b'checking registered compression engines (%s)\n'), | ||||
Augie Fackler
|
r43346 | fm.formatlist( | ||
sorted(e.name() for e in compengines), | ||||
Augie Fackler
|
r43347 | name=b'compengine', | ||
fmt=b'%s', | ||||
sep=b', ', | ||||
Augie Fackler
|
r43346 | ), | ||
) | ||||
fm.write( | ||||
Augie Fackler
|
r43347 | b'compenginesavail', | ||
Martin von Zweigbergk
|
r43387 | _(b'checking available compression engines (%s)\n'), | ||
Augie Fackler
|
r43346 | fm.formatlist( | ||
sorted(e.name() for e in compengines if e.available()), | ||||
Augie Fackler
|
r43347 | name=b'compengine', | ||
fmt=b'%s', | ||||
sep=b', ', | ||||
Augie Fackler
|
r43346 | ), | ||
) | ||||
r42208 | wirecompengines = compression.compengines.supportedwireengines( | |||
Augie Fackler
|
r43346 | compression.SERVERROLE | ||
) | ||||
fm.write( | ||||
Augie Fackler
|
r43347 | b'compenginesserver', | ||
_( | ||||
b'checking available compression engines ' | ||||
b'for wire protocol (%s)\n' | ||||
), | ||||
Augie Fackler
|
r43346 | fm.formatlist( | ||
[e.name() for e in wirecompengines if e.wireprotosupport()], | ||||
Augie Fackler
|
r43347 | name=b'compengine', | ||
fmt=b'%s', | ||||
sep=b', ', | ||||
Augie Fackler
|
r43346 | ), | ||
) | ||||
Augie Fackler
|
r43347 | re2 = b'missing' | ||
r51582 | if util.has_re2(): | |||
Augie Fackler
|
r43347 | re2 = b'available' | ||
fm.plain(_(b'checking "re2" regexp engine (%s)\n') % re2) | ||||
Boris Feld
|
r35464 | fm.data(re2=bool(util._re2)) | ||
Pierre-Yves David
|
r30918 | |||
# templates | ||||
Martin von Zweigbergk
|
r45755 | p = templater.templatedir() | ||
Martin von Zweigbergk
|
r45773 | fm.write(b'templatedirs', b'checking templates (%s)...\n', p or b'') | ||
Augie Fackler
|
r43347 | fm.condwrite(not p, b'', _(b" no template directories found\n")) | ||
Pierre-Yves David
|
r30918 | if p: | ||
Martin von Zweigbergk
|
r45880 | (m, fp) = templater.try_open_template(b"map-cmdline.default") | ||
Pierre-Yves David
|
r30918 | if m: | ||
# template found, check if it is working | ||||
err = None | ||||
try: | ||||
templater.templater.frommapfile(m) | ||||
except Exception as inst: | ||||
Yuya Nishihara
|
r37102 | err = stringutil.forcebytestr(inst) | ||
Pierre-Yves David
|
r30918 | p = None | ||
Augie Fackler
|
r43347 | fm.condwrite(err, b'defaulttemplateerror', b" %s\n", err) | ||
Pierre-Yves David
|
r30918 | else: | ||
p = None | ||||
Augie Fackler
|
r43346 | fm.condwrite( | ||
Augie Fackler
|
r43347 | p, b'defaulttemplate', _(b"checking default template (%s)\n"), m | ||
Augie Fackler
|
r43346 | ) | ||
fm.condwrite( | ||||
not m, | ||||
Augie Fackler
|
r43347 | b'defaulttemplatenotfound', | ||
_(b" template '%s' not found\n"), | ||||
b"default", | ||||
Augie Fackler
|
r43346 | ) | ||
Pierre-Yves David
|
r30918 | if not p: | ||
problems += 1 | ||||
Augie Fackler
|
r43346 | fm.condwrite( | ||
Augie Fackler
|
r43347 | not p, b'', _(b" (templates seem to have been installed incorrectly)\n") | ||
Augie Fackler
|
r43346 | ) | ||
Pierre-Yves David
|
r30918 | |||
# editor | ||||
editor = ui.geteditor() | ||||
editor = util.expandpath(editor) | ||||
Yuya Nishihara
|
r37138 | editorbin = procutil.shellsplit(editor)[0] | ||
Augie Fackler
|
r43347 | fm.write(b'editor', _(b"checking commit editor... (%s)\n"), editorbin) | ||
Yuya Nishihara
|
r37138 | cmdpath = procutil.findexe(editorbin) | ||
Augie Fackler
|
r43346 | fm.condwrite( | ||
Augie Fackler
|
r43347 | not cmdpath and editor == b'vi', | ||
b'vinotfound', | ||||
Augie Fackler
|
r43346 | _( | ||
Augie Fackler
|
r43347 | b" No commit editor set and can't find %s in PATH\n" | ||
b" (specify a commit editor in your configuration" | ||||
b" file)\n" | ||||
Augie Fackler
|
r43346 | ), | ||
Augie Fackler
|
r43347 | not cmdpath and editor == b'vi' and editorbin, | ||
Augie Fackler
|
r43346 | ) | ||
fm.condwrite( | ||||
Augie Fackler
|
r43347 | not cmdpath and editor != b'vi', | ||
b'editornotfound', | ||||
Augie Fackler
|
r43346 | _( | ||
Augie Fackler
|
r43347 | b" Can't find editor '%s' in PATH\n" | ||
b" (specify a commit editor in your configuration" | ||||
b" file)\n" | ||||
Augie Fackler
|
r43346 | ), | ||
not cmdpath and editorbin, | ||||
) | ||||
Augie Fackler
|
r43347 | if not cmdpath and editor != b'vi': | ||
Pierre-Yves David
|
r30918 | problems += 1 | ||
# check username | ||||
username = None | ||||
err = None | ||||
try: | ||||
username = ui.username() | ||||
except error.Abort as e: | ||||
Martin von Zweigbergk
|
r46274 | err = e.message | ||
Pierre-Yves David
|
r30918 | problems += 1 | ||
Augie Fackler
|
r43347 | fm.condwrite( | ||
username, b'username', _(b"checking username (%s)\n"), username | ||||
) | ||||
Augie Fackler
|
r43346 | fm.condwrite( | ||
err, | ||||
Augie Fackler
|
r43347 | b'usernameerror', | ||
Augie Fackler
|
r43346 | _( | ||
Augie Fackler
|
r43347 | b"checking username...\n %s\n" | ||
b" (specify a username in your configuration file)\n" | ||||
Augie Fackler
|
r43346 | ), | ||
err, | ||||
) | ||||
Pierre-Yves David
|
r30918 | |||
Augie Fackler
|
r42878 | for name, mod in extensions.extensions(): | ||
handler = getattr(mod, 'debuginstall', None) | ||||
if handler is not None: | ||||
problems += handler(ui, fm) | ||||
Augie Fackler
|
r43347 | fm.condwrite(not problems, b'', _(b"no problems detected\n")) | ||
Pierre-Yves David
|
r30918 | if not problems: | ||
fm.data(problems=problems) | ||||
Augie Fackler
|
r43346 | fm.condwrite( | ||
problems, | ||||
Augie Fackler
|
r43347 | b'problems', | ||
Martin von Zweigbergk
|
r43387 | _(b"%d problems detected, please check your install!\n"), | ||
Augie Fackler
|
r43346 | problems, | ||
) | ||||
Pierre-Yves David
|
r30918 | fm.end() | ||
return problems | ||||
Augie Fackler
|
r43346 | |||
Augie Fackler
|
r43347 | @command(b'debugknown', [], _(b'REPO ID...'), norepo=True) | ||
Pierre-Yves David
|
r30919 | def debugknown(ui, repopath, *ids, **opts): | ||
"""test whether node ids are known to a repo | ||||
Every ID must be a full-length hex node id string. Returns a list of 0s | ||||
and 1s indicating unknown/known. | ||||
""" | ||||
Matt Harbison
|
r51849 | repo = hg.peer(ui, pycompat.byteskwargs(opts), repopath) | ||
Augie Fackler
|
r43347 | if not repo.capable(b'known'): | ||
raise error.Abort(b"known() not supported by target repository") | ||||
Pierre-Yves David
|
r30919 | flags = repo.known([bin(s) for s in ids]) | ||
Augie Fackler
|
r43347 | ui.write(b"%s\n" % (b"".join([f and b"1" or b"0" for f in flags]))) | ||
@command(b'debuglabelcomplete', [], _(b'LABEL...')) | ||||
Pierre-Yves David
|
r30935 | def debuglabelcomplete(ui, repo, *args): | ||
'''backwards compatibility with old bash completion scripts (DEPRECATED)''' | ||||
Kyle Lippincott
|
r31402 | debugnamecomplete(ui, repo, *args) | ||
Pierre-Yves David
|
r30935 | |||
Augie Fackler
|
r43346 | |||
@command( | ||||
Augie Fackler
|
r43347 | b'debuglocks', | ||
Augie Fackler
|
r43346 | [ | ||
Pulkit Goyal
|
r46854 | (b'L', b'force-free-lock', None, _(b'free the store lock (DANGEROUS)')), | ||
Augie Fackler
|
r43346 | ( | ||
Augie Fackler
|
r43347 | b'W', | ||
Pulkit Goyal
|
r46854 | b'force-free-wlock', | ||
Augie Fackler
|
r43346 | None, | ||
Augie Fackler
|
r43347 | _(b'free the working state lock (DANGEROUS)'), | ||
Augie Fackler
|
r43346 | ), | ||
Augie Fackler
|
r43347 | (b's', b'set-lock', None, _(b'set the store lock until stopped')), | ||
( | ||||
b'S', | ||||
b'set-wlock', | ||||
None, | ||||
_(b'set the working state lock until stopped'), | ||||
), | ||||
Augie Fackler
|
r43346 | ], | ||
Augie Fackler
|
r43347 | _(b'[OPTION]...'), | ||
Augie Fackler
|
r43346 | ) | ||
Pierre-Yves David
|
r30938 | def debuglocks(ui, repo, **opts): | ||
"""show or modify state of locks | ||||
By default, this command will show which locks are held. This | ||||
includes the user and process holding the lock, the amount of time | ||||
the lock has been held, and the machine name where the process is | ||||
running if it's not local. | ||||
Locks protect the integrity of Mercurial's data, so should be | ||||
treated with care. System crashes or other interruptions may cause | ||||
locks to not be properly released, though Mercurial will usually | ||||
detect and remove such stale locks automatically. | ||||
However, detecting stale locks may not always be possible (for | ||||
instance, on a shared filesystem). Removing locks may also be | ||||
blocked by filesystem permissions. | ||||
Paul Morelle
|
r35396 | Setting a lock will prevent other commands from changing the data. | ||
The command will wait until an interruption (SIGINT, SIGTERM, ...) occurs. | ||||
The set locks are removed when the command exits. | ||||
Pierre-Yves David
|
r30938 | Returns 0 if no locks are held. | ||
""" | ||||
Pulkit Goyal
|
r46854 | if opts.get('force_free_lock'): | ||
r49924 | repo.svfs.tryunlink(b'lock') | |||
Pulkit Goyal
|
r46854 | if opts.get('force_free_wlock'): | ||
r49924 | repo.vfs.tryunlink(b'wlock') | |||
Pulkit Goyal
|
r46854 | if opts.get('force_free_lock') or opts.get('force_free_wlock'): | ||
Pierre-Yves David
|
r30938 | return 0 | ||
Paul Morelle
|
r35396 | locks = [] | ||
try: | ||||
Augie Fackler
|
r43906 | if opts.get('set_wlock'): | ||
Paul Morelle
|
r35396 | try: | ||
locks.append(repo.wlock(False)) | ||||
except error.LockHeld: | ||||
Augie Fackler
|
r43347 | raise error.Abort(_(b'wlock is already held')) | ||
Augie Fackler
|
r43906 | if opts.get('set_lock'): | ||
Paul Morelle
|
r35396 | try: | ||
locks.append(repo.lock(False)) | ||||
except error.LockHeld: | ||||
Augie Fackler
|
r43347 | raise error.Abort(_(b'lock is already held')) | ||
Paul Morelle
|
r35396 | if len(locks): | ||
r50092 | try: | |||
if ui.interactive(): | ||||
prompt = _(b"ready to release the lock (y)? $$ &Yes") | ||||
ui.promptchoice(prompt) | ||||
else: | ||||
msg = b"%d locks held, waiting for signal\n" | ||||
msg %= len(locks) | ||||
ui.status(msg) | ||||
while True: # XXX wait for a signal | ||||
time.sleep(0.1) | ||||
except KeyboardInterrupt: | ||||
msg = b"signal-received releasing locks\n" | ||||
ui.status(msg) | ||||
Paul Morelle
|
r35396 | return 0 | ||
finally: | ||||
release(*locks) | ||||
Pierre-Yves David
|
r30938 | now = time.time() | ||
held = 0 | ||||
def report(vfs, name, method): | ||||
# this causes stale locks to get reaped for more accurate reporting | ||||
try: | ||||
l = method(False) | ||||
except error.LockHeld: | ||||
l = None | ||||
if l: | ||||
l.release() | ||||
else: | ||||
try: | ||||
Augie Fackler
|
r36799 | st = vfs.lstat(name) | ||
age = now - st[stat.ST_MTIME] | ||||
user = util.username(st.st_uid) | ||||
Pierre-Yves David
|
r30938 | locker = vfs.readlock(name) | ||
Augie Fackler
|
r43347 | if b":" in locker: | ||
host, pid = locker.split(b':') | ||||
Pierre-Yves David
|
r30938 | if host == socket.gethostname(): | ||
Augie Fackler
|
r43347 | locker = b'user %s, process %s' % (user or b'None', pid) | ||
Pierre-Yves David
|
r30938 | else: | ||
Augie Fackler
|
r43347 | locker = b'user %s, process %s, host %s' % ( | ||
Augie Fackler
|
r43346 | user or b'None', | ||
pid, | ||||
host, | ||||
) | ||||
Augie Fackler
|
r43350 | ui.writenoi18n(b"%-6s %s (%ds)\n" % (name + b":", locker, age)) | ||
Pierre-Yves David
|
r30938 | return 1 | ||
Manuel Jacob
|
r50201 | except FileNotFoundError: | ||
pass | ||||
Pierre-Yves David
|
r30938 | |||
Augie Fackler
|
r43350 | ui.writenoi18n(b"%-6s free\n" % (name + b":")) | ||
Pierre-Yves David
|
r30938 | return 0 | ||
Augie Fackler
|
r43347 | held += report(repo.svfs, b"lock", repo.lock) | ||
held += report(repo.vfs, b"wlock", repo.wlock) | ||||
Pierre-Yves David
|
r30938 | |||
return held | ||||
Augie Fackler
|
r43346 | |||
@command( | ||||
Augie Fackler
|
r43347 | b'debugmanifestfulltextcache', | ||
Augie Fackler
|
r43346 | [ | ||
Augie Fackler
|
r43347 | (b'', b'clear', False, _(b'clear the cache')), | ||
Augie Fackler
|
r43346 | ( | ||
Augie Fackler
|
r43347 | b'a', | ||
b'add', | ||||
Augie Fackler
|
r43346 | [], | ||
Augie Fackler
|
r43347 | _(b'add the given manifest nodes to the cache'), | ||
_(b'NODE'), | ||||
Augie Fackler
|
r43346 | ), | ||
], | ||||
Augie Fackler
|
r43347 | b'', | ||
Augie Fackler
|
r43346 | ) | ||
r42124 | def debugmanifestfulltextcache(ui, repo, add=(), **opts): | |||
Martijn Pieters
|
r38803 | """show, clear or amend the contents of the manifest fulltext cache""" | ||
r42108 | ||||
def getcache(): | ||||
Gregory Szorc
|
r39280 | r = repo.manifestlog.getstorage(b'') | ||
Martijn Pieters
|
r38803 | try: | ||
r42108 | return r._fulltextcache | |||
Martijn Pieters
|
r38803 | except AttributeError: | ||
Augie Fackler
|
r43346 | msg = _( | ||
Augie Fackler
|
r43347 | b"Current revlog implementation doesn't appear to have a " | ||
b"manifest fulltext cache\n" | ||||
Augie Fackler
|
r43346 | ) | ||
r42108 | raise error.Abort(msg) | |||
Augie Fackler
|
r43906 | if opts.get('clear'): | ||
r42130 | with repo.wlock(): | |||
r42108 | cache = getcache() | |||
r42113 | cache.clear(clear_persisted_data=True) | |||
return | ||||
Martijn Pieters
|
r38803 | |||
r42108 | if add: | |||
r42130 | with repo.wlock(): | |||
r42124 | m = repo.manifestlog | |||
store = m.getstorage(b'') | ||||
for n in add: | ||||
try: | ||||
manifest = m[store.lookup(n)] | ||||
except error.LookupError as e: | ||||
Matt Harbison
|
r47516 | raise error.Abort( | ||
bytes(e), hint=b"Check your manifest node id" | ||||
) | ||||
r42124 | manifest.read() # stores revisision in cache too | |||
r42109 | return | |||
Martijn Pieters
|
r38803 | |||
r42108 | cache = getcache() | |||
if not len(cache): | ||||
Augie Fackler
|
r43347 | ui.write(_(b'cache empty\n')) | ||
r42108 | else: | |||
ui.write( | ||||
Augie Fackler
|
r43346 | _( | ||
Augie Fackler
|
r43347 | b'cache contains %d manifest entries, in order of most to ' | ||
b'least recent:\n' | ||||
Augie Fackler
|
r43346 | ) | ||
% (len(cache),) | ||||
) | ||||
r42108 | totalsize = 0 | |||
for nodeid in cache: | ||||
# Use cache.get to not update the LRU order | ||||
r42125 | data = cache.peek(nodeid) | |||
r42108 | size = len(data) | |||
Augie Fackler
|
r43346 | totalsize += size + 24 # 20 bytes nodeid, 4 bytes size | ||
ui.write( | ||||
Augie Fackler
|
r43347 | _(b'id: %s, size %s\n') % (hex(nodeid), util.bytecount(size)) | ||
Augie Fackler
|
r43346 | ) | ||
Augie Fackler
|
r43347 | ondisk = cache._opener.stat(b'manifestfulltextcache').st_size | ||
r42108 | ui.write( | |||
Augie Fackler
|
r43347 | _(b'total cache data size %s, on-disk %s\n') | ||
Augie Fackler
|
r43346 | % (util.bytecount(totalsize), util.bytecount(ondisk)) | ||
r42108 | ) | |||
Martijn Pieters
|
r38803 | |||
Augie Fackler
|
r43346 | |||
Martin von Zweigbergk
|
r44880 | @command(b'debugmergestate', [] + cmdutil.templateopts, b'') | ||
def debugmergestate(ui, repo, *args, **opts): | ||||
Pierre-Yves David
|
r30936 | """print merge state | ||
Use --verbose to print out information about whether v1 or v2 merge state | ||||
was chosen.""" | ||||
Augie Fackler
|
r43346 | |||
Martin von Zweigbergk
|
r44880 | if ui.verbose: | ||
Augie Fackler
|
r45383 | ms = mergestatemod.mergestate(repo) | ||
Martin von Zweigbergk
|
r44880 | |||
# sort so that reasonable information is on top | ||||
v1records = ms._readrecordsv1() | ||||
v2records = ms._readrecordsv2() | ||||
if not v1records and not v2records: | ||||
pass | ||||
elif not v2records: | ||||
ui.writenoi18n(b'no version 2 merge state\n') | ||||
elif ms._v1v2match(v1records, v2records): | ||||
ui.writenoi18n(b'v1 and v2 states match: using v2\n') | ||||
Pierre-Yves David
|
r30936 | else: | ||
Martin von Zweigbergk
|
r44880 | ui.writenoi18n(b'v1 and v2 states mismatch: using v1\n') | ||
Matt Harbison
|
r51850 | if not opts['template']: | ||
opts['template'] = ( | ||||
Martin von Zweigbergk
|
r44880 | b'{if(commits, "", "no merge state found\n")}' | ||
b'{commits % "{name}{if(label, " ({label})")}: {node}\n"}' | ||||
b'{files % "file: {path} (state \\"{state}\\")\n' | ||||
b'{if(local_path, "' | ||||
b' local path: {local_path} (hash {local_key}, flags \\"{local_flags}\\")\n' | ||||
b' ancestor path: {ancestor_path} (node {ancestor_node})\n' | ||||
b' other path: {other_path} (node {other_node})\n' | ||||
b'")}' | ||||
b'{if(rename_side, "' | ||||
b' rename side: {rename_side}\n' | ||||
b' renamed path: {renamed_path}\n' | ||||
b'")}' | ||||
b'{extras % " extra: {key} = {value}\n"}' | ||||
b'"}' | ||||
Pulkit Goyal
|
r46016 | b'{extras % "extra: {file} ({key} = {value})\n"}' | ||
Martin von Zweigbergk
|
r44880 | ) | ||
Augie Fackler
|
r45383 | ms = mergestatemod.mergestate.read(repo) | ||
Martin von Zweigbergk
|
r44880 | |||
Matt Harbison
|
r51850 | fm = ui.formatter(b'debugmergestate', pycompat.byteskwargs(opts)) | ||
Martin von Zweigbergk
|
r44880 | fm.startitem() | ||
fm_commits = fm.nested(b'commits') | ||||
if ms.active(): | ||||
for name, node, label_index in ( | ||||
(b'local', ms.local, 0), | ||||
(b'other', ms.other, 1), | ||||
): | ||||
fm_commits.startitem() | ||||
fm_commits.data(name=name) | ||||
fm_commits.data(node=hex(node)) | ||||
if ms._labels and len(ms._labels) > label_index: | ||||
fm_commits.data(label=ms._labels[label_index]) | ||||
fm_commits.end() | ||||
fm_files = fm.nested(b'files') | ||||
if ms.active(): | ||||
for f in ms: | ||||
fm_files.startitem() | ||||
fm_files.data(path=f) | ||||
state = ms._state[f] | ||||
fm_files.data(state=state[0]) | ||||
if state[0] in ( | ||||
Augie Fackler
|
r45383 | mergestatemod.MERGE_RECORD_UNRESOLVED, | ||
mergestatemod.MERGE_RECORD_RESOLVED, | ||||
Martin von Zweigbergk
|
r44880 | ): | ||
fm_files.data(local_key=state[1]) | ||||
fm_files.data(local_path=state[2]) | ||||
fm_files.data(ancestor_path=state[3]) | ||||
fm_files.data(ancestor_node=state[4]) | ||||
fm_files.data(other_path=state[5]) | ||||
fm_files.data(other_node=state[6]) | ||||
fm_files.data(local_flags=state[7]) | ||||
elif state[0] in ( | ||||
Augie Fackler
|
r45383 | mergestatemod.MERGE_RECORD_UNRESOLVED_PATH, | ||
mergestatemod.MERGE_RECORD_RESOLVED_PATH, | ||||
Martin von Zweigbergk
|
r44880 | ): | ||
fm_files.data(renamed_path=state[1]) | ||||
fm_files.data(rename_side=state[2]) | ||||
fm_extras = fm_files.nested(b'extras') | ||||
Pulkit Goyal
|
r46017 | for k, v in sorted(ms.extras(f).items()): | ||
Martin von Zweigbergk
|
r44880 | fm_extras.startitem() | ||
fm_extras.data(key=k) | ||||
fm_extras.data(value=v) | ||||
fm_extras.end() | ||||
fm_files.end() | ||||
Pulkit Goyal
|
r46016 | fm_extras = fm.nested(b'extras') | ||
Gregory Szorc
|
r49776 | for f, d in sorted(ms.allextras().items()): | ||
Pulkit Goyal
|
r46016 | if f in ms: | ||
# If file is in mergestate, we have already processed it's extras | ||||
continue | ||||
Gregory Szorc
|
r49768 | for k, v in d.items(): | ||
Pulkit Goyal
|
r46016 | fm_extras.startitem() | ||
fm_extras.data(file=f) | ||||
fm_extras.data(key=k) | ||||
fm_extras.data(value=v) | ||||
fm_extras.end() | ||||
Martin von Zweigbergk
|
r44880 | fm.end() | ||
Pierre-Yves David
|
r30936 | |||
Augie Fackler
|
r43346 | |||
Augie Fackler
|
r43347 | @command(b'debugnamecomplete', [], _(b'NAME...')) | ||
Pierre-Yves David
|
r30937 | def debugnamecomplete(ui, repo, *args): | ||
'''complete "names" - tags, open branch names, bookmark names''' | ||||
names = set() | ||||
# since we previously only listed open branches, we will handle that | ||||
# specially (after this for loop) | ||||
Gregory Szorc
|
r49768 | for name, ns in repo.names.items(): | ||
Augie Fackler
|
r43347 | if name != b'branches': | ||
Pierre-Yves David
|
r30937 | names.update(ns.listnames(repo)) | ||
Augie Fackler
|
r43346 | names.update( | ||
tag | ||||
for (tag, heads, tip, closed) in repo.branchmap().iterbranches() | ||||
if not closed | ||||
) | ||||
Pierre-Yves David
|
r30937 | completions = set() | ||
if not args: | ||||
Augie Fackler
|
r43347 | args = [b''] | ||
Pierre-Yves David
|
r30937 | for a in args: | ||
completions.update(n for n in names if n.startswith(a)) | ||||
Augie Fackler
|
r43347 | ui.write(b'\n'.join(sorted(completions))) | ||
ui.write(b'\n') | ||||
Pierre-Yves David
|
r30937 | |||
Augie Fackler
|
r43346 | |||
@command( | ||||
r44788 | b'debugnodemap', | |||
Arseniy Alekseyev
|
r51402 | ( | ||
cmdutil.debugrevlogopts | ||||
+ [ | ||||
( | ||||
b'', | ||||
b'dump-new', | ||||
False, | ||||
_(b'write a (new) persistent binary nodemap on stdout'), | ||||
), | ||||
(b'', b'dump-disk', False, _(b'dump on-disk data on stdout')), | ||||
( | ||||
b'', | ||||
b'check', | ||||
False, | ||||
_(b'check that the data on disk data are correct.'), | ||||
), | ||||
( | ||||
b'', | ||||
b'metadata', | ||||
False, | ||||
_(b'display the on disk meta data for the nodemap'), | ||||
), | ||||
] | ||||
), | ||||
r51441 | _(b'-c|-m|FILE'), | |||
r44788 | ) | |||
Arseniy Alekseyev
|
r51402 | def debugnodemap(ui, repo, file_=None, **opts): | ||
Augie Fackler
|
r46554 | """write and inspect on disk nodemap""" | ||
Arseniy Alekseyev
|
r51402 | if opts.get('changelog') or opts.get('manifest') or opts.get('dir'): | ||
if file_ is not None: | ||||
r51440 | raise error.InputError( | |||
_(b'cannot specify a file with other arguments') | ||||
) | ||||
Arseniy Alekseyev
|
r51402 | elif file_ is None: | ||
opts['changelog'] = True | ||||
r = cmdutil.openstorage( | ||||
repo.unfiltered(), b'debugnodemap', file_, pycompat.byteskwargs(opts) | ||||
) | ||||
r51442 | if isinstance(r, (manifest.manifestrevlog, filelog.filelog)): | |||
Arseniy Alekseyev
|
r51402 | r = r._revlog | ||
r44790 | if opts['dump_new']: | |||
r51821 | if hasattr(r.index, "nodemap_data_all"): | |||
Arseniy Alekseyev
|
r51402 | data = r.index.nodemap_data_all() | ||
r44999 | else: | |||
Arseniy Alekseyev
|
r51402 | data = nodemap.persistent_data(r.index) | ||
r44788 | ui.write(data) | |||
r44790 | elif opts['dump_disk']: | |||
Arseniy Alekseyev
|
r51402 | nm_data = nodemap.persisted_data(r) | ||
r44804 | if nm_data is not None: | |||
docket, data = nm_data | ||||
r44843 | ui.write(data[:]) | |||
r44799 | elif opts['check']: | |||
Arseniy Alekseyev
|
r51402 | nm_data = nodemap.persisted_data(r) | ||
r44804 | if nm_data is not None: | |||
docket, data = nm_data | ||||
Arseniy Alekseyev
|
r51402 | return nodemap.check_data(ui, r.index, data) | ||
r44806 | elif opts['metadata']: | |||
Arseniy Alekseyev
|
r51402 | nm_data = nodemap.persisted_data(r) | ||
r44806 | if nm_data is not None: | |||
docket, data = nm_data | ||||
ui.write((b"uid: %s\n") % docket.uid) | ||||
r44807 | ui.write((b"tip-rev: %d\n") % docket.tip_rev) | |||
r45002 | ui.write((b"tip-node: %s\n") % hex(docket.tip_node)) | |||
r44808 | ui.write((b"data-length: %d\n") % docket.data_length) | |||
ui.write((b"data-unused: %d\n") % docket.data_unused) | ||||
r45125 | unused_perc = docket.data_unused * 100.0 / docket.data_length | |||
ui.write((b"data-unused: %2.3f%%\n") % unused_perc) | ||||
r44788 | ||||
@command( | ||||
Augie Fackler
|
r43347 | b'debugobsolete', | ||
Augie Fackler
|
r43346 | [ | ||
Augie Fackler
|
r43347 | (b'', b'flags', 0, _(b'markers flag')), | ||
Augie Fackler
|
r43346 | ( | ||
Augie Fackler
|
r43347 | b'', | ||
b'record-parents', | ||||
Augie Fackler
|
r43346 | False, | ||
Augie Fackler
|
r43347 | _(b'record parent information for the precursor'), | ||
Augie Fackler
|
r43346 | ), | ||
Augie Fackler
|
r43347 | (b'r', b'rev', [], _(b'display markers relevant to REV')), | ||
Augie Fackler
|
r43346 | ( | ||
Augie Fackler
|
r43347 | b'', | ||
b'exclusive', | ||||
Augie Fackler
|
r43346 | False, | ||
Martin von Zweigbergk
|
r43387 | _(b'restrict display to markers only relevant to REV'), | ||
Augie Fackler
|
r43346 | ), | ||
Augie Fackler
|
r43347 | (b'', b'index', False, _(b'display index of the marker')), | ||
(b'', b'delete', [], _(b'delete markers specified by indices')), | ||||
Augie Fackler
|
r43346 | ] | ||
+ cmdutil.commitopts2 | ||||
+ cmdutil.formatteropts, | ||||
Augie Fackler
|
r43347 | _(b'[OBSOLETED [REPLACEMENT ...]]'), | ||
Augie Fackler
|
r43346 | ) | ||
Pierre-Yves David
|
r30939 | def debugobsolete(ui, repo, precursor=None, *successors, **opts): | ||
"""create arbitrary obsolete marker | ||||
With no arguments, displays the list of obsolescence markers.""" | ||||
def parsenodeid(s): | ||||
try: | ||||
# We do not use revsingle/revrange functions here to accept | ||||
# arbitrary node identifiers, possibly not present in the | ||||
# local repository. | ||||
n = bin(s) | ||||
Joerg Sonnenberger
|
r47771 | if len(n) != repo.nodeconstants.nodelen: | ||
Manuel Jacob
|
r50143 | raise ValueError | ||
Pierre-Yves David
|
r30939 | return n | ||
Manuel Jacob
|
r50143 | except ValueError: | ||
Martin von Zweigbergk
|
r46486 | raise error.InputError( | ||
Augie Fackler
|
r43347 | b'changeset references must be full hexadecimal ' | ||
b'node identifiers' | ||||
Augie Fackler
|
r43346 | ) | ||
Pierre-Yves David
|
r30939 | |||
Matt Harbison
|
r51851 | if opts.get('delete'): | ||
Pierre-Yves David
|
r30939 | indices = [] | ||
Matt Harbison
|
r51851 | for v in opts.get('delete'): | ||
Pierre-Yves David
|
r30939 | try: | ||
indices.append(int(v)) | ||||
except ValueError: | ||||
Martin von Zweigbergk
|
r46486 | raise error.InputError( | ||
Augie Fackler
|
r43347 | _(b'invalid index value: %r') % v, | ||
hint=_(b'use integers for indices'), | ||||
Augie Fackler
|
r43346 | ) | ||
Pierre-Yves David
|
r30939 | |||
if repo.currenttransaction(): | ||||
Augie Fackler
|
r43346 | raise error.Abort( | ||
Martin von Zweigbergk
|
r43387 | _(b'cannot delete obsmarkers in the middle of transaction.') | ||
Augie Fackler
|
r43346 | ) | ||
Pierre-Yves David
|
r30939 | |||
with repo.lock(): | ||||
n = repair.deleteobsmarkers(repo.obsstore, indices) | ||||
Augie Fackler
|
r43347 | ui.write(_(b'deleted %i obsolescence markers\n') % n) | ||
Pierre-Yves David
|
r30939 | |||
return | ||||
if precursor is not None: | ||||
Matt Harbison
|
r51851 | if opts['rev']: | ||
Martin von Zweigbergk
|
r46486 | raise error.InputError( | ||
b'cannot select revision when creating marker' | ||||
) | ||||
Pierre-Yves David
|
r30939 | metadata = {} | ||
Matt Harbison
|
r51851 | metadata[b'user'] = encoding.fromlocal(opts['user'] or ui.username()) | ||
Pierre-Yves David
|
r30939 | succs = tuple(parsenodeid(succ) for succ in successors) | ||
l = repo.lock() | ||||
try: | ||||
Augie Fackler
|
r43347 | tr = repo.transaction(b'debugobsolete') | ||
Pierre-Yves David
|
r30939 | try: | ||
Matt Harbison
|
r51851 | date = opts.get('date') | ||
Pierre-Yves David
|
r30939 | if date: | ||
Boris Feld
|
r36625 | date = dateutil.parsedate(date) | ||
Pierre-Yves David
|
r30939 | else: | ||
date = None | ||||
prec = parsenodeid(precursor) | ||||
parents = None | ||||
Matt Harbison
|
r51851 | if opts['record_parents']: | ||
Pierre-Yves David
|
r30939 | if prec not in repo.unfiltered(): | ||
Augie Fackler
|
r43346 | raise error.Abort( | ||
Augie Fackler
|
r43347 | b'cannot used --record-parents on ' | ||
b'unknown changesets' | ||||
Augie Fackler
|
r43346 | ) | ||
Pierre-Yves David
|
r30939 | parents = repo.unfiltered()[prec].parents() | ||
parents = tuple(p.node() for p in parents) | ||||
Augie Fackler
|
r43346 | repo.obsstore.create( | ||
tr, | ||||
prec, | ||||
succs, | ||||
Matt Harbison
|
r51851 | opts['flags'], | ||
Augie Fackler
|
r43346 | parents=parents, | ||
date=date, | ||||
metadata=metadata, | ||||
ui=ui, | ||||
) | ||||
Pierre-Yves David
|
r30939 | tr.close() | ||
except ValueError as exc: | ||||
Augie Fackler
|
r43346 | raise error.Abort( | ||
Matt Harbison
|
r47516 | _(b'bad obsmarker input: %s') % stringutil.forcebytestr(exc) | ||
Augie Fackler
|
r43346 | ) | ||
Pierre-Yves David
|
r30939 | finally: | ||
tr.release() | ||||
finally: | ||||
l.release() | ||||
else: | ||||
Matt Harbison
|
r51851 | if opts['rev']: | ||
revs = logcmdutil.revrange(repo, opts['rev']) | ||||
Pierre-Yves David
|
r30939 | nodes = [repo[r].node() for r in revs] | ||
Augie Fackler
|
r43346 | markers = list( | ||
obsutil.getmarkers( | ||||
Matt Harbison
|
r51851 | repo, nodes=nodes, exclusive=opts['exclusive'] | ||
Augie Fackler
|
r43346 | ) | ||
) | ||||
Pierre-Yves David
|
r30939 | markers.sort(key=lambda x: x._data) | ||
else: | ||||
r33150 | markers = obsutil.getmarkers(repo) | |||
Pierre-Yves David
|
r30939 | |||
markerstoiter = markers | ||||
isrelevant = lambda m: True | ||||
Matt Harbison
|
r51851 | if opts.get('rev') and opts.get('index'): | ||
r33150 | markerstoiter = obsutil.getmarkers(repo) | |||
Pierre-Yves David
|
r30939 | markerset = set(markers) | ||
isrelevant = lambda m: m in markerset | ||||
Matt Harbison
|
r51851 | fm = ui.formatter(b'debugobsolete', pycompat.byteskwargs(opts)) | ||
Pierre-Yves David
|
r30939 | for i, m in enumerate(markerstoiter): | ||
if not isrelevant(m): | ||||
# marker can be irrelevant when we're iterating over a set | ||||
# of markers (markerstoiter) which is bigger than the set | ||||
# of markers we want to display (markers) | ||||
# this can happen if both --index and --rev options are | ||||
# provided and thus we need to iterate over all of the markers | ||||
# to get the correct indices, but only display the ones that | ||||
# are relevant to --rev value | ||||
continue | ||||
fm.startitem() | ||||
Matt Harbison
|
r51851 | ind = i if opts.get('index') else None | ||
Pierre-Yves David
|
r30939 | cmdutil.showmarker(fm, m, index=ind) | ||
fm.end() | ||||
Augie Fackler
|
r43346 | |||
@command( | ||||
Augie Fackler
|
r43347 | b'debugp1copies', | ||
[(b'r', b'rev', b'', _(b'revision to debug'), _(b'REV'))], | ||||
_(b'[-r REV]'), | ||||
Augie Fackler
|
r43346 | ) | ||
Martin von Zweigbergk
|
r41921 | def debugp1copies(ui, repo, **opts): | ||
"""dump copy information compared to p1""" | ||||
Matt Harbison
|
r51852 | ctx = scmutil.revsingle(repo, opts.get('rev'), default=None) | ||
Martin von Zweigbergk
|
r41921 | for dst, src in ctx.p1copies().items(): | ||
Augie Fackler
|
r43347 | ui.write(b'%s -> %s\n' % (src, dst)) | ||
Martin von Zweigbergk
|
r41921 | |||
Augie Fackler
|
r43346 | |||
@command( | ||||
Augie Fackler
|
r43347 | b'debugp2copies', | ||
[(b'r', b'rev', b'', _(b'revision to debug'), _(b'REV'))], | ||||
_(b'[-r REV]'), | ||||
Augie Fackler
|
r43346 | ) | ||
Manuel Jacob
|
r50176 | def debugp2copies(ui, repo, **opts): | ||
Martin von Zweigbergk
|
r41921 | """dump copy information compared to p2""" | ||
Matt Harbison
|
r51853 | ctx = scmutil.revsingle(repo, opts.get('rev'), default=None) | ||
Martin von Zweigbergk
|
r41921 | for dst, src in ctx.p2copies().items(): | ||
Augie Fackler
|
r43347 | ui.write(b'%s -> %s\n' % (src, dst)) | ||
Martin von Zweigbergk
|
r41921 | |||
Augie Fackler
|
r43346 | |||
@command( | ||||
Augie Fackler
|
r43347 | b'debugpathcomplete', | ||
Augie Fackler
|
r43346 | [ | ||
Augie Fackler
|
r43347 | (b'f', b'full', None, _(b'complete an entire path')), | ||
(b'n', b'normal', None, _(b'show only normal files')), | ||||
(b'a', b'added', None, _(b'show only added files')), | ||||
(b'r', b'removed', None, _(b'show only removed files')), | ||||
Augie Fackler
|
r43346 | ], | ||
Augie Fackler
|
r43347 | _(b'FILESPEC...'), | ||
Augie Fackler
|
r43346 | ) | ||
Pierre-Yves David
|
r30940 | def debugpathcomplete(ui, repo, *specs, **opts): | ||
Augie Fackler
|
r46554 | """complete part or all of a tracked path | ||
Pierre-Yves David
|
r30940 | |||
This command supports shells that offer path name completion. It | ||||
currently completes only files already known to the dirstate. | ||||
Completion extends only to the next path segment unless | ||||
Augie Fackler
|
r46554 | --full is specified, in which case entire paths are used.""" | ||
Pierre-Yves David
|
r30940 | |||
def complete(path, acceptable): | ||||
dirstate = repo.dirstate | ||||
Matt Harbison
|
r39843 | spec = os.path.normpath(os.path.join(encoding.getcwd(), path)) | ||
Pierre-Yves David
|
r30940 | rootdir = repo.root + pycompat.ossep | ||
if spec != repo.root and not spec.startswith(rootdir): | ||||
return [], [] | ||||
if os.path.isdir(spec): | ||||
Augie Fackler
|
r43347 | spec += b'/' | ||
Augie Fackler
|
r43346 | spec = spec[len(rootdir) :] | ||
Augie Fackler
|
r43347 | fixpaths = pycompat.ossep != b'/' | ||
Pierre-Yves David
|
r30940 | if fixpaths: | ||
Augie Fackler
|
r43347 | spec = spec.replace(pycompat.ossep, b'/') | ||
Pierre-Yves David
|
r30940 | speclen = len(spec) | ||
Augie Fackler
|
r43906 | fullpaths = opts['full'] | ||
Pierre-Yves David
|
r30940 | files, dirs = set(), set() | ||
adddir, addfile = dirs.add, files.add | ||||
Gregory Szorc
|
r49768 | for f, st in dirstate.items(): | ||
r48333 | if f.startswith(spec) and st.state in acceptable: | |||
Pierre-Yves David
|
r30940 | if fixpaths: | ||
Augie Fackler
|
r43347 | f = f.replace(b'/', pycompat.ossep) | ||
Pierre-Yves David
|
r30940 | if fullpaths: | ||
addfile(f) | ||||
continue | ||||
s = f.find(pycompat.ossep, speclen) | ||||
if s >= 0: | ||||
adddir(f[:s]) | ||||
else: | ||||
addfile(f) | ||||
return files, dirs | ||||
Augie Fackler
|
r43347 | acceptable = b'' | ||
Augie Fackler
|
r43906 | if opts['normal']: | ||
Augie Fackler
|
r43347 | acceptable += b'nm' | ||
Augie Fackler
|
r43906 | if opts['added']: | ||
Augie Fackler
|
r43347 | acceptable += b'a' | ||
Augie Fackler
|
r43906 | if opts['removed']: | ||
Augie Fackler
|
r43347 | acceptable += b'r' | ||
Pierre-Yves David
|
r30940 | cwd = repo.getcwd() | ||
if not specs: | ||||
Augie Fackler
|
r43347 | specs = [b'.'] | ||
Pierre-Yves David
|
r30940 | |||
files, dirs = set(), set() | ||||
for spec in specs: | ||||
Augie Fackler
|
r43347 | f, d = complete(spec, acceptable or b'nmar') | ||
Pierre-Yves David
|
r30940 | files.update(f) | ||
dirs.update(d) | ||||
files.update(dirs) | ||||
Augie Fackler
|
r43347 | ui.write(b'\n'.join(repo.pathto(p, cwd) for p in sorted(files))) | ||
ui.write(b'\n') | ||||
Pierre-Yves David
|
r30940 | |||
Augie Fackler
|
r43346 | |||
@command( | ||||
Augie Fackler
|
r43347 | b'debugpathcopies', | ||
Augie Fackler
|
r43346 | cmdutil.walkopts, | ||
Augie Fackler
|
r43347 | b'hg debugpathcopies REV1 REV2 [FILE]', | ||
Augie Fackler
|
r43346 | inferrepo=True, | ||
) | ||||
Martin von Zweigbergk
|
r41656 | def debugpathcopies(ui, repo, rev1, rev2, *pats, **opts): | ||
"""show copies between two revisions""" | ||||
ctx1 = scmutil.revsingle(repo, rev1) | ||||
ctx2 = scmutil.revsingle(repo, rev2) | ||||
m = scmutil.match(ctx1, pats, opts) | ||||
Martin von Zweigbergk
|
r41915 | for dst, src in sorted(copies.pathcopies(ctx1, ctx2, m).items()): | ||
Augie Fackler
|
r43347 | ui.write(b'%s -> %s\n' % (src, dst)) | ||
@command(b'debugpeer', [], _(b'PATH'), norepo=True) | ||||
Gregory Szorc
|
r35947 | def debugpeer(ui, path): | ||
"""establish a connection to a peer repository""" | ||||
# Always enable peer request logging. Requires --debug to display | ||||
# though. | ||||
overrides = { | ||||
Augie Fackler
|
r43347 | (b'devel', b'debug.peer-request'): True, | ||
Gregory Szorc
|
r35947 | } | ||
with ui.configoverride(overrides): | ||||
peer = hg.peer(ui, {}, path) | ||||
Valentin Gatien-Baron
|
r47419 | try: | ||
local = peer.local() is not None | ||||
canpush = peer.canpush() | ||||
ui.write(_(b'url: %s\n') % peer.url()) | ||||
ui.write(_(b'local: %s\n') % (_(b'yes') if local else _(b'no'))) | ||||
ui.write( | ||||
_(b'pushable: %s\n') % (_(b'yes') if canpush else _(b'no')) | ||||
) | ||||
finally: | ||||
peer.close() | ||||
Gregory Szorc
|
r35947 | |||
Augie Fackler
|
r43346 | |||
@command( | ||||
Augie Fackler
|
r43347 | b'debugpickmergetool', | ||
Augie Fackler
|
r43346 | [ | ||
Augie Fackler
|
r43347 | (b'r', b'rev', b'', _(b'check for files in this revision'), _(b'REV')), | ||
(b'', b'changedelete', None, _(b'emulate merging change and delete')), | ||||
Augie Fackler
|
r43346 | ] | ||
+ cmdutil.walkopts | ||||
+ cmdutil.mergetoolopts, | ||||
Augie Fackler
|
r43347 | _(b'[PATTERN]...'), | ||
Augie Fackler
|
r43346 | inferrepo=True, | ||
) | ||||
FUJIWARA Katsunori
|
r32256 | def debugpickmergetool(ui, repo, *pats, **opts): | ||
"""examine which merge tool is chosen for specified file | ||||
As described in :hg:`help merge-tools`, Mercurial examines | ||||
configurations below in this order to decide which merge tool is | ||||
chosen for specified file. | ||||
1. ``--tool`` option | ||||
2. ``HGMERGE`` environment variable | ||||
3. configurations in ``merge-patterns`` section | ||||
4. configuration of ``ui.merge`` | ||||
5. configurations in ``merge-tools`` section | ||||
6. ``hgmerge`` tool (for historical reason only) | ||||
7. default tool for fallback (``:merge`` or ``:prompt``) | ||||
This command writes out examination result in the style below:: | ||||
FILE = MERGETOOL | ||||
By default, all files known in the first parent context of the | ||||
working directory are examined. Use file patterns and/or -I/-X | ||||
options to limit target files. -r/--rev is also useful to examine | ||||
files in another context without actual updating to it. | ||||
With --debug, this command shows warning messages while matching | ||||
against ``merge-patterns`` and so on, too. It is recommended to | ||||
use this option with explicit file patterns and/or -I/-X options, | ||||
because this option increases amount of output per file according | ||||
to configurations in hgrc. | ||||
With -v/--verbose, this command shows configurations below at | ||||
first (only if specified). | ||||
- ``--tool`` option | ||||
- ``HGMERGE`` environment variable | ||||
- configuration of ``ui.merge`` | ||||
If merge tool is chosen before matching against | ||||
``merge-patterns``, this command can't show any helpful | ||||
information, even with --debug. In such case, information above is | ||||
useful to know why a merge tool is chosen. | ||||
""" | ||||
overrides = {} | ||||
Matt Harbison
|
r51854 | if opts['tool']: | ||
overrides[(b'ui', b'forcemerge')] = opts['tool'] | ||||
ui.notenoi18n(b'with --tool %r\n' % (pycompat.bytestr(opts['tool']))) | ||||
Augie Fackler
|
r43347 | |||
with ui.configoverride(overrides, b'debugmergepatterns'): | ||||
hgmerge = encoding.environ.get(b"HGMERGE") | ||||
FUJIWARA Katsunori
|
r32256 | if hgmerge is not None: | ||
Augie Fackler
|
r43350 | ui.notenoi18n(b'with HGMERGE=%r\n' % (pycompat.bytestr(hgmerge))) | ||
Augie Fackler
|
r43347 | uimerge = ui.config(b"ui", b"merge") | ||
FUJIWARA Katsunori
|
r32256 | if uimerge: | ||
Augie Fackler
|
r43350 | ui.notenoi18n(b'with ui.merge=%r\n' % (pycompat.bytestr(uimerge))) | ||
Augie Fackler
|
r43347 | |||
Matt Harbison
|
r51854 | ctx = scmutil.revsingle(repo, opts.get('rev')) | ||
m = scmutil.match(ctx, pats, pycompat.byteskwargs(opts)) | ||||
changedelete = opts['changedelete'] | ||||
FUJIWARA Katsunori
|
r32256 | for path in ctx.walk(m): | ||
fctx = ctx[path] | ||||
Martin von Zweigbergk
|
r48230 | with ui.silent( | ||
error=True | ||||
) if not ui.debugflag else util.nullcontextmanager(): | ||||
Augie Fackler
|
r43346 | tool, toolpath = filemerge._picktool( | ||
repo, | ||||
ui, | ||||
path, | ||||
fctx.isbinary(), | ||||
Augie Fackler
|
r43347 | b'l' in fctx.flags(), | ||
Augie Fackler
|
r43346 | changedelete, | ||
) | ||||
Augie Fackler
|
r43347 | ui.write(b'%s = %s\n' % (path, tool)) | ||
@command(b'debugpushkey', [], _(b'REPO NAMESPACE [KEY OLD NEW]'), norepo=True) | ||||
Pierre-Yves David
|
r30946 | def debugpushkey(ui, repopath, namespace, *keyinfo, **opts): | ||
Augie Fackler
|
r46554 | """access the pushkey key/value protocol | ||
Pierre-Yves David
|
r30946 | |||
With two args, list the keys in the given namespace. | ||||
With five args, set a key to new if it currently is set to old. | ||||
Reports success or failure. | ||||
Augie Fackler
|
r46554 | """ | ||
Pierre-Yves David
|
r30946 | |||
target = hg.peer(ui, {}, repopath) | ||||
Valentin Gatien-Baron
|
r47419 | try: | ||
if keyinfo: | ||||
key, old, new = keyinfo | ||||
with target.commandexecutor() as e: | ||||
r = e.callcommand( | ||||
b'pushkey', | ||||
{ | ||||
b'namespace': namespace, | ||||
b'key': key, | ||||
b'old': old, | ||||
b'new': new, | ||||
}, | ||||
).result() | ||||
ui.status(pycompat.bytestr(r) + b'\n') | ||||
return not r | ||||
else: | ||||
Gregory Szorc
|
r49776 | for k, v in sorted(target.listkeys(namespace).items()): | ||
Valentin Gatien-Baron
|
r47419 | ui.write( | ||
b"%s\t%s\n" | ||||
% (stringutil.escapestr(k), stringutil.escapestr(v)) | ||||
) | ||||
finally: | ||||
target.close() | ||||
Augie Fackler
|
r43346 | |||
Pierre-Yves David
|
r30946 | |||
Augie Fackler
|
r43347 | @command(b'debugpvec', [], _(b'A B')) | ||
Pierre-Yves David
|
r30947 | def debugpvec(ui, repo, a, b=None): | ||
ca = scmutil.revsingle(repo, a) | ||||
cb = scmutil.revsingle(repo, b) | ||||
pa = pvec.ctxpvec(ca) | ||||
pb = pvec.ctxpvec(cb) | ||||
if pa == pb: | ||||
Augie Fackler
|
r43347 | rel = b"=" | ||
Pierre-Yves David
|
r30947 | elif pa > pb: | ||
Augie Fackler
|
r43347 | rel = b">" | ||
Pierre-Yves David
|
r30947 | elif pa < pb: | ||
Augie Fackler
|
r43347 | rel = b"<" | ||
Pierre-Yves David
|
r30947 | elif pa | pb: | ||
Augie Fackler
|
r43347 | rel = b"|" | ||
ui.write(_(b"a: %s\n") % pa) | ||||
ui.write(_(b"b: %s\n") % pb) | ||||
ui.write(_(b"depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth)) | ||||
Augie Fackler
|
r43346 | ui.write( | ||
Augie Fackler
|
r43347 | _(b"delta: %d hdist: %d distance: %d relation: %s\n") | ||
Augie Fackler
|
r43346 | % ( | ||
abs(pa._depth - pb._depth), | ||||
pvec._hamming(pa._vec, pb._vec), | ||||
pa.distance(pb), | ||||
rel, | ||||
) | ||||
) | ||||
@command( | ||||
Augie Fackler
|
r43347 | b'debugrebuilddirstate|debugrebuildstate', | ||
Augie Fackler
|
r43346 | [ | ||
Augie Fackler
|
r43347 | (b'r', b'rev', b'', _(b'revision to rebuild to'), _(b'REV')), | ||
Augie Fackler
|
r43346 | ( | ||
Augie Fackler
|
r43347 | b'', | ||
b'minimal', | ||||
Augie Fackler
|
r43346 | None, | ||
_( | ||||
Augie Fackler
|
r43347 | b'only rebuild files that are inconsistent with ' | ||
b'the working copy parent' | ||||
Augie Fackler
|
r43346 | ), | ||
), | ||||
Pierre-Yves David
|
r30948 | ], | ||
Augie Fackler
|
r43347 | _(b'[-r REV]'), | ||
Augie Fackler
|
r43346 | ) | ||
Pierre-Yves David
|
r30948 | def debugrebuilddirstate(ui, repo, rev, **opts): | ||
"""rebuild the dirstate as it would look like for the given revision | ||||
If no revision is specified the first current parent will be used. | ||||
The dirstate will be set to the files of the given revision. | ||||
The actual working directory content or existing dirstate | ||||
information such as adds or removes is not considered. | ||||
``minimal`` will only rebuild the dirstate status for files that claim to be | ||||
tracked but are not in the parent manifest, or that exist in the parent | ||||
manifest but are not in the dirstate. It will not change adds, removes, or | ||||
modified files that are in the working copy parent. | ||||
One use of this command is to make the next :hg:`status` invocation | ||||
check the actual file content. | ||||
""" | ||||
ctx = scmutil.revsingle(repo, rev) | ||||
with repo.wlock(): | ||||
r50895 | if repo.currenttransaction() is not None: | |||
msg = b'rebuild the dirstate outside of a transaction' | ||||
raise error.ProgrammingError(msg) | ||||
Pierre-Yves David
|
r30948 | dirstate = repo.dirstate | ||
changedfiles = None | ||||
# See command doc for what minimal does. | ||||
Augie Fackler
|
r43906 | if opts.get('minimal'): | ||
Pierre-Yves David
|
r30948 | manifestfiles = set(ctx.manifest().keys()) | ||
dirstatefiles = set(dirstate) | ||||
manifestonly = manifestfiles - dirstatefiles | ||||
dsonly = dirstatefiles - manifestfiles | ||||
r48913 | dsnotadded = {f for f in dsonly if not dirstate.get_entry(f).added} | |||
Pierre-Yves David
|
r30948 | changedfiles = manifestonly | dsnotadded | ||
r51008 | with dirstate.changing_parents(repo): | |||
dirstate.rebuild(ctx.node(), ctx.manifest(), changedfiles) | ||||
Pierre-Yves David
|
r30948 | |||
Augie Fackler
|
r43346 | |||
Valentin Gatien-Baron
|
r48689 | @command( | ||
b'debugrebuildfncache', | ||||
[ | ||||
( | ||||
b'', | ||||
b'only-data', | ||||
False, | ||||
_(b'only look for wrong .d files (much faster)'), | ||||
) | ||||
], | ||||
b'', | ||||
) | ||||
def debugrebuildfncache(ui, repo, **opts): | ||||
Pierre-Yves David
|
r30949 | """rebuild the fncache file""" | ||
Matt Harbison
|
r51855 | repair.rebuildfncache(ui, repo, opts.get("only_data")) | ||
Pierre-Yves David
|
r30949 | |||
Augie Fackler
|
r43346 | |||
@command( | ||||
Augie Fackler
|
r43347 | b'debugrename', | ||
[(b'r', b'rev', b'', _(b'revision to debug'), _(b'REV'))], | ||||
_(b'[-r REV] [FILE]...'), | ||||
Augie Fackler
|
r43346 | ) | ||
Martin von Zweigbergk
|
r41839 | def debugrename(ui, repo, *pats, **opts): | ||
Pierre-Yves David
|
r30950 | """dump rename information""" | ||
Matt Harbison
|
r51856 | ctx = scmutil.revsingle(repo, opts.get('rev')) | ||
m = scmutil.match(ctx, pats, pycompat.byteskwargs(opts)) | ||||
Pierre-Yves David
|
r30950 | for abs in ctx.walk(m): | ||
fctx = ctx[abs] | ||||
o = fctx.filelog().renamed(fctx.filenode()) | ||||
Martin von Zweigbergk
|
r41808 | rel = repo.pathto(abs) | ||
Pierre-Yves David
|
r30950 | if o: | ||
Augie Fackler
|
r43347 | ui.write(_(b"%s renamed from %s:%s\n") % (rel, o[0], hex(o[1]))) | ||
Pierre-Yves David
|
r30950 | else: | ||
Augie Fackler
|
r43347 | ui.write(_(b"%s not renamed\n") % rel) | ||
Pierre-Yves David
|
r30950 | |||
Augie Fackler
|
r43346 | |||
Pulkit Goyal
|
r45667 | @command(b'debugrequires|debugrequirements', [], b'') | ||
def debugrequirements(ui, repo): | ||||
Kyle Lippincott
|
r47856 | """print the current repo requirements""" | ||
Pulkit Goyal
|
r45667 | for r in sorted(repo.requirements): | ||
ui.write(b"%s\n" % r) | ||||
Augie Fackler
|
r43346 | @command( | ||
Augie Fackler
|
r43347 | b'debugrevlog', | ||
cmdutil.debugrevlogopts + [(b'd', b'dump', False, _(b'dump index data'))], | ||||
_(b'-c|-m|FILE'), | ||||
Augie Fackler
|
r43346 | optionalrepo=True, | ||
) | ||||
Pierre-Yves David
|
r30951 | def debugrevlog(ui, repo, file_=None, **opts): | ||
"""show data and statistics about a revlog""" | ||||
Matt Harbison
|
r51857 | r = cmdutil.openrevlog( | ||
repo, b'debugrevlog', file_, pycompat.byteskwargs(opts) | ||||
) | ||||
if opts.get("dump"): | ||||
r50554 | revlog_debug.dump(ui, r) | |||
r42757 | else: | |||
r50555 | revlog_debug.debug_revlog(ui, r) | |||
return 0 | ||||
Augie Fackler
|
r43346 | |||
@command( | ||||
Augie Fackler
|
r43347 | b'debugrevlogindex', | ||
Augie Fackler
|
r43346 | cmdutil.debugrevlogopts | ||
Augie Fackler
|
r43347 | + [(b'f', b'format', 0, _(b'revlog format'), _(b'FORMAT'))], | ||
_(b'[-f FORMAT] -c|-m|FILE'), | ||||
Augie Fackler
|
r43346 | optionalrepo=True, | ||
) | ||||
Gregory Szorc
|
r39318 | def debugrevlogindex(ui, repo, file_=None, **opts): | ||
"""dump the contents of a revlog index""" | ||||
Matt Harbison
|
r51858 | r = cmdutil.openrevlog( | ||
repo, b'debugrevlogindex', file_, pycompat.byteskwargs(opts) | ||||
) | ||||
format = opts.get('format', 0) | ||||
Gregory Szorc
|
r39318 | if format not in (0, 1): | ||
Augie Fackler
|
r43347 | raise error.Abort(_(b"unknown format %d") % format) | ||
Gregory Szorc
|
r39318 | |||
if ui.debugflag: | ||||
shortfn = hex | ||||
else: | ||||
shortfn = short | ||||
# There might not be anything in r, so have a sane default | ||||
idlen = 12 | ||||
for i in r: | ||||
idlen = len(shortfn(r.node(i))) | ||||
break | ||||
if format == 0: | ||||
if ui.verbose: | ||||
Martin von Zweigbergk
|
r43386 | ui.writenoi18n( | ||
Martin von Zweigbergk
|
r43387 | b" rev offset length linkrev %s %s p2\n" | ||
Augie Fackler
|
r43347 | % (b"nodeid".ljust(idlen), b"p1".ljust(idlen)) | ||
Augie Fackler
|
r43346 | ) | ||
Gregory Szorc
|
r39318 | else: | ||
Augie Fackler
|
r43350 | ui.writenoi18n( | ||
Augie Fackler
|
r43347 | b" rev linkrev %s %s p2\n" | ||
% (b"nodeid".ljust(idlen), b"p1".ljust(idlen)) | ||||
Augie Fackler
|
r43346 | ) | ||
Gregory Szorc
|
r39318 | elif format == 1: | ||
if ui.verbose: | ||||
Martin von Zweigbergk
|
r43386 | ui.writenoi18n( | ||
Augie Fackler
|
r43346 | ( | ||
Augie Fackler
|
r43347 | b" rev flag offset length size link p1" | ||
b" p2 %s\n" | ||||
Augie Fackler
|
r43346 | ) | ||
Augie Fackler
|
r43347 | % b"nodeid".rjust(idlen) | ||
Augie Fackler
|
r43346 | ) | ||
Gregory Szorc
|
r39318 | else: | ||
Augie Fackler
|
r43350 | ui.writenoi18n( | ||
Augie Fackler
|
r43347 | b" rev flag size link p1 p2 %s\n" | ||
% b"nodeid".rjust(idlen) | ||||
Augie Fackler
|
r43346 | ) | ||
Gregory Szorc
|
r39318 | |||
for i in r: | ||||
node = r.node(i) | ||||
if format == 0: | ||||
try: | ||||
pp = r.parents(node) | ||||
except Exception: | ||||
Joerg Sonnenberger
|
r47771 | pp = [repo.nullid, repo.nullid] | ||
Gregory Szorc
|
r39318 | if ui.verbose: | ||
Augie Fackler
|
r43346 | ui.write( | ||
Augie Fackler
|
r43347 | b"% 6d % 9d % 7d % 7d %s %s %s\n" | ||
Augie Fackler
|
r43346 | % ( | ||
i, | ||||
r.start(i), | ||||
r.length(i), | ||||
r.linkrev(i), | ||||
shortfn(node), | ||||
shortfn(pp[0]), | ||||
shortfn(pp[1]), | ||||
) | ||||
) | ||||
Gregory Szorc
|
r39318 | else: | ||
Augie Fackler
|
r43346 | ui.write( | ||
Augie Fackler
|
r43347 | b"% 6d % 7d %s %s %s\n" | ||
Augie Fackler
|
r43346 | % ( | ||
i, | ||||
r.linkrev(i), | ||||
shortfn(node), | ||||
shortfn(pp[0]), | ||||
shortfn(pp[1]), | ||||
) | ||||
) | ||||
Gregory Szorc
|
r39318 | elif format == 1: | ||
pr = r.parentrevs(i) | ||||
if ui.verbose: | ||||
Augie Fackler
|
r43346 | ui.write( | ||
Augie Fackler
|
r43347 | b"% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d %s\n" | ||
Augie Fackler
|
r43346 | % ( | ||
i, | ||||
r.flags(i), | ||||
r.start(i), | ||||
r.length(i), | ||||
r.rawsize(i), | ||||
r.linkrev(i), | ||||
pr[0], | ||||
pr[1], | ||||
shortfn(node), | ||||
) | ||||
) | ||||
Gregory Szorc
|
r39318 | else: | ||
Augie Fackler
|
r43346 | ui.write( | ||
Augie Fackler
|
r43347 | b"% 6d %04x % 8d % 6d % 6d % 6d %s\n" | ||
Augie Fackler
|
r43346 | % ( | ||
i, | ||||
r.flags(i), | ||||
r.rawsize(i), | ||||
r.linkrev(i), | ||||
pr[0], | ||||
pr[1], | ||||
shortfn(node), | ||||
) | ||||
) | ||||
@command( | ||||
Augie Fackler
|
r43347 | b'debugrevspec', | ||
Augie Fackler
|
r43346 | [ | ||
( | ||||
Augie Fackler
|
r43347 | b'', | ||
b'optimize', | ||||
Augie Fackler
|
r43346 | None, | ||
Augie Fackler
|
r43347 | _(b'print parsed tree after optimizing (DEPRECATED)'), | ||
Augie Fackler
|
r43346 | ), | ||
( | ||||
Augie Fackler
|
r43347 | b'', | ||
b'show-revs', | ||||
True, | ||||
_(b'print list of result revisions (default)'), | ||||
Augie Fackler
|
r43346 | ), | ||
( | ||||
Augie Fackler
|
r43347 | b's', | ||
b'show-set', | ||||
None, | ||||
_(b'print internal representation of result set'), | ||||
), | ||||
( | ||||
b'p', | ||||
b'show-stage', | ||||
Augie Fackler
|
r43346 | [], | ||
Augie Fackler
|
r43347 | _(b'print parsed tree at the given stage'), | ||
_(b'NAME'), | ||||
Augie Fackler
|
r43346 | ), | ||
Augie Fackler
|
r43347 | (b'', b'no-optimized', False, _(b'evaluate tree without optimization')), | ||
(b'', b'verify-optimized', False, _(b'verify optimized result')), | ||||
Augie Fackler
|
r43346 | ], | ||
Augie Fackler
|
r43347 | b'REVSPEC', | ||
Augie Fackler
|
r43346 | ) | ||
Pierre-Yves David
|
r30952 | def debugrevspec(ui, repo, expr, **opts): | ||
"""parse and apply a revision specification | ||||
Use -p/--show-stage option to print the parsed tree at the given stages. | ||||
Use -p all to print tree at every stage. | ||||
Yuya Nishihara
|
r32796 | Use --no-show-revs option with -s or -p to print only the set | ||
representation or the parsed tree respectively. | ||||
Pierre-Yves David
|
r30952 | Use --verify-optimized to compare the optimized result with the unoptimized | ||
one. Returns 1 if the optimized result differs. | ||||
""" | ||||
Augie Fackler
|
r43347 | aliases = ui.configitems(b'revsetalias') | ||
Pierre-Yves David
|
r30952 | stages = [ | ||
Augie Fackler
|
r43347 | (b'parsed', lambda tree: tree), | ||
Augie Fackler
|
r43346 | ( | ||
Augie Fackler
|
r43347 | b'expanded', | ||
Augie Fackler
|
r43346 | lambda tree: revsetlang.expandaliases(tree, aliases, ui.warn), | ||
), | ||||
Augie Fackler
|
r43347 | (b'concatenated', revsetlang.foldconcat), | ||
(b'analyzed', revsetlang.analyze), | ||||
(b'optimized', revsetlang.optimize), | ||||
Pierre-Yves David
|
r30952 | ] | ||
Matt Harbison
|
r51859 | if opts['no_optimized']: | ||
Pierre-Yves David
|
r30952 | stages = stages[:-1] | ||
Matt Harbison
|
r51859 | if opts['verify_optimized'] and opts['no_optimized']: | ||
Augie Fackler
|
r43346 | raise error.Abort( | ||
Martin von Zweigbergk
|
r43387 | _(b'cannot use --verify-optimized with --no-optimized') | ||
Augie Fackler
|
r43346 | ) | ||
Augie Fackler
|
r44937 | stagenames = {n for n, f in stages} | ||
Pierre-Yves David
|
r30952 | |||
showalways = set() | ||||
showchanged = set() | ||||
Matt Harbison
|
r51859 | if ui.verbose and not opts['show_stage']: | ||
Pierre-Yves David
|
r30952 | # show parsed tree by --verbose (deprecated) | ||
Augie Fackler
|
r43347 | showalways.add(b'parsed') | ||
showchanged.update([b'expanded', b'concatenated']) | ||||
Matt Harbison
|
r51859 | if opts['optimize']: | ||
Augie Fackler
|
r43347 | showalways.add(b'optimized') | ||
Matt Harbison
|
r51859 | if opts['show_stage'] and opts['optimize']: | ||
Augie Fackler
|
r43347 | raise error.Abort(_(b'cannot use --optimize with --show-stage')) | ||
Matt Harbison
|
r51859 | if opts['show_stage'] == [b'all']: | ||
Pierre-Yves David
|
r30952 | showalways.update(stagenames) | ||
else: | ||||
Matt Harbison
|
r51859 | for n in opts['show_stage']: | ||
Pierre-Yves David
|
r30952 | if n not in stagenames: | ||
Augie Fackler
|
r43347 | raise error.Abort(_(b'invalid stage name: %s') % n) | ||
Matt Harbison
|
r51859 | showalways.update(opts['show_stage']) | ||
Pierre-Yves David
|
r30952 | |||
treebystage = {} | ||||
printedtree = None | ||||
Martin von Zweigbergk
|
r37368 | tree = revsetlang.parse(expr, lookup=revset.lookupfn(repo)) | ||
Pierre-Yves David
|
r30952 | for n, f in stages: | ||
treebystage[n] = tree = f(tree) | ||||
if n in showalways or (n in showchanged and tree != printedtree): | ||||
Matt Harbison
|
r51859 | if opts['show_stage'] or n != b'parsed': | ||
Augie Fackler
|
r43347 | ui.write(b"* %s:\n" % n) | ||
ui.write(revsetlang.prettyformat(tree), b"\n") | ||||
Pierre-Yves David
|
r30952 | printedtree = tree | ||
Matt Harbison
|
r51859 | if opts['verify_optimized']: | ||
Augie Fackler
|
r43347 | arevs = revset.makematcher(treebystage[b'analyzed'])(repo) | ||
brevs = revset.makematcher(treebystage[b'optimized'])(repo) | ||||
Matt Harbison
|
r51859 | if opts['show_set'] or (opts['show_set'] is None and ui.verbose): | ||
r43364 | ui.writenoi18n( | |||
b"* analyzed set:\n", stringutil.prettyrepr(arevs), b"\n" | ||||
) | ||||
ui.writenoi18n( | ||||
b"* optimized set:\n", stringutil.prettyrepr(brevs), b"\n" | ||||
) | ||||
Pierre-Yves David
|
r30952 | arevs = list(arevs) | ||
brevs = list(brevs) | ||||
if arevs == brevs: | ||||
return 0 | ||||
Augie Fackler
|
r43350 | ui.writenoi18n(b'--- analyzed\n', label=b'diff.file_a') | ||
ui.writenoi18n(b'+++ optimized\n', label=b'diff.file_b') | ||||
Pierre-Yves David
|
r30952 | sm = difflib.SequenceMatcher(None, arevs, brevs) | ||
for tag, alo, ahi, blo, bhi in sm.get_opcodes(): | ||||
Augie Fackler
|
r43906 | if tag in ('delete', 'replace'): | ||
Pierre-Yves David
|
r30952 | for c in arevs[alo:ahi]: | ||
Augie Fackler
|
r43347 | ui.write(b'-%d\n' % c, label=b'diff.deleted') | ||
Augie Fackler
|
r43906 | if tag in ('insert', 'replace'): | ||
Pierre-Yves David
|
r30952 | for c in brevs[blo:bhi]: | ||
Augie Fackler
|
r43347 | ui.write(b'+%d\n' % c, label=b'diff.inserted') | ||
Augie Fackler
|
r43906 | if tag == 'equal': | ||
Pierre-Yves David
|
r30952 | for c in arevs[alo:ahi]: | ||
Augie Fackler
|
r43347 | ui.write(b' %d\n' % c) | ||
Pierre-Yves David
|
r30952 | return 1 | ||
func = revset.makematcher(tree) | ||||
revs = func(repo) | ||||
Matt Harbison
|
r51859 | if opts['show_set'] or (opts['show_set'] is None and ui.verbose): | ||
Augie Fackler
|
r43350 | ui.writenoi18n(b"* set:\n", stringutil.prettyrepr(revs), b"\n") | ||
Matt Harbison
|
r51859 | if not opts['show_revs']: | ||
Yuya Nishihara
|
r32796 | return | ||
Pierre-Yves David
|
r30952 | for c in revs: | ||
Augie Fackler
|
r43347 | ui.write(b"%d\n" % c) | ||
Pierre-Yves David
|
r30952 | |||
Augie Fackler
|
r43346 | |||
@command( | ||||
Augie Fackler
|
r43347 | b'debugserve', | ||
Augie Fackler
|
r43346 | [ | ||
( | ||||
Augie Fackler
|
r43347 | b'', | ||
b'sshstdio', | ||||
Augie Fackler
|
r43346 | False, | ||
Augie Fackler
|
r43347 | _(b'run an SSH server bound to process handles'), | ||
Augie Fackler
|
r43346 | ), | ||
Augie Fackler
|
r43347 | (b'', b'logiofd', b'', _(b'file descriptor to log server I/O to')), | ||
(b'', b'logiofile', b'', _(b'file to log server I/O to')), | ||||
Augie Fackler
|
r43346 | ], | ||
Augie Fackler
|
r43347 | b'', | ||
Augie Fackler
|
r43346 | ) | ||
Gregory Szorc
|
r36544 | def debugserve(ui, repo, **opts): | ||
"""run a server with advanced settings | ||||
This command is similar to :hg:`serve`. It exists partially as a | ||||
workaround to the fact that ``hg serve --stdio`` must have specific | ||||
arguments for security reasons. | ||||
""" | ||||
Matt Harbison
|
r51860 | if not opts['sshstdio']: | ||
Augie Fackler
|
r43347 | raise error.Abort(_(b'only --sshstdio is currently supported')) | ||
Gregory Szorc
|
r36544 | |||
logfh = None | ||||
Matt Harbison
|
r51860 | if opts['logiofd'] and opts['logiofile']: | ||
Augie Fackler
|
r43347 | raise error.Abort(_(b'cannot use both --logiofd and --logiofile')) | ||
Matt Harbison
|
r51860 | if opts['logiofd']: | ||
Gregory Szorc
|
r44582 | # Ideally we would be line buffered. But line buffering in binary | ||
# mode isn't supported and emits a warning in Python 3.8+. Disabling | ||||
# buffering could have performance impacts. But since this isn't | ||||
# performance critical code, it should be fine. | ||||
Augie Fackler
|
r38333 | try: | ||
Matt Harbison
|
r51860 | logfh = os.fdopen(int(opts['logiofd']), 'ab', 0) | ||
Augie Fackler
|
r38333 | except OSError as e: | ||
if e.errno != errno.ESPIPE: | ||||
raise | ||||
# can't seek a pipe, so `ab` mode fails on py3 | ||||
Matt Harbison
|
r51860 | logfh = os.fdopen(int(opts['logiofd']), 'wb', 0) | ||
elif opts['logiofile']: | ||||
logfh = open(opts['logiofile'], b'ab', 0) | ||||
Gregory Szorc
|
r36544 | |||
s = wireprotoserver.sshserver(ui, repo, logfh=logfh) | ||||
s.serve_forever() | ||||
Augie Fackler
|
r43346 | |||
Augie Fackler
|
r43347 | @command(b'debugsetparents', [], _(b'REV1 [REV2]')) | ||
Pierre-Yves David
|
r30953 | def debugsetparents(ui, repo, rev1, rev2=None): | ||
r46817 | """manually set the parents of the current working directory (DANGEROUS) | |||
This command is not what you are looking for and should not be used. Using | ||||
this command will most certainly results in slight corruption of the file | ||||
Jordi Gutiérrez Hermoso
|
r52270 | level histories within your repository. DO NOT USE THIS COMMAND. | ||
The command updates the p1 and p2 fields in the dirstate, without touching | ||||
r46817 | anything else. This useful for writing repository conversion tools, but | |||
should be used with extreme care. For example, neither the working | ||||
Jordi Gutiérrez Hermoso
|
r52270 | directory nor the dirstate is updated, so file statuses may be incorrect | ||
after running this command. Use it only if you are one of the few people who | ||||
deeply understands both conversion tools and file level histories. If you are | ||||
reading this help, you are not one of those people (most of them sailed west | ||||
from Mithlond anyway). | ||||
So, one more time, DO NOT USE THIS COMMAND. | ||||
Pierre-Yves David
|
r30953 | |||
Returns 0 on success. | ||||
""" | ||||
Martin von Zweigbergk
|
r37161 | node1 = scmutil.revsingle(repo, rev1).node() | ||
Augie Fackler
|
r43347 | node2 = scmutil.revsingle(repo, rev2, b'null').node() | ||
Pierre-Yves David
|
r30953 | |||
with repo.wlock(): | ||||
Martin von Zweigbergk
|
r37161 | repo.setparents(node1, node2) | ||
Pierre-Yves David
|
r30953 | |||
Augie Fackler
|
r43346 | |||
Augie Fackler
|
r43347 | @command(b'debugsidedata', cmdutil.debugrevlogopts, _(b'-c|-m|FILE REV')) | ||
r43309 | def debugsidedata(ui, repo, file_, rev=None, **opts): | |||
r43406 | """dump the side data for a cl/manifest/file revision | |||
Use --verbose to dump the sidedata content.""" | ||||
Matt Harbison
|
r51861 | if opts.get('changelog') or opts.get('manifest') or opts.get('dir'): | ||
r43309 | if rev is not None: | |||
r51440 | raise error.InputError( | |||
_(b'cannot specify a revision with other arguments') | ||||
) | ||||
r43309 | file_, rev = None, file_ | |||
elif rev is None: | ||||
r51440 | raise error.InputError(_(b'please specify a revision')) | |||
Matt Harbison
|
r51861 | r = cmdutil.openstorage( | ||
repo, b'debugdata', file_, pycompat.byteskwargs(opts) | ||||
) | ||||
r43309 | r = getattr(r, '_revlog', r) | |||
try: | ||||
sidedata = r.sidedata(r.lookup(rev)) | ||||
except KeyError: | ||||
Augie Fackler
|
r43347 | raise error.Abort(_(b'invalid revision identifier %s') % rev) | ||
r43309 | if sidedata: | |||
sidedata = list(sidedata.items()) | ||||
sidedata.sort() | ||||
Martin von Zweigbergk
|
r43386 | ui.writenoi18n(b'%d sidedata entries\n' % len(sidedata)) | ||
r43309 | for key, value in sidedata: | |||
Martin von Zweigbergk
|
r43386 | ui.writenoi18n(b' entry-%04o size %d\n' % (key, len(value))) | ||
r43309 | if ui.verbose: | |||
Martin von Zweigbergk
|
r43386 | ui.writenoi18n(b' %s\n' % stringutil.pprint(value)) | ||
Augie Fackler
|
r43347 | |||
@command(b'debugssl', [], b'[SOURCE]', optionalrepo=True) | ||||
Matt Harbison
|
r33493 | def debugssl(ui, repo, source=None, **opts): | ||
Augie Fackler
|
r46554 | """test a secure connection to a server | ||
Matt Harbison
|
r33493 | |||
This builds the certificate chain for the server on Windows, installing the | ||||
missing intermediates and trusted root via Windows Update if necessary. It | ||||
does nothing on other platforms. | ||||
If SOURCE is omitted, the 'default' path will be used. If a URL is given, | ||||
that server is used. See :hg:`help urls` for more information. | ||||
If the update succeeds, retry the original operation. Otherwise, the cause | ||||
of the SSL error is likely another issue. | ||||
Augie Fackler
|
r46554 | """ | ||
Jun Wu
|
r34646 | if not pycompat.iswindows: | ||
Augie Fackler
|
r43346 | raise error.Abort( | ||
Martin von Zweigbergk
|
r43387 | _(b'certificate chain building is only possible on Windows') | ||
Augie Fackler
|
r43346 | ) | ||
Matt Harbison
|
r33493 | |||
if not source: | ||||
Matt Harbison
|
r34031 | if not repo: | ||
Augie Fackler
|
r43346 | raise error.Abort( | ||
_( | ||||
Augie Fackler
|
r43347 | b"there is no Mercurial repository here, and no " | ||
b"server specified" | ||||
Augie Fackler
|
r43346 | ) | ||
) | ||||
Augie Fackler
|
r43347 | source = b"default" | ||
Matt Harbison
|
r33493 | |||
r50622 | path = urlutil.get_unique_pull_path_obj(b'debugssl', ui, source) | |||
url = path.url | ||||
Matt Harbison
|
r33493 | |||
Augie Fackler
|
r43347 | defaultport = {b'https': 443, b'ssh': 22} | ||
Yuya Nishihara
|
r35444 | if url.scheme in defaultport: | ||
try: | ||||
addr = (url.host, int(url.port or defaultport[url.scheme])) | ||||
except ValueError: | ||||
Augie Fackler
|
r43347 | raise error.Abort(_(b"malformed port number in URL")) | ||
Matt Harbison
|
r33493 | else: | ||
Augie Fackler
|
r43347 | raise error.Abort(_(b"only https and ssh connections are supported")) | ||
Matt Harbison
|
r33493 | |||
from . import win32 | ||||
Augie Fackler
|
r43346 | s = ssl.wrap_socket( | ||
socket.socket(), | ||||
ssl_version=ssl.PROTOCOL_TLS, | ||||
cert_reqs=ssl.CERT_NONE, | ||||
ca_certs=None, | ||||
) | ||||
Matt Harbison
|
r33493 | |||
try: | ||||
s.connect(addr) | ||||
cert = s.getpeercert(True) | ||||
Augie Fackler
|
r43347 | ui.status(_(b'checking the certificate chain for %s\n') % url.host) | ||
Matt Harbison
|
r33493 | |||
complete = win32.checkcertificatechain(cert, build=False) | ||||
if not complete: | ||||
Augie Fackler
|
r43347 | ui.status(_(b'certificate chain is incomplete, updating... ')) | ||
Matt Harbison
|
r33493 | |||
if not win32.checkcertificatechain(cert): | ||||
Augie Fackler
|
r43347 | ui.status(_(b'failed.\n')) | ||
Matt Harbison
|
r33493 | else: | ||
Augie Fackler
|
r43347 | ui.status(_(b'done.\n')) | ||
Matt Harbison
|
r33493 | else: | ||
Augie Fackler
|
r43347 | ui.status(_(b'full certificate chain is available\n')) | ||
Matt Harbison
|
r33493 | finally: | ||
s.close() | ||||
Augie Fackler
|
r43346 | |||
@command( | ||||
pacien
|
r51288 | b'debug::stable-tail-sort', | ||
[ | ||||
( | ||||
b'T', | ||||
b'template', | ||||
b'{rev}\n', | ||||
_(b'display with template'), | ||||
_(b'TEMPLATE'), | ||||
), | ||||
], | ||||
b'REV', | ||||
) | ||||
def debug_stable_tail_sort(ui, repo, revspec, template, **opts): | ||||
"""display the stable-tail sort of the ancestors of a given node""" | ||||
rev = logcmdutil.revsingle(repo, revspec).rev() | ||||
cl = repo.changelog | ||||
displayer = logcmdutil.maketemplater(ui, repo, template) | ||||
pacien
|
r51418 | sorted_revs = stabletailsort._stable_tail_sort_naive(cl, rev) | ||
pacien
|
r51288 | for ancestor_rev in sorted_revs: | ||
displayer.show(repo[ancestor_rev]) | ||||
@command( | ||||
pacien
|
r51421 | b'debug::stable-tail-sort-leaps', | ||
[ | ||||
( | ||||
b'T', | ||||
b'template', | ||||
b'{rev}', | ||||
_(b'display with template'), | ||||
_(b'TEMPLATE'), | ||||
), | ||||
(b's', b'specific', False, _(b'restrict to specific leaps')), | ||||
], | ||||
b'REV', | ||||
) | ||||
def debug_stable_tail_sort_leaps(ui, repo, rspec, template, specific, **opts): | ||||
"""display the leaps in the stable-tail sort of a node, one per line""" | ||||
rev = logcmdutil.revsingle(repo, rspec).rev() | ||||
if specific: | ||||
get_leaps = stabletailsort._find_specific_leaps_naive | ||||
else: | ||||
get_leaps = stabletailsort._find_all_leaps_naive | ||||
displayer = logcmdutil.maketemplater(ui, repo, template) | ||||
for source, target in get_leaps(repo.changelog, rev): | ||||
displayer.show(repo[source]) | ||||
displayer.show(repo[target]) | ||||
ui.write(b'\n') | ||||
@command( | ||||
Pulkit Goyal
|
r44915 | b"debugbackupbundle", | ||
[ | ||||
( | ||||
b"", | ||||
b"recover", | ||||
b"", | ||||
b"brings the specified changeset back into the repository", | ||||
) | ||||
] | ||||
+ cmdutil.logopts, | ||||
_(b"hg debugbackupbundle [--recover HASH]"), | ||||
) | ||||
def debugbackupbundle(ui, repo, *pats, **opts): | ||||
"""lists the changesets available in backup bundles | ||||
Without any arguments, this command prints a list of the changesets in each | ||||
backup bundle. | ||||
--recover takes a changeset hash and unbundles the first bundle that | ||||
contains that hash, which puts that changeset back in your repository. | ||||
--verbose will print the entire commit message and the bundle path for that | ||||
backup. | ||||
""" | ||||
backups = list( | ||||
filter( | ||||
os.path.isfile, glob.glob(repo.vfs.join(b"strip-backup") + b"/*.hg") | ||||
) | ||||
) | ||||
backups.sort(key=lambda x: os.path.getmtime(x), reverse=True) | ||||
Matt Harbison
|
r51862 | opts["bundle"] = b"" | ||
opts["force"] = None | ||||
limit = logcmdutil.getlimit(pycompat.byteskwargs(opts)) | ||||
Pulkit Goyal
|
r44915 | |||
def display(other, chlist, displayer): | ||||
Matt Harbison
|
r51862 | if opts.get("newest_first"): | ||
Pulkit Goyal
|
r44915 | chlist.reverse() | ||
count = 0 | ||||
for n in chlist: | ||||
if limit is not None and count >= limit: | ||||
break | ||||
Joerg Sonnenberger
|
r47771 | parents = [ | ||
True for p in other.changelog.parents(n) if p != repo.nullid | ||||
] | ||||
Matt Harbison
|
r51862 | if opts.get("no_merges") and len(parents) == 2: | ||
Pulkit Goyal
|
r44915 | continue | ||
count += 1 | ||||
displayer.show(other[n]) | ||||
Matt Harbison
|
r51862 | recovernode = opts.get("recover") | ||
Pulkit Goyal
|
r44915 | if recovernode: | ||
if scmutil.isrevsymbol(repo, recovernode): | ||||
ui.warn(_(b"%s already exists in the repo\n") % recovernode) | ||||
return | ||||
elif backups: | ||||
msg = _( | ||||
b"Recover changesets using: hg debugbackupbundle --recover " | ||||
b"<changeset hash>\n\nAvailable backup changesets:" | ||||
) | ||||
ui.status(msg, label=b"status.removed") | ||||
else: | ||||
ui.status(_(b"no backup changesets found\n")) | ||||
return | ||||
for backup in backups: | ||||
# Much of this is copied from the hg incoming logic | ||||
r47723 | source = os.path.relpath(backup, encoding.getcwd()) | |||
r50623 | path = urlutil.get_unique_pull_path_obj( | |||
r47723 | b'debugbackupbundle', | |||
ui, | ||||
source, | ||||
) | ||||
Pulkit Goyal
|
r44915 | try: | ||
Matt Harbison
|
r51862 | other = hg.peer(repo, pycompat.byteskwargs(opts), path) | ||
Pulkit Goyal
|
r44915 | except error.LookupError as ex: | ||
r50623 | msg = _(b"\nwarning: unable to open bundle %s") % path.loc | |||
Pulkit Goyal
|
r44915 | hint = _(b"\n(missing parent rev %s)\n") % short(ex.name) | ||
ui.warn(msg, hint=hint) | ||||
continue | ||||
Matt Harbison
|
r51862 | branches = (path.branch, opts.get('branch', [])) | ||
Pulkit Goyal
|
r44915 | revs, checkout = hg.addbranchrevs( | ||
Matt Harbison
|
r51862 | repo, other, branches, opts.get("rev") | ||
Pulkit Goyal
|
r44915 | ) | ||
if revs: | ||||
revs = [other.lookup(rev) for rev in revs] | ||||
Martin von Zweigbergk
|
r48231 | with ui.silent(): | ||
try: | ||||
other, chlist, cleanupfn = bundlerepo.getremotechanges( | ||||
Matt Harbison
|
r51862 | ui, repo, other, revs, opts["bundle"], opts["force"] | ||
Martin von Zweigbergk
|
r48231 | ) | ||
except error.LookupError: | ||||
continue | ||||
Pulkit Goyal
|
r44915 | |||
try: | ||||
if not chlist: | ||||
continue | ||||
if recovernode: | ||||
with repo.lock(), repo.transaction(b"unbundle") as tr: | ||||
if scmutil.isrevsymbol(other, recovernode): | ||||
ui.status(_(b"Unbundling %s\n") % (recovernode)) | ||||
r50623 | f = hg.openpath(ui, path.loc) | |||
gen = exchange.readbundle(ui, f, path.loc) | ||||
Pulkit Goyal
|
r44915 | if isinstance(gen, bundle2.unbundle20): | ||
bundle2.applybundle( | ||||
repo, | ||||
gen, | ||||
tr, | ||||
source=b"unbundle", | ||||
r50623 | url=b"bundle:" + path.loc, | |||
Pulkit Goyal
|
r44915 | ) | ||
else: | ||||
r50623 | gen.apply(repo, b"unbundle", b"bundle:" + path.loc) | |||
Pulkit Goyal
|
r44915 | break | ||
else: | ||||
backupdate = encoding.strtolocal( | ||||
time.strftime( | ||||
"%a %H:%M, %Y-%m-%d", | ||||
r50623 | time.localtime(os.path.getmtime(path.loc)), | |||
Pulkit Goyal
|
r44915 | ) | ||
) | ||||
ui.status(b"\n%s\n" % (backupdate.ljust(50))) | ||||
if ui.verbose: | ||||
r50623 | ui.status(b"%s%s\n" % (b"bundle:".ljust(13), path.loc)) | |||
Pulkit Goyal
|
r44915 | else: | ||
opts[ | ||||
Matt Harbison
|
r51862 | "template" | ||
Pulkit Goyal
|
r44915 | ] = b"{label('status.modified', node|short)} {desc|firstline}\n" | ||
displayer = logcmdutil.changesetdisplayer( | ||||
Matt Harbison
|
r51862 | ui, other, pycompat.byteskwargs(opts), False | ||
Pulkit Goyal
|
r44915 | ) | ||
display(other, chlist, displayer) | ||||
displayer.close() | ||||
finally: | ||||
cleanupfn() | ||||
@command( | ||||
Augie Fackler
|
r43347 | b'debugsub', | ||
[(b'r', b'rev', b'', _(b'revision to check'), _(b'REV'))], | ||||
_(b'[-r REV] [REV]'), | ||||
Augie Fackler
|
r43346 | ) | ||
Pierre-Yves David
|
r30955 | def debugsub(ui, repo, rev=None): | ||
ctx = scmutil.revsingle(repo, rev, None) | ||||
for k, v in sorted(ctx.substate.items()): | ||||
Augie Fackler
|
r43350 | ui.writenoi18n(b'path %s\n' % k) | ||
ui.writenoi18n(b' source %s\n' % v[0]) | ||||
ui.writenoi18n(b' revision %s\n' % v[1]) | ||||
Augie Fackler
|
r43346 | |||
Arun Kulshreshtha
|
r50839 | @command( | ||
b'debugshell', | ||||
[ | ||||
( | ||||
b'c', | ||||
b'command', | ||||
b'', | ||||
_(b'program passed in as a string'), | ||||
_(b'COMMAND'), | ||||
) | ||||
], | ||||
_(b'[-c COMMAND]'), | ||||
optionalrepo=True, | ||||
) | ||||
def debugshell(ui, repo, **opts): | ||||
Joerg Sonnenberger
|
r47121 | """run an interactive Python interpreter | ||
The local namespace is provided with a reference to the ui and | ||||
the repo instance (if available). | ||||
""" | ||||
import code | ||||
imported_objects = { | ||||
'ui': ui, | ||||
'repo': repo, | ||||
} | ||||
Matt Harbison
|
r50791 | # py2exe disables initialization of the site module, which is responsible | ||
# for arranging for ``quit()`` to exit the interpreter. Manually initialize | ||||
# the stuff that site normally does here, so that the interpreter can be | ||||
# quit in a consistent manner, whether run with pyoxidizer, exewrapper.c, | ||||
# py.exe, or py2exe. | ||||
if getattr(sys, "frozen", None) == 'console_exe': | ||||
try: | ||||
import site | ||||
site.setcopyright() | ||||
site.sethelper() | ||||
site.setquit() | ||||
except ImportError: | ||||
site = None # Keep PyCharm happy | ||||
Arun Kulshreshtha
|
r50839 | command = opts.get('command') | ||
if command: | ||||
compiled = code.compile_command(encoding.strfromlocal(command)) | ||||
code.InteractiveInterpreter(locals=imported_objects).runcode(compiled) | ||||
return | ||||
Joerg Sonnenberger
|
r47121 | code.interact(local=imported_objects) | ||
Augie Fackler
|
r43346 | @command( | ||
Franck Bret
|
r50714 | b'debug-revlog-stats', | ||
[ | ||||
(b'c', b'changelog', None, _(b'Display changelog statistics')), | ||||
(b'm', b'manifest', None, _(b'Display manifest statistics')), | ||||
(b'f', b'filelogs', None, _(b'Display filelogs statistics')), | ||||
] | ||||
+ cmdutil.formatteropts, | ||||
) | ||||
def debug_revlog_stats(ui, repo, **opts): | ||||
"""display statistics about revlogs in the store""" | ||||
Matt Harbison
|
r51863 | changelog = opts["changelog"] | ||
manifest = opts["manifest"] | ||||
filelogs = opts["filelogs"] | ||||
Franck Bret
|
r50714 | |||
if changelog is None and manifest is None and filelogs is None: | ||||
changelog = True | ||||
manifest = True | ||||
filelogs = True | ||||
repo = repo.unfiltered() | ||||
Matt Harbison
|
r51863 | fm = ui.formatter(b'debug-revlog-stats', pycompat.byteskwargs(opts)) | ||
Franck Bret
|
r50714 | revlog_debug.debug_revlog_stats(repo, fm, changelog, manifest, filelogs) | ||
fm.end() | ||||
@command( | ||||
Augie Fackler
|
r43347 | b'debugsuccessorssets', | ||
[(b'', b'closest', False, _(b'return closest successors sets only'))], | ||||
_(b'[REV]'), | ||||
Augie Fackler
|
r43346 | ) | ||
Boris Feld
|
r33274 | def debugsuccessorssets(ui, repo, *revs, **opts): | ||
Pierre-Yves David
|
r30956 | """show set of successors for revision | ||
A successors set of changeset A is a consistent group of revisions that | ||||
Boris Feld
|
r33274 | succeed A. It contains non-obsolete changesets only unless closests | ||
successors set is set. | ||||
Pierre-Yves David
|
r30956 | |||
In most cases a changeset A has a single successors set containing a single | ||||
successor (changeset A replaced by A'). | ||||
A changeset that is made obsolete with no successors are called "pruned". | ||||
Such changesets have no successors sets at all. | ||||
A changeset that has been "split" will have a successors set containing | ||||
more than one successor. | ||||
A changeset that has been rewritten in multiple different ways is called | ||||
"divergent". Such changesets have multiple successor sets (each of which | ||||
may also be split, i.e. have multiple successors). | ||||
Results are displayed as follows:: | ||||
<rev1> | ||||
<successors-1A> | ||||
<rev2> | ||||
<successors-2A> | ||||
<successors-2B1> <successors-2B2> <successors-2B3> | ||||
Here rev2 has two possible (i.e. divergent) successors sets. The first | ||||
holds one element, whereas the second holds three (i.e. the changeset has | ||||
been split). | ||||
""" | ||||
# passed to successorssets caching computation from one call to another | ||||
cache = {} | ||||
Gregory Szorc
|
r36140 | ctx2str = bytes | ||
Pierre-Yves David
|
r30956 | node2str = short | ||
Martin von Zweigbergk
|
r48928 | for rev in logcmdutil.revrange(repo, revs): | ||
Pierre-Yves David
|
r30956 | ctx = repo[rev] | ||
Augie Fackler
|
r43347 | ui.write(b'%s\n' % ctx2str(ctx)) | ||
Augie Fackler
|
r43346 | for succsset in obsutil.successorssets( | ||
Augie Fackler
|
r43906 | repo, ctx.node(), closest=opts['closest'], cache=cache | ||
Augie Fackler
|
r43346 | ): | ||
Pierre-Yves David
|
r30956 | if succsset: | ||
Augie Fackler
|
r43347 | ui.write(b' ') | ||
Pierre-Yves David
|
r30956 | ui.write(node2str(succsset[0])) | ||
for node in succsset[1:]: | ||||
Augie Fackler
|
r43347 | ui.write(b' ') | ||
Pierre-Yves David
|
r30956 | ui.write(node2str(node)) | ||
Augie Fackler
|
r43347 | ui.write(b'\n') | ||
Pierre-Yves David
|
r30956 | |||
Augie Fackler
|
r44787 | |||
Valentin Gatien-Baron
|
r44772 | @command(b'debugtagscache', []) | ||
def debugtagscache(ui, repo): | ||||
"""display the contents of .hg/cache/hgtagsfnodes1""" | ||||
cache = tagsmod.hgtagsfnodescache(repo.unfiltered()) | ||||
Pulkit Goyal
|
r47401 | flog = repo.file(b'.hgtags') | ||
Valentin Gatien-Baron
|
r44772 | for r in repo: | ||
node = repo[r].node() | ||||
tagsnode = cache.getfnode(node, computemissing=False) | ||||
Matt Harbison
|
r47245 | if tagsnode: | ||
tagsnodedisplay = hex(tagsnode) | ||||
Pulkit Goyal
|
r47401 | if not flog.hasnode(tagsnode): | ||
tagsnodedisplay += b' (unknown node)' | ||||
Pulkit Goyal
|
r47399 | elif tagsnode is None: | ||
tagsnodedisplay = b'missing' | ||||
else: | ||||
Matt Harbison
|
r47245 | tagsnodedisplay = b'invalid' | ||
Augie Fackler
|
r44824 | ui.write(b'%d %s %s\n' % (r, hex(node), tagsnodedisplay)) | ||
Augie Fackler
|
r43346 | |||
Augie Fackler
|
r44787 | |||
Augie Fackler
|
r43346 | @command( | ||
Augie Fackler
|
r43347 | b'debugtemplate', | ||
Augie Fackler
|
r43346 | [ | ||
Augie Fackler
|
r43347 | (b'r', b'rev', [], _(b'apply template on changesets'), _(b'REV')), | ||
(b'D', b'define', [], _(b'define template keyword'), _(b'KEY=VALUE')), | ||||
Augie Fackler
|
r43346 | ], | ||
Augie Fackler
|
r43347 | _(b'[-r REV]... [-D KEY=VALUE]... TEMPLATE'), | ||
Augie Fackler
|
r43346 | optionalrepo=True, | ||
) | ||||
Pierre-Yves David
|
r30957 | def debugtemplate(ui, repo, tmpl, **opts): | ||
"""parse and apply a template | ||||
If -r/--rev is given, the template is processed as a log template and | ||||
applied to the given changesets. Otherwise, it is processed as a generic | ||||
template. | ||||
Use --verbose to print the parsed tree. | ||||
""" | ||||
revs = None | ||||
Augie Fackler
|
r43906 | if opts['rev']: | ||
Pierre-Yves David
|
r30957 | if repo is None: | ||
Augie Fackler
|
r43346 | raise error.RepoError( | ||
Martin von Zweigbergk
|
r43387 | _(b'there is no Mercurial repository here (.hg not found)') | ||
Augie Fackler
|
r43346 | ) | ||
Martin von Zweigbergk
|
r48928 | revs = logcmdutil.revrange(repo, opts['rev']) | ||
Pierre-Yves David
|
r30957 | |||
props = {} | ||||
Augie Fackler
|
r43906 | for d in opts['define']: | ||
Pierre-Yves David
|
r30957 | try: | ||
Augie Fackler
|
r43347 | k, v = (e.strip() for e in d.split(b'=', 1)) | ||
if not k or k == b'ui': | ||||
Pierre-Yves David
|
r30957 | raise ValueError | ||
props[k] = v | ||||
except ValueError: | ||||
Augie Fackler
|
r43347 | raise error.Abort(_(b'malformed keyword definition: %s') % d) | ||
Pierre-Yves David
|
r30957 | |||
if ui.verbose: | ||||
Augie Fackler
|
r43347 | aliases = ui.configitems(b'templatealias') | ||
Pierre-Yves David
|
r30957 | tree = templater.parse(tmpl) | ||
Augie Fackler
|
r43347 | ui.note(templater.prettyformat(tree), b'\n') | ||
Pierre-Yves David
|
r30957 | newtree = templater.expandaliases(tree, aliases) | ||
if newtree != tree: | ||||
r43364 | ui.notenoi18n( | |||
b"* expanded:\n", templater.prettyformat(newtree), b'\n' | ||||
) | ||||
Pierre-Yves David
|
r30957 | |||
if revs is None: | ||||
Yuya Nishihara
|
r35485 | tres = formatter.templateresources(ui, repo) | ||
t = formatter.maketemplater(ui, tmpl, resources=tres) | ||||
Yuya Nishihara
|
r38374 | if ui.verbose: | ||
kwds, funcs = t.symbolsuseddefault() | ||||
Augie Fackler
|
r43350 | ui.writenoi18n(b"* keywords: %s\n" % b', '.join(sorted(kwds))) | ||
ui.writenoi18n(b"* functions: %s\n" % b', '.join(sorted(funcs))) | ||||
Yuya Nishihara
|
r37003 | ui.write(t.renderdefault(props)) | ||
Pierre-Yves David
|
r30957 | else: | ||
Yuya Nishihara
|
r35906 | displayer = logcmdutil.maketemplater(ui, repo, tmpl) | ||
Yuya Nishihara
|
r38374 | if ui.verbose: | ||
kwds, funcs = displayer.t.symbolsuseddefault() | ||||
Augie Fackler
|
r43350 | ui.writenoi18n(b"* keywords: %s\n" % b', '.join(sorted(kwds))) | ||
ui.writenoi18n(b"* functions: %s\n" % b', '.join(sorted(funcs))) | ||||
Pierre-Yves David
|
r30957 | for r in revs: | ||
Pulkit Goyal
|
r33102 | displayer.show(repo[r], **pycompat.strkwargs(props)) | ||
Pierre-Yves David
|
r30957 | displayer.close() | ||
Augie Fackler
|
r43346 | |||
@command( | ||||
Augie Fackler
|
r43347 | b'debuguigetpass', | ||
Augie Fackler
|
r46554 | [ | ||
(b'p', b'prompt', b'', _(b'prompt text'), _(b'TEXT')), | ||||
], | ||||
Augie Fackler
|
r43347 | _(b'[-p TEXT]'), | ||
Augie Fackler
|
r43346 | norepo=True, | ||
) | ||||
Augie Fackler
|
r43347 | def debuguigetpass(ui, prompt=b''): | ||
Yuya Nishihara
|
r36810 | """show prompt to type password""" | ||
r = ui.getpass(prompt) | ||||
Yuya Nishihara
|
r46653 | if r is None: | ||
Matt Harbison
|
r46628 | r = b"<default response>" | ||
Yuya Nishihara
|
r45148 | ui.writenoi18n(b'response: %s\n' % r) | ||
Augie Fackler
|
r43346 | |||
@command( | ||||
Augie Fackler
|
r43347 | b'debuguiprompt', | ||
Augie Fackler
|
r46554 | [ | ||
(b'p', b'prompt', b'', _(b'prompt text'), _(b'TEXT')), | ||||
], | ||||
Augie Fackler
|
r43347 | _(b'[-p TEXT]'), | ||
Augie Fackler
|
r43346 | norepo=True, | ||
) | ||||
Augie Fackler
|
r43347 | def debuguiprompt(ui, prompt=b''): | ||
Yuya Nishihara
|
r36810 | """show plain prompt""" | ||
r = ui.prompt(prompt) | ||||
Augie Fackler
|
r43350 | ui.writenoi18n(b'response: %s\n' % r) | ||
Augie Fackler
|
r43347 | |||
@command(b'debugupdatecaches', []) | ||||
Pierre-Yves David
|
r32265 | def debugupdatecaches(ui, repo, *pats, **opts): | ||
"""warm all known caches in the repository""" | ||||
Jun Wu
|
r33438 | with repo.wlock(), repo.lock(): | ||
r48078 | repo.updatecaches(caches=repository.CACHES_ALL) | |||
Pierre-Yves David
|
r32265 | |||
Augie Fackler
|
r43346 | |||
@command( | ||||
Augie Fackler
|
r43347 | b'debugupgraderepo', | ||
Augie Fackler
|
r43346 | [ | ||
Augie Fackler
|
r43347 | ( | ||
b'o', | ||||
b'optimize', | ||||
[], | ||||
_(b'extra optimization to perform'), | ||||
_(b'NAME'), | ||||
), | ||||
(b'', b'run', False, _(b'performs an upgrade')), | ||||
(b'', b'backup', True, _(b'keep the old repository content around')), | ||||
(b'', b'changelog', None, _(b'select the changelog for upgrade')), | ||||
(b'', b'manifest', None, _(b'select the manifest for upgrade')), | ||||
r46612 | (b'', b'filelogs', None, _(b'select all filelogs for upgrade')), | |||
Augie Fackler
|
r43346 | ], | ||
) | ||||
r43098 | def debugupgraderepo(ui, repo, run=False, optimize=None, backup=True, **opts): | |||
Gregory Szorc
|
r30774 | """upgrade a repository to use different features | ||
If no arguments are specified, the repository is evaluated for upgrade | ||||
and a list of problems and potential optimizations is printed. | ||||
With ``--run``, a repository upgrade is performed. Behavior of the upgrade | ||||
can be influenced via additional arguments. More details will be provided | ||||
by the command output when run without ``--run``. | ||||
During the upgrade, the repository will be locked and no writes will be | ||||
allowed. | ||||
At the end of the upgrade, the repository may not be readable while new | ||||
repository data is swapped in. This window will be as long as it takes to | ||||
rename some directories inside the ``.hg`` directory. On most machines, this | ||||
should complete almost instantaneously and the chances of a consumer being | ||||
unable to access the repository should be low. | ||||
r43098 | ||||
Augie Fackler
|
r47855 | By default, all revlogs will be upgraded. You can restrict this using flags | ||
r43098 | such as `--manifest`: | |||
* `--manifest`: only optimize the manifest | ||||
* `--no-manifest`: optimize all revlog but the manifest | ||||
r43099 | * `--changelog`: optimize the changelog only | |||
* `--no-changelog --no-manifest`: optimize filelogs only | ||||
r46612 | * `--filelogs`: optimize the filelogs only | |||
Pulkit Goyal
|
r46828 | * `--no-changelog --no-manifest --no-filelogs`: skip all revlog optimizations | ||
Gregory Szorc
|
r30774 | """ | ||
Augie Fackler
|
r43346 | return upgrade.upgraderepo( | ||
Pulkit Goyal
|
r46825 | ui, repo, run=run, optimize=set(optimize), backup=backup, **opts | ||
Augie Fackler
|
r43346 | ) | ||
@command( | ||||
r52293 | b'debug::unbundle', | |||
r52448 | [], | |||
_(b'FILE...'), | ||||
r52293 | helpcategory=command.CATEGORY_IMPORT_EXPORT, | |||
) | ||||
r52448 | def debugunbundle(ui, repo, fname1, *fnames): | |||
r52293 | """same as `hg unbundle`, but pretent to come from a push | |||
This is useful to debug behavior and performance change in this case. | ||||
""" | ||||
r52448 | fnames = (fname1,) + fnames | |||
cmdutil.unbundle_files(ui, repo, fnames) | ||||
r52293 | ||||
@command( | ||||
Augie Fackler
|
r43347 | b'debugwalk', cmdutil.walkopts, _(b'[OPTION]... [FILE]...'), inferrepo=True | ||
Augie Fackler
|
r43346 | ) | ||
Pierre-Yves David
|
r30958 | def debugwalk(ui, repo, *pats, **opts): | ||
"""show how files match on given patterns""" | ||||
Matt Harbison
|
r51864 | m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts)) | ||
Yuya Nishihara
|
r38281 | if ui.verbose: | ||
Augie Fackler
|
r43350 | ui.writenoi18n(b'* matcher:\n', stringutil.prettyrepr(m), b'\n') | ||
Augie Fackler
|
r32363 | items = list(repo[None].walk(m)) | ||
Pierre-Yves David
|
r30958 | if not items: | ||
return | ||||
f = lambda fn: fn | ||||
Augie Fackler
|
r43347 | if ui.configbool(b'ui', b'slash') and pycompat.ossep != b'/': | ||
Pierre-Yves David
|
r30958 | f = lambda fn: util.normpath(fn) | ||
Augie Fackler
|
r43347 | fmt = b'f %%-%ds %%-%ds %%s' % ( | ||
Pierre-Yves David
|
r30958 | max([len(abs) for abs in items]), | ||
Augie Fackler
|
r43346 | max([len(repo.pathto(abs)) for abs in items]), | ||
) | ||||
Pierre-Yves David
|
r30958 | for abs in items: | ||
Augie Fackler
|
r43347 | line = fmt % ( | ||
abs, | ||||
f(repo.pathto(abs)), | ||||
m.exact(abs) and b'exact' or b'', | ||||
) | ||||
ui.write(b"%s\n" % line.rstrip()) | ||||
@command(b'debugwhyunstable', [], _(b'REV')) | ||||
r36972 | def debugwhyunstable(ui, repo, rev): | |||
"""explain instabilities of a changeset""" | ||||
Martin von Zweigbergk
|
r37414 | for entry in obsutil.whyunstable(repo, scmutil.revsingle(repo, rev)): | ||
Augie Fackler
|
r43347 | dnodes = b'' | ||
if entry.get(b'divergentnodes'): | ||||
Augie Fackler
|
r43346 | dnodes = ( | ||
Augie Fackler
|
r43347 | b' '.join( | ||
b'%s (%s)' % (ctx.hex(), ctx.phasestr()) | ||||
for ctx in entry[b'divergentnodes'] | ||||
Augie Fackler
|
r43346 | ) | ||
Augie Fackler
|
r43347 | + b' ' | ||
Augie Fackler
|
r43346 | ) | ||
ui.write( | ||||
Augie Fackler
|
r43347 | b'%s: %s%s %s\n' | ||
% (entry[b'instability'], dnodes, entry[b'reason'], entry[b'node']) | ||||
Augie Fackler
|
r43346 | ) | ||
@command( | ||||
Augie Fackler
|
r43347 | b'debugwireargs', | ||
Augie Fackler
|
r43346 | [ | ||
Augie Fackler
|
r43347 | (b'', b'three', b'', b'three'), | ||
(b'', b'four', b'', b'four'), | ||||
(b'', b'five', b'', b'five'), | ||||
Augie Fackler
|
r43346 | ] | ||
+ cmdutil.remoteopts, | ||||
Augie Fackler
|
r43347 | _(b'REPO [OPTIONS]... [ONE [TWO]]'), | ||
Augie Fackler
|
r43346 | norepo=True, | ||
) | ||||
Pierre-Yves David
|
r30959 | def debugwireargs(ui, repopath, *vals, **opts): | ||
Matt Harbison
|
r51865 | repo = hg.peer(ui, pycompat.byteskwargs(opts), repopath) | ||
Valentin Gatien-Baron
|
r47419 | try: | ||
for opt in cmdutil.remoteopts: | ||||
Matt Harbison
|
r51865 | del opts[pycompat.sysstr(opt[1])] | ||
Valentin Gatien-Baron
|
r47419 | args = {} | ||
Gregory Szorc
|
r49768 | for k, v in opts.items(): | ||
Valentin Gatien-Baron
|
r47419 | if v: | ||
args[k] = v | ||||
Matt Harbison
|
r51865 | |||
Valentin Gatien-Baron
|
r47419 | # run twice to check that we don't mess up the stream for the next command | ||
res1 = repo.debugwireargs(*vals, **args) | ||||
res2 = repo.debugwireargs(*vals, **args) | ||||
ui.write(b"%s\n" % res1) | ||||
if res1 != res2: | ||||
ui.warn(b"%s\n" % res2) | ||||
finally: | ||||
repo.close() | ||||
Gregory Szorc
|
r36545 | |||
Augie Fackler
|
r43346 | |||
Gregory Szorc
|
r36545 | def _parsewirelangblocks(fh): | ||
activeaction = None | ||||
blocklines = [] | ||||
Gregory Szorc
|
r40210 | lastindent = 0 | ||
Gregory Szorc
|
r36545 | |||
for line in fh: | ||||
line = line.rstrip() | ||||
if not line: | ||||
continue | ||||
if line.startswith(b'#'): | ||||
continue | ||||
Augie Fackler
|
r39097 | if not line.startswith(b' '): | ||
Gregory Szorc
|
r36545 | # New block. Flush previous one. | ||
if activeaction: | ||||
yield activeaction, blocklines | ||||
activeaction = line | ||||
blocklines = [] | ||||
Gregory Szorc
|
r40210 | lastindent = 0 | ||
Gregory Szorc
|
r36545 | continue | ||
# Else we start with an indent. | ||||
if not activeaction: | ||||
Augie Fackler
|
r43347 | raise error.Abort(_(b'indented line outside of block')) | ||
Gregory Szorc
|
r36545 | |||
Gregory Szorc
|
r40210 | indent = len(line) - len(line.lstrip()) | ||
# If this line is indented more than the last line, concatenate it. | ||||
if indent > lastindent and blocklines: | ||||
blocklines[-1] += line.lstrip() | ||||
else: | ||||
blocklines.append(line) | ||||
lastindent = indent | ||||
Gregory Szorc
|
r36545 | |||
# Flush last block. | ||||
if activeaction: | ||||
yield activeaction, blocklines | ||||
Augie Fackler
|
r43346 | |||
@command( | ||||
Augie Fackler
|
r43347 | b'debugwireproto', | ||
Gregory Szorc
|
r36545 | [ | ||
Augie Fackler
|
r43347 | (b'', b'localssh', False, _(b'start an SSH server for this repo')), | ||
(b'', b'peer', b'', _(b'construct a specific version of the peer')), | ||||
Augie Fackler
|
r43346 | ( | ||
Augie Fackler
|
r43347 | b'', | ||
b'noreadstderr', | ||||
Augie Fackler
|
r43346 | False, | ||
Augie Fackler
|
r43347 | _(b'do not read from stderr of the remote'), | ||
), | ||||
( | ||||
b'', | ||||
b'nologhandshake', | ||||
False, | ||||
_(b'do not log I/O related to the peer handshake'), | ||||
Augie Fackler
|
r43346 | ), | ||
] | ||||
+ cmdutil.remoteopts, | ||||
Augie Fackler
|
r43347 | _(b'[PATH]'), | ||
Augie Fackler
|
r43346 | optionalrepo=True, | ||
) | ||||
Gregory Szorc
|
r37030 | def debugwireproto(ui, repo, path=None, **opts): | ||
Gregory Szorc
|
r36545 | """send wire protocol commands to a server | ||
This command can be used to issue wire protocol commands to remote | ||||
peers and to debug the raw data being exchanged. | ||||
``--localssh`` will start an SSH server against the current repository | ||||
and connect to that. By default, the connection will perform a handshake | ||||
and establish an appropriate peer instance. | ||||
``--peer`` can be used to bypass the handshake protocol and construct a | ||||
peer instance using the specified class type. Valid values are ``raw``, | ||||
Raphaël Gomès
|
r49357 | ``ssh1``. ``raw`` instances only allow sending raw data payloads and | ||
don't support higher-level command actions. | ||||
Gregory Szorc
|
r36545 | |||
Gregory Szorc
|
r36551 | ``--noreadstderr`` can be used to disable automatic reading from stderr | ||
of the peer (for SSH connections only). Disabling automatic reading of | ||||
stderr is useful for making output more deterministic. | ||||
Gregory Szorc
|
r36545 | Commands are issued via a mini language which is specified via stdin. | ||
The language consists of individual actions to perform. An action is | ||||
defined by a block. A block is defined as a line with no leading | ||||
space followed by 0 or more lines with leading space. Blocks are | ||||
effectively a high-level command with additional metadata. | ||||
Lines beginning with ``#`` are ignored. | ||||
The following sections denote available actions. | ||||
raw | ||||
--- | ||||
Send raw data to the server. | ||||
The block payload contains the raw data to send as one atomic send | ||||
operation. The data may not actually be delivered in a single system | ||||
call: it depends on the abilities of the transport being used. | ||||
Each line in the block is de-indented and concatenated. Then, that | ||||
value is evaluated as a Python b'' literal. This allows the use of | ||||
backslash escaping, etc. | ||||
raw+ | ||||
---- | ||||
Behaves like ``raw`` except flushes output afterwards. | ||||
Gregory Szorc
|
r36547 | command <X> | ||
----------- | ||||
Send a request to run a named command, whose name follows the ``command`` | ||||
string. | ||||
Arguments to the command are defined as lines in this block. The format of | ||||
each line is ``<key> <value>``. e.g.:: | ||||
command listkeys | ||||
namespace bookmarks | ||||
Gregory Szorc
|
r37501 | If the value begins with ``eval:``, it will be interpreted as a Python | ||
literal expression. Otherwise values are interpreted as Python b'' literals. | ||||
This allows sending complex types and encoding special byte sequences via | ||||
backslash escaping. | ||||
Gregory Szorc
|
r36547 | |||
Gregory Szorc
|
r36551 | The following arguments have special meaning: | ||
``PUSHFILE`` | ||||
When defined, the *push* mechanism of the peer will be used instead | ||||
of the static request-response mechanism and the content of the | ||||
file specified in the value of this argument will be sent as the | ||||
command payload. | ||||
This can be used to submit a local bundle file to the remote. | ||||
Gregory Szorc
|
r36548 | batchbegin | ||
---------- | ||||
Instruct the peer to begin a batched send. | ||||
All ``command`` blocks are queued for execution until the next | ||||
``batchsubmit`` block. | ||||
batchsubmit | ||||
----------- | ||||
Submit previously queued ``command`` blocks as a batch request. | ||||
This action MUST be paired with a ``batchbegin`` action. | ||||
Gregory Szorc
|
r37031 | httprequest <method> <path> | ||
--------------------------- | ||||
(HTTP peer only) | ||||
Send an HTTP request to the peer. | ||||
The HTTP request line follows the ``httprequest`` action. e.g. ``GET /foo``. | ||||
Arguments of the form ``<key>: <value>`` are interpreted as HTTP request | ||||
headers to add to the request. e.g. ``Accept: foo``. | ||||
The following arguments are special: | ||||
``BODYFILE`` | ||||
The content of the file defined as the value to this argument will be | ||||
transferred verbatim as the HTTP request body. | ||||
Gregory Szorc
|
r37069 | ``frame <type> <flags> <payload>`` | ||
Send a unified protocol frame as part of the request body. | ||||
All frames will be collected and sent as the body to the HTTP | ||||
request. | ||||
Gregory Szorc
|
r36545 | close | ||
----- | ||||
Close the connection to the server. | ||||
flush | ||||
----- | ||||
Flush data written to the server. | ||||
readavailable | ||||
------------- | ||||
Yuya Nishihara
|
r36861 | Close the write end of the connection and read all available data from | ||
the server. | ||||
Gregory Szorc
|
r36545 | |||
If the connection to the server encompasses multiple pipes, we poll both | ||||
pipes and read available data. | ||||
readline | ||||
-------- | ||||
Read a line of output from the server. If there are multiple output | ||||
pipes, reads only the main pipe. | ||||
Gregory Szorc
|
r37025 | |||
ereadline | ||||
--------- | ||||
Like ``readline``, but read from the stderr pipe, if available. | ||||
read <X> | ||||
-------- | ||||
``read()`` N bytes from the server's main output pipe. | ||||
eread <X> | ||||
--------- | ||||
``read()`` N bytes from the server's stderr pipe, if available. | ||||
Gregory Szorc
|
r37069 | |||
Specifying Unified Frame-Based Protocol Frames | ||||
---------------------------------------------- | ||||
It is possible to emit a *Unified Frame-Based Protocol* by using special | ||||
syntax. | ||||
A frame is composed as a type, flags, and payload. These can be parsed | ||||
Gregory Szorc
|
r37304 | from a string of the form: | ||
<request-id> <stream-id> <stream-flags> <type> <flags> <payload> | ||||
``request-id`` and ``stream-id`` are integers defining the request and | ||||
stream identifiers. | ||||
Gregory Szorc
|
r37075 | |||
Gregory Szorc
|
r37069 | ``type`` can be an integer value for the frame type or the string name | ||
of the type. The strings are defined in ``wireprotoframing.py``. e.g. | ||||
``command-name``. | ||||
Gregory Szorc
|
r37304 | ``stream-flags`` and ``flags`` are a ``|`` delimited list of flag | ||
components. Each component (and there can be just one) can be an integer | ||||
or a flag name for stream flags or frame flags, respectively. Values are | ||||
resolved to integers and then bitwise OR'd together. | ||||
Gregory Szorc
|
r37306 | ``payload`` represents the raw frame payload. If it begins with | ||
``cbor:``, the following string is evaluated as Python code and the | ||||
resulting object is fed into a CBOR encoder. Otherwise it is interpreted | ||||
as a Python byte string literal. | ||||
Gregory Szorc
|
r36545 | """ | ||
Matt Harbison
|
r51866 | if opts['localssh'] and not repo: | ||
Augie Fackler
|
r43347 | raise error.Abort(_(b'--localssh requires a repository')) | ||
Matt Harbison
|
r51866 | if opts['peer'] and opts['peer'] not in ( | ||
Augie Fackler
|
r43347 | b'raw', | ||
b'ssh1', | ||||
): | ||||
Augie Fackler
|
r43346 | raise error.Abort( | ||
Augie Fackler
|
r43347 | _(b'invalid value for --peer'), | ||
Raphaël Gomès
|
r49357 | hint=_(b'valid values are "raw" and "ssh1"'), | ||
Augie Fackler
|
r43346 | ) | ||
Gregory Szorc
|
r36545 | |||
Matt Harbison
|
r51866 | if path and opts['localssh']: | ||
Martin von Zweigbergk
|
r43387 | raise error.Abort(_(b'cannot specify --localssh with an explicit path')) | ||
Gregory Szorc
|
r37030 | |||
Gregory Szorc
|
r36545 | if ui.interactive(): | ||
Augie Fackler
|
r43347 | ui.write(_(b'(waiting for commands on stdin)\n')) | ||
Gregory Szorc
|
r36545 | |||
blocks = list(_parsewirelangblocks(ui.fin)) | ||||
proc = None | ||||
Gregory Szorc
|
r37030 | stdin = None | ||
stdout = None | ||||
stderr = None | ||||
Gregory Szorc
|
r37031 | opener = None | ||
Gregory Szorc
|
r36545 | |||
Matt Harbison
|
r51866 | if opts['localssh']: | ||
Gregory Szorc
|
r36545 | # We start the SSH server in its own process so there is process | ||
# separation. This prevents a whole class of potential bugs around | ||||
# shared state from interfering with server operation. | ||||
Yuya Nishihara
|
r37138 | args = procutil.hgcmd() + [ | ||
Augie Fackler
|
r43347 | b'-R', | ||
Augie Fackler
|
r43346 | repo.root, | ||
Augie Fackler
|
r43347 | b'debugserve', | ||
b'--sshstdio', | ||||
Gregory Szorc
|
r36545 | ] | ||
Augie Fackler
|
r43346 | proc = subprocess.Popen( | ||
pycompat.rapply(procutil.tonativestr, args), | ||||
stdin=subprocess.PIPE, | ||||
stdout=subprocess.PIPE, | ||||
stderr=subprocess.PIPE, | ||||
bufsize=0, | ||||
) | ||||
Gregory Szorc
|
r36545 | |||
stdin = proc.stdin | ||||
stdout = proc.stdout | ||||
stderr = proc.stderr | ||||
# We turn the pipes into observers so we can log I/O. | ||||
Matt Harbison
|
r51866 | if ui.verbose or opts['peer'] == b'raw': | ||
Augie Fackler
|
r43346 | stdin = util.makeloggingfileobject( | ||
ui, proc.stdin, b'i', logdata=True | ||||
) | ||||
stdout = util.makeloggingfileobject( | ||||
ui, proc.stdout, b'o', logdata=True | ||||
) | ||||
stderr = util.makeloggingfileobject( | ||||
ui, proc.stderr, b'e', logdata=True | ||||
) | ||||
Gregory Szorc
|
r36545 | |||
# --localssh also implies the peer connection settings. | ||||
Augie Fackler
|
r43347 | url = b'ssh://localserver' | ||
Matt Harbison
|
r51866 | autoreadstderr = not opts['noreadstderr'] | ||
if opts['peer'] == b'ssh1': | ||||
Augie Fackler
|
r43347 | ui.write(_(b'creating ssh peer for wire protocol version 1\n')) | ||
Augie Fackler
|
r43346 | peer = sshpeer.sshv1peer( | ||
ui, | ||||
url, | ||||
proc, | ||||
stdin, | ||||
stdout, | ||||
stderr, | ||||
None, | ||||
autoreadstderr=autoreadstderr, | ||||
) | ||||
Matt Harbison
|
r51866 | elif opts['peer'] == b'raw': | ||
Augie Fackler
|
r43347 | ui.write(_(b'using raw connection to peer\n')) | ||
Gregory Szorc
|
r36545 | peer = None | ||
else: | ||||
Augie Fackler
|
r43347 | ui.write(_(b'creating ssh peer from handshake results\n')) | ||
Manuel Jacob
|
r51284 | peer = sshpeer._make_peer( | ||
Augie Fackler
|
r43346 | ui, | ||
url, | ||||
proc, | ||||
stdin, | ||||
stdout, | ||||
stderr, | ||||
autoreadstderr=autoreadstderr, | ||||
) | ||||
Gregory Szorc
|
r36545 | |||
Gregory Szorc
|
r37030 | elif path: | ||
# We bypass hg.peer() so we can proxy the sockets. | ||||
# TODO consider not doing this because we skip | ||||
# ``hg.wirepeersetupfuncs`` and potentially other useful functionality. | ||||
r47669 | u = urlutil.url(path) | |||
Joerg Sonnenberger
|
r52721 | if u.scheme not in (b'http', b'https'): | ||
raise error.Abort( | ||||
_(b'only http:// and https:// paths are currently supported') | ||||
) | ||||
Gregory Szorc
|
r37030 | |||
url, authinfo = u.authinfo() | ||||
Gregory Szorc
|
r37501 | openerargs = { | ||
Augie Fackler
|
r43906 | 'useragent': b'Mercurial debugwireproto', | ||
Gregory Szorc
|
r37501 | } | ||
Gregory Szorc
|
r37030 | |||
# Turn pipes/sockets into observers so we can log I/O. | ||||
if ui.verbose: | ||||
Augie Fackler
|
r43346 | openerargs.update( | ||
{ | ||||
Augie Fackler
|
r43906 | 'loggingfh': ui, | ||
'loggingname': b's', | ||||
Augie Fackler
|
r46554 | 'loggingopts': { | ||
'logdata': True, | ||||
'logdataapis': False, | ||||
}, | ||||
Augie Fackler
|
r43346 | } | ||
) | ||||
Gregory Szorc
|
r37030 | |||
Gregory Szorc
|
r37062 | if ui.debugflag: | ||
Augie Fackler
|
r43906 | openerargs['loggingopts']['logdataapis'] = True | ||
Gregory Szorc
|
r37062 | |||
Gregory Szorc
|
r37063 | # Don't send default headers when in raw mode. This allows us to | ||
# bypass most of the behavior of our URL handling code so we can | ||||
# have near complete control over what's sent on the wire. | ||||
Matt Harbison
|
r51866 | if opts['peer'] == b'raw': | ||
Augie Fackler
|
r43906 | openerargs['sendaccept'] = False | ||
Gregory Szorc
|
r37063 | |||
Gregory Szorc
|
r37030 | opener = urlmod.opener(ui, authinfo, **openerargs) | ||
Matt Harbison
|
r51866 | if opts['peer'] == b'raw': | ||
Augie Fackler
|
r43347 | ui.write(_(b'using raw connection to peer\n')) | ||
Gregory Szorc
|
r37030 | peer = None | ||
Matt Harbison
|
r51866 | elif opts['peer']: | ||
Augie Fackler
|
r43346 | raise error.Abort( | ||
Matt Harbison
|
r51866 | _(b'--peer %s not supported with HTTP peers') % opts['peer'] | ||
Augie Fackler
|
r43346 | ) | ||
Gregory Szorc
|
r37030 | else: | ||
r50654 | peer_path = urlutil.try_path(ui, path) | |||
Manuel Jacob
|
r51284 | peer = httppeer._make_peer(ui, peer_path, opener=opener) | ||
Gregory Szorc
|
r37030 | |||
# We /could/ populate stdin/stdout with sock.makefile()... | ||||
Gregory Szorc
|
r36545 | else: | ||
Augie Fackler
|
r43347 | raise error.Abort(_(b'unsupported connection configuration')) | ||
Gregory Szorc
|
r36545 | |||
Gregory Szorc
|
r36548 | batchedcommands = None | ||
Gregory Szorc
|
r36545 | # Now perform actions based on the parsed wire language instructions. | ||
for action, lines in blocks: | ||||
Augie Fackler
|
r43347 | if action in (b'raw', b'raw+'): | ||
Gregory Szorc
|
r37030 | if not stdin: | ||
Augie Fackler
|
r43347 | raise error.Abort(_(b'cannot call raw/raw+ on this peer')) | ||
Gregory Szorc
|
r37030 | |||
Gregory Szorc
|
r36545 | # Concatenate the data together. | ||
Augie Fackler
|
r43347 | data = b''.join(l.lstrip() for l in lines) | ||
Yuya Nishihara
|
r37102 | data = stringutil.unescapestr(data) | ||
Gregory Szorc
|
r36545 | stdin.write(data) | ||
Augie Fackler
|
r43347 | if action == b'raw+': | ||
Gregory Szorc
|
r36545 | stdin.flush() | ||
Augie Fackler
|
r43347 | elif action == b'flush': | ||
Gregory Szorc
|
r37030 | if not stdin: | ||
Augie Fackler
|
r43347 | raise error.Abort(_(b'cannot call flush on this peer')) | ||
Gregory Szorc
|
r36545 | stdin.flush() | ||
Augie Fackler
|
r43347 | elif action.startswith(b'command'): | ||
Gregory Szorc
|
r36547 | if not peer: | ||
Augie Fackler
|
r43346 | raise error.Abort( | ||
_( | ||||
Augie Fackler
|
r43347 | b'cannot send commands unless peer instance ' | ||
b'is available' | ||||
Augie Fackler
|
r43346 | ) | ||
) | ||||
Gregory Szorc
|
r36547 | |||
Augie Fackler
|
r43347 | command = action.split(b' ', 1)[1] | ||
Gregory Szorc
|
r36547 | |||
args = {} | ||||
for line in lines: | ||||
# We need to allow empty values. | ||||
Augie Fackler
|
r43347 | fields = line.lstrip().split(b' ', 1) | ||
Gregory Szorc
|
r36547 | if len(fields) == 1: | ||
key = fields[0] | ||||
Augie Fackler
|
r43347 | value = b'' | ||
Gregory Szorc
|
r36547 | else: | ||
key, value = fields | ||||
Augie Fackler
|
r43347 | if value.startswith(b'eval:'): | ||
Gregory Szorc
|
r37501 | value = stringutil.evalpythonliteral(value[5:]) | ||
else: | ||||
value = stringutil.unescapestr(value) | ||||
args[key] = value | ||||
Gregory Szorc
|
r36547 | |||
Gregory Szorc
|
r36548 | if batchedcommands is not None: | ||
batchedcommands.append((command, args)) | ||||
continue | ||||
Augie Fackler
|
r43347 | ui.status(_(b'sending %s command\n') % command) | ||
if b'PUSHFILE' in args: | ||||
Augie Fackler
|
r43906 | with open(args[b'PUSHFILE'], 'rb') as fh: | ||
Augie Fackler
|
r43347 | del args[b'PUSHFILE'] | ||
Augie Fackler
|
r43346 | res, output = peer._callpush( | ||
command, fh, **pycompat.strkwargs(args) | ||||
) | ||||
Augie Fackler
|
r43347 | ui.status(_(b'result: %s\n') % stringutil.escapestr(res)) | ||
Augie Fackler
|
r43346 | ui.status( | ||
Augie Fackler
|
r43347 | _(b'remote output: %s\n') % stringutil.escapestr(output) | ||
Augie Fackler
|
r43346 | ) | ||
Gregory Szorc
|
r36551 | else: | ||
Gregory Szorc
|
r37670 | with peer.commandexecutor() as e: | ||
res = e.callcommand(command, args).result() | ||||
Raphaël Gomès
|
r49357 | ui.status( | ||
_(b'response: %s\n') | ||||
% stringutil.pprint(res, bprefix=True, indent=2) | ||||
) | ||||
Gregory Szorc
|
r36547 | |||
Augie Fackler
|
r43347 | elif action == b'batchbegin': | ||
Gregory Szorc
|
r36548 | if batchedcommands is not None: | ||
Augie Fackler
|
r43347 | raise error.Abort(_(b'nested batchbegin not allowed')) | ||
Gregory Szorc
|
r36548 | |||
batchedcommands = [] | ||||
Augie Fackler
|
r43347 | elif action == b'batchsubmit': | ||
Gregory Szorc
|
r36548 | # There is a batching API we could go through. But it would be | ||
# difficult to normalize requests into function calls. It is easier | ||||
# to bypass this layer and normalize to commands + args. | ||||
Augie Fackler
|
r43346 | ui.status( | ||
Augie Fackler
|
r43347 | _(b'sending batch with %d sub-commands\n') | ||
% len(batchedcommands) | ||||
Augie Fackler
|
r43346 | ) | ||
Augie Fackler
|
r44103 | assert peer is not None | ||
Gregory Szorc
|
r36548 | for i, chunk in enumerate(peer._submitbatch(batchedcommands)): | ||
Augie Fackler
|
r43346 | ui.status( | ||
Augie Fackler
|
r43347 | _(b'response #%d: %s\n') % (i, stringutil.escapestr(chunk)) | ||
Augie Fackler
|
r43346 | ) | ||
Gregory Szorc
|
r36548 | |||
batchedcommands = None | ||||
Gregory Szorc
|
r37031 | |||
Augie Fackler
|
r43347 | elif action.startswith(b'httprequest '): | ||
Gregory Szorc
|
r37031 | if not opener: | ||
Augie Fackler
|
r43346 | raise error.Abort( | ||
Martin von Zweigbergk
|
r43387 | _(b'cannot use httprequest without an HTTP peer') | ||
Augie Fackler
|
r43346 | ) | ||
Gregory Szorc
|
r37031 | |||
Augie Fackler
|
r43347 | request = action.split(b' ', 2) | ||
Gregory Szorc
|
r37031 | if len(request) != 3: | ||
Augie Fackler
|
r43346 | raise error.Abort( | ||
_( | ||||
Augie Fackler
|
r43347 | b'invalid httprequest: expected format is ' | ||
b'"httprequest <method> <path>' | ||||
Augie Fackler
|
r43346 | ) | ||
) | ||||
Gregory Szorc
|
r37031 | |||
method, httppath = request[1:] | ||||
headers = {} | ||||
body = None | ||||
Gregory Szorc
|
r37069 | frames = [] | ||
Gregory Szorc
|
r37031 | for line in lines: | ||
line = line.lstrip() | ||||
m = re.match(b'^([a-zA-Z0-9_-]+): (.*)$', line) | ||||
if m: | ||||
Gregory Szorc
|
r39991 | # Headers need to use native strings. | ||
key = pycompat.strurl(m.group(1)) | ||||
value = pycompat.strurl(m.group(2)) | ||||
headers[key] = value | ||||
Gregory Szorc
|
r37031 | continue | ||
if line.startswith(b'BODYFILE '): | ||||
Augie Fackler
|
r43347 | with open(line.split(b' ', 1), b'rb') as fh: | ||
Gregory Szorc
|
r37031 | body = fh.read() | ||
Gregory Szorc
|
r37069 | elif line.startswith(b'frame '): | ||
frame = wireprotoframing.makeframefromhumanstring( | ||||
Augie Fackler
|
r43346 | line[len(b'frame ') :] | ||
) | ||||
Gregory Szorc
|
r37069 | |||
frames.append(frame) | ||||
Gregory Szorc
|
r37031 | else: | ||
Augie Fackler
|
r43346 | raise error.Abort( | ||
Augie Fackler
|
r43347 | _(b'unknown argument to httprequest: %s') % line | ||
Augie Fackler
|
r43346 | ) | ||
Gregory Szorc
|
r37031 | |||
url = path + httppath | ||||
Gregory Szorc
|
r37069 | |||
if frames: | ||||
body = b''.join(bytes(f) for f in frames) | ||||
Gregory Szorc
|
r37031 | req = urlmod.urlreq.request(pycompat.strurl(url), body, headers) | ||
Gregory Szorc
|
r37065 | # urllib.Request insists on using has_data() as a proxy for | ||
# determining the request method. Override that to use our | ||||
# explicitly requested method. | ||||
Augie Fackler
|
r39099 | req.get_method = lambda: pycompat.sysstr(method) | ||
Gregory Szorc
|
r37065 | |||
Gregory Szorc
|
r37031 | try: | ||
Gregory Szorc
|
r37575 | res = opener.open(req) | ||
body = res.read() | ||||
Gregory Szorc
|
r37031 | except util.urlerr.urlerror as e: | ||
Augie Fackler
|
r39100 | # read() method must be called, but only exists in Python 2 | ||
getattr(e, 'read', lambda: None)() | ||||
Gregory Szorc
|
r37575 | continue | ||
Augie Fackler
|
r43906 | ct = res.headers.get('Content-Type') | ||
if ct == 'application/mercurial-cbor': | ||||
Augie Fackler
|
r43346 | ui.write( | ||
Augie Fackler
|
r43347 | _(b'cbor> %s\n') | ||
Augie Fackler
|
r43346 | % stringutil.pprint( | ||
cborutil.decodeall(body), bprefix=True, indent=2 | ||||
) | ||||
) | ||||
Gregory Szorc
|
r37031 | |||
Augie Fackler
|
r43347 | elif action == b'close': | ||
Augie Fackler
|
r44103 | assert peer is not None | ||
Gregory Szorc
|
r36545 | peer.close() | ||
Augie Fackler
|
r43347 | elif action == b'readavailable': | ||
Gregory Szorc
|
r37030 | if not stdout or not stderr: | ||
Augie Fackler
|
r43347 | raise error.Abort( | ||
_(b'readavailable not available on this peer') | ||||
) | ||||
Gregory Szorc
|
r37030 | |||
Yuya Nishihara
|
r36861 | stdin.close() | ||
stdout.read() | ||||
stderr.read() | ||||
Gregory Szorc
|
r37030 | |||
Augie Fackler
|
r43347 | elif action == b'readline': | ||
Gregory Szorc
|
r37030 | if not stdout: | ||
Augie Fackler
|
r43347 | raise error.Abort(_(b'readline not available on this peer')) | ||
Gregory Szorc
|
r36545 | stdout.readline() | ||
Augie Fackler
|
r43347 | elif action == b'ereadline': | ||
Gregory Szorc
|
r37030 | if not stderr: | ||
Augie Fackler
|
r43347 | raise error.Abort(_(b'ereadline not available on this peer')) | ||
Gregory Szorc
|
r37025 | stderr.readline() | ||
Augie Fackler
|
r43347 | elif action.startswith(b'read '): | ||
count = int(action.split(b' ', 1)[1]) | ||||
Gregory Szorc
|
r37030 | if not stdout: | ||
Augie Fackler
|
r43347 | raise error.Abort(_(b'read not available on this peer')) | ||
Gregory Szorc
|
r37025 | stdout.read(count) | ||
Augie Fackler
|
r43347 | elif action.startswith(b'eread '): | ||
count = int(action.split(b' ', 1)[1]) | ||||
Gregory Szorc
|
r37030 | if not stderr: | ||
Augie Fackler
|
r43347 | raise error.Abort(_(b'eread not available on this peer')) | ||
Gregory Szorc
|
r37025 | stderr.read(count) | ||
Gregory Szorc
|
r36545 | else: | ||
Augie Fackler
|
r43347 | raise error.Abort(_(b'unknown action: %s') % action) | ||
Gregory Szorc
|
r36545 | |||
Gregory Szorc
|
r36548 | if batchedcommands is not None: | ||
Augie Fackler
|
r43347 | raise error.Abort(_(b'unclosed "batchbegin" request')) | ||
Gregory Szorc
|
r36548 | |||
Gregory Szorc
|
r36545 | if peer: | ||
peer.close() | ||||
if proc: | ||||
proc.kill() | ||||