##// END OF EJS Templates
copy-tracing: add a --compute flag to debugchangedfiles...
marmoute -
r47208:c2435280 default
parent child Browse files
Show More
@@ -1,4732 +1,4749 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 glob
15 15 import operator
16 16 import os
17 17 import platform
18 18 import random
19 19 import re
20 20 import socket
21 21 import ssl
22 22 import stat
23 23 import string
24 24 import subprocess
25 25 import sys
26 26 import time
27 27
28 28 from .i18n import _
29 29 from .node import (
30 30 bin,
31 31 hex,
32 32 nullid,
33 33 nullrev,
34 34 short,
35 35 )
36 36 from .pycompat import (
37 37 getattr,
38 38 open,
39 39 )
40 40 from . import (
41 41 bundle2,
42 42 bundlerepo,
43 43 changegroup,
44 44 cmdutil,
45 45 color,
46 46 context,
47 47 copies,
48 48 dagparser,
49 49 encoding,
50 50 error,
51 51 exchange,
52 52 extensions,
53 53 filemerge,
54 54 filesetlang,
55 55 formatter,
56 56 hg,
57 57 httppeer,
58 58 localrepo,
59 59 lock as lockmod,
60 60 logcmdutil,
61 61 mergestate as mergestatemod,
62 62 metadata,
63 63 obsolete,
64 64 obsutil,
65 65 pathutil,
66 66 phases,
67 67 policy,
68 68 pvec,
69 69 pycompat,
70 70 registrar,
71 71 repair,
72 72 repoview,
73 73 revlog,
74 74 revset,
75 75 revsetlang,
76 76 scmutil,
77 77 setdiscovery,
78 78 simplemerge,
79 79 sshpeer,
80 80 sslutil,
81 81 streamclone,
82 82 strip,
83 83 tags as tagsmod,
84 84 templater,
85 85 treediscovery,
86 86 upgrade,
87 87 url as urlmod,
88 88 util,
89 89 vfs as vfsmod,
90 90 wireprotoframing,
91 91 wireprotoserver,
92 92 wireprotov2peer,
93 93 )
94 94 from .utils import (
95 95 cborutil,
96 96 compression,
97 97 dateutil,
98 98 procutil,
99 99 stringutil,
100 100 )
101 101
102 102 from .revlogutils import (
103 103 deltas as deltautil,
104 104 nodemap,
105 105 sidedata,
106 106 )
107 107
108 108 release = lockmod.release
109 109
110 110 table = {}
111 111 table.update(strip.command._table)
112 112 command = registrar.command(table)
113 113
114 114
115 115 @command(b'debugancestor', [], _(b'[INDEX] REV1 REV2'), optionalrepo=True)
116 116 def debugancestor(ui, repo, *args):
117 117 """find the ancestor revision of two revisions in a given index"""
118 118 if len(args) == 3:
119 119 index, rev1, rev2 = args
120 120 r = revlog.revlog(vfsmod.vfs(encoding.getcwd(), audit=False), index)
121 121 lookup = r.lookup
122 122 elif len(args) == 2:
123 123 if not repo:
124 124 raise error.Abort(
125 125 _(b'there is no Mercurial repository here (.hg not found)')
126 126 )
127 127 rev1, rev2 = args
128 128 r = repo.changelog
129 129 lookup = repo.lookup
130 130 else:
131 131 raise error.Abort(_(b'either two or three arguments required'))
132 132 a = r.ancestor(lookup(rev1), lookup(rev2))
133 133 ui.write(b'%d:%s\n' % (r.rev(a), hex(a)))
134 134
135 135
136 136 @command(b'debugantivirusrunning', [])
137 137 def debugantivirusrunning(ui, repo):
138 138 """attempt to trigger an antivirus scanner to see if one is active"""
139 139 with repo.cachevfs.open('eicar-test-file.com', b'wb') as f:
140 140 f.write(
141 141 util.b85decode(
142 142 # This is a base85-armored version of the EICAR test file. See
143 143 # https://en.wikipedia.org/wiki/EICAR_test_file for details.
144 144 b'ST#=}P$fV?P+K%yP+C|uG$>GBDK|qyDK~v2MM*<JQY}+dK~6+LQba95P'
145 145 b'E<)&Nm5l)EmTEQR4qnHOhq9iNGnJx'
146 146 )
147 147 )
148 148 # Give an AV engine time to scan the file.
149 149 time.sleep(2)
150 150 util.unlink(repo.cachevfs.join('eicar-test-file.com'))
151 151
152 152
153 153 @command(b'debugapplystreamclonebundle', [], b'FILE')
154 154 def debugapplystreamclonebundle(ui, repo, fname):
155 155 """apply a stream clone bundle file"""
156 156 f = hg.openpath(ui, fname)
157 157 gen = exchange.readbundle(ui, f, fname)
158 158 gen.apply(repo)
159 159
160 160
161 161 @command(
162 162 b'debugbuilddag',
163 163 [
164 164 (
165 165 b'm',
166 166 b'mergeable-file',
167 167 None,
168 168 _(b'add single file mergeable changes'),
169 169 ),
170 170 (
171 171 b'o',
172 172 b'overwritten-file',
173 173 None,
174 174 _(b'add single file all revs overwrite'),
175 175 ),
176 176 (b'n', b'new-file', None, _(b'add new file at each rev')),
177 177 ],
178 178 _(b'[OPTION]... [TEXT]'),
179 179 )
180 180 def debugbuilddag(
181 181 ui,
182 182 repo,
183 183 text=None,
184 184 mergeable_file=False,
185 185 overwritten_file=False,
186 186 new_file=False,
187 187 ):
188 188 """builds a repo with a given DAG from scratch in the current empty repo
189 189
190 190 The description of the DAG is read from stdin if not given on the
191 191 command line.
192 192
193 193 Elements:
194 194
195 195 - "+n" is a linear run of n nodes based on the current default parent
196 196 - "." is a single node based on the current default parent
197 197 - "$" resets the default parent to null (implied at the start);
198 198 otherwise the default parent is always the last node created
199 199 - "<p" sets the default parent to the backref p
200 200 - "*p" is a fork at parent p, which is a backref
201 201 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
202 202 - "/p2" is a merge of the preceding node and p2
203 203 - ":tag" defines a local tag for the preceding node
204 204 - "@branch" sets the named branch for subsequent nodes
205 205 - "#...\\n" is a comment up to the end of the line
206 206
207 207 Whitespace between the above elements is ignored.
208 208
209 209 A backref is either
210 210
211 211 - a number n, which references the node curr-n, where curr is the current
212 212 node, or
213 213 - the name of a local tag you placed earlier using ":tag", or
214 214 - empty to denote the default parent.
215 215
216 216 All string valued-elements are either strictly alphanumeric, or must
217 217 be enclosed in double quotes ("..."), with "\\" as escape character.
218 218 """
219 219
220 220 if text is None:
221 221 ui.status(_(b"reading DAG from stdin\n"))
222 222 text = ui.fin.read()
223 223
224 224 cl = repo.changelog
225 225 if len(cl) > 0:
226 226 raise error.Abort(_(b'repository is not empty'))
227 227
228 228 # determine number of revs in DAG
229 229 total = 0
230 230 for type, data in dagparser.parsedag(text):
231 231 if type == b'n':
232 232 total += 1
233 233
234 234 if mergeable_file:
235 235 linesperrev = 2
236 236 # make a file with k lines per rev
237 237 initialmergedlines = [
238 238 b'%d' % i for i in pycompat.xrange(0, total * linesperrev)
239 239 ]
240 240 initialmergedlines.append(b"")
241 241
242 242 tags = []
243 243 progress = ui.makeprogress(
244 244 _(b'building'), unit=_(b'revisions'), total=total
245 245 )
246 246 with progress, repo.wlock(), repo.lock(), repo.transaction(b"builddag"):
247 247 at = -1
248 248 atbranch = b'default'
249 249 nodeids = []
250 250 id = 0
251 251 progress.update(id)
252 252 for type, data in dagparser.parsedag(text):
253 253 if type == b'n':
254 254 ui.note((b'node %s\n' % pycompat.bytestr(data)))
255 255 id, ps = data
256 256
257 257 files = []
258 258 filecontent = {}
259 259
260 260 p2 = None
261 261 if mergeable_file:
262 262 fn = b"mf"
263 263 p1 = repo[ps[0]]
264 264 if len(ps) > 1:
265 265 p2 = repo[ps[1]]
266 266 pa = p1.ancestor(p2)
267 267 base, local, other = [
268 268 x[fn].data() for x in (pa, p1, p2)
269 269 ]
270 270 m3 = simplemerge.Merge3Text(base, local, other)
271 271 ml = [l.strip() for l in m3.merge_lines()]
272 272 ml.append(b"")
273 273 elif at > 0:
274 274 ml = p1[fn].data().split(b"\n")
275 275 else:
276 276 ml = initialmergedlines
277 277 ml[id * linesperrev] += b" r%i" % id
278 278 mergedtext = b"\n".join(ml)
279 279 files.append(fn)
280 280 filecontent[fn] = mergedtext
281 281
282 282 if overwritten_file:
283 283 fn = b"of"
284 284 files.append(fn)
285 285 filecontent[fn] = b"r%i\n" % id
286 286
287 287 if new_file:
288 288 fn = b"nf%i" % id
289 289 files.append(fn)
290 290 filecontent[fn] = b"r%i\n" % id
291 291 if len(ps) > 1:
292 292 if not p2:
293 293 p2 = repo[ps[1]]
294 294 for fn in p2:
295 295 if fn.startswith(b"nf"):
296 296 files.append(fn)
297 297 filecontent[fn] = p2[fn].data()
298 298
299 299 def fctxfn(repo, cx, path):
300 300 if path in filecontent:
301 301 return context.memfilectx(
302 302 repo, cx, path, filecontent[path]
303 303 )
304 304 return None
305 305
306 306 if len(ps) == 0 or ps[0] < 0:
307 307 pars = [None, None]
308 308 elif len(ps) == 1:
309 309 pars = [nodeids[ps[0]], None]
310 310 else:
311 311 pars = [nodeids[p] for p in ps]
312 312 cx = context.memctx(
313 313 repo,
314 314 pars,
315 315 b"r%i" % id,
316 316 files,
317 317 fctxfn,
318 318 date=(id, 0),
319 319 user=b"debugbuilddag",
320 320 extra={b'branch': atbranch},
321 321 )
322 322 nodeid = repo.commitctx(cx)
323 323 nodeids.append(nodeid)
324 324 at = id
325 325 elif type == b'l':
326 326 id, name = data
327 327 ui.note((b'tag %s\n' % name))
328 328 tags.append(b"%s %s\n" % (hex(repo.changelog.node(id)), name))
329 329 elif type == b'a':
330 330 ui.note((b'branch %s\n' % data))
331 331 atbranch = data
332 332 progress.update(id)
333 333
334 334 if tags:
335 335 repo.vfs.write(b"localtags", b"".join(tags))
336 336
337 337
338 338 def _debugchangegroup(ui, gen, all=None, indent=0, **opts):
339 339 indent_string = b' ' * indent
340 340 if all:
341 341 ui.writenoi18n(
342 342 b"%sformat: id, p1, p2, cset, delta base, len(delta)\n"
343 343 % indent_string
344 344 )
345 345
346 346 def showchunks(named):
347 347 ui.write(b"\n%s%s\n" % (indent_string, named))
348 348 for deltadata in gen.deltaiter():
349 349 node, p1, p2, cs, deltabase, delta, flags = deltadata
350 350 ui.write(
351 351 b"%s%s %s %s %s %s %d\n"
352 352 % (
353 353 indent_string,
354 354 hex(node),
355 355 hex(p1),
356 356 hex(p2),
357 357 hex(cs),
358 358 hex(deltabase),
359 359 len(delta),
360 360 )
361 361 )
362 362
363 363 gen.changelogheader()
364 364 showchunks(b"changelog")
365 365 gen.manifestheader()
366 366 showchunks(b"manifest")
367 367 for chunkdata in iter(gen.filelogheader, {}):
368 368 fname = chunkdata[b'filename']
369 369 showchunks(fname)
370 370 else:
371 371 if isinstance(gen, bundle2.unbundle20):
372 372 raise error.Abort(_(b'use debugbundle2 for this file'))
373 373 gen.changelogheader()
374 374 for deltadata in gen.deltaiter():
375 375 node, p1, p2, cs, deltabase, delta, flags = deltadata
376 376 ui.write(b"%s%s\n" % (indent_string, hex(node)))
377 377
378 378
379 379 def _debugobsmarkers(ui, part, indent=0, **opts):
380 380 """display version and markers contained in 'data'"""
381 381 opts = pycompat.byteskwargs(opts)
382 382 data = part.read()
383 383 indent_string = b' ' * indent
384 384 try:
385 385 version, markers = obsolete._readmarkers(data)
386 386 except error.UnknownVersion as exc:
387 387 msg = b"%sunsupported version: %s (%d bytes)\n"
388 388 msg %= indent_string, exc.version, len(data)
389 389 ui.write(msg)
390 390 else:
391 391 msg = b"%sversion: %d (%d bytes)\n"
392 392 msg %= indent_string, version, len(data)
393 393 ui.write(msg)
394 394 fm = ui.formatter(b'debugobsolete', opts)
395 395 for rawmarker in sorted(markers):
396 396 m = obsutil.marker(None, rawmarker)
397 397 fm.startitem()
398 398 fm.plain(indent_string)
399 399 cmdutil.showmarker(fm, m)
400 400 fm.end()
401 401
402 402
403 403 def _debugphaseheads(ui, data, indent=0):
404 404 """display version and markers contained in 'data'"""
405 405 indent_string = b' ' * indent
406 406 headsbyphase = phases.binarydecode(data)
407 407 for phase in phases.allphases:
408 408 for head in headsbyphase[phase]:
409 409 ui.write(indent_string)
410 410 ui.write(b'%s %s\n' % (hex(head), phases.phasenames[phase]))
411 411
412 412
413 413 def _quasirepr(thing):
414 414 if isinstance(thing, (dict, util.sortdict, collections.OrderedDict)):
415 415 return b'{%s}' % (
416 416 b', '.join(b'%s: %s' % (k, thing[k]) for k in sorted(thing))
417 417 )
418 418 return pycompat.bytestr(repr(thing))
419 419
420 420
421 421 def _debugbundle2(ui, gen, all=None, **opts):
422 422 """lists the contents of a bundle2"""
423 423 if not isinstance(gen, bundle2.unbundle20):
424 424 raise error.Abort(_(b'not a bundle2 file'))
425 425 ui.write((b'Stream params: %s\n' % _quasirepr(gen.params)))
426 426 parttypes = opts.get('part_type', [])
427 427 for part in gen.iterparts():
428 428 if parttypes and part.type not in parttypes:
429 429 continue
430 430 msg = b'%s -- %s (mandatory: %r)\n'
431 431 ui.write((msg % (part.type, _quasirepr(part.params), part.mandatory)))
432 432 if part.type == b'changegroup':
433 433 version = part.params.get(b'version', b'01')
434 434 cg = changegroup.getunbundler(version, part, b'UN')
435 435 if not ui.quiet:
436 436 _debugchangegroup(ui, cg, all=all, indent=4, **opts)
437 437 if part.type == b'obsmarkers':
438 438 if not ui.quiet:
439 439 _debugobsmarkers(ui, part, indent=4, **opts)
440 440 if part.type == b'phase-heads':
441 441 if not ui.quiet:
442 442 _debugphaseheads(ui, part, indent=4)
443 443
444 444
445 445 @command(
446 446 b'debugbundle',
447 447 [
448 448 (b'a', b'all', None, _(b'show all details')),
449 449 (b'', b'part-type', [], _(b'show only the named part type')),
450 450 (b'', b'spec', None, _(b'print the bundlespec of the bundle')),
451 451 ],
452 452 _(b'FILE'),
453 453 norepo=True,
454 454 )
455 455 def debugbundle(ui, bundlepath, all=None, spec=None, **opts):
456 456 """lists the contents of a bundle"""
457 457 with hg.openpath(ui, bundlepath) as f:
458 458 if spec:
459 459 spec = exchange.getbundlespec(ui, f)
460 460 ui.write(b'%s\n' % spec)
461 461 return
462 462
463 463 gen = exchange.readbundle(ui, f, bundlepath)
464 464 if isinstance(gen, bundle2.unbundle20):
465 465 return _debugbundle2(ui, gen, all=all, **opts)
466 466 _debugchangegroup(ui, gen, all=all, **opts)
467 467
468 468
469 469 @command(b'debugcapabilities', [], _(b'PATH'), norepo=True)
470 470 def debugcapabilities(ui, path, **opts):
471 471 """lists the capabilities of a remote peer"""
472 472 opts = pycompat.byteskwargs(opts)
473 473 peer = hg.peer(ui, opts, path)
474 474 caps = peer.capabilities()
475 475 ui.writenoi18n(b'Main capabilities:\n')
476 476 for c in sorted(caps):
477 477 ui.write(b' %s\n' % c)
478 478 b2caps = bundle2.bundle2caps(peer)
479 479 if b2caps:
480 480 ui.writenoi18n(b'Bundle2 capabilities:\n')
481 481 for key, values in sorted(pycompat.iteritems(b2caps)):
482 482 ui.write(b' %s\n' % key)
483 483 for v in values:
484 484 ui.write(b' %s\n' % v)
485 485
486 486
487 @command(b'debugchangedfiles', [], b'REV')
488 def debugchangedfiles(ui, repo, rev):
487 @command(
488 b'debugchangedfiles',
489 [
490 (
491 b'',
492 b'compute',
493 False,
494 b"compute information instead of reading it from storage",
495 ),
496 ],
497 b'REV',
498 )
499 def debugchangedfiles(ui, repo, rev, **opts):
489 500 """list the stored files changes for a revision"""
490 501 ctx = scmutil.revsingle(repo, rev, None)
502 files = None
503
504 if opts['compute']:
505 files = metadata.compute_all_files_changes(ctx)
506 else:
491 507 sd = repo.changelog.sidedata(ctx.rev())
492 508 files_block = sd.get(sidedata.SD_FILES)
493 509 if files_block is not None:
494 510 files = metadata.decode_files_sidedata(sd)
511 if files is not None:
495 512 for f in sorted(files.touched):
496 513 if f in files.added:
497 514 action = b"added"
498 515 elif f in files.removed:
499 516 action = b"removed"
500 517 elif f in files.merged:
501 518 action = b"merged"
502 519 elif f in files.salvaged:
503 520 action = b"salvaged"
504 521 else:
505 522 action = b"touched"
506 523
507 524 copy_parent = b""
508 525 copy_source = b""
509 526 if f in files.copied_from_p1:
510 527 copy_parent = b"p1"
511 528 copy_source = files.copied_from_p1[f]
512 529 elif f in files.copied_from_p2:
513 530 copy_parent = b"p2"
514 531 copy_source = files.copied_from_p2[f]
515 532
516 533 data = (action, copy_parent, f, copy_source)
517 534 template = b"%-8s %2s: %s, %s;\n"
518 535 ui.write(template % data)
519 536
520 537
521 538 @command(b'debugcheckstate', [], b'')
522 539 def debugcheckstate(ui, repo):
523 540 """validate the correctness of the current dirstate"""
524 541 parent1, parent2 = repo.dirstate.parents()
525 542 m1 = repo[parent1].manifest()
526 543 m2 = repo[parent2].manifest()
527 544 errors = 0
528 545 for f in repo.dirstate:
529 546 state = repo.dirstate[f]
530 547 if state in b"nr" and f not in m1:
531 548 ui.warn(_(b"%s in state %s, but not in manifest1\n") % (f, state))
532 549 errors += 1
533 550 if state in b"a" and f in m1:
534 551 ui.warn(_(b"%s in state %s, but also in manifest1\n") % (f, state))
535 552 errors += 1
536 553 if state in b"m" and f not in m1 and f not in m2:
537 554 ui.warn(
538 555 _(b"%s in state %s, but not in either manifest\n") % (f, state)
539 556 )
540 557 errors += 1
541 558 for f in m1:
542 559 state = repo.dirstate[f]
543 560 if state not in b"nrm":
544 561 ui.warn(_(b"%s in manifest1, but listed as state %s") % (f, state))
545 562 errors += 1
546 563 if errors:
547 564 errstr = _(b".hg/dirstate inconsistent with current parent's manifest")
548 565 raise error.Abort(errstr)
549 566
550 567
551 568 @command(
552 569 b'debugcolor',
553 570 [(b'', b'style', None, _(b'show all configured styles'))],
554 571 b'hg debugcolor',
555 572 )
556 573 def debugcolor(ui, repo, **opts):
557 574 """show available color, effects or style"""
558 575 ui.writenoi18n(b'color mode: %s\n' % stringutil.pprint(ui._colormode))
559 576 if opts.get('style'):
560 577 return _debugdisplaystyle(ui)
561 578 else:
562 579 return _debugdisplaycolor(ui)
563 580
564 581
565 582 def _debugdisplaycolor(ui):
566 583 ui = ui.copy()
567 584 ui._styles.clear()
568 585 for effect in color._activeeffects(ui).keys():
569 586 ui._styles[effect] = effect
570 587 if ui._terminfoparams:
571 588 for k, v in ui.configitems(b'color'):
572 589 if k.startswith(b'color.'):
573 590 ui._styles[k] = k[6:]
574 591 elif k.startswith(b'terminfo.'):
575 592 ui._styles[k] = k[9:]
576 593 ui.write(_(b'available colors:\n'))
577 594 # sort label with a '_' after the other to group '_background' entry.
578 595 items = sorted(ui._styles.items(), key=lambda i: (b'_' in i[0], i[0], i[1]))
579 596 for colorname, label in items:
580 597 ui.write(b'%s\n' % colorname, label=label)
581 598
582 599
583 600 def _debugdisplaystyle(ui):
584 601 ui.write(_(b'available style:\n'))
585 602 if not ui._styles:
586 603 return
587 604 width = max(len(s) for s in ui._styles)
588 605 for label, effects in sorted(ui._styles.items()):
589 606 ui.write(b'%s' % label, label=label)
590 607 if effects:
591 608 # 50
592 609 ui.write(b': ')
593 610 ui.write(b' ' * (max(0, width - len(label))))
594 611 ui.write(b', '.join(ui.label(e, e) for e in effects.split()))
595 612 ui.write(b'\n')
596 613
597 614
598 615 @command(b'debugcreatestreamclonebundle', [], b'FILE')
599 616 def debugcreatestreamclonebundle(ui, repo, fname):
600 617 """create a stream clone bundle file
601 618
602 619 Stream bundles are special bundles that are essentially archives of
603 620 revlog files. They are commonly used for cloning very quickly.
604 621 """
605 622 # TODO we may want to turn this into an abort when this functionality
606 623 # is moved into `hg bundle`.
607 624 if phases.hassecret(repo):
608 625 ui.warn(
609 626 _(
610 627 b'(warning: stream clone bundle will contain secret '
611 628 b'revisions)\n'
612 629 )
613 630 )
614 631
615 632 requirements, gen = streamclone.generatebundlev1(repo)
616 633 changegroup.writechunks(ui, gen, fname)
617 634
618 635 ui.write(_(b'bundle requirements: %s\n') % b', '.join(sorted(requirements)))
619 636
620 637
621 638 @command(
622 639 b'debugdag',
623 640 [
624 641 (b't', b'tags', None, _(b'use tags as labels')),
625 642 (b'b', b'branches', None, _(b'annotate with branch names')),
626 643 (b'', b'dots', None, _(b'use dots for runs')),
627 644 (b's', b'spaces', None, _(b'separate elements by spaces')),
628 645 ],
629 646 _(b'[OPTION]... [FILE [REV]...]'),
630 647 optionalrepo=True,
631 648 )
632 649 def debugdag(ui, repo, file_=None, *revs, **opts):
633 650 """format the changelog or an index DAG as a concise textual description
634 651
635 652 If you pass a revlog index, the revlog's DAG is emitted. If you list
636 653 revision numbers, they get labeled in the output as rN.
637 654
638 655 Otherwise, the changelog DAG of the current repo is emitted.
639 656 """
640 657 spaces = opts.get('spaces')
641 658 dots = opts.get('dots')
642 659 if file_:
643 660 rlog = revlog.revlog(vfsmod.vfs(encoding.getcwd(), audit=False), file_)
644 661 revs = {int(r) for r in revs}
645 662
646 663 def events():
647 664 for r in rlog:
648 665 yield b'n', (r, list(p for p in rlog.parentrevs(r) if p != -1))
649 666 if r in revs:
650 667 yield b'l', (r, b"r%i" % r)
651 668
652 669 elif repo:
653 670 cl = repo.changelog
654 671 tags = opts.get('tags')
655 672 branches = opts.get('branches')
656 673 if tags:
657 674 labels = {}
658 675 for l, n in repo.tags().items():
659 676 labels.setdefault(cl.rev(n), []).append(l)
660 677
661 678 def events():
662 679 b = b"default"
663 680 for r in cl:
664 681 if branches:
665 682 newb = cl.read(cl.node(r))[5][b'branch']
666 683 if newb != b:
667 684 yield b'a', newb
668 685 b = newb
669 686 yield b'n', (r, list(p for p in cl.parentrevs(r) if p != -1))
670 687 if tags:
671 688 ls = labels.get(r)
672 689 if ls:
673 690 for l in ls:
674 691 yield b'l', (r, l)
675 692
676 693 else:
677 694 raise error.Abort(_(b'need repo for changelog dag'))
678 695
679 696 for line in dagparser.dagtextlines(
680 697 events(),
681 698 addspaces=spaces,
682 699 wraplabels=True,
683 700 wrapannotations=True,
684 701 wrapnonlinear=dots,
685 702 usedots=dots,
686 703 maxlinewidth=70,
687 704 ):
688 705 ui.write(line)
689 706 ui.write(b"\n")
690 707
691 708
692 709 @command(b'debugdata', cmdutil.debugrevlogopts, _(b'-c|-m|FILE REV'))
693 710 def debugdata(ui, repo, file_, rev=None, **opts):
694 711 """dump the contents of a data file revision"""
695 712 opts = pycompat.byteskwargs(opts)
696 713 if opts.get(b'changelog') or opts.get(b'manifest') or opts.get(b'dir'):
697 714 if rev is not None:
698 715 raise error.CommandError(b'debugdata', _(b'invalid arguments'))
699 716 file_, rev = None, file_
700 717 elif rev is None:
701 718 raise error.CommandError(b'debugdata', _(b'invalid arguments'))
702 719 r = cmdutil.openstorage(repo, b'debugdata', file_, opts)
703 720 try:
704 721 ui.write(r.rawdata(r.lookup(rev)))
705 722 except KeyError:
706 723 raise error.Abort(_(b'invalid revision identifier %s') % rev)
707 724
708 725
709 726 @command(
710 727 b'debugdate',
711 728 [(b'e', b'extended', None, _(b'try extended date formats'))],
712 729 _(b'[-e] DATE [RANGE]'),
713 730 norepo=True,
714 731 optionalrepo=True,
715 732 )
716 733 def debugdate(ui, date, range=None, **opts):
717 734 """parse and display a date"""
718 735 if opts["extended"]:
719 736 d = dateutil.parsedate(date, dateutil.extendeddateformats)
720 737 else:
721 738 d = dateutil.parsedate(date)
722 739 ui.writenoi18n(b"internal: %d %d\n" % d)
723 740 ui.writenoi18n(b"standard: %s\n" % dateutil.datestr(d))
724 741 if range:
725 742 m = dateutil.matchdate(range)
726 743 ui.writenoi18n(b"match: %s\n" % m(d[0]))
727 744
728 745
729 746 @command(
730 747 b'debugdeltachain',
731 748 cmdutil.debugrevlogopts + cmdutil.formatteropts,
732 749 _(b'-c|-m|FILE'),
733 750 optionalrepo=True,
734 751 )
735 752 def debugdeltachain(ui, repo, file_=None, **opts):
736 753 """dump information about delta chains in a revlog
737 754
738 755 Output can be templatized. Available template keywords are:
739 756
740 757 :``rev``: revision number
741 758 :``chainid``: delta chain identifier (numbered by unique base)
742 759 :``chainlen``: delta chain length to this revision
743 760 :``prevrev``: previous revision in delta chain
744 761 :``deltatype``: role of delta / how it was computed
745 762 :``compsize``: compressed size of revision
746 763 :``uncompsize``: uncompressed size of revision
747 764 :``chainsize``: total size of compressed revisions in chain
748 765 :``chainratio``: total chain size divided by uncompressed revision size
749 766 (new delta chains typically start at ratio 2.00)
750 767 :``lindist``: linear distance from base revision in delta chain to end
751 768 of this revision
752 769 :``extradist``: total size of revisions not part of this delta chain from
753 770 base of delta chain to end of this revision; a measurement
754 771 of how much extra data we need to read/seek across to read
755 772 the delta chain for this revision
756 773 :``extraratio``: extradist divided by chainsize; another representation of
757 774 how much unrelated data is needed to load this delta chain
758 775
759 776 If the repository is configured to use the sparse read, additional keywords
760 777 are available:
761 778
762 779 :``readsize``: total size of data read from the disk for a revision
763 780 (sum of the sizes of all the blocks)
764 781 :``largestblock``: size of the largest block of data read from the disk
765 782 :``readdensity``: density of useful bytes in the data read from the disk
766 783 :``srchunks``: in how many data hunks the whole revision would be read
767 784
768 785 The sparse read can be enabled with experimental.sparse-read = True
769 786 """
770 787 opts = pycompat.byteskwargs(opts)
771 788 r = cmdutil.openrevlog(repo, b'debugdeltachain', file_, opts)
772 789 index = r.index
773 790 start = r.start
774 791 length = r.length
775 792 generaldelta = r.version & revlog.FLAG_GENERALDELTA
776 793 withsparseread = getattr(r, '_withsparseread', False)
777 794
778 795 def revinfo(rev):
779 796 e = index[rev]
780 797 compsize = e[1]
781 798 uncompsize = e[2]
782 799 chainsize = 0
783 800
784 801 if generaldelta:
785 802 if e[3] == e[5]:
786 803 deltatype = b'p1'
787 804 elif e[3] == e[6]:
788 805 deltatype = b'p2'
789 806 elif e[3] == rev - 1:
790 807 deltatype = b'prev'
791 808 elif e[3] == rev:
792 809 deltatype = b'base'
793 810 else:
794 811 deltatype = b'other'
795 812 else:
796 813 if e[3] == rev:
797 814 deltatype = b'base'
798 815 else:
799 816 deltatype = b'prev'
800 817
801 818 chain = r._deltachain(rev)[0]
802 819 for iterrev in chain:
803 820 e = index[iterrev]
804 821 chainsize += e[1]
805 822
806 823 return compsize, uncompsize, deltatype, chain, chainsize
807 824
808 825 fm = ui.formatter(b'debugdeltachain', opts)
809 826
810 827 fm.plain(
811 828 b' rev chain# chainlen prev delta '
812 829 b'size rawsize chainsize ratio lindist extradist '
813 830 b'extraratio'
814 831 )
815 832 if withsparseread:
816 833 fm.plain(b' readsize largestblk rddensity srchunks')
817 834 fm.plain(b'\n')
818 835
819 836 chainbases = {}
820 837 for rev in r:
821 838 comp, uncomp, deltatype, chain, chainsize = revinfo(rev)
822 839 chainbase = chain[0]
823 840 chainid = chainbases.setdefault(chainbase, len(chainbases) + 1)
824 841 basestart = start(chainbase)
825 842 revstart = start(rev)
826 843 lineardist = revstart + comp - basestart
827 844 extradist = lineardist - chainsize
828 845 try:
829 846 prevrev = chain[-2]
830 847 except IndexError:
831 848 prevrev = -1
832 849
833 850 if uncomp != 0:
834 851 chainratio = float(chainsize) / float(uncomp)
835 852 else:
836 853 chainratio = chainsize
837 854
838 855 if chainsize != 0:
839 856 extraratio = float(extradist) / float(chainsize)
840 857 else:
841 858 extraratio = extradist
842 859
843 860 fm.startitem()
844 861 fm.write(
845 862 b'rev chainid chainlen prevrev deltatype compsize '
846 863 b'uncompsize chainsize chainratio lindist extradist '
847 864 b'extraratio',
848 865 b'%7d %7d %8d %8d %7s %10d %10d %10d %9.5f %9d %9d %10.5f',
849 866 rev,
850 867 chainid,
851 868 len(chain),
852 869 prevrev,
853 870 deltatype,
854 871 comp,
855 872 uncomp,
856 873 chainsize,
857 874 chainratio,
858 875 lineardist,
859 876 extradist,
860 877 extraratio,
861 878 rev=rev,
862 879 chainid=chainid,
863 880 chainlen=len(chain),
864 881 prevrev=prevrev,
865 882 deltatype=deltatype,
866 883 compsize=comp,
867 884 uncompsize=uncomp,
868 885 chainsize=chainsize,
869 886 chainratio=chainratio,
870 887 lindist=lineardist,
871 888 extradist=extradist,
872 889 extraratio=extraratio,
873 890 )
874 891 if withsparseread:
875 892 readsize = 0
876 893 largestblock = 0
877 894 srchunks = 0
878 895
879 896 for revschunk in deltautil.slicechunk(r, chain):
880 897 srchunks += 1
881 898 blkend = start(revschunk[-1]) + length(revschunk[-1])
882 899 blksize = blkend - start(revschunk[0])
883 900
884 901 readsize += blksize
885 902 if largestblock < blksize:
886 903 largestblock = blksize
887 904
888 905 if readsize:
889 906 readdensity = float(chainsize) / float(readsize)
890 907 else:
891 908 readdensity = 1
892 909
893 910 fm.write(
894 911 b'readsize largestblock readdensity srchunks',
895 912 b' %10d %10d %9.5f %8d',
896 913 readsize,
897 914 largestblock,
898 915 readdensity,
899 916 srchunks,
900 917 readsize=readsize,
901 918 largestblock=largestblock,
902 919 readdensity=readdensity,
903 920 srchunks=srchunks,
904 921 )
905 922
906 923 fm.plain(b'\n')
907 924
908 925 fm.end()
909 926
910 927
911 928 @command(
912 929 b'debugdirstate|debugstate',
913 930 [
914 931 (
915 932 b'',
916 933 b'nodates',
917 934 None,
918 935 _(b'do not display the saved mtime (DEPRECATED)'),
919 936 ),
920 937 (b'', b'dates', True, _(b'display the saved mtime')),
921 938 (b'', b'datesort', None, _(b'sort by saved mtime')),
922 939 ],
923 940 _(b'[OPTION]...'),
924 941 )
925 942 def debugstate(ui, repo, **opts):
926 943 """show the contents of the current dirstate"""
927 944
928 945 nodates = not opts['dates']
929 946 if opts.get('nodates') is not None:
930 947 nodates = True
931 948 datesort = opts.get('datesort')
932 949
933 950 if datesort:
934 951 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
935 952 else:
936 953 keyfunc = None # sort by filename
937 954 for file_, ent in sorted(pycompat.iteritems(repo.dirstate), key=keyfunc):
938 955 if ent[3] == -1:
939 956 timestr = b'unset '
940 957 elif nodates:
941 958 timestr = b'set '
942 959 else:
943 960 timestr = time.strftime(
944 961 "%Y-%m-%d %H:%M:%S ", time.localtime(ent[3])
945 962 )
946 963 timestr = encoding.strtolocal(timestr)
947 964 if ent[1] & 0o20000:
948 965 mode = b'lnk'
949 966 else:
950 967 mode = b'%3o' % (ent[1] & 0o777 & ~util.umask)
951 968 ui.write(b"%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
952 969 for f in repo.dirstate.copies():
953 970 ui.write(_(b"copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
954 971
955 972
956 973 @command(
957 974 b'debugdiscovery',
958 975 [
959 976 (b'', b'old', None, _(b'use old-style discovery')),
960 977 (
961 978 b'',
962 979 b'nonheads',
963 980 None,
964 981 _(b'use old-style discovery with non-heads included'),
965 982 ),
966 983 (b'', b'rev', [], b'restrict discovery to this set of revs'),
967 984 (b'', b'seed', b'12323', b'specify the random seed use for discovery'),
968 985 (
969 986 b'',
970 987 b'local-as-revs',
971 988 "",
972 989 'treat local has having these revisions only',
973 990 ),
974 991 (
975 992 b'',
976 993 b'remote-as-revs',
977 994 "",
978 995 'use local as remote, with only these these revisions',
979 996 ),
980 997 ]
981 998 + cmdutil.remoteopts,
982 999 _(b'[--rev REV] [OTHER]'),
983 1000 )
984 1001 def debugdiscovery(ui, repo, remoteurl=b"default", **opts):
985 1002 """runs the changeset discovery protocol in isolation
986 1003
987 1004 The local peer can be "replaced" by a subset of the local repository by
988 1005 using the `--local-as-revs` flag. Int he same way, usual `remote` peer can
989 1006 be "replaced" by a subset of the local repository using the
990 1007 `--local-as-revs` flag. This is useful to efficiently debug pathological
991 1008 discovery situation.
992 1009 """
993 1010 opts = pycompat.byteskwargs(opts)
994 1011 unfi = repo.unfiltered()
995 1012
996 1013 # setup potential extra filtering
997 1014 local_revs = opts[b"local_as_revs"]
998 1015 remote_revs = opts[b"remote_as_revs"]
999 1016
1000 1017 # make sure tests are repeatable
1001 1018 random.seed(int(opts[b'seed']))
1002 1019
1003 1020 if not remote_revs:
1004 1021
1005 1022 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl))
1006 1023 remote = hg.peer(repo, opts, remoteurl)
1007 1024 ui.status(_(b'comparing with %s\n') % util.hidepassword(remoteurl))
1008 1025 else:
1009 1026 branches = (None, [])
1010 1027 remote_filtered_revs = scmutil.revrange(
1011 1028 unfi, [b"not (::(%s))" % remote_revs]
1012 1029 )
1013 1030 remote_filtered_revs = frozenset(remote_filtered_revs)
1014 1031
1015 1032 def remote_func(x):
1016 1033 return remote_filtered_revs
1017 1034
1018 1035 repoview.filtertable[b'debug-discovery-remote-filter'] = remote_func
1019 1036
1020 1037 remote = repo.peer()
1021 1038 remote._repo = remote._repo.filtered(b'debug-discovery-remote-filter')
1022 1039
1023 1040 if local_revs:
1024 1041 local_filtered_revs = scmutil.revrange(
1025 1042 unfi, [b"not (::(%s))" % local_revs]
1026 1043 )
1027 1044 local_filtered_revs = frozenset(local_filtered_revs)
1028 1045
1029 1046 def local_func(x):
1030 1047 return local_filtered_revs
1031 1048
1032 1049 repoview.filtertable[b'debug-discovery-local-filter'] = local_func
1033 1050 repo = repo.filtered(b'debug-discovery-local-filter')
1034 1051
1035 1052 data = {}
1036 1053 if opts.get(b'old'):
1037 1054
1038 1055 def doit(pushedrevs, remoteheads, remote=remote):
1039 1056 if not util.safehasattr(remote, b'branches'):
1040 1057 # enable in-client legacy support
1041 1058 remote = localrepo.locallegacypeer(remote.local())
1042 1059 common, _in, hds = treediscovery.findcommonincoming(
1043 1060 repo, remote, force=True, audit=data
1044 1061 )
1045 1062 common = set(common)
1046 1063 if not opts.get(b'nonheads'):
1047 1064 ui.writenoi18n(
1048 1065 b"unpruned common: %s\n"
1049 1066 % b" ".join(sorted(short(n) for n in common))
1050 1067 )
1051 1068
1052 1069 clnode = repo.changelog.node
1053 1070 common = repo.revs(b'heads(::%ln)', common)
1054 1071 common = {clnode(r) for r in common}
1055 1072 return common, hds
1056 1073
1057 1074 else:
1058 1075
1059 1076 def doit(pushedrevs, remoteheads, remote=remote):
1060 1077 nodes = None
1061 1078 if pushedrevs:
1062 1079 revs = scmutil.revrange(repo, pushedrevs)
1063 1080 nodes = [repo[r].node() for r in revs]
1064 1081 common, any, hds = setdiscovery.findcommonheads(
1065 1082 ui, repo, remote, ancestorsof=nodes, audit=data
1066 1083 )
1067 1084 return common, hds
1068 1085
1069 1086 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches, revs=None)
1070 1087 localrevs = opts[b'rev']
1071 1088 with util.timedcm('debug-discovery') as t:
1072 1089 common, hds = doit(localrevs, remoterevs)
1073 1090
1074 1091 # compute all statistics
1075 1092 heads_common = set(common)
1076 1093 heads_remote = set(hds)
1077 1094 heads_local = set(repo.heads())
1078 1095 # note: they cannot be a local or remote head that is in common and not
1079 1096 # itself a head of common.
1080 1097 heads_common_local = heads_common & heads_local
1081 1098 heads_common_remote = heads_common & heads_remote
1082 1099 heads_common_both = heads_common & heads_remote & heads_local
1083 1100
1084 1101 all = repo.revs(b'all()')
1085 1102 common = repo.revs(b'::%ln', common)
1086 1103 roots_common = repo.revs(b'roots(::%ld)', common)
1087 1104 missing = repo.revs(b'not ::%ld', common)
1088 1105 heads_missing = repo.revs(b'heads(%ld)', missing)
1089 1106 roots_missing = repo.revs(b'roots(%ld)', missing)
1090 1107 assert len(common) + len(missing) == len(all)
1091 1108
1092 1109 initial_undecided = repo.revs(
1093 1110 b'not (::%ln or %ln::)', heads_common_remote, heads_common_local
1094 1111 )
1095 1112 heads_initial_undecided = repo.revs(b'heads(%ld)', initial_undecided)
1096 1113 roots_initial_undecided = repo.revs(b'roots(%ld)', initial_undecided)
1097 1114 common_initial_undecided = initial_undecided & common
1098 1115 missing_initial_undecided = initial_undecided & missing
1099 1116
1100 1117 data[b'elapsed'] = t.elapsed
1101 1118 data[b'nb-common-heads'] = len(heads_common)
1102 1119 data[b'nb-common-heads-local'] = len(heads_common_local)
1103 1120 data[b'nb-common-heads-remote'] = len(heads_common_remote)
1104 1121 data[b'nb-common-heads-both'] = len(heads_common_both)
1105 1122 data[b'nb-common-roots'] = len(roots_common)
1106 1123 data[b'nb-head-local'] = len(heads_local)
1107 1124 data[b'nb-head-local-missing'] = len(heads_local) - len(heads_common_local)
1108 1125 data[b'nb-head-remote'] = len(heads_remote)
1109 1126 data[b'nb-head-remote-unknown'] = len(heads_remote) - len(
1110 1127 heads_common_remote
1111 1128 )
1112 1129 data[b'nb-revs'] = len(all)
1113 1130 data[b'nb-revs-common'] = len(common)
1114 1131 data[b'nb-revs-missing'] = len(missing)
1115 1132 data[b'nb-missing-heads'] = len(heads_missing)
1116 1133 data[b'nb-missing-roots'] = len(roots_missing)
1117 1134 data[b'nb-ini_und'] = len(initial_undecided)
1118 1135 data[b'nb-ini_und-heads'] = len(heads_initial_undecided)
1119 1136 data[b'nb-ini_und-roots'] = len(roots_initial_undecided)
1120 1137 data[b'nb-ini_und-common'] = len(common_initial_undecided)
1121 1138 data[b'nb-ini_und-missing'] = len(missing_initial_undecided)
1122 1139
1123 1140 # display discovery summary
1124 1141 ui.writenoi18n(b"elapsed time: %(elapsed)f seconds\n" % data)
1125 1142 ui.writenoi18n(b"round-trips: %(total-roundtrips)9d\n" % data)
1126 1143 ui.writenoi18n(b"heads summary:\n")
1127 1144 ui.writenoi18n(b" total common heads: %(nb-common-heads)9d\n" % data)
1128 1145 ui.writenoi18n(
1129 1146 b" also local heads: %(nb-common-heads-local)9d\n" % data
1130 1147 )
1131 1148 ui.writenoi18n(
1132 1149 b" also remote heads: %(nb-common-heads-remote)9d\n" % data
1133 1150 )
1134 1151 ui.writenoi18n(b" both: %(nb-common-heads-both)9d\n" % data)
1135 1152 ui.writenoi18n(b" local heads: %(nb-head-local)9d\n" % data)
1136 1153 ui.writenoi18n(
1137 1154 b" common: %(nb-common-heads-local)9d\n" % data
1138 1155 )
1139 1156 ui.writenoi18n(
1140 1157 b" missing: %(nb-head-local-missing)9d\n" % data
1141 1158 )
1142 1159 ui.writenoi18n(b" remote heads: %(nb-head-remote)9d\n" % data)
1143 1160 ui.writenoi18n(
1144 1161 b" common: %(nb-common-heads-remote)9d\n" % data
1145 1162 )
1146 1163 ui.writenoi18n(
1147 1164 b" unknown: %(nb-head-remote-unknown)9d\n" % data
1148 1165 )
1149 1166 ui.writenoi18n(b"local changesets: %(nb-revs)9d\n" % data)
1150 1167 ui.writenoi18n(b" common: %(nb-revs-common)9d\n" % data)
1151 1168 ui.writenoi18n(b" heads: %(nb-common-heads)9d\n" % data)
1152 1169 ui.writenoi18n(b" roots: %(nb-common-roots)9d\n" % data)
1153 1170 ui.writenoi18n(b" missing: %(nb-revs-missing)9d\n" % data)
1154 1171 ui.writenoi18n(b" heads: %(nb-missing-heads)9d\n" % data)
1155 1172 ui.writenoi18n(b" roots: %(nb-missing-roots)9d\n" % data)
1156 1173 ui.writenoi18n(b" first undecided set: %(nb-ini_und)9d\n" % data)
1157 1174 ui.writenoi18n(b" heads: %(nb-ini_und-heads)9d\n" % data)
1158 1175 ui.writenoi18n(b" roots: %(nb-ini_und-roots)9d\n" % data)
1159 1176 ui.writenoi18n(b" common: %(nb-ini_und-common)9d\n" % data)
1160 1177 ui.writenoi18n(b" missing: %(nb-ini_und-missing)9d\n" % data)
1161 1178
1162 1179 if ui.verbose:
1163 1180 ui.writenoi18n(
1164 1181 b"common heads: %s\n"
1165 1182 % b" ".join(sorted(short(n) for n in heads_common))
1166 1183 )
1167 1184
1168 1185
1169 1186 _chunksize = 4 << 10
1170 1187
1171 1188
1172 1189 @command(
1173 1190 b'debugdownload',
1174 1191 [
1175 1192 (b'o', b'output', b'', _(b'path')),
1176 1193 ],
1177 1194 optionalrepo=True,
1178 1195 )
1179 1196 def debugdownload(ui, repo, url, output=None, **opts):
1180 1197 """download a resource using Mercurial logic and config"""
1181 1198 fh = urlmod.open(ui, url, output)
1182 1199
1183 1200 dest = ui
1184 1201 if output:
1185 1202 dest = open(output, b"wb", _chunksize)
1186 1203 try:
1187 1204 data = fh.read(_chunksize)
1188 1205 while data:
1189 1206 dest.write(data)
1190 1207 data = fh.read(_chunksize)
1191 1208 finally:
1192 1209 if output:
1193 1210 dest.close()
1194 1211
1195 1212
1196 1213 @command(b'debugextensions', cmdutil.formatteropts, [], optionalrepo=True)
1197 1214 def debugextensions(ui, repo, **opts):
1198 1215 '''show information about active extensions'''
1199 1216 opts = pycompat.byteskwargs(opts)
1200 1217 exts = extensions.extensions(ui)
1201 1218 hgver = util.version()
1202 1219 fm = ui.formatter(b'debugextensions', opts)
1203 1220 for extname, extmod in sorted(exts, key=operator.itemgetter(0)):
1204 1221 isinternal = extensions.ismoduleinternal(extmod)
1205 1222 extsource = None
1206 1223
1207 1224 if util.safehasattr(extmod, '__file__'):
1208 1225 extsource = pycompat.fsencode(extmod.__file__)
1209 1226 elif getattr(sys, 'oxidized', False):
1210 1227 extsource = pycompat.sysexecutable
1211 1228 if isinternal:
1212 1229 exttestedwith = [] # never expose magic string to users
1213 1230 else:
1214 1231 exttestedwith = getattr(extmod, 'testedwith', b'').split()
1215 1232 extbuglink = getattr(extmod, 'buglink', None)
1216 1233
1217 1234 fm.startitem()
1218 1235
1219 1236 if ui.quiet or ui.verbose:
1220 1237 fm.write(b'name', b'%s\n', extname)
1221 1238 else:
1222 1239 fm.write(b'name', b'%s', extname)
1223 1240 if isinternal or hgver in exttestedwith:
1224 1241 fm.plain(b'\n')
1225 1242 elif not exttestedwith:
1226 1243 fm.plain(_(b' (untested!)\n'))
1227 1244 else:
1228 1245 lasttestedversion = exttestedwith[-1]
1229 1246 fm.plain(b' (%s!)\n' % lasttestedversion)
1230 1247
1231 1248 fm.condwrite(
1232 1249 ui.verbose and extsource,
1233 1250 b'source',
1234 1251 _(b' location: %s\n'),
1235 1252 extsource or b"",
1236 1253 )
1237 1254
1238 1255 if ui.verbose:
1239 1256 fm.plain(_(b' bundled: %s\n') % [b'no', b'yes'][isinternal])
1240 1257 fm.data(bundled=isinternal)
1241 1258
1242 1259 fm.condwrite(
1243 1260 ui.verbose and exttestedwith,
1244 1261 b'testedwith',
1245 1262 _(b' tested with: %s\n'),
1246 1263 fm.formatlist(exttestedwith, name=b'ver'),
1247 1264 )
1248 1265
1249 1266 fm.condwrite(
1250 1267 ui.verbose and extbuglink,
1251 1268 b'buglink',
1252 1269 _(b' bug reporting: %s\n'),
1253 1270 extbuglink or b"",
1254 1271 )
1255 1272
1256 1273 fm.end()
1257 1274
1258 1275
1259 1276 @command(
1260 1277 b'debugfileset',
1261 1278 [
1262 1279 (
1263 1280 b'r',
1264 1281 b'rev',
1265 1282 b'',
1266 1283 _(b'apply the filespec on this revision'),
1267 1284 _(b'REV'),
1268 1285 ),
1269 1286 (
1270 1287 b'',
1271 1288 b'all-files',
1272 1289 False,
1273 1290 _(b'test files from all revisions and working directory'),
1274 1291 ),
1275 1292 (
1276 1293 b's',
1277 1294 b'show-matcher',
1278 1295 None,
1279 1296 _(b'print internal representation of matcher'),
1280 1297 ),
1281 1298 (
1282 1299 b'p',
1283 1300 b'show-stage',
1284 1301 [],
1285 1302 _(b'print parsed tree at the given stage'),
1286 1303 _(b'NAME'),
1287 1304 ),
1288 1305 ],
1289 1306 _(b'[-r REV] [--all-files] [OPTION]... FILESPEC'),
1290 1307 )
1291 1308 def debugfileset(ui, repo, expr, **opts):
1292 1309 '''parse and apply a fileset specification'''
1293 1310 from . import fileset
1294 1311
1295 1312 fileset.symbols # force import of fileset so we have predicates to optimize
1296 1313 opts = pycompat.byteskwargs(opts)
1297 1314 ctx = scmutil.revsingle(repo, opts.get(b'rev'), None)
1298 1315
1299 1316 stages = [
1300 1317 (b'parsed', pycompat.identity),
1301 1318 (b'analyzed', filesetlang.analyze),
1302 1319 (b'optimized', filesetlang.optimize),
1303 1320 ]
1304 1321 stagenames = {n for n, f in stages}
1305 1322
1306 1323 showalways = set()
1307 1324 if ui.verbose and not opts[b'show_stage']:
1308 1325 # show parsed tree by --verbose (deprecated)
1309 1326 showalways.add(b'parsed')
1310 1327 if opts[b'show_stage'] == [b'all']:
1311 1328 showalways.update(stagenames)
1312 1329 else:
1313 1330 for n in opts[b'show_stage']:
1314 1331 if n not in stagenames:
1315 1332 raise error.Abort(_(b'invalid stage name: %s') % n)
1316 1333 showalways.update(opts[b'show_stage'])
1317 1334
1318 1335 tree = filesetlang.parse(expr)
1319 1336 for n, f in stages:
1320 1337 tree = f(tree)
1321 1338 if n in showalways:
1322 1339 if opts[b'show_stage'] or n != b'parsed':
1323 1340 ui.write(b"* %s:\n" % n)
1324 1341 ui.write(filesetlang.prettyformat(tree), b"\n")
1325 1342
1326 1343 files = set()
1327 1344 if opts[b'all_files']:
1328 1345 for r in repo:
1329 1346 c = repo[r]
1330 1347 files.update(c.files())
1331 1348 files.update(c.substate)
1332 1349 if opts[b'all_files'] or ctx.rev() is None:
1333 1350 wctx = repo[None]
1334 1351 files.update(
1335 1352 repo.dirstate.walk(
1336 1353 scmutil.matchall(repo),
1337 1354 subrepos=list(wctx.substate),
1338 1355 unknown=True,
1339 1356 ignored=True,
1340 1357 )
1341 1358 )
1342 1359 files.update(wctx.substate)
1343 1360 else:
1344 1361 files.update(ctx.files())
1345 1362 files.update(ctx.substate)
1346 1363
1347 1364 m = ctx.matchfileset(repo.getcwd(), expr)
1348 1365 if opts[b'show_matcher'] or (opts[b'show_matcher'] is None and ui.verbose):
1349 1366 ui.writenoi18n(b'* matcher:\n', stringutil.prettyrepr(m), b'\n')
1350 1367 for f in sorted(files):
1351 1368 if not m(f):
1352 1369 continue
1353 1370 ui.write(b"%s\n" % f)
1354 1371
1355 1372
1356 1373 @command(b'debugformat', [] + cmdutil.formatteropts)
1357 1374 def debugformat(ui, repo, **opts):
1358 1375 """display format information about the current repository
1359 1376
1360 1377 Use --verbose to get extra information about current config value and
1361 1378 Mercurial default."""
1362 1379 opts = pycompat.byteskwargs(opts)
1363 1380 maxvariantlength = max(len(fv.name) for fv in upgrade.allformatvariant)
1364 1381 maxvariantlength = max(len(b'format-variant'), maxvariantlength)
1365 1382
1366 1383 def makeformatname(name):
1367 1384 return b'%s:' + (b' ' * (maxvariantlength - len(name)))
1368 1385
1369 1386 fm = ui.formatter(b'debugformat', opts)
1370 1387 if fm.isplain():
1371 1388
1372 1389 def formatvalue(value):
1373 1390 if util.safehasattr(value, b'startswith'):
1374 1391 return value
1375 1392 if value:
1376 1393 return b'yes'
1377 1394 else:
1378 1395 return b'no'
1379 1396
1380 1397 else:
1381 1398 formatvalue = pycompat.identity
1382 1399
1383 1400 fm.plain(b'format-variant')
1384 1401 fm.plain(b' ' * (maxvariantlength - len(b'format-variant')))
1385 1402 fm.plain(b' repo')
1386 1403 if ui.verbose:
1387 1404 fm.plain(b' config default')
1388 1405 fm.plain(b'\n')
1389 1406 for fv in upgrade.allformatvariant:
1390 1407 fm.startitem()
1391 1408 repovalue = fv.fromrepo(repo)
1392 1409 configvalue = fv.fromconfig(repo)
1393 1410
1394 1411 if repovalue != configvalue:
1395 1412 namelabel = b'formatvariant.name.mismatchconfig'
1396 1413 repolabel = b'formatvariant.repo.mismatchconfig'
1397 1414 elif repovalue != fv.default:
1398 1415 namelabel = b'formatvariant.name.mismatchdefault'
1399 1416 repolabel = b'formatvariant.repo.mismatchdefault'
1400 1417 else:
1401 1418 namelabel = b'formatvariant.name.uptodate'
1402 1419 repolabel = b'formatvariant.repo.uptodate'
1403 1420
1404 1421 fm.write(b'name', makeformatname(fv.name), fv.name, label=namelabel)
1405 1422 fm.write(b'repo', b' %3s', formatvalue(repovalue), label=repolabel)
1406 1423 if fv.default != configvalue:
1407 1424 configlabel = b'formatvariant.config.special'
1408 1425 else:
1409 1426 configlabel = b'formatvariant.config.default'
1410 1427 fm.condwrite(
1411 1428 ui.verbose,
1412 1429 b'config',
1413 1430 b' %6s',
1414 1431 formatvalue(configvalue),
1415 1432 label=configlabel,
1416 1433 )
1417 1434 fm.condwrite(
1418 1435 ui.verbose,
1419 1436 b'default',
1420 1437 b' %7s',
1421 1438 formatvalue(fv.default),
1422 1439 label=b'formatvariant.default',
1423 1440 )
1424 1441 fm.plain(b'\n')
1425 1442 fm.end()
1426 1443
1427 1444
1428 1445 @command(b'debugfsinfo', [], _(b'[PATH]'), norepo=True)
1429 1446 def debugfsinfo(ui, path=b"."):
1430 1447 """show information detected about current filesystem"""
1431 1448 ui.writenoi18n(b'path: %s\n' % path)
1432 1449 ui.writenoi18n(
1433 1450 b'mounted on: %s\n' % (util.getfsmountpoint(path) or b'(unknown)')
1434 1451 )
1435 1452 ui.writenoi18n(b'exec: %s\n' % (util.checkexec(path) and b'yes' or b'no'))
1436 1453 ui.writenoi18n(b'fstype: %s\n' % (util.getfstype(path) or b'(unknown)'))
1437 1454 ui.writenoi18n(
1438 1455 b'symlink: %s\n' % (util.checklink(path) and b'yes' or b'no')
1439 1456 )
1440 1457 ui.writenoi18n(
1441 1458 b'hardlink: %s\n' % (util.checknlink(path) and b'yes' or b'no')
1442 1459 )
1443 1460 casesensitive = b'(unknown)'
1444 1461 try:
1445 1462 with pycompat.namedtempfile(prefix=b'.debugfsinfo', dir=path) as f:
1446 1463 casesensitive = util.fscasesensitive(f.name) and b'yes' or b'no'
1447 1464 except OSError:
1448 1465 pass
1449 1466 ui.writenoi18n(b'case-sensitive: %s\n' % casesensitive)
1450 1467
1451 1468
1452 1469 @command(
1453 1470 b'debuggetbundle',
1454 1471 [
1455 1472 (b'H', b'head', [], _(b'id of head node'), _(b'ID')),
1456 1473 (b'C', b'common', [], _(b'id of common node'), _(b'ID')),
1457 1474 (
1458 1475 b't',
1459 1476 b'type',
1460 1477 b'bzip2',
1461 1478 _(b'bundle compression type to use'),
1462 1479 _(b'TYPE'),
1463 1480 ),
1464 1481 ],
1465 1482 _(b'REPO FILE [-H|-C ID]...'),
1466 1483 norepo=True,
1467 1484 )
1468 1485 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1469 1486 """retrieves a bundle from a repo
1470 1487
1471 1488 Every ID must be a full-length hex node id string. Saves the bundle to the
1472 1489 given file.
1473 1490 """
1474 1491 opts = pycompat.byteskwargs(opts)
1475 1492 repo = hg.peer(ui, opts, repopath)
1476 1493 if not repo.capable(b'getbundle'):
1477 1494 raise error.Abort(b"getbundle() not supported by target repository")
1478 1495 args = {}
1479 1496 if common:
1480 1497 args['common'] = [bin(s) for s in common]
1481 1498 if head:
1482 1499 args['heads'] = [bin(s) for s in head]
1483 1500 # TODO: get desired bundlecaps from command line.
1484 1501 args['bundlecaps'] = None
1485 1502 bundle = repo.getbundle(b'debug', **args)
1486 1503
1487 1504 bundletype = opts.get(b'type', b'bzip2').lower()
1488 1505 btypes = {
1489 1506 b'none': b'HG10UN',
1490 1507 b'bzip2': b'HG10BZ',
1491 1508 b'gzip': b'HG10GZ',
1492 1509 b'bundle2': b'HG20',
1493 1510 }
1494 1511 bundletype = btypes.get(bundletype)
1495 1512 if bundletype not in bundle2.bundletypes:
1496 1513 raise error.Abort(_(b'unknown bundle type specified with --type'))
1497 1514 bundle2.writebundle(ui, bundle, bundlepath, bundletype)
1498 1515
1499 1516
1500 1517 @command(b'debugignore', [], b'[FILE]')
1501 1518 def debugignore(ui, repo, *files, **opts):
1502 1519 """display the combined ignore pattern and information about ignored files
1503 1520
1504 1521 With no argument display the combined ignore pattern.
1505 1522
1506 1523 Given space separated file names, shows if the given file is ignored and
1507 1524 if so, show the ignore rule (file and line number) that matched it.
1508 1525 """
1509 1526 ignore = repo.dirstate._ignore
1510 1527 if not files:
1511 1528 # Show all the patterns
1512 1529 ui.write(b"%s\n" % pycompat.byterepr(ignore))
1513 1530 else:
1514 1531 m = scmutil.match(repo[None], pats=files)
1515 1532 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
1516 1533 for f in m.files():
1517 1534 nf = util.normpath(f)
1518 1535 ignored = None
1519 1536 ignoredata = None
1520 1537 if nf != b'.':
1521 1538 if ignore(nf):
1522 1539 ignored = nf
1523 1540 ignoredata = repo.dirstate._ignorefileandline(nf)
1524 1541 else:
1525 1542 for p in pathutil.finddirs(nf):
1526 1543 if ignore(p):
1527 1544 ignored = p
1528 1545 ignoredata = repo.dirstate._ignorefileandline(p)
1529 1546 break
1530 1547 if ignored:
1531 1548 if ignored == nf:
1532 1549 ui.write(_(b"%s is ignored\n") % uipathfn(f))
1533 1550 else:
1534 1551 ui.write(
1535 1552 _(
1536 1553 b"%s is ignored because of "
1537 1554 b"containing directory %s\n"
1538 1555 )
1539 1556 % (uipathfn(f), ignored)
1540 1557 )
1541 1558 ignorefile, lineno, line = ignoredata
1542 1559 ui.write(
1543 1560 _(b"(ignore rule in %s, line %d: '%s')\n")
1544 1561 % (ignorefile, lineno, line)
1545 1562 )
1546 1563 else:
1547 1564 ui.write(_(b"%s is not ignored\n") % uipathfn(f))
1548 1565
1549 1566
1550 1567 @command(
1551 1568 b'debugindex',
1552 1569 cmdutil.debugrevlogopts + cmdutil.formatteropts,
1553 1570 _(b'-c|-m|FILE'),
1554 1571 )
1555 1572 def debugindex(ui, repo, file_=None, **opts):
1556 1573 """dump index data for a storage primitive"""
1557 1574 opts = pycompat.byteskwargs(opts)
1558 1575 store = cmdutil.openstorage(repo, b'debugindex', file_, opts)
1559 1576
1560 1577 if ui.debugflag:
1561 1578 shortfn = hex
1562 1579 else:
1563 1580 shortfn = short
1564 1581
1565 1582 idlen = 12
1566 1583 for i in store:
1567 1584 idlen = len(shortfn(store.node(i)))
1568 1585 break
1569 1586
1570 1587 fm = ui.formatter(b'debugindex', opts)
1571 1588 fm.plain(
1572 1589 b' rev linkrev %s %s p2\n'
1573 1590 % (b'nodeid'.ljust(idlen), b'p1'.ljust(idlen))
1574 1591 )
1575 1592
1576 1593 for rev in store:
1577 1594 node = store.node(rev)
1578 1595 parents = store.parents(node)
1579 1596
1580 1597 fm.startitem()
1581 1598 fm.write(b'rev', b'%6d ', rev)
1582 1599 fm.write(b'linkrev', b'%7d ', store.linkrev(rev))
1583 1600 fm.write(b'node', b'%s ', shortfn(node))
1584 1601 fm.write(b'p1', b'%s ', shortfn(parents[0]))
1585 1602 fm.write(b'p2', b'%s', shortfn(parents[1]))
1586 1603 fm.plain(b'\n')
1587 1604
1588 1605 fm.end()
1589 1606
1590 1607
1591 1608 @command(
1592 1609 b'debugindexdot',
1593 1610 cmdutil.debugrevlogopts,
1594 1611 _(b'-c|-m|FILE'),
1595 1612 optionalrepo=True,
1596 1613 )
1597 1614 def debugindexdot(ui, repo, file_=None, **opts):
1598 1615 """dump an index DAG as a graphviz dot file"""
1599 1616 opts = pycompat.byteskwargs(opts)
1600 1617 r = cmdutil.openstorage(repo, b'debugindexdot', file_, opts)
1601 1618 ui.writenoi18n(b"digraph G {\n")
1602 1619 for i in r:
1603 1620 node = r.node(i)
1604 1621 pp = r.parents(node)
1605 1622 ui.write(b"\t%d -> %d\n" % (r.rev(pp[0]), i))
1606 1623 if pp[1] != nullid:
1607 1624 ui.write(b"\t%d -> %d\n" % (r.rev(pp[1]), i))
1608 1625 ui.write(b"}\n")
1609 1626
1610 1627
1611 1628 @command(b'debugindexstats', [])
1612 1629 def debugindexstats(ui, repo):
1613 1630 """show stats related to the changelog index"""
1614 1631 repo.changelog.shortest(nullid, 1)
1615 1632 index = repo.changelog.index
1616 1633 if not util.safehasattr(index, b'stats'):
1617 1634 raise error.Abort(_(b'debugindexstats only works with native code'))
1618 1635 for k, v in sorted(index.stats().items()):
1619 1636 ui.write(b'%s: %d\n' % (k, v))
1620 1637
1621 1638
1622 1639 @command(b'debuginstall', [] + cmdutil.formatteropts, b'', norepo=True)
1623 1640 def debuginstall(ui, **opts):
1624 1641 """test Mercurial installation
1625 1642
1626 1643 Returns 0 on success.
1627 1644 """
1628 1645 opts = pycompat.byteskwargs(opts)
1629 1646
1630 1647 problems = 0
1631 1648
1632 1649 fm = ui.formatter(b'debuginstall', opts)
1633 1650 fm.startitem()
1634 1651
1635 1652 # encoding might be unknown or wrong. don't translate these messages.
1636 1653 fm.write(b'encoding', b"checking encoding (%s)...\n", encoding.encoding)
1637 1654 err = None
1638 1655 try:
1639 1656 codecs.lookup(pycompat.sysstr(encoding.encoding))
1640 1657 except LookupError as inst:
1641 1658 err = stringutil.forcebytestr(inst)
1642 1659 problems += 1
1643 1660 fm.condwrite(
1644 1661 err,
1645 1662 b'encodingerror',
1646 1663 b" %s\n (check that your locale is properly set)\n",
1647 1664 err,
1648 1665 )
1649 1666
1650 1667 # Python
1651 1668 pythonlib = None
1652 1669 if util.safehasattr(os, '__file__'):
1653 1670 pythonlib = os.path.dirname(pycompat.fsencode(os.__file__))
1654 1671 elif getattr(sys, 'oxidized', False):
1655 1672 pythonlib = pycompat.sysexecutable
1656 1673
1657 1674 fm.write(
1658 1675 b'pythonexe',
1659 1676 _(b"checking Python executable (%s)\n"),
1660 1677 pycompat.sysexecutable or _(b"unknown"),
1661 1678 )
1662 1679 fm.write(
1663 1680 b'pythonimplementation',
1664 1681 _(b"checking Python implementation (%s)\n"),
1665 1682 pycompat.sysbytes(platform.python_implementation()),
1666 1683 )
1667 1684 fm.write(
1668 1685 b'pythonver',
1669 1686 _(b"checking Python version (%s)\n"),
1670 1687 (b"%d.%d.%d" % sys.version_info[:3]),
1671 1688 )
1672 1689 fm.write(
1673 1690 b'pythonlib',
1674 1691 _(b"checking Python lib (%s)...\n"),
1675 1692 pythonlib or _(b"unknown"),
1676 1693 )
1677 1694
1678 1695 try:
1679 1696 from . import rustext
1680 1697
1681 1698 rustext.__doc__ # trigger lazy import
1682 1699 except ImportError:
1683 1700 rustext = None
1684 1701
1685 1702 security = set(sslutil.supportedprotocols)
1686 1703 if sslutil.hassni:
1687 1704 security.add(b'sni')
1688 1705
1689 1706 fm.write(
1690 1707 b'pythonsecurity',
1691 1708 _(b"checking Python security support (%s)\n"),
1692 1709 fm.formatlist(sorted(security), name=b'protocol', fmt=b'%s', sep=b','),
1693 1710 )
1694 1711
1695 1712 # These are warnings, not errors. So don't increment problem count. This
1696 1713 # may change in the future.
1697 1714 if b'tls1.2' not in security:
1698 1715 fm.plain(
1699 1716 _(
1700 1717 b' TLS 1.2 not supported by Python install; '
1701 1718 b'network connections lack modern security\n'
1702 1719 )
1703 1720 )
1704 1721 if b'sni' not in security:
1705 1722 fm.plain(
1706 1723 _(
1707 1724 b' SNI not supported by Python install; may have '
1708 1725 b'connectivity issues with some servers\n'
1709 1726 )
1710 1727 )
1711 1728
1712 1729 fm.plain(
1713 1730 _(
1714 1731 b"checking Rust extensions (%s)\n"
1715 1732 % (b'missing' if rustext is None else b'installed')
1716 1733 ),
1717 1734 )
1718 1735
1719 1736 # TODO print CA cert info
1720 1737
1721 1738 # hg version
1722 1739 hgver = util.version()
1723 1740 fm.write(
1724 1741 b'hgver', _(b"checking Mercurial version (%s)\n"), hgver.split(b'+')[0]
1725 1742 )
1726 1743 fm.write(
1727 1744 b'hgverextra',
1728 1745 _(b"checking Mercurial custom build (%s)\n"),
1729 1746 b'+'.join(hgver.split(b'+')[1:]),
1730 1747 )
1731 1748
1732 1749 # compiled modules
1733 1750 hgmodules = None
1734 1751 if util.safehasattr(sys.modules[__name__], '__file__'):
1735 1752 hgmodules = os.path.dirname(pycompat.fsencode(__file__))
1736 1753 elif getattr(sys, 'oxidized', False):
1737 1754 hgmodules = pycompat.sysexecutable
1738 1755
1739 1756 fm.write(
1740 1757 b'hgmodulepolicy', _(b"checking module policy (%s)\n"), policy.policy
1741 1758 )
1742 1759 fm.write(
1743 1760 b'hgmodules',
1744 1761 _(b"checking installed modules (%s)...\n"),
1745 1762 hgmodules or _(b"unknown"),
1746 1763 )
1747 1764
1748 1765 rustandc = policy.policy in (b'rust+c', b'rust+c-allow')
1749 1766 rustext = rustandc # for now, that's the only case
1750 1767 cext = policy.policy in (b'c', b'allow') or rustandc
1751 1768 nopure = cext or rustext
1752 1769 if nopure:
1753 1770 err = None
1754 1771 try:
1755 1772 if cext:
1756 1773 from .cext import ( # pytype: disable=import-error
1757 1774 base85,
1758 1775 bdiff,
1759 1776 mpatch,
1760 1777 osutil,
1761 1778 )
1762 1779
1763 1780 # quiet pyflakes
1764 1781 dir(bdiff), dir(mpatch), dir(base85), dir(osutil)
1765 1782 if rustext:
1766 1783 from .rustext import ( # pytype: disable=import-error
1767 1784 ancestor,
1768 1785 dirstate,
1769 1786 )
1770 1787
1771 1788 dir(ancestor), dir(dirstate) # quiet pyflakes
1772 1789 except Exception as inst:
1773 1790 err = stringutil.forcebytestr(inst)
1774 1791 problems += 1
1775 1792 fm.condwrite(err, b'extensionserror', b" %s\n", err)
1776 1793
1777 1794 compengines = util.compengines._engines.values()
1778 1795 fm.write(
1779 1796 b'compengines',
1780 1797 _(b'checking registered compression engines (%s)\n'),
1781 1798 fm.formatlist(
1782 1799 sorted(e.name() for e in compengines),
1783 1800 name=b'compengine',
1784 1801 fmt=b'%s',
1785 1802 sep=b', ',
1786 1803 ),
1787 1804 )
1788 1805 fm.write(
1789 1806 b'compenginesavail',
1790 1807 _(b'checking available compression engines (%s)\n'),
1791 1808 fm.formatlist(
1792 1809 sorted(e.name() for e in compengines if e.available()),
1793 1810 name=b'compengine',
1794 1811 fmt=b'%s',
1795 1812 sep=b', ',
1796 1813 ),
1797 1814 )
1798 1815 wirecompengines = compression.compengines.supportedwireengines(
1799 1816 compression.SERVERROLE
1800 1817 )
1801 1818 fm.write(
1802 1819 b'compenginesserver',
1803 1820 _(
1804 1821 b'checking available compression engines '
1805 1822 b'for wire protocol (%s)\n'
1806 1823 ),
1807 1824 fm.formatlist(
1808 1825 [e.name() for e in wirecompengines if e.wireprotosupport()],
1809 1826 name=b'compengine',
1810 1827 fmt=b'%s',
1811 1828 sep=b', ',
1812 1829 ),
1813 1830 )
1814 1831 re2 = b'missing'
1815 1832 if util._re2:
1816 1833 re2 = b'available'
1817 1834 fm.plain(_(b'checking "re2" regexp engine (%s)\n') % re2)
1818 1835 fm.data(re2=bool(util._re2))
1819 1836
1820 1837 # templates
1821 1838 p = templater.templatedir()
1822 1839 fm.write(b'templatedirs', b'checking templates (%s)...\n', p or b'')
1823 1840 fm.condwrite(not p, b'', _(b" no template directories found\n"))
1824 1841 if p:
1825 1842 (m, fp) = templater.try_open_template(b"map-cmdline.default")
1826 1843 if m:
1827 1844 # template found, check if it is working
1828 1845 err = None
1829 1846 try:
1830 1847 templater.templater.frommapfile(m)
1831 1848 except Exception as inst:
1832 1849 err = stringutil.forcebytestr(inst)
1833 1850 p = None
1834 1851 fm.condwrite(err, b'defaulttemplateerror', b" %s\n", err)
1835 1852 else:
1836 1853 p = None
1837 1854 fm.condwrite(
1838 1855 p, b'defaulttemplate', _(b"checking default template (%s)\n"), m
1839 1856 )
1840 1857 fm.condwrite(
1841 1858 not m,
1842 1859 b'defaulttemplatenotfound',
1843 1860 _(b" template '%s' not found\n"),
1844 1861 b"default",
1845 1862 )
1846 1863 if not p:
1847 1864 problems += 1
1848 1865 fm.condwrite(
1849 1866 not p, b'', _(b" (templates seem to have been installed incorrectly)\n")
1850 1867 )
1851 1868
1852 1869 # editor
1853 1870 editor = ui.geteditor()
1854 1871 editor = util.expandpath(editor)
1855 1872 editorbin = procutil.shellsplit(editor)[0]
1856 1873 fm.write(b'editor', _(b"checking commit editor... (%s)\n"), editorbin)
1857 1874 cmdpath = procutil.findexe(editorbin)
1858 1875 fm.condwrite(
1859 1876 not cmdpath and editor == b'vi',
1860 1877 b'vinotfound',
1861 1878 _(
1862 1879 b" No commit editor set and can't find %s in PATH\n"
1863 1880 b" (specify a commit editor in your configuration"
1864 1881 b" file)\n"
1865 1882 ),
1866 1883 not cmdpath and editor == b'vi' and editorbin,
1867 1884 )
1868 1885 fm.condwrite(
1869 1886 not cmdpath and editor != b'vi',
1870 1887 b'editornotfound',
1871 1888 _(
1872 1889 b" Can't find editor '%s' in PATH\n"
1873 1890 b" (specify a commit editor in your configuration"
1874 1891 b" file)\n"
1875 1892 ),
1876 1893 not cmdpath and editorbin,
1877 1894 )
1878 1895 if not cmdpath and editor != b'vi':
1879 1896 problems += 1
1880 1897
1881 1898 # check username
1882 1899 username = None
1883 1900 err = None
1884 1901 try:
1885 1902 username = ui.username()
1886 1903 except error.Abort as e:
1887 1904 err = e.message
1888 1905 problems += 1
1889 1906
1890 1907 fm.condwrite(
1891 1908 username, b'username', _(b"checking username (%s)\n"), username
1892 1909 )
1893 1910 fm.condwrite(
1894 1911 err,
1895 1912 b'usernameerror',
1896 1913 _(
1897 1914 b"checking username...\n %s\n"
1898 1915 b" (specify a username in your configuration file)\n"
1899 1916 ),
1900 1917 err,
1901 1918 )
1902 1919
1903 1920 for name, mod in extensions.extensions():
1904 1921 handler = getattr(mod, 'debuginstall', None)
1905 1922 if handler is not None:
1906 1923 problems += handler(ui, fm)
1907 1924
1908 1925 fm.condwrite(not problems, b'', _(b"no problems detected\n"))
1909 1926 if not problems:
1910 1927 fm.data(problems=problems)
1911 1928 fm.condwrite(
1912 1929 problems,
1913 1930 b'problems',
1914 1931 _(b"%d problems detected, please check your install!\n"),
1915 1932 problems,
1916 1933 )
1917 1934 fm.end()
1918 1935
1919 1936 return problems
1920 1937
1921 1938
1922 1939 @command(b'debugknown', [], _(b'REPO ID...'), norepo=True)
1923 1940 def debugknown(ui, repopath, *ids, **opts):
1924 1941 """test whether node ids are known to a repo
1925 1942
1926 1943 Every ID must be a full-length hex node id string. Returns a list of 0s
1927 1944 and 1s indicating unknown/known.
1928 1945 """
1929 1946 opts = pycompat.byteskwargs(opts)
1930 1947 repo = hg.peer(ui, opts, repopath)
1931 1948 if not repo.capable(b'known'):
1932 1949 raise error.Abort(b"known() not supported by target repository")
1933 1950 flags = repo.known([bin(s) for s in ids])
1934 1951 ui.write(b"%s\n" % (b"".join([f and b"1" or b"0" for f in flags])))
1935 1952
1936 1953
1937 1954 @command(b'debuglabelcomplete', [], _(b'LABEL...'))
1938 1955 def debuglabelcomplete(ui, repo, *args):
1939 1956 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
1940 1957 debugnamecomplete(ui, repo, *args)
1941 1958
1942 1959
1943 1960 @command(
1944 1961 b'debuglocks',
1945 1962 [
1946 1963 (b'L', b'force-free-lock', None, _(b'free the store lock (DANGEROUS)')),
1947 1964 (
1948 1965 b'W',
1949 1966 b'force-free-wlock',
1950 1967 None,
1951 1968 _(b'free the working state lock (DANGEROUS)'),
1952 1969 ),
1953 1970 (b's', b'set-lock', None, _(b'set the store lock until stopped')),
1954 1971 (
1955 1972 b'S',
1956 1973 b'set-wlock',
1957 1974 None,
1958 1975 _(b'set the working state lock until stopped'),
1959 1976 ),
1960 1977 ],
1961 1978 _(b'[OPTION]...'),
1962 1979 )
1963 1980 def debuglocks(ui, repo, **opts):
1964 1981 """show or modify state of locks
1965 1982
1966 1983 By default, this command will show which locks are held. This
1967 1984 includes the user and process holding the lock, the amount of time
1968 1985 the lock has been held, and the machine name where the process is
1969 1986 running if it's not local.
1970 1987
1971 1988 Locks protect the integrity of Mercurial's data, so should be
1972 1989 treated with care. System crashes or other interruptions may cause
1973 1990 locks to not be properly released, though Mercurial will usually
1974 1991 detect and remove such stale locks automatically.
1975 1992
1976 1993 However, detecting stale locks may not always be possible (for
1977 1994 instance, on a shared filesystem). Removing locks may also be
1978 1995 blocked by filesystem permissions.
1979 1996
1980 1997 Setting a lock will prevent other commands from changing the data.
1981 1998 The command will wait until an interruption (SIGINT, SIGTERM, ...) occurs.
1982 1999 The set locks are removed when the command exits.
1983 2000
1984 2001 Returns 0 if no locks are held.
1985 2002
1986 2003 """
1987 2004
1988 2005 if opts.get('force_free_lock'):
1989 2006 repo.svfs.unlink(b'lock')
1990 2007 if opts.get('force_free_wlock'):
1991 2008 repo.vfs.unlink(b'wlock')
1992 2009 if opts.get('force_free_lock') or opts.get('force_free_wlock'):
1993 2010 return 0
1994 2011
1995 2012 locks = []
1996 2013 try:
1997 2014 if opts.get('set_wlock'):
1998 2015 try:
1999 2016 locks.append(repo.wlock(False))
2000 2017 except error.LockHeld:
2001 2018 raise error.Abort(_(b'wlock is already held'))
2002 2019 if opts.get('set_lock'):
2003 2020 try:
2004 2021 locks.append(repo.lock(False))
2005 2022 except error.LockHeld:
2006 2023 raise error.Abort(_(b'lock is already held'))
2007 2024 if len(locks):
2008 2025 ui.promptchoice(_(b"ready to release the lock (y)? $$ &Yes"))
2009 2026 return 0
2010 2027 finally:
2011 2028 release(*locks)
2012 2029
2013 2030 now = time.time()
2014 2031 held = 0
2015 2032
2016 2033 def report(vfs, name, method):
2017 2034 # this causes stale locks to get reaped for more accurate reporting
2018 2035 try:
2019 2036 l = method(False)
2020 2037 except error.LockHeld:
2021 2038 l = None
2022 2039
2023 2040 if l:
2024 2041 l.release()
2025 2042 else:
2026 2043 try:
2027 2044 st = vfs.lstat(name)
2028 2045 age = now - st[stat.ST_MTIME]
2029 2046 user = util.username(st.st_uid)
2030 2047 locker = vfs.readlock(name)
2031 2048 if b":" in locker:
2032 2049 host, pid = locker.split(b':')
2033 2050 if host == socket.gethostname():
2034 2051 locker = b'user %s, process %s' % (user or b'None', pid)
2035 2052 else:
2036 2053 locker = b'user %s, process %s, host %s' % (
2037 2054 user or b'None',
2038 2055 pid,
2039 2056 host,
2040 2057 )
2041 2058 ui.writenoi18n(b"%-6s %s (%ds)\n" % (name + b":", locker, age))
2042 2059 return 1
2043 2060 except OSError as e:
2044 2061 if e.errno != errno.ENOENT:
2045 2062 raise
2046 2063
2047 2064 ui.writenoi18n(b"%-6s free\n" % (name + b":"))
2048 2065 return 0
2049 2066
2050 2067 held += report(repo.svfs, b"lock", repo.lock)
2051 2068 held += report(repo.vfs, b"wlock", repo.wlock)
2052 2069
2053 2070 return held
2054 2071
2055 2072
2056 2073 @command(
2057 2074 b'debugmanifestfulltextcache',
2058 2075 [
2059 2076 (b'', b'clear', False, _(b'clear the cache')),
2060 2077 (
2061 2078 b'a',
2062 2079 b'add',
2063 2080 [],
2064 2081 _(b'add the given manifest nodes to the cache'),
2065 2082 _(b'NODE'),
2066 2083 ),
2067 2084 ],
2068 2085 b'',
2069 2086 )
2070 2087 def debugmanifestfulltextcache(ui, repo, add=(), **opts):
2071 2088 """show, clear or amend the contents of the manifest fulltext cache"""
2072 2089
2073 2090 def getcache():
2074 2091 r = repo.manifestlog.getstorage(b'')
2075 2092 try:
2076 2093 return r._fulltextcache
2077 2094 except AttributeError:
2078 2095 msg = _(
2079 2096 b"Current revlog implementation doesn't appear to have a "
2080 2097 b"manifest fulltext cache\n"
2081 2098 )
2082 2099 raise error.Abort(msg)
2083 2100
2084 2101 if opts.get('clear'):
2085 2102 with repo.wlock():
2086 2103 cache = getcache()
2087 2104 cache.clear(clear_persisted_data=True)
2088 2105 return
2089 2106
2090 2107 if add:
2091 2108 with repo.wlock():
2092 2109 m = repo.manifestlog
2093 2110 store = m.getstorage(b'')
2094 2111 for n in add:
2095 2112 try:
2096 2113 manifest = m[store.lookup(n)]
2097 2114 except error.LookupError as e:
2098 2115 raise error.Abort(e, hint=b"Check your manifest node id")
2099 2116 manifest.read() # stores revisision in cache too
2100 2117 return
2101 2118
2102 2119 cache = getcache()
2103 2120 if not len(cache):
2104 2121 ui.write(_(b'cache empty\n'))
2105 2122 else:
2106 2123 ui.write(
2107 2124 _(
2108 2125 b'cache contains %d manifest entries, in order of most to '
2109 2126 b'least recent:\n'
2110 2127 )
2111 2128 % (len(cache),)
2112 2129 )
2113 2130 totalsize = 0
2114 2131 for nodeid in cache:
2115 2132 # Use cache.get to not update the LRU order
2116 2133 data = cache.peek(nodeid)
2117 2134 size = len(data)
2118 2135 totalsize += size + 24 # 20 bytes nodeid, 4 bytes size
2119 2136 ui.write(
2120 2137 _(b'id: %s, size %s\n') % (hex(nodeid), util.bytecount(size))
2121 2138 )
2122 2139 ondisk = cache._opener.stat(b'manifestfulltextcache').st_size
2123 2140 ui.write(
2124 2141 _(b'total cache data size %s, on-disk %s\n')
2125 2142 % (util.bytecount(totalsize), util.bytecount(ondisk))
2126 2143 )
2127 2144
2128 2145
2129 2146 @command(b'debugmergestate', [] + cmdutil.templateopts, b'')
2130 2147 def debugmergestate(ui, repo, *args, **opts):
2131 2148 """print merge state
2132 2149
2133 2150 Use --verbose to print out information about whether v1 or v2 merge state
2134 2151 was chosen."""
2135 2152
2136 2153 if ui.verbose:
2137 2154 ms = mergestatemod.mergestate(repo)
2138 2155
2139 2156 # sort so that reasonable information is on top
2140 2157 v1records = ms._readrecordsv1()
2141 2158 v2records = ms._readrecordsv2()
2142 2159
2143 2160 if not v1records and not v2records:
2144 2161 pass
2145 2162 elif not v2records:
2146 2163 ui.writenoi18n(b'no version 2 merge state\n')
2147 2164 elif ms._v1v2match(v1records, v2records):
2148 2165 ui.writenoi18n(b'v1 and v2 states match: using v2\n')
2149 2166 else:
2150 2167 ui.writenoi18n(b'v1 and v2 states mismatch: using v1\n')
2151 2168
2152 2169 opts = pycompat.byteskwargs(opts)
2153 2170 if not opts[b'template']:
2154 2171 opts[b'template'] = (
2155 2172 b'{if(commits, "", "no merge state found\n")}'
2156 2173 b'{commits % "{name}{if(label, " ({label})")}: {node}\n"}'
2157 2174 b'{files % "file: {path} (state \\"{state}\\")\n'
2158 2175 b'{if(local_path, "'
2159 2176 b' local path: {local_path} (hash {local_key}, flags \\"{local_flags}\\")\n'
2160 2177 b' ancestor path: {ancestor_path} (node {ancestor_node})\n'
2161 2178 b' other path: {other_path} (node {other_node})\n'
2162 2179 b'")}'
2163 2180 b'{if(rename_side, "'
2164 2181 b' rename side: {rename_side}\n'
2165 2182 b' renamed path: {renamed_path}\n'
2166 2183 b'")}'
2167 2184 b'{extras % " extra: {key} = {value}\n"}'
2168 2185 b'"}'
2169 2186 b'{extras % "extra: {file} ({key} = {value})\n"}'
2170 2187 )
2171 2188
2172 2189 ms = mergestatemod.mergestate.read(repo)
2173 2190
2174 2191 fm = ui.formatter(b'debugmergestate', opts)
2175 2192 fm.startitem()
2176 2193
2177 2194 fm_commits = fm.nested(b'commits')
2178 2195 if ms.active():
2179 2196 for name, node, label_index in (
2180 2197 (b'local', ms.local, 0),
2181 2198 (b'other', ms.other, 1),
2182 2199 ):
2183 2200 fm_commits.startitem()
2184 2201 fm_commits.data(name=name)
2185 2202 fm_commits.data(node=hex(node))
2186 2203 if ms._labels and len(ms._labels) > label_index:
2187 2204 fm_commits.data(label=ms._labels[label_index])
2188 2205 fm_commits.end()
2189 2206
2190 2207 fm_files = fm.nested(b'files')
2191 2208 if ms.active():
2192 2209 for f in ms:
2193 2210 fm_files.startitem()
2194 2211 fm_files.data(path=f)
2195 2212 state = ms._state[f]
2196 2213 fm_files.data(state=state[0])
2197 2214 if state[0] in (
2198 2215 mergestatemod.MERGE_RECORD_UNRESOLVED,
2199 2216 mergestatemod.MERGE_RECORD_RESOLVED,
2200 2217 ):
2201 2218 fm_files.data(local_key=state[1])
2202 2219 fm_files.data(local_path=state[2])
2203 2220 fm_files.data(ancestor_path=state[3])
2204 2221 fm_files.data(ancestor_node=state[4])
2205 2222 fm_files.data(other_path=state[5])
2206 2223 fm_files.data(other_node=state[6])
2207 2224 fm_files.data(local_flags=state[7])
2208 2225 elif state[0] in (
2209 2226 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH,
2210 2227 mergestatemod.MERGE_RECORD_RESOLVED_PATH,
2211 2228 ):
2212 2229 fm_files.data(renamed_path=state[1])
2213 2230 fm_files.data(rename_side=state[2])
2214 2231 fm_extras = fm_files.nested(b'extras')
2215 2232 for k, v in sorted(ms.extras(f).items()):
2216 2233 fm_extras.startitem()
2217 2234 fm_extras.data(key=k)
2218 2235 fm_extras.data(value=v)
2219 2236 fm_extras.end()
2220 2237
2221 2238 fm_files.end()
2222 2239
2223 2240 fm_extras = fm.nested(b'extras')
2224 2241 for f, d in sorted(pycompat.iteritems(ms.allextras())):
2225 2242 if f in ms:
2226 2243 # If file is in mergestate, we have already processed it's extras
2227 2244 continue
2228 2245 for k, v in pycompat.iteritems(d):
2229 2246 fm_extras.startitem()
2230 2247 fm_extras.data(file=f)
2231 2248 fm_extras.data(key=k)
2232 2249 fm_extras.data(value=v)
2233 2250 fm_extras.end()
2234 2251
2235 2252 fm.end()
2236 2253
2237 2254
2238 2255 @command(b'debugnamecomplete', [], _(b'NAME...'))
2239 2256 def debugnamecomplete(ui, repo, *args):
2240 2257 '''complete "names" - tags, open branch names, bookmark names'''
2241 2258
2242 2259 names = set()
2243 2260 # since we previously only listed open branches, we will handle that
2244 2261 # specially (after this for loop)
2245 2262 for name, ns in pycompat.iteritems(repo.names):
2246 2263 if name != b'branches':
2247 2264 names.update(ns.listnames(repo))
2248 2265 names.update(
2249 2266 tag
2250 2267 for (tag, heads, tip, closed) in repo.branchmap().iterbranches()
2251 2268 if not closed
2252 2269 )
2253 2270 completions = set()
2254 2271 if not args:
2255 2272 args = [b'']
2256 2273 for a in args:
2257 2274 completions.update(n for n in names if n.startswith(a))
2258 2275 ui.write(b'\n'.join(sorted(completions)))
2259 2276 ui.write(b'\n')
2260 2277
2261 2278
2262 2279 @command(
2263 2280 b'debugnodemap',
2264 2281 [
2265 2282 (
2266 2283 b'',
2267 2284 b'dump-new',
2268 2285 False,
2269 2286 _(b'write a (new) persistent binary nodemap on stdout'),
2270 2287 ),
2271 2288 (b'', b'dump-disk', False, _(b'dump on-disk data on stdout')),
2272 2289 (
2273 2290 b'',
2274 2291 b'check',
2275 2292 False,
2276 2293 _(b'check that the data on disk data are correct.'),
2277 2294 ),
2278 2295 (
2279 2296 b'',
2280 2297 b'metadata',
2281 2298 False,
2282 2299 _(b'display the on disk meta data for the nodemap'),
2283 2300 ),
2284 2301 ],
2285 2302 )
2286 2303 def debugnodemap(ui, repo, **opts):
2287 2304 """write and inspect on disk nodemap"""
2288 2305 if opts['dump_new']:
2289 2306 unfi = repo.unfiltered()
2290 2307 cl = unfi.changelog
2291 2308 if util.safehasattr(cl.index, "nodemap_data_all"):
2292 2309 data = cl.index.nodemap_data_all()
2293 2310 else:
2294 2311 data = nodemap.persistent_data(cl.index)
2295 2312 ui.write(data)
2296 2313 elif opts['dump_disk']:
2297 2314 unfi = repo.unfiltered()
2298 2315 cl = unfi.changelog
2299 2316 nm_data = nodemap.persisted_data(cl)
2300 2317 if nm_data is not None:
2301 2318 docket, data = nm_data
2302 2319 ui.write(data[:])
2303 2320 elif opts['check']:
2304 2321 unfi = repo.unfiltered()
2305 2322 cl = unfi.changelog
2306 2323 nm_data = nodemap.persisted_data(cl)
2307 2324 if nm_data is not None:
2308 2325 docket, data = nm_data
2309 2326 return nodemap.check_data(ui, cl.index, data)
2310 2327 elif opts['metadata']:
2311 2328 unfi = repo.unfiltered()
2312 2329 cl = unfi.changelog
2313 2330 nm_data = nodemap.persisted_data(cl)
2314 2331 if nm_data is not None:
2315 2332 docket, data = nm_data
2316 2333 ui.write((b"uid: %s\n") % docket.uid)
2317 2334 ui.write((b"tip-rev: %d\n") % docket.tip_rev)
2318 2335 ui.write((b"tip-node: %s\n") % hex(docket.tip_node))
2319 2336 ui.write((b"data-length: %d\n") % docket.data_length)
2320 2337 ui.write((b"data-unused: %d\n") % docket.data_unused)
2321 2338 unused_perc = docket.data_unused * 100.0 / docket.data_length
2322 2339 ui.write((b"data-unused: %2.3f%%\n") % unused_perc)
2323 2340
2324 2341
2325 2342 @command(
2326 2343 b'debugobsolete',
2327 2344 [
2328 2345 (b'', b'flags', 0, _(b'markers flag')),
2329 2346 (
2330 2347 b'',
2331 2348 b'record-parents',
2332 2349 False,
2333 2350 _(b'record parent information for the precursor'),
2334 2351 ),
2335 2352 (b'r', b'rev', [], _(b'display markers relevant to REV')),
2336 2353 (
2337 2354 b'',
2338 2355 b'exclusive',
2339 2356 False,
2340 2357 _(b'restrict display to markers only relevant to REV'),
2341 2358 ),
2342 2359 (b'', b'index', False, _(b'display index of the marker')),
2343 2360 (b'', b'delete', [], _(b'delete markers specified by indices')),
2344 2361 ]
2345 2362 + cmdutil.commitopts2
2346 2363 + cmdutil.formatteropts,
2347 2364 _(b'[OBSOLETED [REPLACEMENT ...]]'),
2348 2365 )
2349 2366 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2350 2367 """create arbitrary obsolete marker
2351 2368
2352 2369 With no arguments, displays the list of obsolescence markers."""
2353 2370
2354 2371 opts = pycompat.byteskwargs(opts)
2355 2372
2356 2373 def parsenodeid(s):
2357 2374 try:
2358 2375 # We do not use revsingle/revrange functions here to accept
2359 2376 # arbitrary node identifiers, possibly not present in the
2360 2377 # local repository.
2361 2378 n = bin(s)
2362 2379 if len(n) != len(nullid):
2363 2380 raise TypeError()
2364 2381 return n
2365 2382 except TypeError:
2366 2383 raise error.InputError(
2367 2384 b'changeset references must be full hexadecimal '
2368 2385 b'node identifiers'
2369 2386 )
2370 2387
2371 2388 if opts.get(b'delete'):
2372 2389 indices = []
2373 2390 for v in opts.get(b'delete'):
2374 2391 try:
2375 2392 indices.append(int(v))
2376 2393 except ValueError:
2377 2394 raise error.InputError(
2378 2395 _(b'invalid index value: %r') % v,
2379 2396 hint=_(b'use integers for indices'),
2380 2397 )
2381 2398
2382 2399 if repo.currenttransaction():
2383 2400 raise error.Abort(
2384 2401 _(b'cannot delete obsmarkers in the middle of transaction.')
2385 2402 )
2386 2403
2387 2404 with repo.lock():
2388 2405 n = repair.deleteobsmarkers(repo.obsstore, indices)
2389 2406 ui.write(_(b'deleted %i obsolescence markers\n') % n)
2390 2407
2391 2408 return
2392 2409
2393 2410 if precursor is not None:
2394 2411 if opts[b'rev']:
2395 2412 raise error.InputError(
2396 2413 b'cannot select revision when creating marker'
2397 2414 )
2398 2415 metadata = {}
2399 2416 metadata[b'user'] = encoding.fromlocal(opts[b'user'] or ui.username())
2400 2417 succs = tuple(parsenodeid(succ) for succ in successors)
2401 2418 l = repo.lock()
2402 2419 try:
2403 2420 tr = repo.transaction(b'debugobsolete')
2404 2421 try:
2405 2422 date = opts.get(b'date')
2406 2423 if date:
2407 2424 date = dateutil.parsedate(date)
2408 2425 else:
2409 2426 date = None
2410 2427 prec = parsenodeid(precursor)
2411 2428 parents = None
2412 2429 if opts[b'record_parents']:
2413 2430 if prec not in repo.unfiltered():
2414 2431 raise error.Abort(
2415 2432 b'cannot used --record-parents on '
2416 2433 b'unknown changesets'
2417 2434 )
2418 2435 parents = repo.unfiltered()[prec].parents()
2419 2436 parents = tuple(p.node() for p in parents)
2420 2437 repo.obsstore.create(
2421 2438 tr,
2422 2439 prec,
2423 2440 succs,
2424 2441 opts[b'flags'],
2425 2442 parents=parents,
2426 2443 date=date,
2427 2444 metadata=metadata,
2428 2445 ui=ui,
2429 2446 )
2430 2447 tr.close()
2431 2448 except ValueError as exc:
2432 2449 raise error.Abort(
2433 2450 _(b'bad obsmarker input: %s') % pycompat.bytestr(exc)
2434 2451 )
2435 2452 finally:
2436 2453 tr.release()
2437 2454 finally:
2438 2455 l.release()
2439 2456 else:
2440 2457 if opts[b'rev']:
2441 2458 revs = scmutil.revrange(repo, opts[b'rev'])
2442 2459 nodes = [repo[r].node() for r in revs]
2443 2460 markers = list(
2444 2461 obsutil.getmarkers(
2445 2462 repo, nodes=nodes, exclusive=opts[b'exclusive']
2446 2463 )
2447 2464 )
2448 2465 markers.sort(key=lambda x: x._data)
2449 2466 else:
2450 2467 markers = obsutil.getmarkers(repo)
2451 2468
2452 2469 markerstoiter = markers
2453 2470 isrelevant = lambda m: True
2454 2471 if opts.get(b'rev') and opts.get(b'index'):
2455 2472 markerstoiter = obsutil.getmarkers(repo)
2456 2473 markerset = set(markers)
2457 2474 isrelevant = lambda m: m in markerset
2458 2475
2459 2476 fm = ui.formatter(b'debugobsolete', opts)
2460 2477 for i, m in enumerate(markerstoiter):
2461 2478 if not isrelevant(m):
2462 2479 # marker can be irrelevant when we're iterating over a set
2463 2480 # of markers (markerstoiter) which is bigger than the set
2464 2481 # of markers we want to display (markers)
2465 2482 # this can happen if both --index and --rev options are
2466 2483 # provided and thus we need to iterate over all of the markers
2467 2484 # to get the correct indices, but only display the ones that
2468 2485 # are relevant to --rev value
2469 2486 continue
2470 2487 fm.startitem()
2471 2488 ind = i if opts.get(b'index') else None
2472 2489 cmdutil.showmarker(fm, m, index=ind)
2473 2490 fm.end()
2474 2491
2475 2492
2476 2493 @command(
2477 2494 b'debugp1copies',
2478 2495 [(b'r', b'rev', b'', _(b'revision to debug'), _(b'REV'))],
2479 2496 _(b'[-r REV]'),
2480 2497 )
2481 2498 def debugp1copies(ui, repo, **opts):
2482 2499 """dump copy information compared to p1"""
2483 2500
2484 2501 opts = pycompat.byteskwargs(opts)
2485 2502 ctx = scmutil.revsingle(repo, opts.get(b'rev'), default=None)
2486 2503 for dst, src in ctx.p1copies().items():
2487 2504 ui.write(b'%s -> %s\n' % (src, dst))
2488 2505
2489 2506
2490 2507 @command(
2491 2508 b'debugp2copies',
2492 2509 [(b'r', b'rev', b'', _(b'revision to debug'), _(b'REV'))],
2493 2510 _(b'[-r REV]'),
2494 2511 )
2495 2512 def debugp1copies(ui, repo, **opts):
2496 2513 """dump copy information compared to p2"""
2497 2514
2498 2515 opts = pycompat.byteskwargs(opts)
2499 2516 ctx = scmutil.revsingle(repo, opts.get(b'rev'), default=None)
2500 2517 for dst, src in ctx.p2copies().items():
2501 2518 ui.write(b'%s -> %s\n' % (src, dst))
2502 2519
2503 2520
2504 2521 @command(
2505 2522 b'debugpathcomplete',
2506 2523 [
2507 2524 (b'f', b'full', None, _(b'complete an entire path')),
2508 2525 (b'n', b'normal', None, _(b'show only normal files')),
2509 2526 (b'a', b'added', None, _(b'show only added files')),
2510 2527 (b'r', b'removed', None, _(b'show only removed files')),
2511 2528 ],
2512 2529 _(b'FILESPEC...'),
2513 2530 )
2514 2531 def debugpathcomplete(ui, repo, *specs, **opts):
2515 2532 """complete part or all of a tracked path
2516 2533
2517 2534 This command supports shells that offer path name completion. It
2518 2535 currently completes only files already known to the dirstate.
2519 2536
2520 2537 Completion extends only to the next path segment unless
2521 2538 --full is specified, in which case entire paths are used."""
2522 2539
2523 2540 def complete(path, acceptable):
2524 2541 dirstate = repo.dirstate
2525 2542 spec = os.path.normpath(os.path.join(encoding.getcwd(), path))
2526 2543 rootdir = repo.root + pycompat.ossep
2527 2544 if spec != repo.root and not spec.startswith(rootdir):
2528 2545 return [], []
2529 2546 if os.path.isdir(spec):
2530 2547 spec += b'/'
2531 2548 spec = spec[len(rootdir) :]
2532 2549 fixpaths = pycompat.ossep != b'/'
2533 2550 if fixpaths:
2534 2551 spec = spec.replace(pycompat.ossep, b'/')
2535 2552 speclen = len(spec)
2536 2553 fullpaths = opts['full']
2537 2554 files, dirs = set(), set()
2538 2555 adddir, addfile = dirs.add, files.add
2539 2556 for f, st in pycompat.iteritems(dirstate):
2540 2557 if f.startswith(spec) and st[0] in acceptable:
2541 2558 if fixpaths:
2542 2559 f = f.replace(b'/', pycompat.ossep)
2543 2560 if fullpaths:
2544 2561 addfile(f)
2545 2562 continue
2546 2563 s = f.find(pycompat.ossep, speclen)
2547 2564 if s >= 0:
2548 2565 adddir(f[:s])
2549 2566 else:
2550 2567 addfile(f)
2551 2568 return files, dirs
2552 2569
2553 2570 acceptable = b''
2554 2571 if opts['normal']:
2555 2572 acceptable += b'nm'
2556 2573 if opts['added']:
2557 2574 acceptable += b'a'
2558 2575 if opts['removed']:
2559 2576 acceptable += b'r'
2560 2577 cwd = repo.getcwd()
2561 2578 if not specs:
2562 2579 specs = [b'.']
2563 2580
2564 2581 files, dirs = set(), set()
2565 2582 for spec in specs:
2566 2583 f, d = complete(spec, acceptable or b'nmar')
2567 2584 files.update(f)
2568 2585 dirs.update(d)
2569 2586 files.update(dirs)
2570 2587 ui.write(b'\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
2571 2588 ui.write(b'\n')
2572 2589
2573 2590
2574 2591 @command(
2575 2592 b'debugpathcopies',
2576 2593 cmdutil.walkopts,
2577 2594 b'hg debugpathcopies REV1 REV2 [FILE]',
2578 2595 inferrepo=True,
2579 2596 )
2580 2597 def debugpathcopies(ui, repo, rev1, rev2, *pats, **opts):
2581 2598 """show copies between two revisions"""
2582 2599 ctx1 = scmutil.revsingle(repo, rev1)
2583 2600 ctx2 = scmutil.revsingle(repo, rev2)
2584 2601 m = scmutil.match(ctx1, pats, opts)
2585 2602 for dst, src in sorted(copies.pathcopies(ctx1, ctx2, m).items()):
2586 2603 ui.write(b'%s -> %s\n' % (src, dst))
2587 2604
2588 2605
2589 2606 @command(b'debugpeer', [], _(b'PATH'), norepo=True)
2590 2607 def debugpeer(ui, path):
2591 2608 """establish a connection to a peer repository"""
2592 2609 # Always enable peer request logging. Requires --debug to display
2593 2610 # though.
2594 2611 overrides = {
2595 2612 (b'devel', b'debug.peer-request'): True,
2596 2613 }
2597 2614
2598 2615 with ui.configoverride(overrides):
2599 2616 peer = hg.peer(ui, {}, path)
2600 2617
2601 2618 local = peer.local() is not None
2602 2619 canpush = peer.canpush()
2603 2620
2604 2621 ui.write(_(b'url: %s\n') % peer.url())
2605 2622 ui.write(_(b'local: %s\n') % (_(b'yes') if local else _(b'no')))
2606 2623 ui.write(_(b'pushable: %s\n') % (_(b'yes') if canpush else _(b'no')))
2607 2624
2608 2625
2609 2626 @command(
2610 2627 b'debugpickmergetool',
2611 2628 [
2612 2629 (b'r', b'rev', b'', _(b'check for files in this revision'), _(b'REV')),
2613 2630 (b'', b'changedelete', None, _(b'emulate merging change and delete')),
2614 2631 ]
2615 2632 + cmdutil.walkopts
2616 2633 + cmdutil.mergetoolopts,
2617 2634 _(b'[PATTERN]...'),
2618 2635 inferrepo=True,
2619 2636 )
2620 2637 def debugpickmergetool(ui, repo, *pats, **opts):
2621 2638 """examine which merge tool is chosen for specified file
2622 2639
2623 2640 As described in :hg:`help merge-tools`, Mercurial examines
2624 2641 configurations below in this order to decide which merge tool is
2625 2642 chosen for specified file.
2626 2643
2627 2644 1. ``--tool`` option
2628 2645 2. ``HGMERGE`` environment variable
2629 2646 3. configurations in ``merge-patterns`` section
2630 2647 4. configuration of ``ui.merge``
2631 2648 5. configurations in ``merge-tools`` section
2632 2649 6. ``hgmerge`` tool (for historical reason only)
2633 2650 7. default tool for fallback (``:merge`` or ``:prompt``)
2634 2651
2635 2652 This command writes out examination result in the style below::
2636 2653
2637 2654 FILE = MERGETOOL
2638 2655
2639 2656 By default, all files known in the first parent context of the
2640 2657 working directory are examined. Use file patterns and/or -I/-X
2641 2658 options to limit target files. -r/--rev is also useful to examine
2642 2659 files in another context without actual updating to it.
2643 2660
2644 2661 With --debug, this command shows warning messages while matching
2645 2662 against ``merge-patterns`` and so on, too. It is recommended to
2646 2663 use this option with explicit file patterns and/or -I/-X options,
2647 2664 because this option increases amount of output per file according
2648 2665 to configurations in hgrc.
2649 2666
2650 2667 With -v/--verbose, this command shows configurations below at
2651 2668 first (only if specified).
2652 2669
2653 2670 - ``--tool`` option
2654 2671 - ``HGMERGE`` environment variable
2655 2672 - configuration of ``ui.merge``
2656 2673
2657 2674 If merge tool is chosen before matching against
2658 2675 ``merge-patterns``, this command can't show any helpful
2659 2676 information, even with --debug. In such case, information above is
2660 2677 useful to know why a merge tool is chosen.
2661 2678 """
2662 2679 opts = pycompat.byteskwargs(opts)
2663 2680 overrides = {}
2664 2681 if opts[b'tool']:
2665 2682 overrides[(b'ui', b'forcemerge')] = opts[b'tool']
2666 2683 ui.notenoi18n(b'with --tool %r\n' % (pycompat.bytestr(opts[b'tool'])))
2667 2684
2668 2685 with ui.configoverride(overrides, b'debugmergepatterns'):
2669 2686 hgmerge = encoding.environ.get(b"HGMERGE")
2670 2687 if hgmerge is not None:
2671 2688 ui.notenoi18n(b'with HGMERGE=%r\n' % (pycompat.bytestr(hgmerge)))
2672 2689 uimerge = ui.config(b"ui", b"merge")
2673 2690 if uimerge:
2674 2691 ui.notenoi18n(b'with ui.merge=%r\n' % (pycompat.bytestr(uimerge)))
2675 2692
2676 2693 ctx = scmutil.revsingle(repo, opts.get(b'rev'))
2677 2694 m = scmutil.match(ctx, pats, opts)
2678 2695 changedelete = opts[b'changedelete']
2679 2696 for path in ctx.walk(m):
2680 2697 fctx = ctx[path]
2681 2698 try:
2682 2699 if not ui.debugflag:
2683 2700 ui.pushbuffer(error=True)
2684 2701 tool, toolpath = filemerge._picktool(
2685 2702 repo,
2686 2703 ui,
2687 2704 path,
2688 2705 fctx.isbinary(),
2689 2706 b'l' in fctx.flags(),
2690 2707 changedelete,
2691 2708 )
2692 2709 finally:
2693 2710 if not ui.debugflag:
2694 2711 ui.popbuffer()
2695 2712 ui.write(b'%s = %s\n' % (path, tool))
2696 2713
2697 2714
2698 2715 @command(b'debugpushkey', [], _(b'REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
2699 2716 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2700 2717 """access the pushkey key/value protocol
2701 2718
2702 2719 With two args, list the keys in the given namespace.
2703 2720
2704 2721 With five args, set a key to new if it currently is set to old.
2705 2722 Reports success or failure.
2706 2723 """
2707 2724
2708 2725 target = hg.peer(ui, {}, repopath)
2709 2726 if keyinfo:
2710 2727 key, old, new = keyinfo
2711 2728 with target.commandexecutor() as e:
2712 2729 r = e.callcommand(
2713 2730 b'pushkey',
2714 2731 {
2715 2732 b'namespace': namespace,
2716 2733 b'key': key,
2717 2734 b'old': old,
2718 2735 b'new': new,
2719 2736 },
2720 2737 ).result()
2721 2738
2722 2739 ui.status(pycompat.bytestr(r) + b'\n')
2723 2740 return not r
2724 2741 else:
2725 2742 for k, v in sorted(pycompat.iteritems(target.listkeys(namespace))):
2726 2743 ui.write(
2727 2744 b"%s\t%s\n" % (stringutil.escapestr(k), stringutil.escapestr(v))
2728 2745 )
2729 2746
2730 2747
2731 2748 @command(b'debugpvec', [], _(b'A B'))
2732 2749 def debugpvec(ui, repo, a, b=None):
2733 2750 ca = scmutil.revsingle(repo, a)
2734 2751 cb = scmutil.revsingle(repo, b)
2735 2752 pa = pvec.ctxpvec(ca)
2736 2753 pb = pvec.ctxpvec(cb)
2737 2754 if pa == pb:
2738 2755 rel = b"="
2739 2756 elif pa > pb:
2740 2757 rel = b">"
2741 2758 elif pa < pb:
2742 2759 rel = b"<"
2743 2760 elif pa | pb:
2744 2761 rel = b"|"
2745 2762 ui.write(_(b"a: %s\n") % pa)
2746 2763 ui.write(_(b"b: %s\n") % pb)
2747 2764 ui.write(_(b"depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2748 2765 ui.write(
2749 2766 _(b"delta: %d hdist: %d distance: %d relation: %s\n")
2750 2767 % (
2751 2768 abs(pa._depth - pb._depth),
2752 2769 pvec._hamming(pa._vec, pb._vec),
2753 2770 pa.distance(pb),
2754 2771 rel,
2755 2772 )
2756 2773 )
2757 2774
2758 2775
2759 2776 @command(
2760 2777 b'debugrebuilddirstate|debugrebuildstate',
2761 2778 [
2762 2779 (b'r', b'rev', b'', _(b'revision to rebuild to'), _(b'REV')),
2763 2780 (
2764 2781 b'',
2765 2782 b'minimal',
2766 2783 None,
2767 2784 _(
2768 2785 b'only rebuild files that are inconsistent with '
2769 2786 b'the working copy parent'
2770 2787 ),
2771 2788 ),
2772 2789 ],
2773 2790 _(b'[-r REV]'),
2774 2791 )
2775 2792 def debugrebuilddirstate(ui, repo, rev, **opts):
2776 2793 """rebuild the dirstate as it would look like for the given revision
2777 2794
2778 2795 If no revision is specified the first current parent will be used.
2779 2796
2780 2797 The dirstate will be set to the files of the given revision.
2781 2798 The actual working directory content or existing dirstate
2782 2799 information such as adds or removes is not considered.
2783 2800
2784 2801 ``minimal`` will only rebuild the dirstate status for files that claim to be
2785 2802 tracked but are not in the parent manifest, or that exist in the parent
2786 2803 manifest but are not in the dirstate. It will not change adds, removes, or
2787 2804 modified files that are in the working copy parent.
2788 2805
2789 2806 One use of this command is to make the next :hg:`status` invocation
2790 2807 check the actual file content.
2791 2808 """
2792 2809 ctx = scmutil.revsingle(repo, rev)
2793 2810 with repo.wlock():
2794 2811 dirstate = repo.dirstate
2795 2812 changedfiles = None
2796 2813 # See command doc for what minimal does.
2797 2814 if opts.get('minimal'):
2798 2815 manifestfiles = set(ctx.manifest().keys())
2799 2816 dirstatefiles = set(dirstate)
2800 2817 manifestonly = manifestfiles - dirstatefiles
2801 2818 dsonly = dirstatefiles - manifestfiles
2802 2819 dsnotadded = {f for f in dsonly if dirstate[f] != b'a'}
2803 2820 changedfiles = manifestonly | dsnotadded
2804 2821
2805 2822 dirstate.rebuild(ctx.node(), ctx.manifest(), changedfiles)
2806 2823
2807 2824
2808 2825 @command(b'debugrebuildfncache', [], b'')
2809 2826 def debugrebuildfncache(ui, repo):
2810 2827 """rebuild the fncache file"""
2811 2828 repair.rebuildfncache(ui, repo)
2812 2829
2813 2830
2814 2831 @command(
2815 2832 b'debugrename',
2816 2833 [(b'r', b'rev', b'', _(b'revision to debug'), _(b'REV'))],
2817 2834 _(b'[-r REV] [FILE]...'),
2818 2835 )
2819 2836 def debugrename(ui, repo, *pats, **opts):
2820 2837 """dump rename information"""
2821 2838
2822 2839 opts = pycompat.byteskwargs(opts)
2823 2840 ctx = scmutil.revsingle(repo, opts.get(b'rev'))
2824 2841 m = scmutil.match(ctx, pats, opts)
2825 2842 for abs in ctx.walk(m):
2826 2843 fctx = ctx[abs]
2827 2844 o = fctx.filelog().renamed(fctx.filenode())
2828 2845 rel = repo.pathto(abs)
2829 2846 if o:
2830 2847 ui.write(_(b"%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2831 2848 else:
2832 2849 ui.write(_(b"%s not renamed\n") % rel)
2833 2850
2834 2851
2835 2852 @command(b'debugrequires|debugrequirements', [], b'')
2836 2853 def debugrequirements(ui, repo):
2837 2854 """ print the current repo requirements """
2838 2855 for r in sorted(repo.requirements):
2839 2856 ui.write(b"%s\n" % r)
2840 2857
2841 2858
2842 2859 @command(
2843 2860 b'debugrevlog',
2844 2861 cmdutil.debugrevlogopts + [(b'd', b'dump', False, _(b'dump index data'))],
2845 2862 _(b'-c|-m|FILE'),
2846 2863 optionalrepo=True,
2847 2864 )
2848 2865 def debugrevlog(ui, repo, file_=None, **opts):
2849 2866 """show data and statistics about a revlog"""
2850 2867 opts = pycompat.byteskwargs(opts)
2851 2868 r = cmdutil.openrevlog(repo, b'debugrevlog', file_, opts)
2852 2869
2853 2870 if opts.get(b"dump"):
2854 2871 numrevs = len(r)
2855 2872 ui.write(
2856 2873 (
2857 2874 b"# rev p1rev p2rev start end deltastart base p1 p2"
2858 2875 b" rawsize totalsize compression heads chainlen\n"
2859 2876 )
2860 2877 )
2861 2878 ts = 0
2862 2879 heads = set()
2863 2880
2864 2881 for rev in pycompat.xrange(numrevs):
2865 2882 dbase = r.deltaparent(rev)
2866 2883 if dbase == -1:
2867 2884 dbase = rev
2868 2885 cbase = r.chainbase(rev)
2869 2886 clen = r.chainlen(rev)
2870 2887 p1, p2 = r.parentrevs(rev)
2871 2888 rs = r.rawsize(rev)
2872 2889 ts = ts + rs
2873 2890 heads -= set(r.parentrevs(rev))
2874 2891 heads.add(rev)
2875 2892 try:
2876 2893 compression = ts / r.end(rev)
2877 2894 except ZeroDivisionError:
2878 2895 compression = 0
2879 2896 ui.write(
2880 2897 b"%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
2881 2898 b"%11d %5d %8d\n"
2882 2899 % (
2883 2900 rev,
2884 2901 p1,
2885 2902 p2,
2886 2903 r.start(rev),
2887 2904 r.end(rev),
2888 2905 r.start(dbase),
2889 2906 r.start(cbase),
2890 2907 r.start(p1),
2891 2908 r.start(p2),
2892 2909 rs,
2893 2910 ts,
2894 2911 compression,
2895 2912 len(heads),
2896 2913 clen,
2897 2914 )
2898 2915 )
2899 2916 return 0
2900 2917
2901 2918 v = r.version
2902 2919 format = v & 0xFFFF
2903 2920 flags = []
2904 2921 gdelta = False
2905 2922 if v & revlog.FLAG_INLINE_DATA:
2906 2923 flags.append(b'inline')
2907 2924 if v & revlog.FLAG_GENERALDELTA:
2908 2925 gdelta = True
2909 2926 flags.append(b'generaldelta')
2910 2927 if not flags:
2911 2928 flags = [b'(none)']
2912 2929
2913 2930 ### tracks merge vs single parent
2914 2931 nummerges = 0
2915 2932
2916 2933 ### tracks ways the "delta" are build
2917 2934 # nodelta
2918 2935 numempty = 0
2919 2936 numemptytext = 0
2920 2937 numemptydelta = 0
2921 2938 # full file content
2922 2939 numfull = 0
2923 2940 # intermediate snapshot against a prior snapshot
2924 2941 numsemi = 0
2925 2942 # snapshot count per depth
2926 2943 numsnapdepth = collections.defaultdict(lambda: 0)
2927 2944 # delta against previous revision
2928 2945 numprev = 0
2929 2946 # delta against first or second parent (not prev)
2930 2947 nump1 = 0
2931 2948 nump2 = 0
2932 2949 # delta against neither prev nor parents
2933 2950 numother = 0
2934 2951 # delta against prev that are also first or second parent
2935 2952 # (details of `numprev`)
2936 2953 nump1prev = 0
2937 2954 nump2prev = 0
2938 2955
2939 2956 # data about delta chain of each revs
2940 2957 chainlengths = []
2941 2958 chainbases = []
2942 2959 chainspans = []
2943 2960
2944 2961 # data about each revision
2945 2962 datasize = [None, 0, 0]
2946 2963 fullsize = [None, 0, 0]
2947 2964 semisize = [None, 0, 0]
2948 2965 # snapshot count per depth
2949 2966 snapsizedepth = collections.defaultdict(lambda: [None, 0, 0])
2950 2967 deltasize = [None, 0, 0]
2951 2968 chunktypecounts = {}
2952 2969 chunktypesizes = {}
2953 2970
2954 2971 def addsize(size, l):
2955 2972 if l[0] is None or size < l[0]:
2956 2973 l[0] = size
2957 2974 if size > l[1]:
2958 2975 l[1] = size
2959 2976 l[2] += size
2960 2977
2961 2978 numrevs = len(r)
2962 2979 for rev in pycompat.xrange(numrevs):
2963 2980 p1, p2 = r.parentrevs(rev)
2964 2981 delta = r.deltaparent(rev)
2965 2982 if format > 0:
2966 2983 addsize(r.rawsize(rev), datasize)
2967 2984 if p2 != nullrev:
2968 2985 nummerges += 1
2969 2986 size = r.length(rev)
2970 2987 if delta == nullrev:
2971 2988 chainlengths.append(0)
2972 2989 chainbases.append(r.start(rev))
2973 2990 chainspans.append(size)
2974 2991 if size == 0:
2975 2992 numempty += 1
2976 2993 numemptytext += 1
2977 2994 else:
2978 2995 numfull += 1
2979 2996 numsnapdepth[0] += 1
2980 2997 addsize(size, fullsize)
2981 2998 addsize(size, snapsizedepth[0])
2982 2999 else:
2983 3000 chainlengths.append(chainlengths[delta] + 1)
2984 3001 baseaddr = chainbases[delta]
2985 3002 revaddr = r.start(rev)
2986 3003 chainbases.append(baseaddr)
2987 3004 chainspans.append((revaddr - baseaddr) + size)
2988 3005 if size == 0:
2989 3006 numempty += 1
2990 3007 numemptydelta += 1
2991 3008 elif r.issnapshot(rev):
2992 3009 addsize(size, semisize)
2993 3010 numsemi += 1
2994 3011 depth = r.snapshotdepth(rev)
2995 3012 numsnapdepth[depth] += 1
2996 3013 addsize(size, snapsizedepth[depth])
2997 3014 else:
2998 3015 addsize(size, deltasize)
2999 3016 if delta == rev - 1:
3000 3017 numprev += 1
3001 3018 if delta == p1:
3002 3019 nump1prev += 1
3003 3020 elif delta == p2:
3004 3021 nump2prev += 1
3005 3022 elif delta == p1:
3006 3023 nump1 += 1
3007 3024 elif delta == p2:
3008 3025 nump2 += 1
3009 3026 elif delta != nullrev:
3010 3027 numother += 1
3011 3028
3012 3029 # Obtain data on the raw chunks in the revlog.
3013 3030 if util.safehasattr(r, b'_getsegmentforrevs'):
3014 3031 segment = r._getsegmentforrevs(rev, rev)[1]
3015 3032 else:
3016 3033 segment = r._revlog._getsegmentforrevs(rev, rev)[1]
3017 3034 if segment:
3018 3035 chunktype = bytes(segment[0:1])
3019 3036 else:
3020 3037 chunktype = b'empty'
3021 3038
3022 3039 if chunktype not in chunktypecounts:
3023 3040 chunktypecounts[chunktype] = 0
3024 3041 chunktypesizes[chunktype] = 0
3025 3042
3026 3043 chunktypecounts[chunktype] += 1
3027 3044 chunktypesizes[chunktype] += size
3028 3045
3029 3046 # Adjust size min value for empty cases
3030 3047 for size in (datasize, fullsize, semisize, deltasize):
3031 3048 if size[0] is None:
3032 3049 size[0] = 0
3033 3050
3034 3051 numdeltas = numrevs - numfull - numempty - numsemi
3035 3052 numoprev = numprev - nump1prev - nump2prev
3036 3053 totalrawsize = datasize[2]
3037 3054 datasize[2] /= numrevs
3038 3055 fulltotal = fullsize[2]
3039 3056 if numfull == 0:
3040 3057 fullsize[2] = 0
3041 3058 else:
3042 3059 fullsize[2] /= numfull
3043 3060 semitotal = semisize[2]
3044 3061 snaptotal = {}
3045 3062 if numsemi > 0:
3046 3063 semisize[2] /= numsemi
3047 3064 for depth in snapsizedepth:
3048 3065 snaptotal[depth] = snapsizedepth[depth][2]
3049 3066 snapsizedepth[depth][2] /= numsnapdepth[depth]
3050 3067
3051 3068 deltatotal = deltasize[2]
3052 3069 if numdeltas > 0:
3053 3070 deltasize[2] /= numdeltas
3054 3071 totalsize = fulltotal + semitotal + deltatotal
3055 3072 avgchainlen = sum(chainlengths) / numrevs
3056 3073 maxchainlen = max(chainlengths)
3057 3074 maxchainspan = max(chainspans)
3058 3075 compratio = 1
3059 3076 if totalsize:
3060 3077 compratio = totalrawsize / totalsize
3061 3078
3062 3079 basedfmtstr = b'%%%dd\n'
3063 3080 basepcfmtstr = b'%%%dd %s(%%5.2f%%%%)\n'
3064 3081
3065 3082 def dfmtstr(max):
3066 3083 return basedfmtstr % len(str(max))
3067 3084
3068 3085 def pcfmtstr(max, padding=0):
3069 3086 return basepcfmtstr % (len(str(max)), b' ' * padding)
3070 3087
3071 3088 def pcfmt(value, total):
3072 3089 if total:
3073 3090 return (value, 100 * float(value) / total)
3074 3091 else:
3075 3092 return value, 100.0
3076 3093
3077 3094 ui.writenoi18n(b'format : %d\n' % format)
3078 3095 ui.writenoi18n(b'flags : %s\n' % b', '.join(flags))
3079 3096
3080 3097 ui.write(b'\n')
3081 3098 fmt = pcfmtstr(totalsize)
3082 3099 fmt2 = dfmtstr(totalsize)
3083 3100 ui.writenoi18n(b'revisions : ' + fmt2 % numrevs)
3084 3101 ui.writenoi18n(b' merges : ' + fmt % pcfmt(nummerges, numrevs))
3085 3102 ui.writenoi18n(
3086 3103 b' normal : ' + fmt % pcfmt(numrevs - nummerges, numrevs)
3087 3104 )
3088 3105 ui.writenoi18n(b'revisions : ' + fmt2 % numrevs)
3089 3106 ui.writenoi18n(b' empty : ' + fmt % pcfmt(numempty, numrevs))
3090 3107 ui.writenoi18n(
3091 3108 b' text : '
3092 3109 + fmt % pcfmt(numemptytext, numemptytext + numemptydelta)
3093 3110 )
3094 3111 ui.writenoi18n(
3095 3112 b' delta : '
3096 3113 + fmt % pcfmt(numemptydelta, numemptytext + numemptydelta)
3097 3114 )
3098 3115 ui.writenoi18n(
3099 3116 b' snapshot : ' + fmt % pcfmt(numfull + numsemi, numrevs)
3100 3117 )
3101 3118 for depth in sorted(numsnapdepth):
3102 3119 ui.write(
3103 3120 (b' lvl-%-3d : ' % depth)
3104 3121 + fmt % pcfmt(numsnapdepth[depth], numrevs)
3105 3122 )
3106 3123 ui.writenoi18n(b' deltas : ' + fmt % pcfmt(numdeltas, numrevs))
3107 3124 ui.writenoi18n(b'revision size : ' + fmt2 % totalsize)
3108 3125 ui.writenoi18n(
3109 3126 b' snapshot : ' + fmt % pcfmt(fulltotal + semitotal, totalsize)
3110 3127 )
3111 3128 for depth in sorted(numsnapdepth):
3112 3129 ui.write(
3113 3130 (b' lvl-%-3d : ' % depth)
3114 3131 + fmt % pcfmt(snaptotal[depth], totalsize)
3115 3132 )
3116 3133 ui.writenoi18n(b' deltas : ' + fmt % pcfmt(deltatotal, totalsize))
3117 3134
3118 3135 def fmtchunktype(chunktype):
3119 3136 if chunktype == b'empty':
3120 3137 return b' %s : ' % chunktype
3121 3138 elif chunktype in pycompat.bytestr(string.ascii_letters):
3122 3139 return b' 0x%s (%s) : ' % (hex(chunktype), chunktype)
3123 3140 else:
3124 3141 return b' 0x%s : ' % hex(chunktype)
3125 3142
3126 3143 ui.write(b'\n')
3127 3144 ui.writenoi18n(b'chunks : ' + fmt2 % numrevs)
3128 3145 for chunktype in sorted(chunktypecounts):
3129 3146 ui.write(fmtchunktype(chunktype))
3130 3147 ui.write(fmt % pcfmt(chunktypecounts[chunktype], numrevs))
3131 3148 ui.writenoi18n(b'chunks size : ' + fmt2 % totalsize)
3132 3149 for chunktype in sorted(chunktypecounts):
3133 3150 ui.write(fmtchunktype(chunktype))
3134 3151 ui.write(fmt % pcfmt(chunktypesizes[chunktype], totalsize))
3135 3152
3136 3153 ui.write(b'\n')
3137 3154 fmt = dfmtstr(max(avgchainlen, maxchainlen, maxchainspan, compratio))
3138 3155 ui.writenoi18n(b'avg chain length : ' + fmt % avgchainlen)
3139 3156 ui.writenoi18n(b'max chain length : ' + fmt % maxchainlen)
3140 3157 ui.writenoi18n(b'max chain reach : ' + fmt % maxchainspan)
3141 3158 ui.writenoi18n(b'compression ratio : ' + fmt % compratio)
3142 3159
3143 3160 if format > 0:
3144 3161 ui.write(b'\n')
3145 3162 ui.writenoi18n(
3146 3163 b'uncompressed data size (min/max/avg) : %d / %d / %d\n'
3147 3164 % tuple(datasize)
3148 3165 )
3149 3166 ui.writenoi18n(
3150 3167 b'full revision size (min/max/avg) : %d / %d / %d\n'
3151 3168 % tuple(fullsize)
3152 3169 )
3153 3170 ui.writenoi18n(
3154 3171 b'inter-snapshot size (min/max/avg) : %d / %d / %d\n'
3155 3172 % tuple(semisize)
3156 3173 )
3157 3174 for depth in sorted(snapsizedepth):
3158 3175 if depth == 0:
3159 3176 continue
3160 3177 ui.writenoi18n(
3161 3178 b' level-%-3d (min/max/avg) : %d / %d / %d\n'
3162 3179 % ((depth,) + tuple(snapsizedepth[depth]))
3163 3180 )
3164 3181 ui.writenoi18n(
3165 3182 b'delta size (min/max/avg) : %d / %d / %d\n'
3166 3183 % tuple(deltasize)
3167 3184 )
3168 3185
3169 3186 if numdeltas > 0:
3170 3187 ui.write(b'\n')
3171 3188 fmt = pcfmtstr(numdeltas)
3172 3189 fmt2 = pcfmtstr(numdeltas, 4)
3173 3190 ui.writenoi18n(
3174 3191 b'deltas against prev : ' + fmt % pcfmt(numprev, numdeltas)
3175 3192 )
3176 3193 if numprev > 0:
3177 3194 ui.writenoi18n(
3178 3195 b' where prev = p1 : ' + fmt2 % pcfmt(nump1prev, numprev)
3179 3196 )
3180 3197 ui.writenoi18n(
3181 3198 b' where prev = p2 : ' + fmt2 % pcfmt(nump2prev, numprev)
3182 3199 )
3183 3200 ui.writenoi18n(
3184 3201 b' other : ' + fmt2 % pcfmt(numoprev, numprev)
3185 3202 )
3186 3203 if gdelta:
3187 3204 ui.writenoi18n(
3188 3205 b'deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas)
3189 3206 )
3190 3207 ui.writenoi18n(
3191 3208 b'deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas)
3192 3209 )
3193 3210 ui.writenoi18n(
3194 3211 b'deltas against other : ' + fmt % pcfmt(numother, numdeltas)
3195 3212 )
3196 3213
3197 3214
3198 3215 @command(
3199 3216 b'debugrevlogindex',
3200 3217 cmdutil.debugrevlogopts
3201 3218 + [(b'f', b'format', 0, _(b'revlog format'), _(b'FORMAT'))],
3202 3219 _(b'[-f FORMAT] -c|-m|FILE'),
3203 3220 optionalrepo=True,
3204 3221 )
3205 3222 def debugrevlogindex(ui, repo, file_=None, **opts):
3206 3223 """dump the contents of a revlog index"""
3207 3224 opts = pycompat.byteskwargs(opts)
3208 3225 r = cmdutil.openrevlog(repo, b'debugrevlogindex', file_, opts)
3209 3226 format = opts.get(b'format', 0)
3210 3227 if format not in (0, 1):
3211 3228 raise error.Abort(_(b"unknown format %d") % format)
3212 3229
3213 3230 if ui.debugflag:
3214 3231 shortfn = hex
3215 3232 else:
3216 3233 shortfn = short
3217 3234
3218 3235 # There might not be anything in r, so have a sane default
3219 3236 idlen = 12
3220 3237 for i in r:
3221 3238 idlen = len(shortfn(r.node(i)))
3222 3239 break
3223 3240
3224 3241 if format == 0:
3225 3242 if ui.verbose:
3226 3243 ui.writenoi18n(
3227 3244 b" rev offset length linkrev %s %s p2\n"
3228 3245 % (b"nodeid".ljust(idlen), b"p1".ljust(idlen))
3229 3246 )
3230 3247 else:
3231 3248 ui.writenoi18n(
3232 3249 b" rev linkrev %s %s p2\n"
3233 3250 % (b"nodeid".ljust(idlen), b"p1".ljust(idlen))
3234 3251 )
3235 3252 elif format == 1:
3236 3253 if ui.verbose:
3237 3254 ui.writenoi18n(
3238 3255 (
3239 3256 b" rev flag offset length size link p1"
3240 3257 b" p2 %s\n"
3241 3258 )
3242 3259 % b"nodeid".rjust(idlen)
3243 3260 )
3244 3261 else:
3245 3262 ui.writenoi18n(
3246 3263 b" rev flag size link p1 p2 %s\n"
3247 3264 % b"nodeid".rjust(idlen)
3248 3265 )
3249 3266
3250 3267 for i in r:
3251 3268 node = r.node(i)
3252 3269 if format == 0:
3253 3270 try:
3254 3271 pp = r.parents(node)
3255 3272 except Exception:
3256 3273 pp = [nullid, nullid]
3257 3274 if ui.verbose:
3258 3275 ui.write(
3259 3276 b"% 6d % 9d % 7d % 7d %s %s %s\n"
3260 3277 % (
3261 3278 i,
3262 3279 r.start(i),
3263 3280 r.length(i),
3264 3281 r.linkrev(i),
3265 3282 shortfn(node),
3266 3283 shortfn(pp[0]),
3267 3284 shortfn(pp[1]),
3268 3285 )
3269 3286 )
3270 3287 else:
3271 3288 ui.write(
3272 3289 b"% 6d % 7d %s %s %s\n"
3273 3290 % (
3274 3291 i,
3275 3292 r.linkrev(i),
3276 3293 shortfn(node),
3277 3294 shortfn(pp[0]),
3278 3295 shortfn(pp[1]),
3279 3296 )
3280 3297 )
3281 3298 elif format == 1:
3282 3299 pr = r.parentrevs(i)
3283 3300 if ui.verbose:
3284 3301 ui.write(
3285 3302 b"% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d %s\n"
3286 3303 % (
3287 3304 i,
3288 3305 r.flags(i),
3289 3306 r.start(i),
3290 3307 r.length(i),
3291 3308 r.rawsize(i),
3292 3309 r.linkrev(i),
3293 3310 pr[0],
3294 3311 pr[1],
3295 3312 shortfn(node),
3296 3313 )
3297 3314 )
3298 3315 else:
3299 3316 ui.write(
3300 3317 b"% 6d %04x % 8d % 6d % 6d % 6d %s\n"
3301 3318 % (
3302 3319 i,
3303 3320 r.flags(i),
3304 3321 r.rawsize(i),
3305 3322 r.linkrev(i),
3306 3323 pr[0],
3307 3324 pr[1],
3308 3325 shortfn(node),
3309 3326 )
3310 3327 )
3311 3328
3312 3329
3313 3330 @command(
3314 3331 b'debugrevspec',
3315 3332 [
3316 3333 (
3317 3334 b'',
3318 3335 b'optimize',
3319 3336 None,
3320 3337 _(b'print parsed tree after optimizing (DEPRECATED)'),
3321 3338 ),
3322 3339 (
3323 3340 b'',
3324 3341 b'show-revs',
3325 3342 True,
3326 3343 _(b'print list of result revisions (default)'),
3327 3344 ),
3328 3345 (
3329 3346 b's',
3330 3347 b'show-set',
3331 3348 None,
3332 3349 _(b'print internal representation of result set'),
3333 3350 ),
3334 3351 (
3335 3352 b'p',
3336 3353 b'show-stage',
3337 3354 [],
3338 3355 _(b'print parsed tree at the given stage'),
3339 3356 _(b'NAME'),
3340 3357 ),
3341 3358 (b'', b'no-optimized', False, _(b'evaluate tree without optimization')),
3342 3359 (b'', b'verify-optimized', False, _(b'verify optimized result')),
3343 3360 ],
3344 3361 b'REVSPEC',
3345 3362 )
3346 3363 def debugrevspec(ui, repo, expr, **opts):
3347 3364 """parse and apply a revision specification
3348 3365
3349 3366 Use -p/--show-stage option to print the parsed tree at the given stages.
3350 3367 Use -p all to print tree at every stage.
3351 3368
3352 3369 Use --no-show-revs option with -s or -p to print only the set
3353 3370 representation or the parsed tree respectively.
3354 3371
3355 3372 Use --verify-optimized to compare the optimized result with the unoptimized
3356 3373 one. Returns 1 if the optimized result differs.
3357 3374 """
3358 3375 opts = pycompat.byteskwargs(opts)
3359 3376 aliases = ui.configitems(b'revsetalias')
3360 3377 stages = [
3361 3378 (b'parsed', lambda tree: tree),
3362 3379 (
3363 3380 b'expanded',
3364 3381 lambda tree: revsetlang.expandaliases(tree, aliases, ui.warn),
3365 3382 ),
3366 3383 (b'concatenated', revsetlang.foldconcat),
3367 3384 (b'analyzed', revsetlang.analyze),
3368 3385 (b'optimized', revsetlang.optimize),
3369 3386 ]
3370 3387 if opts[b'no_optimized']:
3371 3388 stages = stages[:-1]
3372 3389 if opts[b'verify_optimized'] and opts[b'no_optimized']:
3373 3390 raise error.Abort(
3374 3391 _(b'cannot use --verify-optimized with --no-optimized')
3375 3392 )
3376 3393 stagenames = {n for n, f in stages}
3377 3394
3378 3395 showalways = set()
3379 3396 showchanged = set()
3380 3397 if ui.verbose and not opts[b'show_stage']:
3381 3398 # show parsed tree by --verbose (deprecated)
3382 3399 showalways.add(b'parsed')
3383 3400 showchanged.update([b'expanded', b'concatenated'])
3384 3401 if opts[b'optimize']:
3385 3402 showalways.add(b'optimized')
3386 3403 if opts[b'show_stage'] and opts[b'optimize']:
3387 3404 raise error.Abort(_(b'cannot use --optimize with --show-stage'))
3388 3405 if opts[b'show_stage'] == [b'all']:
3389 3406 showalways.update(stagenames)
3390 3407 else:
3391 3408 for n in opts[b'show_stage']:
3392 3409 if n not in stagenames:
3393 3410 raise error.Abort(_(b'invalid stage name: %s') % n)
3394 3411 showalways.update(opts[b'show_stage'])
3395 3412
3396 3413 treebystage = {}
3397 3414 printedtree = None
3398 3415 tree = revsetlang.parse(expr, lookup=revset.lookupfn(repo))
3399 3416 for n, f in stages:
3400 3417 treebystage[n] = tree = f(tree)
3401 3418 if n in showalways or (n in showchanged and tree != printedtree):
3402 3419 if opts[b'show_stage'] or n != b'parsed':
3403 3420 ui.write(b"* %s:\n" % n)
3404 3421 ui.write(revsetlang.prettyformat(tree), b"\n")
3405 3422 printedtree = tree
3406 3423
3407 3424 if opts[b'verify_optimized']:
3408 3425 arevs = revset.makematcher(treebystage[b'analyzed'])(repo)
3409 3426 brevs = revset.makematcher(treebystage[b'optimized'])(repo)
3410 3427 if opts[b'show_set'] or (opts[b'show_set'] is None and ui.verbose):
3411 3428 ui.writenoi18n(
3412 3429 b"* analyzed set:\n", stringutil.prettyrepr(arevs), b"\n"
3413 3430 )
3414 3431 ui.writenoi18n(
3415 3432 b"* optimized set:\n", stringutil.prettyrepr(brevs), b"\n"
3416 3433 )
3417 3434 arevs = list(arevs)
3418 3435 brevs = list(brevs)
3419 3436 if arevs == brevs:
3420 3437 return 0
3421 3438 ui.writenoi18n(b'--- analyzed\n', label=b'diff.file_a')
3422 3439 ui.writenoi18n(b'+++ optimized\n', label=b'diff.file_b')
3423 3440 sm = difflib.SequenceMatcher(None, arevs, brevs)
3424 3441 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3425 3442 if tag in ('delete', 'replace'):
3426 3443 for c in arevs[alo:ahi]:
3427 3444 ui.write(b'-%d\n' % c, label=b'diff.deleted')
3428 3445 if tag in ('insert', 'replace'):
3429 3446 for c in brevs[blo:bhi]:
3430 3447 ui.write(b'+%d\n' % c, label=b'diff.inserted')
3431 3448 if tag == 'equal':
3432 3449 for c in arevs[alo:ahi]:
3433 3450 ui.write(b' %d\n' % c)
3434 3451 return 1
3435 3452
3436 3453 func = revset.makematcher(tree)
3437 3454 revs = func(repo)
3438 3455 if opts[b'show_set'] or (opts[b'show_set'] is None and ui.verbose):
3439 3456 ui.writenoi18n(b"* set:\n", stringutil.prettyrepr(revs), b"\n")
3440 3457 if not opts[b'show_revs']:
3441 3458 return
3442 3459 for c in revs:
3443 3460 ui.write(b"%d\n" % c)
3444 3461
3445 3462
3446 3463 @command(
3447 3464 b'debugserve',
3448 3465 [
3449 3466 (
3450 3467 b'',
3451 3468 b'sshstdio',
3452 3469 False,
3453 3470 _(b'run an SSH server bound to process handles'),
3454 3471 ),
3455 3472 (b'', b'logiofd', b'', _(b'file descriptor to log server I/O to')),
3456 3473 (b'', b'logiofile', b'', _(b'file to log server I/O to')),
3457 3474 ],
3458 3475 b'',
3459 3476 )
3460 3477 def debugserve(ui, repo, **opts):
3461 3478 """run a server with advanced settings
3462 3479
3463 3480 This command is similar to :hg:`serve`. It exists partially as a
3464 3481 workaround to the fact that ``hg serve --stdio`` must have specific
3465 3482 arguments for security reasons.
3466 3483 """
3467 3484 opts = pycompat.byteskwargs(opts)
3468 3485
3469 3486 if not opts[b'sshstdio']:
3470 3487 raise error.Abort(_(b'only --sshstdio is currently supported'))
3471 3488
3472 3489 logfh = None
3473 3490
3474 3491 if opts[b'logiofd'] and opts[b'logiofile']:
3475 3492 raise error.Abort(_(b'cannot use both --logiofd and --logiofile'))
3476 3493
3477 3494 if opts[b'logiofd']:
3478 3495 # Ideally we would be line buffered. But line buffering in binary
3479 3496 # mode isn't supported and emits a warning in Python 3.8+. Disabling
3480 3497 # buffering could have performance impacts. But since this isn't
3481 3498 # performance critical code, it should be fine.
3482 3499 try:
3483 3500 logfh = os.fdopen(int(opts[b'logiofd']), 'ab', 0)
3484 3501 except OSError as e:
3485 3502 if e.errno != errno.ESPIPE:
3486 3503 raise
3487 3504 # can't seek a pipe, so `ab` mode fails on py3
3488 3505 logfh = os.fdopen(int(opts[b'logiofd']), 'wb', 0)
3489 3506 elif opts[b'logiofile']:
3490 3507 logfh = open(opts[b'logiofile'], b'ab', 0)
3491 3508
3492 3509 s = wireprotoserver.sshserver(ui, repo, logfh=logfh)
3493 3510 s.serve_forever()
3494 3511
3495 3512
3496 3513 @command(b'debugsetparents', [], _(b'REV1 [REV2]'))
3497 3514 def debugsetparents(ui, repo, rev1, rev2=None):
3498 3515 """manually set the parents of the current working directory (DANGEROUS)
3499 3516
3500 3517 This command is not what you are looking for and should not be used. Using
3501 3518 this command will most certainly results in slight corruption of the file
3502 3519 level histories withing your repository. DO NOT USE THIS COMMAND.
3503 3520
3504 3521 The command update the p1 and p2 field in the dirstate, and not touching
3505 3522 anything else. This useful for writing repository conversion tools, but
3506 3523 should be used with extreme care. For example, neither the working
3507 3524 directory nor the dirstate is updated, so file status may be incorrect
3508 3525 after running this command. Only used if you are one of the few people that
3509 3526 deeply unstand both conversion tools and file level histories. If you are
3510 3527 reading this help, you are not one of this people (most of them sailed west
3511 3528 from Mithlond anyway.
3512 3529
3513 3530 So one last time DO NOT USE THIS COMMAND.
3514 3531
3515 3532 Returns 0 on success.
3516 3533 """
3517 3534
3518 3535 node1 = scmutil.revsingle(repo, rev1).node()
3519 3536 node2 = scmutil.revsingle(repo, rev2, b'null').node()
3520 3537
3521 3538 with repo.wlock():
3522 3539 repo.setparents(node1, node2)
3523 3540
3524 3541
3525 3542 @command(b'debugsidedata', cmdutil.debugrevlogopts, _(b'-c|-m|FILE REV'))
3526 3543 def debugsidedata(ui, repo, file_, rev=None, **opts):
3527 3544 """dump the side data for a cl/manifest/file revision
3528 3545
3529 3546 Use --verbose to dump the sidedata content."""
3530 3547 opts = pycompat.byteskwargs(opts)
3531 3548 if opts.get(b'changelog') or opts.get(b'manifest') or opts.get(b'dir'):
3532 3549 if rev is not None:
3533 3550 raise error.CommandError(b'debugdata', _(b'invalid arguments'))
3534 3551 file_, rev = None, file_
3535 3552 elif rev is None:
3536 3553 raise error.CommandError(b'debugdata', _(b'invalid arguments'))
3537 3554 r = cmdutil.openstorage(repo, b'debugdata', file_, opts)
3538 3555 r = getattr(r, '_revlog', r)
3539 3556 try:
3540 3557 sidedata = r.sidedata(r.lookup(rev))
3541 3558 except KeyError:
3542 3559 raise error.Abort(_(b'invalid revision identifier %s') % rev)
3543 3560 if sidedata:
3544 3561 sidedata = list(sidedata.items())
3545 3562 sidedata.sort()
3546 3563 ui.writenoi18n(b'%d sidedata entries\n' % len(sidedata))
3547 3564 for key, value in sidedata:
3548 3565 ui.writenoi18n(b' entry-%04o size %d\n' % (key, len(value)))
3549 3566 if ui.verbose:
3550 3567 ui.writenoi18n(b' %s\n' % stringutil.pprint(value))
3551 3568
3552 3569
3553 3570 @command(b'debugssl', [], b'[SOURCE]', optionalrepo=True)
3554 3571 def debugssl(ui, repo, source=None, **opts):
3555 3572 """test a secure connection to a server
3556 3573
3557 3574 This builds the certificate chain for the server on Windows, installing the
3558 3575 missing intermediates and trusted root via Windows Update if necessary. It
3559 3576 does nothing on other platforms.
3560 3577
3561 3578 If SOURCE is omitted, the 'default' path will be used. If a URL is given,
3562 3579 that server is used. See :hg:`help urls` for more information.
3563 3580
3564 3581 If the update succeeds, retry the original operation. Otherwise, the cause
3565 3582 of the SSL error is likely another issue.
3566 3583 """
3567 3584 if not pycompat.iswindows:
3568 3585 raise error.Abort(
3569 3586 _(b'certificate chain building is only possible on Windows')
3570 3587 )
3571 3588
3572 3589 if not source:
3573 3590 if not repo:
3574 3591 raise error.Abort(
3575 3592 _(
3576 3593 b"there is no Mercurial repository here, and no "
3577 3594 b"server specified"
3578 3595 )
3579 3596 )
3580 3597 source = b"default"
3581 3598
3582 3599 source, branches = hg.parseurl(ui.expandpath(source))
3583 3600 url = util.url(source)
3584 3601
3585 3602 defaultport = {b'https': 443, b'ssh': 22}
3586 3603 if url.scheme in defaultport:
3587 3604 try:
3588 3605 addr = (url.host, int(url.port or defaultport[url.scheme]))
3589 3606 except ValueError:
3590 3607 raise error.Abort(_(b"malformed port number in URL"))
3591 3608 else:
3592 3609 raise error.Abort(_(b"only https and ssh connections are supported"))
3593 3610
3594 3611 from . import win32
3595 3612
3596 3613 s = ssl.wrap_socket(
3597 3614 socket.socket(),
3598 3615 ssl_version=ssl.PROTOCOL_TLS,
3599 3616 cert_reqs=ssl.CERT_NONE,
3600 3617 ca_certs=None,
3601 3618 )
3602 3619
3603 3620 try:
3604 3621 s.connect(addr)
3605 3622 cert = s.getpeercert(True)
3606 3623
3607 3624 ui.status(_(b'checking the certificate chain for %s\n') % url.host)
3608 3625
3609 3626 complete = win32.checkcertificatechain(cert, build=False)
3610 3627
3611 3628 if not complete:
3612 3629 ui.status(_(b'certificate chain is incomplete, updating... '))
3613 3630
3614 3631 if not win32.checkcertificatechain(cert):
3615 3632 ui.status(_(b'failed.\n'))
3616 3633 else:
3617 3634 ui.status(_(b'done.\n'))
3618 3635 else:
3619 3636 ui.status(_(b'full certificate chain is available\n'))
3620 3637 finally:
3621 3638 s.close()
3622 3639
3623 3640
3624 3641 @command(
3625 3642 b"debugbackupbundle",
3626 3643 [
3627 3644 (
3628 3645 b"",
3629 3646 b"recover",
3630 3647 b"",
3631 3648 b"brings the specified changeset back into the repository",
3632 3649 )
3633 3650 ]
3634 3651 + cmdutil.logopts,
3635 3652 _(b"hg debugbackupbundle [--recover HASH]"),
3636 3653 )
3637 3654 def debugbackupbundle(ui, repo, *pats, **opts):
3638 3655 """lists the changesets available in backup bundles
3639 3656
3640 3657 Without any arguments, this command prints a list of the changesets in each
3641 3658 backup bundle.
3642 3659
3643 3660 --recover takes a changeset hash and unbundles the first bundle that
3644 3661 contains that hash, which puts that changeset back in your repository.
3645 3662
3646 3663 --verbose will print the entire commit message and the bundle path for that
3647 3664 backup.
3648 3665 """
3649 3666 backups = list(
3650 3667 filter(
3651 3668 os.path.isfile, glob.glob(repo.vfs.join(b"strip-backup") + b"/*.hg")
3652 3669 )
3653 3670 )
3654 3671 backups.sort(key=lambda x: os.path.getmtime(x), reverse=True)
3655 3672
3656 3673 opts = pycompat.byteskwargs(opts)
3657 3674 opts[b"bundle"] = b""
3658 3675 opts[b"force"] = None
3659 3676 limit = logcmdutil.getlimit(opts)
3660 3677
3661 3678 def display(other, chlist, displayer):
3662 3679 if opts.get(b"newest_first"):
3663 3680 chlist.reverse()
3664 3681 count = 0
3665 3682 for n in chlist:
3666 3683 if limit is not None and count >= limit:
3667 3684 break
3668 3685 parents = [True for p in other.changelog.parents(n) if p != nullid]
3669 3686 if opts.get(b"no_merges") and len(parents) == 2:
3670 3687 continue
3671 3688 count += 1
3672 3689 displayer.show(other[n])
3673 3690
3674 3691 recovernode = opts.get(b"recover")
3675 3692 if recovernode:
3676 3693 if scmutil.isrevsymbol(repo, recovernode):
3677 3694 ui.warn(_(b"%s already exists in the repo\n") % recovernode)
3678 3695 return
3679 3696 elif backups:
3680 3697 msg = _(
3681 3698 b"Recover changesets using: hg debugbackupbundle --recover "
3682 3699 b"<changeset hash>\n\nAvailable backup changesets:"
3683 3700 )
3684 3701 ui.status(msg, label=b"status.removed")
3685 3702 else:
3686 3703 ui.status(_(b"no backup changesets found\n"))
3687 3704 return
3688 3705
3689 3706 for backup in backups:
3690 3707 # Much of this is copied from the hg incoming logic
3691 3708 source = ui.expandpath(os.path.relpath(backup, encoding.getcwd()))
3692 3709 source, branches = hg.parseurl(source, opts.get(b"branch"))
3693 3710 try:
3694 3711 other = hg.peer(repo, opts, source)
3695 3712 except error.LookupError as ex:
3696 3713 msg = _(b"\nwarning: unable to open bundle %s") % source
3697 3714 hint = _(b"\n(missing parent rev %s)\n") % short(ex.name)
3698 3715 ui.warn(msg, hint=hint)
3699 3716 continue
3700 3717 revs, checkout = hg.addbranchrevs(
3701 3718 repo, other, branches, opts.get(b"rev")
3702 3719 )
3703 3720
3704 3721 if revs:
3705 3722 revs = [other.lookup(rev) for rev in revs]
3706 3723
3707 3724 quiet = ui.quiet
3708 3725 try:
3709 3726 ui.quiet = True
3710 3727 other, chlist, cleanupfn = bundlerepo.getremotechanges(
3711 3728 ui, repo, other, revs, opts[b"bundle"], opts[b"force"]
3712 3729 )
3713 3730 except error.LookupError:
3714 3731 continue
3715 3732 finally:
3716 3733 ui.quiet = quiet
3717 3734
3718 3735 try:
3719 3736 if not chlist:
3720 3737 continue
3721 3738 if recovernode:
3722 3739 with repo.lock(), repo.transaction(b"unbundle") as tr:
3723 3740 if scmutil.isrevsymbol(other, recovernode):
3724 3741 ui.status(_(b"Unbundling %s\n") % (recovernode))
3725 3742 f = hg.openpath(ui, source)
3726 3743 gen = exchange.readbundle(ui, f, source)
3727 3744 if isinstance(gen, bundle2.unbundle20):
3728 3745 bundle2.applybundle(
3729 3746 repo,
3730 3747 gen,
3731 3748 tr,
3732 3749 source=b"unbundle",
3733 3750 url=b"bundle:" + source,
3734 3751 )
3735 3752 else:
3736 3753 gen.apply(repo, b"unbundle", b"bundle:" + source)
3737 3754 break
3738 3755 else:
3739 3756 backupdate = encoding.strtolocal(
3740 3757 time.strftime(
3741 3758 "%a %H:%M, %Y-%m-%d",
3742 3759 time.localtime(os.path.getmtime(source)),
3743 3760 )
3744 3761 )
3745 3762 ui.status(b"\n%s\n" % (backupdate.ljust(50)))
3746 3763 if ui.verbose:
3747 3764 ui.status(b"%s%s\n" % (b"bundle:".ljust(13), source))
3748 3765 else:
3749 3766 opts[
3750 3767 b"template"
3751 3768 ] = b"{label('status.modified', node|short)} {desc|firstline}\n"
3752 3769 displayer = logcmdutil.changesetdisplayer(
3753 3770 ui, other, opts, False
3754 3771 )
3755 3772 display(other, chlist, displayer)
3756 3773 displayer.close()
3757 3774 finally:
3758 3775 cleanupfn()
3759 3776
3760 3777
3761 3778 @command(
3762 3779 b'debugsub',
3763 3780 [(b'r', b'rev', b'', _(b'revision to check'), _(b'REV'))],
3764 3781 _(b'[-r REV] [REV]'),
3765 3782 )
3766 3783 def debugsub(ui, repo, rev=None):
3767 3784 ctx = scmutil.revsingle(repo, rev, None)
3768 3785 for k, v in sorted(ctx.substate.items()):
3769 3786 ui.writenoi18n(b'path %s\n' % k)
3770 3787 ui.writenoi18n(b' source %s\n' % v[0])
3771 3788 ui.writenoi18n(b' revision %s\n' % v[1])
3772 3789
3773 3790
3774 3791 @command(b'debugshell', optionalrepo=True)
3775 3792 def debugshell(ui, repo):
3776 3793 """run an interactive Python interpreter
3777 3794
3778 3795 The local namespace is provided with a reference to the ui and
3779 3796 the repo instance (if available).
3780 3797 """
3781 3798 import code
3782 3799
3783 3800 imported_objects = {
3784 3801 'ui': ui,
3785 3802 'repo': repo,
3786 3803 }
3787 3804
3788 3805 code.interact(local=imported_objects)
3789 3806
3790 3807
3791 3808 @command(
3792 3809 b'debugsuccessorssets',
3793 3810 [(b'', b'closest', False, _(b'return closest successors sets only'))],
3794 3811 _(b'[REV]'),
3795 3812 )
3796 3813 def debugsuccessorssets(ui, repo, *revs, **opts):
3797 3814 """show set of successors for revision
3798 3815
3799 3816 A successors set of changeset A is a consistent group of revisions that
3800 3817 succeed A. It contains non-obsolete changesets only unless closests
3801 3818 successors set is set.
3802 3819
3803 3820 In most cases a changeset A has a single successors set containing a single
3804 3821 successor (changeset A replaced by A').
3805 3822
3806 3823 A changeset that is made obsolete with no successors are called "pruned".
3807 3824 Such changesets have no successors sets at all.
3808 3825
3809 3826 A changeset that has been "split" will have a successors set containing
3810 3827 more than one successor.
3811 3828
3812 3829 A changeset that has been rewritten in multiple different ways is called
3813 3830 "divergent". Such changesets have multiple successor sets (each of which
3814 3831 may also be split, i.e. have multiple successors).
3815 3832
3816 3833 Results are displayed as follows::
3817 3834
3818 3835 <rev1>
3819 3836 <successors-1A>
3820 3837 <rev2>
3821 3838 <successors-2A>
3822 3839 <successors-2B1> <successors-2B2> <successors-2B3>
3823 3840
3824 3841 Here rev2 has two possible (i.e. divergent) successors sets. The first
3825 3842 holds one element, whereas the second holds three (i.e. the changeset has
3826 3843 been split).
3827 3844 """
3828 3845 # passed to successorssets caching computation from one call to another
3829 3846 cache = {}
3830 3847 ctx2str = bytes
3831 3848 node2str = short
3832 3849 for rev in scmutil.revrange(repo, revs):
3833 3850 ctx = repo[rev]
3834 3851 ui.write(b'%s\n' % ctx2str(ctx))
3835 3852 for succsset in obsutil.successorssets(
3836 3853 repo, ctx.node(), closest=opts['closest'], cache=cache
3837 3854 ):
3838 3855 if succsset:
3839 3856 ui.write(b' ')
3840 3857 ui.write(node2str(succsset[0]))
3841 3858 for node in succsset[1:]:
3842 3859 ui.write(b' ')
3843 3860 ui.write(node2str(node))
3844 3861 ui.write(b'\n')
3845 3862
3846 3863
3847 3864 @command(b'debugtagscache', [])
3848 3865 def debugtagscache(ui, repo):
3849 3866 """display the contents of .hg/cache/hgtagsfnodes1"""
3850 3867 cache = tagsmod.hgtagsfnodescache(repo.unfiltered())
3851 3868 for r in repo:
3852 3869 node = repo[r].node()
3853 3870 tagsnode = cache.getfnode(node, computemissing=False)
3854 3871 tagsnodedisplay = hex(tagsnode) if tagsnode else b'missing/invalid'
3855 3872 ui.write(b'%d %s %s\n' % (r, hex(node), tagsnodedisplay))
3856 3873
3857 3874
3858 3875 @command(
3859 3876 b'debugtemplate',
3860 3877 [
3861 3878 (b'r', b'rev', [], _(b'apply template on changesets'), _(b'REV')),
3862 3879 (b'D', b'define', [], _(b'define template keyword'), _(b'KEY=VALUE')),
3863 3880 ],
3864 3881 _(b'[-r REV]... [-D KEY=VALUE]... TEMPLATE'),
3865 3882 optionalrepo=True,
3866 3883 )
3867 3884 def debugtemplate(ui, repo, tmpl, **opts):
3868 3885 """parse and apply a template
3869 3886
3870 3887 If -r/--rev is given, the template is processed as a log template and
3871 3888 applied to the given changesets. Otherwise, it is processed as a generic
3872 3889 template.
3873 3890
3874 3891 Use --verbose to print the parsed tree.
3875 3892 """
3876 3893 revs = None
3877 3894 if opts['rev']:
3878 3895 if repo is None:
3879 3896 raise error.RepoError(
3880 3897 _(b'there is no Mercurial repository here (.hg not found)')
3881 3898 )
3882 3899 revs = scmutil.revrange(repo, opts['rev'])
3883 3900
3884 3901 props = {}
3885 3902 for d in opts['define']:
3886 3903 try:
3887 3904 k, v = (e.strip() for e in d.split(b'=', 1))
3888 3905 if not k or k == b'ui':
3889 3906 raise ValueError
3890 3907 props[k] = v
3891 3908 except ValueError:
3892 3909 raise error.Abort(_(b'malformed keyword definition: %s') % d)
3893 3910
3894 3911 if ui.verbose:
3895 3912 aliases = ui.configitems(b'templatealias')
3896 3913 tree = templater.parse(tmpl)
3897 3914 ui.note(templater.prettyformat(tree), b'\n')
3898 3915 newtree = templater.expandaliases(tree, aliases)
3899 3916 if newtree != tree:
3900 3917 ui.notenoi18n(
3901 3918 b"* expanded:\n", templater.prettyformat(newtree), b'\n'
3902 3919 )
3903 3920
3904 3921 if revs is None:
3905 3922 tres = formatter.templateresources(ui, repo)
3906 3923 t = formatter.maketemplater(ui, tmpl, resources=tres)
3907 3924 if ui.verbose:
3908 3925 kwds, funcs = t.symbolsuseddefault()
3909 3926 ui.writenoi18n(b"* keywords: %s\n" % b', '.join(sorted(kwds)))
3910 3927 ui.writenoi18n(b"* functions: %s\n" % b', '.join(sorted(funcs)))
3911 3928 ui.write(t.renderdefault(props))
3912 3929 else:
3913 3930 displayer = logcmdutil.maketemplater(ui, repo, tmpl)
3914 3931 if ui.verbose:
3915 3932 kwds, funcs = displayer.t.symbolsuseddefault()
3916 3933 ui.writenoi18n(b"* keywords: %s\n" % b', '.join(sorted(kwds)))
3917 3934 ui.writenoi18n(b"* functions: %s\n" % b', '.join(sorted(funcs)))
3918 3935 for r in revs:
3919 3936 displayer.show(repo[r], **pycompat.strkwargs(props))
3920 3937 displayer.close()
3921 3938
3922 3939
3923 3940 @command(
3924 3941 b'debuguigetpass',
3925 3942 [
3926 3943 (b'p', b'prompt', b'', _(b'prompt text'), _(b'TEXT')),
3927 3944 ],
3928 3945 _(b'[-p TEXT]'),
3929 3946 norepo=True,
3930 3947 )
3931 3948 def debuguigetpass(ui, prompt=b''):
3932 3949 """show prompt to type password"""
3933 3950 r = ui.getpass(prompt)
3934 3951 if r is None:
3935 3952 r = b"<default response>"
3936 3953 ui.writenoi18n(b'response: %s\n' % r)
3937 3954
3938 3955
3939 3956 @command(
3940 3957 b'debuguiprompt',
3941 3958 [
3942 3959 (b'p', b'prompt', b'', _(b'prompt text'), _(b'TEXT')),
3943 3960 ],
3944 3961 _(b'[-p TEXT]'),
3945 3962 norepo=True,
3946 3963 )
3947 3964 def debuguiprompt(ui, prompt=b''):
3948 3965 """show plain prompt"""
3949 3966 r = ui.prompt(prompt)
3950 3967 ui.writenoi18n(b'response: %s\n' % r)
3951 3968
3952 3969
3953 3970 @command(b'debugupdatecaches', [])
3954 3971 def debugupdatecaches(ui, repo, *pats, **opts):
3955 3972 """warm all known caches in the repository"""
3956 3973 with repo.wlock(), repo.lock():
3957 3974 repo.updatecaches(full=True)
3958 3975
3959 3976
3960 3977 @command(
3961 3978 b'debugupgraderepo',
3962 3979 [
3963 3980 (
3964 3981 b'o',
3965 3982 b'optimize',
3966 3983 [],
3967 3984 _(b'extra optimization to perform'),
3968 3985 _(b'NAME'),
3969 3986 ),
3970 3987 (b'', b'run', False, _(b'performs an upgrade')),
3971 3988 (b'', b'backup', True, _(b'keep the old repository content around')),
3972 3989 (b'', b'changelog', None, _(b'select the changelog for upgrade')),
3973 3990 (b'', b'manifest', None, _(b'select the manifest for upgrade')),
3974 3991 (b'', b'filelogs', None, _(b'select all filelogs for upgrade')),
3975 3992 ],
3976 3993 )
3977 3994 def debugupgraderepo(ui, repo, run=False, optimize=None, backup=True, **opts):
3978 3995 """upgrade a repository to use different features
3979 3996
3980 3997 If no arguments are specified, the repository is evaluated for upgrade
3981 3998 and a list of problems and potential optimizations is printed.
3982 3999
3983 4000 With ``--run``, a repository upgrade is performed. Behavior of the upgrade
3984 4001 can be influenced via additional arguments. More details will be provided
3985 4002 by the command output when run without ``--run``.
3986 4003
3987 4004 During the upgrade, the repository will be locked and no writes will be
3988 4005 allowed.
3989 4006
3990 4007 At the end of the upgrade, the repository may not be readable while new
3991 4008 repository data is swapped in. This window will be as long as it takes to
3992 4009 rename some directories inside the ``.hg`` directory. On most machines, this
3993 4010 should complete almost instantaneously and the chances of a consumer being
3994 4011 unable to access the repository should be low.
3995 4012
3996 4013 By default, all revlog will be upgraded. You can restrict this using flag
3997 4014 such as `--manifest`:
3998 4015
3999 4016 * `--manifest`: only optimize the manifest
4000 4017 * `--no-manifest`: optimize all revlog but the manifest
4001 4018 * `--changelog`: optimize the changelog only
4002 4019 * `--no-changelog --no-manifest`: optimize filelogs only
4003 4020 * `--filelogs`: optimize the filelogs only
4004 4021 * `--no-changelog --no-manifest --no-filelogs`: skip all revlog optimizations
4005 4022 """
4006 4023 return upgrade.upgraderepo(
4007 4024 ui, repo, run=run, optimize=set(optimize), backup=backup, **opts
4008 4025 )
4009 4026
4010 4027
4011 4028 @command(
4012 4029 b'debugwalk', cmdutil.walkopts, _(b'[OPTION]... [FILE]...'), inferrepo=True
4013 4030 )
4014 4031 def debugwalk(ui, repo, *pats, **opts):
4015 4032 """show how files match on given patterns"""
4016 4033 opts = pycompat.byteskwargs(opts)
4017 4034 m = scmutil.match(repo[None], pats, opts)
4018 4035 if ui.verbose:
4019 4036 ui.writenoi18n(b'* matcher:\n', stringutil.prettyrepr(m), b'\n')
4020 4037 items = list(repo[None].walk(m))
4021 4038 if not items:
4022 4039 return
4023 4040 f = lambda fn: fn
4024 4041 if ui.configbool(b'ui', b'slash') and pycompat.ossep != b'/':
4025 4042 f = lambda fn: util.normpath(fn)
4026 4043 fmt = b'f %%-%ds %%-%ds %%s' % (
4027 4044 max([len(abs) for abs in items]),
4028 4045 max([len(repo.pathto(abs)) for abs in items]),
4029 4046 )
4030 4047 for abs in items:
4031 4048 line = fmt % (
4032 4049 abs,
4033 4050 f(repo.pathto(abs)),
4034 4051 m.exact(abs) and b'exact' or b'',
4035 4052 )
4036 4053 ui.write(b"%s\n" % line.rstrip())
4037 4054
4038 4055
4039 4056 @command(b'debugwhyunstable', [], _(b'REV'))
4040 4057 def debugwhyunstable(ui, repo, rev):
4041 4058 """explain instabilities of a changeset"""
4042 4059 for entry in obsutil.whyunstable(repo, scmutil.revsingle(repo, rev)):
4043 4060 dnodes = b''
4044 4061 if entry.get(b'divergentnodes'):
4045 4062 dnodes = (
4046 4063 b' '.join(
4047 4064 b'%s (%s)' % (ctx.hex(), ctx.phasestr())
4048 4065 for ctx in entry[b'divergentnodes']
4049 4066 )
4050 4067 + b' '
4051 4068 )
4052 4069 ui.write(
4053 4070 b'%s: %s%s %s\n'
4054 4071 % (entry[b'instability'], dnodes, entry[b'reason'], entry[b'node'])
4055 4072 )
4056 4073
4057 4074
4058 4075 @command(
4059 4076 b'debugwireargs',
4060 4077 [
4061 4078 (b'', b'three', b'', b'three'),
4062 4079 (b'', b'four', b'', b'four'),
4063 4080 (b'', b'five', b'', b'five'),
4064 4081 ]
4065 4082 + cmdutil.remoteopts,
4066 4083 _(b'REPO [OPTIONS]... [ONE [TWO]]'),
4067 4084 norepo=True,
4068 4085 )
4069 4086 def debugwireargs(ui, repopath, *vals, **opts):
4070 4087 opts = pycompat.byteskwargs(opts)
4071 4088 repo = hg.peer(ui, opts, repopath)
4072 4089 for opt in cmdutil.remoteopts:
4073 4090 del opts[opt[1]]
4074 4091 args = {}
4075 4092 for k, v in pycompat.iteritems(opts):
4076 4093 if v:
4077 4094 args[k] = v
4078 4095 args = pycompat.strkwargs(args)
4079 4096 # run twice to check that we don't mess up the stream for the next command
4080 4097 res1 = repo.debugwireargs(*vals, **args)
4081 4098 res2 = repo.debugwireargs(*vals, **args)
4082 4099 ui.write(b"%s\n" % res1)
4083 4100 if res1 != res2:
4084 4101 ui.warn(b"%s\n" % res2)
4085 4102
4086 4103
4087 4104 def _parsewirelangblocks(fh):
4088 4105 activeaction = None
4089 4106 blocklines = []
4090 4107 lastindent = 0
4091 4108
4092 4109 for line in fh:
4093 4110 line = line.rstrip()
4094 4111 if not line:
4095 4112 continue
4096 4113
4097 4114 if line.startswith(b'#'):
4098 4115 continue
4099 4116
4100 4117 if not line.startswith(b' '):
4101 4118 # New block. Flush previous one.
4102 4119 if activeaction:
4103 4120 yield activeaction, blocklines
4104 4121
4105 4122 activeaction = line
4106 4123 blocklines = []
4107 4124 lastindent = 0
4108 4125 continue
4109 4126
4110 4127 # Else we start with an indent.
4111 4128
4112 4129 if not activeaction:
4113 4130 raise error.Abort(_(b'indented line outside of block'))
4114 4131
4115 4132 indent = len(line) - len(line.lstrip())
4116 4133
4117 4134 # If this line is indented more than the last line, concatenate it.
4118 4135 if indent > lastindent and blocklines:
4119 4136 blocklines[-1] += line.lstrip()
4120 4137 else:
4121 4138 blocklines.append(line)
4122 4139 lastindent = indent
4123 4140
4124 4141 # Flush last block.
4125 4142 if activeaction:
4126 4143 yield activeaction, blocklines
4127 4144
4128 4145
4129 4146 @command(
4130 4147 b'debugwireproto',
4131 4148 [
4132 4149 (b'', b'localssh', False, _(b'start an SSH server for this repo')),
4133 4150 (b'', b'peer', b'', _(b'construct a specific version of the peer')),
4134 4151 (
4135 4152 b'',
4136 4153 b'noreadstderr',
4137 4154 False,
4138 4155 _(b'do not read from stderr of the remote'),
4139 4156 ),
4140 4157 (
4141 4158 b'',
4142 4159 b'nologhandshake',
4143 4160 False,
4144 4161 _(b'do not log I/O related to the peer handshake'),
4145 4162 ),
4146 4163 ]
4147 4164 + cmdutil.remoteopts,
4148 4165 _(b'[PATH]'),
4149 4166 optionalrepo=True,
4150 4167 )
4151 4168 def debugwireproto(ui, repo, path=None, **opts):
4152 4169 """send wire protocol commands to a server
4153 4170
4154 4171 This command can be used to issue wire protocol commands to remote
4155 4172 peers and to debug the raw data being exchanged.
4156 4173
4157 4174 ``--localssh`` will start an SSH server against the current repository
4158 4175 and connect to that. By default, the connection will perform a handshake
4159 4176 and establish an appropriate peer instance.
4160 4177
4161 4178 ``--peer`` can be used to bypass the handshake protocol and construct a
4162 4179 peer instance using the specified class type. Valid values are ``raw``,
4163 4180 ``http2``, ``ssh1``, and ``ssh2``. ``raw`` instances only allow sending
4164 4181 raw data payloads and don't support higher-level command actions.
4165 4182
4166 4183 ``--noreadstderr`` can be used to disable automatic reading from stderr
4167 4184 of the peer (for SSH connections only). Disabling automatic reading of
4168 4185 stderr is useful for making output more deterministic.
4169 4186
4170 4187 Commands are issued via a mini language which is specified via stdin.
4171 4188 The language consists of individual actions to perform. An action is
4172 4189 defined by a block. A block is defined as a line with no leading
4173 4190 space followed by 0 or more lines with leading space. Blocks are
4174 4191 effectively a high-level command with additional metadata.
4175 4192
4176 4193 Lines beginning with ``#`` are ignored.
4177 4194
4178 4195 The following sections denote available actions.
4179 4196
4180 4197 raw
4181 4198 ---
4182 4199
4183 4200 Send raw data to the server.
4184 4201
4185 4202 The block payload contains the raw data to send as one atomic send
4186 4203 operation. The data may not actually be delivered in a single system
4187 4204 call: it depends on the abilities of the transport being used.
4188 4205
4189 4206 Each line in the block is de-indented and concatenated. Then, that
4190 4207 value is evaluated as a Python b'' literal. This allows the use of
4191 4208 backslash escaping, etc.
4192 4209
4193 4210 raw+
4194 4211 ----
4195 4212
4196 4213 Behaves like ``raw`` except flushes output afterwards.
4197 4214
4198 4215 command <X>
4199 4216 -----------
4200 4217
4201 4218 Send a request to run a named command, whose name follows the ``command``
4202 4219 string.
4203 4220
4204 4221 Arguments to the command are defined as lines in this block. The format of
4205 4222 each line is ``<key> <value>``. e.g.::
4206 4223
4207 4224 command listkeys
4208 4225 namespace bookmarks
4209 4226
4210 4227 If the value begins with ``eval:``, it will be interpreted as a Python
4211 4228 literal expression. Otherwise values are interpreted as Python b'' literals.
4212 4229 This allows sending complex types and encoding special byte sequences via
4213 4230 backslash escaping.
4214 4231
4215 4232 The following arguments have special meaning:
4216 4233
4217 4234 ``PUSHFILE``
4218 4235 When defined, the *push* mechanism of the peer will be used instead
4219 4236 of the static request-response mechanism and the content of the
4220 4237 file specified in the value of this argument will be sent as the
4221 4238 command payload.
4222 4239
4223 4240 This can be used to submit a local bundle file to the remote.
4224 4241
4225 4242 batchbegin
4226 4243 ----------
4227 4244
4228 4245 Instruct the peer to begin a batched send.
4229 4246
4230 4247 All ``command`` blocks are queued for execution until the next
4231 4248 ``batchsubmit`` block.
4232 4249
4233 4250 batchsubmit
4234 4251 -----------
4235 4252
4236 4253 Submit previously queued ``command`` blocks as a batch request.
4237 4254
4238 4255 This action MUST be paired with a ``batchbegin`` action.
4239 4256
4240 4257 httprequest <method> <path>
4241 4258 ---------------------------
4242 4259
4243 4260 (HTTP peer only)
4244 4261
4245 4262 Send an HTTP request to the peer.
4246 4263
4247 4264 The HTTP request line follows the ``httprequest`` action. e.g. ``GET /foo``.
4248 4265
4249 4266 Arguments of the form ``<key>: <value>`` are interpreted as HTTP request
4250 4267 headers to add to the request. e.g. ``Accept: foo``.
4251 4268
4252 4269 The following arguments are special:
4253 4270
4254 4271 ``BODYFILE``
4255 4272 The content of the file defined as the value to this argument will be
4256 4273 transferred verbatim as the HTTP request body.
4257 4274
4258 4275 ``frame <type> <flags> <payload>``
4259 4276 Send a unified protocol frame as part of the request body.
4260 4277
4261 4278 All frames will be collected and sent as the body to the HTTP
4262 4279 request.
4263 4280
4264 4281 close
4265 4282 -----
4266 4283
4267 4284 Close the connection to the server.
4268 4285
4269 4286 flush
4270 4287 -----
4271 4288
4272 4289 Flush data written to the server.
4273 4290
4274 4291 readavailable
4275 4292 -------------
4276 4293
4277 4294 Close the write end of the connection and read all available data from
4278 4295 the server.
4279 4296
4280 4297 If the connection to the server encompasses multiple pipes, we poll both
4281 4298 pipes and read available data.
4282 4299
4283 4300 readline
4284 4301 --------
4285 4302
4286 4303 Read a line of output from the server. If there are multiple output
4287 4304 pipes, reads only the main pipe.
4288 4305
4289 4306 ereadline
4290 4307 ---------
4291 4308
4292 4309 Like ``readline``, but read from the stderr pipe, if available.
4293 4310
4294 4311 read <X>
4295 4312 --------
4296 4313
4297 4314 ``read()`` N bytes from the server's main output pipe.
4298 4315
4299 4316 eread <X>
4300 4317 ---------
4301 4318
4302 4319 ``read()`` N bytes from the server's stderr pipe, if available.
4303 4320
4304 4321 Specifying Unified Frame-Based Protocol Frames
4305 4322 ----------------------------------------------
4306 4323
4307 4324 It is possible to emit a *Unified Frame-Based Protocol* by using special
4308 4325 syntax.
4309 4326
4310 4327 A frame is composed as a type, flags, and payload. These can be parsed
4311 4328 from a string of the form:
4312 4329
4313 4330 <request-id> <stream-id> <stream-flags> <type> <flags> <payload>
4314 4331
4315 4332 ``request-id`` and ``stream-id`` are integers defining the request and
4316 4333 stream identifiers.
4317 4334
4318 4335 ``type`` can be an integer value for the frame type or the string name
4319 4336 of the type. The strings are defined in ``wireprotoframing.py``. e.g.
4320 4337 ``command-name``.
4321 4338
4322 4339 ``stream-flags`` and ``flags`` are a ``|`` delimited list of flag
4323 4340 components. Each component (and there can be just one) can be an integer
4324 4341 or a flag name for stream flags or frame flags, respectively. Values are
4325 4342 resolved to integers and then bitwise OR'd together.
4326 4343
4327 4344 ``payload`` represents the raw frame payload. If it begins with
4328 4345 ``cbor:``, the following string is evaluated as Python code and the
4329 4346 resulting object is fed into a CBOR encoder. Otherwise it is interpreted
4330 4347 as a Python byte string literal.
4331 4348 """
4332 4349 opts = pycompat.byteskwargs(opts)
4333 4350
4334 4351 if opts[b'localssh'] and not repo:
4335 4352 raise error.Abort(_(b'--localssh requires a repository'))
4336 4353
4337 4354 if opts[b'peer'] and opts[b'peer'] not in (
4338 4355 b'raw',
4339 4356 b'http2',
4340 4357 b'ssh1',
4341 4358 b'ssh2',
4342 4359 ):
4343 4360 raise error.Abort(
4344 4361 _(b'invalid value for --peer'),
4345 4362 hint=_(b'valid values are "raw", "ssh1", and "ssh2"'),
4346 4363 )
4347 4364
4348 4365 if path and opts[b'localssh']:
4349 4366 raise error.Abort(_(b'cannot specify --localssh with an explicit path'))
4350 4367
4351 4368 if ui.interactive():
4352 4369 ui.write(_(b'(waiting for commands on stdin)\n'))
4353 4370
4354 4371 blocks = list(_parsewirelangblocks(ui.fin))
4355 4372
4356 4373 proc = None
4357 4374 stdin = None
4358 4375 stdout = None
4359 4376 stderr = None
4360 4377 opener = None
4361 4378
4362 4379 if opts[b'localssh']:
4363 4380 # We start the SSH server in its own process so there is process
4364 4381 # separation. This prevents a whole class of potential bugs around
4365 4382 # shared state from interfering with server operation.
4366 4383 args = procutil.hgcmd() + [
4367 4384 b'-R',
4368 4385 repo.root,
4369 4386 b'debugserve',
4370 4387 b'--sshstdio',
4371 4388 ]
4372 4389 proc = subprocess.Popen(
4373 4390 pycompat.rapply(procutil.tonativestr, args),
4374 4391 stdin=subprocess.PIPE,
4375 4392 stdout=subprocess.PIPE,
4376 4393 stderr=subprocess.PIPE,
4377 4394 bufsize=0,
4378 4395 )
4379 4396
4380 4397 stdin = proc.stdin
4381 4398 stdout = proc.stdout
4382 4399 stderr = proc.stderr
4383 4400
4384 4401 # We turn the pipes into observers so we can log I/O.
4385 4402 if ui.verbose or opts[b'peer'] == b'raw':
4386 4403 stdin = util.makeloggingfileobject(
4387 4404 ui, proc.stdin, b'i', logdata=True
4388 4405 )
4389 4406 stdout = util.makeloggingfileobject(
4390 4407 ui, proc.stdout, b'o', logdata=True
4391 4408 )
4392 4409 stderr = util.makeloggingfileobject(
4393 4410 ui, proc.stderr, b'e', logdata=True
4394 4411 )
4395 4412
4396 4413 # --localssh also implies the peer connection settings.
4397 4414
4398 4415 url = b'ssh://localserver'
4399 4416 autoreadstderr = not opts[b'noreadstderr']
4400 4417
4401 4418 if opts[b'peer'] == b'ssh1':
4402 4419 ui.write(_(b'creating ssh peer for wire protocol version 1\n'))
4403 4420 peer = sshpeer.sshv1peer(
4404 4421 ui,
4405 4422 url,
4406 4423 proc,
4407 4424 stdin,
4408 4425 stdout,
4409 4426 stderr,
4410 4427 None,
4411 4428 autoreadstderr=autoreadstderr,
4412 4429 )
4413 4430 elif opts[b'peer'] == b'ssh2':
4414 4431 ui.write(_(b'creating ssh peer for wire protocol version 2\n'))
4415 4432 peer = sshpeer.sshv2peer(
4416 4433 ui,
4417 4434 url,
4418 4435 proc,
4419 4436 stdin,
4420 4437 stdout,
4421 4438 stderr,
4422 4439 None,
4423 4440 autoreadstderr=autoreadstderr,
4424 4441 )
4425 4442 elif opts[b'peer'] == b'raw':
4426 4443 ui.write(_(b'using raw connection to peer\n'))
4427 4444 peer = None
4428 4445 else:
4429 4446 ui.write(_(b'creating ssh peer from handshake results\n'))
4430 4447 peer = sshpeer.makepeer(
4431 4448 ui,
4432 4449 url,
4433 4450 proc,
4434 4451 stdin,
4435 4452 stdout,
4436 4453 stderr,
4437 4454 autoreadstderr=autoreadstderr,
4438 4455 )
4439 4456
4440 4457 elif path:
4441 4458 # We bypass hg.peer() so we can proxy the sockets.
4442 4459 # TODO consider not doing this because we skip
4443 4460 # ``hg.wirepeersetupfuncs`` and potentially other useful functionality.
4444 4461 u = util.url(path)
4445 4462 if u.scheme != b'http':
4446 4463 raise error.Abort(_(b'only http:// paths are currently supported'))
4447 4464
4448 4465 url, authinfo = u.authinfo()
4449 4466 openerargs = {
4450 4467 'useragent': b'Mercurial debugwireproto',
4451 4468 }
4452 4469
4453 4470 # Turn pipes/sockets into observers so we can log I/O.
4454 4471 if ui.verbose:
4455 4472 openerargs.update(
4456 4473 {
4457 4474 'loggingfh': ui,
4458 4475 'loggingname': b's',
4459 4476 'loggingopts': {
4460 4477 'logdata': True,
4461 4478 'logdataapis': False,
4462 4479 },
4463 4480 }
4464 4481 )
4465 4482
4466 4483 if ui.debugflag:
4467 4484 openerargs['loggingopts']['logdataapis'] = True
4468 4485
4469 4486 # Don't send default headers when in raw mode. This allows us to
4470 4487 # bypass most of the behavior of our URL handling code so we can
4471 4488 # have near complete control over what's sent on the wire.
4472 4489 if opts[b'peer'] == b'raw':
4473 4490 openerargs['sendaccept'] = False
4474 4491
4475 4492 opener = urlmod.opener(ui, authinfo, **openerargs)
4476 4493
4477 4494 if opts[b'peer'] == b'http2':
4478 4495 ui.write(_(b'creating http peer for wire protocol version 2\n'))
4479 4496 # We go through makepeer() because we need an API descriptor for
4480 4497 # the peer instance to be useful.
4481 4498 with ui.configoverride(
4482 4499 {(b'experimental', b'httppeer.advertise-v2'): True}
4483 4500 ):
4484 4501 if opts[b'nologhandshake']:
4485 4502 ui.pushbuffer()
4486 4503
4487 4504 peer = httppeer.makepeer(ui, path, opener=opener)
4488 4505
4489 4506 if opts[b'nologhandshake']:
4490 4507 ui.popbuffer()
4491 4508
4492 4509 if not isinstance(peer, httppeer.httpv2peer):
4493 4510 raise error.Abort(
4494 4511 _(
4495 4512 b'could not instantiate HTTP peer for '
4496 4513 b'wire protocol version 2'
4497 4514 ),
4498 4515 hint=_(
4499 4516 b'the server may not have the feature '
4500 4517 b'enabled or is not allowing this '
4501 4518 b'client version'
4502 4519 ),
4503 4520 )
4504 4521
4505 4522 elif opts[b'peer'] == b'raw':
4506 4523 ui.write(_(b'using raw connection to peer\n'))
4507 4524 peer = None
4508 4525 elif opts[b'peer']:
4509 4526 raise error.Abort(
4510 4527 _(b'--peer %s not supported with HTTP peers') % opts[b'peer']
4511 4528 )
4512 4529 else:
4513 4530 peer = httppeer.makepeer(ui, path, opener=opener)
4514 4531
4515 4532 # We /could/ populate stdin/stdout with sock.makefile()...
4516 4533 else:
4517 4534 raise error.Abort(_(b'unsupported connection configuration'))
4518 4535
4519 4536 batchedcommands = None
4520 4537
4521 4538 # Now perform actions based on the parsed wire language instructions.
4522 4539 for action, lines in blocks:
4523 4540 if action in (b'raw', b'raw+'):
4524 4541 if not stdin:
4525 4542 raise error.Abort(_(b'cannot call raw/raw+ on this peer'))
4526 4543
4527 4544 # Concatenate the data together.
4528 4545 data = b''.join(l.lstrip() for l in lines)
4529 4546 data = stringutil.unescapestr(data)
4530 4547 stdin.write(data)
4531 4548
4532 4549 if action == b'raw+':
4533 4550 stdin.flush()
4534 4551 elif action == b'flush':
4535 4552 if not stdin:
4536 4553 raise error.Abort(_(b'cannot call flush on this peer'))
4537 4554 stdin.flush()
4538 4555 elif action.startswith(b'command'):
4539 4556 if not peer:
4540 4557 raise error.Abort(
4541 4558 _(
4542 4559 b'cannot send commands unless peer instance '
4543 4560 b'is available'
4544 4561 )
4545 4562 )
4546 4563
4547 4564 command = action.split(b' ', 1)[1]
4548 4565
4549 4566 args = {}
4550 4567 for line in lines:
4551 4568 # We need to allow empty values.
4552 4569 fields = line.lstrip().split(b' ', 1)
4553 4570 if len(fields) == 1:
4554 4571 key = fields[0]
4555 4572 value = b''
4556 4573 else:
4557 4574 key, value = fields
4558 4575
4559 4576 if value.startswith(b'eval:'):
4560 4577 value = stringutil.evalpythonliteral(value[5:])
4561 4578 else:
4562 4579 value = stringutil.unescapestr(value)
4563 4580
4564 4581 args[key] = value
4565 4582
4566 4583 if batchedcommands is not None:
4567 4584 batchedcommands.append((command, args))
4568 4585 continue
4569 4586
4570 4587 ui.status(_(b'sending %s command\n') % command)
4571 4588
4572 4589 if b'PUSHFILE' in args:
4573 4590 with open(args[b'PUSHFILE'], 'rb') as fh:
4574 4591 del args[b'PUSHFILE']
4575 4592 res, output = peer._callpush(
4576 4593 command, fh, **pycompat.strkwargs(args)
4577 4594 )
4578 4595 ui.status(_(b'result: %s\n') % stringutil.escapestr(res))
4579 4596 ui.status(
4580 4597 _(b'remote output: %s\n') % stringutil.escapestr(output)
4581 4598 )
4582 4599 else:
4583 4600 with peer.commandexecutor() as e:
4584 4601 res = e.callcommand(command, args).result()
4585 4602
4586 4603 if isinstance(res, wireprotov2peer.commandresponse):
4587 4604 val = res.objects()
4588 4605 ui.status(
4589 4606 _(b'response: %s\n')
4590 4607 % stringutil.pprint(val, bprefix=True, indent=2)
4591 4608 )
4592 4609 else:
4593 4610 ui.status(
4594 4611 _(b'response: %s\n')
4595 4612 % stringutil.pprint(res, bprefix=True, indent=2)
4596 4613 )
4597 4614
4598 4615 elif action == b'batchbegin':
4599 4616 if batchedcommands is not None:
4600 4617 raise error.Abort(_(b'nested batchbegin not allowed'))
4601 4618
4602 4619 batchedcommands = []
4603 4620 elif action == b'batchsubmit':
4604 4621 # There is a batching API we could go through. But it would be
4605 4622 # difficult to normalize requests into function calls. It is easier
4606 4623 # to bypass this layer and normalize to commands + args.
4607 4624 ui.status(
4608 4625 _(b'sending batch with %d sub-commands\n')
4609 4626 % len(batchedcommands)
4610 4627 )
4611 4628 assert peer is not None
4612 4629 for i, chunk in enumerate(peer._submitbatch(batchedcommands)):
4613 4630 ui.status(
4614 4631 _(b'response #%d: %s\n') % (i, stringutil.escapestr(chunk))
4615 4632 )
4616 4633
4617 4634 batchedcommands = None
4618 4635
4619 4636 elif action.startswith(b'httprequest '):
4620 4637 if not opener:
4621 4638 raise error.Abort(
4622 4639 _(b'cannot use httprequest without an HTTP peer')
4623 4640 )
4624 4641
4625 4642 request = action.split(b' ', 2)
4626 4643 if len(request) != 3:
4627 4644 raise error.Abort(
4628 4645 _(
4629 4646 b'invalid httprequest: expected format is '
4630 4647 b'"httprequest <method> <path>'
4631 4648 )
4632 4649 )
4633 4650
4634 4651 method, httppath = request[1:]
4635 4652 headers = {}
4636 4653 body = None
4637 4654 frames = []
4638 4655 for line in lines:
4639 4656 line = line.lstrip()
4640 4657 m = re.match(b'^([a-zA-Z0-9_-]+): (.*)$', line)
4641 4658 if m:
4642 4659 # Headers need to use native strings.
4643 4660 key = pycompat.strurl(m.group(1))
4644 4661 value = pycompat.strurl(m.group(2))
4645 4662 headers[key] = value
4646 4663 continue
4647 4664
4648 4665 if line.startswith(b'BODYFILE '):
4649 4666 with open(line.split(b' ', 1), b'rb') as fh:
4650 4667 body = fh.read()
4651 4668 elif line.startswith(b'frame '):
4652 4669 frame = wireprotoframing.makeframefromhumanstring(
4653 4670 line[len(b'frame ') :]
4654 4671 )
4655 4672
4656 4673 frames.append(frame)
4657 4674 else:
4658 4675 raise error.Abort(
4659 4676 _(b'unknown argument to httprequest: %s') % line
4660 4677 )
4661 4678
4662 4679 url = path + httppath
4663 4680
4664 4681 if frames:
4665 4682 body = b''.join(bytes(f) for f in frames)
4666 4683
4667 4684 req = urlmod.urlreq.request(pycompat.strurl(url), body, headers)
4668 4685
4669 4686 # urllib.Request insists on using has_data() as a proxy for
4670 4687 # determining the request method. Override that to use our
4671 4688 # explicitly requested method.
4672 4689 req.get_method = lambda: pycompat.sysstr(method)
4673 4690
4674 4691 try:
4675 4692 res = opener.open(req)
4676 4693 body = res.read()
4677 4694 except util.urlerr.urlerror as e:
4678 4695 # read() method must be called, but only exists in Python 2
4679 4696 getattr(e, 'read', lambda: None)()
4680 4697 continue
4681 4698
4682 4699 ct = res.headers.get('Content-Type')
4683 4700 if ct == 'application/mercurial-cbor':
4684 4701 ui.write(
4685 4702 _(b'cbor> %s\n')
4686 4703 % stringutil.pprint(
4687 4704 cborutil.decodeall(body), bprefix=True, indent=2
4688 4705 )
4689 4706 )
4690 4707
4691 4708 elif action == b'close':
4692 4709 assert peer is not None
4693 4710 peer.close()
4694 4711 elif action == b'readavailable':
4695 4712 if not stdout or not stderr:
4696 4713 raise error.Abort(
4697 4714 _(b'readavailable not available on this peer')
4698 4715 )
4699 4716
4700 4717 stdin.close()
4701 4718 stdout.read()
4702 4719 stderr.read()
4703 4720
4704 4721 elif action == b'readline':
4705 4722 if not stdout:
4706 4723 raise error.Abort(_(b'readline not available on this peer'))
4707 4724 stdout.readline()
4708 4725 elif action == b'ereadline':
4709 4726 if not stderr:
4710 4727 raise error.Abort(_(b'ereadline not available on this peer'))
4711 4728 stderr.readline()
4712 4729 elif action.startswith(b'read '):
4713 4730 count = int(action.split(b' ', 1)[1])
4714 4731 if not stdout:
4715 4732 raise error.Abort(_(b'read not available on this peer'))
4716 4733 stdout.read(count)
4717 4734 elif action.startswith(b'eread '):
4718 4735 count = int(action.split(b' ', 1)[1])
4719 4736 if not stderr:
4720 4737 raise error.Abort(_(b'eread not available on this peer'))
4721 4738 stderr.read(count)
4722 4739 else:
4723 4740 raise error.Abort(_(b'unknown action: %s') % action)
4724 4741
4725 4742 if batchedcommands is not None:
4726 4743 raise error.Abort(_(b'unclosed "batchbegin" request'))
4727 4744
4728 4745 if peer:
4729 4746 peer.close()
4730 4747
4731 4748 if proc:
4732 4749 proc.kill()
@@ -1,443 +1,443 b''
1 1 Show all commands except debug commands
2 2 $ hg debugcomplete
3 3 abort
4 4 add
5 5 addremove
6 6 annotate
7 7 archive
8 8 backout
9 9 bisect
10 10 bookmarks
11 11 branch
12 12 branches
13 13 bundle
14 14 cat
15 15 clone
16 16 commit
17 17 config
18 18 continue
19 19 copy
20 20 diff
21 21 export
22 22 files
23 23 forget
24 24 graft
25 25 grep
26 26 heads
27 27 help
28 28 identify
29 29 import
30 30 incoming
31 31 init
32 32 locate
33 33 log
34 34 manifest
35 35 merge
36 36 outgoing
37 37 parents
38 38 paths
39 39 phase
40 40 pull
41 41 purge
42 42 push
43 43 recover
44 44 remove
45 45 rename
46 46 resolve
47 47 revert
48 48 rollback
49 49 root
50 50 serve
51 51 shelve
52 52 status
53 53 summary
54 54 tag
55 55 tags
56 56 tip
57 57 unbundle
58 58 unshelve
59 59 update
60 60 verify
61 61 version
62 62
63 63 Show all commands that start with "a"
64 64 $ hg debugcomplete a
65 65 abort
66 66 add
67 67 addremove
68 68 annotate
69 69 archive
70 70
71 71 Do not show debug commands if there are other candidates
72 72 $ hg debugcomplete d
73 73 diff
74 74
75 75 Show debug commands if there are no other candidates
76 76 $ hg debugcomplete debug
77 77 debugancestor
78 78 debugantivirusrunning
79 79 debugapplystreamclonebundle
80 80 debugbackupbundle
81 81 debugbuilddag
82 82 debugbundle
83 83 debugcapabilities
84 84 debugchangedfiles
85 85 debugcheckstate
86 86 debugcolor
87 87 debugcommands
88 88 debugcomplete
89 89 debugconfig
90 90 debugcreatestreamclonebundle
91 91 debugdag
92 92 debugdata
93 93 debugdate
94 94 debugdeltachain
95 95 debugdirstate
96 96 debugdiscovery
97 97 debugdownload
98 98 debugextensions
99 99 debugfileset
100 100 debugformat
101 101 debugfsinfo
102 102 debuggetbundle
103 103 debugignore
104 104 debugindex
105 105 debugindexdot
106 106 debugindexstats
107 107 debuginstall
108 108 debugknown
109 109 debuglabelcomplete
110 110 debuglocks
111 111 debugmanifestfulltextcache
112 112 debugmergestate
113 113 debugnamecomplete
114 114 debugnodemap
115 115 debugobsolete
116 116 debugp1copies
117 117 debugp2copies
118 118 debugpathcomplete
119 119 debugpathcopies
120 120 debugpeer
121 121 debugpickmergetool
122 122 debugpushkey
123 123 debugpvec
124 124 debugrebuilddirstate
125 125 debugrebuildfncache
126 126 debugrename
127 127 debugrequires
128 128 debugrevlog
129 129 debugrevlogindex
130 130 debugrevspec
131 131 debugserve
132 132 debugsetparents
133 133 debugshell
134 134 debugsidedata
135 135 debugssl
136 136 debugstrip
137 137 debugsub
138 138 debugsuccessorssets
139 139 debugtagscache
140 140 debugtemplate
141 141 debuguigetpass
142 142 debuguiprompt
143 143 debugupdatecaches
144 144 debugupgraderepo
145 145 debugwalk
146 146 debugwhyunstable
147 147 debugwireargs
148 148 debugwireproto
149 149
150 150 Do not show the alias of a debug command if there are other candidates
151 151 (this should hide rawcommit)
152 152 $ hg debugcomplete r
153 153 recover
154 154 remove
155 155 rename
156 156 resolve
157 157 revert
158 158 rollback
159 159 root
160 160 Show the alias of a debug command if there are no other candidates
161 161 $ hg debugcomplete rawc
162 162
163 163
164 164 Show the global options
165 165 $ hg debugcomplete --options | sort
166 166 --color
167 167 --config
168 168 --cwd
169 169 --debug
170 170 --debugger
171 171 --encoding
172 172 --encodingmode
173 173 --help
174 174 --hidden
175 175 --noninteractive
176 176 --pager
177 177 --profile
178 178 --quiet
179 179 --repository
180 180 --time
181 181 --traceback
182 182 --verbose
183 183 --version
184 184 -R
185 185 -h
186 186 -q
187 187 -v
188 188 -y
189 189
190 190 Show the options for the "serve" command
191 191 $ hg debugcomplete --options serve | sort
192 192 --accesslog
193 193 --address
194 194 --certificate
195 195 --cmdserver
196 196 --color
197 197 --config
198 198 --cwd
199 199 --daemon
200 200 --daemon-postexec
201 201 --debug
202 202 --debugger
203 203 --encoding
204 204 --encodingmode
205 205 --errorlog
206 206 --help
207 207 --hidden
208 208 --ipv6
209 209 --name
210 210 --noninteractive
211 211 --pager
212 212 --pid-file
213 213 --port
214 214 --prefix
215 215 --print-url
216 216 --profile
217 217 --quiet
218 218 --repository
219 219 --stdio
220 220 --style
221 221 --subrepos
222 222 --templates
223 223 --time
224 224 --traceback
225 225 --verbose
226 226 --version
227 227 --web-conf
228 228 -6
229 229 -A
230 230 -E
231 231 -R
232 232 -S
233 233 -a
234 234 -d
235 235 -h
236 236 -n
237 237 -p
238 238 -q
239 239 -t
240 240 -v
241 241 -y
242 242
243 243 Show an error if we use --options with an ambiguous abbreviation
244 244 $ hg debugcomplete --options s
245 245 hg: command 's' is ambiguous:
246 246 serve shelve showconfig status summary
247 247 [10]
248 248
249 249 Show all commands + options
250 250 $ hg debugcommands
251 251 abort: dry-run
252 252 add: include, exclude, subrepos, dry-run
253 253 addremove: similarity, subrepos, include, exclude, dry-run
254 254 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
255 255 archive: no-decode, prefix, rev, type, subrepos, include, exclude
256 256 backout: merge, commit, no-commit, parent, rev, edit, tool, include, exclude, message, logfile, date, user
257 257 bisect: reset, good, bad, skip, extend, command, noupdate
258 258 bookmarks: force, rev, delete, rename, inactive, list, template
259 259 branch: force, clean, rev
260 260 branches: active, closed, rev, template
261 261 bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure
262 262 cat: output, rev, decode, include, exclude, template
263 263 clone: noupdate, updaterev, rev, branch, pull, uncompressed, stream, ssh, remotecmd, insecure
264 264 commit: addremove, close-branch, amend, secret, edit, force-close-branch, interactive, include, exclude, message, logfile, date, user, subrepos
265 265 config: untrusted, edit, local, shared, non-shared, global, template
266 266 continue: dry-run
267 267 copy: forget, after, at-rev, force, include, exclude, dry-run
268 268 debugancestor:
269 269 debugantivirusrunning:
270 270 debugapplystreamclonebundle:
271 271 debugbackupbundle: recover, patch, git, limit, no-merges, stat, graph, style, template
272 272 debugbuilddag: mergeable-file, overwritten-file, new-file
273 273 debugbundle: all, part-type, spec
274 274 debugcapabilities:
275 debugchangedfiles:
275 debugchangedfiles: compute
276 276 debugcheckstate:
277 277 debugcolor: style
278 278 debugcommands:
279 279 debugcomplete: options
280 280 debugcreatestreamclonebundle:
281 281 debugdag: tags, branches, dots, spaces
282 282 debugdata: changelog, manifest, dir
283 283 debugdate: extended
284 284 debugdeltachain: changelog, manifest, dir, template
285 285 debugdirstate: nodates, dates, datesort
286 286 debugdiscovery: old, nonheads, rev, seed, local-as-revs, remote-as-revs, ssh, remotecmd, insecure
287 287 debugdownload: output
288 288 debugextensions: template
289 289 debugfileset: rev, all-files, show-matcher, show-stage
290 290 debugformat: template
291 291 debugfsinfo:
292 292 debuggetbundle: head, common, type
293 293 debugignore:
294 294 debugindex: changelog, manifest, dir, template
295 295 debugindexdot: changelog, manifest, dir
296 296 debugindexstats:
297 297 debuginstall: template
298 298 debugknown:
299 299 debuglabelcomplete:
300 300 debuglocks: force-free-lock, force-free-wlock, set-lock, set-wlock
301 301 debugmanifestfulltextcache: clear, add
302 302 debugmergestate: style, template
303 303 debugnamecomplete:
304 304 debugnodemap: dump-new, dump-disk, check, metadata
305 305 debugobsolete: flags, record-parents, rev, exclusive, index, delete, date, user, template
306 306 debugp1copies: rev
307 307 debugp2copies: rev
308 308 debugpathcomplete: full, normal, added, removed
309 309 debugpathcopies: include, exclude
310 310 debugpeer:
311 311 debugpickmergetool: rev, changedelete, include, exclude, tool
312 312 debugpushkey:
313 313 debugpvec:
314 314 debugrebuilddirstate: rev, minimal
315 315 debugrebuildfncache:
316 316 debugrename: rev
317 317 debugrequires:
318 318 debugrevlog: changelog, manifest, dir, dump
319 319 debugrevlogindex: changelog, manifest, dir, format
320 320 debugrevspec: optimize, show-revs, show-set, show-stage, no-optimized, verify-optimized
321 321 debugserve: sshstdio, logiofd, logiofile
322 322 debugsetparents:
323 323 debugshell:
324 324 debugsidedata: changelog, manifest, dir
325 325 debugssl:
326 326 debugstrip: rev, force, no-backup, nobackup, , keep, bookmark, soft
327 327 debugsub: rev
328 328 debugsuccessorssets: closest
329 329 debugtagscache:
330 330 debugtemplate: rev, define
331 331 debuguigetpass: prompt
332 332 debuguiprompt: prompt
333 333 debugupdatecaches:
334 334 debugupgraderepo: optimize, run, backup, changelog, manifest, filelogs
335 335 debugwalk: include, exclude
336 336 debugwhyunstable:
337 337 debugwireargs: three, four, five, ssh, remotecmd, insecure
338 338 debugwireproto: localssh, peer, noreadstderr, nologhandshake, ssh, remotecmd, insecure
339 339 diff: rev, from, to, change, merge, 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
340 340 export: bookmark, output, switch-parent, rev, text, git, binary, nodates, template
341 341 files: rev, print0, include, exclude, template, subrepos
342 342 forget: interactive, include, exclude, dry-run
343 343 graft: rev, base, continue, stop, abort, edit, log, no-commit, force, currentdate, currentuser, date, user, tool, dry-run
344 344 grep: print0, all, diff, text, follow, ignore-case, files-with-matches, line-number, rev, all-files, user, date, template, include, exclude
345 345 heads: rev, topo, active, closed, style, template
346 346 help: extension, command, keyword, system
347 347 identify: rev, num, id, branch, tags, bookmarks, ssh, remotecmd, insecure, template
348 348 import: strip, base, secret, edit, force, no-commit, bypass, partial, exact, prefix, import-branch, message, logfile, date, user, similarity
349 349 incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
350 350 init: ssh, remotecmd, insecure
351 351 locate: rev, print0, fullpath, include, exclude
352 352 log: follow, follow-first, date, copies, keyword, rev, line-range, removed, only-merges, user, only-branch, branch, bookmark, prune, patch, git, limit, no-merges, stat, graph, style, template, include, exclude
353 353 manifest: rev, all, template
354 354 merge: force, rev, preview, abort, tool
355 355 outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
356 356 parents: rev, style, template
357 357 paths: template
358 358 phase: public, draft, secret, force, rev
359 359 pull: update, force, confirm, rev, bookmark, branch, ssh, remotecmd, insecure
360 360 purge: abort-on-err, all, ignored, dirs, files, print, print0, confirm, include, exclude
361 361 push: force, rev, bookmark, all-bookmarks, branch, new-branch, pushvars, publish, ssh, remotecmd, insecure
362 362 recover: verify
363 363 remove: after, force, subrepos, include, exclude, dry-run
364 364 rename: after, at-rev, force, include, exclude, dry-run
365 365 resolve: all, list, mark, unmark, no-status, re-merge, tool, include, exclude, template
366 366 revert: all, date, rev, no-backup, interactive, include, exclude, dry-run
367 367 rollback: dry-run, force
368 368 root: template
369 369 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
370 370 shelve: addremove, unknown, cleanup, date, delete, edit, keep, list, message, name, patch, interactive, stat, include, exclude
371 371 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, terse, copies, print0, rev, change, include, exclude, subrepos, template
372 372 summary: remote
373 373 tag: force, local, rev, remove, edit, message, date, user
374 374 tags: template
375 375 tip: patch, git, style, template
376 376 unbundle: update
377 377 unshelve: abort, continue, interactive, keep, name, tool, date
378 378 update: clean, check, merge, date, rev, tool
379 379 verify: full
380 380 version: template
381 381
382 382 $ hg init a
383 383 $ cd a
384 384 $ echo fee > fee
385 385 $ hg ci -q -Amfee
386 386 $ hg tag fee
387 387 $ mkdir fie
388 388 $ echo dead > fie/dead
389 389 $ echo live > fie/live
390 390 $ hg bookmark fo
391 391 $ hg branch -q fie
392 392 $ hg ci -q -Amfie
393 393 $ echo fo > fo
394 394 $ hg branch -qf default
395 395 $ hg ci -q -Amfo
396 396 $ echo Fum > Fum
397 397 $ hg ci -q -AmFum
398 398 $ hg bookmark Fum
399 399
400 400 Test debugpathcomplete
401 401
402 402 $ hg debugpathcomplete f
403 403 fee
404 404 fie
405 405 fo
406 406 $ hg debugpathcomplete -f f
407 407 fee
408 408 fie/dead
409 409 fie/live
410 410 fo
411 411
412 412 $ hg rm Fum
413 413 $ hg debugpathcomplete -r F
414 414 Fum
415 415
416 416 Test debugnamecomplete
417 417
418 418 $ hg debugnamecomplete
419 419 Fum
420 420 default
421 421 fee
422 422 fie
423 423 fo
424 424 tip
425 425 $ hg debugnamecomplete f
426 426 fee
427 427 fie
428 428 fo
429 429
430 430 Test debuglabelcomplete, a deprecated name for debugnamecomplete that is still
431 431 used for completions in some shells.
432 432
433 433 $ hg debuglabelcomplete
434 434 Fum
435 435 default
436 436 fee
437 437 fie
438 438 fo
439 439 tip
440 440 $ hg debuglabelcomplete f
441 441 fee
442 442 fie
443 443 fo
@@ -1,1754 +1,1759 b''
1 1 #testcases filelog compatibility changeset sidedata upgraded
2 2
3 3 =====================================================
4 4 Test Copy tracing for chain of copies involving merge
5 5 =====================================================
6 6
7 7 This test files covers copies/rename case for a chains of commit where merges
8 8 are involved. It cheks we do not have unwanted update of behavior and that the
9 9 different options to retrieve copies behave correctly.
10 10
11 11
12 12 Setup
13 13 =====
14 14
15 15 use git diff to see rename
16 16
17 17 $ cat << EOF >> $HGRCPATH
18 18 > [diff]
19 19 > git=yes
20 20 > [command-templates]
21 21 > log={rev} {desc}\n
22 22 > EOF
23 23
24 24 #if compatibility
25 25 $ cat >> $HGRCPATH << EOF
26 26 > [experimental]
27 27 > copies.read-from = compatibility
28 28 > EOF
29 29 #endif
30 30
31 31 #if changeset
32 32 $ cat >> $HGRCPATH << EOF
33 33 > [experimental]
34 34 > copies.read-from = changeset-only
35 35 > copies.write-to = changeset-only
36 36 > EOF
37 37 #endif
38 38
39 39 #if sidedata
40 40 $ cat >> $HGRCPATH << EOF
41 41 > [format]
42 42 > exp-use-side-data = yes
43 43 > exp-use-copies-side-data-changeset = yes
44 44 > EOF
45 45 #endif
46 46
47 47
48 48 $ hg init repo-chain
49 49 $ cd repo-chain
50 50
51 51 Add some linear rename initialy
52 52
53 53 $ echo a > a
54 54 $ echo b > b
55 55 $ echo h > h
56 56 $ hg ci -Am 'i-0 initial commit: a b h'
57 57 adding a
58 58 adding b
59 59 adding h
60 60 $ hg mv a c
61 61 $ hg ci -Am 'i-1: a -move-> c'
62 62 $ hg mv c d
63 63 $ hg ci -Am 'i-2: c -move-> d'
64 64 $ hg log -G
65 65 @ 2 i-2: c -move-> d
66 66 |
67 67 o 1 i-1: a -move-> c
68 68 |
69 69 o 0 i-0 initial commit: a b h
70 70
71 71
72 72 And having another branch with renames on the other side
73 73
74 74 $ hg mv d e
75 75 $ hg ci -Am 'a-1: d -move-> e'
76 76 $ hg mv e f
77 77 $ hg ci -Am 'a-2: e -move-> f'
78 78 $ hg log -G --rev '::.'
79 79 @ 4 a-2: e -move-> f
80 80 |
81 81 o 3 a-1: d -move-> e
82 82 |
83 83 o 2 i-2: c -move-> d
84 84 |
85 85 o 1 i-1: a -move-> c
86 86 |
87 87 o 0 i-0 initial commit: a b h
88 88
89 89
90 90 Have a branching with nothing on one side
91 91
92 92 $ hg up 'desc("i-2")'
93 93 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
94 94 $ echo foo > b
95 95 $ hg ci -m 'b-1: b update'
96 96 created new head
97 97 $ hg log -G --rev '::.'
98 98 @ 5 b-1: b update
99 99 |
100 100 o 2 i-2: c -move-> d
101 101 |
102 102 o 1 i-1: a -move-> c
103 103 |
104 104 o 0 i-0 initial commit: a b h
105 105
106 106
107 107 Create a branch that delete a file previous renamed
108 108
109 109 $ hg up 'desc("i-2")'
110 110 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
111 111 $ hg rm d
112 112 $ hg ci -m 'c-1 delete d'
113 113 created new head
114 114 $ hg log -G --rev '::.'
115 115 @ 6 c-1 delete d
116 116 |
117 117 o 2 i-2: c -move-> d
118 118 |
119 119 o 1 i-1: a -move-> c
120 120 |
121 121 o 0 i-0 initial commit: a b h
122 122
123 123
124 124 Create a branch that delete a file previous renamed and recreate it
125 125
126 126 $ hg up 'desc("i-2")'
127 127 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
128 128 $ hg rm d
129 129 $ hg ci -m 'd-1 delete d'
130 130 created new head
131 131 $ echo bar > d
132 132 $ hg add d
133 133 $ hg ci -m 'd-2 re-add d'
134 134 $ hg log -G --rev '::.'
135 135 @ 8 d-2 re-add d
136 136 |
137 137 o 7 d-1 delete d
138 138 |
139 139 o 2 i-2: c -move-> d
140 140 |
141 141 o 1 i-1: a -move-> c
142 142 |
143 143 o 0 i-0 initial commit: a b h
144 144
145 145
146 146 Having another branch renaming a different file to the same filename as another
147 147
148 148 $ hg up 'desc("i-2")'
149 149 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
150 150 $ hg mv b g
151 151 $ hg ci -m 'e-1 b -move-> g'
152 152 created new head
153 153 $ hg mv g f
154 154 $ hg ci -m 'e-2 g -move-> f'
155 155 $ hg log -G --rev '::.'
156 156 @ 10 e-2 g -move-> f
157 157 |
158 158 o 9 e-1 b -move-> g
159 159 |
160 160 o 2 i-2: c -move-> d
161 161 |
162 162 o 1 i-1: a -move-> c
163 163 |
164 164 o 0 i-0 initial commit: a b h
165 165
166 166
167 167 Setup all merge
168 168 ===============
169 169
170 170 This is done beforehand to validate that the upgrade process creates valid copy
171 171 information.
172 172
173 173 merging with unrelated change does not interfere with the renames
174 174 ---------------------------------------------------------------
175 175
176 176 - rename on one side
177 177 - unrelated change on the other side
178 178
179 179 $ hg up 'desc("b-1")'
180 180 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
181 181 $ hg merge 'desc("a-2")'
182 182 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
183 183 (branch merge, don't forget to commit)
184 184 $ hg ci -m 'mBAm-0 simple merge - one way'
185 185 $ hg up 'desc("a-2")'
186 186 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
187 187 $ hg merge 'desc("b-1")'
188 188 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
189 189 (branch merge, don't forget to commit)
190 190 $ hg ci -m 'mABm-0 simple merge - the other way'
191 191 created new head
192 192 $ hg log -G --rev '::(desc("mABm")+desc("mBAm"))'
193 193 @ 12 mABm-0 simple merge - the other way
194 194 |\
195 195 +---o 11 mBAm-0 simple merge - one way
196 196 | |/
197 197 | o 5 b-1: b update
198 198 | |
199 199 o | 4 a-2: e -move-> f
200 200 | |
201 201 o | 3 a-1: d -move-> e
202 202 |/
203 203 o 2 i-2: c -move-> d
204 204 |
205 205 o 1 i-1: a -move-> c
206 206 |
207 207 o 0 i-0 initial commit: a b h
208 208
209 209
210 210
211 211 merging with the side having a delete
212 212 -------------------------------------
213 213
214 214 case summary:
215 215 - one with change to an unrelated file
216 216 - one deleting the change
217 217 and recreate an unrelated file after the merge
218 218
219 219 $ hg up 'desc("b-1")'
220 220 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
221 221 $ hg merge 'desc("c-1")'
222 222 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
223 223 (branch merge, don't forget to commit)
224 224 $ hg ci -m 'mBCm-0 simple merge - one way'
225 225 $ echo bar > d
226 226 $ hg add d
227 227 $ hg ci -m 'mBCm-1 re-add d'
228 228 $ hg up 'desc("c-1")'
229 229 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
230 230 $ hg merge 'desc("b-1")'
231 231 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
232 232 (branch merge, don't forget to commit)
233 233 $ hg ci -m 'mCBm-0 simple merge - the other way'
234 234 created new head
235 235 $ echo bar > d
236 236 $ hg add d
237 237 $ hg ci -m 'mCBm-1 re-add d'
238 238 $ hg log -G --rev '::(desc("mCBm")+desc("mBCm"))'
239 239 @ 16 mCBm-1 re-add d
240 240 |
241 241 o 15 mCBm-0 simple merge - the other way
242 242 |\
243 243 | | o 14 mBCm-1 re-add d
244 244 | | |
245 245 +---o 13 mBCm-0 simple merge - one way
246 246 | |/
247 247 | o 6 c-1 delete d
248 248 | |
249 249 o | 5 b-1: b update
250 250 |/
251 251 o 2 i-2: c -move-> d
252 252 |
253 253 o 1 i-1: a -move-> c
254 254 |
255 255 o 0 i-0 initial commit: a b h
256 256
257 257
258 258 Comparing with a merge re-adding the file afterward
259 259 ---------------------------------------------------
260 260
261 261 Merge:
262 262 - one with change to an unrelated file
263 263 - one deleting and recreating the change
264 264
265 265 $ hg up 'desc("b-1")'
266 266 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
267 267 $ hg merge 'desc("d-2")'
268 268 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
269 269 (branch merge, don't forget to commit)
270 270 $ hg ci -m 'mBDm-0 simple merge - one way'
271 271 $ hg up 'desc("d-2")'
272 272 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
273 273 $ hg merge 'desc("b-1")'
274 274 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
275 275 (branch merge, don't forget to commit)
276 276 $ hg ci -m 'mDBm-0 simple merge - the other way'
277 277 created new head
278 278 $ hg log -G --rev '::(desc("mDBm")+desc("mBDm"))'
279 279 @ 18 mDBm-0 simple merge - the other way
280 280 |\
281 281 +---o 17 mBDm-0 simple merge - one way
282 282 | |/
283 283 | o 8 d-2 re-add d
284 284 | |
285 285 | o 7 d-1 delete d
286 286 | |
287 287 o | 5 b-1: b update
288 288 |/
289 289 o 2 i-2: c -move-> d
290 290 |
291 291 o 1 i-1: a -move-> c
292 292 |
293 293 o 0 i-0 initial commit: a b h
294 294
295 295
296 296
297 297 Comparing with a merge with colliding rename
298 298 --------------------------------------------
299 299
300 300 - the "e-" branch renaming b to f (through 'g')
301 301 - the "a-" branch renaming d to f (through e)
302 302
303 303 $ hg up 'desc("a-2")'
304 304 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
305 305 $ hg merge 'desc("e-2")' --tool :union
306 306 merging f
307 307 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
308 308 (branch merge, don't forget to commit)
309 309 $ hg ci -m 'mAEm-0 simple merge - one way'
310 310 $ hg up 'desc("e-2")'
311 311 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
312 312 $ hg merge 'desc("a-2")' --tool :union
313 313 merging f
314 314 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
315 315 (branch merge, don't forget to commit)
316 316 $ hg ci -m 'mEAm-0 simple merge - the other way'
317 317 created new head
318 318 $ hg log -G --rev '::(desc("mAEm")+desc("mEAm"))'
319 319 @ 20 mEAm-0 simple merge - the other way
320 320 |\
321 321 +---o 19 mAEm-0 simple merge - one way
322 322 | |/
323 323 | o 10 e-2 g -move-> f
324 324 | |
325 325 | o 9 e-1 b -move-> g
326 326 | |
327 327 o | 4 a-2: e -move-> f
328 328 | |
329 329 o | 3 a-1: d -move-> e
330 330 |/
331 331 o 2 i-2: c -move-> d
332 332 |
333 333 o 1 i-1: a -move-> c
334 334 |
335 335 o 0 i-0 initial commit: a b h
336 336
337 337
338 338
339 339 Merge:
340 340 - one with change to an unrelated file (b)
341 341 - one overwriting a file (d) with a rename (from h to i to d)
342 342
343 343 $ hg up 'desc("i-2")'
344 344 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
345 345 $ hg mv h i
346 346 $ hg commit -m "f-1: rename h -> i"
347 347 created new head
348 348 $ hg mv --force i d
349 349 $ hg commit -m "f-2: rename i -> d"
350 350 $ hg debugindex d
351 351 rev linkrev nodeid p1 p2
352 352 0 2 169be882533b 000000000000 000000000000 (no-changeset !)
353 353 0 2 b789fdd96dc2 000000000000 000000000000 (changeset !)
354 354 1 8 b004912a8510 000000000000 000000000000
355 355 2 22 4a067cf8965d 000000000000 000000000000 (no-changeset !)
356 356 2 22 fe6f8b4f507f 000000000000 000000000000 (changeset !)
357 357 $ hg up 'desc("b-1")'
358 358 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
359 359 $ hg merge 'desc("f-2")'
360 360 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
361 361 (branch merge, don't forget to commit)
362 362 $ hg ci -m 'mBFm-0 simple merge - one way'
363 363 $ hg up 'desc("f-2")'
364 364 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
365 365 $ hg merge 'desc("b-1")'
366 366 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
367 367 (branch merge, don't forget to commit)
368 368 $ hg ci -m 'mFBm-0 simple merge - the other way'
369 369 created new head
370 370 $ hg log -G --rev '::(desc("mBFm")+desc("mFBm"))'
371 371 @ 24 mFBm-0 simple merge - the other way
372 372 |\
373 373 +---o 23 mBFm-0 simple merge - one way
374 374 | |/
375 375 | o 22 f-2: rename i -> d
376 376 | |
377 377 | o 21 f-1: rename h -> i
378 378 | |
379 379 o | 5 b-1: b update
380 380 |/
381 381 o 2 i-2: c -move-> d
382 382 |
383 383 o 1 i-1: a -move-> c
384 384 |
385 385 o 0 i-0 initial commit: a b h
386 386
387 387
388 388
389 389 Merge:
390 390 - one with change to a file
391 391 - one deleting and recreating the file
392 392
393 393 Unlike in the 'BD/DB' cases, an actual merge happened here. So we should
394 394 consider history and rename on both branch of the merge.
395 395
396 396 $ hg up 'desc("i-2")'
397 397 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
398 398 $ echo "some update" >> d
399 399 $ hg commit -m "g-1: update d"
400 400 created new head
401 401 $ hg up 'desc("d-2")'
402 402 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
403 403 $ hg merge 'desc("g-1")' --tool :union
404 404 merging d
405 405 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
406 406 (branch merge, don't forget to commit)
407 407 $ hg ci -m 'mDGm-0 simple merge - one way'
408 408 $ hg up 'desc("g-1")'
409 409 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
410 410 $ hg merge 'desc("d-2")' --tool :union
411 411 merging d
412 412 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
413 413 (branch merge, don't forget to commit)
414 414 $ hg ci -m 'mGDm-0 simple merge - the other way'
415 415 created new head
416 416 $ hg log -G --rev '::(desc("mDGm")+desc("mGDm"))'
417 417 @ 27 mGDm-0 simple merge - the other way
418 418 |\
419 419 +---o 26 mDGm-0 simple merge - one way
420 420 | |/
421 421 | o 25 g-1: update d
422 422 | |
423 423 o | 8 d-2 re-add d
424 424 | |
425 425 o | 7 d-1 delete d
426 426 |/
427 427 o 2 i-2: c -move-> d
428 428 |
429 429 o 1 i-1: a -move-> c
430 430 |
431 431 o 0 i-0 initial commit: a b h
432 432
433 433
434 434
435 435 Merge:
436 436 - one with change to a file (d)
437 437 - one overwriting that file with a rename (from h to i, to d)
438 438
439 439 This case is similar to BF/FB, but an actual merge happens, so both side of the
440 440 history are relevant.
441 441
442 442 Note:
443 443 | In this case, the merge get conflicting information since on one side we have
444 444 | "a -> c -> d". and one the other one we have "h -> i -> d".
445 445 |
446 446 | The current code arbitrarily pick one side
447 447
448 448 $ hg up 'desc("f-2")'
449 449 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
450 450 $ hg merge 'desc("g-1")' --tool :union
451 451 merging d
452 452 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
453 453 (branch merge, don't forget to commit)
454 454 $ hg ci -m 'mFGm-0 simple merge - one way'
455 455 created new head
456 456 $ hg up 'desc("g-1")'
457 457 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
458 458 $ hg merge 'desc("f-2")' --tool :union
459 459 merging d
460 460 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
461 461 (branch merge, don't forget to commit)
462 462 $ hg ci -m 'mGFm-0 simple merge - the other way'
463 463 created new head
464 464 $ hg log -G --rev '::(desc("mGFm")+desc("mFGm"))'
465 465 @ 29 mGFm-0 simple merge - the other way
466 466 |\
467 467 +---o 28 mFGm-0 simple merge - one way
468 468 | |/
469 469 | o 25 g-1: update d
470 470 | |
471 471 o | 22 f-2: rename i -> d
472 472 | |
473 473 o | 21 f-1: rename h -> i
474 474 |/
475 475 o 2 i-2: c -move-> d
476 476 |
477 477 o 1 i-1: a -move-> c
478 478 |
479 479 o 0 i-0 initial commit: a b h
480 480
481 481
482 482
483 483 Comparing with merging with a deletion (and keeping the file)
484 484 -------------------------------------------------------------
485 485
486 486 Merge:
487 487 - one removing a file (d)
488 488 - one updating that file
489 489 - the merge keep the modified version of the file (canceling the delete)
490 490
491 491 In this case, the file keep on living after the merge. So we should not drop its
492 492 copy tracing chain.
493 493
494 494 $ hg up 'desc("c-1")'
495 495 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
496 496 $ hg merge 'desc("g-1")'
497 497 file 'd' was deleted in local [working copy] but was modified in other [merge rev].
498 498 You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.
499 499 What do you want to do? u
500 500 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
501 501 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
502 502 [1]
503 503 $ hg resolve -t :other d
504 504 (no more unresolved files)
505 505 $ hg ci -m "mCGm-0"
506 506 created new head
507 507
508 508 $ hg up 'desc("g-1")'
509 509 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
510 510 $ hg merge 'desc("c-1")'
511 511 file 'd' was deleted in other [merge rev] but was modified in local [working copy].
512 512 You can use (c)hanged version, (d)elete, or leave (u)nresolved.
513 513 What do you want to do? u
514 514 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
515 515 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
516 516 [1]
517 517 $ hg resolve -t :local d
518 518 (no more unresolved files)
519 519 $ hg ci -m "mGCm-0"
520 520 created new head
521 521
522 522 $ hg log -G --rev '::(desc("mCGm")+desc("mGCm"))'
523 523 @ 31 mGCm-0
524 524 |\
525 525 +---o 30 mCGm-0
526 526 | |/
527 527 | o 25 g-1: update d
528 528 | |
529 529 o | 6 c-1 delete d
530 530 |/
531 531 o 2 i-2: c -move-> d
532 532 |
533 533 o 1 i-1: a -move-> c
534 534 |
535 535 o 0 i-0 initial commit: a b h
536 536
537 537
538 538
539 539
540 540 Comparing with merge restoring an untouched deleted file
541 541 --------------------------------------------------------
542 542
543 543 Merge:
544 544 - one removing a file (d)
545 545 - one leaving the file untouched
546 546 - the merge actively restore the file to the same content.
547 547
548 548 In this case, the file keep on living after the merge. So we should not drop its
549 549 copy tracing chain.
550 550
551 551 $ hg up 'desc("c-1")'
552 552 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
553 553 $ hg merge 'desc("b-1")'
554 554 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
555 555 (branch merge, don't forget to commit)
556 556 $ hg revert --rev 'desc("b-1")' d
557 557 $ hg ci -m "mCB-revert-m-0"
558 558 created new head
559 559
560 560 $ hg up 'desc("b-1")'
561 561 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
562 562 $ hg merge 'desc("c-1")'
563 563 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
564 564 (branch merge, don't forget to commit)
565 565 $ hg revert --rev 'desc("b-1")' d
566 566 $ hg ci -m "mBC-revert-m-0"
567 567 created new head
568 568
569 569 $ hg log -G --rev '::(desc("mCB-revert-m")+desc("mBC-revert-m"))'
570 570 @ 33 mBC-revert-m-0
571 571 |\
572 572 +---o 32 mCB-revert-m-0
573 573 | |/
574 574 | o 6 c-1 delete d
575 575 | |
576 576 o | 5 b-1: b update
577 577 |/
578 578 o 2 i-2: c -move-> d
579 579 |
580 580 o 1 i-1: a -move-> c
581 581 |
582 582 o 0 i-0 initial commit: a b h
583 583
584 584
585 585
586 586 $ hg up null --quiet
587 587
588 588 Merging a branch where a rename was deleted with a branch where the same file was renamed
589 589 ------------------------------------------------------------------------------------------
590 590
591 591 Create a "conflicting" merge where `d` get removed on one branch before its
592 592 rename information actually conflict with the other branch.
593 593
594 594 (the copy information from the branch that was not deleted should win).
595 595
596 596 $ hg up 'desc("i-0")'
597 597 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
598 598 $ hg mv b d
599 599 $ hg ci -m "h-1: b -(move)-> d"
600 600 created new head
601 601
602 602 $ hg up 'desc("c-1")'
603 603 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
604 604 $ hg merge 'desc("h-1")'
605 605 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
606 606 (branch merge, don't forget to commit)
607 607 $ hg ci -m "mCH-delete-before-conflict-m-0"
608 608
609 609 $ hg up 'desc("h-1")'
610 610 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
611 611 $ hg merge 'desc("c-1")'
612 612 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
613 613 (branch merge, don't forget to commit)
614 614 $ hg ci -m "mHC-delete-before-conflict-m-0"
615 615 created new head
616 616 $ hg log -G --rev '::(desc("mCH-delete-before-conflict-m")+desc("mHC-delete-before-conflict-m"))'
617 617 @ 36 mHC-delete-before-conflict-m-0
618 618 |\
619 619 +---o 35 mCH-delete-before-conflict-m-0
620 620 | |/
621 621 | o 34 h-1: b -(move)-> d
622 622 | |
623 623 o | 6 c-1 delete d
624 624 | |
625 625 o | 2 i-2: c -move-> d
626 626 | |
627 627 o | 1 i-1: a -move-> c
628 628 |/
629 629 o 0 i-0 initial commit: a b h
630 630
631 631
632 632
633 633 Summary of all created cases
634 634 ----------------------------
635 635
636 636 $ hg up --quiet null
637 637
638 638 (This exists to help keeping a compact list of the various cases we have built)
639 639
640 640 $ hg log -T '{desc|firstline}\n'| sort
641 641 a-1: d -move-> e
642 642 a-2: e -move-> f
643 643 b-1: b update
644 644 c-1 delete d
645 645 d-1 delete d
646 646 d-2 re-add d
647 647 e-1 b -move-> g
648 648 e-2 g -move-> f
649 649 f-1: rename h -> i
650 650 f-2: rename i -> d
651 651 g-1: update d
652 652 h-1: b -(move)-> d
653 653 i-0 initial commit: a b h
654 654 i-1: a -move-> c
655 655 i-2: c -move-> d
656 656 mABm-0 simple merge - the other way
657 657 mAEm-0 simple merge - one way
658 658 mBAm-0 simple merge - one way
659 659 mBC-revert-m-0
660 660 mBCm-0 simple merge - one way
661 661 mBCm-1 re-add d
662 662 mBDm-0 simple merge - one way
663 663 mBFm-0 simple merge - one way
664 664 mCB-revert-m-0
665 665 mCBm-0 simple merge - the other way
666 666 mCBm-1 re-add d
667 667 mCGm-0
668 668 mCH-delete-before-conflict-m-0
669 669 mDBm-0 simple merge - the other way
670 670 mDGm-0 simple merge - one way
671 671 mEAm-0 simple merge - the other way
672 672 mFBm-0 simple merge - the other way
673 673 mFGm-0 simple merge - one way
674 674 mGCm-0
675 675 mGDm-0 simple merge - the other way
676 676 mGFm-0 simple merge - the other way
677 677 mHC-delete-before-conflict-m-0
678 678
679 679
680 680 Test that sidedata computations during upgrades are correct
681 681 ===========================================================
682 682
683 683 We upgrade a repository that is not using sidedata (the filelog case) and
684 684 check that the same side data have been generated as if they were computed at
685 685 commit time.
686 686
687 687
688 688 #if upgraded
689 689 $ cat >> $HGRCPATH << EOF
690 690 > [format]
691 691 > exp-use-side-data = yes
692 692 > exp-use-copies-side-data-changeset = yes
693 693 > EOF
694 694 $ hg debugformat -v
695 695 format-variant repo config default
696 696 fncache: yes yes yes
697 697 dotencode: yes yes yes
698 698 generaldelta: yes yes yes
699 699 share-safe: no no no
700 700 sparserevlog: yes yes yes
701 701 sidedata: no yes no
702 702 persistent-nodemap: no no no
703 703 copies-sdc: no yes no
704 704 plain-cl-delta: yes yes yes
705 705 compression: * (glob)
706 706 compression-level: default default default
707 707 $ hg debugupgraderepo --run --quiet
708 708 upgrade will perform the following actions:
709 709
710 710 requirements
711 711 preserved: * (glob)
712 712 added: exp-copies-sidedata-changeset, exp-sidedata-flag
713 713
714 714 processed revlogs:
715 715 - all-filelogs
716 716 - changelog
717 717 - manifest
718 718
719 719 #endif
720 720
721 721
722 722 #if no-compatibility no-filelog no-changeset
723 723
724 $ hg debugchangedfiles --compute 0
725 added : a, ;
726 added : b, ;
727 added : h, ;
728
724 729 $ for rev in `hg log --rev 'all()' -T '{rev}\n'`; do
725 730 > echo "##### revision $rev #####"
726 731 > hg debugsidedata -c -v -- $rev
727 732 > hg debugchangedfiles $rev
728 733 > done
729 734 ##### revision 0 #####
730 735 1 sidedata entries
731 736 entry-0014 size 34
732 737 '\x00\x00\x00\x03\x04\x00\x00\x00\x01\x00\x00\x00\x00\x04\x00\x00\x00\x02\x00\x00\x00\x00\x04\x00\x00\x00\x03\x00\x00\x00\x00abh'
733 738 added : a, ;
734 739 added : b, ;
735 740 added : h, ;
736 741 ##### revision 1 #####
737 742 1 sidedata entries
738 743 entry-0014 size 24
739 744 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00ac'
740 745 removed : a, ;
741 746 added p1: c, a;
742 747 ##### revision 2 #####
743 748 1 sidedata entries
744 749 entry-0014 size 24
745 750 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00cd'
746 751 removed : c, ;
747 752 added p1: d, c;
748 753 ##### revision 3 #####
749 754 1 sidedata entries
750 755 entry-0014 size 24
751 756 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00de'
752 757 removed : d, ;
753 758 added p1: e, d;
754 759 ##### revision 4 #####
755 760 1 sidedata entries
756 761 entry-0014 size 24
757 762 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00ef'
758 763 removed : e, ;
759 764 added p1: f, e;
760 765 ##### revision 5 #####
761 766 1 sidedata entries
762 767 entry-0014 size 14
763 768 '\x00\x00\x00\x01\x14\x00\x00\x00\x01\x00\x00\x00\x00b'
764 769 touched : b, ;
765 770 ##### revision 6 #####
766 771 1 sidedata entries
767 772 entry-0014 size 14
768 773 '\x00\x00\x00\x01\x0c\x00\x00\x00\x01\x00\x00\x00\x00d'
769 774 removed : d, ;
770 775 ##### revision 7 #####
771 776 1 sidedata entries
772 777 entry-0014 size 14
773 778 '\x00\x00\x00\x01\x0c\x00\x00\x00\x01\x00\x00\x00\x00d'
774 779 removed : d, ;
775 780 ##### revision 8 #####
776 781 1 sidedata entries
777 782 entry-0014 size 14
778 783 '\x00\x00\x00\x01\x04\x00\x00\x00\x01\x00\x00\x00\x00d'
779 784 added : d, ;
780 785 ##### revision 9 #####
781 786 1 sidedata entries
782 787 entry-0014 size 24
783 788 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00bg'
784 789 removed : b, ;
785 790 added p1: g, b;
786 791 ##### revision 10 #####
787 792 1 sidedata entries
788 793 entry-0014 size 24
789 794 '\x00\x00\x00\x02\x06\x00\x00\x00\x01\x00\x00\x00\x01\x0c\x00\x00\x00\x02\x00\x00\x00\x00fg'
790 795 added p1: f, g;
791 796 removed : g, ;
792 797 ##### revision 11 #####
793 798 1 sidedata entries
794 799 entry-0014 size 4
795 800 '\x00\x00\x00\x00'
796 801 ##### revision 12 #####
797 802 1 sidedata entries
798 803 entry-0014 size 4
799 804 '\x00\x00\x00\x00'
800 805 ##### revision 13 #####
801 806 1 sidedata entries
802 807 entry-0014 size 4
803 808 '\x00\x00\x00\x00'
804 809 ##### revision 14 #####
805 810 1 sidedata entries
806 811 entry-0014 size 14
807 812 '\x00\x00\x00\x01\x04\x00\x00\x00\x01\x00\x00\x00\x00d'
808 813 added : d, ;
809 814 ##### revision 15 #####
810 815 1 sidedata entries
811 816 entry-0014 size 4
812 817 '\x00\x00\x00\x00'
813 818 ##### revision 16 #####
814 819 1 sidedata entries
815 820 entry-0014 size 14
816 821 '\x00\x00\x00\x01\x04\x00\x00\x00\x01\x00\x00\x00\x00d'
817 822 added : d, ;
818 823 ##### revision 17 #####
819 824 1 sidedata entries
820 825 entry-0014 size 4
821 826 '\x00\x00\x00\x00'
822 827 ##### revision 18 #####
823 828 1 sidedata entries
824 829 entry-0014 size 4
825 830 '\x00\x00\x00\x00'
826 831 ##### revision 19 #####
827 832 1 sidedata entries
828 833 entry-0014 size 14
829 834 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00f'
830 835 merged : f, ;
831 836 ##### revision 20 #####
832 837 1 sidedata entries
833 838 entry-0014 size 14
834 839 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00f'
835 840 merged : f, ;
836 841 ##### revision 21 #####
837 842 1 sidedata entries
838 843 entry-0014 size 24
839 844 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00hi'
840 845 removed : h, ;
841 846 added p1: i, h;
842 847 ##### revision 22 #####
843 848 1 sidedata entries
844 849 entry-0014 size 24
845 850 '\x00\x00\x00\x02\x16\x00\x00\x00\x01\x00\x00\x00\x01\x0c\x00\x00\x00\x02\x00\x00\x00\x00di'
846 851 touched p1: d, i;
847 852 removed : i, ;
848 853 ##### revision 23 #####
849 854 1 sidedata entries
850 855 entry-0014 size 4
851 856 '\x00\x00\x00\x00'
852 857 ##### revision 24 #####
853 858 1 sidedata entries
854 859 entry-0014 size 4
855 860 '\x00\x00\x00\x00'
856 861 ##### revision 25 #####
857 862 1 sidedata entries
858 863 entry-0014 size 14
859 864 '\x00\x00\x00\x01\x14\x00\x00\x00\x01\x00\x00\x00\x00d'
860 865 touched : d, ;
861 866 ##### revision 26 #####
862 867 1 sidedata entries
863 868 entry-0014 size 14
864 869 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
865 870 merged : d, ;
866 871 ##### revision 27 #####
867 872 1 sidedata entries
868 873 entry-0014 size 14
869 874 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
870 875 merged : d, ;
871 876 ##### revision 28 #####
872 877 1 sidedata entries
873 878 entry-0014 size 14
874 879 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
875 880 merged : d, ;
876 881 ##### revision 29 #####
877 882 1 sidedata entries
878 883 entry-0014 size 14
879 884 '\x00\x00\x00\x01\x08\x00\x00\x00\x01\x00\x00\x00\x00d'
880 885 merged : d, ;
881 886 ##### revision 30 #####
882 887 1 sidedata entries
883 888 entry-0014 size 14
884 889 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
885 890 salvaged : d, ;
886 891 ##### revision 31 #####
887 892 1 sidedata entries
888 893 entry-0014 size 14
889 894 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
890 895 salvaged : d, ;
891 896 ##### revision 32 #####
892 897 1 sidedata entries
893 898 entry-0014 size 14
894 899 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
895 900 salvaged : d, ;
896 901 ##### revision 33 #####
897 902 1 sidedata entries
898 903 entry-0014 size 14
899 904 '\x00\x00\x00\x01\x10\x00\x00\x00\x01\x00\x00\x00\x00d'
900 905 salvaged : d, ;
901 906 ##### revision 34 #####
902 907 1 sidedata entries
903 908 entry-0014 size 24
904 909 '\x00\x00\x00\x02\x0c\x00\x00\x00\x01\x00\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\x00bd'
905 910 removed : b, ;
906 911 added p1: d, b;
907 912 ##### revision 35 #####
908 913 1 sidedata entries
909 914 entry-0014 size 4
910 915 '\x00\x00\x00\x00'
911 916 ##### revision 36 #####
912 917 1 sidedata entries
913 918 entry-0014 size 4
914 919 '\x00\x00\x00\x00'
915 920
916 921 #endif
917 922
918 923
919 924 Test copy information chaining
920 925 ==============================
921 926
922 927 Check that matching only affect the destination and not intermediate path
923 928 -------------------------------------------------------------------------
924 929
925 930 The two status call should give the same value for f
926 931
927 932 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("a-2")'
928 933 A f
929 934 a
930 935 R a
931 936 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("a-2")' f
932 937 A f
933 938 a (no-changeset no-compatibility !)
934 939
935 940 merging with unrelated change does not interfere with the renames
936 941 ---------------------------------------------------------------
937 942
938 943 - rename on one side
939 944 - unrelated change on the other side
940 945
941 946 $ hg log -G --rev '::(desc("mABm")+desc("mBAm"))'
942 947 o 12 mABm-0 simple merge - the other way
943 948 |\
944 949 +---o 11 mBAm-0 simple merge - one way
945 950 | |/
946 951 | o 5 b-1: b update
947 952 | |
948 953 o | 4 a-2: e -move-> f
949 954 | |
950 955 o | 3 a-1: d -move-> e
951 956 |/
952 957 o 2 i-2: c -move-> d
953 958 |
954 959 o 1 i-1: a -move-> c
955 960 |
956 961 o 0 i-0 initial commit: a b h
957 962
958 963
959 964 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mABm")'
960 965 A f
961 966 d
962 967 R d
963 968 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBAm")'
964 969 A f
965 970 d
966 971 R d
967 972 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mABm")'
968 973 M b
969 974 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mBAm")'
970 975 M b
971 976 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mABm")'
972 977 M b
973 978 A f
974 979 d
975 980 R d
976 981 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBAm")'
977 982 M b
978 983 A f
979 984 d
980 985 R d
981 986 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mABm")'
982 987 M b
983 988 A f
984 989 a
985 990 R a
986 991 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBAm")'
987 992 M b
988 993 A f
989 994 a
990 995 R a
991 996
992 997 merging with the side having a delete
993 998 -------------------------------------
994 999
995 1000 case summary:
996 1001 - one with change to an unrelated file
997 1002 - one deleting the change
998 1003 and recreate an unrelated file after the merge
999 1004
1000 1005 $ hg log -G --rev '::(desc("mCBm")+desc("mBCm"))'
1001 1006 o 16 mCBm-1 re-add d
1002 1007 |
1003 1008 o 15 mCBm-0 simple merge - the other way
1004 1009 |\
1005 1010 | | o 14 mBCm-1 re-add d
1006 1011 | | |
1007 1012 +---o 13 mBCm-0 simple merge - one way
1008 1013 | |/
1009 1014 | o 6 c-1 delete d
1010 1015 | |
1011 1016 o | 5 b-1: b update
1012 1017 |/
1013 1018 o 2 i-2: c -move-> d
1014 1019 |
1015 1020 o 1 i-1: a -move-> c
1016 1021 |
1017 1022 o 0 i-0 initial commit: a b h
1018 1023
1019 1024 - comparing from the merge
1020 1025
1021 1026 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBCm-0")'
1022 1027 R d
1023 1028 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCBm-0")'
1024 1029 R d
1025 1030 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBCm-0")'
1026 1031 M b
1027 1032 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCBm-0")'
1028 1033 M b
1029 1034 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBCm-0")'
1030 1035 M b
1031 1036 R d
1032 1037 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mCBm-0")'
1033 1038 M b
1034 1039 R d
1035 1040 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBCm-0")'
1036 1041 M b
1037 1042 R a
1038 1043 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCBm-0")'
1039 1044 M b
1040 1045 R a
1041 1046
1042 1047 - comparing with the merge children re-adding the file
1043 1048
1044 1049 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBCm-1")'
1045 1050 M d
1046 1051 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCBm-1")'
1047 1052 M d
1048 1053 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBCm-1")'
1049 1054 M b
1050 1055 A d
1051 1056 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCBm-1")'
1052 1057 M b
1053 1058 A d
1054 1059 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBCm-1")'
1055 1060 M b
1056 1061 M d
1057 1062 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mCBm-1")'
1058 1063 M b
1059 1064 M d
1060 1065 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBCm-1")'
1061 1066 M b
1062 1067 A d
1063 1068 R a
1064 1069 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCBm-1")'
1065 1070 M b
1066 1071 A d
1067 1072 R a
1068 1073
1069 1074 Comparing with a merge re-adding the file afterward
1070 1075 ---------------------------------------------------
1071 1076
1072 1077 Merge:
1073 1078 - one with change to an unrelated file
1074 1079 - one deleting and recreating the change
1075 1080
1076 1081 $ hg log -G --rev '::(desc("mDBm")+desc("mBDm"))'
1077 1082 o 18 mDBm-0 simple merge - the other way
1078 1083 |\
1079 1084 +---o 17 mBDm-0 simple merge - one way
1080 1085 | |/
1081 1086 | o 8 d-2 re-add d
1082 1087 | |
1083 1088 | o 7 d-1 delete d
1084 1089 | |
1085 1090 o | 5 b-1: b update
1086 1091 |/
1087 1092 o 2 i-2: c -move-> d
1088 1093 |
1089 1094 o 1 i-1: a -move-> c
1090 1095 |
1091 1096 o 0 i-0 initial commit: a b h
1092 1097
1093 1098 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBDm-0")'
1094 1099 M d
1095 1100 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mDBm-0")'
1096 1101 M d
1097 1102 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mBDm-0")'
1098 1103 M b
1099 1104 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mDBm-0")'
1100 1105 M b
1101 1106 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mBDm-0")'
1102 1107 M b
1103 1108 M d
1104 1109 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mDBm-0")'
1105 1110 M b
1106 1111 M d
1107 1112
1108 1113 The bugs makes recorded copy is different depending of where we started the merge from since
1109 1114
1110 1115 $ hg manifest --debug --rev 'desc("mBDm-0")' | grep '644 d'
1111 1116 b004912a8510032a0350a74daa2803dadfb00e12 644 d
1112 1117 $ hg manifest --debug --rev 'desc("mDBm-0")' | grep '644 d'
1113 1118 b004912a8510032a0350a74daa2803dadfb00e12 644 d
1114 1119
1115 1120 $ hg manifest --debug --rev 'desc("d-2")' | grep '644 d'
1116 1121 b004912a8510032a0350a74daa2803dadfb00e12 644 d
1117 1122 $ hg manifest --debug --rev 'desc("b-1")' | grep '644 d'
1118 1123 169be882533bc917905d46c0c951aa9a1e288dcf 644 d (no-changeset !)
1119 1124 b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3 644 d (changeset !)
1120 1125 $ hg debugindex d | head -n 4
1121 1126 rev linkrev nodeid p1 p2
1122 1127 0 2 169be882533b 000000000000 000000000000 (no-changeset !)
1123 1128 0 2 b789fdd96dc2 000000000000 000000000000 (changeset !)
1124 1129 1 8 b004912a8510 000000000000 000000000000
1125 1130 2 22 4a067cf8965d 000000000000 000000000000 (no-changeset !)
1126 1131 2 22 fe6f8b4f507f 000000000000 000000000000 (changeset !)
1127 1132
1128 1133 Log output should not include a merge commit as it did not happen
1129 1134
1130 1135 $ hg log -Gfr 'desc("mBDm-0")' d
1131 1136 o 8 d-2 re-add d
1132 1137 |
1133 1138 ~
1134 1139
1135 1140 $ hg log -Gfr 'desc("mDBm-0")' d
1136 1141 o 8 d-2 re-add d
1137 1142 |
1138 1143 ~
1139 1144
1140 1145 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBDm-0")'
1141 1146 M b
1142 1147 A d
1143 1148 R a
1144 1149 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mDBm-0")'
1145 1150 M b
1146 1151 A d
1147 1152 R a
1148 1153
1149 1154
1150 1155 Comparing with a merge with colliding rename
1151 1156 --------------------------------------------
1152 1157
1153 1158 - the "e-" branch renaming b to f (through 'g')
1154 1159 - the "a-" branch renaming d to f (through e)
1155 1160
1156 1161 $ hg log -G --rev '::(desc("mAEm")+desc("mEAm"))'
1157 1162 o 20 mEAm-0 simple merge - the other way
1158 1163 |\
1159 1164 +---o 19 mAEm-0 simple merge - one way
1160 1165 | |/
1161 1166 | o 10 e-2 g -move-> f
1162 1167 | |
1163 1168 | o 9 e-1 b -move-> g
1164 1169 | |
1165 1170 o | 4 a-2: e -move-> f
1166 1171 | |
1167 1172 o | 3 a-1: d -move-> e
1168 1173 |/
1169 1174 o 2 i-2: c -move-> d
1170 1175 |
1171 1176 o 1 i-1: a -move-> c
1172 1177 |
1173 1178 o 0 i-0 initial commit: a b h
1174 1179
1175 1180 #if no-changeset
1176 1181 $ hg manifest --debug --rev 'desc("mAEm-0")' | grep '644 f'
1177 1182 c39c6083dad048d5138618a46f123e2f397f4f18 644 f
1178 1183 $ hg manifest --debug --rev 'desc("mEAm-0")' | grep '644 f'
1179 1184 a9a8bc3860c9d8fa5f2f7e6ea8d40498322737fd 644 f
1180 1185 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
1181 1186 263ea25e220aaeb7b9bac551c702037849aa75e8 644 f
1182 1187 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
1183 1188 71b9b7e73d973572ade6dd765477fcee6890e8b1 644 f
1184 1189 $ hg debugindex f
1185 1190 rev linkrev nodeid p1 p2
1186 1191 0 4 263ea25e220a 000000000000 000000000000
1187 1192 1 10 71b9b7e73d97 000000000000 000000000000
1188 1193 2 19 c39c6083dad0 263ea25e220a 71b9b7e73d97
1189 1194 3 20 a9a8bc3860c9 71b9b7e73d97 263ea25e220a
1190 1195 #else
1191 1196 $ hg manifest --debug --rev 'desc("mAEm-0")' | grep '644 f'
1192 1197 498e8799f49f9da1ca06bb2d6d4accf165c5b572 644 f
1193 1198 $ hg manifest --debug --rev 'desc("mEAm-0")' | grep '644 f'
1194 1199 c5b506a7118667a38a9c9348a1f63b679e382f57 644 f
1195 1200 $ hg manifest --debug --rev 'desc("a-2")' | grep '644 f'
1196 1201 b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3 644 f
1197 1202 $ hg manifest --debug --rev 'desc("e-2")' | grep '644 f'
1198 1203 1e88685f5ddec574a34c70af492f95b6debc8741 644 f
1199 1204 $ hg debugindex f
1200 1205 rev linkrev nodeid p1 p2
1201 1206 0 4 b789fdd96dc2 000000000000 000000000000
1202 1207 1 10 1e88685f5dde 000000000000 000000000000
1203 1208 2 19 498e8799f49f b789fdd96dc2 1e88685f5dde
1204 1209 3 20 c5b506a71186 1e88685f5dde b789fdd96dc2
1205 1210 #endif
1206 1211
1207 1212 # Here the filelog based implementation is not looking at the rename
1208 1213 # information (because the file exist on both side). However the changelog
1209 1214 # based on works fine. We have different output.
1210 1215
1211 1216 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mAEm-0")'
1212 1217 M f
1213 1218 b (no-filelog !)
1214 1219 R b
1215 1220 $ hg status --copies --rev 'desc("a-2")' --rev 'desc("mEAm-0")'
1216 1221 M f
1217 1222 b (no-filelog !)
1218 1223 R b
1219 1224 $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mAEm-0")'
1220 1225 M f
1221 1226 d (no-filelog !)
1222 1227 R d
1223 1228 $ hg status --copies --rev 'desc("e-2")' --rev 'desc("mEAm-0")'
1224 1229 M f
1225 1230 d (no-filelog !)
1226 1231 R d
1227 1232 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("a-2")'
1228 1233 A f
1229 1234 d
1230 1235 R d
1231 1236 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("e-2")'
1232 1237 A f
1233 1238 b
1234 1239 R b
1235 1240
1236 1241 # From here, we run status against revision where both source file exists.
1237 1242 #
1238 1243 # The filelog based implementation picks an arbitrary side based on revision
1239 1244 # numbers. So the same side "wins" whatever the parents order is. This is
1240 1245 # sub-optimal because depending on revision numbers means the result can be
1241 1246 # different from one repository to the next.
1242 1247 #
1243 1248 # The changeset based algorithm use the parent order to break tie on conflicting
1244 1249 # information and will have a different order depending on who is p1 and p2.
1245 1250 # That order is stable accross repositories. (data from p1 prevails)
1246 1251
1247 1252 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mAEm-0")'
1248 1253 A f
1249 1254 d
1250 1255 R b
1251 1256 R d
1252 1257 $ hg status --copies --rev 'desc("i-2")' --rev 'desc("mEAm-0")'
1253 1258 A f
1254 1259 d (filelog !)
1255 1260 b (no-filelog !)
1256 1261 R b
1257 1262 R d
1258 1263 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mAEm-0")'
1259 1264 A f
1260 1265 a
1261 1266 R a
1262 1267 R b
1263 1268 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mEAm-0")'
1264 1269 A f
1265 1270 a (filelog !)
1266 1271 b (no-filelog !)
1267 1272 R a
1268 1273 R b
1269 1274
1270 1275
1271 1276 Note:
1272 1277 | In this case, one of the merge wrongly record a merge while there is none.
1273 1278 | This lead to bad copy tracing information to be dug up.
1274 1279
1275 1280
1276 1281 Merge:
1277 1282 - one with change to an unrelated file (b)
1278 1283 - one overwriting a file (d) with a rename (from h to i to d)
1279 1284
1280 1285 $ hg log -G --rev '::(desc("mBFm")+desc("mFBm"))'
1281 1286 o 24 mFBm-0 simple merge - the other way
1282 1287 |\
1283 1288 +---o 23 mBFm-0 simple merge - one way
1284 1289 | |/
1285 1290 | o 22 f-2: rename i -> d
1286 1291 | |
1287 1292 | o 21 f-1: rename h -> i
1288 1293 | |
1289 1294 o | 5 b-1: b update
1290 1295 |/
1291 1296 o 2 i-2: c -move-> d
1292 1297 |
1293 1298 o 1 i-1: a -move-> c
1294 1299 |
1295 1300 o 0 i-0 initial commit: a b h
1296 1301
1297 1302 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBFm-0")'
1298 1303 M b
1299 1304 A d
1300 1305 h
1301 1306 R a
1302 1307 R h
1303 1308 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFBm-0")'
1304 1309 M b
1305 1310 A d
1306 1311 h
1307 1312 R a
1308 1313 R h
1309 1314 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBFm-0")'
1310 1315 M d
1311 1316 h (no-filelog !)
1312 1317 R h
1313 1318 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mBFm-0")'
1314 1319 M b
1315 1320 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mBFm-0")'
1316 1321 M b
1317 1322 M d
1318 1323 i (no-filelog !)
1319 1324 R i
1320 1325 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mFBm-0")'
1321 1326 M d
1322 1327 h (no-filelog !)
1323 1328 R h
1324 1329 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mFBm-0")'
1325 1330 M b
1326 1331 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mFBm-0")'
1327 1332 M b
1328 1333 M d
1329 1334 i (no-filelog !)
1330 1335 R i
1331 1336
1332 1337 #if no-changeset
1333 1338 $ hg log -Gfr 'desc("mBFm-0")' d
1334 1339 o 22 f-2: rename i -> d
1335 1340 |
1336 1341 o 21 f-1: rename h -> i
1337 1342 :
1338 1343 o 0 i-0 initial commit: a b h
1339 1344
1340 1345 #else
1341 1346 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
1342 1347 $ hg log -Gfr 'desc("mBFm-0")' d
1343 1348 o 22 f-2: rename i -> d
1344 1349 |
1345 1350 ~
1346 1351 #endif
1347 1352
1348 1353 #if no-changeset
1349 1354 $ hg log -Gfr 'desc("mFBm-0")' d
1350 1355 o 22 f-2: rename i -> d
1351 1356 |
1352 1357 o 21 f-1: rename h -> i
1353 1358 :
1354 1359 o 0 i-0 initial commit: a b h
1355 1360
1356 1361 #else
1357 1362 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
1358 1363 $ hg log -Gfr 'desc("mFBm-0")' d
1359 1364 o 22 f-2: rename i -> d
1360 1365 |
1361 1366 ~
1362 1367 #endif
1363 1368
1364 1369
1365 1370 Merge:
1366 1371 - one with change to a file
1367 1372 - one deleting and recreating the file
1368 1373
1369 1374 Unlike in the 'BD/DB' cases, an actual merge happened here. So we should
1370 1375 consider history and rename on both branch of the merge.
1371 1376
1372 1377 $ hg log -G --rev '::(desc("mDGm")+desc("mGDm"))'
1373 1378 o 27 mGDm-0 simple merge - the other way
1374 1379 |\
1375 1380 +---o 26 mDGm-0 simple merge - one way
1376 1381 | |/
1377 1382 | o 25 g-1: update d
1378 1383 | |
1379 1384 o | 8 d-2 re-add d
1380 1385 | |
1381 1386 o | 7 d-1 delete d
1382 1387 |/
1383 1388 o 2 i-2: c -move-> d
1384 1389 |
1385 1390 o 1 i-1: a -move-> c
1386 1391 |
1387 1392 o 0 i-0 initial commit: a b h
1388 1393
1389 1394 One side of the merge have a long history with rename. The other side of the
1390 1395 merge point to a new file with a smaller history. Each side is "valid".
1391 1396
1392 1397 (and again the filelog based algorithm only explore one, with a pick based on
1393 1398 revision numbers)
1394 1399
1395 1400 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mDGm-0")'
1396 1401 A d
1397 1402 a (filelog !)
1398 1403 R a
1399 1404 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGDm-0")'
1400 1405 A d
1401 1406 a
1402 1407 R a
1403 1408 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mDGm-0")'
1404 1409 M d
1405 1410 $ hg status --copies --rev 'desc("d-2")' --rev 'desc("mGDm-0")'
1406 1411 M d
1407 1412 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mDGm-0")'
1408 1413 M d
1409 1414 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGDm-0")'
1410 1415 M d
1411 1416
1412 1417 #if no-changeset
1413 1418 $ hg log -Gfr 'desc("mDGm-0")' d
1414 1419 o 26 mDGm-0 simple merge - one way
1415 1420 |\
1416 1421 | o 25 g-1: update d
1417 1422 | |
1418 1423 o | 8 d-2 re-add d
1419 1424 |/
1420 1425 o 2 i-2: c -move-> d
1421 1426 |
1422 1427 o 1 i-1: a -move-> c
1423 1428 |
1424 1429 o 0 i-0 initial commit: a b h
1425 1430
1426 1431 #else
1427 1432 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
1428 1433 $ hg log -Gfr 'desc("mDGm-0")' d
1429 1434 o 26 mDGm-0 simple merge - one way
1430 1435 |\
1431 1436 | o 25 g-1: update d
1432 1437 | |
1433 1438 o | 8 d-2 re-add d
1434 1439 |/
1435 1440 o 2 i-2: c -move-> d
1436 1441 |
1437 1442 ~
1438 1443 #endif
1439 1444
1440 1445
1441 1446 #if no-changeset
1442 1447 $ hg log -Gfr 'desc("mDGm-0")' d
1443 1448 o 26 mDGm-0 simple merge - one way
1444 1449 |\
1445 1450 | o 25 g-1: update d
1446 1451 | |
1447 1452 o | 8 d-2 re-add d
1448 1453 |/
1449 1454 o 2 i-2: c -move-> d
1450 1455 |
1451 1456 o 1 i-1: a -move-> c
1452 1457 |
1453 1458 o 0 i-0 initial commit: a b h
1454 1459
1455 1460 #else
1456 1461 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
1457 1462 $ hg log -Gfr 'desc("mDGm-0")' d
1458 1463 o 26 mDGm-0 simple merge - one way
1459 1464 |\
1460 1465 | o 25 g-1: update d
1461 1466 | |
1462 1467 o | 8 d-2 re-add d
1463 1468 |/
1464 1469 o 2 i-2: c -move-> d
1465 1470 |
1466 1471 ~
1467 1472 #endif
1468 1473
1469 1474
1470 1475 Merge:
1471 1476 - one with change to a file (d)
1472 1477 - one overwriting that file with a rename (from h to i, to d)
1473 1478
1474 1479 This case is similar to BF/FB, but an actual merge happens, so both side of the
1475 1480 history are relevant.
1476 1481
1477 1482
1478 1483 $ hg log -G --rev '::(desc("mGFm")+desc("mFGm"))'
1479 1484 o 29 mGFm-0 simple merge - the other way
1480 1485 |\
1481 1486 +---o 28 mFGm-0 simple merge - one way
1482 1487 | |/
1483 1488 | o 25 g-1: update d
1484 1489 | |
1485 1490 o | 22 f-2: rename i -> d
1486 1491 | |
1487 1492 o | 21 f-1: rename h -> i
1488 1493 |/
1489 1494 o 2 i-2: c -move-> d
1490 1495 |
1491 1496 o 1 i-1: a -move-> c
1492 1497 |
1493 1498 o 0 i-0 initial commit: a b h
1494 1499
1495 1500
1496 1501 Note:
1497 1502 | In this case, the merge get conflicting information since on one side we have
1498 1503 | "a -> c -> d". and one the other one we have "h -> i -> d".
1499 1504 |
1500 1505 | The current code arbitrarily pick one side depending the ordering of the merged hash:
1501 1506
1502 1507 In this case, the file hash from "f-2" is lower, so it will be `p1` of the resulting filenode its copy tracing information will win (and trace back to "h"):
1503 1508
1504 1509 Details on this hash ordering pick:
1505 1510
1506 1511 $ hg manifest --debug 'desc("g-1")' | egrep 'd$'
1507 1512 f2b277c39e0d2bbac99d8aae075c0d8b5304d266 644 d (no-changeset !)
1508 1513 4ff57b4e8dceedb487e70e6965ea188a7c042cca 644 d (changeset !)
1509 1514 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("g-1")' d
1510 1515 A d
1511 1516 a (no-changeset no-compatibility !)
1512 1517
1513 1518 $ hg manifest --debug 'desc("f-2")' | egrep 'd$'
1514 1519 4a067cf8965d1bfff130057ade26b44f580231be 644 d (no-changeset !)
1515 1520 fe6f8b4f507fe3eb524c527192a84920a4288dac 644 d (changeset !)
1516 1521 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("f-2")' d
1517 1522 A d
1518 1523 h (no-changeset no-compatibility !)
1519 1524
1520 1525 Copy tracing data on the resulting merge:
1521 1526
1522 1527 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mFGm-0")'
1523 1528 A d
1524 1529 h
1525 1530 R a
1526 1531 R h
1527 1532 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGFm-0")'
1528 1533 A d
1529 1534 a (no-filelog !)
1530 1535 h (filelog !)
1531 1536 R a
1532 1537 R h
1533 1538 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mFGm-0")'
1534 1539 M d
1535 1540 $ hg status --copies --rev 'desc("f-2")' --rev 'desc("mGFm-0")'
1536 1541 M d
1537 1542 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mFGm-0")'
1538 1543 M d
1539 1544 i (no-filelog !)
1540 1545 R i
1541 1546 $ hg status --copies --rev 'desc("f-1")' --rev 'desc("mGFm-0")'
1542 1547 M d
1543 1548 i (no-filelog !)
1544 1549 R i
1545 1550 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mFGm-0")'
1546 1551 M d
1547 1552 h (no-filelog !)
1548 1553 R h
1549 1554 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGFm-0")'
1550 1555 M d
1551 1556 h (no-filelog !)
1552 1557 R h
1553 1558
1554 1559 #if no-changeset
1555 1560 $ hg log -Gfr 'desc("mFGm-0")' d
1556 1561 o 28 mFGm-0 simple merge - one way
1557 1562 |\
1558 1563 | o 25 g-1: update d
1559 1564 | |
1560 1565 o | 22 f-2: rename i -> d
1561 1566 | |
1562 1567 o | 21 f-1: rename h -> i
1563 1568 |/
1564 1569 o 2 i-2: c -move-> d
1565 1570 |
1566 1571 o 1 i-1: a -move-> c
1567 1572 |
1568 1573 o 0 i-0 initial commit: a b h
1569 1574
1570 1575 #else
1571 1576 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
1572 1577 $ hg log -Gfr 'desc("mFGm-0")' d
1573 1578 o 28 mFGm-0 simple merge - one way
1574 1579 |\
1575 1580 | o 25 g-1: update d
1576 1581 | |
1577 1582 o | 22 f-2: rename i -> d
1578 1583 |/
1579 1584 o 2 i-2: c -move-> d
1580 1585 |
1581 1586 ~
1582 1587 #endif
1583 1588
1584 1589 #if no-changeset
1585 1590 $ hg log -Gfr 'desc("mGFm-0")' d
1586 1591 o 29 mGFm-0 simple merge - the other way
1587 1592 |\
1588 1593 | o 25 g-1: update d
1589 1594 | |
1590 1595 o | 22 f-2: rename i -> d
1591 1596 | |
1592 1597 o | 21 f-1: rename h -> i
1593 1598 |/
1594 1599 o 2 i-2: c -move-> d
1595 1600 |
1596 1601 o 1 i-1: a -move-> c
1597 1602 |
1598 1603 o 0 i-0 initial commit: a b h
1599 1604
1600 1605 #else
1601 1606 BROKEN: `hg log --follow <file>` relies on filelog metadata to work
1602 1607 $ hg log -Gfr 'desc("mGFm-0")' d
1603 1608 o 29 mGFm-0 simple merge - the other way
1604 1609 |\
1605 1610 | o 25 g-1: update d
1606 1611 | |
1607 1612 o | 22 f-2: rename i -> d
1608 1613 |/
1609 1614 o 2 i-2: c -move-> d
1610 1615 |
1611 1616 ~
1612 1617 #endif
1613 1618
1614 1619
1615 1620 Comparing with merging with a deletion (and keeping the file)
1616 1621 -------------------------------------------------------------
1617 1622
1618 1623 Merge:
1619 1624 - one removing a file (d)
1620 1625 - one updating that file
1621 1626 - the merge keep the modified version of the file (canceling the delete)
1622 1627
1623 1628 In this case, the file keep on living after the merge. So we should not drop its
1624 1629 copy tracing chain.
1625 1630
1626 1631 $ hg log -G --rev '::(desc("mCGm")+desc("mGCm"))'
1627 1632 o 31 mGCm-0
1628 1633 |\
1629 1634 +---o 30 mCGm-0
1630 1635 | |/
1631 1636 | o 25 g-1: update d
1632 1637 | |
1633 1638 o | 6 c-1 delete d
1634 1639 |/
1635 1640 o 2 i-2: c -move-> d
1636 1641 |
1637 1642 o 1 i-1: a -move-> c
1638 1643 |
1639 1644 o 0 i-0 initial commit: a b h
1640 1645
1641 1646
1642 1647 'a' is the copy source of 'd'
1643 1648
1644 1649 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCGm-0")'
1645 1650 A d
1646 1651 a (no-compatibility no-changeset !)
1647 1652 R a
1648 1653 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mGCm-0")'
1649 1654 A d
1650 1655 a (no-compatibility no-changeset !)
1651 1656 R a
1652 1657 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCGm-0")'
1653 1658 A d
1654 1659 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mGCm-0")'
1655 1660 A d
1656 1661 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mCGm-0")'
1657 1662 $ hg status --copies --rev 'desc("g-1")' --rev 'desc("mGCm-0")'
1658 1663
1659 1664
1660 1665 Comparing with merge restoring an untouched deleted file
1661 1666 --------------------------------------------------------
1662 1667
1663 1668 Merge:
1664 1669 - one removing a file (d)
1665 1670 - one leaving the file untouched
1666 1671 - the merge actively restore the file to the same content.
1667 1672
1668 1673 In this case, the file keep on living after the merge. So we should not drop its
1669 1674 copy tracing chain.
1670 1675
1671 1676 $ hg log -G --rev '::(desc("mCB-revert-m")+desc("mBC-revert-m"))'
1672 1677 o 33 mBC-revert-m-0
1673 1678 |\
1674 1679 +---o 32 mCB-revert-m-0
1675 1680 | |/
1676 1681 | o 6 c-1 delete d
1677 1682 | |
1678 1683 o | 5 b-1: b update
1679 1684 |/
1680 1685 o 2 i-2: c -move-> d
1681 1686 |
1682 1687 o 1 i-1: a -move-> c
1683 1688 |
1684 1689 o 0 i-0 initial commit: a b h
1685 1690
1686 1691
1687 1692 'a' is the the copy source of 'd'
1688 1693
1689 1694 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCB-revert-m-0")'
1690 1695 M b
1691 1696 A d
1692 1697 a (no-compatibility no-changeset !)
1693 1698 R a
1694 1699 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mBC-revert-m-0")'
1695 1700 M b
1696 1701 A d
1697 1702 a (no-compatibility no-changeset !)
1698 1703 R a
1699 1704 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCB-revert-m-0")'
1700 1705 M b
1701 1706 A d
1702 1707 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mBC-revert-m-0")'
1703 1708 M b
1704 1709 A d
1705 1710 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mCB-revert-m-0")'
1706 1711 $ hg status --copies --rev 'desc("b-1")' --rev 'desc("mBC-revert-m-0")'
1707 1712
1708 1713
1709 1714 Merging a branch where a rename was deleted with a branch where the same file was renamed
1710 1715 ------------------------------------------------------------------------------------------
1711 1716
1712 1717 Create a "conflicting" merge where `d` get removed on one branch before its
1713 1718 rename information actually conflict with the other branch.
1714 1719
1715 1720 (the copy information from the branch that was not deleted should win).
1716 1721
1717 1722 $ hg log -G --rev '::(desc("mCH-delete-before-conflict-m")+desc("mHC-delete-before-conflict-m"))'
1718 1723 o 36 mHC-delete-before-conflict-m-0
1719 1724 |\
1720 1725 +---o 35 mCH-delete-before-conflict-m-0
1721 1726 | |/
1722 1727 | o 34 h-1: b -(move)-> d
1723 1728 | |
1724 1729 o | 6 c-1 delete d
1725 1730 | |
1726 1731 o | 2 i-2: c -move-> d
1727 1732 | |
1728 1733 o | 1 i-1: a -move-> c
1729 1734 |/
1730 1735 o 0 i-0 initial commit: a b h
1731 1736
1732 1737
1733 1738 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mCH-delete-before-conflict-m")'
1734 1739 A d
1735 1740 b (no-compatibility no-changeset !)
1736 1741 R a
1737 1742 R b
1738 1743 $ hg status --copies --rev 'desc("i-0")' --rev 'desc("mHC-delete-before-conflict-m")'
1739 1744 A d
1740 1745 b
1741 1746 R a
1742 1747 R b
1743 1748 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mCH-delete-before-conflict-m")'
1744 1749 A d
1745 1750 b
1746 1751 R b
1747 1752 $ hg status --copies --rev 'desc("c-1")' --rev 'desc("mHC-delete-before-conflict-m")'
1748 1753 A d
1749 1754 b
1750 1755 R b
1751 1756 $ hg status --copies --rev 'desc("h-1")' --rev 'desc("mCH-delete-before-conflict-m")'
1752 1757 R a
1753 1758 $ hg status --copies --rev 'desc("h-1")' --rev 'desc("mHC-delete-before-conflict-m")'
1754 1759 R a
General Comments 0
You need to be logged in to leave comments. Login now