##// END OF EJS Templates
py3: use '%d' to convert integers to bytes...
Pulkit Goyal -
r36477:580f75f7 default
parent child Browse files
Show More
@@ -1,2498 +1,2498 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 socket
18 18 import ssl
19 19 import string
20 20 import sys
21 21 import tempfile
22 22 import time
23 23
24 24 from .i18n import _
25 25 from .node import (
26 26 bin,
27 27 hex,
28 28 nullhex,
29 29 nullid,
30 30 nullrev,
31 31 short,
32 32 )
33 33 from . import (
34 34 bundle2,
35 35 changegroup,
36 36 cmdutil,
37 37 color,
38 38 context,
39 39 dagparser,
40 40 dagutil,
41 41 encoding,
42 42 error,
43 43 exchange,
44 44 extensions,
45 45 filemerge,
46 46 fileset,
47 47 formatter,
48 48 hg,
49 49 localrepo,
50 50 lock as lockmod,
51 51 logcmdutil,
52 52 merge as mergemod,
53 53 obsolete,
54 54 obsutil,
55 55 phases,
56 56 policy,
57 57 pvec,
58 58 pycompat,
59 59 registrar,
60 60 repair,
61 61 revlog,
62 62 revset,
63 63 revsetlang,
64 64 scmutil,
65 65 setdiscovery,
66 66 simplemerge,
67 67 smartset,
68 68 sslutil,
69 69 streamclone,
70 70 templater,
71 71 treediscovery,
72 72 upgrade,
73 73 url as urlmod,
74 74 util,
75 75 vfs as vfsmod,
76 76 )
77 77
78 78 release = lockmod.release
79 79
80 80 command = registrar.command()
81 81
82 82 @command('debugancestor', [], _('[INDEX] REV1 REV2'), optionalrepo=True)
83 83 def debugancestor(ui, repo, *args):
84 84 """find the ancestor revision of two revisions in a given index"""
85 85 if len(args) == 3:
86 86 index, rev1, rev2 = args
87 87 r = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False), index)
88 88 lookup = r.lookup
89 89 elif len(args) == 2:
90 90 if not repo:
91 91 raise error.Abort(_('there is no Mercurial repository here '
92 92 '(.hg not found)'))
93 93 rev1, rev2 = args
94 94 r = repo.changelog
95 95 lookup = repo.lookup
96 96 else:
97 97 raise error.Abort(_('either two or three arguments required'))
98 98 a = r.ancestor(lookup(rev1), lookup(rev2))
99 99 ui.write('%d:%s\n' % (r.rev(a), hex(a)))
100 100
101 101 @command('debugapplystreamclonebundle', [], 'FILE')
102 102 def debugapplystreamclonebundle(ui, repo, fname):
103 103 """apply a stream clone bundle file"""
104 104 f = hg.openpath(ui, fname)
105 105 gen = exchange.readbundle(ui, f, fname)
106 106 gen.apply(repo)
107 107
108 108 @command('debugbuilddag',
109 109 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
110 110 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
111 111 ('n', 'new-file', None, _('add new file at each rev'))],
112 112 _('[OPTION]... [TEXT]'))
113 113 def debugbuilddag(ui, repo, text=None,
114 114 mergeable_file=False,
115 115 overwritten_file=False,
116 116 new_file=False):
117 117 """builds a repo with a given DAG from scratch in the current empty repo
118 118
119 119 The description of the DAG is read from stdin if not given on the
120 120 command line.
121 121
122 122 Elements:
123 123
124 124 - "+n" is a linear run of n nodes based on the current default parent
125 125 - "." is a single node based on the current default parent
126 126 - "$" resets the default parent to null (implied at the start);
127 127 otherwise the default parent is always the last node created
128 128 - "<p" sets the default parent to the backref p
129 129 - "*p" is a fork at parent p, which is a backref
130 130 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
131 131 - "/p2" is a merge of the preceding node and p2
132 132 - ":tag" defines a local tag for the preceding node
133 133 - "@branch" sets the named branch for subsequent nodes
134 134 - "#...\\n" is a comment up to the end of the line
135 135
136 136 Whitespace between the above elements is ignored.
137 137
138 138 A backref is either
139 139
140 140 - a number n, which references the node curr-n, where curr is the current
141 141 node, or
142 142 - the name of a local tag you placed earlier using ":tag", or
143 143 - empty to denote the default parent.
144 144
145 145 All string valued-elements are either strictly alphanumeric, or must
146 146 be enclosed in double quotes ("..."), with "\\" as escape character.
147 147 """
148 148
149 149 if text is None:
150 150 ui.status(_("reading DAG from stdin\n"))
151 151 text = ui.fin.read()
152 152
153 153 cl = repo.changelog
154 154 if len(cl) > 0:
155 155 raise error.Abort(_('repository is not empty'))
156 156
157 157 # determine number of revs in DAG
158 158 total = 0
159 159 for type, data in dagparser.parsedag(text):
160 160 if type == 'n':
161 161 total += 1
162 162
163 163 if mergeable_file:
164 164 linesperrev = 2
165 165 # make a file with k lines per rev
166 166 initialmergedlines = ['%d' % i for i in xrange(0, total * linesperrev)]
167 167 initialmergedlines.append("")
168 168
169 169 tags = []
170 170
171 171 wlock = lock = tr = None
172 172 try:
173 173 wlock = repo.wlock()
174 174 lock = repo.lock()
175 175 tr = repo.transaction("builddag")
176 176
177 177 at = -1
178 178 atbranch = 'default'
179 179 nodeids = []
180 180 id = 0
181 181 ui.progress(_('building'), id, unit=_('revisions'), total=total)
182 182 for type, data in dagparser.parsedag(text):
183 183 if type == 'n':
184 184 ui.note(('node %s\n' % pycompat.bytestr(data)))
185 185 id, ps = data
186 186
187 187 files = []
188 188 filecontent = {}
189 189
190 190 p2 = None
191 191 if mergeable_file:
192 192 fn = "mf"
193 193 p1 = repo[ps[0]]
194 194 if len(ps) > 1:
195 195 p2 = repo[ps[1]]
196 196 pa = p1.ancestor(p2)
197 197 base, local, other = [x[fn].data() for x in (pa, p1,
198 198 p2)]
199 199 m3 = simplemerge.Merge3Text(base, local, other)
200 200 ml = [l.strip() for l in m3.merge_lines()]
201 201 ml.append("")
202 202 elif at > 0:
203 203 ml = p1[fn].data().split("\n")
204 204 else:
205 205 ml = initialmergedlines
206 206 ml[id * linesperrev] += " r%i" % id
207 207 mergedtext = "\n".join(ml)
208 208 files.append(fn)
209 209 filecontent[fn] = mergedtext
210 210
211 211 if overwritten_file:
212 212 fn = "of"
213 213 files.append(fn)
214 214 filecontent[fn] = "r%i\n" % id
215 215
216 216 if new_file:
217 217 fn = "nf%i" % id
218 218 files.append(fn)
219 219 filecontent[fn] = "r%i\n" % id
220 220 if len(ps) > 1:
221 221 if not p2:
222 222 p2 = repo[ps[1]]
223 223 for fn in p2:
224 224 if fn.startswith("nf"):
225 225 files.append(fn)
226 226 filecontent[fn] = p2[fn].data()
227 227
228 228 def fctxfn(repo, cx, path):
229 229 if path in filecontent:
230 230 return context.memfilectx(repo, cx, path,
231 231 filecontent[path])
232 232 return None
233 233
234 234 if len(ps) == 0 or ps[0] < 0:
235 235 pars = [None, None]
236 236 elif len(ps) == 1:
237 237 pars = [nodeids[ps[0]], None]
238 238 else:
239 239 pars = [nodeids[p] for p in ps]
240 240 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
241 241 date=(id, 0),
242 242 user="debugbuilddag",
243 243 extra={'branch': atbranch})
244 244 nodeid = repo.commitctx(cx)
245 245 nodeids.append(nodeid)
246 246 at = id
247 247 elif type == 'l':
248 248 id, name = data
249 249 ui.note(('tag %s\n' % name))
250 250 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
251 251 elif type == 'a':
252 252 ui.note(('branch %s\n' % data))
253 253 atbranch = data
254 254 ui.progress(_('building'), id, unit=_('revisions'), total=total)
255 255 tr.close()
256 256
257 257 if tags:
258 258 repo.vfs.write("localtags", "".join(tags))
259 259 finally:
260 260 ui.progress(_('building'), None)
261 261 release(tr, lock, wlock)
262 262
263 263 def _debugchangegroup(ui, gen, all=None, indent=0, **opts):
264 264 indent_string = ' ' * indent
265 265 if all:
266 266 ui.write(("%sformat: id, p1, p2, cset, delta base, len(delta)\n")
267 267 % indent_string)
268 268
269 269 def showchunks(named):
270 270 ui.write("\n%s%s\n" % (indent_string, named))
271 271 for deltadata in gen.deltaiter():
272 272 node, p1, p2, cs, deltabase, delta, flags = deltadata
273 ui.write("%s%s %s %s %s %s %s\n" %
273 ui.write("%s%s %s %s %s %s %d\n" %
274 274 (indent_string, hex(node), hex(p1), hex(p2),
275 275 hex(cs), hex(deltabase), len(delta)))
276 276
277 277 chunkdata = gen.changelogheader()
278 278 showchunks("changelog")
279 279 chunkdata = gen.manifestheader()
280 280 showchunks("manifest")
281 281 for chunkdata in iter(gen.filelogheader, {}):
282 282 fname = chunkdata['filename']
283 283 showchunks(fname)
284 284 else:
285 285 if isinstance(gen, bundle2.unbundle20):
286 286 raise error.Abort(_('use debugbundle2 for this file'))
287 287 chunkdata = gen.changelogheader()
288 288 for deltadata in gen.deltaiter():
289 289 node, p1, p2, cs, deltabase, delta, flags = deltadata
290 290 ui.write("%s%s\n" % (indent_string, hex(node)))
291 291
292 292 def _debugobsmarkers(ui, part, indent=0, **opts):
293 293 """display version and markers contained in 'data'"""
294 294 opts = pycompat.byteskwargs(opts)
295 295 data = part.read()
296 296 indent_string = ' ' * indent
297 297 try:
298 298 version, markers = obsolete._readmarkers(data)
299 299 except error.UnknownVersion as exc:
300 300 msg = "%sunsupported version: %s (%d bytes)\n"
301 301 msg %= indent_string, exc.version, len(data)
302 302 ui.write(msg)
303 303 else:
304 304 msg = "%sversion: %d (%d bytes)\n"
305 305 msg %= indent_string, version, len(data)
306 306 ui.write(msg)
307 307 fm = ui.formatter('debugobsolete', opts)
308 308 for rawmarker in sorted(markers):
309 309 m = obsutil.marker(None, rawmarker)
310 310 fm.startitem()
311 311 fm.plain(indent_string)
312 312 cmdutil.showmarker(fm, m)
313 313 fm.end()
314 314
315 315 def _debugphaseheads(ui, data, indent=0):
316 316 """display version and markers contained in 'data'"""
317 317 indent_string = ' ' * indent
318 318 headsbyphase = phases.binarydecode(data)
319 319 for phase in phases.allphases:
320 320 for head in headsbyphase[phase]:
321 321 ui.write(indent_string)
322 322 ui.write('%s %s\n' % (hex(head), phases.phasenames[phase]))
323 323
324 324 def _quasirepr(thing):
325 325 if isinstance(thing, (dict, util.sortdict, collections.OrderedDict)):
326 326 return '{%s}' % (
327 327 b', '.join(b'%s: %s' % (k, thing[k]) for k in sorted(thing)))
328 328 return pycompat.bytestr(repr(thing))
329 329
330 330 def _debugbundle2(ui, gen, all=None, **opts):
331 331 """lists the contents of a bundle2"""
332 332 if not isinstance(gen, bundle2.unbundle20):
333 333 raise error.Abort(_('not a bundle2 file'))
334 334 ui.write(('Stream params: %s\n' % _quasirepr(gen.params)))
335 335 parttypes = opts.get(r'part_type', [])
336 336 for part in gen.iterparts():
337 337 if parttypes and part.type not in parttypes:
338 338 continue
339 339 ui.write('%s -- %s\n' % (part.type, _quasirepr(part.params)))
340 340 if part.type == 'changegroup':
341 341 version = part.params.get('version', '01')
342 342 cg = changegroup.getunbundler(version, part, 'UN')
343 343 _debugchangegroup(ui, cg, all=all, indent=4, **opts)
344 344 if part.type == 'obsmarkers':
345 345 _debugobsmarkers(ui, part, indent=4, **opts)
346 346 if part.type == 'phase-heads':
347 347 _debugphaseheads(ui, part, indent=4)
348 348
349 349 @command('debugbundle',
350 350 [('a', 'all', None, _('show all details')),
351 351 ('', 'part-type', [], _('show only the named part type')),
352 352 ('', 'spec', None, _('print the bundlespec of the bundle'))],
353 353 _('FILE'),
354 354 norepo=True)
355 355 def debugbundle(ui, bundlepath, all=None, spec=None, **opts):
356 356 """lists the contents of a bundle"""
357 357 with hg.openpath(ui, bundlepath) as f:
358 358 if spec:
359 359 spec = exchange.getbundlespec(ui, f)
360 360 ui.write('%s\n' % spec)
361 361 return
362 362
363 363 gen = exchange.readbundle(ui, f, bundlepath)
364 364 if isinstance(gen, bundle2.unbundle20):
365 365 return _debugbundle2(ui, gen, all=all, **opts)
366 366 _debugchangegroup(ui, gen, all=all, **opts)
367 367
368 368 @command('debugcapabilities',
369 369 [], _('PATH'),
370 370 norepo=True)
371 371 def debugcapabilities(ui, path, **opts):
372 372 """lists the capabilities of a remote peer"""
373 373 opts = pycompat.byteskwargs(opts)
374 374 peer = hg.peer(ui, opts, path)
375 375 caps = peer.capabilities()
376 376 ui.write(('Main capabilities:\n'))
377 377 for c in sorted(caps):
378 378 ui.write((' %s\n') % c)
379 379 b2caps = bundle2.bundle2caps(peer)
380 380 if b2caps:
381 381 ui.write(('Bundle2 capabilities:\n'))
382 382 for key, values in sorted(b2caps.iteritems()):
383 383 ui.write((' %s\n') % key)
384 384 for v in values:
385 385 ui.write((' %s\n') % v)
386 386
387 387 @command('debugcheckstate', [], '')
388 388 def debugcheckstate(ui, repo):
389 389 """validate the correctness of the current dirstate"""
390 390 parent1, parent2 = repo.dirstate.parents()
391 391 m1 = repo[parent1].manifest()
392 392 m2 = repo[parent2].manifest()
393 393 errors = 0
394 394 for f in repo.dirstate:
395 395 state = repo.dirstate[f]
396 396 if state in "nr" and f not in m1:
397 397 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
398 398 errors += 1
399 399 if state in "a" and f in m1:
400 400 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
401 401 errors += 1
402 402 if state in "m" and f not in m1 and f not in m2:
403 403 ui.warn(_("%s in state %s, but not in either manifest\n") %
404 404 (f, state))
405 405 errors += 1
406 406 for f in m1:
407 407 state = repo.dirstate[f]
408 408 if state not in "nrm":
409 409 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
410 410 errors += 1
411 411 if errors:
412 412 error = _(".hg/dirstate inconsistent with current parent's manifest")
413 413 raise error.Abort(error)
414 414
415 415 @command('debugcolor',
416 416 [('', 'style', None, _('show all configured styles'))],
417 417 'hg debugcolor')
418 418 def debugcolor(ui, repo, **opts):
419 419 """show available color, effects or style"""
420 420 ui.write(('color mode: %s\n') % ui._colormode)
421 421 if opts.get(r'style'):
422 422 return _debugdisplaystyle(ui)
423 423 else:
424 424 return _debugdisplaycolor(ui)
425 425
426 426 def _debugdisplaycolor(ui):
427 427 ui = ui.copy()
428 428 ui._styles.clear()
429 429 for effect in color._activeeffects(ui).keys():
430 430 ui._styles[effect] = effect
431 431 if ui._terminfoparams:
432 432 for k, v in ui.configitems('color'):
433 433 if k.startswith('color.'):
434 434 ui._styles[k] = k[6:]
435 435 elif k.startswith('terminfo.'):
436 436 ui._styles[k] = k[9:]
437 437 ui.write(_('available colors:\n'))
438 438 # sort label with a '_' after the other to group '_background' entry.
439 439 items = sorted(ui._styles.items(),
440 440 key=lambda i: ('_' in i[0], i[0], i[1]))
441 441 for colorname, label in items:
442 442 ui.write(('%s\n') % colorname, label=label)
443 443
444 444 def _debugdisplaystyle(ui):
445 445 ui.write(_('available style:\n'))
446 446 width = max(len(s) for s in ui._styles)
447 447 for label, effects in sorted(ui._styles.items()):
448 448 ui.write('%s' % label, label=label)
449 449 if effects:
450 450 # 50
451 451 ui.write(': ')
452 452 ui.write(' ' * (max(0, width - len(label))))
453 453 ui.write(', '.join(ui.label(e, e) for e in effects.split()))
454 454 ui.write('\n')
455 455
456 456 @command('debugcreatestreamclonebundle', [], 'FILE')
457 457 def debugcreatestreamclonebundle(ui, repo, fname):
458 458 """create a stream clone bundle file
459 459
460 460 Stream bundles are special bundles that are essentially archives of
461 461 revlog files. They are commonly used for cloning very quickly.
462 462 """
463 463 # TODO we may want to turn this into an abort when this functionality
464 464 # is moved into `hg bundle`.
465 465 if phases.hassecret(repo):
466 466 ui.warn(_('(warning: stream clone bundle will contain secret '
467 467 'revisions)\n'))
468 468
469 469 requirements, gen = streamclone.generatebundlev1(repo)
470 470 changegroup.writechunks(ui, gen, fname)
471 471
472 472 ui.write(_('bundle requirements: %s\n') % ', '.join(sorted(requirements)))
473 473
474 474 @command('debugdag',
475 475 [('t', 'tags', None, _('use tags as labels')),
476 476 ('b', 'branches', None, _('annotate with branch names')),
477 477 ('', 'dots', None, _('use dots for runs')),
478 478 ('s', 'spaces', None, _('separate elements by spaces'))],
479 479 _('[OPTION]... [FILE [REV]...]'),
480 480 optionalrepo=True)
481 481 def debugdag(ui, repo, file_=None, *revs, **opts):
482 482 """format the changelog or an index DAG as a concise textual description
483 483
484 484 If you pass a revlog index, the revlog's DAG is emitted. If you list
485 485 revision numbers, they get labeled in the output as rN.
486 486
487 487 Otherwise, the changelog DAG of the current repo is emitted.
488 488 """
489 489 spaces = opts.get(r'spaces')
490 490 dots = opts.get(r'dots')
491 491 if file_:
492 492 rlog = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False),
493 493 file_)
494 494 revs = set((int(r) for r in revs))
495 495 def events():
496 496 for r in rlog:
497 497 yield 'n', (r, list(p for p in rlog.parentrevs(r)
498 498 if p != -1))
499 499 if r in revs:
500 500 yield 'l', (r, "r%i" % r)
501 501 elif repo:
502 502 cl = repo.changelog
503 503 tags = opts.get(r'tags')
504 504 branches = opts.get(r'branches')
505 505 if tags:
506 506 labels = {}
507 507 for l, n in repo.tags().items():
508 508 labels.setdefault(cl.rev(n), []).append(l)
509 509 def events():
510 510 b = "default"
511 511 for r in cl:
512 512 if branches:
513 513 newb = cl.read(cl.node(r))[5]['branch']
514 514 if newb != b:
515 515 yield 'a', newb
516 516 b = newb
517 517 yield 'n', (r, list(p for p in cl.parentrevs(r)
518 518 if p != -1))
519 519 if tags:
520 520 ls = labels.get(r)
521 521 if ls:
522 522 for l in ls:
523 523 yield 'l', (r, l)
524 524 else:
525 525 raise error.Abort(_('need repo for changelog dag'))
526 526
527 527 for line in dagparser.dagtextlines(events(),
528 528 addspaces=spaces,
529 529 wraplabels=True,
530 530 wrapannotations=True,
531 531 wrapnonlinear=dots,
532 532 usedots=dots,
533 533 maxlinewidth=70):
534 534 ui.write(line)
535 535 ui.write("\n")
536 536
537 537 @command('debugdata', cmdutil.debugrevlogopts, _('-c|-m|FILE REV'))
538 538 def debugdata(ui, repo, file_, rev=None, **opts):
539 539 """dump the contents of a data file revision"""
540 540 opts = pycompat.byteskwargs(opts)
541 541 if opts.get('changelog') or opts.get('manifest') or opts.get('dir'):
542 542 if rev is not None:
543 543 raise error.CommandError('debugdata', _('invalid arguments'))
544 544 file_, rev = None, file_
545 545 elif rev is None:
546 546 raise error.CommandError('debugdata', _('invalid arguments'))
547 547 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
548 548 try:
549 549 ui.write(r.revision(r.lookup(rev), raw=True))
550 550 except KeyError:
551 551 raise error.Abort(_('invalid revision identifier %s') % rev)
552 552
553 553 @command('debugdate',
554 554 [('e', 'extended', None, _('try extended date formats'))],
555 555 _('[-e] DATE [RANGE]'),
556 556 norepo=True, optionalrepo=True)
557 557 def debugdate(ui, date, range=None, **opts):
558 558 """parse and display a date"""
559 559 if opts[r"extended"]:
560 560 d = util.parsedate(date, util.extendeddateformats)
561 561 else:
562 562 d = util.parsedate(date)
563 563 ui.write(("internal: %d %d\n") % d)
564 564 ui.write(("standard: %s\n") % util.datestr(d))
565 565 if range:
566 566 m = util.matchdate(range)
567 567 ui.write(("match: %s\n") % m(d[0]))
568 568
569 569 @command('debugdeltachain',
570 570 cmdutil.debugrevlogopts + cmdutil.formatteropts,
571 571 _('-c|-m|FILE'),
572 572 optionalrepo=True)
573 573 def debugdeltachain(ui, repo, file_=None, **opts):
574 574 """dump information about delta chains in a revlog
575 575
576 576 Output can be templatized. Available template keywords are:
577 577
578 578 :``rev``: revision number
579 579 :``chainid``: delta chain identifier (numbered by unique base)
580 580 :``chainlen``: delta chain length to this revision
581 581 :``prevrev``: previous revision in delta chain
582 582 :``deltatype``: role of delta / how it was computed
583 583 :``compsize``: compressed size of revision
584 584 :``uncompsize``: uncompressed size of revision
585 585 :``chainsize``: total size of compressed revisions in chain
586 586 :``chainratio``: total chain size divided by uncompressed revision size
587 587 (new delta chains typically start at ratio 2.00)
588 588 :``lindist``: linear distance from base revision in delta chain to end
589 589 of this revision
590 590 :``extradist``: total size of revisions not part of this delta chain from
591 591 base of delta chain to end of this revision; a measurement
592 592 of how much extra data we need to read/seek across to read
593 593 the delta chain for this revision
594 594 :``extraratio``: extradist divided by chainsize; another representation of
595 595 how much unrelated data is needed to load this delta chain
596 596
597 597 If the repository is configured to use the sparse read, additional keywords
598 598 are available:
599 599
600 600 :``readsize``: total size of data read from the disk for a revision
601 601 (sum of the sizes of all the blocks)
602 602 :``largestblock``: size of the largest block of data read from the disk
603 603 :``readdensity``: density of useful bytes in the data read from the disk
604 604 :``srchunks``: in how many data hunks the whole revision would be read
605 605
606 606 The sparse read can be enabled with experimental.sparse-read = True
607 607 """
608 608 opts = pycompat.byteskwargs(opts)
609 609 r = cmdutil.openrevlog(repo, 'debugdeltachain', file_, opts)
610 610 index = r.index
611 611 generaldelta = r.version & revlog.FLAG_GENERALDELTA
612 612 withsparseread = getattr(r, '_withsparseread', False)
613 613
614 614 def revinfo(rev):
615 615 e = index[rev]
616 616 compsize = e[1]
617 617 uncompsize = e[2]
618 618 chainsize = 0
619 619
620 620 if generaldelta:
621 621 if e[3] == e[5]:
622 622 deltatype = 'p1'
623 623 elif e[3] == e[6]:
624 624 deltatype = 'p2'
625 625 elif e[3] == rev - 1:
626 626 deltatype = 'prev'
627 627 elif e[3] == rev:
628 628 deltatype = 'base'
629 629 else:
630 630 deltatype = 'other'
631 631 else:
632 632 if e[3] == rev:
633 633 deltatype = 'base'
634 634 else:
635 635 deltatype = 'prev'
636 636
637 637 chain = r._deltachain(rev)[0]
638 638 for iterrev in chain:
639 639 e = index[iterrev]
640 640 chainsize += e[1]
641 641
642 642 return compsize, uncompsize, deltatype, chain, chainsize
643 643
644 644 fm = ui.formatter('debugdeltachain', opts)
645 645
646 646 fm.plain(' rev chain# chainlen prev delta '
647 647 'size rawsize chainsize ratio lindist extradist '
648 648 'extraratio')
649 649 if withsparseread:
650 650 fm.plain(' readsize largestblk rddensity srchunks')
651 651 fm.plain('\n')
652 652
653 653 chainbases = {}
654 654 for rev in r:
655 655 comp, uncomp, deltatype, chain, chainsize = revinfo(rev)
656 656 chainbase = chain[0]
657 657 chainid = chainbases.setdefault(chainbase, len(chainbases) + 1)
658 658 start = r.start
659 659 length = r.length
660 660 basestart = start(chainbase)
661 661 revstart = start(rev)
662 662 lineardist = revstart + comp - basestart
663 663 extradist = lineardist - chainsize
664 664 try:
665 665 prevrev = chain[-2]
666 666 except IndexError:
667 667 prevrev = -1
668 668
669 669 chainratio = float(chainsize) / float(uncomp)
670 670 extraratio = float(extradist) / float(chainsize)
671 671
672 672 fm.startitem()
673 673 fm.write('rev chainid chainlen prevrev deltatype compsize '
674 674 'uncompsize chainsize chainratio lindist extradist '
675 675 'extraratio',
676 676 '%7d %7d %8d %8d %7s %10d %10d %10d %9.5f %9d %9d %10.5f',
677 677 rev, chainid, len(chain), prevrev, deltatype, comp,
678 678 uncomp, chainsize, chainratio, lineardist, extradist,
679 679 extraratio,
680 680 rev=rev, chainid=chainid, chainlen=len(chain),
681 681 prevrev=prevrev, deltatype=deltatype, compsize=comp,
682 682 uncompsize=uncomp, chainsize=chainsize,
683 683 chainratio=chainratio, lindist=lineardist,
684 684 extradist=extradist, extraratio=extraratio)
685 685 if withsparseread:
686 686 readsize = 0
687 687 largestblock = 0
688 688 srchunks = 0
689 689
690 690 for revschunk in revlog._slicechunk(r, chain):
691 691 srchunks += 1
692 692 blkend = start(revschunk[-1]) + length(revschunk[-1])
693 693 blksize = blkend - start(revschunk[0])
694 694
695 695 readsize += blksize
696 696 if largestblock < blksize:
697 697 largestblock = blksize
698 698
699 699 readdensity = float(chainsize) / float(readsize)
700 700
701 701 fm.write('readsize largestblock readdensity srchunks',
702 702 ' %10d %10d %9.5f %8d',
703 703 readsize, largestblock, readdensity, srchunks,
704 704 readsize=readsize, largestblock=largestblock,
705 705 readdensity=readdensity, srchunks=srchunks)
706 706
707 707 fm.plain('\n')
708 708
709 709 fm.end()
710 710
711 711 @command('debugdirstate|debugstate',
712 712 [('', 'nodates', None, _('do not display the saved mtime')),
713 713 ('', 'datesort', None, _('sort by saved mtime'))],
714 714 _('[OPTION]...'))
715 715 def debugstate(ui, repo, **opts):
716 716 """show the contents of the current dirstate"""
717 717
718 718 nodates = opts.get(r'nodates')
719 719 datesort = opts.get(r'datesort')
720 720
721 721 timestr = ""
722 722 if datesort:
723 723 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
724 724 else:
725 725 keyfunc = None # sort by filename
726 726 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
727 727 if ent[3] == -1:
728 728 timestr = 'unset '
729 729 elif nodates:
730 730 timestr = 'set '
731 731 else:
732 732 timestr = time.strftime(r"%Y-%m-%d %H:%M:%S ",
733 733 time.localtime(ent[3]))
734 734 timestr = encoding.strtolocal(timestr)
735 735 if ent[1] & 0o20000:
736 736 mode = 'lnk'
737 737 else:
738 738 mode = '%3o' % (ent[1] & 0o777 & ~util.umask)
739 739 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
740 740 for f in repo.dirstate.copies():
741 741 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
742 742
743 743 @command('debugdiscovery',
744 744 [('', 'old', None, _('use old-style discovery')),
745 745 ('', 'nonheads', None,
746 746 _('use old-style discovery with non-heads included')),
747 747 ('', 'rev', [], 'restrict discovery to this set of revs'),
748 748 ] + cmdutil.remoteopts,
749 749 _('[--rev REV] [OTHER]'))
750 750 def debugdiscovery(ui, repo, remoteurl="default", **opts):
751 751 """runs the changeset discovery protocol in isolation"""
752 752 opts = pycompat.byteskwargs(opts)
753 753 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl))
754 754 remote = hg.peer(repo, opts, remoteurl)
755 755 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
756 756
757 757 # make sure tests are repeatable
758 758 random.seed(12323)
759 759
760 760 def doit(pushedrevs, remoteheads, remote=remote):
761 761 if opts.get('old'):
762 762 if not util.safehasattr(remote, 'branches'):
763 763 # enable in-client legacy support
764 764 remote = localrepo.locallegacypeer(remote.local())
765 765 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
766 766 force=True)
767 767 common = set(common)
768 768 if not opts.get('nonheads'):
769 769 ui.write(("unpruned common: %s\n") %
770 770 " ".join(sorted(short(n) for n in common)))
771 771 dag = dagutil.revlogdag(repo.changelog)
772 772 all = dag.ancestorset(dag.internalizeall(common))
773 773 common = dag.externalizeall(dag.headsetofconnecteds(all))
774 774 else:
775 775 nodes = None
776 776 if pushedrevs:
777 777 revs = scmutil.revrange(repo, pushedrevs)
778 778 nodes = [repo[r].node() for r in revs]
779 779 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote,
780 780 ancestorsof=nodes)
781 781 common = set(common)
782 782 rheads = set(hds)
783 783 lheads = set(repo.heads())
784 784 ui.write(("common heads: %s\n") %
785 785 " ".join(sorted(short(n) for n in common)))
786 786 if lheads <= common:
787 787 ui.write(("local is subset\n"))
788 788 elif rheads <= common:
789 789 ui.write(("remote is subset\n"))
790 790
791 791 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches, revs=None)
792 792 localrevs = opts['rev']
793 793 doit(localrevs, remoterevs)
794 794
795 795 _chunksize = 4 << 10
796 796
797 797 @command('debugdownload',
798 798 [
799 799 ('o', 'output', '', _('path')),
800 800 ],
801 801 optionalrepo=True)
802 802 def debugdownload(ui, repo, url, output=None, **opts):
803 803 """download a resource using Mercurial logic and config
804 804 """
805 805 fh = urlmod.open(ui, url, output)
806 806
807 807 dest = ui
808 808 if output:
809 809 dest = open(output, "wb", _chunksize)
810 810 try:
811 811 data = fh.read(_chunksize)
812 812 while data:
813 813 dest.write(data)
814 814 data = fh.read(_chunksize)
815 815 finally:
816 816 if output:
817 817 dest.close()
818 818
819 819 @command('debugextensions', cmdutil.formatteropts, [], norepo=True)
820 820 def debugextensions(ui, **opts):
821 821 '''show information about active extensions'''
822 822 opts = pycompat.byteskwargs(opts)
823 823 exts = extensions.extensions(ui)
824 824 hgver = util.version()
825 825 fm = ui.formatter('debugextensions', opts)
826 826 for extname, extmod in sorted(exts, key=operator.itemgetter(0)):
827 827 isinternal = extensions.ismoduleinternal(extmod)
828 828 extsource = pycompat.fsencode(extmod.__file__)
829 829 if isinternal:
830 830 exttestedwith = [] # never expose magic string to users
831 831 else:
832 832 exttestedwith = getattr(extmod, 'testedwith', '').split()
833 833 extbuglink = getattr(extmod, 'buglink', None)
834 834
835 835 fm.startitem()
836 836
837 837 if ui.quiet or ui.verbose:
838 838 fm.write('name', '%s\n', extname)
839 839 else:
840 840 fm.write('name', '%s', extname)
841 841 if isinternal or hgver in exttestedwith:
842 842 fm.plain('\n')
843 843 elif not exttestedwith:
844 844 fm.plain(_(' (untested!)\n'))
845 845 else:
846 846 lasttestedversion = exttestedwith[-1]
847 847 fm.plain(' (%s!)\n' % lasttestedversion)
848 848
849 849 fm.condwrite(ui.verbose and extsource, 'source',
850 850 _(' location: %s\n'), extsource or "")
851 851
852 852 if ui.verbose:
853 853 fm.plain(_(' bundled: %s\n') % ['no', 'yes'][isinternal])
854 854 fm.data(bundled=isinternal)
855 855
856 856 fm.condwrite(ui.verbose and exttestedwith, 'testedwith',
857 857 _(' tested with: %s\n'),
858 858 fm.formatlist(exttestedwith, name='ver'))
859 859
860 860 fm.condwrite(ui.verbose and extbuglink, 'buglink',
861 861 _(' bug reporting: %s\n'), extbuglink or "")
862 862
863 863 fm.end()
864 864
865 865 @command('debugfileset',
866 866 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
867 867 _('[-r REV] FILESPEC'))
868 868 def debugfileset(ui, repo, expr, **opts):
869 869 '''parse and apply a fileset specification'''
870 870 ctx = scmutil.revsingle(repo, opts.get(r'rev'), None)
871 871 if ui.verbose:
872 872 tree = fileset.parse(expr)
873 873 ui.note(fileset.prettyformat(tree), "\n")
874 874
875 875 for f in ctx.getfileset(expr):
876 876 ui.write("%s\n" % f)
877 877
878 878 @command('debugformat',
879 879 [] + cmdutil.formatteropts,
880 880 _(''))
881 881 def debugformat(ui, repo, **opts):
882 882 """display format information about the current repository
883 883
884 884 Use --verbose to get extra information about current config value and
885 885 Mercurial default."""
886 886 opts = pycompat.byteskwargs(opts)
887 887 maxvariantlength = max(len(fv.name) for fv in upgrade.allformatvariant)
888 888 maxvariantlength = max(len('format-variant'), maxvariantlength)
889 889
890 890 def makeformatname(name):
891 891 return '%s:' + (' ' * (maxvariantlength - len(name)))
892 892
893 893 fm = ui.formatter('debugformat', opts)
894 894 if fm.isplain():
895 895 def formatvalue(value):
896 896 if util.safehasattr(value, 'startswith'):
897 897 return value
898 898 if value:
899 899 return 'yes'
900 900 else:
901 901 return 'no'
902 902 else:
903 903 formatvalue = pycompat.identity
904 904
905 905 fm.plain('format-variant')
906 906 fm.plain(' ' * (maxvariantlength - len('format-variant')))
907 907 fm.plain(' repo')
908 908 if ui.verbose:
909 909 fm.plain(' config default')
910 910 fm.plain('\n')
911 911 for fv in upgrade.allformatvariant:
912 912 fm.startitem()
913 913 repovalue = fv.fromrepo(repo)
914 914 configvalue = fv.fromconfig(repo)
915 915
916 916 if repovalue != configvalue:
917 917 namelabel = 'formatvariant.name.mismatchconfig'
918 918 repolabel = 'formatvariant.repo.mismatchconfig'
919 919 elif repovalue != fv.default:
920 920 namelabel = 'formatvariant.name.mismatchdefault'
921 921 repolabel = 'formatvariant.repo.mismatchdefault'
922 922 else:
923 923 namelabel = 'formatvariant.name.uptodate'
924 924 repolabel = 'formatvariant.repo.uptodate'
925 925
926 926 fm.write('name', makeformatname(fv.name), fv.name,
927 927 label=namelabel)
928 928 fm.write('repo', ' %3s', formatvalue(repovalue),
929 929 label=repolabel)
930 930 if fv.default != configvalue:
931 931 configlabel = 'formatvariant.config.special'
932 932 else:
933 933 configlabel = 'formatvariant.config.default'
934 934 fm.condwrite(ui.verbose, 'config', ' %6s', formatvalue(configvalue),
935 935 label=configlabel)
936 936 fm.condwrite(ui.verbose, 'default', ' %7s', formatvalue(fv.default),
937 937 label='formatvariant.default')
938 938 fm.plain('\n')
939 939 fm.end()
940 940
941 941 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
942 942 def debugfsinfo(ui, path="."):
943 943 """show information detected about current filesystem"""
944 944 ui.write(('path: %s\n') % path)
945 945 ui.write(('mounted on: %s\n') % (util.getfsmountpoint(path) or '(unknown)'))
946 946 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
947 947 ui.write(('fstype: %s\n') % (util.getfstype(path) or '(unknown)'))
948 948 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
949 949 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
950 950 casesensitive = '(unknown)'
951 951 try:
952 952 with tempfile.NamedTemporaryFile(prefix='.debugfsinfo', dir=path) as f:
953 953 casesensitive = util.fscasesensitive(f.name) and 'yes' or 'no'
954 954 except OSError:
955 955 pass
956 956 ui.write(('case-sensitive: %s\n') % casesensitive)
957 957
958 958 @command('debuggetbundle',
959 959 [('H', 'head', [], _('id of head node'), _('ID')),
960 960 ('C', 'common', [], _('id of common node'), _('ID')),
961 961 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
962 962 _('REPO FILE [-H|-C ID]...'),
963 963 norepo=True)
964 964 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
965 965 """retrieves a bundle from a repo
966 966
967 967 Every ID must be a full-length hex node id string. Saves the bundle to the
968 968 given file.
969 969 """
970 970 opts = pycompat.byteskwargs(opts)
971 971 repo = hg.peer(ui, opts, repopath)
972 972 if not repo.capable('getbundle'):
973 973 raise error.Abort("getbundle() not supported by target repository")
974 974 args = {}
975 975 if common:
976 976 args[r'common'] = [bin(s) for s in common]
977 977 if head:
978 978 args[r'heads'] = [bin(s) for s in head]
979 979 # TODO: get desired bundlecaps from command line.
980 980 args[r'bundlecaps'] = None
981 981 bundle = repo.getbundle('debug', **args)
982 982
983 983 bundletype = opts.get('type', 'bzip2').lower()
984 984 btypes = {'none': 'HG10UN',
985 985 'bzip2': 'HG10BZ',
986 986 'gzip': 'HG10GZ',
987 987 'bundle2': 'HG20'}
988 988 bundletype = btypes.get(bundletype)
989 989 if bundletype not in bundle2.bundletypes:
990 990 raise error.Abort(_('unknown bundle type specified with --type'))
991 991 bundle2.writebundle(ui, bundle, bundlepath, bundletype)
992 992
993 993 @command('debugignore', [], '[FILE]')
994 994 def debugignore(ui, repo, *files, **opts):
995 995 """display the combined ignore pattern and information about ignored files
996 996
997 997 With no argument display the combined ignore pattern.
998 998
999 999 Given space separated file names, shows if the given file is ignored and
1000 1000 if so, show the ignore rule (file and line number) that matched it.
1001 1001 """
1002 1002 ignore = repo.dirstate._ignore
1003 1003 if not files:
1004 1004 # Show all the patterns
1005 1005 ui.write("%s\n" % repr(ignore))
1006 1006 else:
1007 1007 m = scmutil.match(repo[None], pats=files)
1008 1008 for f in m.files():
1009 1009 nf = util.normpath(f)
1010 1010 ignored = None
1011 1011 ignoredata = None
1012 1012 if nf != '.':
1013 1013 if ignore(nf):
1014 1014 ignored = nf
1015 1015 ignoredata = repo.dirstate._ignorefileandline(nf)
1016 1016 else:
1017 1017 for p in util.finddirs(nf):
1018 1018 if ignore(p):
1019 1019 ignored = p
1020 1020 ignoredata = repo.dirstate._ignorefileandline(p)
1021 1021 break
1022 1022 if ignored:
1023 1023 if ignored == nf:
1024 1024 ui.write(_("%s is ignored\n") % m.uipath(f))
1025 1025 else:
1026 1026 ui.write(_("%s is ignored because of "
1027 1027 "containing folder %s\n")
1028 1028 % (m.uipath(f), ignored))
1029 1029 ignorefile, lineno, line = ignoredata
1030 1030 ui.write(_("(ignore rule in %s, line %d: '%s')\n")
1031 1031 % (ignorefile, lineno, line))
1032 1032 else:
1033 1033 ui.write(_("%s is not ignored\n") % m.uipath(f))
1034 1034
1035 1035 @command('debugindex', cmdutil.debugrevlogopts +
1036 1036 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1037 1037 _('[-f FORMAT] -c|-m|FILE'),
1038 1038 optionalrepo=True)
1039 1039 def debugindex(ui, repo, file_=None, **opts):
1040 1040 """dump the contents of an index file"""
1041 1041 opts = pycompat.byteskwargs(opts)
1042 1042 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1043 1043 format = opts.get('format', 0)
1044 1044 if format not in (0, 1):
1045 1045 raise error.Abort(_("unknown format %d") % format)
1046 1046
1047 1047 generaldelta = r.version & revlog.FLAG_GENERALDELTA
1048 1048 if generaldelta:
1049 1049 basehdr = ' delta'
1050 1050 else:
1051 1051 basehdr = ' base'
1052 1052
1053 1053 if ui.debugflag:
1054 1054 shortfn = hex
1055 1055 else:
1056 1056 shortfn = short
1057 1057
1058 1058 # There might not be anything in r, so have a sane default
1059 1059 idlen = 12
1060 1060 for i in r:
1061 1061 idlen = len(shortfn(r.node(i)))
1062 1062 break
1063 1063
1064 1064 if format == 0:
1065 1065 ui.write((" rev offset length " + basehdr + " linkrev"
1066 1066 " %s %s p2\n") % ("nodeid".ljust(idlen), "p1".ljust(idlen)))
1067 1067 elif format == 1:
1068 1068 ui.write((" rev flag offset length"
1069 1069 " size " + basehdr + " link p1 p2"
1070 1070 " %s\n") % "nodeid".rjust(idlen))
1071 1071
1072 1072 for i in r:
1073 1073 node = r.node(i)
1074 1074 if generaldelta:
1075 1075 base = r.deltaparent(i)
1076 1076 else:
1077 1077 base = r.chainbase(i)
1078 1078 if format == 0:
1079 1079 try:
1080 1080 pp = r.parents(node)
1081 1081 except Exception:
1082 1082 pp = [nullid, nullid]
1083 1083 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1084 1084 i, r.start(i), r.length(i), base, r.linkrev(i),
1085 1085 shortfn(node), shortfn(pp[0]), shortfn(pp[1])))
1086 1086 elif format == 1:
1087 1087 pr = r.parentrevs(i)
1088 1088 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1089 1089 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1090 1090 base, r.linkrev(i), pr[0], pr[1], shortfn(node)))
1091 1091
1092 1092 @command('debugindexdot', cmdutil.debugrevlogopts,
1093 1093 _('-c|-m|FILE'), optionalrepo=True)
1094 1094 def debugindexdot(ui, repo, file_=None, **opts):
1095 1095 """dump an index DAG as a graphviz dot file"""
1096 1096 opts = pycompat.byteskwargs(opts)
1097 1097 r = cmdutil.openrevlog(repo, 'debugindexdot', file_, opts)
1098 1098 ui.write(("digraph G {\n"))
1099 1099 for i in r:
1100 1100 node = r.node(i)
1101 1101 pp = r.parents(node)
1102 1102 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1103 1103 if pp[1] != nullid:
1104 1104 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1105 1105 ui.write("}\n")
1106 1106
1107 1107 @command('debuginstall', [] + cmdutil.formatteropts, '', norepo=True)
1108 1108 def debuginstall(ui, **opts):
1109 1109 '''test Mercurial installation
1110 1110
1111 1111 Returns 0 on success.
1112 1112 '''
1113 1113 opts = pycompat.byteskwargs(opts)
1114 1114
1115 1115 def writetemp(contents):
1116 1116 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1117 1117 f = os.fdopen(fd, pycompat.sysstr("wb"))
1118 1118 f.write(contents)
1119 1119 f.close()
1120 1120 return name
1121 1121
1122 1122 problems = 0
1123 1123
1124 1124 fm = ui.formatter('debuginstall', opts)
1125 1125 fm.startitem()
1126 1126
1127 1127 # encoding
1128 1128 fm.write('encoding', _("checking encoding (%s)...\n"), encoding.encoding)
1129 1129 err = None
1130 1130 try:
1131 1131 codecs.lookup(pycompat.sysstr(encoding.encoding))
1132 1132 except LookupError as inst:
1133 1133 err = util.forcebytestr(inst)
1134 1134 problems += 1
1135 1135 fm.condwrite(err, 'encodingerror', _(" %s\n"
1136 1136 " (check that your locale is properly set)\n"), err)
1137 1137
1138 1138 # Python
1139 1139 fm.write('pythonexe', _("checking Python executable (%s)\n"),
1140 1140 pycompat.sysexecutable)
1141 1141 fm.write('pythonver', _("checking Python version (%s)\n"),
1142 1142 ("%d.%d.%d" % sys.version_info[:3]))
1143 1143 fm.write('pythonlib', _("checking Python lib (%s)...\n"),
1144 1144 os.path.dirname(pycompat.fsencode(os.__file__)))
1145 1145
1146 1146 security = set(sslutil.supportedprotocols)
1147 1147 if sslutil.hassni:
1148 1148 security.add('sni')
1149 1149
1150 1150 fm.write('pythonsecurity', _("checking Python security support (%s)\n"),
1151 1151 fm.formatlist(sorted(security), name='protocol',
1152 1152 fmt='%s', sep=','))
1153 1153
1154 1154 # These are warnings, not errors. So don't increment problem count. This
1155 1155 # may change in the future.
1156 1156 if 'tls1.2' not in security:
1157 1157 fm.plain(_(' TLS 1.2 not supported by Python install; '
1158 1158 'network connections lack modern security\n'))
1159 1159 if 'sni' not in security:
1160 1160 fm.plain(_(' SNI not supported by Python install; may have '
1161 1161 'connectivity issues with some servers\n'))
1162 1162
1163 1163 # TODO print CA cert info
1164 1164
1165 1165 # hg version
1166 1166 hgver = util.version()
1167 1167 fm.write('hgver', _("checking Mercurial version (%s)\n"),
1168 1168 hgver.split('+')[0])
1169 1169 fm.write('hgverextra', _("checking Mercurial custom build (%s)\n"),
1170 1170 '+'.join(hgver.split('+')[1:]))
1171 1171
1172 1172 # compiled modules
1173 1173 fm.write('hgmodulepolicy', _("checking module policy (%s)\n"),
1174 1174 policy.policy)
1175 1175 fm.write('hgmodules', _("checking installed modules (%s)...\n"),
1176 1176 os.path.dirname(pycompat.fsencode(__file__)))
1177 1177
1178 1178 if policy.policy in ('c', 'allow'):
1179 1179 err = None
1180 1180 try:
1181 1181 from .cext import (
1182 1182 base85,
1183 1183 bdiff,
1184 1184 mpatch,
1185 1185 osutil,
1186 1186 )
1187 1187 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
1188 1188 except Exception as inst:
1189 1189 err = util.forcebytestr(inst)
1190 1190 problems += 1
1191 1191 fm.condwrite(err, 'extensionserror', " %s\n", err)
1192 1192
1193 1193 compengines = util.compengines._engines.values()
1194 1194 fm.write('compengines', _('checking registered compression engines (%s)\n'),
1195 1195 fm.formatlist(sorted(e.name() for e in compengines),
1196 1196 name='compengine', fmt='%s', sep=', '))
1197 1197 fm.write('compenginesavail', _('checking available compression engines '
1198 1198 '(%s)\n'),
1199 1199 fm.formatlist(sorted(e.name() for e in compengines
1200 1200 if e.available()),
1201 1201 name='compengine', fmt='%s', sep=', '))
1202 1202 wirecompengines = util.compengines.supportedwireengines(util.SERVERROLE)
1203 1203 fm.write('compenginesserver', _('checking available compression engines '
1204 1204 'for wire protocol (%s)\n'),
1205 1205 fm.formatlist([e.name() for e in wirecompengines
1206 1206 if e.wireprotosupport()],
1207 1207 name='compengine', fmt='%s', sep=', '))
1208 1208 re2 = 'missing'
1209 1209 if util._re2:
1210 1210 re2 = 'available'
1211 1211 fm.plain(_('checking "re2" regexp engine (%s)\n') % re2)
1212 1212 fm.data(re2=bool(util._re2))
1213 1213
1214 1214 # templates
1215 1215 p = templater.templatepaths()
1216 1216 fm.write('templatedirs', 'checking templates (%s)...\n', ' '.join(p))
1217 1217 fm.condwrite(not p, '', _(" no template directories found\n"))
1218 1218 if p:
1219 1219 m = templater.templatepath("map-cmdline.default")
1220 1220 if m:
1221 1221 # template found, check if it is working
1222 1222 err = None
1223 1223 try:
1224 1224 templater.templater.frommapfile(m)
1225 1225 except Exception as inst:
1226 1226 err = util.forcebytestr(inst)
1227 1227 p = None
1228 1228 fm.condwrite(err, 'defaulttemplateerror', " %s\n", err)
1229 1229 else:
1230 1230 p = None
1231 1231 fm.condwrite(p, 'defaulttemplate',
1232 1232 _("checking default template (%s)\n"), m)
1233 1233 fm.condwrite(not m, 'defaulttemplatenotfound',
1234 1234 _(" template '%s' not found\n"), "default")
1235 1235 if not p:
1236 1236 problems += 1
1237 1237 fm.condwrite(not p, '',
1238 1238 _(" (templates seem to have been installed incorrectly)\n"))
1239 1239
1240 1240 # editor
1241 1241 editor = ui.geteditor()
1242 1242 editor = util.expandpath(editor)
1243 1243 editorbin = util.shellsplit(editor)[0]
1244 1244 fm.write('editor', _("checking commit editor... (%s)\n"), editorbin)
1245 1245 cmdpath = util.findexe(editorbin)
1246 1246 fm.condwrite(not cmdpath and editor == 'vi', 'vinotfound',
1247 1247 _(" No commit editor set and can't find %s in PATH\n"
1248 1248 " (specify a commit editor in your configuration"
1249 1249 " file)\n"), not cmdpath and editor == 'vi' and editorbin)
1250 1250 fm.condwrite(not cmdpath and editor != 'vi', 'editornotfound',
1251 1251 _(" Can't find editor '%s' in PATH\n"
1252 1252 " (specify a commit editor in your configuration"
1253 1253 " file)\n"), not cmdpath and editorbin)
1254 1254 if not cmdpath and editor != 'vi':
1255 1255 problems += 1
1256 1256
1257 1257 # check username
1258 1258 username = None
1259 1259 err = None
1260 1260 try:
1261 1261 username = ui.username()
1262 1262 except error.Abort as e:
1263 1263 err = util.forcebytestr(e)
1264 1264 problems += 1
1265 1265
1266 1266 fm.condwrite(username, 'username', _("checking username (%s)\n"), username)
1267 1267 fm.condwrite(err, 'usernameerror', _("checking username...\n %s\n"
1268 1268 " (specify a username in your configuration file)\n"), err)
1269 1269
1270 1270 fm.condwrite(not problems, '',
1271 1271 _("no problems detected\n"))
1272 1272 if not problems:
1273 1273 fm.data(problems=problems)
1274 1274 fm.condwrite(problems, 'problems',
1275 1275 _("%d problems detected,"
1276 1276 " please check your install!\n"), problems)
1277 1277 fm.end()
1278 1278
1279 1279 return problems
1280 1280
1281 1281 @command('debugknown', [], _('REPO ID...'), norepo=True)
1282 1282 def debugknown(ui, repopath, *ids, **opts):
1283 1283 """test whether node ids are known to a repo
1284 1284
1285 1285 Every ID must be a full-length hex node id string. Returns a list of 0s
1286 1286 and 1s indicating unknown/known.
1287 1287 """
1288 1288 opts = pycompat.byteskwargs(opts)
1289 1289 repo = hg.peer(ui, opts, repopath)
1290 1290 if not repo.capable('known'):
1291 1291 raise error.Abort("known() not supported by target repository")
1292 1292 flags = repo.known([bin(s) for s in ids])
1293 1293 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1294 1294
1295 1295 @command('debuglabelcomplete', [], _('LABEL...'))
1296 1296 def debuglabelcomplete(ui, repo, *args):
1297 1297 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
1298 1298 debugnamecomplete(ui, repo, *args)
1299 1299
1300 1300 @command('debuglocks',
1301 1301 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
1302 1302 ('W', 'force-wlock', None,
1303 1303 _('free the working state lock (DANGEROUS)')),
1304 1304 ('s', 'set-lock', None, _('set the store lock until stopped')),
1305 1305 ('S', 'set-wlock', None,
1306 1306 _('set the working state lock until stopped'))],
1307 1307 _('[OPTION]...'))
1308 1308 def debuglocks(ui, repo, **opts):
1309 1309 """show or modify state of locks
1310 1310
1311 1311 By default, this command will show which locks are held. This
1312 1312 includes the user and process holding the lock, the amount of time
1313 1313 the lock has been held, and the machine name where the process is
1314 1314 running if it's not local.
1315 1315
1316 1316 Locks protect the integrity of Mercurial's data, so should be
1317 1317 treated with care. System crashes or other interruptions may cause
1318 1318 locks to not be properly released, though Mercurial will usually
1319 1319 detect and remove such stale locks automatically.
1320 1320
1321 1321 However, detecting stale locks may not always be possible (for
1322 1322 instance, on a shared filesystem). Removing locks may also be
1323 1323 blocked by filesystem permissions.
1324 1324
1325 1325 Setting a lock will prevent other commands from changing the data.
1326 1326 The command will wait until an interruption (SIGINT, SIGTERM, ...) occurs.
1327 1327 The set locks are removed when the command exits.
1328 1328
1329 1329 Returns 0 if no locks are held.
1330 1330
1331 1331 """
1332 1332
1333 1333 if opts.get(r'force_lock'):
1334 1334 repo.svfs.unlink('lock')
1335 1335 if opts.get(r'force_wlock'):
1336 1336 repo.vfs.unlink('wlock')
1337 1337 if opts.get(r'force_lock') or opts.get(r'force_wlock'):
1338 1338 return 0
1339 1339
1340 1340 locks = []
1341 1341 try:
1342 1342 if opts.get(r'set_wlock'):
1343 1343 try:
1344 1344 locks.append(repo.wlock(False))
1345 1345 except error.LockHeld:
1346 1346 raise error.Abort(_('wlock is already held'))
1347 1347 if opts.get(r'set_lock'):
1348 1348 try:
1349 1349 locks.append(repo.lock(False))
1350 1350 except error.LockHeld:
1351 1351 raise error.Abort(_('lock is already held'))
1352 1352 if len(locks):
1353 1353 ui.promptchoice(_("ready to release the lock (y)? $$ &Yes"))
1354 1354 return 0
1355 1355 finally:
1356 1356 release(*locks)
1357 1357
1358 1358 now = time.time()
1359 1359 held = 0
1360 1360
1361 1361 def report(vfs, name, method):
1362 1362 # this causes stale locks to get reaped for more accurate reporting
1363 1363 try:
1364 1364 l = method(False)
1365 1365 except error.LockHeld:
1366 1366 l = None
1367 1367
1368 1368 if l:
1369 1369 l.release()
1370 1370 else:
1371 1371 try:
1372 1372 stat = vfs.lstat(name)
1373 1373 age = now - stat.st_mtime
1374 1374 user = util.username(stat.st_uid)
1375 1375 locker = vfs.readlock(name)
1376 1376 if ":" in locker:
1377 1377 host, pid = locker.split(':')
1378 1378 if host == socket.gethostname():
1379 1379 locker = 'user %s, process %s' % (user, pid)
1380 1380 else:
1381 1381 locker = 'user %s, process %s, host %s' \
1382 1382 % (user, pid, host)
1383 1383 ui.write(("%-6s %s (%ds)\n") % (name + ":", locker, age))
1384 1384 return 1
1385 1385 except OSError as e:
1386 1386 if e.errno != errno.ENOENT:
1387 1387 raise
1388 1388
1389 1389 ui.write(("%-6s free\n") % (name + ":"))
1390 1390 return 0
1391 1391
1392 1392 held += report(repo.svfs, "lock", repo.lock)
1393 1393 held += report(repo.vfs, "wlock", repo.wlock)
1394 1394
1395 1395 return held
1396 1396
1397 1397 @command('debugmergestate', [], '')
1398 1398 def debugmergestate(ui, repo, *args):
1399 1399 """print merge state
1400 1400
1401 1401 Use --verbose to print out information about whether v1 or v2 merge state
1402 1402 was chosen."""
1403 1403 def _hashornull(h):
1404 1404 if h == nullhex:
1405 1405 return 'null'
1406 1406 else:
1407 1407 return h
1408 1408
1409 1409 def printrecords(version):
1410 1410 ui.write(('* version %d records\n') % version)
1411 1411 if version == 1:
1412 1412 records = v1records
1413 1413 else:
1414 1414 records = v2records
1415 1415
1416 1416 for rtype, record in records:
1417 1417 # pretty print some record types
1418 1418 if rtype == 'L':
1419 1419 ui.write(('local: %s\n') % record)
1420 1420 elif rtype == 'O':
1421 1421 ui.write(('other: %s\n') % record)
1422 1422 elif rtype == 'm':
1423 1423 driver, mdstate = record.split('\0', 1)
1424 1424 ui.write(('merge driver: %s (state "%s")\n')
1425 1425 % (driver, mdstate))
1426 1426 elif rtype in 'FDC':
1427 1427 r = record.split('\0')
1428 1428 f, state, hash, lfile, afile, anode, ofile = r[0:7]
1429 1429 if version == 1:
1430 1430 onode = 'not stored in v1 format'
1431 1431 flags = r[7]
1432 1432 else:
1433 1433 onode, flags = r[7:9]
1434 1434 ui.write(('file: %s (record type "%s", state "%s", hash %s)\n')
1435 1435 % (f, rtype, state, _hashornull(hash)))
1436 1436 ui.write((' local path: %s (flags "%s")\n') % (lfile, flags))
1437 1437 ui.write((' ancestor path: %s (node %s)\n')
1438 1438 % (afile, _hashornull(anode)))
1439 1439 ui.write((' other path: %s (node %s)\n')
1440 1440 % (ofile, _hashornull(onode)))
1441 1441 elif rtype == 'f':
1442 1442 filename, rawextras = record.split('\0', 1)
1443 1443 extras = rawextras.split('\0')
1444 1444 i = 0
1445 1445 extrastrings = []
1446 1446 while i < len(extras):
1447 1447 extrastrings.append('%s = %s' % (extras[i], extras[i + 1]))
1448 1448 i += 2
1449 1449
1450 1450 ui.write(('file extras: %s (%s)\n')
1451 1451 % (filename, ', '.join(extrastrings)))
1452 1452 elif rtype == 'l':
1453 1453 labels = record.split('\0', 2)
1454 1454 labels = [l for l in labels if len(l) > 0]
1455 1455 ui.write(('labels:\n'))
1456 1456 ui.write((' local: %s\n' % labels[0]))
1457 1457 ui.write((' other: %s\n' % labels[1]))
1458 1458 if len(labels) > 2:
1459 1459 ui.write((' base: %s\n' % labels[2]))
1460 1460 else:
1461 1461 ui.write(('unrecognized entry: %s\t%s\n')
1462 1462 % (rtype, record.replace('\0', '\t')))
1463 1463
1464 1464 # Avoid mergestate.read() since it may raise an exception for unsupported
1465 1465 # merge state records. We shouldn't be doing this, but this is OK since this
1466 1466 # command is pretty low-level.
1467 1467 ms = mergemod.mergestate(repo)
1468 1468
1469 1469 # sort so that reasonable information is on top
1470 1470 v1records = ms._readrecordsv1()
1471 1471 v2records = ms._readrecordsv2()
1472 1472 order = 'LOml'
1473 1473 def key(r):
1474 1474 idx = order.find(r[0])
1475 1475 if idx == -1:
1476 1476 return (1, r[1])
1477 1477 else:
1478 1478 return (0, idx)
1479 1479 v1records.sort(key=key)
1480 1480 v2records.sort(key=key)
1481 1481
1482 1482 if not v1records and not v2records:
1483 1483 ui.write(('no merge state found\n'))
1484 1484 elif not v2records:
1485 1485 ui.note(('no version 2 merge state\n'))
1486 1486 printrecords(1)
1487 1487 elif ms._v1v2match(v1records, v2records):
1488 1488 ui.note(('v1 and v2 states match: using v2\n'))
1489 1489 printrecords(2)
1490 1490 else:
1491 1491 ui.note(('v1 and v2 states mismatch: using v1\n'))
1492 1492 printrecords(1)
1493 1493 if ui.verbose:
1494 1494 printrecords(2)
1495 1495
1496 1496 @command('debugnamecomplete', [], _('NAME...'))
1497 1497 def debugnamecomplete(ui, repo, *args):
1498 1498 '''complete "names" - tags, open branch names, bookmark names'''
1499 1499
1500 1500 names = set()
1501 1501 # since we previously only listed open branches, we will handle that
1502 1502 # specially (after this for loop)
1503 1503 for name, ns in repo.names.iteritems():
1504 1504 if name != 'branches':
1505 1505 names.update(ns.listnames(repo))
1506 1506 names.update(tag for (tag, heads, tip, closed)
1507 1507 in repo.branchmap().iterbranches() if not closed)
1508 1508 completions = set()
1509 1509 if not args:
1510 1510 args = ['']
1511 1511 for a in args:
1512 1512 completions.update(n for n in names if n.startswith(a))
1513 1513 ui.write('\n'.join(sorted(completions)))
1514 1514 ui.write('\n')
1515 1515
1516 1516 @command('debugobsolete',
1517 1517 [('', 'flags', 0, _('markers flag')),
1518 1518 ('', 'record-parents', False,
1519 1519 _('record parent information for the precursor')),
1520 1520 ('r', 'rev', [], _('display markers relevant to REV')),
1521 1521 ('', 'exclusive', False, _('restrict display to markers only '
1522 1522 'relevant to REV')),
1523 1523 ('', 'index', False, _('display index of the marker')),
1524 1524 ('', 'delete', [], _('delete markers specified by indices')),
1525 1525 ] + cmdutil.commitopts2 + cmdutil.formatteropts,
1526 1526 _('[OBSOLETED [REPLACEMENT ...]]'))
1527 1527 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
1528 1528 """create arbitrary obsolete marker
1529 1529
1530 1530 With no arguments, displays the list of obsolescence markers."""
1531 1531
1532 1532 opts = pycompat.byteskwargs(opts)
1533 1533
1534 1534 def parsenodeid(s):
1535 1535 try:
1536 1536 # We do not use revsingle/revrange functions here to accept
1537 1537 # arbitrary node identifiers, possibly not present in the
1538 1538 # local repository.
1539 1539 n = bin(s)
1540 1540 if len(n) != len(nullid):
1541 1541 raise TypeError()
1542 1542 return n
1543 1543 except TypeError:
1544 1544 raise error.Abort('changeset references must be full hexadecimal '
1545 1545 'node identifiers')
1546 1546
1547 1547 if opts.get('delete'):
1548 1548 indices = []
1549 1549 for v in opts.get('delete'):
1550 1550 try:
1551 1551 indices.append(int(v))
1552 1552 except ValueError:
1553 1553 raise error.Abort(_('invalid index value: %r') % v,
1554 1554 hint=_('use integers for indices'))
1555 1555
1556 1556 if repo.currenttransaction():
1557 1557 raise error.Abort(_('cannot delete obsmarkers in the middle '
1558 1558 'of transaction.'))
1559 1559
1560 1560 with repo.lock():
1561 1561 n = repair.deleteobsmarkers(repo.obsstore, indices)
1562 1562 ui.write(_('deleted %i obsolescence markers\n') % n)
1563 1563
1564 1564 return
1565 1565
1566 1566 if precursor is not None:
1567 1567 if opts['rev']:
1568 1568 raise error.Abort('cannot select revision when creating marker')
1569 1569 metadata = {}
1570 1570 metadata['user'] = opts['user'] or ui.username()
1571 1571 succs = tuple(parsenodeid(succ) for succ in successors)
1572 1572 l = repo.lock()
1573 1573 try:
1574 1574 tr = repo.transaction('debugobsolete')
1575 1575 try:
1576 1576 date = opts.get('date')
1577 1577 if date:
1578 1578 date = util.parsedate(date)
1579 1579 else:
1580 1580 date = None
1581 1581 prec = parsenodeid(precursor)
1582 1582 parents = None
1583 1583 if opts['record_parents']:
1584 1584 if prec not in repo.unfiltered():
1585 1585 raise error.Abort('cannot used --record-parents on '
1586 1586 'unknown changesets')
1587 1587 parents = repo.unfiltered()[prec].parents()
1588 1588 parents = tuple(p.node() for p in parents)
1589 1589 repo.obsstore.create(tr, prec, succs, opts['flags'],
1590 1590 parents=parents, date=date,
1591 1591 metadata=metadata, ui=ui)
1592 1592 tr.close()
1593 1593 except ValueError as exc:
1594 1594 raise error.Abort(_('bad obsmarker input: %s') % exc)
1595 1595 finally:
1596 1596 tr.release()
1597 1597 finally:
1598 1598 l.release()
1599 1599 else:
1600 1600 if opts['rev']:
1601 1601 revs = scmutil.revrange(repo, opts['rev'])
1602 1602 nodes = [repo[r].node() for r in revs]
1603 1603 markers = list(obsutil.getmarkers(repo, nodes=nodes,
1604 1604 exclusive=opts['exclusive']))
1605 1605 markers.sort(key=lambda x: x._data)
1606 1606 else:
1607 1607 markers = obsutil.getmarkers(repo)
1608 1608
1609 1609 markerstoiter = markers
1610 1610 isrelevant = lambda m: True
1611 1611 if opts.get('rev') and opts.get('index'):
1612 1612 markerstoiter = obsutil.getmarkers(repo)
1613 1613 markerset = set(markers)
1614 1614 isrelevant = lambda m: m in markerset
1615 1615
1616 1616 fm = ui.formatter('debugobsolete', opts)
1617 1617 for i, m in enumerate(markerstoiter):
1618 1618 if not isrelevant(m):
1619 1619 # marker can be irrelevant when we're iterating over a set
1620 1620 # of markers (markerstoiter) which is bigger than the set
1621 1621 # of markers we want to display (markers)
1622 1622 # this can happen if both --index and --rev options are
1623 1623 # provided and thus we need to iterate over all of the markers
1624 1624 # to get the correct indices, but only display the ones that
1625 1625 # are relevant to --rev value
1626 1626 continue
1627 1627 fm.startitem()
1628 1628 ind = i if opts.get('index') else None
1629 1629 cmdutil.showmarker(fm, m, index=ind)
1630 1630 fm.end()
1631 1631
1632 1632 @command('debugpathcomplete',
1633 1633 [('f', 'full', None, _('complete an entire path')),
1634 1634 ('n', 'normal', None, _('show only normal files')),
1635 1635 ('a', 'added', None, _('show only added files')),
1636 1636 ('r', 'removed', None, _('show only removed files'))],
1637 1637 _('FILESPEC...'))
1638 1638 def debugpathcomplete(ui, repo, *specs, **opts):
1639 1639 '''complete part or all of a tracked path
1640 1640
1641 1641 This command supports shells that offer path name completion. It
1642 1642 currently completes only files already known to the dirstate.
1643 1643
1644 1644 Completion extends only to the next path segment unless
1645 1645 --full is specified, in which case entire paths are used.'''
1646 1646
1647 1647 def complete(path, acceptable):
1648 1648 dirstate = repo.dirstate
1649 1649 spec = os.path.normpath(os.path.join(pycompat.getcwd(), path))
1650 1650 rootdir = repo.root + pycompat.ossep
1651 1651 if spec != repo.root and not spec.startswith(rootdir):
1652 1652 return [], []
1653 1653 if os.path.isdir(spec):
1654 1654 spec += '/'
1655 1655 spec = spec[len(rootdir):]
1656 1656 fixpaths = pycompat.ossep != '/'
1657 1657 if fixpaths:
1658 1658 spec = spec.replace(pycompat.ossep, '/')
1659 1659 speclen = len(spec)
1660 1660 fullpaths = opts[r'full']
1661 1661 files, dirs = set(), set()
1662 1662 adddir, addfile = dirs.add, files.add
1663 1663 for f, st in dirstate.iteritems():
1664 1664 if f.startswith(spec) and st[0] in acceptable:
1665 1665 if fixpaths:
1666 1666 f = f.replace('/', pycompat.ossep)
1667 1667 if fullpaths:
1668 1668 addfile(f)
1669 1669 continue
1670 1670 s = f.find(pycompat.ossep, speclen)
1671 1671 if s >= 0:
1672 1672 adddir(f[:s])
1673 1673 else:
1674 1674 addfile(f)
1675 1675 return files, dirs
1676 1676
1677 1677 acceptable = ''
1678 1678 if opts[r'normal']:
1679 1679 acceptable += 'nm'
1680 1680 if opts[r'added']:
1681 1681 acceptable += 'a'
1682 1682 if opts[r'removed']:
1683 1683 acceptable += 'r'
1684 1684 cwd = repo.getcwd()
1685 1685 if not specs:
1686 1686 specs = ['.']
1687 1687
1688 1688 files, dirs = set(), set()
1689 1689 for spec in specs:
1690 1690 f, d = complete(spec, acceptable or 'nmar')
1691 1691 files.update(f)
1692 1692 dirs.update(d)
1693 1693 files.update(dirs)
1694 1694 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
1695 1695 ui.write('\n')
1696 1696
1697 1697 @command('debugpeer', [], _('PATH'), norepo=True)
1698 1698 def debugpeer(ui, path):
1699 1699 """establish a connection to a peer repository"""
1700 1700 # Always enable peer request logging. Requires --debug to display
1701 1701 # though.
1702 1702 overrides = {
1703 1703 ('devel', 'debug.peer-request'): True,
1704 1704 }
1705 1705
1706 1706 with ui.configoverride(overrides):
1707 1707 peer = hg.peer(ui, {}, path)
1708 1708
1709 1709 local = peer.local() is not None
1710 1710 canpush = peer.canpush()
1711 1711
1712 1712 ui.write(_('url: %s\n') % peer.url())
1713 1713 ui.write(_('local: %s\n') % (_('yes') if local else _('no')))
1714 1714 ui.write(_('pushable: %s\n') % (_('yes') if canpush else _('no')))
1715 1715
1716 1716 @command('debugpickmergetool',
1717 1717 [('r', 'rev', '', _('check for files in this revision'), _('REV')),
1718 1718 ('', 'changedelete', None, _('emulate merging change and delete')),
1719 1719 ] + cmdutil.walkopts + cmdutil.mergetoolopts,
1720 1720 _('[PATTERN]...'),
1721 1721 inferrepo=True)
1722 1722 def debugpickmergetool(ui, repo, *pats, **opts):
1723 1723 """examine which merge tool is chosen for specified file
1724 1724
1725 1725 As described in :hg:`help merge-tools`, Mercurial examines
1726 1726 configurations below in this order to decide which merge tool is
1727 1727 chosen for specified file.
1728 1728
1729 1729 1. ``--tool`` option
1730 1730 2. ``HGMERGE`` environment variable
1731 1731 3. configurations in ``merge-patterns`` section
1732 1732 4. configuration of ``ui.merge``
1733 1733 5. configurations in ``merge-tools`` section
1734 1734 6. ``hgmerge`` tool (for historical reason only)
1735 1735 7. default tool for fallback (``:merge`` or ``:prompt``)
1736 1736
1737 1737 This command writes out examination result in the style below::
1738 1738
1739 1739 FILE = MERGETOOL
1740 1740
1741 1741 By default, all files known in the first parent context of the
1742 1742 working directory are examined. Use file patterns and/or -I/-X
1743 1743 options to limit target files. -r/--rev is also useful to examine
1744 1744 files in another context without actual updating to it.
1745 1745
1746 1746 With --debug, this command shows warning messages while matching
1747 1747 against ``merge-patterns`` and so on, too. It is recommended to
1748 1748 use this option with explicit file patterns and/or -I/-X options,
1749 1749 because this option increases amount of output per file according
1750 1750 to configurations in hgrc.
1751 1751
1752 1752 With -v/--verbose, this command shows configurations below at
1753 1753 first (only if specified).
1754 1754
1755 1755 - ``--tool`` option
1756 1756 - ``HGMERGE`` environment variable
1757 1757 - configuration of ``ui.merge``
1758 1758
1759 1759 If merge tool is chosen before matching against
1760 1760 ``merge-patterns``, this command can't show any helpful
1761 1761 information, even with --debug. In such case, information above is
1762 1762 useful to know why a merge tool is chosen.
1763 1763 """
1764 1764 opts = pycompat.byteskwargs(opts)
1765 1765 overrides = {}
1766 1766 if opts['tool']:
1767 1767 overrides[('ui', 'forcemerge')] = opts['tool']
1768 1768 ui.note(('with --tool %r\n') % (opts['tool']))
1769 1769
1770 1770 with ui.configoverride(overrides, 'debugmergepatterns'):
1771 1771 hgmerge = encoding.environ.get("HGMERGE")
1772 1772 if hgmerge is not None:
1773 1773 ui.note(('with HGMERGE=%r\n') % (hgmerge))
1774 1774 uimerge = ui.config("ui", "merge")
1775 1775 if uimerge:
1776 1776 ui.note(('with ui.merge=%r\n') % (uimerge))
1777 1777
1778 1778 ctx = scmutil.revsingle(repo, opts.get('rev'))
1779 1779 m = scmutil.match(ctx, pats, opts)
1780 1780 changedelete = opts['changedelete']
1781 1781 for path in ctx.walk(m):
1782 1782 fctx = ctx[path]
1783 1783 try:
1784 1784 if not ui.debugflag:
1785 1785 ui.pushbuffer(error=True)
1786 1786 tool, toolpath = filemerge._picktool(repo, ui, path,
1787 1787 fctx.isbinary(),
1788 1788 'l' in fctx.flags(),
1789 1789 changedelete)
1790 1790 finally:
1791 1791 if not ui.debugflag:
1792 1792 ui.popbuffer()
1793 1793 ui.write(('%s = %s\n') % (path, tool))
1794 1794
1795 1795 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
1796 1796 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
1797 1797 '''access the pushkey key/value protocol
1798 1798
1799 1799 With two args, list the keys in the given namespace.
1800 1800
1801 1801 With five args, set a key to new if it currently is set to old.
1802 1802 Reports success or failure.
1803 1803 '''
1804 1804
1805 1805 target = hg.peer(ui, {}, repopath)
1806 1806 if keyinfo:
1807 1807 key, old, new = keyinfo
1808 1808 r = target.pushkey(namespace, key, old, new)
1809 1809 ui.status(str(r) + '\n')
1810 1810 return not r
1811 1811 else:
1812 1812 for k, v in sorted(target.listkeys(namespace).iteritems()):
1813 1813 ui.write("%s\t%s\n" % (util.escapestr(k),
1814 1814 util.escapestr(v)))
1815 1815
1816 1816 @command('debugpvec', [], _('A B'))
1817 1817 def debugpvec(ui, repo, a, b=None):
1818 1818 ca = scmutil.revsingle(repo, a)
1819 1819 cb = scmutil.revsingle(repo, b)
1820 1820 pa = pvec.ctxpvec(ca)
1821 1821 pb = pvec.ctxpvec(cb)
1822 1822 if pa == pb:
1823 1823 rel = "="
1824 1824 elif pa > pb:
1825 1825 rel = ">"
1826 1826 elif pa < pb:
1827 1827 rel = "<"
1828 1828 elif pa | pb:
1829 1829 rel = "|"
1830 1830 ui.write(_("a: %s\n") % pa)
1831 1831 ui.write(_("b: %s\n") % pb)
1832 1832 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
1833 1833 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
1834 1834 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
1835 1835 pa.distance(pb), rel))
1836 1836
1837 1837 @command('debugrebuilddirstate|debugrebuildstate',
1838 1838 [('r', 'rev', '', _('revision to rebuild to'), _('REV')),
1839 1839 ('', 'minimal', None, _('only rebuild files that are inconsistent with '
1840 1840 'the working copy parent')),
1841 1841 ],
1842 1842 _('[-r REV]'))
1843 1843 def debugrebuilddirstate(ui, repo, rev, **opts):
1844 1844 """rebuild the dirstate as it would look like for the given revision
1845 1845
1846 1846 If no revision is specified the first current parent will be used.
1847 1847
1848 1848 The dirstate will be set to the files of the given revision.
1849 1849 The actual working directory content or existing dirstate
1850 1850 information such as adds or removes is not considered.
1851 1851
1852 1852 ``minimal`` will only rebuild the dirstate status for files that claim to be
1853 1853 tracked but are not in the parent manifest, or that exist in the parent
1854 1854 manifest but are not in the dirstate. It will not change adds, removes, or
1855 1855 modified files that are in the working copy parent.
1856 1856
1857 1857 One use of this command is to make the next :hg:`status` invocation
1858 1858 check the actual file content.
1859 1859 """
1860 1860 ctx = scmutil.revsingle(repo, rev)
1861 1861 with repo.wlock():
1862 1862 dirstate = repo.dirstate
1863 1863 changedfiles = None
1864 1864 # See command doc for what minimal does.
1865 1865 if opts.get(r'minimal'):
1866 1866 manifestfiles = set(ctx.manifest().keys())
1867 1867 dirstatefiles = set(dirstate)
1868 1868 manifestonly = manifestfiles - dirstatefiles
1869 1869 dsonly = dirstatefiles - manifestfiles
1870 1870 dsnotadded = set(f for f in dsonly if dirstate[f] != 'a')
1871 1871 changedfiles = manifestonly | dsnotadded
1872 1872
1873 1873 dirstate.rebuild(ctx.node(), ctx.manifest(), changedfiles)
1874 1874
1875 1875 @command('debugrebuildfncache', [], '')
1876 1876 def debugrebuildfncache(ui, repo):
1877 1877 """rebuild the fncache file"""
1878 1878 repair.rebuildfncache(ui, repo)
1879 1879
1880 1880 @command('debugrename',
1881 1881 [('r', 'rev', '', _('revision to debug'), _('REV'))],
1882 1882 _('[-r REV] FILE'))
1883 1883 def debugrename(ui, repo, file1, *pats, **opts):
1884 1884 """dump rename information"""
1885 1885
1886 1886 opts = pycompat.byteskwargs(opts)
1887 1887 ctx = scmutil.revsingle(repo, opts.get('rev'))
1888 1888 m = scmutil.match(ctx, (file1,) + pats, opts)
1889 1889 for abs in ctx.walk(m):
1890 1890 fctx = ctx[abs]
1891 1891 o = fctx.filelog().renamed(fctx.filenode())
1892 1892 rel = m.rel(abs)
1893 1893 if o:
1894 1894 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1895 1895 else:
1896 1896 ui.write(_("%s not renamed\n") % rel)
1897 1897
1898 1898 @command('debugrevlog', cmdutil.debugrevlogopts +
1899 1899 [('d', 'dump', False, _('dump index data'))],
1900 1900 _('-c|-m|FILE'),
1901 1901 optionalrepo=True)
1902 1902 def debugrevlog(ui, repo, file_=None, **opts):
1903 1903 """show data and statistics about a revlog"""
1904 1904 opts = pycompat.byteskwargs(opts)
1905 1905 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
1906 1906
1907 1907 if opts.get("dump"):
1908 1908 numrevs = len(r)
1909 1909 ui.write(("# rev p1rev p2rev start end deltastart base p1 p2"
1910 1910 " rawsize totalsize compression heads chainlen\n"))
1911 1911 ts = 0
1912 1912 heads = set()
1913 1913
1914 1914 for rev in xrange(numrevs):
1915 1915 dbase = r.deltaparent(rev)
1916 1916 if dbase == -1:
1917 1917 dbase = rev
1918 1918 cbase = r.chainbase(rev)
1919 1919 clen = r.chainlen(rev)
1920 1920 p1, p2 = r.parentrevs(rev)
1921 1921 rs = r.rawsize(rev)
1922 1922 ts = ts + rs
1923 1923 heads -= set(r.parentrevs(rev))
1924 1924 heads.add(rev)
1925 1925 try:
1926 1926 compression = ts / r.end(rev)
1927 1927 except ZeroDivisionError:
1928 1928 compression = 0
1929 1929 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
1930 1930 "%11d %5d %8d\n" %
1931 1931 (rev, p1, p2, r.start(rev), r.end(rev),
1932 1932 r.start(dbase), r.start(cbase),
1933 1933 r.start(p1), r.start(p2),
1934 1934 rs, ts, compression, len(heads), clen))
1935 1935 return 0
1936 1936
1937 1937 v = r.version
1938 1938 format = v & 0xFFFF
1939 1939 flags = []
1940 1940 gdelta = False
1941 1941 if v & revlog.FLAG_INLINE_DATA:
1942 1942 flags.append('inline')
1943 1943 if v & revlog.FLAG_GENERALDELTA:
1944 1944 gdelta = True
1945 1945 flags.append('generaldelta')
1946 1946 if not flags:
1947 1947 flags = ['(none)']
1948 1948
1949 1949 nummerges = 0
1950 1950 numfull = 0
1951 1951 numprev = 0
1952 1952 nump1 = 0
1953 1953 nump2 = 0
1954 1954 numother = 0
1955 1955 nump1prev = 0
1956 1956 nump2prev = 0
1957 1957 chainlengths = []
1958 1958 chainbases = []
1959 1959 chainspans = []
1960 1960
1961 1961 datasize = [None, 0, 0]
1962 1962 fullsize = [None, 0, 0]
1963 1963 deltasize = [None, 0, 0]
1964 1964 chunktypecounts = {}
1965 1965 chunktypesizes = {}
1966 1966
1967 1967 def addsize(size, l):
1968 1968 if l[0] is None or size < l[0]:
1969 1969 l[0] = size
1970 1970 if size > l[1]:
1971 1971 l[1] = size
1972 1972 l[2] += size
1973 1973
1974 1974 numrevs = len(r)
1975 1975 for rev in xrange(numrevs):
1976 1976 p1, p2 = r.parentrevs(rev)
1977 1977 delta = r.deltaparent(rev)
1978 1978 if format > 0:
1979 1979 addsize(r.rawsize(rev), datasize)
1980 1980 if p2 != nullrev:
1981 1981 nummerges += 1
1982 1982 size = r.length(rev)
1983 1983 if delta == nullrev:
1984 1984 chainlengths.append(0)
1985 1985 chainbases.append(r.start(rev))
1986 1986 chainspans.append(size)
1987 1987 numfull += 1
1988 1988 addsize(size, fullsize)
1989 1989 else:
1990 1990 chainlengths.append(chainlengths[delta] + 1)
1991 1991 baseaddr = chainbases[delta]
1992 1992 revaddr = r.start(rev)
1993 1993 chainbases.append(baseaddr)
1994 1994 chainspans.append((revaddr - baseaddr) + size)
1995 1995 addsize(size, deltasize)
1996 1996 if delta == rev - 1:
1997 1997 numprev += 1
1998 1998 if delta == p1:
1999 1999 nump1prev += 1
2000 2000 elif delta == p2:
2001 2001 nump2prev += 1
2002 2002 elif delta == p1:
2003 2003 nump1 += 1
2004 2004 elif delta == p2:
2005 2005 nump2 += 1
2006 2006 elif delta != nullrev:
2007 2007 numother += 1
2008 2008
2009 2009 # Obtain data on the raw chunks in the revlog.
2010 2010 segment = r._getsegmentforrevs(rev, rev)[1]
2011 2011 if segment:
2012 2012 chunktype = bytes(segment[0:1])
2013 2013 else:
2014 2014 chunktype = 'empty'
2015 2015
2016 2016 if chunktype not in chunktypecounts:
2017 2017 chunktypecounts[chunktype] = 0
2018 2018 chunktypesizes[chunktype] = 0
2019 2019
2020 2020 chunktypecounts[chunktype] += 1
2021 2021 chunktypesizes[chunktype] += size
2022 2022
2023 2023 # Adjust size min value for empty cases
2024 2024 for size in (datasize, fullsize, deltasize):
2025 2025 if size[0] is None:
2026 2026 size[0] = 0
2027 2027
2028 2028 numdeltas = numrevs - numfull
2029 2029 numoprev = numprev - nump1prev - nump2prev
2030 2030 totalrawsize = datasize[2]
2031 2031 datasize[2] /= numrevs
2032 2032 fulltotal = fullsize[2]
2033 2033 fullsize[2] /= numfull
2034 2034 deltatotal = deltasize[2]
2035 2035 if numrevs - numfull > 0:
2036 2036 deltasize[2] /= numrevs - numfull
2037 2037 totalsize = fulltotal + deltatotal
2038 2038 avgchainlen = sum(chainlengths) / numrevs
2039 2039 maxchainlen = max(chainlengths)
2040 2040 maxchainspan = max(chainspans)
2041 2041 compratio = 1
2042 2042 if totalsize:
2043 2043 compratio = totalrawsize / totalsize
2044 2044
2045 2045 basedfmtstr = '%%%dd\n'
2046 2046 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2047 2047
2048 2048 def dfmtstr(max):
2049 2049 return basedfmtstr % len(str(max))
2050 2050 def pcfmtstr(max, padding=0):
2051 2051 return basepcfmtstr % (len(str(max)), ' ' * padding)
2052 2052
2053 2053 def pcfmt(value, total):
2054 2054 if total:
2055 2055 return (value, 100 * float(value) / total)
2056 2056 else:
2057 2057 return value, 100.0
2058 2058
2059 2059 ui.write(('format : %d\n') % format)
2060 2060 ui.write(('flags : %s\n') % ', '.join(flags))
2061 2061
2062 2062 ui.write('\n')
2063 2063 fmt = pcfmtstr(totalsize)
2064 2064 fmt2 = dfmtstr(totalsize)
2065 2065 ui.write(('revisions : ') + fmt2 % numrevs)
2066 2066 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2067 2067 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2068 2068 ui.write(('revisions : ') + fmt2 % numrevs)
2069 2069 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2070 2070 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2071 2071 ui.write(('revision size : ') + fmt2 % totalsize)
2072 2072 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2073 2073 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2074 2074
2075 2075 def fmtchunktype(chunktype):
2076 2076 if chunktype == 'empty':
2077 2077 return ' %s : ' % chunktype
2078 2078 elif chunktype in pycompat.bytestr(string.ascii_letters):
2079 2079 return ' 0x%s (%s) : ' % (hex(chunktype), chunktype)
2080 2080 else:
2081 2081 return ' 0x%s : ' % hex(chunktype)
2082 2082
2083 2083 ui.write('\n')
2084 2084 ui.write(('chunks : ') + fmt2 % numrevs)
2085 2085 for chunktype in sorted(chunktypecounts):
2086 2086 ui.write(fmtchunktype(chunktype))
2087 2087 ui.write(fmt % pcfmt(chunktypecounts[chunktype], numrevs))
2088 2088 ui.write(('chunks size : ') + fmt2 % totalsize)
2089 2089 for chunktype in sorted(chunktypecounts):
2090 2090 ui.write(fmtchunktype(chunktype))
2091 2091 ui.write(fmt % pcfmt(chunktypesizes[chunktype], totalsize))
2092 2092
2093 2093 ui.write('\n')
2094 2094 fmt = dfmtstr(max(avgchainlen, maxchainlen, maxchainspan, compratio))
2095 2095 ui.write(('avg chain length : ') + fmt % avgchainlen)
2096 2096 ui.write(('max chain length : ') + fmt % maxchainlen)
2097 2097 ui.write(('max chain reach : ') + fmt % maxchainspan)
2098 2098 ui.write(('compression ratio : ') + fmt % compratio)
2099 2099
2100 2100 if format > 0:
2101 2101 ui.write('\n')
2102 2102 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2103 2103 % tuple(datasize))
2104 2104 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2105 2105 % tuple(fullsize))
2106 2106 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2107 2107 % tuple(deltasize))
2108 2108
2109 2109 if numdeltas > 0:
2110 2110 ui.write('\n')
2111 2111 fmt = pcfmtstr(numdeltas)
2112 2112 fmt2 = pcfmtstr(numdeltas, 4)
2113 2113 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2114 2114 if numprev > 0:
2115 2115 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2116 2116 numprev))
2117 2117 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2118 2118 numprev))
2119 2119 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2120 2120 numprev))
2121 2121 if gdelta:
2122 2122 ui.write(('deltas against p1 : ')
2123 2123 + fmt % pcfmt(nump1, numdeltas))
2124 2124 ui.write(('deltas against p2 : ')
2125 2125 + fmt % pcfmt(nump2, numdeltas))
2126 2126 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2127 2127 numdeltas))
2128 2128
2129 2129 @command('debugrevspec',
2130 2130 [('', 'optimize', None,
2131 2131 _('print parsed tree after optimizing (DEPRECATED)')),
2132 2132 ('', 'show-revs', True, _('print list of result revisions (default)')),
2133 2133 ('s', 'show-set', None, _('print internal representation of result set')),
2134 2134 ('p', 'show-stage', [],
2135 2135 _('print parsed tree at the given stage'), _('NAME')),
2136 2136 ('', 'no-optimized', False, _('evaluate tree without optimization')),
2137 2137 ('', 'verify-optimized', False, _('verify optimized result')),
2138 2138 ],
2139 2139 ('REVSPEC'))
2140 2140 def debugrevspec(ui, repo, expr, **opts):
2141 2141 """parse and apply a revision specification
2142 2142
2143 2143 Use -p/--show-stage option to print the parsed tree at the given stages.
2144 2144 Use -p all to print tree at every stage.
2145 2145
2146 2146 Use --no-show-revs option with -s or -p to print only the set
2147 2147 representation or the parsed tree respectively.
2148 2148
2149 2149 Use --verify-optimized to compare the optimized result with the unoptimized
2150 2150 one. Returns 1 if the optimized result differs.
2151 2151 """
2152 2152 opts = pycompat.byteskwargs(opts)
2153 2153 aliases = ui.configitems('revsetalias')
2154 2154 stages = [
2155 2155 ('parsed', lambda tree: tree),
2156 2156 ('expanded', lambda tree: revsetlang.expandaliases(tree, aliases,
2157 2157 ui.warn)),
2158 2158 ('concatenated', revsetlang.foldconcat),
2159 2159 ('analyzed', revsetlang.analyze),
2160 2160 ('optimized', revsetlang.optimize),
2161 2161 ]
2162 2162 if opts['no_optimized']:
2163 2163 stages = stages[:-1]
2164 2164 if opts['verify_optimized'] and opts['no_optimized']:
2165 2165 raise error.Abort(_('cannot use --verify-optimized with '
2166 2166 '--no-optimized'))
2167 2167 stagenames = set(n for n, f in stages)
2168 2168
2169 2169 showalways = set()
2170 2170 showchanged = set()
2171 2171 if ui.verbose and not opts['show_stage']:
2172 2172 # show parsed tree by --verbose (deprecated)
2173 2173 showalways.add('parsed')
2174 2174 showchanged.update(['expanded', 'concatenated'])
2175 2175 if opts['optimize']:
2176 2176 showalways.add('optimized')
2177 2177 if opts['show_stage'] and opts['optimize']:
2178 2178 raise error.Abort(_('cannot use --optimize with --show-stage'))
2179 2179 if opts['show_stage'] == ['all']:
2180 2180 showalways.update(stagenames)
2181 2181 else:
2182 2182 for n in opts['show_stage']:
2183 2183 if n not in stagenames:
2184 2184 raise error.Abort(_('invalid stage name: %s') % n)
2185 2185 showalways.update(opts['show_stage'])
2186 2186
2187 2187 treebystage = {}
2188 2188 printedtree = None
2189 2189 tree = revsetlang.parse(expr, lookup=repo.__contains__)
2190 2190 for n, f in stages:
2191 2191 treebystage[n] = tree = f(tree)
2192 2192 if n in showalways or (n in showchanged and tree != printedtree):
2193 2193 if opts['show_stage'] or n != 'parsed':
2194 2194 ui.write(("* %s:\n") % n)
2195 2195 ui.write(revsetlang.prettyformat(tree), "\n")
2196 2196 printedtree = tree
2197 2197
2198 2198 if opts['verify_optimized']:
2199 2199 arevs = revset.makematcher(treebystage['analyzed'])(repo)
2200 2200 brevs = revset.makematcher(treebystage['optimized'])(repo)
2201 2201 if opts['show_set'] or (opts['show_set'] is None and ui.verbose):
2202 2202 ui.write(("* analyzed set:\n"), smartset.prettyformat(arevs), "\n")
2203 2203 ui.write(("* optimized set:\n"), smartset.prettyformat(brevs), "\n")
2204 2204 arevs = list(arevs)
2205 2205 brevs = list(brevs)
2206 2206 if arevs == brevs:
2207 2207 return 0
2208 2208 ui.write(('--- analyzed\n'), label='diff.file_a')
2209 2209 ui.write(('+++ optimized\n'), label='diff.file_b')
2210 2210 sm = difflib.SequenceMatcher(None, arevs, brevs)
2211 2211 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2212 2212 if tag in ('delete', 'replace'):
2213 2213 for c in arevs[alo:ahi]:
2214 2214 ui.write('-%s\n' % c, label='diff.deleted')
2215 2215 if tag in ('insert', 'replace'):
2216 2216 for c in brevs[blo:bhi]:
2217 2217 ui.write('+%s\n' % c, label='diff.inserted')
2218 2218 if tag == 'equal':
2219 2219 for c in arevs[alo:ahi]:
2220 2220 ui.write(' %s\n' % c)
2221 2221 return 1
2222 2222
2223 2223 func = revset.makematcher(tree)
2224 2224 revs = func(repo)
2225 2225 if opts['show_set'] or (opts['show_set'] is None and ui.verbose):
2226 2226 ui.write(("* set:\n"), smartset.prettyformat(revs), "\n")
2227 2227 if not opts['show_revs']:
2228 2228 return
2229 2229 for c in revs:
2230 2230 ui.write("%d\n" % c)
2231 2231
2232 2232 @command('debugsetparents', [], _('REV1 [REV2]'))
2233 2233 def debugsetparents(ui, repo, rev1, rev2=None):
2234 2234 """manually set the parents of the current working directory
2235 2235
2236 2236 This is useful for writing repository conversion tools, but should
2237 2237 be used with care. For example, neither the working directory nor the
2238 2238 dirstate is updated, so file status may be incorrect after running this
2239 2239 command.
2240 2240
2241 2241 Returns 0 on success.
2242 2242 """
2243 2243
2244 2244 r1 = scmutil.revsingle(repo, rev1).node()
2245 2245 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2246 2246
2247 2247 with repo.wlock():
2248 2248 repo.setparents(r1, r2)
2249 2249
2250 2250 @command('debugssl', [], '[SOURCE]', optionalrepo=True)
2251 2251 def debugssl(ui, repo, source=None, **opts):
2252 2252 '''test a secure connection to a server
2253 2253
2254 2254 This builds the certificate chain for the server on Windows, installing the
2255 2255 missing intermediates and trusted root via Windows Update if necessary. It
2256 2256 does nothing on other platforms.
2257 2257
2258 2258 If SOURCE is omitted, the 'default' path will be used. If a URL is given,
2259 2259 that server is used. See :hg:`help urls` for more information.
2260 2260
2261 2261 If the update succeeds, retry the original operation. Otherwise, the cause
2262 2262 of the SSL error is likely another issue.
2263 2263 '''
2264 2264 if not pycompat.iswindows:
2265 2265 raise error.Abort(_('certificate chain building is only possible on '
2266 2266 'Windows'))
2267 2267
2268 2268 if not source:
2269 2269 if not repo:
2270 2270 raise error.Abort(_("there is no Mercurial repository here, and no "
2271 2271 "server specified"))
2272 2272 source = "default"
2273 2273
2274 2274 source, branches = hg.parseurl(ui.expandpath(source))
2275 2275 url = util.url(source)
2276 2276 addr = None
2277 2277
2278 2278 defaultport = {'https': 443, 'ssh': 22}
2279 2279 if url.scheme in defaultport:
2280 2280 try:
2281 2281 addr = (url.host, int(url.port or defaultport[url.scheme]))
2282 2282 except ValueError:
2283 2283 raise error.Abort(_("malformed port number in URL"))
2284 2284 else:
2285 2285 raise error.Abort(_("only https and ssh connections are supported"))
2286 2286
2287 2287 from . import win32
2288 2288
2289 2289 s = ssl.wrap_socket(socket.socket(), ssl_version=ssl.PROTOCOL_TLS,
2290 2290 cert_reqs=ssl.CERT_NONE, ca_certs=None)
2291 2291
2292 2292 try:
2293 2293 s.connect(addr)
2294 2294 cert = s.getpeercert(True)
2295 2295
2296 2296 ui.status(_('checking the certificate chain for %s\n') % url.host)
2297 2297
2298 2298 complete = win32.checkcertificatechain(cert, build=False)
2299 2299
2300 2300 if not complete:
2301 2301 ui.status(_('certificate chain is incomplete, updating... '))
2302 2302
2303 2303 if not win32.checkcertificatechain(cert):
2304 2304 ui.status(_('failed.\n'))
2305 2305 else:
2306 2306 ui.status(_('done.\n'))
2307 2307 else:
2308 2308 ui.status(_('full certificate chain is available\n'))
2309 2309 finally:
2310 2310 s.close()
2311 2311
2312 2312 @command('debugsub',
2313 2313 [('r', 'rev', '',
2314 2314 _('revision to check'), _('REV'))],
2315 2315 _('[-r REV] [REV]'))
2316 2316 def debugsub(ui, repo, rev=None):
2317 2317 ctx = scmutil.revsingle(repo, rev, None)
2318 2318 for k, v in sorted(ctx.substate.items()):
2319 2319 ui.write(('path %s\n') % k)
2320 2320 ui.write((' source %s\n') % v[0])
2321 2321 ui.write((' revision %s\n') % v[1])
2322 2322
2323 2323 @command('debugsuccessorssets',
2324 2324 [('', 'closest', False, _('return closest successors sets only'))],
2325 2325 _('[REV]'))
2326 2326 def debugsuccessorssets(ui, repo, *revs, **opts):
2327 2327 """show set of successors for revision
2328 2328
2329 2329 A successors set of changeset A is a consistent group of revisions that
2330 2330 succeed A. It contains non-obsolete changesets only unless closests
2331 2331 successors set is set.
2332 2332
2333 2333 In most cases a changeset A has a single successors set containing a single
2334 2334 successor (changeset A replaced by A').
2335 2335
2336 2336 A changeset that is made obsolete with no successors are called "pruned".
2337 2337 Such changesets have no successors sets at all.
2338 2338
2339 2339 A changeset that has been "split" will have a successors set containing
2340 2340 more than one successor.
2341 2341
2342 2342 A changeset that has been rewritten in multiple different ways is called
2343 2343 "divergent". Such changesets have multiple successor sets (each of which
2344 2344 may also be split, i.e. have multiple successors).
2345 2345
2346 2346 Results are displayed as follows::
2347 2347
2348 2348 <rev1>
2349 2349 <successors-1A>
2350 2350 <rev2>
2351 2351 <successors-2A>
2352 2352 <successors-2B1> <successors-2B2> <successors-2B3>
2353 2353
2354 2354 Here rev2 has two possible (i.e. divergent) successors sets. The first
2355 2355 holds one element, whereas the second holds three (i.e. the changeset has
2356 2356 been split).
2357 2357 """
2358 2358 # passed to successorssets caching computation from one call to another
2359 2359 cache = {}
2360 2360 ctx2str = bytes
2361 2361 node2str = short
2362 2362 for rev in scmutil.revrange(repo, revs):
2363 2363 ctx = repo[rev]
2364 2364 ui.write('%s\n'% ctx2str(ctx))
2365 2365 for succsset in obsutil.successorssets(repo, ctx.node(),
2366 2366 closest=opts[r'closest'],
2367 2367 cache=cache):
2368 2368 if succsset:
2369 2369 ui.write(' ')
2370 2370 ui.write(node2str(succsset[0]))
2371 2371 for node in succsset[1:]:
2372 2372 ui.write(' ')
2373 2373 ui.write(node2str(node))
2374 2374 ui.write('\n')
2375 2375
2376 2376 @command('debugtemplate',
2377 2377 [('r', 'rev', [], _('apply template on changesets'), _('REV')),
2378 2378 ('D', 'define', [], _('define template keyword'), _('KEY=VALUE'))],
2379 2379 _('[-r REV]... [-D KEY=VALUE]... TEMPLATE'),
2380 2380 optionalrepo=True)
2381 2381 def debugtemplate(ui, repo, tmpl, **opts):
2382 2382 """parse and apply a template
2383 2383
2384 2384 If -r/--rev is given, the template is processed as a log template and
2385 2385 applied to the given changesets. Otherwise, it is processed as a generic
2386 2386 template.
2387 2387
2388 2388 Use --verbose to print the parsed tree.
2389 2389 """
2390 2390 revs = None
2391 2391 if opts[r'rev']:
2392 2392 if repo is None:
2393 2393 raise error.RepoError(_('there is no Mercurial repository here '
2394 2394 '(.hg not found)'))
2395 2395 revs = scmutil.revrange(repo, opts[r'rev'])
2396 2396
2397 2397 props = {}
2398 2398 for d in opts[r'define']:
2399 2399 try:
2400 2400 k, v = (e.strip() for e in d.split('=', 1))
2401 2401 if not k or k == 'ui':
2402 2402 raise ValueError
2403 2403 props[k] = v
2404 2404 except ValueError:
2405 2405 raise error.Abort(_('malformed keyword definition: %s') % d)
2406 2406
2407 2407 if ui.verbose:
2408 2408 aliases = ui.configitems('templatealias')
2409 2409 tree = templater.parse(tmpl)
2410 2410 ui.note(templater.prettyformat(tree), '\n')
2411 2411 newtree = templater.expandaliases(tree, aliases)
2412 2412 if newtree != tree:
2413 2413 ui.note(("* expanded:\n"), templater.prettyformat(newtree), '\n')
2414 2414
2415 2415 if revs is None:
2416 2416 tres = formatter.templateresources(ui, repo)
2417 2417 t = formatter.maketemplater(ui, tmpl, resources=tres)
2418 2418 ui.write(t.render(props))
2419 2419 else:
2420 2420 displayer = logcmdutil.maketemplater(ui, repo, tmpl)
2421 2421 for r in revs:
2422 2422 displayer.show(repo[r], **pycompat.strkwargs(props))
2423 2423 displayer.close()
2424 2424
2425 2425 @command('debugupdatecaches', [])
2426 2426 def debugupdatecaches(ui, repo, *pats, **opts):
2427 2427 """warm all known caches in the repository"""
2428 2428 with repo.wlock(), repo.lock():
2429 2429 repo.updatecaches()
2430 2430
2431 2431 @command('debugupgraderepo', [
2432 2432 ('o', 'optimize', [], _('extra optimization to perform'), _('NAME')),
2433 2433 ('', 'run', False, _('performs an upgrade')),
2434 2434 ])
2435 2435 def debugupgraderepo(ui, repo, run=False, optimize=None):
2436 2436 """upgrade a repository to use different features
2437 2437
2438 2438 If no arguments are specified, the repository is evaluated for upgrade
2439 2439 and a list of problems and potential optimizations is printed.
2440 2440
2441 2441 With ``--run``, a repository upgrade is performed. Behavior of the upgrade
2442 2442 can be influenced via additional arguments. More details will be provided
2443 2443 by the command output when run without ``--run``.
2444 2444
2445 2445 During the upgrade, the repository will be locked and no writes will be
2446 2446 allowed.
2447 2447
2448 2448 At the end of the upgrade, the repository may not be readable while new
2449 2449 repository data is swapped in. This window will be as long as it takes to
2450 2450 rename some directories inside the ``.hg`` directory. On most machines, this
2451 2451 should complete almost instantaneously and the chances of a consumer being
2452 2452 unable to access the repository should be low.
2453 2453 """
2454 2454 return upgrade.upgraderepo(ui, repo, run=run, optimize=optimize)
2455 2455
2456 2456 @command('debugwalk', cmdutil.walkopts, _('[OPTION]... [FILE]...'),
2457 2457 inferrepo=True)
2458 2458 def debugwalk(ui, repo, *pats, **opts):
2459 2459 """show how files match on given patterns"""
2460 2460 opts = pycompat.byteskwargs(opts)
2461 2461 m = scmutil.match(repo[None], pats, opts)
2462 2462 ui.write(('matcher: %r\n' % m))
2463 2463 items = list(repo[None].walk(m))
2464 2464 if not items:
2465 2465 return
2466 2466 f = lambda fn: fn
2467 2467 if ui.configbool('ui', 'slash') and pycompat.ossep != '/':
2468 2468 f = lambda fn: util.normpath(fn)
2469 2469 fmt = 'f %%-%ds %%-%ds %%s' % (
2470 2470 max([len(abs) for abs in items]),
2471 2471 max([len(m.rel(abs)) for abs in items]))
2472 2472 for abs in items:
2473 2473 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
2474 2474 ui.write("%s\n" % line.rstrip())
2475 2475
2476 2476 @command('debugwireargs',
2477 2477 [('', 'three', '', 'three'),
2478 2478 ('', 'four', '', 'four'),
2479 2479 ('', 'five', '', 'five'),
2480 2480 ] + cmdutil.remoteopts,
2481 2481 _('REPO [OPTIONS]... [ONE [TWO]]'),
2482 2482 norepo=True)
2483 2483 def debugwireargs(ui, repopath, *vals, **opts):
2484 2484 opts = pycompat.byteskwargs(opts)
2485 2485 repo = hg.peer(ui, opts, repopath)
2486 2486 for opt in cmdutil.remoteopts:
2487 2487 del opts[opt[1]]
2488 2488 args = {}
2489 2489 for k, v in opts.iteritems():
2490 2490 if v:
2491 2491 args[k] = v
2492 2492 args = pycompat.strkwargs(args)
2493 2493 # run twice to check that we don't mess up the stream for the next command
2494 2494 res1 = repo.debugwireargs(*vals, **args)
2495 2495 res2 = repo.debugwireargs(*vals, **args)
2496 2496 ui.write("%s\n" % res1)
2497 2497 if res1 != res2:
2498 2498 ui.warn("%s\n" % res2)
General Comments 0
You need to be logged in to leave comments. Login now