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