##// END OF EJS Templates
py3: use stringutil.pprint() to print NoneType...
Pulkit Goyal -
r38463:a0c3d83d default
parent child Browse files
Show More
@@ -1,3144 +1,3144
1 1 # debugcommands.py - command processing for debug* commands
2 2 #
3 3 # Copyright 2005-2016 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import codecs
11 11 import collections
12 12 import difflib
13 13 import errno
14 14 import operator
15 15 import os
16 16 import random
17 17 import re
18 18 import socket
19 19 import ssl
20 20 import stat
21 21 import string
22 22 import subprocess
23 23 import sys
24 24 import time
25 25
26 26 from .i18n import _
27 27 from .node import (
28 28 bin,
29 29 hex,
30 30 nullhex,
31 31 nullid,
32 32 nullrev,
33 33 short,
34 34 )
35 35 from .thirdparty import (
36 36 cbor,
37 37 )
38 38 from . import (
39 39 bundle2,
40 40 changegroup,
41 41 cmdutil,
42 42 color,
43 43 context,
44 44 dagparser,
45 45 dagutil,
46 46 encoding,
47 47 error,
48 48 exchange,
49 49 extensions,
50 50 filemerge,
51 51 fileset,
52 52 formatter,
53 53 hg,
54 54 httppeer,
55 55 localrepo,
56 56 lock as lockmod,
57 57 logcmdutil,
58 58 merge as mergemod,
59 59 obsolete,
60 60 obsutil,
61 61 phases,
62 62 policy,
63 63 pvec,
64 64 pycompat,
65 65 registrar,
66 66 repair,
67 67 revlog,
68 68 revset,
69 69 revsetlang,
70 70 scmutil,
71 71 setdiscovery,
72 72 simplemerge,
73 73 sshpeer,
74 74 sslutil,
75 75 streamclone,
76 76 templater,
77 77 treediscovery,
78 78 upgrade,
79 79 url as urlmod,
80 80 util,
81 81 vfs as vfsmod,
82 82 wireprotoframing,
83 83 wireprotoserver,
84 84 wireprotov2peer,
85 85 )
86 86 from .utils import (
87 87 dateutil,
88 88 procutil,
89 89 stringutil,
90 90 )
91 91
92 92 release = lockmod.release
93 93
94 94 command = registrar.command()
95 95
96 96 @command('debugancestor', [], _('[INDEX] REV1 REV2'), optionalrepo=True)
97 97 def debugancestor(ui, repo, *args):
98 98 """find the ancestor revision of two revisions in a given index"""
99 99 if len(args) == 3:
100 100 index, rev1, rev2 = args
101 101 r = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False), index)
102 102 lookup = r.lookup
103 103 elif len(args) == 2:
104 104 if not repo:
105 105 raise error.Abort(_('there is no Mercurial repository here '
106 106 '(.hg not found)'))
107 107 rev1, rev2 = args
108 108 r = repo.changelog
109 109 lookup = repo.lookup
110 110 else:
111 111 raise error.Abort(_('either two or three arguments required'))
112 112 a = r.ancestor(lookup(rev1), lookup(rev2))
113 113 ui.write('%d:%s\n' % (r.rev(a), hex(a)))
114 114
115 115 @command('debugapplystreamclonebundle', [], 'FILE')
116 116 def debugapplystreamclonebundle(ui, repo, fname):
117 117 """apply a stream clone bundle file"""
118 118 f = hg.openpath(ui, fname)
119 119 gen = exchange.readbundle(ui, f, fname)
120 120 gen.apply(repo)
121 121
122 122 @command('debugbuilddag',
123 123 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
124 124 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
125 125 ('n', 'new-file', None, _('add new file at each rev'))],
126 126 _('[OPTION]... [TEXT]'))
127 127 def debugbuilddag(ui, repo, text=None,
128 128 mergeable_file=False,
129 129 overwritten_file=False,
130 130 new_file=False):
131 131 """builds a repo with a given DAG from scratch in the current empty repo
132 132
133 133 The description of the DAG is read from stdin if not given on the
134 134 command line.
135 135
136 136 Elements:
137 137
138 138 - "+n" is a linear run of n nodes based on the current default parent
139 139 - "." is a single node based on the current default parent
140 140 - "$" resets the default parent to null (implied at the start);
141 141 otherwise the default parent is always the last node created
142 142 - "<p" sets the default parent to the backref p
143 143 - "*p" is a fork at parent p, which is a backref
144 144 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
145 145 - "/p2" is a merge of the preceding node and p2
146 146 - ":tag" defines a local tag for the preceding node
147 147 - "@branch" sets the named branch for subsequent nodes
148 148 - "#...\\n" is a comment up to the end of the line
149 149
150 150 Whitespace between the above elements is ignored.
151 151
152 152 A backref is either
153 153
154 154 - a number n, which references the node curr-n, where curr is the current
155 155 node, or
156 156 - the name of a local tag you placed earlier using ":tag", or
157 157 - empty to denote the default parent.
158 158
159 159 All string valued-elements are either strictly alphanumeric, or must
160 160 be enclosed in double quotes ("..."), with "\\" as escape character.
161 161 """
162 162
163 163 if text is None:
164 164 ui.status(_("reading DAG from stdin\n"))
165 165 text = ui.fin.read()
166 166
167 167 cl = repo.changelog
168 168 if len(cl) > 0:
169 169 raise error.Abort(_('repository is not empty'))
170 170
171 171 # determine number of revs in DAG
172 172 total = 0
173 173 for type, data in dagparser.parsedag(text):
174 174 if type == 'n':
175 175 total += 1
176 176
177 177 if mergeable_file:
178 178 linesperrev = 2
179 179 # make a file with k lines per rev
180 180 initialmergedlines = ['%d' % i for i in xrange(0, total * linesperrev)]
181 181 initialmergedlines.append("")
182 182
183 183 tags = []
184 184 progress = ui.makeprogress(_('building'), unit=_('revisions'),
185 185 total=total)
186 186 with progress, repo.wlock(), repo.lock(), repo.transaction("builddag"):
187 187 at = -1
188 188 atbranch = 'default'
189 189 nodeids = []
190 190 id = 0
191 191 progress.update(id)
192 192 for type, data in dagparser.parsedag(text):
193 193 if type == 'n':
194 194 ui.note(('node %s\n' % pycompat.bytestr(data)))
195 195 id, ps = data
196 196
197 197 files = []
198 198 filecontent = {}
199 199
200 200 p2 = None
201 201 if mergeable_file:
202 202 fn = "mf"
203 203 p1 = repo[ps[0]]
204 204 if len(ps) > 1:
205 205 p2 = repo[ps[1]]
206 206 pa = p1.ancestor(p2)
207 207 base, local, other = [x[fn].data() for x in (pa, p1,
208 208 p2)]
209 209 m3 = simplemerge.Merge3Text(base, local, other)
210 210 ml = [l.strip() for l in m3.merge_lines()]
211 211 ml.append("")
212 212 elif at > 0:
213 213 ml = p1[fn].data().split("\n")
214 214 else:
215 215 ml = initialmergedlines
216 216 ml[id * linesperrev] += " r%i" % id
217 217 mergedtext = "\n".join(ml)
218 218 files.append(fn)
219 219 filecontent[fn] = mergedtext
220 220
221 221 if overwritten_file:
222 222 fn = "of"
223 223 files.append(fn)
224 224 filecontent[fn] = "r%i\n" % id
225 225
226 226 if new_file:
227 227 fn = "nf%i" % id
228 228 files.append(fn)
229 229 filecontent[fn] = "r%i\n" % id
230 230 if len(ps) > 1:
231 231 if not p2:
232 232 p2 = repo[ps[1]]
233 233 for fn in p2:
234 234 if fn.startswith("nf"):
235 235 files.append(fn)
236 236 filecontent[fn] = p2[fn].data()
237 237
238 238 def fctxfn(repo, cx, path):
239 239 if path in filecontent:
240 240 return context.memfilectx(repo, cx, path,
241 241 filecontent[path])
242 242 return None
243 243
244 244 if len(ps) == 0 or ps[0] < 0:
245 245 pars = [None, None]
246 246 elif len(ps) == 1:
247 247 pars = [nodeids[ps[0]], None]
248 248 else:
249 249 pars = [nodeids[p] for p in ps]
250 250 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
251 251 date=(id, 0),
252 252 user="debugbuilddag",
253 253 extra={'branch': atbranch})
254 254 nodeid = repo.commitctx(cx)
255 255 nodeids.append(nodeid)
256 256 at = id
257 257 elif type == 'l':
258 258 id, name = data
259 259 ui.note(('tag %s\n' % name))
260 260 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
261 261 elif type == 'a':
262 262 ui.note(('branch %s\n' % data))
263 263 atbranch = data
264 264 progress.update(id)
265 265
266 266 if tags:
267 267 repo.vfs.write("localtags", "".join(tags))
268 268
269 269 def _debugchangegroup(ui, gen, all=None, indent=0, **opts):
270 270 indent_string = ' ' * indent
271 271 if all:
272 272 ui.write(("%sformat: id, p1, p2, cset, delta base, len(delta)\n")
273 273 % indent_string)
274 274
275 275 def showchunks(named):
276 276 ui.write("\n%s%s\n" % (indent_string, named))
277 277 for deltadata in gen.deltaiter():
278 278 node, p1, p2, cs, deltabase, delta, flags = deltadata
279 279 ui.write("%s%s %s %s %s %s %d\n" %
280 280 (indent_string, hex(node), hex(p1), hex(p2),
281 281 hex(cs), hex(deltabase), len(delta)))
282 282
283 283 chunkdata = gen.changelogheader()
284 284 showchunks("changelog")
285 285 chunkdata = gen.manifestheader()
286 286 showchunks("manifest")
287 287 for chunkdata in iter(gen.filelogheader, {}):
288 288 fname = chunkdata['filename']
289 289 showchunks(fname)
290 290 else:
291 291 if isinstance(gen, bundle2.unbundle20):
292 292 raise error.Abort(_('use debugbundle2 for this file'))
293 293 chunkdata = gen.changelogheader()
294 294 for deltadata in gen.deltaiter():
295 295 node, p1, p2, cs, deltabase, delta, flags = deltadata
296 296 ui.write("%s%s\n" % (indent_string, hex(node)))
297 297
298 298 def _debugobsmarkers(ui, part, indent=0, **opts):
299 299 """display version and markers contained in 'data'"""
300 300 opts = pycompat.byteskwargs(opts)
301 301 data = part.read()
302 302 indent_string = ' ' * indent
303 303 try:
304 304 version, markers = obsolete._readmarkers(data)
305 305 except error.UnknownVersion as exc:
306 306 msg = "%sunsupported version: %s (%d bytes)\n"
307 307 msg %= indent_string, exc.version, len(data)
308 308 ui.write(msg)
309 309 else:
310 310 msg = "%sversion: %d (%d bytes)\n"
311 311 msg %= indent_string, version, len(data)
312 312 ui.write(msg)
313 313 fm = ui.formatter('debugobsolete', opts)
314 314 for rawmarker in sorted(markers):
315 315 m = obsutil.marker(None, rawmarker)
316 316 fm.startitem()
317 317 fm.plain(indent_string)
318 318 cmdutil.showmarker(fm, m)
319 319 fm.end()
320 320
321 321 def _debugphaseheads(ui, data, indent=0):
322 322 """display version and markers contained in 'data'"""
323 323 indent_string = ' ' * indent
324 324 headsbyphase = phases.binarydecode(data)
325 325 for phase in phases.allphases:
326 326 for head in headsbyphase[phase]:
327 327 ui.write(indent_string)
328 328 ui.write('%s %s\n' % (hex(head), phases.phasenames[phase]))
329 329
330 330 def _quasirepr(thing):
331 331 if isinstance(thing, (dict, util.sortdict, collections.OrderedDict)):
332 332 return '{%s}' % (
333 333 b', '.join(b'%s: %s' % (k, thing[k]) for k in sorted(thing)))
334 334 return pycompat.bytestr(repr(thing))
335 335
336 336 def _debugbundle2(ui, gen, all=None, **opts):
337 337 """lists the contents of a bundle2"""
338 338 if not isinstance(gen, bundle2.unbundle20):
339 339 raise error.Abort(_('not a bundle2 file'))
340 340 ui.write(('Stream params: %s\n' % _quasirepr(gen.params)))
341 341 parttypes = opts.get(r'part_type', [])
342 342 for part in gen.iterparts():
343 343 if parttypes and part.type not in parttypes:
344 344 continue
345 345 msg = '%s -- %s (mandatory: %r)\n'
346 346 ui.write((msg % (part.type, _quasirepr(part.params), part.mandatory)))
347 347 if part.type == 'changegroup':
348 348 version = part.params.get('version', '01')
349 349 cg = changegroup.getunbundler(version, part, 'UN')
350 350 if not ui.quiet:
351 351 _debugchangegroup(ui, cg, all=all, indent=4, **opts)
352 352 if part.type == 'obsmarkers':
353 353 if not ui.quiet:
354 354 _debugobsmarkers(ui, part, indent=4, **opts)
355 355 if part.type == 'phase-heads':
356 356 if not ui.quiet:
357 357 _debugphaseheads(ui, part, indent=4)
358 358
359 359 @command('debugbundle',
360 360 [('a', 'all', None, _('show all details')),
361 361 ('', 'part-type', [], _('show only the named part type')),
362 362 ('', 'spec', None, _('print the bundlespec of the bundle'))],
363 363 _('FILE'),
364 364 norepo=True)
365 365 def debugbundle(ui, bundlepath, all=None, spec=None, **opts):
366 366 """lists the contents of a bundle"""
367 367 with hg.openpath(ui, bundlepath) as f:
368 368 if spec:
369 369 spec = exchange.getbundlespec(ui, f)
370 370 ui.write('%s\n' % spec)
371 371 return
372 372
373 373 gen = exchange.readbundle(ui, f, bundlepath)
374 374 if isinstance(gen, bundle2.unbundle20):
375 375 return _debugbundle2(ui, gen, all=all, **opts)
376 376 _debugchangegroup(ui, gen, all=all, **opts)
377 377
378 378 @command('debugcapabilities',
379 379 [], _('PATH'),
380 380 norepo=True)
381 381 def debugcapabilities(ui, path, **opts):
382 382 """lists the capabilities of a remote peer"""
383 383 opts = pycompat.byteskwargs(opts)
384 384 peer = hg.peer(ui, opts, path)
385 385 caps = peer.capabilities()
386 386 ui.write(('Main capabilities:\n'))
387 387 for c in sorted(caps):
388 388 ui.write((' %s\n') % c)
389 389 b2caps = bundle2.bundle2caps(peer)
390 390 if b2caps:
391 391 ui.write(('Bundle2 capabilities:\n'))
392 392 for key, values in sorted(b2caps.iteritems()):
393 393 ui.write((' %s\n') % key)
394 394 for v in values:
395 395 ui.write((' %s\n') % v)
396 396
397 397 @command('debugcheckstate', [], '')
398 398 def debugcheckstate(ui, repo):
399 399 """validate the correctness of the current dirstate"""
400 400 parent1, parent2 = repo.dirstate.parents()
401 401 m1 = repo[parent1].manifest()
402 402 m2 = repo[parent2].manifest()
403 403 errors = 0
404 404 for f in repo.dirstate:
405 405 state = repo.dirstate[f]
406 406 if state in "nr" and f not in m1:
407 407 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
408 408 errors += 1
409 409 if state in "a" and f in m1:
410 410 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
411 411 errors += 1
412 412 if state in "m" and f not in m1 and f not in m2:
413 413 ui.warn(_("%s in state %s, but not in either manifest\n") %
414 414 (f, state))
415 415 errors += 1
416 416 for f in m1:
417 417 state = repo.dirstate[f]
418 418 if state not in "nrm":
419 419 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
420 420 errors += 1
421 421 if errors:
422 422 error = _(".hg/dirstate inconsistent with current parent's manifest")
423 423 raise error.Abort(error)
424 424
425 425 @command('debugcolor',
426 426 [('', 'style', None, _('show all configured styles'))],
427 427 'hg debugcolor')
428 428 def debugcolor(ui, repo, **opts):
429 429 """show available color, effects or style"""
430 ui.write(('color mode: %s\n') % ui._colormode)
430 ui.write(('color mode: %s\n') % stringutil.pprint(ui._colormode))
431 431 if opts.get(r'style'):
432 432 return _debugdisplaystyle(ui)
433 433 else:
434 434 return _debugdisplaycolor(ui)
435 435
436 436 def _debugdisplaycolor(ui):
437 437 ui = ui.copy()
438 438 ui._styles.clear()
439 439 for effect in color._activeeffects(ui).keys():
440 440 ui._styles[effect] = effect
441 441 if ui._terminfoparams:
442 442 for k, v in ui.configitems('color'):
443 443 if k.startswith('color.'):
444 444 ui._styles[k] = k[6:]
445 445 elif k.startswith('terminfo.'):
446 446 ui._styles[k] = k[9:]
447 447 ui.write(_('available colors:\n'))
448 448 # sort label with a '_' after the other to group '_background' entry.
449 449 items = sorted(ui._styles.items(),
450 450 key=lambda i: ('_' in i[0], i[0], i[1]))
451 451 for colorname, label in items:
452 452 ui.write(('%s\n') % colorname, label=label)
453 453
454 454 def _debugdisplaystyle(ui):
455 455 ui.write(_('available style:\n'))
456 456 if not ui._styles:
457 457 return
458 458 width = max(len(s) for s in ui._styles)
459 459 for label, effects in sorted(ui._styles.items()):
460 460 ui.write('%s' % label, label=label)
461 461 if effects:
462 462 # 50
463 463 ui.write(': ')
464 464 ui.write(' ' * (max(0, width - len(label))))
465 465 ui.write(', '.join(ui.label(e, e) for e in effects.split()))
466 466 ui.write('\n')
467 467
468 468 @command('debugcreatestreamclonebundle', [], 'FILE')
469 469 def debugcreatestreamclonebundle(ui, repo, fname):
470 470 """create a stream clone bundle file
471 471
472 472 Stream bundles are special bundles that are essentially archives of
473 473 revlog files. They are commonly used for cloning very quickly.
474 474 """
475 475 # TODO we may want to turn this into an abort when this functionality
476 476 # is moved into `hg bundle`.
477 477 if phases.hassecret(repo):
478 478 ui.warn(_('(warning: stream clone bundle will contain secret '
479 479 'revisions)\n'))
480 480
481 481 requirements, gen = streamclone.generatebundlev1(repo)
482 482 changegroup.writechunks(ui, gen, fname)
483 483
484 484 ui.write(_('bundle requirements: %s\n') % ', '.join(sorted(requirements)))
485 485
486 486 @command('debugdag',
487 487 [('t', 'tags', None, _('use tags as labels')),
488 488 ('b', 'branches', None, _('annotate with branch names')),
489 489 ('', 'dots', None, _('use dots for runs')),
490 490 ('s', 'spaces', None, _('separate elements by spaces'))],
491 491 _('[OPTION]... [FILE [REV]...]'),
492 492 optionalrepo=True)
493 493 def debugdag(ui, repo, file_=None, *revs, **opts):
494 494 """format the changelog or an index DAG as a concise textual description
495 495
496 496 If you pass a revlog index, the revlog's DAG is emitted. If you list
497 497 revision numbers, they get labeled in the output as rN.
498 498
499 499 Otherwise, the changelog DAG of the current repo is emitted.
500 500 """
501 501 spaces = opts.get(r'spaces')
502 502 dots = opts.get(r'dots')
503 503 if file_:
504 504 rlog = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False),
505 505 file_)
506 506 revs = set((int(r) for r in revs))
507 507 def events():
508 508 for r in rlog:
509 509 yield 'n', (r, list(p for p in rlog.parentrevs(r)
510 510 if p != -1))
511 511 if r in revs:
512 512 yield 'l', (r, "r%i" % r)
513 513 elif repo:
514 514 cl = repo.changelog
515 515 tags = opts.get(r'tags')
516 516 branches = opts.get(r'branches')
517 517 if tags:
518 518 labels = {}
519 519 for l, n in repo.tags().items():
520 520 labels.setdefault(cl.rev(n), []).append(l)
521 521 def events():
522 522 b = "default"
523 523 for r in cl:
524 524 if branches:
525 525 newb = cl.read(cl.node(r))[5]['branch']
526 526 if newb != b:
527 527 yield 'a', newb
528 528 b = newb
529 529 yield 'n', (r, list(p for p in cl.parentrevs(r)
530 530 if p != -1))
531 531 if tags:
532 532 ls = labels.get(r)
533 533 if ls:
534 534 for l in ls:
535 535 yield 'l', (r, l)
536 536 else:
537 537 raise error.Abort(_('need repo for changelog dag'))
538 538
539 539 for line in dagparser.dagtextlines(events(),
540 540 addspaces=spaces,
541 541 wraplabels=True,
542 542 wrapannotations=True,
543 543 wrapnonlinear=dots,
544 544 usedots=dots,
545 545 maxlinewidth=70):
546 546 ui.write(line)
547 547 ui.write("\n")
548 548
549 549 @command('debugdata', cmdutil.debugrevlogopts, _('-c|-m|FILE REV'))
550 550 def debugdata(ui, repo, file_, rev=None, **opts):
551 551 """dump the contents of a data file revision"""
552 552 opts = pycompat.byteskwargs(opts)
553 553 if opts.get('changelog') or opts.get('manifest') or opts.get('dir'):
554 554 if rev is not None:
555 555 raise error.CommandError('debugdata', _('invalid arguments'))
556 556 file_, rev = None, file_
557 557 elif rev is None:
558 558 raise error.CommandError('debugdata', _('invalid arguments'))
559 559 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
560 560 try:
561 561 ui.write(r.revision(r.lookup(rev), raw=True))
562 562 except KeyError:
563 563 raise error.Abort(_('invalid revision identifier %s') % rev)
564 564
565 565 @command('debugdate',
566 566 [('e', 'extended', None, _('try extended date formats'))],
567 567 _('[-e] DATE [RANGE]'),
568 568 norepo=True, optionalrepo=True)
569 569 def debugdate(ui, date, range=None, **opts):
570 570 """parse and display a date"""
571 571 if opts[r"extended"]:
572 572 d = dateutil.parsedate(date, util.extendeddateformats)
573 573 else:
574 574 d = dateutil.parsedate(date)
575 575 ui.write(("internal: %d %d\n") % d)
576 576 ui.write(("standard: %s\n") % dateutil.datestr(d))
577 577 if range:
578 578 m = dateutil.matchdate(range)
579 579 ui.write(("match: %s\n") % m(d[0]))
580 580
581 581 @command('debugdeltachain',
582 582 cmdutil.debugrevlogopts + cmdutil.formatteropts,
583 583 _('-c|-m|FILE'),
584 584 optionalrepo=True)
585 585 def debugdeltachain(ui, repo, file_=None, **opts):
586 586 """dump information about delta chains in a revlog
587 587
588 588 Output can be templatized. Available template keywords are:
589 589
590 590 :``rev``: revision number
591 591 :``chainid``: delta chain identifier (numbered by unique base)
592 592 :``chainlen``: delta chain length to this revision
593 593 :``prevrev``: previous revision in delta chain
594 594 :``deltatype``: role of delta / how it was computed
595 595 :``compsize``: compressed size of revision
596 596 :``uncompsize``: uncompressed size of revision
597 597 :``chainsize``: total size of compressed revisions in chain
598 598 :``chainratio``: total chain size divided by uncompressed revision size
599 599 (new delta chains typically start at ratio 2.00)
600 600 :``lindist``: linear distance from base revision in delta chain to end
601 601 of this revision
602 602 :``extradist``: total size of revisions not part of this delta chain from
603 603 base of delta chain to end of this revision; a measurement
604 604 of how much extra data we need to read/seek across to read
605 605 the delta chain for this revision
606 606 :``extraratio``: extradist divided by chainsize; another representation of
607 607 how much unrelated data is needed to load this delta chain
608 608
609 609 If the repository is configured to use the sparse read, additional keywords
610 610 are available:
611 611
612 612 :``readsize``: total size of data read from the disk for a revision
613 613 (sum of the sizes of all the blocks)
614 614 :``largestblock``: size of the largest block of data read from the disk
615 615 :``readdensity``: density of useful bytes in the data read from the disk
616 616 :``srchunks``: in how many data hunks the whole revision would be read
617 617
618 618 The sparse read can be enabled with experimental.sparse-read = True
619 619 """
620 620 opts = pycompat.byteskwargs(opts)
621 621 r = cmdutil.openrevlog(repo, 'debugdeltachain', file_, opts)
622 622 index = r.index
623 623 start = r.start
624 624 length = r.length
625 625 generaldelta = r.version & revlog.FLAG_GENERALDELTA
626 626 withsparseread = getattr(r, '_withsparseread', False)
627 627
628 628 def revinfo(rev):
629 629 e = index[rev]
630 630 compsize = e[1]
631 631 uncompsize = e[2]
632 632 chainsize = 0
633 633
634 634 if generaldelta:
635 635 if e[3] == e[5]:
636 636 deltatype = 'p1'
637 637 elif e[3] == e[6]:
638 638 deltatype = 'p2'
639 639 elif e[3] == rev - 1:
640 640 deltatype = 'prev'
641 641 elif e[3] == rev:
642 642 deltatype = 'base'
643 643 else:
644 644 deltatype = 'other'
645 645 else:
646 646 if e[3] == rev:
647 647 deltatype = 'base'
648 648 else:
649 649 deltatype = 'prev'
650 650
651 651 chain = r._deltachain(rev)[0]
652 652 for iterrev in chain:
653 653 e = index[iterrev]
654 654 chainsize += e[1]
655 655
656 656 return compsize, uncompsize, deltatype, chain, chainsize
657 657
658 658 fm = ui.formatter('debugdeltachain', opts)
659 659
660 660 fm.plain(' rev chain# chainlen prev delta '
661 661 'size rawsize chainsize ratio lindist extradist '
662 662 'extraratio')
663 663 if withsparseread:
664 664 fm.plain(' readsize largestblk rddensity srchunks')
665 665 fm.plain('\n')
666 666
667 667 chainbases = {}
668 668 for rev in r:
669 669 comp, uncomp, deltatype, chain, chainsize = revinfo(rev)
670 670 chainbase = chain[0]
671 671 chainid = chainbases.setdefault(chainbase, len(chainbases) + 1)
672 672 basestart = start(chainbase)
673 673 revstart = start(rev)
674 674 lineardist = revstart + comp - basestart
675 675 extradist = lineardist - chainsize
676 676 try:
677 677 prevrev = chain[-2]
678 678 except IndexError:
679 679 prevrev = -1
680 680
681 681 chainratio = float(chainsize) / float(uncomp)
682 682 extraratio = float(extradist) / float(chainsize)
683 683
684 684 fm.startitem()
685 685 fm.write('rev chainid chainlen prevrev deltatype compsize '
686 686 'uncompsize chainsize chainratio lindist extradist '
687 687 'extraratio',
688 688 '%7d %7d %8d %8d %7s %10d %10d %10d %9.5f %9d %9d %10.5f',
689 689 rev, chainid, len(chain), prevrev, deltatype, comp,
690 690 uncomp, chainsize, chainratio, lineardist, extradist,
691 691 extraratio,
692 692 rev=rev, chainid=chainid, chainlen=len(chain),
693 693 prevrev=prevrev, deltatype=deltatype, compsize=comp,
694 694 uncompsize=uncomp, chainsize=chainsize,
695 695 chainratio=chainratio, lindist=lineardist,
696 696 extradist=extradist, extraratio=extraratio)
697 697 if withsparseread:
698 698 readsize = 0
699 699 largestblock = 0
700 700 srchunks = 0
701 701
702 702 for revschunk in revlog._slicechunk(r, chain):
703 703 srchunks += 1
704 704 blkend = start(revschunk[-1]) + length(revschunk[-1])
705 705 blksize = blkend - start(revschunk[0])
706 706
707 707 readsize += blksize
708 708 if largestblock < blksize:
709 709 largestblock = blksize
710 710
711 711 readdensity = float(chainsize) / float(readsize)
712 712
713 713 fm.write('readsize largestblock readdensity srchunks',
714 714 ' %10d %10d %9.5f %8d',
715 715 readsize, largestblock, readdensity, srchunks,
716 716 readsize=readsize, largestblock=largestblock,
717 717 readdensity=readdensity, srchunks=srchunks)
718 718
719 719 fm.plain('\n')
720 720
721 721 fm.end()
722 722
723 723 @command('debugdirstate|debugstate',
724 724 [('', 'nodates', None, _('do not display the saved mtime')),
725 725 ('', 'datesort', None, _('sort by saved mtime'))],
726 726 _('[OPTION]...'))
727 727 def debugstate(ui, repo, **opts):
728 728 """show the contents of the current dirstate"""
729 729
730 730 nodates = opts.get(r'nodates')
731 731 datesort = opts.get(r'datesort')
732 732
733 733 timestr = ""
734 734 if datesort:
735 735 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
736 736 else:
737 737 keyfunc = None # sort by filename
738 738 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
739 739 if ent[3] == -1:
740 740 timestr = 'unset '
741 741 elif nodates:
742 742 timestr = 'set '
743 743 else:
744 744 timestr = time.strftime(r"%Y-%m-%d %H:%M:%S ",
745 745 time.localtime(ent[3]))
746 746 timestr = encoding.strtolocal(timestr)
747 747 if ent[1] & 0o20000:
748 748 mode = 'lnk'
749 749 else:
750 750 mode = '%3o' % (ent[1] & 0o777 & ~util.umask)
751 751 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
752 752 for f in repo.dirstate.copies():
753 753 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
754 754
755 755 @command('debugdiscovery',
756 756 [('', 'old', None, _('use old-style discovery')),
757 757 ('', 'nonheads', None,
758 758 _('use old-style discovery with non-heads included')),
759 759 ('', 'rev', [], 'restrict discovery to this set of revs'),
760 760 ] + cmdutil.remoteopts,
761 761 _('[--rev REV] [OTHER]'))
762 762 def debugdiscovery(ui, repo, remoteurl="default", **opts):
763 763 """runs the changeset discovery protocol in isolation"""
764 764 opts = pycompat.byteskwargs(opts)
765 765 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl))
766 766 remote = hg.peer(repo, opts, remoteurl)
767 767 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
768 768
769 769 # make sure tests are repeatable
770 770 random.seed(12323)
771 771
772 772 def doit(pushedrevs, remoteheads, remote=remote):
773 773 if opts.get('old'):
774 774 if not util.safehasattr(remote, 'branches'):
775 775 # enable in-client legacy support
776 776 remote = localrepo.locallegacypeer(remote.local())
777 777 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
778 778 force=True)
779 779 common = set(common)
780 780 if not opts.get('nonheads'):
781 781 ui.write(("unpruned common: %s\n") %
782 782 " ".join(sorted(short(n) for n in common)))
783 783 dag = dagutil.revlogdag(repo.changelog)
784 784 all = dag.ancestorset(dag.internalizeall(common))
785 785 common = dag.externalizeall(dag.headsetofconnecteds(all))
786 786 else:
787 787 nodes = None
788 788 if pushedrevs:
789 789 revs = scmutil.revrange(repo, pushedrevs)
790 790 nodes = [repo[r].node() for r in revs]
791 791 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote,
792 792 ancestorsof=nodes)
793 793 common = set(common)
794 794 rheads = set(hds)
795 795 lheads = set(repo.heads())
796 796 ui.write(("common heads: %s\n") %
797 797 " ".join(sorted(short(n) for n in common)))
798 798 if lheads <= common:
799 799 ui.write(("local is subset\n"))
800 800 elif rheads <= common:
801 801 ui.write(("remote is subset\n"))
802 802
803 803 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches, revs=None)
804 804 localrevs = opts['rev']
805 805 doit(localrevs, remoterevs)
806 806
807 807 _chunksize = 4 << 10
808 808
809 809 @command('debugdownload',
810 810 [
811 811 ('o', 'output', '', _('path')),
812 812 ],
813 813 optionalrepo=True)
814 814 def debugdownload(ui, repo, url, output=None, **opts):
815 815 """download a resource using Mercurial logic and config
816 816 """
817 817 fh = urlmod.open(ui, url, output)
818 818
819 819 dest = ui
820 820 if output:
821 821 dest = open(output, "wb", _chunksize)
822 822 try:
823 823 data = fh.read(_chunksize)
824 824 while data:
825 825 dest.write(data)
826 826 data = fh.read(_chunksize)
827 827 finally:
828 828 if output:
829 829 dest.close()
830 830
831 831 @command('debugextensions', cmdutil.formatteropts, [], optionalrepo=True)
832 832 def debugextensions(ui, repo, **opts):
833 833 '''show information about active extensions'''
834 834 opts = pycompat.byteskwargs(opts)
835 835 exts = extensions.extensions(ui)
836 836 hgver = util.version()
837 837 fm = ui.formatter('debugextensions', opts)
838 838 for extname, extmod in sorted(exts, key=operator.itemgetter(0)):
839 839 isinternal = extensions.ismoduleinternal(extmod)
840 840 extsource = pycompat.fsencode(extmod.__file__)
841 841 if isinternal:
842 842 exttestedwith = [] # never expose magic string to users
843 843 else:
844 844 exttestedwith = getattr(extmod, 'testedwith', '').split()
845 845 extbuglink = getattr(extmod, 'buglink', None)
846 846
847 847 fm.startitem()
848 848
849 849 if ui.quiet or ui.verbose:
850 850 fm.write('name', '%s\n', extname)
851 851 else:
852 852 fm.write('name', '%s', extname)
853 853 if isinternal or hgver in exttestedwith:
854 854 fm.plain('\n')
855 855 elif not exttestedwith:
856 856 fm.plain(_(' (untested!)\n'))
857 857 else:
858 858 lasttestedversion = exttestedwith[-1]
859 859 fm.plain(' (%s!)\n' % lasttestedversion)
860 860
861 861 fm.condwrite(ui.verbose and extsource, 'source',
862 862 _(' location: %s\n'), extsource or "")
863 863
864 864 if ui.verbose:
865 865 fm.plain(_(' bundled: %s\n') % ['no', 'yes'][isinternal])
866 866 fm.data(bundled=isinternal)
867 867
868 868 fm.condwrite(ui.verbose and exttestedwith, 'testedwith',
869 869 _(' tested with: %s\n'),
870 870 fm.formatlist(exttestedwith, name='ver'))
871 871
872 872 fm.condwrite(ui.verbose and extbuglink, 'buglink',
873 873 _(' bug reporting: %s\n'), extbuglink or "")
874 874
875 875 fm.end()
876 876
877 877 @command('debugfileset',
878 878 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
879 879 _('[-r REV] FILESPEC'))
880 880 def debugfileset(ui, repo, expr, **opts):
881 881 '''parse and apply a fileset specification'''
882 882 ctx = scmutil.revsingle(repo, opts.get(r'rev'), None)
883 883 if ui.verbose:
884 884 tree = fileset.parse(expr)
885 885 ui.note(fileset.prettyformat(tree), "\n")
886 886
887 887 for f in ctx.getfileset(expr):
888 888 ui.write("%s\n" % f)
889 889
890 890 @command('debugformat',
891 891 [] + cmdutil.formatteropts,
892 892 _(''))
893 893 def debugformat(ui, repo, **opts):
894 894 """display format information about the current repository
895 895
896 896 Use --verbose to get extra information about current config value and
897 897 Mercurial default."""
898 898 opts = pycompat.byteskwargs(opts)
899 899 maxvariantlength = max(len(fv.name) for fv in upgrade.allformatvariant)
900 900 maxvariantlength = max(len('format-variant'), maxvariantlength)
901 901
902 902 def makeformatname(name):
903 903 return '%s:' + (' ' * (maxvariantlength - len(name)))
904 904
905 905 fm = ui.formatter('debugformat', opts)
906 906 if fm.isplain():
907 907 def formatvalue(value):
908 908 if util.safehasattr(value, 'startswith'):
909 909 return value
910 910 if value:
911 911 return 'yes'
912 912 else:
913 913 return 'no'
914 914 else:
915 915 formatvalue = pycompat.identity
916 916
917 917 fm.plain('format-variant')
918 918 fm.plain(' ' * (maxvariantlength - len('format-variant')))
919 919 fm.plain(' repo')
920 920 if ui.verbose:
921 921 fm.plain(' config default')
922 922 fm.plain('\n')
923 923 for fv in upgrade.allformatvariant:
924 924 fm.startitem()
925 925 repovalue = fv.fromrepo(repo)
926 926 configvalue = fv.fromconfig(repo)
927 927
928 928 if repovalue != configvalue:
929 929 namelabel = 'formatvariant.name.mismatchconfig'
930 930 repolabel = 'formatvariant.repo.mismatchconfig'
931 931 elif repovalue != fv.default:
932 932 namelabel = 'formatvariant.name.mismatchdefault'
933 933 repolabel = 'formatvariant.repo.mismatchdefault'
934 934 else:
935 935 namelabel = 'formatvariant.name.uptodate'
936 936 repolabel = 'formatvariant.repo.uptodate'
937 937
938 938 fm.write('name', makeformatname(fv.name), fv.name,
939 939 label=namelabel)
940 940 fm.write('repo', ' %3s', formatvalue(repovalue),
941 941 label=repolabel)
942 942 if fv.default != configvalue:
943 943 configlabel = 'formatvariant.config.special'
944 944 else:
945 945 configlabel = 'formatvariant.config.default'
946 946 fm.condwrite(ui.verbose, 'config', ' %6s', formatvalue(configvalue),
947 947 label=configlabel)
948 948 fm.condwrite(ui.verbose, 'default', ' %7s', formatvalue(fv.default),
949 949 label='formatvariant.default')
950 950 fm.plain('\n')
951 951 fm.end()
952 952
953 953 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
954 954 def debugfsinfo(ui, path="."):
955 955 """show information detected about current filesystem"""
956 956 ui.write(('path: %s\n') % path)
957 957 ui.write(('mounted on: %s\n') % (util.getfsmountpoint(path) or '(unknown)'))
958 958 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
959 959 ui.write(('fstype: %s\n') % (util.getfstype(path) or '(unknown)'))
960 960 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
961 961 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
962 962 casesensitive = '(unknown)'
963 963 try:
964 964 with pycompat.namedtempfile(prefix='.debugfsinfo', dir=path) as f:
965 965 casesensitive = util.fscasesensitive(f.name) and 'yes' or 'no'
966 966 except OSError:
967 967 pass
968 968 ui.write(('case-sensitive: %s\n') % casesensitive)
969 969
970 970 @command('debuggetbundle',
971 971 [('H', 'head', [], _('id of head node'), _('ID')),
972 972 ('C', 'common', [], _('id of common node'), _('ID')),
973 973 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
974 974 _('REPO FILE [-H|-C ID]...'),
975 975 norepo=True)
976 976 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
977 977 """retrieves a bundle from a repo
978 978
979 979 Every ID must be a full-length hex node id string. Saves the bundle to the
980 980 given file.
981 981 """
982 982 opts = pycompat.byteskwargs(opts)
983 983 repo = hg.peer(ui, opts, repopath)
984 984 if not repo.capable('getbundle'):
985 985 raise error.Abort("getbundle() not supported by target repository")
986 986 args = {}
987 987 if common:
988 988 args[r'common'] = [bin(s) for s in common]
989 989 if head:
990 990 args[r'heads'] = [bin(s) for s in head]
991 991 # TODO: get desired bundlecaps from command line.
992 992 args[r'bundlecaps'] = None
993 993 bundle = repo.getbundle('debug', **args)
994 994
995 995 bundletype = opts.get('type', 'bzip2').lower()
996 996 btypes = {'none': 'HG10UN',
997 997 'bzip2': 'HG10BZ',
998 998 'gzip': 'HG10GZ',
999 999 'bundle2': 'HG20'}
1000 1000 bundletype = btypes.get(bundletype)
1001 1001 if bundletype not in bundle2.bundletypes:
1002 1002 raise error.Abort(_('unknown bundle type specified with --type'))
1003 1003 bundle2.writebundle(ui, bundle, bundlepath, bundletype)
1004 1004
1005 1005 @command('debugignore', [], '[FILE]')
1006 1006 def debugignore(ui, repo, *files, **opts):
1007 1007 """display the combined ignore pattern and information about ignored files
1008 1008
1009 1009 With no argument display the combined ignore pattern.
1010 1010
1011 1011 Given space separated file names, shows if the given file is ignored and
1012 1012 if so, show the ignore rule (file and line number) that matched it.
1013 1013 """
1014 1014 ignore = repo.dirstate._ignore
1015 1015 if not files:
1016 1016 # Show all the patterns
1017 1017 ui.write("%s\n" % pycompat.byterepr(ignore))
1018 1018 else:
1019 1019 m = scmutil.match(repo[None], pats=files)
1020 1020 for f in m.files():
1021 1021 nf = util.normpath(f)
1022 1022 ignored = None
1023 1023 ignoredata = None
1024 1024 if nf != '.':
1025 1025 if ignore(nf):
1026 1026 ignored = nf
1027 1027 ignoredata = repo.dirstate._ignorefileandline(nf)
1028 1028 else:
1029 1029 for p in util.finddirs(nf):
1030 1030 if ignore(p):
1031 1031 ignored = p
1032 1032 ignoredata = repo.dirstate._ignorefileandline(p)
1033 1033 break
1034 1034 if ignored:
1035 1035 if ignored == nf:
1036 1036 ui.write(_("%s is ignored\n") % m.uipath(f))
1037 1037 else:
1038 1038 ui.write(_("%s is ignored because of "
1039 1039 "containing folder %s\n")
1040 1040 % (m.uipath(f), ignored))
1041 1041 ignorefile, lineno, line = ignoredata
1042 1042 ui.write(_("(ignore rule in %s, line %d: '%s')\n")
1043 1043 % (ignorefile, lineno, line))
1044 1044 else:
1045 1045 ui.write(_("%s is not ignored\n") % m.uipath(f))
1046 1046
1047 1047 @command('debugindex', cmdutil.debugrevlogopts +
1048 1048 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1049 1049 _('[-f FORMAT] -c|-m|FILE'),
1050 1050 optionalrepo=True)
1051 1051 def debugindex(ui, repo, file_=None, **opts):
1052 1052 """dump the contents of an index file"""
1053 1053 opts = pycompat.byteskwargs(opts)
1054 1054 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1055 1055 format = opts.get('format', 0)
1056 1056 if format not in (0, 1):
1057 1057 raise error.Abort(_("unknown format %d") % format)
1058 1058
1059 1059 if ui.debugflag:
1060 1060 shortfn = hex
1061 1061 else:
1062 1062 shortfn = short
1063 1063
1064 1064 # There might not be anything in r, so have a sane default
1065 1065 idlen = 12
1066 1066 for i in r:
1067 1067 idlen = len(shortfn(r.node(i)))
1068 1068 break
1069 1069
1070 1070 if format == 0:
1071 1071 if ui.verbose:
1072 1072 ui.write((" rev offset length linkrev"
1073 1073 " %s %s p2\n") % ("nodeid".ljust(idlen),
1074 1074 "p1".ljust(idlen)))
1075 1075 else:
1076 1076 ui.write((" rev linkrev %s %s p2\n") % (
1077 1077 "nodeid".ljust(idlen), "p1".ljust(idlen)))
1078 1078 elif format == 1:
1079 1079 if ui.verbose:
1080 1080 ui.write((" rev flag offset length size link p1"
1081 1081 " p2 %s\n") % "nodeid".rjust(idlen))
1082 1082 else:
1083 1083 ui.write((" rev flag size link p1 p2 %s\n") %
1084 1084 "nodeid".rjust(idlen))
1085 1085
1086 1086 for i in r:
1087 1087 node = r.node(i)
1088 1088 if format == 0:
1089 1089 try:
1090 1090 pp = r.parents(node)
1091 1091 except Exception:
1092 1092 pp = [nullid, nullid]
1093 1093 if ui.verbose:
1094 1094 ui.write("% 6d % 9d % 7d % 7d %s %s %s\n" % (
1095 1095 i, r.start(i), r.length(i), r.linkrev(i),
1096 1096 shortfn(node), shortfn(pp[0]), shortfn(pp[1])))
1097 1097 else:
1098 1098 ui.write("% 6d % 7d %s %s %s\n" % (
1099 1099 i, r.linkrev(i), shortfn(node), shortfn(pp[0]),
1100 1100 shortfn(pp[1])))
1101 1101 elif format == 1:
1102 1102 pr = r.parentrevs(i)
1103 1103 if ui.verbose:
1104 1104 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d %s\n" % (
1105 1105 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1106 1106 r.linkrev(i), pr[0], pr[1], shortfn(node)))
1107 1107 else:
1108 1108 ui.write("% 6d %04x % 8d % 6d % 6d % 6d %s\n" % (
1109 1109 i, r.flags(i), r.rawsize(i), r.linkrev(i), pr[0], pr[1],
1110 1110 shortfn(node)))
1111 1111
1112 1112 @command('debugindexdot', cmdutil.debugrevlogopts,
1113 1113 _('-c|-m|FILE'), optionalrepo=True)
1114 1114 def debugindexdot(ui, repo, file_=None, **opts):
1115 1115 """dump an index DAG as a graphviz dot file"""
1116 1116 opts = pycompat.byteskwargs(opts)
1117 1117 r = cmdutil.openrevlog(repo, 'debugindexdot', file_, opts)
1118 1118 ui.write(("digraph G {\n"))
1119 1119 for i in r:
1120 1120 node = r.node(i)
1121 1121 pp = r.parents(node)
1122 1122 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1123 1123 if pp[1] != nullid:
1124 1124 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1125 1125 ui.write("}\n")
1126 1126
1127 1127 @command('debuginstall', [] + cmdutil.formatteropts, '', norepo=True)
1128 1128 def debuginstall(ui, **opts):
1129 1129 '''test Mercurial installation
1130 1130
1131 1131 Returns 0 on success.
1132 1132 '''
1133 1133 opts = pycompat.byteskwargs(opts)
1134 1134
1135 1135 def writetemp(contents):
1136 1136 (fd, name) = pycompat.mkstemp(prefix="hg-debuginstall-")
1137 1137 f = os.fdopen(fd, r"wb")
1138 1138 f.write(contents)
1139 1139 f.close()
1140 1140 return name
1141 1141
1142 1142 problems = 0
1143 1143
1144 1144 fm = ui.formatter('debuginstall', opts)
1145 1145 fm.startitem()
1146 1146
1147 1147 # encoding
1148 1148 fm.write('encoding', _("checking encoding (%s)...\n"), encoding.encoding)
1149 1149 err = None
1150 1150 try:
1151 1151 codecs.lookup(pycompat.sysstr(encoding.encoding))
1152 1152 except LookupError as inst:
1153 1153 err = stringutil.forcebytestr(inst)
1154 1154 problems += 1
1155 1155 fm.condwrite(err, 'encodingerror', _(" %s\n"
1156 1156 " (check that your locale is properly set)\n"), err)
1157 1157
1158 1158 # Python
1159 1159 fm.write('pythonexe', _("checking Python executable (%s)\n"),
1160 1160 pycompat.sysexecutable)
1161 1161 fm.write('pythonver', _("checking Python version (%s)\n"),
1162 1162 ("%d.%d.%d" % sys.version_info[:3]))
1163 1163 fm.write('pythonlib', _("checking Python lib (%s)...\n"),
1164 1164 os.path.dirname(pycompat.fsencode(os.__file__)))
1165 1165
1166 1166 security = set(sslutil.supportedprotocols)
1167 1167 if sslutil.hassni:
1168 1168 security.add('sni')
1169 1169
1170 1170 fm.write('pythonsecurity', _("checking Python security support (%s)\n"),
1171 1171 fm.formatlist(sorted(security), name='protocol',
1172 1172 fmt='%s', sep=','))
1173 1173
1174 1174 # These are warnings, not errors. So don't increment problem count. This
1175 1175 # may change in the future.
1176 1176 if 'tls1.2' not in security:
1177 1177 fm.plain(_(' TLS 1.2 not supported by Python install; '
1178 1178 'network connections lack modern security\n'))
1179 1179 if 'sni' not in security:
1180 1180 fm.plain(_(' SNI not supported by Python install; may have '
1181 1181 'connectivity issues with some servers\n'))
1182 1182
1183 1183 # TODO print CA cert info
1184 1184
1185 1185 # hg version
1186 1186 hgver = util.version()
1187 1187 fm.write('hgver', _("checking Mercurial version (%s)\n"),
1188 1188 hgver.split('+')[0])
1189 1189 fm.write('hgverextra', _("checking Mercurial custom build (%s)\n"),
1190 1190 '+'.join(hgver.split('+')[1:]))
1191 1191
1192 1192 # compiled modules
1193 1193 fm.write('hgmodulepolicy', _("checking module policy (%s)\n"),
1194 1194 policy.policy)
1195 1195 fm.write('hgmodules', _("checking installed modules (%s)...\n"),
1196 1196 os.path.dirname(pycompat.fsencode(__file__)))
1197 1197
1198 1198 if policy.policy in ('c', 'allow'):
1199 1199 err = None
1200 1200 try:
1201 1201 from .cext import (
1202 1202 base85,
1203 1203 bdiff,
1204 1204 mpatch,
1205 1205 osutil,
1206 1206 )
1207 1207 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
1208 1208 except Exception as inst:
1209 1209 err = stringutil.forcebytestr(inst)
1210 1210 problems += 1
1211 1211 fm.condwrite(err, 'extensionserror', " %s\n", err)
1212 1212
1213 1213 compengines = util.compengines._engines.values()
1214 1214 fm.write('compengines', _('checking registered compression engines (%s)\n'),
1215 1215 fm.formatlist(sorted(e.name() for e in compengines),
1216 1216 name='compengine', fmt='%s', sep=', '))
1217 1217 fm.write('compenginesavail', _('checking available compression engines '
1218 1218 '(%s)\n'),
1219 1219 fm.formatlist(sorted(e.name() for e in compengines
1220 1220 if e.available()),
1221 1221 name='compengine', fmt='%s', sep=', '))
1222 1222 wirecompengines = util.compengines.supportedwireengines(util.SERVERROLE)
1223 1223 fm.write('compenginesserver', _('checking available compression engines '
1224 1224 'for wire protocol (%s)\n'),
1225 1225 fm.formatlist([e.name() for e in wirecompengines
1226 1226 if e.wireprotosupport()],
1227 1227 name='compengine', fmt='%s', sep=', '))
1228 1228 re2 = 'missing'
1229 1229 if util._re2:
1230 1230 re2 = 'available'
1231 1231 fm.plain(_('checking "re2" regexp engine (%s)\n') % re2)
1232 1232 fm.data(re2=bool(util._re2))
1233 1233
1234 1234 # templates
1235 1235 p = templater.templatepaths()
1236 1236 fm.write('templatedirs', 'checking templates (%s)...\n', ' '.join(p))
1237 1237 fm.condwrite(not p, '', _(" no template directories found\n"))
1238 1238 if p:
1239 1239 m = templater.templatepath("map-cmdline.default")
1240 1240 if m:
1241 1241 # template found, check if it is working
1242 1242 err = None
1243 1243 try:
1244 1244 templater.templater.frommapfile(m)
1245 1245 except Exception as inst:
1246 1246 err = stringutil.forcebytestr(inst)
1247 1247 p = None
1248 1248 fm.condwrite(err, 'defaulttemplateerror', " %s\n", err)
1249 1249 else:
1250 1250 p = None
1251 1251 fm.condwrite(p, 'defaulttemplate',
1252 1252 _("checking default template (%s)\n"), m)
1253 1253 fm.condwrite(not m, 'defaulttemplatenotfound',
1254 1254 _(" template '%s' not found\n"), "default")
1255 1255 if not p:
1256 1256 problems += 1
1257 1257 fm.condwrite(not p, '',
1258 1258 _(" (templates seem to have been installed incorrectly)\n"))
1259 1259
1260 1260 # editor
1261 1261 editor = ui.geteditor()
1262 1262 editor = util.expandpath(editor)
1263 1263 editorbin = procutil.shellsplit(editor)[0]
1264 1264 fm.write('editor', _("checking commit editor... (%s)\n"), editorbin)
1265 1265 cmdpath = procutil.findexe(editorbin)
1266 1266 fm.condwrite(not cmdpath and editor == 'vi', 'vinotfound',
1267 1267 _(" No commit editor set and can't find %s in PATH\n"
1268 1268 " (specify a commit editor in your configuration"
1269 1269 " file)\n"), not cmdpath and editor == 'vi' and editorbin)
1270 1270 fm.condwrite(not cmdpath and editor != 'vi', 'editornotfound',
1271 1271 _(" Can't find editor '%s' in PATH\n"
1272 1272 " (specify a commit editor in your configuration"
1273 1273 " file)\n"), not cmdpath and editorbin)
1274 1274 if not cmdpath and editor != 'vi':
1275 1275 problems += 1
1276 1276
1277 1277 # check username
1278 1278 username = None
1279 1279 err = None
1280 1280 try:
1281 1281 username = ui.username()
1282 1282 except error.Abort as e:
1283 1283 err = stringutil.forcebytestr(e)
1284 1284 problems += 1
1285 1285
1286 1286 fm.condwrite(username, 'username', _("checking username (%s)\n"), username)
1287 1287 fm.condwrite(err, 'usernameerror', _("checking username...\n %s\n"
1288 1288 " (specify a username in your configuration file)\n"), err)
1289 1289
1290 1290 fm.condwrite(not problems, '',
1291 1291 _("no problems detected\n"))
1292 1292 if not problems:
1293 1293 fm.data(problems=problems)
1294 1294 fm.condwrite(problems, 'problems',
1295 1295 _("%d problems detected,"
1296 1296 " please check your install!\n"), problems)
1297 1297 fm.end()
1298 1298
1299 1299 return problems
1300 1300
1301 1301 @command('debugknown', [], _('REPO ID...'), norepo=True)
1302 1302 def debugknown(ui, repopath, *ids, **opts):
1303 1303 """test whether node ids are known to a repo
1304 1304
1305 1305 Every ID must be a full-length hex node id string. Returns a list of 0s
1306 1306 and 1s indicating unknown/known.
1307 1307 """
1308 1308 opts = pycompat.byteskwargs(opts)
1309 1309 repo = hg.peer(ui, opts, repopath)
1310 1310 if not repo.capable('known'):
1311 1311 raise error.Abort("known() not supported by target repository")
1312 1312 flags = repo.known([bin(s) for s in ids])
1313 1313 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1314 1314
1315 1315 @command('debuglabelcomplete', [], _('LABEL...'))
1316 1316 def debuglabelcomplete(ui, repo, *args):
1317 1317 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
1318 1318 debugnamecomplete(ui, repo, *args)
1319 1319
1320 1320 @command('debuglocks',
1321 1321 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
1322 1322 ('W', 'force-wlock', None,
1323 1323 _('free the working state lock (DANGEROUS)')),
1324 1324 ('s', 'set-lock', None, _('set the store lock until stopped')),
1325 1325 ('S', 'set-wlock', None,
1326 1326 _('set the working state lock until stopped'))],
1327 1327 _('[OPTION]...'))
1328 1328 def debuglocks(ui, repo, **opts):
1329 1329 """show or modify state of locks
1330 1330
1331 1331 By default, this command will show which locks are held. This
1332 1332 includes the user and process holding the lock, the amount of time
1333 1333 the lock has been held, and the machine name where the process is
1334 1334 running if it's not local.
1335 1335
1336 1336 Locks protect the integrity of Mercurial's data, so should be
1337 1337 treated with care. System crashes or other interruptions may cause
1338 1338 locks to not be properly released, though Mercurial will usually
1339 1339 detect and remove such stale locks automatically.
1340 1340
1341 1341 However, detecting stale locks may not always be possible (for
1342 1342 instance, on a shared filesystem). Removing locks may also be
1343 1343 blocked by filesystem permissions.
1344 1344
1345 1345 Setting a lock will prevent other commands from changing the data.
1346 1346 The command will wait until an interruption (SIGINT, SIGTERM, ...) occurs.
1347 1347 The set locks are removed when the command exits.
1348 1348
1349 1349 Returns 0 if no locks are held.
1350 1350
1351 1351 """
1352 1352
1353 1353 if opts.get(r'force_lock'):
1354 1354 repo.svfs.unlink('lock')
1355 1355 if opts.get(r'force_wlock'):
1356 1356 repo.vfs.unlink('wlock')
1357 1357 if opts.get(r'force_lock') or opts.get(r'force_wlock'):
1358 1358 return 0
1359 1359
1360 1360 locks = []
1361 1361 try:
1362 1362 if opts.get(r'set_wlock'):
1363 1363 try:
1364 1364 locks.append(repo.wlock(False))
1365 1365 except error.LockHeld:
1366 1366 raise error.Abort(_('wlock is already held'))
1367 1367 if opts.get(r'set_lock'):
1368 1368 try:
1369 1369 locks.append(repo.lock(False))
1370 1370 except error.LockHeld:
1371 1371 raise error.Abort(_('lock is already held'))
1372 1372 if len(locks):
1373 1373 ui.promptchoice(_("ready to release the lock (y)? $$ &Yes"))
1374 1374 return 0
1375 1375 finally:
1376 1376 release(*locks)
1377 1377
1378 1378 now = time.time()
1379 1379 held = 0
1380 1380
1381 1381 def report(vfs, name, method):
1382 1382 # this causes stale locks to get reaped for more accurate reporting
1383 1383 try:
1384 1384 l = method(False)
1385 1385 except error.LockHeld:
1386 1386 l = None
1387 1387
1388 1388 if l:
1389 1389 l.release()
1390 1390 else:
1391 1391 try:
1392 1392 st = vfs.lstat(name)
1393 1393 age = now - st[stat.ST_MTIME]
1394 1394 user = util.username(st.st_uid)
1395 1395 locker = vfs.readlock(name)
1396 1396 if ":" in locker:
1397 1397 host, pid = locker.split(':')
1398 1398 if host == socket.gethostname():
1399 1399 locker = 'user %s, process %s' % (user, pid)
1400 1400 else:
1401 1401 locker = 'user %s, process %s, host %s' \
1402 1402 % (user, pid, host)
1403 1403 ui.write(("%-6s %s (%ds)\n") % (name + ":", locker, age))
1404 1404 return 1
1405 1405 except OSError as e:
1406 1406 if e.errno != errno.ENOENT:
1407 1407 raise
1408 1408
1409 1409 ui.write(("%-6s free\n") % (name + ":"))
1410 1410 return 0
1411 1411
1412 1412 held += report(repo.svfs, "lock", repo.lock)
1413 1413 held += report(repo.vfs, "wlock", repo.wlock)
1414 1414
1415 1415 return held
1416 1416
1417 1417 @command('debugmergestate', [], '')
1418 1418 def debugmergestate(ui, repo, *args):
1419 1419 """print merge state
1420 1420
1421 1421 Use --verbose to print out information about whether v1 or v2 merge state
1422 1422 was chosen."""
1423 1423 def _hashornull(h):
1424 1424 if h == nullhex:
1425 1425 return 'null'
1426 1426 else:
1427 1427 return h
1428 1428
1429 1429 def printrecords(version):
1430 1430 ui.write(('* version %d records\n') % version)
1431 1431 if version == 1:
1432 1432 records = v1records
1433 1433 else:
1434 1434 records = v2records
1435 1435
1436 1436 for rtype, record in records:
1437 1437 # pretty print some record types
1438 1438 if rtype == 'L':
1439 1439 ui.write(('local: %s\n') % record)
1440 1440 elif rtype == 'O':
1441 1441 ui.write(('other: %s\n') % record)
1442 1442 elif rtype == 'm':
1443 1443 driver, mdstate = record.split('\0', 1)
1444 1444 ui.write(('merge driver: %s (state "%s")\n')
1445 1445 % (driver, mdstate))
1446 1446 elif rtype in 'FDC':
1447 1447 r = record.split('\0')
1448 1448 f, state, hash, lfile, afile, anode, ofile = r[0:7]
1449 1449 if version == 1:
1450 1450 onode = 'not stored in v1 format'
1451 1451 flags = r[7]
1452 1452 else:
1453 1453 onode, flags = r[7:9]
1454 1454 ui.write(('file: %s (record type "%s", state "%s", hash %s)\n')
1455 1455 % (f, rtype, state, _hashornull(hash)))
1456 1456 ui.write((' local path: %s (flags "%s")\n') % (lfile, flags))
1457 1457 ui.write((' ancestor path: %s (node %s)\n')
1458 1458 % (afile, _hashornull(anode)))
1459 1459 ui.write((' other path: %s (node %s)\n')
1460 1460 % (ofile, _hashornull(onode)))
1461 1461 elif rtype == 'f':
1462 1462 filename, rawextras = record.split('\0', 1)
1463 1463 extras = rawextras.split('\0')
1464 1464 i = 0
1465 1465 extrastrings = []
1466 1466 while i < len(extras):
1467 1467 extrastrings.append('%s = %s' % (extras[i], extras[i + 1]))
1468 1468 i += 2
1469 1469
1470 1470 ui.write(('file extras: %s (%s)\n')
1471 1471 % (filename, ', '.join(extrastrings)))
1472 1472 elif rtype == 'l':
1473 1473 labels = record.split('\0', 2)
1474 1474 labels = [l for l in labels if len(l) > 0]
1475 1475 ui.write(('labels:\n'))
1476 1476 ui.write((' local: %s\n' % labels[0]))
1477 1477 ui.write((' other: %s\n' % labels[1]))
1478 1478 if len(labels) > 2:
1479 1479 ui.write((' base: %s\n' % labels[2]))
1480 1480 else:
1481 1481 ui.write(('unrecognized entry: %s\t%s\n')
1482 1482 % (rtype, record.replace('\0', '\t')))
1483 1483
1484 1484 # Avoid mergestate.read() since it may raise an exception for unsupported
1485 1485 # merge state records. We shouldn't be doing this, but this is OK since this
1486 1486 # command is pretty low-level.
1487 1487 ms = mergemod.mergestate(repo)
1488 1488
1489 1489 # sort so that reasonable information is on top
1490 1490 v1records = ms._readrecordsv1()
1491 1491 v2records = ms._readrecordsv2()
1492 1492 order = 'LOml'
1493 1493 def key(r):
1494 1494 idx = order.find(r[0])
1495 1495 if idx == -1:
1496 1496 return (1, r[1])
1497 1497 else:
1498 1498 return (0, idx)
1499 1499 v1records.sort(key=key)
1500 1500 v2records.sort(key=key)
1501 1501
1502 1502 if not v1records and not v2records:
1503 1503 ui.write(('no merge state found\n'))
1504 1504 elif not v2records:
1505 1505 ui.note(('no version 2 merge state\n'))
1506 1506 printrecords(1)
1507 1507 elif ms._v1v2match(v1records, v2records):
1508 1508 ui.note(('v1 and v2 states match: using v2\n'))
1509 1509 printrecords(2)
1510 1510 else:
1511 1511 ui.note(('v1 and v2 states mismatch: using v1\n'))
1512 1512 printrecords(1)
1513 1513 if ui.verbose:
1514 1514 printrecords(2)
1515 1515
1516 1516 @command('debugnamecomplete', [], _('NAME...'))
1517 1517 def debugnamecomplete(ui, repo, *args):
1518 1518 '''complete "names" - tags, open branch names, bookmark names'''
1519 1519
1520 1520 names = set()
1521 1521 # since we previously only listed open branches, we will handle that
1522 1522 # specially (after this for loop)
1523 1523 for name, ns in repo.names.iteritems():
1524 1524 if name != 'branches':
1525 1525 names.update(ns.listnames(repo))
1526 1526 names.update(tag for (tag, heads, tip, closed)
1527 1527 in repo.branchmap().iterbranches() if not closed)
1528 1528 completions = set()
1529 1529 if not args:
1530 1530 args = ['']
1531 1531 for a in args:
1532 1532 completions.update(n for n in names if n.startswith(a))
1533 1533 ui.write('\n'.join(sorted(completions)))
1534 1534 ui.write('\n')
1535 1535
1536 1536 @command('debugobsolete',
1537 1537 [('', 'flags', 0, _('markers flag')),
1538 1538 ('', 'record-parents', False,
1539 1539 _('record parent information for the precursor')),
1540 1540 ('r', 'rev', [], _('display markers relevant to REV')),
1541 1541 ('', 'exclusive', False, _('restrict display to markers only '
1542 1542 'relevant to REV')),
1543 1543 ('', 'index', False, _('display index of the marker')),
1544 1544 ('', 'delete', [], _('delete markers specified by indices')),
1545 1545 ] + cmdutil.commitopts2 + cmdutil.formatteropts,
1546 1546 _('[OBSOLETED [REPLACEMENT ...]]'))
1547 1547 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
1548 1548 """create arbitrary obsolete marker
1549 1549
1550 1550 With no arguments, displays the list of obsolescence markers."""
1551 1551
1552 1552 opts = pycompat.byteskwargs(opts)
1553 1553
1554 1554 def parsenodeid(s):
1555 1555 try:
1556 1556 # We do not use revsingle/revrange functions here to accept
1557 1557 # arbitrary node identifiers, possibly not present in the
1558 1558 # local repository.
1559 1559 n = bin(s)
1560 1560 if len(n) != len(nullid):
1561 1561 raise TypeError()
1562 1562 return n
1563 1563 except TypeError:
1564 1564 raise error.Abort('changeset references must be full hexadecimal '
1565 1565 'node identifiers')
1566 1566
1567 1567 if opts.get('delete'):
1568 1568 indices = []
1569 1569 for v in opts.get('delete'):
1570 1570 try:
1571 1571 indices.append(int(v))
1572 1572 except ValueError:
1573 1573 raise error.Abort(_('invalid index value: %r') % v,
1574 1574 hint=_('use integers for indices'))
1575 1575
1576 1576 if repo.currenttransaction():
1577 1577 raise error.Abort(_('cannot delete obsmarkers in the middle '
1578 1578 'of transaction.'))
1579 1579
1580 1580 with repo.lock():
1581 1581 n = repair.deleteobsmarkers(repo.obsstore, indices)
1582 1582 ui.write(_('deleted %i obsolescence markers\n') % n)
1583 1583
1584 1584 return
1585 1585
1586 1586 if precursor is not None:
1587 1587 if opts['rev']:
1588 1588 raise error.Abort('cannot select revision when creating marker')
1589 1589 metadata = {}
1590 1590 metadata['user'] = opts['user'] or ui.username()
1591 1591 succs = tuple(parsenodeid(succ) for succ in successors)
1592 1592 l = repo.lock()
1593 1593 try:
1594 1594 tr = repo.transaction('debugobsolete')
1595 1595 try:
1596 1596 date = opts.get('date')
1597 1597 if date:
1598 1598 date = dateutil.parsedate(date)
1599 1599 else:
1600 1600 date = None
1601 1601 prec = parsenodeid(precursor)
1602 1602 parents = None
1603 1603 if opts['record_parents']:
1604 1604 if prec not in repo.unfiltered():
1605 1605 raise error.Abort('cannot used --record-parents on '
1606 1606 'unknown changesets')
1607 1607 parents = repo.unfiltered()[prec].parents()
1608 1608 parents = tuple(p.node() for p in parents)
1609 1609 repo.obsstore.create(tr, prec, succs, opts['flags'],
1610 1610 parents=parents, date=date,
1611 1611 metadata=metadata, ui=ui)
1612 1612 tr.close()
1613 1613 except ValueError as exc:
1614 1614 raise error.Abort(_('bad obsmarker input: %s') %
1615 1615 pycompat.bytestr(exc))
1616 1616 finally:
1617 1617 tr.release()
1618 1618 finally:
1619 1619 l.release()
1620 1620 else:
1621 1621 if opts['rev']:
1622 1622 revs = scmutil.revrange(repo, opts['rev'])
1623 1623 nodes = [repo[r].node() for r in revs]
1624 1624 markers = list(obsutil.getmarkers(repo, nodes=nodes,
1625 1625 exclusive=opts['exclusive']))
1626 1626 markers.sort(key=lambda x: x._data)
1627 1627 else:
1628 1628 markers = obsutil.getmarkers(repo)
1629 1629
1630 1630 markerstoiter = markers
1631 1631 isrelevant = lambda m: True
1632 1632 if opts.get('rev') and opts.get('index'):
1633 1633 markerstoiter = obsutil.getmarkers(repo)
1634 1634 markerset = set(markers)
1635 1635 isrelevant = lambda m: m in markerset
1636 1636
1637 1637 fm = ui.formatter('debugobsolete', opts)
1638 1638 for i, m in enumerate(markerstoiter):
1639 1639 if not isrelevant(m):
1640 1640 # marker can be irrelevant when we're iterating over a set
1641 1641 # of markers (markerstoiter) which is bigger than the set
1642 1642 # of markers we want to display (markers)
1643 1643 # this can happen if both --index and --rev options are
1644 1644 # provided and thus we need to iterate over all of the markers
1645 1645 # to get the correct indices, but only display the ones that
1646 1646 # are relevant to --rev value
1647 1647 continue
1648 1648 fm.startitem()
1649 1649 ind = i if opts.get('index') else None
1650 1650 cmdutil.showmarker(fm, m, index=ind)
1651 1651 fm.end()
1652 1652
1653 1653 @command('debugpathcomplete',
1654 1654 [('f', 'full', None, _('complete an entire path')),
1655 1655 ('n', 'normal', None, _('show only normal files')),
1656 1656 ('a', 'added', None, _('show only added files')),
1657 1657 ('r', 'removed', None, _('show only removed files'))],
1658 1658 _('FILESPEC...'))
1659 1659 def debugpathcomplete(ui, repo, *specs, **opts):
1660 1660 '''complete part or all of a tracked path
1661 1661
1662 1662 This command supports shells that offer path name completion. It
1663 1663 currently completes only files already known to the dirstate.
1664 1664
1665 1665 Completion extends only to the next path segment unless
1666 1666 --full is specified, in which case entire paths are used.'''
1667 1667
1668 1668 def complete(path, acceptable):
1669 1669 dirstate = repo.dirstate
1670 1670 spec = os.path.normpath(os.path.join(pycompat.getcwd(), path))
1671 1671 rootdir = repo.root + pycompat.ossep
1672 1672 if spec != repo.root and not spec.startswith(rootdir):
1673 1673 return [], []
1674 1674 if os.path.isdir(spec):
1675 1675 spec += '/'
1676 1676 spec = spec[len(rootdir):]
1677 1677 fixpaths = pycompat.ossep != '/'
1678 1678 if fixpaths:
1679 1679 spec = spec.replace(pycompat.ossep, '/')
1680 1680 speclen = len(spec)
1681 1681 fullpaths = opts[r'full']
1682 1682 files, dirs = set(), set()
1683 1683 adddir, addfile = dirs.add, files.add
1684 1684 for f, st in dirstate.iteritems():
1685 1685 if f.startswith(spec) and st[0] in acceptable:
1686 1686 if fixpaths:
1687 1687 f = f.replace('/', pycompat.ossep)
1688 1688 if fullpaths:
1689 1689 addfile(f)
1690 1690 continue
1691 1691 s = f.find(pycompat.ossep, speclen)
1692 1692 if s >= 0:
1693 1693 adddir(f[:s])
1694 1694 else:
1695 1695 addfile(f)
1696 1696 return files, dirs
1697 1697
1698 1698 acceptable = ''
1699 1699 if opts[r'normal']:
1700 1700 acceptable += 'nm'
1701 1701 if opts[r'added']:
1702 1702 acceptable += 'a'
1703 1703 if opts[r'removed']:
1704 1704 acceptable += 'r'
1705 1705 cwd = repo.getcwd()
1706 1706 if not specs:
1707 1707 specs = ['.']
1708 1708
1709 1709 files, dirs = set(), set()
1710 1710 for spec in specs:
1711 1711 f, d = complete(spec, acceptable or 'nmar')
1712 1712 files.update(f)
1713 1713 dirs.update(d)
1714 1714 files.update(dirs)
1715 1715 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
1716 1716 ui.write('\n')
1717 1717
1718 1718 @command('debugpeer', [], _('PATH'), norepo=True)
1719 1719 def debugpeer(ui, path):
1720 1720 """establish a connection to a peer repository"""
1721 1721 # Always enable peer request logging. Requires --debug to display
1722 1722 # though.
1723 1723 overrides = {
1724 1724 ('devel', 'debug.peer-request'): True,
1725 1725 }
1726 1726
1727 1727 with ui.configoverride(overrides):
1728 1728 peer = hg.peer(ui, {}, path)
1729 1729
1730 1730 local = peer.local() is not None
1731 1731 canpush = peer.canpush()
1732 1732
1733 1733 ui.write(_('url: %s\n') % peer.url())
1734 1734 ui.write(_('local: %s\n') % (_('yes') if local else _('no')))
1735 1735 ui.write(_('pushable: %s\n') % (_('yes') if canpush else _('no')))
1736 1736
1737 1737 @command('debugpickmergetool',
1738 1738 [('r', 'rev', '', _('check for files in this revision'), _('REV')),
1739 1739 ('', 'changedelete', None, _('emulate merging change and delete')),
1740 1740 ] + cmdutil.walkopts + cmdutil.mergetoolopts,
1741 1741 _('[PATTERN]...'),
1742 1742 inferrepo=True)
1743 1743 def debugpickmergetool(ui, repo, *pats, **opts):
1744 1744 """examine which merge tool is chosen for specified file
1745 1745
1746 1746 As described in :hg:`help merge-tools`, Mercurial examines
1747 1747 configurations below in this order to decide which merge tool is
1748 1748 chosen for specified file.
1749 1749
1750 1750 1. ``--tool`` option
1751 1751 2. ``HGMERGE`` environment variable
1752 1752 3. configurations in ``merge-patterns`` section
1753 1753 4. configuration of ``ui.merge``
1754 1754 5. configurations in ``merge-tools`` section
1755 1755 6. ``hgmerge`` tool (for historical reason only)
1756 1756 7. default tool for fallback (``:merge`` or ``:prompt``)
1757 1757
1758 1758 This command writes out examination result in the style below::
1759 1759
1760 1760 FILE = MERGETOOL
1761 1761
1762 1762 By default, all files known in the first parent context of the
1763 1763 working directory are examined. Use file patterns and/or -I/-X
1764 1764 options to limit target files. -r/--rev is also useful to examine
1765 1765 files in another context without actual updating to it.
1766 1766
1767 1767 With --debug, this command shows warning messages while matching
1768 1768 against ``merge-patterns`` and so on, too. It is recommended to
1769 1769 use this option with explicit file patterns and/or -I/-X options,
1770 1770 because this option increases amount of output per file according
1771 1771 to configurations in hgrc.
1772 1772
1773 1773 With -v/--verbose, this command shows configurations below at
1774 1774 first (only if specified).
1775 1775
1776 1776 - ``--tool`` option
1777 1777 - ``HGMERGE`` environment variable
1778 1778 - configuration of ``ui.merge``
1779 1779
1780 1780 If merge tool is chosen before matching against
1781 1781 ``merge-patterns``, this command can't show any helpful
1782 1782 information, even with --debug. In such case, information above is
1783 1783 useful to know why a merge tool is chosen.
1784 1784 """
1785 1785 opts = pycompat.byteskwargs(opts)
1786 1786 overrides = {}
1787 1787 if opts['tool']:
1788 1788 overrides[('ui', 'forcemerge')] = opts['tool']
1789 1789 ui.note(('with --tool %r\n') % (pycompat.bytestr(opts['tool'])))
1790 1790
1791 1791 with ui.configoverride(overrides, 'debugmergepatterns'):
1792 1792 hgmerge = encoding.environ.get("HGMERGE")
1793 1793 if hgmerge is not None:
1794 1794 ui.note(('with HGMERGE=%r\n') % (pycompat.bytestr(hgmerge)))
1795 1795 uimerge = ui.config("ui", "merge")
1796 1796 if uimerge:
1797 1797 ui.note(('with ui.merge=%r\n') % (pycompat.bytestr(uimerge)))
1798 1798
1799 1799 ctx = scmutil.revsingle(repo, opts.get('rev'))
1800 1800 m = scmutil.match(ctx, pats, opts)
1801 1801 changedelete = opts['changedelete']
1802 1802 for path in ctx.walk(m):
1803 1803 fctx = ctx[path]
1804 1804 try:
1805 1805 if not ui.debugflag:
1806 1806 ui.pushbuffer(error=True)
1807 1807 tool, toolpath = filemerge._picktool(repo, ui, path,
1808 1808 fctx.isbinary(),
1809 1809 'l' in fctx.flags(),
1810 1810 changedelete)
1811 1811 finally:
1812 1812 if not ui.debugflag:
1813 1813 ui.popbuffer()
1814 1814 ui.write(('%s = %s\n') % (path, tool))
1815 1815
1816 1816 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
1817 1817 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
1818 1818 '''access the pushkey key/value protocol
1819 1819
1820 1820 With two args, list the keys in the given namespace.
1821 1821
1822 1822 With five args, set a key to new if it currently is set to old.
1823 1823 Reports success or failure.
1824 1824 '''
1825 1825
1826 1826 target = hg.peer(ui, {}, repopath)
1827 1827 if keyinfo:
1828 1828 key, old, new = keyinfo
1829 1829 with target.commandexecutor() as e:
1830 1830 r = e.callcommand('pushkey', {
1831 1831 'namespace': namespace,
1832 1832 'key': key,
1833 1833 'old': old,
1834 1834 'new': new,
1835 1835 }).result()
1836 1836
1837 1837 ui.status(pycompat.bytestr(r) + '\n')
1838 1838 return not r
1839 1839 else:
1840 1840 for k, v in sorted(target.listkeys(namespace).iteritems()):
1841 1841 ui.write("%s\t%s\n" % (stringutil.escapestr(k),
1842 1842 stringutil.escapestr(v)))
1843 1843
1844 1844 @command('debugpvec', [], _('A B'))
1845 1845 def debugpvec(ui, repo, a, b=None):
1846 1846 ca = scmutil.revsingle(repo, a)
1847 1847 cb = scmutil.revsingle(repo, b)
1848 1848 pa = pvec.ctxpvec(ca)
1849 1849 pb = pvec.ctxpvec(cb)
1850 1850 if pa == pb:
1851 1851 rel = "="
1852 1852 elif pa > pb:
1853 1853 rel = ">"
1854 1854 elif pa < pb:
1855 1855 rel = "<"
1856 1856 elif pa | pb:
1857 1857 rel = "|"
1858 1858 ui.write(_("a: %s\n") % pa)
1859 1859 ui.write(_("b: %s\n") % pb)
1860 1860 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
1861 1861 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
1862 1862 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
1863 1863 pa.distance(pb), rel))
1864 1864
1865 1865 @command('debugrebuilddirstate|debugrebuildstate',
1866 1866 [('r', 'rev', '', _('revision to rebuild to'), _('REV')),
1867 1867 ('', 'minimal', None, _('only rebuild files that are inconsistent with '
1868 1868 'the working copy parent')),
1869 1869 ],
1870 1870 _('[-r REV]'))
1871 1871 def debugrebuilddirstate(ui, repo, rev, **opts):
1872 1872 """rebuild the dirstate as it would look like for the given revision
1873 1873
1874 1874 If no revision is specified the first current parent will be used.
1875 1875
1876 1876 The dirstate will be set to the files of the given revision.
1877 1877 The actual working directory content or existing dirstate
1878 1878 information such as adds or removes is not considered.
1879 1879
1880 1880 ``minimal`` will only rebuild the dirstate status for files that claim to be
1881 1881 tracked but are not in the parent manifest, or that exist in the parent
1882 1882 manifest but are not in the dirstate. It will not change adds, removes, or
1883 1883 modified files that are in the working copy parent.
1884 1884
1885 1885 One use of this command is to make the next :hg:`status` invocation
1886 1886 check the actual file content.
1887 1887 """
1888 1888 ctx = scmutil.revsingle(repo, rev)
1889 1889 with repo.wlock():
1890 1890 dirstate = repo.dirstate
1891 1891 changedfiles = None
1892 1892 # See command doc for what minimal does.
1893 1893 if opts.get(r'minimal'):
1894 1894 manifestfiles = set(ctx.manifest().keys())
1895 1895 dirstatefiles = set(dirstate)
1896 1896 manifestonly = manifestfiles - dirstatefiles
1897 1897 dsonly = dirstatefiles - manifestfiles
1898 1898 dsnotadded = set(f for f in dsonly if dirstate[f] != 'a')
1899 1899 changedfiles = manifestonly | dsnotadded
1900 1900
1901 1901 dirstate.rebuild(ctx.node(), ctx.manifest(), changedfiles)
1902 1902
1903 1903 @command('debugrebuildfncache', [], '')
1904 1904 def debugrebuildfncache(ui, repo):
1905 1905 """rebuild the fncache file"""
1906 1906 repair.rebuildfncache(ui, repo)
1907 1907
1908 1908 @command('debugrename',
1909 1909 [('r', 'rev', '', _('revision to debug'), _('REV'))],
1910 1910 _('[-r REV] FILE'))
1911 1911 def debugrename(ui, repo, file1, *pats, **opts):
1912 1912 """dump rename information"""
1913 1913
1914 1914 opts = pycompat.byteskwargs(opts)
1915 1915 ctx = scmutil.revsingle(repo, opts.get('rev'))
1916 1916 m = scmutil.match(ctx, (file1,) + pats, opts)
1917 1917 for abs in ctx.walk(m):
1918 1918 fctx = ctx[abs]
1919 1919 o = fctx.filelog().renamed(fctx.filenode())
1920 1920 rel = m.rel(abs)
1921 1921 if o:
1922 1922 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1923 1923 else:
1924 1924 ui.write(_("%s not renamed\n") % rel)
1925 1925
1926 1926 @command('debugrevlog', cmdutil.debugrevlogopts +
1927 1927 [('d', 'dump', False, _('dump index data'))],
1928 1928 _('-c|-m|FILE'),
1929 1929 optionalrepo=True)
1930 1930 def debugrevlog(ui, repo, file_=None, **opts):
1931 1931 """show data and statistics about a revlog"""
1932 1932 opts = pycompat.byteskwargs(opts)
1933 1933 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
1934 1934
1935 1935 if opts.get("dump"):
1936 1936 numrevs = len(r)
1937 1937 ui.write(("# rev p1rev p2rev start end deltastart base p1 p2"
1938 1938 " rawsize totalsize compression heads chainlen\n"))
1939 1939 ts = 0
1940 1940 heads = set()
1941 1941
1942 1942 for rev in xrange(numrevs):
1943 1943 dbase = r.deltaparent(rev)
1944 1944 if dbase == -1:
1945 1945 dbase = rev
1946 1946 cbase = r.chainbase(rev)
1947 1947 clen = r.chainlen(rev)
1948 1948 p1, p2 = r.parentrevs(rev)
1949 1949 rs = r.rawsize(rev)
1950 1950 ts = ts + rs
1951 1951 heads -= set(r.parentrevs(rev))
1952 1952 heads.add(rev)
1953 1953 try:
1954 1954 compression = ts / r.end(rev)
1955 1955 except ZeroDivisionError:
1956 1956 compression = 0
1957 1957 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
1958 1958 "%11d %5d %8d\n" %
1959 1959 (rev, p1, p2, r.start(rev), r.end(rev),
1960 1960 r.start(dbase), r.start(cbase),
1961 1961 r.start(p1), r.start(p2),
1962 1962 rs, ts, compression, len(heads), clen))
1963 1963 return 0
1964 1964
1965 1965 v = r.version
1966 1966 format = v & 0xFFFF
1967 1967 flags = []
1968 1968 gdelta = False
1969 1969 if v & revlog.FLAG_INLINE_DATA:
1970 1970 flags.append('inline')
1971 1971 if v & revlog.FLAG_GENERALDELTA:
1972 1972 gdelta = True
1973 1973 flags.append('generaldelta')
1974 1974 if not flags:
1975 1975 flags = ['(none)']
1976 1976
1977 1977 nummerges = 0
1978 1978 numfull = 0
1979 1979 numprev = 0
1980 1980 nump1 = 0
1981 1981 nump2 = 0
1982 1982 numother = 0
1983 1983 nump1prev = 0
1984 1984 nump2prev = 0
1985 1985 chainlengths = []
1986 1986 chainbases = []
1987 1987 chainspans = []
1988 1988
1989 1989 datasize = [None, 0, 0]
1990 1990 fullsize = [None, 0, 0]
1991 1991 deltasize = [None, 0, 0]
1992 1992 chunktypecounts = {}
1993 1993 chunktypesizes = {}
1994 1994
1995 1995 def addsize(size, l):
1996 1996 if l[0] is None or size < l[0]:
1997 1997 l[0] = size
1998 1998 if size > l[1]:
1999 1999 l[1] = size
2000 2000 l[2] += size
2001 2001
2002 2002 numrevs = len(r)
2003 2003 for rev in xrange(numrevs):
2004 2004 p1, p2 = r.parentrevs(rev)
2005 2005 delta = r.deltaparent(rev)
2006 2006 if format > 0:
2007 2007 addsize(r.rawsize(rev), datasize)
2008 2008 if p2 != nullrev:
2009 2009 nummerges += 1
2010 2010 size = r.length(rev)
2011 2011 if delta == nullrev:
2012 2012 chainlengths.append(0)
2013 2013 chainbases.append(r.start(rev))
2014 2014 chainspans.append(size)
2015 2015 numfull += 1
2016 2016 addsize(size, fullsize)
2017 2017 else:
2018 2018 chainlengths.append(chainlengths[delta] + 1)
2019 2019 baseaddr = chainbases[delta]
2020 2020 revaddr = r.start(rev)
2021 2021 chainbases.append(baseaddr)
2022 2022 chainspans.append((revaddr - baseaddr) + size)
2023 2023 addsize(size, deltasize)
2024 2024 if delta == rev - 1:
2025 2025 numprev += 1
2026 2026 if delta == p1:
2027 2027 nump1prev += 1
2028 2028 elif delta == p2:
2029 2029 nump2prev += 1
2030 2030 elif delta == p1:
2031 2031 nump1 += 1
2032 2032 elif delta == p2:
2033 2033 nump2 += 1
2034 2034 elif delta != nullrev:
2035 2035 numother += 1
2036 2036
2037 2037 # Obtain data on the raw chunks in the revlog.
2038 2038 segment = r._getsegmentforrevs(rev, rev)[1]
2039 2039 if segment:
2040 2040 chunktype = bytes(segment[0:1])
2041 2041 else:
2042 2042 chunktype = 'empty'
2043 2043
2044 2044 if chunktype not in chunktypecounts:
2045 2045 chunktypecounts[chunktype] = 0
2046 2046 chunktypesizes[chunktype] = 0
2047 2047
2048 2048 chunktypecounts[chunktype] += 1
2049 2049 chunktypesizes[chunktype] += size
2050 2050
2051 2051 # Adjust size min value for empty cases
2052 2052 for size in (datasize, fullsize, deltasize):
2053 2053 if size[0] is None:
2054 2054 size[0] = 0
2055 2055
2056 2056 numdeltas = numrevs - numfull
2057 2057 numoprev = numprev - nump1prev - nump2prev
2058 2058 totalrawsize = datasize[2]
2059 2059 datasize[2] /= numrevs
2060 2060 fulltotal = fullsize[2]
2061 2061 fullsize[2] /= numfull
2062 2062 deltatotal = deltasize[2]
2063 2063 if numrevs - numfull > 0:
2064 2064 deltasize[2] /= numrevs - numfull
2065 2065 totalsize = fulltotal + deltatotal
2066 2066 avgchainlen = sum(chainlengths) / numrevs
2067 2067 maxchainlen = max(chainlengths)
2068 2068 maxchainspan = max(chainspans)
2069 2069 compratio = 1
2070 2070 if totalsize:
2071 2071 compratio = totalrawsize / totalsize
2072 2072
2073 2073 basedfmtstr = '%%%dd\n'
2074 2074 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2075 2075
2076 2076 def dfmtstr(max):
2077 2077 return basedfmtstr % len(str(max))
2078 2078 def pcfmtstr(max, padding=0):
2079 2079 return basepcfmtstr % (len(str(max)), ' ' * padding)
2080 2080
2081 2081 def pcfmt(value, total):
2082 2082 if total:
2083 2083 return (value, 100 * float(value) / total)
2084 2084 else:
2085 2085 return value, 100.0
2086 2086
2087 2087 ui.write(('format : %d\n') % format)
2088 2088 ui.write(('flags : %s\n') % ', '.join(flags))
2089 2089
2090 2090 ui.write('\n')
2091 2091 fmt = pcfmtstr(totalsize)
2092 2092 fmt2 = dfmtstr(totalsize)
2093 2093 ui.write(('revisions : ') + fmt2 % numrevs)
2094 2094 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2095 2095 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2096 2096 ui.write(('revisions : ') + fmt2 % numrevs)
2097 2097 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2098 2098 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2099 2099 ui.write(('revision size : ') + fmt2 % totalsize)
2100 2100 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2101 2101 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2102 2102
2103 2103 def fmtchunktype(chunktype):
2104 2104 if chunktype == 'empty':
2105 2105 return ' %s : ' % chunktype
2106 2106 elif chunktype in pycompat.bytestr(string.ascii_letters):
2107 2107 return ' 0x%s (%s) : ' % (hex(chunktype), chunktype)
2108 2108 else:
2109 2109 return ' 0x%s : ' % hex(chunktype)
2110 2110
2111 2111 ui.write('\n')
2112 2112 ui.write(('chunks : ') + fmt2 % numrevs)
2113 2113 for chunktype in sorted(chunktypecounts):
2114 2114 ui.write(fmtchunktype(chunktype))
2115 2115 ui.write(fmt % pcfmt(chunktypecounts[chunktype], numrevs))
2116 2116 ui.write(('chunks size : ') + fmt2 % totalsize)
2117 2117 for chunktype in sorted(chunktypecounts):
2118 2118 ui.write(fmtchunktype(chunktype))
2119 2119 ui.write(fmt % pcfmt(chunktypesizes[chunktype], totalsize))
2120 2120
2121 2121 ui.write('\n')
2122 2122 fmt = dfmtstr(max(avgchainlen, maxchainlen, maxchainspan, compratio))
2123 2123 ui.write(('avg chain length : ') + fmt % avgchainlen)
2124 2124 ui.write(('max chain length : ') + fmt % maxchainlen)
2125 2125 ui.write(('max chain reach : ') + fmt % maxchainspan)
2126 2126 ui.write(('compression ratio : ') + fmt % compratio)
2127 2127
2128 2128 if format > 0:
2129 2129 ui.write('\n')
2130 2130 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2131 2131 % tuple(datasize))
2132 2132 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2133 2133 % tuple(fullsize))
2134 2134 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2135 2135 % tuple(deltasize))
2136 2136
2137 2137 if numdeltas > 0:
2138 2138 ui.write('\n')
2139 2139 fmt = pcfmtstr(numdeltas)
2140 2140 fmt2 = pcfmtstr(numdeltas, 4)
2141 2141 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2142 2142 if numprev > 0:
2143 2143 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2144 2144 numprev))
2145 2145 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2146 2146 numprev))
2147 2147 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2148 2148 numprev))
2149 2149 if gdelta:
2150 2150 ui.write(('deltas against p1 : ')
2151 2151 + fmt % pcfmt(nump1, numdeltas))
2152 2152 ui.write(('deltas against p2 : ')
2153 2153 + fmt % pcfmt(nump2, numdeltas))
2154 2154 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2155 2155 numdeltas))
2156 2156
2157 2157 @command('debugrevspec',
2158 2158 [('', 'optimize', None,
2159 2159 _('print parsed tree after optimizing (DEPRECATED)')),
2160 2160 ('', 'show-revs', True, _('print list of result revisions (default)')),
2161 2161 ('s', 'show-set', None, _('print internal representation of result set')),
2162 2162 ('p', 'show-stage', [],
2163 2163 _('print parsed tree at the given stage'), _('NAME')),
2164 2164 ('', 'no-optimized', False, _('evaluate tree without optimization')),
2165 2165 ('', 'verify-optimized', False, _('verify optimized result')),
2166 2166 ],
2167 2167 ('REVSPEC'))
2168 2168 def debugrevspec(ui, repo, expr, **opts):
2169 2169 """parse and apply a revision specification
2170 2170
2171 2171 Use -p/--show-stage option to print the parsed tree at the given stages.
2172 2172 Use -p all to print tree at every stage.
2173 2173
2174 2174 Use --no-show-revs option with -s or -p to print only the set
2175 2175 representation or the parsed tree respectively.
2176 2176
2177 2177 Use --verify-optimized to compare the optimized result with the unoptimized
2178 2178 one. Returns 1 if the optimized result differs.
2179 2179 """
2180 2180 opts = pycompat.byteskwargs(opts)
2181 2181 aliases = ui.configitems('revsetalias')
2182 2182 stages = [
2183 2183 ('parsed', lambda tree: tree),
2184 2184 ('expanded', lambda tree: revsetlang.expandaliases(tree, aliases,
2185 2185 ui.warn)),
2186 2186 ('concatenated', revsetlang.foldconcat),
2187 2187 ('analyzed', revsetlang.analyze),
2188 2188 ('optimized', revsetlang.optimize),
2189 2189 ]
2190 2190 if opts['no_optimized']:
2191 2191 stages = stages[:-1]
2192 2192 if opts['verify_optimized'] and opts['no_optimized']:
2193 2193 raise error.Abort(_('cannot use --verify-optimized with '
2194 2194 '--no-optimized'))
2195 2195 stagenames = set(n for n, f in stages)
2196 2196
2197 2197 showalways = set()
2198 2198 showchanged = set()
2199 2199 if ui.verbose and not opts['show_stage']:
2200 2200 # show parsed tree by --verbose (deprecated)
2201 2201 showalways.add('parsed')
2202 2202 showchanged.update(['expanded', 'concatenated'])
2203 2203 if opts['optimize']:
2204 2204 showalways.add('optimized')
2205 2205 if opts['show_stage'] and opts['optimize']:
2206 2206 raise error.Abort(_('cannot use --optimize with --show-stage'))
2207 2207 if opts['show_stage'] == ['all']:
2208 2208 showalways.update(stagenames)
2209 2209 else:
2210 2210 for n in opts['show_stage']:
2211 2211 if n not in stagenames:
2212 2212 raise error.Abort(_('invalid stage name: %s') % n)
2213 2213 showalways.update(opts['show_stage'])
2214 2214
2215 2215 treebystage = {}
2216 2216 printedtree = None
2217 2217 tree = revsetlang.parse(expr, lookup=revset.lookupfn(repo))
2218 2218 for n, f in stages:
2219 2219 treebystage[n] = tree = f(tree)
2220 2220 if n in showalways or (n in showchanged and tree != printedtree):
2221 2221 if opts['show_stage'] or n != 'parsed':
2222 2222 ui.write(("* %s:\n") % n)
2223 2223 ui.write(revsetlang.prettyformat(tree), "\n")
2224 2224 printedtree = tree
2225 2225
2226 2226 if opts['verify_optimized']:
2227 2227 arevs = revset.makematcher(treebystage['analyzed'])(repo)
2228 2228 brevs = revset.makematcher(treebystage['optimized'])(repo)
2229 2229 if opts['show_set'] or (opts['show_set'] is None and ui.verbose):
2230 2230 ui.write(("* analyzed set:\n"), stringutil.prettyrepr(arevs), "\n")
2231 2231 ui.write(("* optimized set:\n"), stringutil.prettyrepr(brevs), "\n")
2232 2232 arevs = list(arevs)
2233 2233 brevs = list(brevs)
2234 2234 if arevs == brevs:
2235 2235 return 0
2236 2236 ui.write(('--- analyzed\n'), label='diff.file_a')
2237 2237 ui.write(('+++ optimized\n'), label='diff.file_b')
2238 2238 sm = difflib.SequenceMatcher(None, arevs, brevs)
2239 2239 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2240 2240 if tag in ('delete', 'replace'):
2241 2241 for c in arevs[alo:ahi]:
2242 2242 ui.write('-%s\n' % c, label='diff.deleted')
2243 2243 if tag in ('insert', 'replace'):
2244 2244 for c in brevs[blo:bhi]:
2245 2245 ui.write('+%s\n' % c, label='diff.inserted')
2246 2246 if tag == 'equal':
2247 2247 for c in arevs[alo:ahi]:
2248 2248 ui.write(' %s\n' % c)
2249 2249 return 1
2250 2250
2251 2251 func = revset.makematcher(tree)
2252 2252 revs = func(repo)
2253 2253 if opts['show_set'] or (opts['show_set'] is None and ui.verbose):
2254 2254 ui.write(("* set:\n"), stringutil.prettyrepr(revs), "\n")
2255 2255 if not opts['show_revs']:
2256 2256 return
2257 2257 for c in revs:
2258 2258 ui.write("%d\n" % c)
2259 2259
2260 2260 @command('debugserve', [
2261 2261 ('', 'sshstdio', False, _('run an SSH server bound to process handles')),
2262 2262 ('', 'logiofd', '', _('file descriptor to log server I/O to')),
2263 2263 ('', 'logiofile', '', _('file to log server I/O to')),
2264 2264 ], '')
2265 2265 def debugserve(ui, repo, **opts):
2266 2266 """run a server with advanced settings
2267 2267
2268 2268 This command is similar to :hg:`serve`. It exists partially as a
2269 2269 workaround to the fact that ``hg serve --stdio`` must have specific
2270 2270 arguments for security reasons.
2271 2271 """
2272 2272 opts = pycompat.byteskwargs(opts)
2273 2273
2274 2274 if not opts['sshstdio']:
2275 2275 raise error.Abort(_('only --sshstdio is currently supported'))
2276 2276
2277 2277 logfh = None
2278 2278
2279 2279 if opts['logiofd'] and opts['logiofile']:
2280 2280 raise error.Abort(_('cannot use both --logiofd and --logiofile'))
2281 2281
2282 2282 if opts['logiofd']:
2283 2283 # Line buffered because output is line based.
2284 2284 try:
2285 2285 logfh = os.fdopen(int(opts['logiofd']), r'ab', 1)
2286 2286 except OSError as e:
2287 2287 if e.errno != errno.ESPIPE:
2288 2288 raise
2289 2289 # can't seek a pipe, so `ab` mode fails on py3
2290 2290 logfh = os.fdopen(int(opts['logiofd']), r'wb', 1)
2291 2291 elif opts['logiofile']:
2292 2292 logfh = open(opts['logiofile'], 'ab', 1)
2293 2293
2294 2294 s = wireprotoserver.sshserver(ui, repo, logfh=logfh)
2295 2295 s.serve_forever()
2296 2296
2297 2297 @command('debugsetparents', [], _('REV1 [REV2]'))
2298 2298 def debugsetparents(ui, repo, rev1, rev2=None):
2299 2299 """manually set the parents of the current working directory
2300 2300
2301 2301 This is useful for writing repository conversion tools, but should
2302 2302 be used with care. For example, neither the working directory nor the
2303 2303 dirstate is updated, so file status may be incorrect after running this
2304 2304 command.
2305 2305
2306 2306 Returns 0 on success.
2307 2307 """
2308 2308
2309 2309 node1 = scmutil.revsingle(repo, rev1).node()
2310 2310 node2 = scmutil.revsingle(repo, rev2, 'null').node()
2311 2311
2312 2312 with repo.wlock():
2313 2313 repo.setparents(node1, node2)
2314 2314
2315 2315 @command('debugssl', [], '[SOURCE]', optionalrepo=True)
2316 2316 def debugssl(ui, repo, source=None, **opts):
2317 2317 '''test a secure connection to a server
2318 2318
2319 2319 This builds the certificate chain for the server on Windows, installing the
2320 2320 missing intermediates and trusted root via Windows Update if necessary. It
2321 2321 does nothing on other platforms.
2322 2322
2323 2323 If SOURCE is omitted, the 'default' path will be used. If a URL is given,
2324 2324 that server is used. See :hg:`help urls` for more information.
2325 2325
2326 2326 If the update succeeds, retry the original operation. Otherwise, the cause
2327 2327 of the SSL error is likely another issue.
2328 2328 '''
2329 2329 if not pycompat.iswindows:
2330 2330 raise error.Abort(_('certificate chain building is only possible on '
2331 2331 'Windows'))
2332 2332
2333 2333 if not source:
2334 2334 if not repo:
2335 2335 raise error.Abort(_("there is no Mercurial repository here, and no "
2336 2336 "server specified"))
2337 2337 source = "default"
2338 2338
2339 2339 source, branches = hg.parseurl(ui.expandpath(source))
2340 2340 url = util.url(source)
2341 2341 addr = None
2342 2342
2343 2343 defaultport = {'https': 443, 'ssh': 22}
2344 2344 if url.scheme in defaultport:
2345 2345 try:
2346 2346 addr = (url.host, int(url.port or defaultport[url.scheme]))
2347 2347 except ValueError:
2348 2348 raise error.Abort(_("malformed port number in URL"))
2349 2349 else:
2350 2350 raise error.Abort(_("only https and ssh connections are supported"))
2351 2351
2352 2352 from . import win32
2353 2353
2354 2354 s = ssl.wrap_socket(socket.socket(), ssl_version=ssl.PROTOCOL_TLS,
2355 2355 cert_reqs=ssl.CERT_NONE, ca_certs=None)
2356 2356
2357 2357 try:
2358 2358 s.connect(addr)
2359 2359 cert = s.getpeercert(True)
2360 2360
2361 2361 ui.status(_('checking the certificate chain for %s\n') % url.host)
2362 2362
2363 2363 complete = win32.checkcertificatechain(cert, build=False)
2364 2364
2365 2365 if not complete:
2366 2366 ui.status(_('certificate chain is incomplete, updating... '))
2367 2367
2368 2368 if not win32.checkcertificatechain(cert):
2369 2369 ui.status(_('failed.\n'))
2370 2370 else:
2371 2371 ui.status(_('done.\n'))
2372 2372 else:
2373 2373 ui.status(_('full certificate chain is available\n'))
2374 2374 finally:
2375 2375 s.close()
2376 2376
2377 2377 @command('debugsub',
2378 2378 [('r', 'rev', '',
2379 2379 _('revision to check'), _('REV'))],
2380 2380 _('[-r REV] [REV]'))
2381 2381 def debugsub(ui, repo, rev=None):
2382 2382 ctx = scmutil.revsingle(repo, rev, None)
2383 2383 for k, v in sorted(ctx.substate.items()):
2384 2384 ui.write(('path %s\n') % k)
2385 2385 ui.write((' source %s\n') % v[0])
2386 2386 ui.write((' revision %s\n') % v[1])
2387 2387
2388 2388 @command('debugsuccessorssets',
2389 2389 [('', 'closest', False, _('return closest successors sets only'))],
2390 2390 _('[REV]'))
2391 2391 def debugsuccessorssets(ui, repo, *revs, **opts):
2392 2392 """show set of successors for revision
2393 2393
2394 2394 A successors set of changeset A is a consistent group of revisions that
2395 2395 succeed A. It contains non-obsolete changesets only unless closests
2396 2396 successors set is set.
2397 2397
2398 2398 In most cases a changeset A has a single successors set containing a single
2399 2399 successor (changeset A replaced by A').
2400 2400
2401 2401 A changeset that is made obsolete with no successors are called "pruned".
2402 2402 Such changesets have no successors sets at all.
2403 2403
2404 2404 A changeset that has been "split" will have a successors set containing
2405 2405 more than one successor.
2406 2406
2407 2407 A changeset that has been rewritten in multiple different ways is called
2408 2408 "divergent". Such changesets have multiple successor sets (each of which
2409 2409 may also be split, i.e. have multiple successors).
2410 2410
2411 2411 Results are displayed as follows::
2412 2412
2413 2413 <rev1>
2414 2414 <successors-1A>
2415 2415 <rev2>
2416 2416 <successors-2A>
2417 2417 <successors-2B1> <successors-2B2> <successors-2B3>
2418 2418
2419 2419 Here rev2 has two possible (i.e. divergent) successors sets. The first
2420 2420 holds one element, whereas the second holds three (i.e. the changeset has
2421 2421 been split).
2422 2422 """
2423 2423 # passed to successorssets caching computation from one call to another
2424 2424 cache = {}
2425 2425 ctx2str = bytes
2426 2426 node2str = short
2427 2427 for rev in scmutil.revrange(repo, revs):
2428 2428 ctx = repo[rev]
2429 2429 ui.write('%s\n'% ctx2str(ctx))
2430 2430 for succsset in obsutil.successorssets(repo, ctx.node(),
2431 2431 closest=opts[r'closest'],
2432 2432 cache=cache):
2433 2433 if succsset:
2434 2434 ui.write(' ')
2435 2435 ui.write(node2str(succsset[0]))
2436 2436 for node in succsset[1:]:
2437 2437 ui.write(' ')
2438 2438 ui.write(node2str(node))
2439 2439 ui.write('\n')
2440 2440
2441 2441 @command('debugtemplate',
2442 2442 [('r', 'rev', [], _('apply template on changesets'), _('REV')),
2443 2443 ('D', 'define', [], _('define template keyword'), _('KEY=VALUE'))],
2444 2444 _('[-r REV]... [-D KEY=VALUE]... TEMPLATE'),
2445 2445 optionalrepo=True)
2446 2446 def debugtemplate(ui, repo, tmpl, **opts):
2447 2447 """parse and apply a template
2448 2448
2449 2449 If -r/--rev is given, the template is processed as a log template and
2450 2450 applied to the given changesets. Otherwise, it is processed as a generic
2451 2451 template.
2452 2452
2453 2453 Use --verbose to print the parsed tree.
2454 2454 """
2455 2455 revs = None
2456 2456 if opts[r'rev']:
2457 2457 if repo is None:
2458 2458 raise error.RepoError(_('there is no Mercurial repository here '
2459 2459 '(.hg not found)'))
2460 2460 revs = scmutil.revrange(repo, opts[r'rev'])
2461 2461
2462 2462 props = {}
2463 2463 for d in opts[r'define']:
2464 2464 try:
2465 2465 k, v = (e.strip() for e in d.split('=', 1))
2466 2466 if not k or k == 'ui':
2467 2467 raise ValueError
2468 2468 props[k] = v
2469 2469 except ValueError:
2470 2470 raise error.Abort(_('malformed keyword definition: %s') % d)
2471 2471
2472 2472 if ui.verbose:
2473 2473 aliases = ui.configitems('templatealias')
2474 2474 tree = templater.parse(tmpl)
2475 2475 ui.note(templater.prettyformat(tree), '\n')
2476 2476 newtree = templater.expandaliases(tree, aliases)
2477 2477 if newtree != tree:
2478 2478 ui.note(("* expanded:\n"), templater.prettyformat(newtree), '\n')
2479 2479
2480 2480 if revs is None:
2481 2481 tres = formatter.templateresources(ui, repo)
2482 2482 t = formatter.maketemplater(ui, tmpl, resources=tres)
2483 2483 if ui.verbose:
2484 2484 kwds, funcs = t.symbolsuseddefault()
2485 2485 ui.write(("* keywords: %s\n") % ', '.join(sorted(kwds)))
2486 2486 ui.write(("* functions: %s\n") % ', '.join(sorted(funcs)))
2487 2487 ui.write(t.renderdefault(props))
2488 2488 else:
2489 2489 displayer = logcmdutil.maketemplater(ui, repo, tmpl)
2490 2490 if ui.verbose:
2491 2491 kwds, funcs = displayer.t.symbolsuseddefault()
2492 2492 ui.write(("* keywords: %s\n") % ', '.join(sorted(kwds)))
2493 2493 ui.write(("* functions: %s\n") % ', '.join(sorted(funcs)))
2494 2494 for r in revs:
2495 2495 displayer.show(repo[r], **pycompat.strkwargs(props))
2496 2496 displayer.close()
2497 2497
2498 2498 @command('debuguigetpass', [
2499 2499 ('p', 'prompt', '', _('prompt text'), _('TEXT')),
2500 2500 ], _('[-p TEXT]'), norepo=True)
2501 2501 def debuguigetpass(ui, prompt=''):
2502 2502 """show prompt to type password"""
2503 2503 r = ui.getpass(prompt)
2504 2504 ui.write(('respose: %s\n') % r)
2505 2505
2506 2506 @command('debuguiprompt', [
2507 2507 ('p', 'prompt', '', _('prompt text'), _('TEXT')),
2508 2508 ], _('[-p TEXT]'), norepo=True)
2509 2509 def debuguiprompt(ui, prompt=''):
2510 2510 """show plain prompt"""
2511 2511 r = ui.prompt(prompt)
2512 2512 ui.write(('response: %s\n') % r)
2513 2513
2514 2514 @command('debugupdatecaches', [])
2515 2515 def debugupdatecaches(ui, repo, *pats, **opts):
2516 2516 """warm all known caches in the repository"""
2517 2517 with repo.wlock(), repo.lock():
2518 2518 repo.updatecaches(full=True)
2519 2519
2520 2520 @command('debugupgraderepo', [
2521 2521 ('o', 'optimize', [], _('extra optimization to perform'), _('NAME')),
2522 2522 ('', 'run', False, _('performs an upgrade')),
2523 2523 ])
2524 2524 def debugupgraderepo(ui, repo, run=False, optimize=None):
2525 2525 """upgrade a repository to use different features
2526 2526
2527 2527 If no arguments are specified, the repository is evaluated for upgrade
2528 2528 and a list of problems and potential optimizations is printed.
2529 2529
2530 2530 With ``--run``, a repository upgrade is performed. Behavior of the upgrade
2531 2531 can be influenced via additional arguments. More details will be provided
2532 2532 by the command output when run without ``--run``.
2533 2533
2534 2534 During the upgrade, the repository will be locked and no writes will be
2535 2535 allowed.
2536 2536
2537 2537 At the end of the upgrade, the repository may not be readable while new
2538 2538 repository data is swapped in. This window will be as long as it takes to
2539 2539 rename some directories inside the ``.hg`` directory. On most machines, this
2540 2540 should complete almost instantaneously and the chances of a consumer being
2541 2541 unable to access the repository should be low.
2542 2542 """
2543 2543 return upgrade.upgraderepo(ui, repo, run=run, optimize=optimize)
2544 2544
2545 2545 @command('debugwalk', cmdutil.walkopts, _('[OPTION]... [FILE]...'),
2546 2546 inferrepo=True)
2547 2547 def debugwalk(ui, repo, *pats, **opts):
2548 2548 """show how files match on given patterns"""
2549 2549 opts = pycompat.byteskwargs(opts)
2550 2550 m = scmutil.match(repo[None], pats, opts)
2551 2551 if ui.verbose:
2552 2552 ui.write(('* matcher:\n'), stringutil.prettyrepr(m), '\n')
2553 2553 items = list(repo[None].walk(m))
2554 2554 if not items:
2555 2555 return
2556 2556 f = lambda fn: fn
2557 2557 if ui.configbool('ui', 'slash') and pycompat.ossep != '/':
2558 2558 f = lambda fn: util.normpath(fn)
2559 2559 fmt = 'f %%-%ds %%-%ds %%s' % (
2560 2560 max([len(abs) for abs in items]),
2561 2561 max([len(m.rel(abs)) for abs in items]))
2562 2562 for abs in items:
2563 2563 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
2564 2564 ui.write("%s\n" % line.rstrip())
2565 2565
2566 2566 @command('debugwhyunstable', [], _('REV'))
2567 2567 def debugwhyunstable(ui, repo, rev):
2568 2568 """explain instabilities of a changeset"""
2569 2569 for entry in obsutil.whyunstable(repo, scmutil.revsingle(repo, rev)):
2570 2570 dnodes = ''
2571 2571 if entry.get('divergentnodes'):
2572 2572 dnodes = ' '.join('%s (%s)' % (ctx.hex(), ctx.phasestr())
2573 2573 for ctx in entry['divergentnodes']) + ' '
2574 2574 ui.write('%s: %s%s %s\n' % (entry['instability'], dnodes,
2575 2575 entry['reason'], entry['node']))
2576 2576
2577 2577 @command('debugwireargs',
2578 2578 [('', 'three', '', 'three'),
2579 2579 ('', 'four', '', 'four'),
2580 2580 ('', 'five', '', 'five'),
2581 2581 ] + cmdutil.remoteopts,
2582 2582 _('REPO [OPTIONS]... [ONE [TWO]]'),
2583 2583 norepo=True)
2584 2584 def debugwireargs(ui, repopath, *vals, **opts):
2585 2585 opts = pycompat.byteskwargs(opts)
2586 2586 repo = hg.peer(ui, opts, repopath)
2587 2587 for opt in cmdutil.remoteopts:
2588 2588 del opts[opt[1]]
2589 2589 args = {}
2590 2590 for k, v in opts.iteritems():
2591 2591 if v:
2592 2592 args[k] = v
2593 2593 args = pycompat.strkwargs(args)
2594 2594 # run twice to check that we don't mess up the stream for the next command
2595 2595 res1 = repo.debugwireargs(*vals, **args)
2596 2596 res2 = repo.debugwireargs(*vals, **args)
2597 2597 ui.write("%s\n" % res1)
2598 2598 if res1 != res2:
2599 2599 ui.warn("%s\n" % res2)
2600 2600
2601 2601 def _parsewirelangblocks(fh):
2602 2602 activeaction = None
2603 2603 blocklines = []
2604 2604
2605 2605 for line in fh:
2606 2606 line = line.rstrip()
2607 2607 if not line:
2608 2608 continue
2609 2609
2610 2610 if line.startswith(b'#'):
2611 2611 continue
2612 2612
2613 2613 if not line.startswith(' '):
2614 2614 # New block. Flush previous one.
2615 2615 if activeaction:
2616 2616 yield activeaction, blocklines
2617 2617
2618 2618 activeaction = line
2619 2619 blocklines = []
2620 2620 continue
2621 2621
2622 2622 # Else we start with an indent.
2623 2623
2624 2624 if not activeaction:
2625 2625 raise error.Abort(_('indented line outside of block'))
2626 2626
2627 2627 blocklines.append(line)
2628 2628
2629 2629 # Flush last block.
2630 2630 if activeaction:
2631 2631 yield activeaction, blocklines
2632 2632
2633 2633 @command('debugwireproto',
2634 2634 [
2635 2635 ('', 'localssh', False, _('start an SSH server for this repo')),
2636 2636 ('', 'peer', '', _('construct a specific version of the peer')),
2637 2637 ('', 'noreadstderr', False, _('do not read from stderr of the remote')),
2638 2638 ('', 'nologhandshake', False,
2639 2639 _('do not log I/O related to the peer handshake')),
2640 2640 ] + cmdutil.remoteopts,
2641 2641 _('[PATH]'),
2642 2642 optionalrepo=True)
2643 2643 def debugwireproto(ui, repo, path=None, **opts):
2644 2644 """send wire protocol commands to a server
2645 2645
2646 2646 This command can be used to issue wire protocol commands to remote
2647 2647 peers and to debug the raw data being exchanged.
2648 2648
2649 2649 ``--localssh`` will start an SSH server against the current repository
2650 2650 and connect to that. By default, the connection will perform a handshake
2651 2651 and establish an appropriate peer instance.
2652 2652
2653 2653 ``--peer`` can be used to bypass the handshake protocol and construct a
2654 2654 peer instance using the specified class type. Valid values are ``raw``,
2655 2655 ``http2``, ``ssh1``, and ``ssh2``. ``raw`` instances only allow sending
2656 2656 raw data payloads and don't support higher-level command actions.
2657 2657
2658 2658 ``--noreadstderr`` can be used to disable automatic reading from stderr
2659 2659 of the peer (for SSH connections only). Disabling automatic reading of
2660 2660 stderr is useful for making output more deterministic.
2661 2661
2662 2662 Commands are issued via a mini language which is specified via stdin.
2663 2663 The language consists of individual actions to perform. An action is
2664 2664 defined by a block. A block is defined as a line with no leading
2665 2665 space followed by 0 or more lines with leading space. Blocks are
2666 2666 effectively a high-level command with additional metadata.
2667 2667
2668 2668 Lines beginning with ``#`` are ignored.
2669 2669
2670 2670 The following sections denote available actions.
2671 2671
2672 2672 raw
2673 2673 ---
2674 2674
2675 2675 Send raw data to the server.
2676 2676
2677 2677 The block payload contains the raw data to send as one atomic send
2678 2678 operation. The data may not actually be delivered in a single system
2679 2679 call: it depends on the abilities of the transport being used.
2680 2680
2681 2681 Each line in the block is de-indented and concatenated. Then, that
2682 2682 value is evaluated as a Python b'' literal. This allows the use of
2683 2683 backslash escaping, etc.
2684 2684
2685 2685 raw+
2686 2686 ----
2687 2687
2688 2688 Behaves like ``raw`` except flushes output afterwards.
2689 2689
2690 2690 command <X>
2691 2691 -----------
2692 2692
2693 2693 Send a request to run a named command, whose name follows the ``command``
2694 2694 string.
2695 2695
2696 2696 Arguments to the command are defined as lines in this block. The format of
2697 2697 each line is ``<key> <value>``. e.g.::
2698 2698
2699 2699 command listkeys
2700 2700 namespace bookmarks
2701 2701
2702 2702 If the value begins with ``eval:``, it will be interpreted as a Python
2703 2703 literal expression. Otherwise values are interpreted as Python b'' literals.
2704 2704 This allows sending complex types and encoding special byte sequences via
2705 2705 backslash escaping.
2706 2706
2707 2707 The following arguments have special meaning:
2708 2708
2709 2709 ``PUSHFILE``
2710 2710 When defined, the *push* mechanism of the peer will be used instead
2711 2711 of the static request-response mechanism and the content of the
2712 2712 file specified in the value of this argument will be sent as the
2713 2713 command payload.
2714 2714
2715 2715 This can be used to submit a local bundle file to the remote.
2716 2716
2717 2717 batchbegin
2718 2718 ----------
2719 2719
2720 2720 Instruct the peer to begin a batched send.
2721 2721
2722 2722 All ``command`` blocks are queued for execution until the next
2723 2723 ``batchsubmit`` block.
2724 2724
2725 2725 batchsubmit
2726 2726 -----------
2727 2727
2728 2728 Submit previously queued ``command`` blocks as a batch request.
2729 2729
2730 2730 This action MUST be paired with a ``batchbegin`` action.
2731 2731
2732 2732 httprequest <method> <path>
2733 2733 ---------------------------
2734 2734
2735 2735 (HTTP peer only)
2736 2736
2737 2737 Send an HTTP request to the peer.
2738 2738
2739 2739 The HTTP request line follows the ``httprequest`` action. e.g. ``GET /foo``.
2740 2740
2741 2741 Arguments of the form ``<key>: <value>`` are interpreted as HTTP request
2742 2742 headers to add to the request. e.g. ``Accept: foo``.
2743 2743
2744 2744 The following arguments are special:
2745 2745
2746 2746 ``BODYFILE``
2747 2747 The content of the file defined as the value to this argument will be
2748 2748 transferred verbatim as the HTTP request body.
2749 2749
2750 2750 ``frame <type> <flags> <payload>``
2751 2751 Send a unified protocol frame as part of the request body.
2752 2752
2753 2753 All frames will be collected and sent as the body to the HTTP
2754 2754 request.
2755 2755
2756 2756 close
2757 2757 -----
2758 2758
2759 2759 Close the connection to the server.
2760 2760
2761 2761 flush
2762 2762 -----
2763 2763
2764 2764 Flush data written to the server.
2765 2765
2766 2766 readavailable
2767 2767 -------------
2768 2768
2769 2769 Close the write end of the connection and read all available data from
2770 2770 the server.
2771 2771
2772 2772 If the connection to the server encompasses multiple pipes, we poll both
2773 2773 pipes and read available data.
2774 2774
2775 2775 readline
2776 2776 --------
2777 2777
2778 2778 Read a line of output from the server. If there are multiple output
2779 2779 pipes, reads only the main pipe.
2780 2780
2781 2781 ereadline
2782 2782 ---------
2783 2783
2784 2784 Like ``readline``, but read from the stderr pipe, if available.
2785 2785
2786 2786 read <X>
2787 2787 --------
2788 2788
2789 2789 ``read()`` N bytes from the server's main output pipe.
2790 2790
2791 2791 eread <X>
2792 2792 ---------
2793 2793
2794 2794 ``read()`` N bytes from the server's stderr pipe, if available.
2795 2795
2796 2796 Specifying Unified Frame-Based Protocol Frames
2797 2797 ----------------------------------------------
2798 2798
2799 2799 It is possible to emit a *Unified Frame-Based Protocol* by using special
2800 2800 syntax.
2801 2801
2802 2802 A frame is composed as a type, flags, and payload. These can be parsed
2803 2803 from a string of the form:
2804 2804
2805 2805 <request-id> <stream-id> <stream-flags> <type> <flags> <payload>
2806 2806
2807 2807 ``request-id`` and ``stream-id`` are integers defining the request and
2808 2808 stream identifiers.
2809 2809
2810 2810 ``type`` can be an integer value for the frame type or the string name
2811 2811 of the type. The strings are defined in ``wireprotoframing.py``. e.g.
2812 2812 ``command-name``.
2813 2813
2814 2814 ``stream-flags`` and ``flags`` are a ``|`` delimited list of flag
2815 2815 components. Each component (and there can be just one) can be an integer
2816 2816 or a flag name for stream flags or frame flags, respectively. Values are
2817 2817 resolved to integers and then bitwise OR'd together.
2818 2818
2819 2819 ``payload`` represents the raw frame payload. If it begins with
2820 2820 ``cbor:``, the following string is evaluated as Python code and the
2821 2821 resulting object is fed into a CBOR encoder. Otherwise it is interpreted
2822 2822 as a Python byte string literal.
2823 2823 """
2824 2824 opts = pycompat.byteskwargs(opts)
2825 2825
2826 2826 if opts['localssh'] and not repo:
2827 2827 raise error.Abort(_('--localssh requires a repository'))
2828 2828
2829 2829 if opts['peer'] and opts['peer'] not in ('raw', 'http2', 'ssh1', 'ssh2'):
2830 2830 raise error.Abort(_('invalid value for --peer'),
2831 2831 hint=_('valid values are "raw", "ssh1", and "ssh2"'))
2832 2832
2833 2833 if path and opts['localssh']:
2834 2834 raise error.Abort(_('cannot specify --localssh with an explicit '
2835 2835 'path'))
2836 2836
2837 2837 if ui.interactive():
2838 2838 ui.write(_('(waiting for commands on stdin)\n'))
2839 2839
2840 2840 blocks = list(_parsewirelangblocks(ui.fin))
2841 2841
2842 2842 proc = None
2843 2843 stdin = None
2844 2844 stdout = None
2845 2845 stderr = None
2846 2846 opener = None
2847 2847
2848 2848 if opts['localssh']:
2849 2849 # We start the SSH server in its own process so there is process
2850 2850 # separation. This prevents a whole class of potential bugs around
2851 2851 # shared state from interfering with server operation.
2852 2852 args = procutil.hgcmd() + [
2853 2853 '-R', repo.root,
2854 2854 'debugserve', '--sshstdio',
2855 2855 ]
2856 2856 proc = subprocess.Popen(args, stdin=subprocess.PIPE,
2857 2857 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
2858 2858 bufsize=0)
2859 2859
2860 2860 stdin = proc.stdin
2861 2861 stdout = proc.stdout
2862 2862 stderr = proc.stderr
2863 2863
2864 2864 # We turn the pipes into observers so we can log I/O.
2865 2865 if ui.verbose or opts['peer'] == 'raw':
2866 2866 stdin = util.makeloggingfileobject(ui, proc.stdin, b'i',
2867 2867 logdata=True)
2868 2868 stdout = util.makeloggingfileobject(ui, proc.stdout, b'o',
2869 2869 logdata=True)
2870 2870 stderr = util.makeloggingfileobject(ui, proc.stderr, b'e',
2871 2871 logdata=True)
2872 2872
2873 2873 # --localssh also implies the peer connection settings.
2874 2874
2875 2875 url = 'ssh://localserver'
2876 2876 autoreadstderr = not opts['noreadstderr']
2877 2877
2878 2878 if opts['peer'] == 'ssh1':
2879 2879 ui.write(_('creating ssh peer for wire protocol version 1\n'))
2880 2880 peer = sshpeer.sshv1peer(ui, url, proc, stdin, stdout, stderr,
2881 2881 None, autoreadstderr=autoreadstderr)
2882 2882 elif opts['peer'] == 'ssh2':
2883 2883 ui.write(_('creating ssh peer for wire protocol version 2\n'))
2884 2884 peer = sshpeer.sshv2peer(ui, url, proc, stdin, stdout, stderr,
2885 2885 None, autoreadstderr=autoreadstderr)
2886 2886 elif opts['peer'] == 'raw':
2887 2887 ui.write(_('using raw connection to peer\n'))
2888 2888 peer = None
2889 2889 else:
2890 2890 ui.write(_('creating ssh peer from handshake results\n'))
2891 2891 peer = sshpeer.makepeer(ui, url, proc, stdin, stdout, stderr,
2892 2892 autoreadstderr=autoreadstderr)
2893 2893
2894 2894 elif path:
2895 2895 # We bypass hg.peer() so we can proxy the sockets.
2896 2896 # TODO consider not doing this because we skip
2897 2897 # ``hg.wirepeersetupfuncs`` and potentially other useful functionality.
2898 2898 u = util.url(path)
2899 2899 if u.scheme != 'http':
2900 2900 raise error.Abort(_('only http:// paths are currently supported'))
2901 2901
2902 2902 url, authinfo = u.authinfo()
2903 2903 openerargs = {
2904 2904 r'useragent': b'Mercurial debugwireproto',
2905 2905 }
2906 2906
2907 2907 # Turn pipes/sockets into observers so we can log I/O.
2908 2908 if ui.verbose:
2909 2909 openerargs.update({
2910 2910 r'loggingfh': ui,
2911 2911 r'loggingname': b's',
2912 2912 r'loggingopts': {
2913 2913 r'logdata': True,
2914 2914 r'logdataapis': False,
2915 2915 },
2916 2916 })
2917 2917
2918 2918 if ui.debugflag:
2919 2919 openerargs[r'loggingopts'][r'logdataapis'] = True
2920 2920
2921 2921 # Don't send default headers when in raw mode. This allows us to
2922 2922 # bypass most of the behavior of our URL handling code so we can
2923 2923 # have near complete control over what's sent on the wire.
2924 2924 if opts['peer'] == 'raw':
2925 2925 openerargs[r'sendaccept'] = False
2926 2926
2927 2927 opener = urlmod.opener(ui, authinfo, **openerargs)
2928 2928
2929 2929 if opts['peer'] == 'http2':
2930 2930 ui.write(_('creating http peer for wire protocol version 2\n'))
2931 2931 # We go through makepeer() because we need an API descriptor for
2932 2932 # the peer instance to be useful.
2933 2933 with ui.configoverride({
2934 2934 ('experimental', 'httppeer.advertise-v2'): True}):
2935 2935 if opts['nologhandshake']:
2936 2936 ui.pushbuffer()
2937 2937
2938 2938 peer = httppeer.makepeer(ui, path, opener=opener)
2939 2939
2940 2940 if opts['nologhandshake']:
2941 2941 ui.popbuffer()
2942 2942
2943 2943 if not isinstance(peer, httppeer.httpv2peer):
2944 2944 raise error.Abort(_('could not instantiate HTTP peer for '
2945 2945 'wire protocol version 2'),
2946 2946 hint=_('the server may not have the feature '
2947 2947 'enabled or is not allowing this '
2948 2948 'client version'))
2949 2949
2950 2950 elif opts['peer'] == 'raw':
2951 2951 ui.write(_('using raw connection to peer\n'))
2952 2952 peer = None
2953 2953 elif opts['peer']:
2954 2954 raise error.Abort(_('--peer %s not supported with HTTP peers') %
2955 2955 opts['peer'])
2956 2956 else:
2957 2957 peer = httppeer.makepeer(ui, path, opener=opener)
2958 2958
2959 2959 # We /could/ populate stdin/stdout with sock.makefile()...
2960 2960 else:
2961 2961 raise error.Abort(_('unsupported connection configuration'))
2962 2962
2963 2963 batchedcommands = None
2964 2964
2965 2965 # Now perform actions based on the parsed wire language instructions.
2966 2966 for action, lines in blocks:
2967 2967 if action in ('raw', 'raw+'):
2968 2968 if not stdin:
2969 2969 raise error.Abort(_('cannot call raw/raw+ on this peer'))
2970 2970
2971 2971 # Concatenate the data together.
2972 2972 data = ''.join(l.lstrip() for l in lines)
2973 2973 data = stringutil.unescapestr(data)
2974 2974 stdin.write(data)
2975 2975
2976 2976 if action == 'raw+':
2977 2977 stdin.flush()
2978 2978 elif action == 'flush':
2979 2979 if not stdin:
2980 2980 raise error.Abort(_('cannot call flush on this peer'))
2981 2981 stdin.flush()
2982 2982 elif action.startswith('command'):
2983 2983 if not peer:
2984 2984 raise error.Abort(_('cannot send commands unless peer instance '
2985 2985 'is available'))
2986 2986
2987 2987 command = action.split(' ', 1)[1]
2988 2988
2989 2989 args = {}
2990 2990 for line in lines:
2991 2991 # We need to allow empty values.
2992 2992 fields = line.lstrip().split(' ', 1)
2993 2993 if len(fields) == 1:
2994 2994 key = fields[0]
2995 2995 value = ''
2996 2996 else:
2997 2997 key, value = fields
2998 2998
2999 2999 if value.startswith('eval:'):
3000 3000 value = stringutil.evalpythonliteral(value[5:])
3001 3001 else:
3002 3002 value = stringutil.unescapestr(value)
3003 3003
3004 3004 args[key] = value
3005 3005
3006 3006 if batchedcommands is not None:
3007 3007 batchedcommands.append((command, args))
3008 3008 continue
3009 3009
3010 3010 ui.status(_('sending %s command\n') % command)
3011 3011
3012 3012 if 'PUSHFILE' in args:
3013 3013 with open(args['PUSHFILE'], r'rb') as fh:
3014 3014 del args['PUSHFILE']
3015 3015 res, output = peer._callpush(command, fh,
3016 3016 **pycompat.strkwargs(args))
3017 3017 ui.status(_('result: %s\n') % stringutil.escapestr(res))
3018 3018 ui.status(_('remote output: %s\n') %
3019 3019 stringutil.escapestr(output))
3020 3020 else:
3021 3021 with peer.commandexecutor() as e:
3022 3022 res = e.callcommand(command, args).result()
3023 3023
3024 3024 if isinstance(res, wireprotov2peer.commandresponse):
3025 3025 val = list(res.cborobjects())
3026 3026 ui.status(_('response: %s\n') %
3027 3027 stringutil.pprint(val, bprefix=True))
3028 3028
3029 3029 else:
3030 3030 ui.status(_('response: %s\n') %
3031 3031 stringutil.pprint(res, bprefix=True))
3032 3032
3033 3033 elif action == 'batchbegin':
3034 3034 if batchedcommands is not None:
3035 3035 raise error.Abort(_('nested batchbegin not allowed'))
3036 3036
3037 3037 batchedcommands = []
3038 3038 elif action == 'batchsubmit':
3039 3039 # There is a batching API we could go through. But it would be
3040 3040 # difficult to normalize requests into function calls. It is easier
3041 3041 # to bypass this layer and normalize to commands + args.
3042 3042 ui.status(_('sending batch with %d sub-commands\n') %
3043 3043 len(batchedcommands))
3044 3044 for i, chunk in enumerate(peer._submitbatch(batchedcommands)):
3045 3045 ui.status(_('response #%d: %s\n') %
3046 3046 (i, stringutil.escapestr(chunk)))
3047 3047
3048 3048 batchedcommands = None
3049 3049
3050 3050 elif action.startswith('httprequest '):
3051 3051 if not opener:
3052 3052 raise error.Abort(_('cannot use httprequest without an HTTP '
3053 3053 'peer'))
3054 3054
3055 3055 request = action.split(' ', 2)
3056 3056 if len(request) != 3:
3057 3057 raise error.Abort(_('invalid httprequest: expected format is '
3058 3058 '"httprequest <method> <path>'))
3059 3059
3060 3060 method, httppath = request[1:]
3061 3061 headers = {}
3062 3062 body = None
3063 3063 frames = []
3064 3064 for line in lines:
3065 3065 line = line.lstrip()
3066 3066 m = re.match(b'^([a-zA-Z0-9_-]+): (.*)$', line)
3067 3067 if m:
3068 3068 headers[m.group(1)] = m.group(2)
3069 3069 continue
3070 3070
3071 3071 if line.startswith(b'BODYFILE '):
3072 3072 with open(line.split(b' ', 1), 'rb') as fh:
3073 3073 body = fh.read()
3074 3074 elif line.startswith(b'frame '):
3075 3075 frame = wireprotoframing.makeframefromhumanstring(
3076 3076 line[len(b'frame '):])
3077 3077
3078 3078 frames.append(frame)
3079 3079 else:
3080 3080 raise error.Abort(_('unknown argument to httprequest: %s') %
3081 3081 line)
3082 3082
3083 3083 url = path + httppath
3084 3084
3085 3085 if frames:
3086 3086 body = b''.join(bytes(f) for f in frames)
3087 3087
3088 3088 req = urlmod.urlreq.request(pycompat.strurl(url), body, headers)
3089 3089
3090 3090 # urllib.Request insists on using has_data() as a proxy for
3091 3091 # determining the request method. Override that to use our
3092 3092 # explicitly requested method.
3093 3093 req.get_method = lambda: method
3094 3094
3095 3095 try:
3096 3096 res = opener.open(req)
3097 3097 body = res.read()
3098 3098 except util.urlerr.urlerror as e:
3099 3099 e.read()
3100 3100 continue
3101 3101
3102 3102 if res.headers.get('Content-Type') == 'application/mercurial-cbor':
3103 3103 ui.write(_('cbor> %s\n') %
3104 3104 stringutil.pprint(cbor.loads(body), bprefix=True))
3105 3105
3106 3106 elif action == 'close':
3107 3107 peer.close()
3108 3108 elif action == 'readavailable':
3109 3109 if not stdout or not stderr:
3110 3110 raise error.Abort(_('readavailable not available on this peer'))
3111 3111
3112 3112 stdin.close()
3113 3113 stdout.read()
3114 3114 stderr.read()
3115 3115
3116 3116 elif action == 'readline':
3117 3117 if not stdout:
3118 3118 raise error.Abort(_('readline not available on this peer'))
3119 3119 stdout.readline()
3120 3120 elif action == 'ereadline':
3121 3121 if not stderr:
3122 3122 raise error.Abort(_('ereadline not available on this peer'))
3123 3123 stderr.readline()
3124 3124 elif action.startswith('read '):
3125 3125 count = int(action.split(' ', 1)[1])
3126 3126 if not stdout:
3127 3127 raise error.Abort(_('read not available on this peer'))
3128 3128 stdout.read(count)
3129 3129 elif action.startswith('eread '):
3130 3130 count = int(action.split(' ', 1)[1])
3131 3131 if not stderr:
3132 3132 raise error.Abort(_('eread not available on this peer'))
3133 3133 stderr.read(count)
3134 3134 else:
3135 3135 raise error.Abort(_('unknown action: %s') % action)
3136 3136
3137 3137 if batchedcommands is not None:
3138 3138 raise error.Abort(_('unclosed "batchbegin" request'))
3139 3139
3140 3140 if peer:
3141 3141 peer.close()
3142 3142
3143 3143 if proc:
3144 3144 proc.kill()
@@ -1,439 +1,439
1 1 $ cat << EOF >> $HGRCPATH
2 2 > [ui]
3 3 > interactive=yes
4 4 > EOF
5 5
6 6 $ hg init debugrevlog
7 7 $ cd debugrevlog
8 8 $ echo a > a
9 9 $ hg ci -Am adda
10 10 adding a
11 11 #if reporevlogstore
12 12 $ hg debugrevlog -m
13 13 format : 1
14 14 flags : inline, generaldelta
15 15
16 16 revisions : 1
17 17 merges : 0 ( 0.00%)
18 18 normal : 1 (100.00%)
19 19 revisions : 1
20 20 full : 1 (100.00%)
21 21 deltas : 0 ( 0.00%)
22 22 revision size : 44
23 23 full : 44 (100.00%)
24 24 deltas : 0 ( 0.00%)
25 25
26 26 chunks : 1
27 27 0x75 (u) : 1 (100.00%)
28 28 chunks size : 44
29 29 0x75 (u) : 44 (100.00%)
30 30
31 31 avg chain length : 0
32 32 max chain length : 0
33 33 max chain reach : 44
34 34 compression ratio : 0
35 35
36 36 uncompressed data size (min/max/avg) : 43 / 43 / 43
37 37 full revision size (min/max/avg) : 44 / 44 / 44
38 38 delta size (min/max/avg) : 0 / 0 / 0
39 39 #endif
40 40
41 41 Test debugindex, with and without the --verbose/--debug flag
42 42 $ hg debugindex a
43 43 rev linkrev nodeid p1 p2
44 44 0 0 b789fdd96dc2 000000000000 000000000000
45 45
46 46 #if no-reposimplestore
47 47 $ hg --verbose debugindex a
48 48 rev offset length linkrev nodeid p1 p2
49 49 0 0 3 0 b789fdd96dc2 000000000000 000000000000
50 50
51 51 $ hg --debug debugindex a
52 52 rev offset length linkrev nodeid p1 p2
53 53 0 0 3 0 b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000
54 54 #endif
55 55
56 56 $ hg debugindex -f 1 a
57 57 rev flag size link p1 p2 nodeid
58 58 0 0000 2 0 -1 -1 b789fdd96dc2
59 59
60 60 #if no-reposimplestore
61 61 $ hg --verbose debugindex -f 1 a
62 62 rev flag offset length size link p1 p2 nodeid
63 63 0 0000 0 3 2 0 -1 -1 b789fdd96dc2
64 64
65 65 $ hg --debug debugindex -f 1 a
66 66 rev flag offset length size link p1 p2 nodeid
67 67 0 0000 0 3 2 0 -1 -1 b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3
68 68 #endif
69 69
70 70 debugdelta chain basic output
71 71
72 72 #if reporevlogstore
73 73 $ hg debugdeltachain -m
74 74 rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio
75 75 0 1 1 -1 base 44 43 44 1.02326 44 0 0.00000
76 76
77 77 $ hg debugdeltachain -m -T '{rev} {chainid} {chainlen}\n'
78 78 0 1 1
79 79
80 80 $ hg debugdeltachain -m -Tjson
81 81 [
82 82 {
83 83 "chainid": 1,
84 84 "chainlen": 1,
85 85 "chainratio": 1.02325581395,
86 86 "chainsize": 44,
87 87 "compsize": 44,
88 88 "deltatype": "base",
89 89 "extradist": 0,
90 90 "extraratio": 0.0,
91 91 "lindist": 44,
92 92 "prevrev": -1,
93 93 "rev": 0,
94 94 "uncompsize": 43
95 95 }
96 96 ]
97 97
98 98 debugdelta chain with sparse read enabled
99 99
100 100 $ cat >> $HGRCPATH <<EOF
101 101 > [experimental]
102 102 > sparse-read = True
103 103 > EOF
104 104 $ hg debugdeltachain -m
105 105 rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio readsize largestblk rddensity srchunks
106 106 0 1 1 -1 base 44 43 44 1.02326 44 0 0.00000 44 44 1.00000 1
107 107
108 108 $ hg debugdeltachain -m -T '{rev} {chainid} {chainlen} {readsize} {largestblock} {readdensity}\n'
109 109 0 1 1 44 44 1.0
110 110
111 111 $ hg debugdeltachain -m -Tjson
112 112 [
113 113 {
114 114 "chainid": 1,
115 115 "chainlen": 1,
116 116 "chainratio": 1.02325581395,
117 117 "chainsize": 44,
118 118 "compsize": 44,
119 119 "deltatype": "base",
120 120 "extradist": 0,
121 121 "extraratio": 0.0,
122 122 "largestblock": 44,
123 123 "lindist": 44,
124 124 "prevrev": -1,
125 125 "readdensity": 1.0,
126 126 "readsize": 44,
127 127 "rev": 0,
128 128 "srchunks": 1,
129 129 "uncompsize": 43
130 130 }
131 131 ]
132 132
133 133 $ printf "This test checks things.\n" >> a
134 134 $ hg ci -m a
135 135 $ hg branch other
136 136 marked working directory as branch other
137 137 (branches are permanent and global, did you want a bookmark?)
138 138 $ for i in `$TESTDIR/seq.py 5`; do
139 139 > printf "shorter ${i}" >> a
140 140 > hg ci -m "a other:$i"
141 141 > hg up -q default
142 142 > printf "for the branch default we want longer chains: ${i}" >> a
143 143 > hg ci -m "a default:$i"
144 144 > hg up -q other
145 145 > done
146 146 $ hg debugdeltachain a -T '{rev} {srchunks}\n' \
147 147 > --config experimental.sparse-read.density-threshold=0.50 \
148 148 > --config experimental.sparse-read.min-gap-size=0
149 149 0 1
150 150 1 1
151 151 2 1
152 152 3 1
153 153 4 1
154 154 5 1
155 155 6 1
156 156 7 1
157 157 8 1
158 158 9 1
159 159 10 2
160 160 11 1
161 161 $ hg --config extensions.strip= strip --no-backup -r 1
162 162 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
163 163
164 164 Test max chain len
165 165 $ cat >> $HGRCPATH << EOF
166 166 > [format]
167 167 > maxchainlen=4
168 168 > EOF
169 169
170 170 $ printf "This test checks if maxchainlen config value is respected also it can serve as basic test for debugrevlog -d <file>.\n" >> a
171 171 $ hg ci -m a
172 172 $ printf "b\n" >> a
173 173 $ hg ci -m a
174 174 $ printf "c\n" >> a
175 175 $ hg ci -m a
176 176 $ printf "d\n" >> a
177 177 $ hg ci -m a
178 178 $ printf "e\n" >> a
179 179 $ hg ci -m a
180 180 $ printf "f\n" >> a
181 181 $ hg ci -m a
182 182 $ printf 'g\n' >> a
183 183 $ hg ci -m a
184 184 $ printf 'h\n' >> a
185 185 $ hg ci -m a
186 186
187 187 $ hg debugrevlog -d a
188 188 # rev p1rev p2rev start end deltastart base p1 p2 rawsize totalsize compression heads chainlen
189 189 0 -1 -1 0 ??? 0 0 0 0 ??? ???? ? 1 0 (glob)
190 190 1 0 -1 ??? ??? 0 0 0 0 ??? ???? ? 1 1 (glob)
191 191 2 1 -1 ??? ??? ??? ??? ??? 0 ??? ???? ? 1 2 (glob)
192 192 3 2 -1 ??? ??? ??? ??? ??? 0 ??? ???? ? 1 3 (glob)
193 193 4 3 -1 ??? ??? ??? ??? ??? 0 ??? ???? ? 1 4 (glob)
194 194 5 4 -1 ??? ??? ??? ??? ??? 0 ??? ???? ? 1 0 (glob)
195 195 6 5 -1 ??? ??? ??? ??? ??? 0 ??? ???? ? 1 1 (glob)
196 196 7 6 -1 ??? ??? ??? ??? ??? 0 ??? ???? ? 1 2 (glob)
197 197 8 7 -1 ??? ??? ??? ??? ??? 0 ??? ???? ? 1 3 (glob)
198 198 #endif
199 199
200 200 Test debuglocks command:
201 201
202 202 $ hg debuglocks
203 203 lock: free
204 204 wlock: free
205 205
206 206 * Test setting the lock
207 207
208 208 waitlock <file> will wait for file to be created. If it isn't in a reasonable
209 209 amount of time, displays error message and returns 1
210 210 $ waitlock() {
211 211 > start=`date +%s`
212 212 > timeout=5
213 213 > while [ \( ! -f $1 \) -a \( ! -L $1 \) ]; do
214 214 > now=`date +%s`
215 215 > if [ "`expr $now - $start`" -gt $timeout ]; then
216 216 > echo "timeout: $1 was not created in $timeout seconds"
217 217 > return 1
218 218 > fi
219 219 > sleep 0.1
220 220 > done
221 221 > }
222 222 $ dolock() {
223 223 > {
224 224 > waitlock .hg/unlock
225 225 > rm -f .hg/unlock
226 226 > echo y
227 227 > } | hg debuglocks "$@" > /dev/null
228 228 > }
229 229 $ dolock -s &
230 230 $ waitlock .hg/store/lock
231 231
232 232 $ hg debuglocks
233 233 lock: user *, process * (*s) (glob)
234 234 wlock: free
235 235 [1]
236 236 $ touch .hg/unlock
237 237 $ wait
238 238 $ [ -f .hg/store/lock ] || echo "There is no lock"
239 239 There is no lock
240 240
241 241 * Test setting the wlock
242 242
243 243 $ dolock -S &
244 244 $ waitlock .hg/wlock
245 245
246 246 $ hg debuglocks
247 247 lock: free
248 248 wlock: user *, process * (*s) (glob)
249 249 [1]
250 250 $ touch .hg/unlock
251 251 $ wait
252 252 $ [ -f .hg/wlock ] || echo "There is no wlock"
253 253 There is no wlock
254 254
255 255 * Test setting both locks
256 256
257 257 $ dolock -Ss &
258 258 $ waitlock .hg/wlock && waitlock .hg/store/lock
259 259
260 260 $ hg debuglocks
261 261 lock: user *, process * (*s) (glob)
262 262 wlock: user *, process * (*s) (glob)
263 263 [2]
264 264
265 265 * Test failing to set a lock
266 266
267 267 $ hg debuglocks -s
268 268 abort: lock is already held
269 269 [255]
270 270
271 271 $ hg debuglocks -S
272 272 abort: wlock is already held
273 273 [255]
274 274
275 275 $ touch .hg/unlock
276 276 $ wait
277 277
278 278 $ hg debuglocks
279 279 lock: free
280 280 wlock: free
281 281
282 282 * Test forcing the lock
283 283
284 284 $ dolock -s &
285 285 $ waitlock .hg/store/lock
286 286
287 287 $ hg debuglocks
288 288 lock: user *, process * (*s) (glob)
289 289 wlock: free
290 290 [1]
291 291
292 292 $ hg debuglocks -L
293 293
294 294 $ hg debuglocks
295 295 lock: free
296 296 wlock: free
297 297
298 298 $ touch .hg/unlock
299 299 $ wait
300 300
301 301 * Test forcing the wlock
302 302
303 303 $ dolock -S &
304 304 $ waitlock .hg/wlock
305 305
306 306 $ hg debuglocks
307 307 lock: free
308 308 wlock: user *, process * (*s) (glob)
309 309 [1]
310 310
311 311 $ hg debuglocks -W
312 312
313 313 $ hg debuglocks
314 314 lock: free
315 315 wlock: free
316 316
317 317 $ touch .hg/unlock
318 318 $ wait
319 319
320 320 Test WdirUnsupported exception
321 321
322 322 $ hg debugdata -c ffffffffffffffffffffffffffffffffffffffff
323 323 abort: working directory revision cannot be specified
324 324 [255]
325 325
326 326 Test cache warming command
327 327
328 328 $ rm -rf .hg/cache/
329 329 $ hg debugupdatecaches --debug
330 330 updating the branch cache
331 331 $ ls -r .hg/cache/*
332 332 .hg/cache/rbc-revs-v1
333 333 .hg/cache/rbc-names-v1
334 334 .hg/cache/branch2-served
335 335
336 336 Test debugcolor
337 337
338 338 #if no-windows
339 339 $ hg debugcolor --style --color always | egrep 'mode|style|log\.'
340 color mode: ansi
340 color mode: 'ansi'
341 341 available style:
342 342 \x1b[0;33mlog.changeset\x1b[0m: \x1b[0;33myellow\x1b[0m (esc)
343 343 #endif
344 344
345 345 $ hg debugcolor --style --color never
346 346 color mode: None
347 347 available style:
348 348
349 349 $ cd ..
350 350
351 351 Test internal debugstacktrace command
352 352
353 353 $ cat > debugstacktrace.py << EOF
354 354 > from __future__ import absolute_import
355 355 > import sys
356 356 > from mercurial import util
357 357 > def f():
358 358 > util.debugstacktrace(f=sys.stdout)
359 359 > g()
360 360 > def g():
361 361 > util.dst('hello from g\\n', skip=1)
362 362 > h()
363 363 > def h():
364 364 > util.dst('hi ...\\nfrom h hidden in g', 1, depth=2)
365 365 > f()
366 366 > EOF
367 367 $ $PYTHON debugstacktrace.py
368 368 stacktrace at:
369 369 debugstacktrace.py:12 in * (glob)
370 370 debugstacktrace.py:5 in f
371 371 hello from g at:
372 372 debugstacktrace.py:12 in * (glob)
373 373 debugstacktrace.py:6 in f
374 374 hi ...
375 375 from h hidden in g at:
376 376 debugstacktrace.py:6 in f
377 377 debugstacktrace.py:9 in g
378 378
379 379 Test debugcapabilities command:
380 380
381 381 $ hg debugcapabilities ./debugrevlog/
382 382 Main capabilities:
383 383 branchmap
384 384 $USUAL_BUNDLE2_CAPS$
385 385 getbundle
386 386 known
387 387 lookup
388 388 pushkey
389 389 unbundle
390 390 Bundle2 capabilities:
391 391 HG20
392 392 bookmarks
393 393 changegroup
394 394 01
395 395 02
396 396 digests
397 397 md5
398 398 sha1
399 399 sha512
400 400 error
401 401 abort
402 402 unsupportedcontent
403 403 pushraced
404 404 pushkey
405 405 hgtagsfnodes
406 406 listkeys
407 407 phases
408 408 heads
409 409 pushkey
410 410 remote-changegroup
411 411 http
412 412 https
413 413 rev-branch-cache
414 414 stream
415 415 v2
416 416
417 417 Test debugpeer
418 418
419 419 $ hg --config ui.ssh="\"$PYTHON\" \"$TESTDIR/dummyssh\"" debugpeer ssh://user@dummy/debugrevlog
420 420 url: ssh://user@dummy/debugrevlog
421 421 local: no
422 422 pushable: yes
423 423
424 424 $ hg --config ui.ssh="\"$PYTHON\" \"$TESTDIR/dummyssh\"" --debug debugpeer ssh://user@dummy/debugrevlog
425 425 running "*" "*/tests/dummyssh" 'user@dummy' 'hg -R debugrevlog serve --stdio' (glob) (no-windows !)
426 426 running "*" "*\tests/dummyssh" "user@dummy" "hg -R debugrevlog serve --stdio" (glob) (windows !)
427 427 devel-peer-request: hello+between
428 428 devel-peer-request: pairs: 81 bytes
429 429 sending hello command
430 430 sending between command
431 431 remote: 413
432 432 remote: capabilities: batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset getbundle known lookup protocaps pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
433 433 remote: 1
434 434 devel-peer-request: protocaps
435 435 devel-peer-request: caps: * bytes (glob)
436 436 sending protocaps command
437 437 url: ssh://user@dummy/debugrevlog
438 438 local: no
439 439 pushable: yes
General Comments 0
You need to be logged in to leave comments. Login now