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