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