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