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