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