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