##// END OF EJS Templates
verify: also check dirstate...
Raphaël Gomès -
r50721:c84844cd default
parent child Browse files
Show More
@@ -1,4743 +1,4738 b''
1 1 # debugcommands.py - command processing for debug* commands
2 2 #
3 3 # Copyright 2005-2016 Olivia Mackall <olivia@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
9 9 import binascii
10 10 import codecs
11 11 import collections
12 12 import contextlib
13 13 import difflib
14 14 import errno
15 15 import glob
16 16 import operator
17 17 import os
18 18 import platform
19 19 import random
20 20 import re
21 21 import socket
22 22 import ssl
23 23 import stat
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 nullrev,
33 33 short,
34 34 )
35 35 from .pycompat import (
36 36 getattr,
37 37 open,
38 38 )
39 39 from . import (
40 40 bundle2,
41 41 bundlerepo,
42 42 changegroup,
43 43 cmdutil,
44 44 color,
45 45 context,
46 46 copies,
47 47 dagparser,
48 48 dirstateutils,
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 requirements,
74 74 revlog,
75 75 revset,
76 76 revsetlang,
77 77 scmutil,
78 78 setdiscovery,
79 79 simplemerge,
80 80 sshpeer,
81 81 sslutil,
82 82 streamclone,
83 83 strip,
84 84 tags as tagsmod,
85 85 templater,
86 86 treediscovery,
87 87 upgrade,
88 88 url as urlmod,
89 89 util,
90 verify,
90 91 vfs as vfsmod,
91 92 wireprotoframing,
92 93 wireprotoserver,
93 94 )
94 95 from .interfaces import repository
95 96 from .utils import (
96 97 cborutil,
97 98 compression,
98 99 dateutil,
99 100 procutil,
100 101 stringutil,
101 102 urlutil,
102 103 )
103 104
104 105 from .revlogutils import (
105 106 constants as revlog_constants,
106 107 debug as revlog_debug,
107 108 deltas as deltautil,
108 109 nodemap,
109 110 rewrite,
110 111 sidedata,
111 112 )
112 113
113 114 release = lockmod.release
114 115
115 116 table = {}
116 117 table.update(strip.command._table)
117 118 command = registrar.command(table)
118 119
119 120
120 121 @command(b'debugancestor', [], _(b'[INDEX] REV1 REV2'), optionalrepo=True)
121 122 def debugancestor(ui, repo, *args):
122 123 """find the ancestor revision of two revisions in a given index"""
123 124 if len(args) == 3:
124 125 index, rev1, rev2 = args
125 126 r = revlog.revlog(vfsmod.vfs(encoding.getcwd(), audit=False), index)
126 127 lookup = r.lookup
127 128 elif len(args) == 2:
128 129 if not repo:
129 130 raise error.Abort(
130 131 _(b'there is no Mercurial repository here (.hg not found)')
131 132 )
132 133 rev1, rev2 = args
133 134 r = repo.changelog
134 135 lookup = repo.lookup
135 136 else:
136 137 raise error.Abort(_(b'either two or three arguments required'))
137 138 a = r.ancestor(lookup(rev1), lookup(rev2))
138 139 ui.write(b'%d:%s\n' % (r.rev(a), hex(a)))
139 140
140 141
141 142 @command(b'debugantivirusrunning', [])
142 143 def debugantivirusrunning(ui, repo):
143 144 """attempt to trigger an antivirus scanner to see if one is active"""
144 145 with repo.cachevfs.open('eicar-test-file.com', b'wb') as f:
145 146 f.write(
146 147 util.b85decode(
147 148 # This is a base85-armored version of the EICAR test file. See
148 149 # https://en.wikipedia.org/wiki/EICAR_test_file for details.
149 150 b'ST#=}P$fV?P+K%yP+C|uG$>GBDK|qyDK~v2MM*<JQY}+dK~6+LQba95P'
150 151 b'E<)&Nm5l)EmTEQR4qnHOhq9iNGnJx'
151 152 )
152 153 )
153 154 # Give an AV engine time to scan the file.
154 155 time.sleep(2)
155 156 util.unlink(repo.cachevfs.join('eicar-test-file.com'))
156 157
157 158
158 159 @command(b'debugapplystreamclonebundle', [], b'FILE')
159 160 def debugapplystreamclonebundle(ui, repo, fname):
160 161 """apply a stream clone bundle file"""
161 162 f = hg.openpath(ui, fname)
162 163 gen = exchange.readbundle(ui, f, fname)
163 164 gen.apply(repo)
164 165
165 166
166 167 @command(
167 168 b'debugbuilddag',
168 169 [
169 170 (
170 171 b'm',
171 172 b'mergeable-file',
172 173 None,
173 174 _(b'add single file mergeable changes'),
174 175 ),
175 176 (
176 177 b'o',
177 178 b'overwritten-file',
178 179 None,
179 180 _(b'add single file all revs overwrite'),
180 181 ),
181 182 (b'n', b'new-file', None, _(b'add new file at each rev')),
182 183 (
183 184 b'',
184 185 b'from-existing',
185 186 None,
186 187 _(b'continue from a non-empty repository'),
187 188 ),
188 189 ],
189 190 _(b'[OPTION]... [TEXT]'),
190 191 )
191 192 def debugbuilddag(
192 193 ui,
193 194 repo,
194 195 text=None,
195 196 mergeable_file=False,
196 197 overwritten_file=False,
197 198 new_file=False,
198 199 from_existing=False,
199 200 ):
200 201 """builds a repo with a given DAG from scratch in the current empty repo
201 202
202 203 The description of the DAG is read from stdin if not given on the
203 204 command line.
204 205
205 206 Elements:
206 207
207 208 - "+n" is a linear run of n nodes based on the current default parent
208 209 - "." is a single node based on the current default parent
209 210 - "$" resets the default parent to null (implied at the start);
210 211 otherwise the default parent is always the last node created
211 212 - "<p" sets the default parent to the backref p
212 213 - "*p" is a fork at parent p, which is a backref
213 214 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
214 215 - "/p2" is a merge of the preceding node and p2
215 216 - ":tag" defines a local tag for the preceding node
216 217 - "@branch" sets the named branch for subsequent nodes
217 218 - "#...\\n" is a comment up to the end of the line
218 219
219 220 Whitespace between the above elements is ignored.
220 221
221 222 A backref is either
222 223
223 224 - a number n, which references the node curr-n, where curr is the current
224 225 node, or
225 226 - the name of a local tag you placed earlier using ":tag", or
226 227 - empty to denote the default parent.
227 228
228 229 All string valued-elements are either strictly alphanumeric, or must
229 230 be enclosed in double quotes ("..."), with "\\" as escape character.
230 231 """
231 232
232 233 if text is None:
233 234 ui.status(_(b"reading DAG from stdin\n"))
234 235 text = ui.fin.read()
235 236
236 237 cl = repo.changelog
237 238 if len(cl) > 0 and not from_existing:
238 239 raise error.Abort(_(b'repository is not empty'))
239 240
240 241 # determine number of revs in DAG
241 242 total = 0
242 243 for type, data in dagparser.parsedag(text):
243 244 if type == b'n':
244 245 total += 1
245 246
246 247 if mergeable_file:
247 248 linesperrev = 2
248 249 # make a file with k lines per rev
249 250 initialmergedlines = [b'%d' % i for i in range(0, total * linesperrev)]
250 251 initialmergedlines.append(b"")
251 252
252 253 tags = []
253 254 progress = ui.makeprogress(
254 255 _(b'building'), unit=_(b'revisions'), total=total
255 256 )
256 257 with progress, repo.wlock(), repo.lock(), repo.transaction(b"builddag"):
257 258 at = -1
258 259 atbranch = b'default'
259 260 nodeids = []
260 261 id = 0
261 262 progress.update(id)
262 263 for type, data in dagparser.parsedag(text):
263 264 if type == b'n':
264 265 ui.note((b'node %s\n' % pycompat.bytestr(data)))
265 266 id, ps = data
266 267
267 268 files = []
268 269 filecontent = {}
269 270
270 271 p2 = None
271 272 if mergeable_file:
272 273 fn = b"mf"
273 274 p1 = repo[ps[0]]
274 275 if len(ps) > 1:
275 276 p2 = repo[ps[1]]
276 277 pa = p1.ancestor(p2)
277 278 base, local, other = [
278 279 x[fn].data() for x in (pa, p1, p2)
279 280 ]
280 281 m3 = simplemerge.Merge3Text(base, local, other)
281 282 ml = [
282 283 l.strip()
283 284 for l in simplemerge.render_minimized(m3)[0]
284 285 ]
285 286 ml.append(b"")
286 287 elif at > 0:
287 288 ml = p1[fn].data().split(b"\n")
288 289 else:
289 290 ml = initialmergedlines
290 291 ml[id * linesperrev] += b" r%i" % id
291 292 mergedtext = b"\n".join(ml)
292 293 files.append(fn)
293 294 filecontent[fn] = mergedtext
294 295
295 296 if overwritten_file:
296 297 fn = b"of"
297 298 files.append(fn)
298 299 filecontent[fn] = b"r%i\n" % id
299 300
300 301 if new_file:
301 302 fn = b"nf%i" % id
302 303 files.append(fn)
303 304 filecontent[fn] = b"r%i\n" % id
304 305 if len(ps) > 1:
305 306 if not p2:
306 307 p2 = repo[ps[1]]
307 308 for fn in p2:
308 309 if fn.startswith(b"nf"):
309 310 files.append(fn)
310 311 filecontent[fn] = p2[fn].data()
311 312
312 313 def fctxfn(repo, cx, path):
313 314 if path in filecontent:
314 315 return context.memfilectx(
315 316 repo, cx, path, filecontent[path]
316 317 )
317 318 return None
318 319
319 320 if len(ps) == 0 or ps[0] < 0:
320 321 pars = [None, None]
321 322 elif len(ps) == 1:
322 323 pars = [nodeids[ps[0]], None]
323 324 else:
324 325 pars = [nodeids[p] for p in ps]
325 326 cx = context.memctx(
326 327 repo,
327 328 pars,
328 329 b"r%i" % id,
329 330 files,
330 331 fctxfn,
331 332 date=(id, 0),
332 333 user=b"debugbuilddag",
333 334 extra={b'branch': atbranch},
334 335 )
335 336 nodeid = repo.commitctx(cx)
336 337 nodeids.append(nodeid)
337 338 at = id
338 339 elif type == b'l':
339 340 id, name = data
340 341 ui.note((b'tag %s\n' % name))
341 342 tags.append(b"%s %s\n" % (hex(repo.changelog.node(id)), name))
342 343 elif type == b'a':
343 344 ui.note((b'branch %s\n' % data))
344 345 atbranch = data
345 346 progress.update(id)
346 347
347 348 if tags:
348 349 repo.vfs.write(b"localtags", b"".join(tags))
349 350
350 351
351 352 def _debugchangegroup(ui, gen, all=None, indent=0, **opts):
352 353 indent_string = b' ' * indent
353 354 if all:
354 355 ui.writenoi18n(
355 356 b"%sformat: id, p1, p2, cset, delta base, len(delta)\n"
356 357 % indent_string
357 358 )
358 359
359 360 def showchunks(named):
360 361 ui.write(b"\n%s%s\n" % (indent_string, named))
361 362 for deltadata in gen.deltaiter():
362 363 node, p1, p2, cs, deltabase, delta, flags, sidedata = deltadata
363 364 ui.write(
364 365 b"%s%s %s %s %s %s %d\n"
365 366 % (
366 367 indent_string,
367 368 hex(node),
368 369 hex(p1),
369 370 hex(p2),
370 371 hex(cs),
371 372 hex(deltabase),
372 373 len(delta),
373 374 )
374 375 )
375 376
376 377 gen.changelogheader()
377 378 showchunks(b"changelog")
378 379 gen.manifestheader()
379 380 showchunks(b"manifest")
380 381 for chunkdata in iter(gen.filelogheader, {}):
381 382 fname = chunkdata[b'filename']
382 383 showchunks(fname)
383 384 else:
384 385 if isinstance(gen, bundle2.unbundle20):
385 386 raise error.Abort(_(b'use debugbundle2 for this file'))
386 387 gen.changelogheader()
387 388 for deltadata in gen.deltaiter():
388 389 node, p1, p2, cs, deltabase, delta, flags, sidedata = deltadata
389 390 ui.write(b"%s%s\n" % (indent_string, hex(node)))
390 391
391 392
392 393 def _debugobsmarkers(ui, part, indent=0, **opts):
393 394 """display version and markers contained in 'data'"""
394 395 opts = pycompat.byteskwargs(opts)
395 396 data = part.read()
396 397 indent_string = b' ' * indent
397 398 try:
398 399 version, markers = obsolete._readmarkers(data)
399 400 except error.UnknownVersion as exc:
400 401 msg = b"%sunsupported version: %s (%d bytes)\n"
401 402 msg %= indent_string, exc.version, len(data)
402 403 ui.write(msg)
403 404 else:
404 405 msg = b"%sversion: %d (%d bytes)\n"
405 406 msg %= indent_string, version, len(data)
406 407 ui.write(msg)
407 408 fm = ui.formatter(b'debugobsolete', opts)
408 409 for rawmarker in sorted(markers):
409 410 m = obsutil.marker(None, rawmarker)
410 411 fm.startitem()
411 412 fm.plain(indent_string)
412 413 cmdutil.showmarker(fm, m)
413 414 fm.end()
414 415
415 416
416 417 def _debugphaseheads(ui, data, indent=0):
417 418 """display version and markers contained in 'data'"""
418 419 indent_string = b' ' * indent
419 420 headsbyphase = phases.binarydecode(data)
420 421 for phase in phases.allphases:
421 422 for head in headsbyphase[phase]:
422 423 ui.write(indent_string)
423 424 ui.write(b'%s %s\n' % (hex(head), phases.phasenames[phase]))
424 425
425 426
426 427 def _quasirepr(thing):
427 428 if isinstance(thing, (dict, util.sortdict, collections.OrderedDict)):
428 429 return b'{%s}' % (
429 430 b', '.join(b'%s: %s' % (k, thing[k]) for k in sorted(thing))
430 431 )
431 432 return pycompat.bytestr(repr(thing))
432 433
433 434
434 435 def _debugbundle2(ui, gen, all=None, **opts):
435 436 """lists the contents of a bundle2"""
436 437 if not isinstance(gen, bundle2.unbundle20):
437 438 raise error.Abort(_(b'not a bundle2 file'))
438 439 ui.write((b'Stream params: %s\n' % _quasirepr(gen.params)))
439 440 parttypes = opts.get('part_type', [])
440 441 for part in gen.iterparts():
441 442 if parttypes and part.type not in parttypes:
442 443 continue
443 444 msg = b'%s -- %s (mandatory: %r)\n'
444 445 ui.write((msg % (part.type, _quasirepr(part.params), part.mandatory)))
445 446 if part.type == b'changegroup':
446 447 version = part.params.get(b'version', b'01')
447 448 cg = changegroup.getunbundler(version, part, b'UN')
448 449 if not ui.quiet:
449 450 _debugchangegroup(ui, cg, all=all, indent=4, **opts)
450 451 if part.type == b'obsmarkers':
451 452 if not ui.quiet:
452 453 _debugobsmarkers(ui, part, indent=4, **opts)
453 454 if part.type == b'phase-heads':
454 455 if not ui.quiet:
455 456 _debugphaseheads(ui, part, indent=4)
456 457
457 458
458 459 @command(
459 460 b'debugbundle',
460 461 [
461 462 (b'a', b'all', None, _(b'show all details')),
462 463 (b'', b'part-type', [], _(b'show only the named part type')),
463 464 (b'', b'spec', None, _(b'print the bundlespec of the bundle')),
464 465 ],
465 466 _(b'FILE'),
466 467 norepo=True,
467 468 )
468 469 def debugbundle(ui, bundlepath, all=None, spec=None, **opts):
469 470 """lists the contents of a bundle"""
470 471 with hg.openpath(ui, bundlepath) as f:
471 472 if spec:
472 473 spec = exchange.getbundlespec(ui, f)
473 474 ui.write(b'%s\n' % spec)
474 475 return
475 476
476 477 gen = exchange.readbundle(ui, f, bundlepath)
477 478 if isinstance(gen, bundle2.unbundle20):
478 479 return _debugbundle2(ui, gen, all=all, **opts)
479 480 _debugchangegroup(ui, gen, all=all, **opts)
480 481
481 482
482 483 @command(b'debugcapabilities', [], _(b'PATH'), norepo=True)
483 484 def debugcapabilities(ui, path, **opts):
484 485 """lists the capabilities of a remote peer"""
485 486 opts = pycompat.byteskwargs(opts)
486 487 peer = hg.peer(ui, opts, path)
487 488 try:
488 489 caps = peer.capabilities()
489 490 ui.writenoi18n(b'Main capabilities:\n')
490 491 for c in sorted(caps):
491 492 ui.write(b' %s\n' % c)
492 493 b2caps = bundle2.bundle2caps(peer)
493 494 if b2caps:
494 495 ui.writenoi18n(b'Bundle2 capabilities:\n')
495 496 for key, values in sorted(b2caps.items()):
496 497 ui.write(b' %s\n' % key)
497 498 for v in values:
498 499 ui.write(b' %s\n' % v)
499 500 finally:
500 501 peer.close()
501 502
502 503
503 504 @command(
504 505 b'debugchangedfiles',
505 506 [
506 507 (
507 508 b'',
508 509 b'compute',
509 510 False,
510 511 b"compute information instead of reading it from storage",
511 512 ),
512 513 ],
513 514 b'REV',
514 515 )
515 516 def debugchangedfiles(ui, repo, rev, **opts):
516 517 """list the stored files changes for a revision"""
517 518 ctx = logcmdutil.revsingle(repo, rev, None)
518 519 files = None
519 520
520 521 if opts['compute']:
521 522 files = metadata.compute_all_files_changes(ctx)
522 523 else:
523 524 sd = repo.changelog.sidedata(ctx.rev())
524 525 files_block = sd.get(sidedata.SD_FILES)
525 526 if files_block is not None:
526 527 files = metadata.decode_files_sidedata(sd)
527 528 if files is not None:
528 529 for f in sorted(files.touched):
529 530 if f in files.added:
530 531 action = b"added"
531 532 elif f in files.removed:
532 533 action = b"removed"
533 534 elif f in files.merged:
534 535 action = b"merged"
535 536 elif f in files.salvaged:
536 537 action = b"salvaged"
537 538 else:
538 539 action = b"touched"
539 540
540 541 copy_parent = b""
541 542 copy_source = b""
542 543 if f in files.copied_from_p1:
543 544 copy_parent = b"p1"
544 545 copy_source = files.copied_from_p1[f]
545 546 elif f in files.copied_from_p2:
546 547 copy_parent = b"p2"
547 548 copy_source = files.copied_from_p2[f]
548 549
549 550 data = (action, copy_parent, f, copy_source)
550 551 template = b"%-8s %2s: %s, %s;\n"
551 552 ui.write(template % data)
552 553
553 554
554 555 @command(b'debugcheckstate', [], b'')
555 556 def debugcheckstate(ui, repo):
556 557 """validate the correctness of the current dirstate"""
557 parent1, parent2 = repo.dirstate.parents()
558 m1 = repo[parent1].manifest()
559 m2 = repo[parent2].manifest()
560 errors = 0
561 for err in repo.dirstate.verify(m1, m2):
562 ui.warn(err)
563 errors += 1
558 errors = verify.verifier(repo)._verify_dirstate()
564 559 if errors:
565 errstr = _(b".hg/dirstate inconsistent with current parent's manifest")
560 errstr = _(b"dirstate inconsistent with current parent's manifest")
566 561 raise error.Abort(errstr)
567 562
568 563
569 564 @command(
570 565 b'debugcolor',
571 566 [(b'', b'style', None, _(b'show all configured styles'))],
572 567 b'hg debugcolor',
573 568 )
574 569 def debugcolor(ui, repo, **opts):
575 570 """show available color, effects or style"""
576 571 ui.writenoi18n(b'color mode: %s\n' % stringutil.pprint(ui._colormode))
577 572 if opts.get('style'):
578 573 return _debugdisplaystyle(ui)
579 574 else:
580 575 return _debugdisplaycolor(ui)
581 576
582 577
583 578 def _debugdisplaycolor(ui):
584 579 ui = ui.copy()
585 580 ui._styles.clear()
586 581 for effect in color._activeeffects(ui).keys():
587 582 ui._styles[effect] = effect
588 583 if ui._terminfoparams:
589 584 for k, v in ui.configitems(b'color'):
590 585 if k.startswith(b'color.'):
591 586 ui._styles[k] = k[6:]
592 587 elif k.startswith(b'terminfo.'):
593 588 ui._styles[k] = k[9:]
594 589 ui.write(_(b'available colors:\n'))
595 590 # sort label with a '_' after the other to group '_background' entry.
596 591 items = sorted(ui._styles.items(), key=lambda i: (b'_' in i[0], i[0], i[1]))
597 592 for colorname, label in items:
598 593 ui.write(b'%s\n' % colorname, label=label)
599 594
600 595
601 596 def _debugdisplaystyle(ui):
602 597 ui.write(_(b'available style:\n'))
603 598 if not ui._styles:
604 599 return
605 600 width = max(len(s) for s in ui._styles)
606 601 for label, effects in sorted(ui._styles.items()):
607 602 ui.write(b'%s' % label, label=label)
608 603 if effects:
609 604 # 50
610 605 ui.write(b': ')
611 606 ui.write(b' ' * (max(0, width - len(label))))
612 607 ui.write(b', '.join(ui.label(e, e) for e in effects.split()))
613 608 ui.write(b'\n')
614 609
615 610
616 611 @command(b'debugcreatestreamclonebundle', [], b'FILE')
617 612 def debugcreatestreamclonebundle(ui, repo, fname):
618 613 """create a stream clone bundle file
619 614
620 615 Stream bundles are special bundles that are essentially archives of
621 616 revlog files. They are commonly used for cloning very quickly.
622 617 """
623 618 # TODO we may want to turn this into an abort when this functionality
624 619 # is moved into `hg bundle`.
625 620 if phases.hassecret(repo):
626 621 ui.warn(
627 622 _(
628 623 b'(warning: stream clone bundle will contain secret '
629 624 b'revisions)\n'
630 625 )
631 626 )
632 627
633 628 requirements, gen = streamclone.generatebundlev1(repo)
634 629 changegroup.writechunks(ui, gen, fname)
635 630
636 631 ui.write(_(b'bundle requirements: %s\n') % b', '.join(sorted(requirements)))
637 632
638 633
639 634 @command(
640 635 b'debugdag',
641 636 [
642 637 (b't', b'tags', None, _(b'use tags as labels')),
643 638 (b'b', b'branches', None, _(b'annotate with branch names')),
644 639 (b'', b'dots', None, _(b'use dots for runs')),
645 640 (b's', b'spaces', None, _(b'separate elements by spaces')),
646 641 ],
647 642 _(b'[OPTION]... [FILE [REV]...]'),
648 643 optionalrepo=True,
649 644 )
650 645 def debugdag(ui, repo, file_=None, *revs, **opts):
651 646 """format the changelog or an index DAG as a concise textual description
652 647
653 648 If you pass a revlog index, the revlog's DAG is emitted. If you list
654 649 revision numbers, they get labeled in the output as rN.
655 650
656 651 Otherwise, the changelog DAG of the current repo is emitted.
657 652 """
658 653 spaces = opts.get('spaces')
659 654 dots = opts.get('dots')
660 655 if file_:
661 656 rlog = revlog.revlog(vfsmod.vfs(encoding.getcwd(), audit=False), file_)
662 657 revs = {int(r) for r in revs}
663 658
664 659 def events():
665 660 for r in rlog:
666 661 yield b'n', (r, list(p for p in rlog.parentrevs(r) if p != -1))
667 662 if r in revs:
668 663 yield b'l', (r, b"r%i" % r)
669 664
670 665 elif repo:
671 666 cl = repo.changelog
672 667 tags = opts.get('tags')
673 668 branches = opts.get('branches')
674 669 if tags:
675 670 labels = {}
676 671 for l, n in repo.tags().items():
677 672 labels.setdefault(cl.rev(n), []).append(l)
678 673
679 674 def events():
680 675 b = b"default"
681 676 for r in cl:
682 677 if branches:
683 678 newb = cl.read(cl.node(r))[5][b'branch']
684 679 if newb != b:
685 680 yield b'a', newb
686 681 b = newb
687 682 yield b'n', (r, list(p for p in cl.parentrevs(r) if p != -1))
688 683 if tags:
689 684 ls = labels.get(r)
690 685 if ls:
691 686 for l in ls:
692 687 yield b'l', (r, l)
693 688
694 689 else:
695 690 raise error.Abort(_(b'need repo for changelog dag'))
696 691
697 692 for line in dagparser.dagtextlines(
698 693 events(),
699 694 addspaces=spaces,
700 695 wraplabels=True,
701 696 wrapannotations=True,
702 697 wrapnonlinear=dots,
703 698 usedots=dots,
704 699 maxlinewidth=70,
705 700 ):
706 701 ui.write(line)
707 702 ui.write(b"\n")
708 703
709 704
710 705 @command(b'debugdata', cmdutil.debugrevlogopts, _(b'-c|-m|FILE REV'))
711 706 def debugdata(ui, repo, file_, rev=None, **opts):
712 707 """dump the contents of a data file revision"""
713 708 opts = pycompat.byteskwargs(opts)
714 709 if opts.get(b'changelog') or opts.get(b'manifest') or opts.get(b'dir'):
715 710 if rev is not None:
716 711 raise error.CommandError(b'debugdata', _(b'invalid arguments'))
717 712 file_, rev = None, file_
718 713 elif rev is None:
719 714 raise error.CommandError(b'debugdata', _(b'invalid arguments'))
720 715 r = cmdutil.openstorage(repo, b'debugdata', file_, opts)
721 716 try:
722 717 ui.write(r.rawdata(r.lookup(rev)))
723 718 except KeyError:
724 719 raise error.Abort(_(b'invalid revision identifier %s') % rev)
725 720
726 721
727 722 @command(
728 723 b'debugdate',
729 724 [(b'e', b'extended', None, _(b'try extended date formats'))],
730 725 _(b'[-e] DATE [RANGE]'),
731 726 norepo=True,
732 727 optionalrepo=True,
733 728 )
734 729 def debugdate(ui, date, range=None, **opts):
735 730 """parse and display a date"""
736 731 if opts["extended"]:
737 732 d = dateutil.parsedate(date, dateutil.extendeddateformats)
738 733 else:
739 734 d = dateutil.parsedate(date)
740 735 ui.writenoi18n(b"internal: %d %d\n" % d)
741 736 ui.writenoi18n(b"standard: %s\n" % dateutil.datestr(d))
742 737 if range:
743 738 m = dateutil.matchdate(range)
744 739 ui.writenoi18n(b"match: %s\n" % m(d[0]))
745 740
746 741
747 742 @command(
748 743 b'debugdeltachain',
749 744 cmdutil.debugrevlogopts + cmdutil.formatteropts,
750 745 _(b'-c|-m|FILE'),
751 746 optionalrepo=True,
752 747 )
753 748 def debugdeltachain(ui, repo, file_=None, **opts):
754 749 """dump information about delta chains in a revlog
755 750
756 751 Output can be templatized. Available template keywords are:
757 752
758 753 :``rev``: revision number
759 754 :``p1``: parent 1 revision number (for reference)
760 755 :``p2``: parent 2 revision number (for reference)
761 756 :``chainid``: delta chain identifier (numbered by unique base)
762 757 :``chainlen``: delta chain length to this revision
763 758 :``prevrev``: previous revision in delta chain
764 759 :``deltatype``: role of delta / how it was computed
765 760 - base: a full snapshot
766 761 - snap: an intermediate snapshot
767 762 - p1: a delta against the first parent
768 763 - p2: a delta against the second parent
769 764 - skip1: a delta against the same base as p1
770 765 (when p1 has empty delta
771 766 - skip2: a delta against the same base as p2
772 767 (when p2 has empty delta
773 768 - prev: a delta against the previous revision
774 769 - other: a delta against an arbitrary revision
775 770 :``compsize``: compressed size of revision
776 771 :``uncompsize``: uncompressed size of revision
777 772 :``chainsize``: total size of compressed revisions in chain
778 773 :``chainratio``: total chain size divided by uncompressed revision size
779 774 (new delta chains typically start at ratio 2.00)
780 775 :``lindist``: linear distance from base revision in delta chain to end
781 776 of this revision
782 777 :``extradist``: total size of revisions not part of this delta chain from
783 778 base of delta chain to end of this revision; a measurement
784 779 of how much extra data we need to read/seek across to read
785 780 the delta chain for this revision
786 781 :``extraratio``: extradist divided by chainsize; another representation of
787 782 how much unrelated data is needed to load this delta chain
788 783
789 784 If the repository is configured to use the sparse read, additional keywords
790 785 are available:
791 786
792 787 :``readsize``: total size of data read from the disk for a revision
793 788 (sum of the sizes of all the blocks)
794 789 :``largestblock``: size of the largest block of data read from the disk
795 790 :``readdensity``: density of useful bytes in the data read from the disk
796 791 :``srchunks``: in how many data hunks the whole revision would be read
797 792
798 793 The sparse read can be enabled with experimental.sparse-read = True
799 794 """
800 795 opts = pycompat.byteskwargs(opts)
801 796 r = cmdutil.openrevlog(repo, b'debugdeltachain', file_, opts)
802 797 index = r.index
803 798 start = r.start
804 799 length = r.length
805 800 generaldelta = r._generaldelta
806 801 withsparseread = getattr(r, '_withsparseread', False)
807 802
808 803 # security to avoid crash on corrupted revlogs
809 804 total_revs = len(index)
810 805
811 806 def revinfo(rev):
812 807 e = index[rev]
813 808 compsize = e[revlog_constants.ENTRY_DATA_COMPRESSED_LENGTH]
814 809 uncompsize = e[revlog_constants.ENTRY_DATA_UNCOMPRESSED_LENGTH]
815 810 chainsize = 0
816 811
817 812 base = e[revlog_constants.ENTRY_DELTA_BASE]
818 813 p1 = e[revlog_constants.ENTRY_PARENT_1]
819 814 p2 = e[revlog_constants.ENTRY_PARENT_2]
820 815
821 816 # If the parents of a revision has an empty delta, we never try to delta
822 817 # against that parent, but directly against the delta base of that
823 818 # parent (recursively). It avoids adding a useless entry in the chain.
824 819 #
825 820 # However we need to detect that as a special case for delta-type, that
826 821 # is not simply "other".
827 822 p1_base = p1
828 823 if p1 != nullrev and p1 < total_revs:
829 824 e1 = index[p1]
830 825 while e1[revlog_constants.ENTRY_DATA_COMPRESSED_LENGTH] == 0:
831 826 new_base = e1[revlog_constants.ENTRY_DELTA_BASE]
832 827 if (
833 828 new_base == p1_base
834 829 or new_base == nullrev
835 830 or new_base >= total_revs
836 831 ):
837 832 break
838 833 p1_base = new_base
839 834 e1 = index[p1_base]
840 835 p2_base = p2
841 836 if p2 != nullrev and p2 < total_revs:
842 837 e2 = index[p2]
843 838 while e2[revlog_constants.ENTRY_DATA_COMPRESSED_LENGTH] == 0:
844 839 new_base = e2[revlog_constants.ENTRY_DELTA_BASE]
845 840 if (
846 841 new_base == p2_base
847 842 or new_base == nullrev
848 843 or new_base >= total_revs
849 844 ):
850 845 break
851 846 p2_base = new_base
852 847 e2 = index[p2_base]
853 848
854 849 if generaldelta:
855 850 if base == p1:
856 851 deltatype = b'p1'
857 852 elif base == p2:
858 853 deltatype = b'p2'
859 854 elif base == rev:
860 855 deltatype = b'base'
861 856 elif base == p1_base:
862 857 deltatype = b'skip1'
863 858 elif base == p2_base:
864 859 deltatype = b'skip2'
865 860 elif r.issnapshot(rev):
866 861 deltatype = b'snap'
867 862 elif base == rev - 1:
868 863 deltatype = b'prev'
869 864 else:
870 865 deltatype = b'other'
871 866 else:
872 867 if base == rev:
873 868 deltatype = b'base'
874 869 else:
875 870 deltatype = b'prev'
876 871
877 872 chain = r._deltachain(rev)[0]
878 873 for iterrev in chain:
879 874 e = index[iterrev]
880 875 chainsize += e[revlog_constants.ENTRY_DATA_COMPRESSED_LENGTH]
881 876
882 877 return p1, p2, compsize, uncompsize, deltatype, chain, chainsize
883 878
884 879 fm = ui.formatter(b'debugdeltachain', opts)
885 880
886 881 fm.plain(
887 882 b' rev p1 p2 chain# chainlen prev delta '
888 883 b'size rawsize chainsize ratio lindist extradist '
889 884 b'extraratio'
890 885 )
891 886 if withsparseread:
892 887 fm.plain(b' readsize largestblk rddensity srchunks')
893 888 fm.plain(b'\n')
894 889
895 890 chainbases = {}
896 891 for rev in r:
897 892 p1, p2, comp, uncomp, deltatype, chain, chainsize = revinfo(rev)
898 893 chainbase = chain[0]
899 894 chainid = chainbases.setdefault(chainbase, len(chainbases) + 1)
900 895 basestart = start(chainbase)
901 896 revstart = start(rev)
902 897 lineardist = revstart + comp - basestart
903 898 extradist = lineardist - chainsize
904 899 try:
905 900 prevrev = chain[-2]
906 901 except IndexError:
907 902 prevrev = -1
908 903
909 904 if uncomp != 0:
910 905 chainratio = float(chainsize) / float(uncomp)
911 906 else:
912 907 chainratio = chainsize
913 908
914 909 if chainsize != 0:
915 910 extraratio = float(extradist) / float(chainsize)
916 911 else:
917 912 extraratio = extradist
918 913
919 914 fm.startitem()
920 915 fm.write(
921 916 b'rev p1 p2 chainid chainlen prevrev deltatype compsize '
922 917 b'uncompsize chainsize chainratio lindist extradist '
923 918 b'extraratio',
924 919 b'%7d %7d %7d %7d %8d %8d %7s %10d %10d %10d %9.5f %9d %9d %10.5f',
925 920 rev,
926 921 p1,
927 922 p2,
928 923 chainid,
929 924 len(chain),
930 925 prevrev,
931 926 deltatype,
932 927 comp,
933 928 uncomp,
934 929 chainsize,
935 930 chainratio,
936 931 lineardist,
937 932 extradist,
938 933 extraratio,
939 934 rev=rev,
940 935 chainid=chainid,
941 936 chainlen=len(chain),
942 937 prevrev=prevrev,
943 938 deltatype=deltatype,
944 939 compsize=comp,
945 940 uncompsize=uncomp,
946 941 chainsize=chainsize,
947 942 chainratio=chainratio,
948 943 lindist=lineardist,
949 944 extradist=extradist,
950 945 extraratio=extraratio,
951 946 )
952 947 if withsparseread:
953 948 readsize = 0
954 949 largestblock = 0
955 950 srchunks = 0
956 951
957 952 for revschunk in deltautil.slicechunk(r, chain):
958 953 srchunks += 1
959 954 blkend = start(revschunk[-1]) + length(revschunk[-1])
960 955 blksize = blkend - start(revschunk[0])
961 956
962 957 readsize += blksize
963 958 if largestblock < blksize:
964 959 largestblock = blksize
965 960
966 961 if readsize:
967 962 readdensity = float(chainsize) / float(readsize)
968 963 else:
969 964 readdensity = 1
970 965
971 966 fm.write(
972 967 b'readsize largestblock readdensity srchunks',
973 968 b' %10d %10d %9.5f %8d',
974 969 readsize,
975 970 largestblock,
976 971 readdensity,
977 972 srchunks,
978 973 readsize=readsize,
979 974 largestblock=largestblock,
980 975 readdensity=readdensity,
981 976 srchunks=srchunks,
982 977 )
983 978
984 979 fm.plain(b'\n')
985 980
986 981 fm.end()
987 982
988 983
989 984 @command(
990 985 b'debug-delta-find',
991 986 cmdutil.debugrevlogopts
992 987 + cmdutil.formatteropts
993 988 + [
994 989 (
995 990 b'',
996 991 b'source',
997 992 b'full',
998 993 _(b'input data feed to the process (full, storage, p1, p2, prev)'),
999 994 ),
1000 995 ],
1001 996 _(b'-c|-m|FILE REV'),
1002 997 optionalrepo=True,
1003 998 )
1004 999 def debugdeltafind(ui, repo, arg_1, arg_2=None, source=b'full', **opts):
1005 1000 """display the computation to get to a valid delta for storing REV
1006 1001
1007 1002 This command will replay the process used to find the "best" delta to store
1008 1003 a revision and display information about all the steps used to get to that
1009 1004 result.
1010 1005
1011 1006 By default, the process is fed with a the full-text for the revision. This
1012 1007 can be controlled with the --source flag.
1013 1008
1014 1009 The revision use the revision number of the target storage (not changelog
1015 1010 revision number).
1016 1011
1017 1012 note: the process is initiated from a full text of the revision to store.
1018 1013 """
1019 1014 opts = pycompat.byteskwargs(opts)
1020 1015 if arg_2 is None:
1021 1016 file_ = None
1022 1017 rev = arg_1
1023 1018 else:
1024 1019 file_ = arg_1
1025 1020 rev = arg_2
1026 1021
1027 1022 rev = int(rev)
1028 1023
1029 1024 revlog = cmdutil.openrevlog(repo, b'debugdeltachain', file_, opts)
1030 1025 p1r, p2r = revlog.parentrevs(rev)
1031 1026
1032 1027 if source == b'full':
1033 1028 base_rev = nullrev
1034 1029 elif source == b'storage':
1035 1030 base_rev = revlog.deltaparent(rev)
1036 1031 elif source == b'p1':
1037 1032 base_rev = p1r
1038 1033 elif source == b'p2':
1039 1034 base_rev = p2r
1040 1035 elif source == b'prev':
1041 1036 base_rev = rev - 1
1042 1037 else:
1043 1038 raise error.InputError(b"invalid --source value: %s" % source)
1044 1039
1045 1040 revlog_debug.debug_delta_find(ui, revlog, rev, base_rev=base_rev)
1046 1041
1047 1042
1048 1043 @command(
1049 1044 b'debugdirstate|debugstate',
1050 1045 [
1051 1046 (
1052 1047 b'',
1053 1048 b'nodates',
1054 1049 None,
1055 1050 _(b'do not display the saved mtime (DEPRECATED)'),
1056 1051 ),
1057 1052 (b'', b'dates', True, _(b'display the saved mtime')),
1058 1053 (b'', b'datesort', None, _(b'sort by saved mtime')),
1059 1054 (
1060 1055 b'',
1061 1056 b'docket',
1062 1057 False,
1063 1058 _(b'display the docket (metadata file) instead'),
1064 1059 ),
1065 1060 (
1066 1061 b'',
1067 1062 b'all',
1068 1063 False,
1069 1064 _(b'display dirstate-v2 tree nodes that would not exist in v1'),
1070 1065 ),
1071 1066 ],
1072 1067 _(b'[OPTION]...'),
1073 1068 )
1074 1069 def debugstate(ui, repo, **opts):
1075 1070 """show the contents of the current dirstate"""
1076 1071
1077 1072 if opts.get("docket"):
1078 1073 if not repo.dirstate._use_dirstate_v2:
1079 1074 raise error.Abort(_(b'dirstate v1 does not have a docket'))
1080 1075
1081 1076 docket = repo.dirstate._map.docket
1082 1077 (
1083 1078 start_offset,
1084 1079 root_nodes,
1085 1080 nodes_with_entry,
1086 1081 nodes_with_copy,
1087 1082 unused_bytes,
1088 1083 _unused,
1089 1084 ignore_pattern,
1090 1085 ) = dirstateutils.v2.TREE_METADATA.unpack(docket.tree_metadata)
1091 1086
1092 1087 ui.write(_(b"size of dirstate data: %d\n") % docket.data_size)
1093 1088 ui.write(_(b"data file uuid: %s\n") % docket.uuid)
1094 1089 ui.write(_(b"start offset of root nodes: %d\n") % start_offset)
1095 1090 ui.write(_(b"number of root nodes: %d\n") % root_nodes)
1096 1091 ui.write(_(b"nodes with entries: %d\n") % nodes_with_entry)
1097 1092 ui.write(_(b"nodes with copies: %d\n") % nodes_with_copy)
1098 1093 ui.write(_(b"number of unused bytes: %d\n") % unused_bytes)
1099 1094 ui.write(
1100 1095 _(b"ignore pattern hash: %s\n") % binascii.hexlify(ignore_pattern)
1101 1096 )
1102 1097 return
1103 1098
1104 1099 nodates = not opts['dates']
1105 1100 if opts.get('nodates') is not None:
1106 1101 nodates = True
1107 1102 datesort = opts.get('datesort')
1108 1103
1109 1104 if datesort:
1110 1105
1111 1106 def keyfunc(entry):
1112 1107 filename, _state, _mode, _size, mtime = entry
1113 1108 return (mtime, filename)
1114 1109
1115 1110 else:
1116 1111 keyfunc = None # sort by filename
1117 1112 entries = list(repo.dirstate._map.debug_iter(all=opts['all']))
1118 1113 entries.sort(key=keyfunc)
1119 1114 for entry in entries:
1120 1115 filename, state, mode, size, mtime = entry
1121 1116 if mtime == -1:
1122 1117 timestr = b'unset '
1123 1118 elif nodates:
1124 1119 timestr = b'set '
1125 1120 else:
1126 1121 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(mtime))
1127 1122 timestr = encoding.strtolocal(timestr)
1128 1123 if mode & 0o20000:
1129 1124 mode = b'lnk'
1130 1125 else:
1131 1126 mode = b'%3o' % (mode & 0o777 & ~util.umask)
1132 1127 ui.write(b"%c %s %10d %s%s\n" % (state, mode, size, timestr, filename))
1133 1128 for f in repo.dirstate.copies():
1134 1129 ui.write(_(b"copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
1135 1130
1136 1131
1137 1132 @command(
1138 1133 b'debugdirstateignorepatternshash',
1139 1134 [],
1140 1135 _(b''),
1141 1136 )
1142 1137 def debugdirstateignorepatternshash(ui, repo, **opts):
1143 1138 """show the hash of ignore patterns stored in dirstate if v2,
1144 1139 or nothing for dirstate-v2
1145 1140 """
1146 1141 if repo.dirstate._use_dirstate_v2:
1147 1142 docket = repo.dirstate._map.docket
1148 1143 hash_len = 20 # 160 bits for SHA-1
1149 1144 hash_bytes = docket.tree_metadata[-hash_len:]
1150 1145 ui.write(binascii.hexlify(hash_bytes) + b'\n')
1151 1146
1152 1147
1153 1148 @command(
1154 1149 b'debugdiscovery',
1155 1150 [
1156 1151 (b'', b'old', None, _(b'use old-style discovery')),
1157 1152 (
1158 1153 b'',
1159 1154 b'nonheads',
1160 1155 None,
1161 1156 _(b'use old-style discovery with non-heads included'),
1162 1157 ),
1163 1158 (b'', b'rev', [], b'restrict discovery to this set of revs'),
1164 1159 (b'', b'seed', b'12323', b'specify the random seed use for discovery'),
1165 1160 (
1166 1161 b'',
1167 1162 b'local-as-revs',
1168 1163 b"",
1169 1164 b'treat local has having these revisions only',
1170 1165 ),
1171 1166 (
1172 1167 b'',
1173 1168 b'remote-as-revs',
1174 1169 b"",
1175 1170 b'use local as remote, with only these revisions',
1176 1171 ),
1177 1172 ]
1178 1173 + cmdutil.remoteopts
1179 1174 + cmdutil.formatteropts,
1180 1175 _(b'[--rev REV] [OTHER]'),
1181 1176 )
1182 1177 def debugdiscovery(ui, repo, remoteurl=b"default", **opts):
1183 1178 """runs the changeset discovery protocol in isolation
1184 1179
1185 1180 The local peer can be "replaced" by a subset of the local repository by
1186 1181 using the `--local-as-revs` flag. In the same way, the usual `remote` peer
1187 1182 can be "replaced" by a subset of the local repository using the
1188 1183 `--remote-as-revs` flag. This is useful to efficiently debug pathological
1189 1184 discovery situations.
1190 1185
1191 1186 The following developer oriented config are relevant for people playing with this command:
1192 1187
1193 1188 * devel.discovery.exchange-heads=True
1194 1189
1195 1190 If False, the discovery will not start with
1196 1191 remote head fetching and local head querying.
1197 1192
1198 1193 * devel.discovery.grow-sample=True
1199 1194
1200 1195 If False, the sample size used in set discovery will not be increased
1201 1196 through the process
1202 1197
1203 1198 * devel.discovery.grow-sample.dynamic=True
1204 1199
1205 1200 When discovery.grow-sample.dynamic is True, the default, the sample size is
1206 1201 adapted to the shape of the undecided set (it is set to the max of:
1207 1202 <target-size>, len(roots(undecided)), len(heads(undecided)
1208 1203
1209 1204 * devel.discovery.grow-sample.rate=1.05
1210 1205
1211 1206 the rate at which the sample grow
1212 1207
1213 1208 * devel.discovery.randomize=True
1214 1209
1215 1210 If andom sampling during discovery are deterministic. It is meant for
1216 1211 integration tests.
1217 1212
1218 1213 * devel.discovery.sample-size=200
1219 1214
1220 1215 Control the initial size of the discovery sample
1221 1216
1222 1217 * devel.discovery.sample-size.initial=100
1223 1218
1224 1219 Control the initial size of the discovery for initial change
1225 1220 """
1226 1221 opts = pycompat.byteskwargs(opts)
1227 1222 unfi = repo.unfiltered()
1228 1223
1229 1224 # setup potential extra filtering
1230 1225 local_revs = opts[b"local_as_revs"]
1231 1226 remote_revs = opts[b"remote_as_revs"]
1232 1227
1233 1228 # make sure tests are repeatable
1234 1229 random.seed(int(opts[b'seed']))
1235 1230
1236 1231 if not remote_revs:
1237 1232 path = urlutil.get_unique_pull_path_obj(
1238 1233 b'debugdiscovery', ui, remoteurl
1239 1234 )
1240 1235 branches = (path.branch, [])
1241 1236 remote = hg.peer(repo, opts, path)
1242 1237 ui.status(_(b'comparing with %s\n') % urlutil.hidepassword(path.loc))
1243 1238 else:
1244 1239 branches = (None, [])
1245 1240 remote_filtered_revs = logcmdutil.revrange(
1246 1241 unfi, [b"not (::(%s))" % remote_revs]
1247 1242 )
1248 1243 remote_filtered_revs = frozenset(remote_filtered_revs)
1249 1244
1250 1245 def remote_func(x):
1251 1246 return remote_filtered_revs
1252 1247
1253 1248 repoview.filtertable[b'debug-discovery-remote-filter'] = remote_func
1254 1249
1255 1250 remote = repo.peer()
1256 1251 remote._repo = remote._repo.filtered(b'debug-discovery-remote-filter')
1257 1252
1258 1253 if local_revs:
1259 1254 local_filtered_revs = logcmdutil.revrange(
1260 1255 unfi, [b"not (::(%s))" % local_revs]
1261 1256 )
1262 1257 local_filtered_revs = frozenset(local_filtered_revs)
1263 1258
1264 1259 def local_func(x):
1265 1260 return local_filtered_revs
1266 1261
1267 1262 repoview.filtertable[b'debug-discovery-local-filter'] = local_func
1268 1263 repo = repo.filtered(b'debug-discovery-local-filter')
1269 1264
1270 1265 data = {}
1271 1266 if opts.get(b'old'):
1272 1267
1273 1268 def doit(pushedrevs, remoteheads, remote=remote):
1274 1269 if not util.safehasattr(remote, b'branches'):
1275 1270 # enable in-client legacy support
1276 1271 remote = localrepo.locallegacypeer(remote.local())
1277 1272 if remote_revs:
1278 1273 r = remote._repo.filtered(b'debug-discovery-remote-filter')
1279 1274 remote._repo = r
1280 1275 common, _in, hds = treediscovery.findcommonincoming(
1281 1276 repo, remote, force=True, audit=data
1282 1277 )
1283 1278 common = set(common)
1284 1279 if not opts.get(b'nonheads'):
1285 1280 ui.writenoi18n(
1286 1281 b"unpruned common: %s\n"
1287 1282 % b" ".join(sorted(short(n) for n in common))
1288 1283 )
1289 1284
1290 1285 clnode = repo.changelog.node
1291 1286 common = repo.revs(b'heads(::%ln)', common)
1292 1287 common = {clnode(r) for r in common}
1293 1288 return common, hds
1294 1289
1295 1290 else:
1296 1291
1297 1292 def doit(pushedrevs, remoteheads, remote=remote):
1298 1293 nodes = None
1299 1294 if pushedrevs:
1300 1295 revs = logcmdutil.revrange(repo, pushedrevs)
1301 1296 nodes = [repo[r].node() for r in revs]
1302 1297 common, any, hds = setdiscovery.findcommonheads(
1303 1298 ui,
1304 1299 repo,
1305 1300 remote,
1306 1301 ancestorsof=nodes,
1307 1302 audit=data,
1308 1303 abortwhenunrelated=False,
1309 1304 )
1310 1305 return common, hds
1311 1306
1312 1307 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches, revs=None)
1313 1308 localrevs = opts[b'rev']
1314 1309
1315 1310 fm = ui.formatter(b'debugdiscovery', opts)
1316 1311 if fm.strict_format:
1317 1312
1318 1313 @contextlib.contextmanager
1319 1314 def may_capture_output():
1320 1315 ui.pushbuffer()
1321 1316 yield
1322 1317 data[b'output'] = ui.popbuffer()
1323 1318
1324 1319 else:
1325 1320 may_capture_output = util.nullcontextmanager
1326 1321 with may_capture_output():
1327 1322 with util.timedcm('debug-discovery') as t:
1328 1323 common, hds = doit(localrevs, remoterevs)
1329 1324
1330 1325 # compute all statistics
1331 1326 if len(common) == 1 and repo.nullid in common:
1332 1327 common = set()
1333 1328 heads_common = set(common)
1334 1329 heads_remote = set(hds)
1335 1330 heads_local = set(repo.heads())
1336 1331 # note: they cannot be a local or remote head that is in common and not
1337 1332 # itself a head of common.
1338 1333 heads_common_local = heads_common & heads_local
1339 1334 heads_common_remote = heads_common & heads_remote
1340 1335 heads_common_both = heads_common & heads_remote & heads_local
1341 1336
1342 1337 all = repo.revs(b'all()')
1343 1338 common = repo.revs(b'::%ln', common)
1344 1339 roots_common = repo.revs(b'roots(::%ld)', common)
1345 1340 missing = repo.revs(b'not ::%ld', common)
1346 1341 heads_missing = repo.revs(b'heads(%ld)', missing)
1347 1342 roots_missing = repo.revs(b'roots(%ld)', missing)
1348 1343 assert len(common) + len(missing) == len(all)
1349 1344
1350 1345 initial_undecided = repo.revs(
1351 1346 b'not (::%ln or %ln::)', heads_common_remote, heads_common_local
1352 1347 )
1353 1348 heads_initial_undecided = repo.revs(b'heads(%ld)', initial_undecided)
1354 1349 roots_initial_undecided = repo.revs(b'roots(%ld)', initial_undecided)
1355 1350 common_initial_undecided = initial_undecided & common
1356 1351 missing_initial_undecided = initial_undecided & missing
1357 1352
1358 1353 data[b'elapsed'] = t.elapsed
1359 1354 data[b'nb-common-heads'] = len(heads_common)
1360 1355 data[b'nb-common-heads-local'] = len(heads_common_local)
1361 1356 data[b'nb-common-heads-remote'] = len(heads_common_remote)
1362 1357 data[b'nb-common-heads-both'] = len(heads_common_both)
1363 1358 data[b'nb-common-roots'] = len(roots_common)
1364 1359 data[b'nb-head-local'] = len(heads_local)
1365 1360 data[b'nb-head-local-missing'] = len(heads_local) - len(heads_common_local)
1366 1361 data[b'nb-head-remote'] = len(heads_remote)
1367 1362 data[b'nb-head-remote-unknown'] = len(heads_remote) - len(
1368 1363 heads_common_remote
1369 1364 )
1370 1365 data[b'nb-revs'] = len(all)
1371 1366 data[b'nb-revs-common'] = len(common)
1372 1367 data[b'nb-revs-missing'] = len(missing)
1373 1368 data[b'nb-missing-heads'] = len(heads_missing)
1374 1369 data[b'nb-missing-roots'] = len(roots_missing)
1375 1370 data[b'nb-ini_und'] = len(initial_undecided)
1376 1371 data[b'nb-ini_und-heads'] = len(heads_initial_undecided)
1377 1372 data[b'nb-ini_und-roots'] = len(roots_initial_undecided)
1378 1373 data[b'nb-ini_und-common'] = len(common_initial_undecided)
1379 1374 data[b'nb-ini_und-missing'] = len(missing_initial_undecided)
1380 1375
1381 1376 fm.startitem()
1382 1377 fm.data(**pycompat.strkwargs(data))
1383 1378 # display discovery summary
1384 1379 fm.plain(b"elapsed time: %(elapsed)f seconds\n" % data)
1385 1380 fm.plain(b"round-trips: %(total-roundtrips)9d\n" % data)
1386 1381 if b'total-round-trips-heads' in data:
1387 1382 fm.plain(
1388 1383 b" round-trips-heads: %(total-round-trips-heads)9d\n" % data
1389 1384 )
1390 1385 if b'total-round-trips-branches' in data:
1391 1386 fm.plain(
1392 1387 b" round-trips-branches: %(total-round-trips-branches)9d\n"
1393 1388 % data
1394 1389 )
1395 1390 if b'total-round-trips-between' in data:
1396 1391 fm.plain(
1397 1392 b" round-trips-between: %(total-round-trips-between)9d\n" % data
1398 1393 )
1399 1394 fm.plain(b"queries: %(total-queries)9d\n" % data)
1400 1395 if b'total-queries-branches' in data:
1401 1396 fm.plain(b" queries-branches: %(total-queries-branches)9d\n" % data)
1402 1397 if b'total-queries-between' in data:
1403 1398 fm.plain(b" queries-between: %(total-queries-between)9d\n" % data)
1404 1399 fm.plain(b"heads summary:\n")
1405 1400 fm.plain(b" total common heads: %(nb-common-heads)9d\n" % data)
1406 1401 fm.plain(b" also local heads: %(nb-common-heads-local)9d\n" % data)
1407 1402 fm.plain(b" also remote heads: %(nb-common-heads-remote)9d\n" % data)
1408 1403 fm.plain(b" both: %(nb-common-heads-both)9d\n" % data)
1409 1404 fm.plain(b" local heads: %(nb-head-local)9d\n" % data)
1410 1405 fm.plain(b" common: %(nb-common-heads-local)9d\n" % data)
1411 1406 fm.plain(b" missing: %(nb-head-local-missing)9d\n" % data)
1412 1407 fm.plain(b" remote heads: %(nb-head-remote)9d\n" % data)
1413 1408 fm.plain(b" common: %(nb-common-heads-remote)9d\n" % data)
1414 1409 fm.plain(b" unknown: %(nb-head-remote-unknown)9d\n" % data)
1415 1410 fm.plain(b"local changesets: %(nb-revs)9d\n" % data)
1416 1411 fm.plain(b" common: %(nb-revs-common)9d\n" % data)
1417 1412 fm.plain(b" heads: %(nb-common-heads)9d\n" % data)
1418 1413 fm.plain(b" roots: %(nb-common-roots)9d\n" % data)
1419 1414 fm.plain(b" missing: %(nb-revs-missing)9d\n" % data)
1420 1415 fm.plain(b" heads: %(nb-missing-heads)9d\n" % data)
1421 1416 fm.plain(b" roots: %(nb-missing-roots)9d\n" % data)
1422 1417 fm.plain(b" first undecided set: %(nb-ini_und)9d\n" % data)
1423 1418 fm.plain(b" heads: %(nb-ini_und-heads)9d\n" % data)
1424 1419 fm.plain(b" roots: %(nb-ini_und-roots)9d\n" % data)
1425 1420 fm.plain(b" common: %(nb-ini_und-common)9d\n" % data)
1426 1421 fm.plain(b" missing: %(nb-ini_und-missing)9d\n" % data)
1427 1422
1428 1423 if ui.verbose:
1429 1424 fm.plain(
1430 1425 b"common heads: %s\n"
1431 1426 % b" ".join(sorted(short(n) for n in heads_common))
1432 1427 )
1433 1428 fm.end()
1434 1429
1435 1430
1436 1431 _chunksize = 4 << 10
1437 1432
1438 1433
1439 1434 @command(
1440 1435 b'debugdownload',
1441 1436 [
1442 1437 (b'o', b'output', b'', _(b'path')),
1443 1438 ],
1444 1439 optionalrepo=True,
1445 1440 )
1446 1441 def debugdownload(ui, repo, url, output=None, **opts):
1447 1442 """download a resource using Mercurial logic and config"""
1448 1443 fh = urlmod.open(ui, url, output)
1449 1444
1450 1445 dest = ui
1451 1446 if output:
1452 1447 dest = open(output, b"wb", _chunksize)
1453 1448 try:
1454 1449 data = fh.read(_chunksize)
1455 1450 while data:
1456 1451 dest.write(data)
1457 1452 data = fh.read(_chunksize)
1458 1453 finally:
1459 1454 if output:
1460 1455 dest.close()
1461 1456
1462 1457
1463 1458 @command(b'debugextensions', cmdutil.formatteropts, [], optionalrepo=True)
1464 1459 def debugextensions(ui, repo, **opts):
1465 1460 '''show information about active extensions'''
1466 1461 opts = pycompat.byteskwargs(opts)
1467 1462 exts = extensions.extensions(ui)
1468 1463 hgver = util.version()
1469 1464 fm = ui.formatter(b'debugextensions', opts)
1470 1465 for extname, extmod in sorted(exts, key=operator.itemgetter(0)):
1471 1466 isinternal = extensions.ismoduleinternal(extmod)
1472 1467 extsource = None
1473 1468
1474 1469 if util.safehasattr(extmod, '__file__'):
1475 1470 extsource = pycompat.fsencode(extmod.__file__)
1476 1471 elif getattr(sys, 'oxidized', False):
1477 1472 extsource = pycompat.sysexecutable
1478 1473 if isinternal:
1479 1474 exttestedwith = [] # never expose magic string to users
1480 1475 else:
1481 1476 exttestedwith = getattr(extmod, 'testedwith', b'').split()
1482 1477 extbuglink = getattr(extmod, 'buglink', None)
1483 1478
1484 1479 fm.startitem()
1485 1480
1486 1481 if ui.quiet or ui.verbose:
1487 1482 fm.write(b'name', b'%s\n', extname)
1488 1483 else:
1489 1484 fm.write(b'name', b'%s', extname)
1490 1485 if isinternal or hgver in exttestedwith:
1491 1486 fm.plain(b'\n')
1492 1487 elif not exttestedwith:
1493 1488 fm.plain(_(b' (untested!)\n'))
1494 1489 else:
1495 1490 lasttestedversion = exttestedwith[-1]
1496 1491 fm.plain(b' (%s!)\n' % lasttestedversion)
1497 1492
1498 1493 fm.condwrite(
1499 1494 ui.verbose and extsource,
1500 1495 b'source',
1501 1496 _(b' location: %s\n'),
1502 1497 extsource or b"",
1503 1498 )
1504 1499
1505 1500 if ui.verbose:
1506 1501 fm.plain(_(b' bundled: %s\n') % [b'no', b'yes'][isinternal])
1507 1502 fm.data(bundled=isinternal)
1508 1503
1509 1504 fm.condwrite(
1510 1505 ui.verbose and exttestedwith,
1511 1506 b'testedwith',
1512 1507 _(b' tested with: %s\n'),
1513 1508 fm.formatlist(exttestedwith, name=b'ver'),
1514 1509 )
1515 1510
1516 1511 fm.condwrite(
1517 1512 ui.verbose and extbuglink,
1518 1513 b'buglink',
1519 1514 _(b' bug reporting: %s\n'),
1520 1515 extbuglink or b"",
1521 1516 )
1522 1517
1523 1518 fm.end()
1524 1519
1525 1520
1526 1521 @command(
1527 1522 b'debugfileset',
1528 1523 [
1529 1524 (
1530 1525 b'r',
1531 1526 b'rev',
1532 1527 b'',
1533 1528 _(b'apply the filespec on this revision'),
1534 1529 _(b'REV'),
1535 1530 ),
1536 1531 (
1537 1532 b'',
1538 1533 b'all-files',
1539 1534 False,
1540 1535 _(b'test files from all revisions and working directory'),
1541 1536 ),
1542 1537 (
1543 1538 b's',
1544 1539 b'show-matcher',
1545 1540 None,
1546 1541 _(b'print internal representation of matcher'),
1547 1542 ),
1548 1543 (
1549 1544 b'p',
1550 1545 b'show-stage',
1551 1546 [],
1552 1547 _(b'print parsed tree at the given stage'),
1553 1548 _(b'NAME'),
1554 1549 ),
1555 1550 ],
1556 1551 _(b'[-r REV] [--all-files] [OPTION]... FILESPEC'),
1557 1552 )
1558 1553 def debugfileset(ui, repo, expr, **opts):
1559 1554 '''parse and apply a fileset specification'''
1560 1555 from . import fileset
1561 1556
1562 1557 fileset.symbols # force import of fileset so we have predicates to optimize
1563 1558 opts = pycompat.byteskwargs(opts)
1564 1559 ctx = logcmdutil.revsingle(repo, opts.get(b'rev'), None)
1565 1560
1566 1561 stages = [
1567 1562 (b'parsed', pycompat.identity),
1568 1563 (b'analyzed', filesetlang.analyze),
1569 1564 (b'optimized', filesetlang.optimize),
1570 1565 ]
1571 1566 stagenames = {n for n, f in stages}
1572 1567
1573 1568 showalways = set()
1574 1569 if ui.verbose and not opts[b'show_stage']:
1575 1570 # show parsed tree by --verbose (deprecated)
1576 1571 showalways.add(b'parsed')
1577 1572 if opts[b'show_stage'] == [b'all']:
1578 1573 showalways.update(stagenames)
1579 1574 else:
1580 1575 for n in opts[b'show_stage']:
1581 1576 if n not in stagenames:
1582 1577 raise error.Abort(_(b'invalid stage name: %s') % n)
1583 1578 showalways.update(opts[b'show_stage'])
1584 1579
1585 1580 tree = filesetlang.parse(expr)
1586 1581 for n, f in stages:
1587 1582 tree = f(tree)
1588 1583 if n in showalways:
1589 1584 if opts[b'show_stage'] or n != b'parsed':
1590 1585 ui.write(b"* %s:\n" % n)
1591 1586 ui.write(filesetlang.prettyformat(tree), b"\n")
1592 1587
1593 1588 files = set()
1594 1589 if opts[b'all_files']:
1595 1590 for r in repo:
1596 1591 c = repo[r]
1597 1592 files.update(c.files())
1598 1593 files.update(c.substate)
1599 1594 if opts[b'all_files'] or ctx.rev() is None:
1600 1595 wctx = repo[None]
1601 1596 files.update(
1602 1597 repo.dirstate.walk(
1603 1598 scmutil.matchall(repo),
1604 1599 subrepos=list(wctx.substate),
1605 1600 unknown=True,
1606 1601 ignored=True,
1607 1602 )
1608 1603 )
1609 1604 files.update(wctx.substate)
1610 1605 else:
1611 1606 files.update(ctx.files())
1612 1607 files.update(ctx.substate)
1613 1608
1614 1609 m = ctx.matchfileset(repo.getcwd(), expr)
1615 1610 if opts[b'show_matcher'] or (opts[b'show_matcher'] is None and ui.verbose):
1616 1611 ui.writenoi18n(b'* matcher:\n', stringutil.prettyrepr(m), b'\n')
1617 1612 for f in sorted(files):
1618 1613 if not m(f):
1619 1614 continue
1620 1615 ui.write(b"%s\n" % f)
1621 1616
1622 1617
1623 1618 @command(
1624 1619 b"debug-repair-issue6528",
1625 1620 [
1626 1621 (
1627 1622 b'',
1628 1623 b'to-report',
1629 1624 b'',
1630 1625 _(b'build a report of affected revisions to this file'),
1631 1626 _(b'FILE'),
1632 1627 ),
1633 1628 (
1634 1629 b'',
1635 1630 b'from-report',
1636 1631 b'',
1637 1632 _(b'repair revisions listed in this report file'),
1638 1633 _(b'FILE'),
1639 1634 ),
1640 1635 (
1641 1636 b'',
1642 1637 b'paranoid',
1643 1638 False,
1644 1639 _(b'check that both detection methods do the same thing'),
1645 1640 ),
1646 1641 ]
1647 1642 + cmdutil.dryrunopts,
1648 1643 )
1649 1644 def debug_repair_issue6528(ui, repo, **opts):
1650 1645 """find affected revisions and repair them. See issue6528 for more details.
1651 1646
1652 1647 The `--to-report` and `--from-report` flags allow you to cache and reuse the
1653 1648 computation of affected revisions for a given repository across clones.
1654 1649 The report format is line-based (with empty lines ignored):
1655 1650
1656 1651 ```
1657 1652 <ascii-hex of the affected revision>,... <unencoded filelog index filename>
1658 1653 ```
1659 1654
1660 1655 There can be multiple broken revisions per filelog, they are separated by
1661 1656 a comma with no spaces. The only space is between the revision(s) and the
1662 1657 filename.
1663 1658
1664 1659 Note that this does *not* mean that this repairs future affected revisions,
1665 1660 that needs a separate fix at the exchange level that was introduced in
1666 1661 Mercurial 5.9.1.
1667 1662
1668 1663 There is a `--paranoid` flag to test that the fast implementation is correct
1669 1664 by checking it against the slow implementation. Since this matter is quite
1670 1665 urgent and testing every edge-case is probably quite costly, we use this
1671 1666 method to test on large repositories as a fuzzing method of sorts.
1672 1667 """
1673 1668 cmdutil.check_incompatible_arguments(
1674 1669 opts, 'to_report', ['from_report', 'dry_run']
1675 1670 )
1676 1671 dry_run = opts.get('dry_run')
1677 1672 to_report = opts.get('to_report')
1678 1673 from_report = opts.get('from_report')
1679 1674 paranoid = opts.get('paranoid')
1680 1675 # TODO maybe add filelog pattern and revision pattern parameters to help
1681 1676 # narrow down the search for users that know what they're looking for?
1682 1677
1683 1678 if requirements.REVLOGV1_REQUIREMENT not in repo.requirements:
1684 1679 msg = b"can only repair revlogv1 repositories, v2 is not affected"
1685 1680 raise error.Abort(_(msg))
1686 1681
1687 1682 rewrite.repair_issue6528(
1688 1683 ui,
1689 1684 repo,
1690 1685 dry_run=dry_run,
1691 1686 to_report=to_report,
1692 1687 from_report=from_report,
1693 1688 paranoid=paranoid,
1694 1689 )
1695 1690
1696 1691
1697 1692 @command(b'debugformat', [] + cmdutil.formatteropts)
1698 1693 def debugformat(ui, repo, **opts):
1699 1694 """display format information about the current repository
1700 1695
1701 1696 Use --verbose to get extra information about current config value and
1702 1697 Mercurial default."""
1703 1698 opts = pycompat.byteskwargs(opts)
1704 1699 maxvariantlength = max(len(fv.name) for fv in upgrade.allformatvariant)
1705 1700 maxvariantlength = max(len(b'format-variant'), maxvariantlength)
1706 1701
1707 1702 def makeformatname(name):
1708 1703 return b'%s:' + (b' ' * (maxvariantlength - len(name)))
1709 1704
1710 1705 fm = ui.formatter(b'debugformat', opts)
1711 1706 if fm.isplain():
1712 1707
1713 1708 def formatvalue(value):
1714 1709 if util.safehasattr(value, b'startswith'):
1715 1710 return value
1716 1711 if value:
1717 1712 return b'yes'
1718 1713 else:
1719 1714 return b'no'
1720 1715
1721 1716 else:
1722 1717 formatvalue = pycompat.identity
1723 1718
1724 1719 fm.plain(b'format-variant')
1725 1720 fm.plain(b' ' * (maxvariantlength - len(b'format-variant')))
1726 1721 fm.plain(b' repo')
1727 1722 if ui.verbose:
1728 1723 fm.plain(b' config default')
1729 1724 fm.plain(b'\n')
1730 1725 for fv in upgrade.allformatvariant:
1731 1726 fm.startitem()
1732 1727 repovalue = fv.fromrepo(repo)
1733 1728 configvalue = fv.fromconfig(repo)
1734 1729
1735 1730 if repovalue != configvalue:
1736 1731 namelabel = b'formatvariant.name.mismatchconfig'
1737 1732 repolabel = b'formatvariant.repo.mismatchconfig'
1738 1733 elif repovalue != fv.default:
1739 1734 namelabel = b'formatvariant.name.mismatchdefault'
1740 1735 repolabel = b'formatvariant.repo.mismatchdefault'
1741 1736 else:
1742 1737 namelabel = b'formatvariant.name.uptodate'
1743 1738 repolabel = b'formatvariant.repo.uptodate'
1744 1739
1745 1740 fm.write(b'name', makeformatname(fv.name), fv.name, label=namelabel)
1746 1741 fm.write(b'repo', b' %3s', formatvalue(repovalue), label=repolabel)
1747 1742 if fv.default != configvalue:
1748 1743 configlabel = b'formatvariant.config.special'
1749 1744 else:
1750 1745 configlabel = b'formatvariant.config.default'
1751 1746 fm.condwrite(
1752 1747 ui.verbose,
1753 1748 b'config',
1754 1749 b' %6s',
1755 1750 formatvalue(configvalue),
1756 1751 label=configlabel,
1757 1752 )
1758 1753 fm.condwrite(
1759 1754 ui.verbose,
1760 1755 b'default',
1761 1756 b' %7s',
1762 1757 formatvalue(fv.default),
1763 1758 label=b'formatvariant.default',
1764 1759 )
1765 1760 fm.plain(b'\n')
1766 1761 fm.end()
1767 1762
1768 1763
1769 1764 @command(b'debugfsinfo', [], _(b'[PATH]'), norepo=True)
1770 1765 def debugfsinfo(ui, path=b"."):
1771 1766 """show information detected about current filesystem"""
1772 1767 ui.writenoi18n(b'path: %s\n' % path)
1773 1768 ui.writenoi18n(
1774 1769 b'mounted on: %s\n' % (util.getfsmountpoint(path) or b'(unknown)')
1775 1770 )
1776 1771 ui.writenoi18n(b'exec: %s\n' % (util.checkexec(path) and b'yes' or b'no'))
1777 1772 ui.writenoi18n(b'fstype: %s\n' % (util.getfstype(path) or b'(unknown)'))
1778 1773 ui.writenoi18n(
1779 1774 b'symlink: %s\n' % (util.checklink(path) and b'yes' or b'no')
1780 1775 )
1781 1776 ui.writenoi18n(
1782 1777 b'hardlink: %s\n' % (util.checknlink(path) and b'yes' or b'no')
1783 1778 )
1784 1779 casesensitive = b'(unknown)'
1785 1780 try:
1786 1781 with pycompat.namedtempfile(prefix=b'.debugfsinfo', dir=path) as f:
1787 1782 casesensitive = util.fscasesensitive(f.name) and b'yes' or b'no'
1788 1783 except OSError:
1789 1784 pass
1790 1785 ui.writenoi18n(b'case-sensitive: %s\n' % casesensitive)
1791 1786
1792 1787
1793 1788 @command(
1794 1789 b'debuggetbundle',
1795 1790 [
1796 1791 (b'H', b'head', [], _(b'id of head node'), _(b'ID')),
1797 1792 (b'C', b'common', [], _(b'id of common node'), _(b'ID')),
1798 1793 (
1799 1794 b't',
1800 1795 b'type',
1801 1796 b'bzip2',
1802 1797 _(b'bundle compression type to use'),
1803 1798 _(b'TYPE'),
1804 1799 ),
1805 1800 ],
1806 1801 _(b'REPO FILE [-H|-C ID]...'),
1807 1802 norepo=True,
1808 1803 )
1809 1804 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1810 1805 """retrieves a bundle from a repo
1811 1806
1812 1807 Every ID must be a full-length hex node id string. Saves the bundle to the
1813 1808 given file.
1814 1809 """
1815 1810 opts = pycompat.byteskwargs(opts)
1816 1811 repo = hg.peer(ui, opts, repopath)
1817 1812 if not repo.capable(b'getbundle'):
1818 1813 raise error.Abort(b"getbundle() not supported by target repository")
1819 1814 args = {}
1820 1815 if common:
1821 1816 args['common'] = [bin(s) for s in common]
1822 1817 if head:
1823 1818 args['heads'] = [bin(s) for s in head]
1824 1819 # TODO: get desired bundlecaps from command line.
1825 1820 args['bundlecaps'] = None
1826 1821 bundle = repo.getbundle(b'debug', **args)
1827 1822
1828 1823 bundletype = opts.get(b'type', b'bzip2').lower()
1829 1824 btypes = {
1830 1825 b'none': b'HG10UN',
1831 1826 b'bzip2': b'HG10BZ',
1832 1827 b'gzip': b'HG10GZ',
1833 1828 b'bundle2': b'HG20',
1834 1829 }
1835 1830 bundletype = btypes.get(bundletype)
1836 1831 if bundletype not in bundle2.bundletypes:
1837 1832 raise error.Abort(_(b'unknown bundle type specified with --type'))
1838 1833 bundle2.writebundle(ui, bundle, bundlepath, bundletype)
1839 1834
1840 1835
1841 1836 @command(b'debugignore', [], b'[FILE]')
1842 1837 def debugignore(ui, repo, *files, **opts):
1843 1838 """display the combined ignore pattern and information about ignored files
1844 1839
1845 1840 With no argument display the combined ignore pattern.
1846 1841
1847 1842 Given space separated file names, shows if the given file is ignored and
1848 1843 if so, show the ignore rule (file and line number) that matched it.
1849 1844 """
1850 1845 ignore = repo.dirstate._ignore
1851 1846 if not files:
1852 1847 # Show all the patterns
1853 1848 ui.write(b"%s\n" % pycompat.byterepr(ignore))
1854 1849 else:
1855 1850 m = scmutil.match(repo[None], pats=files)
1856 1851 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
1857 1852 for f in m.files():
1858 1853 nf = util.normpath(f)
1859 1854 ignored = None
1860 1855 ignoredata = None
1861 1856 if nf != b'.':
1862 1857 if ignore(nf):
1863 1858 ignored = nf
1864 1859 ignoredata = repo.dirstate._ignorefileandline(nf)
1865 1860 else:
1866 1861 for p in pathutil.finddirs(nf):
1867 1862 if ignore(p):
1868 1863 ignored = p
1869 1864 ignoredata = repo.dirstate._ignorefileandline(p)
1870 1865 break
1871 1866 if ignored:
1872 1867 if ignored == nf:
1873 1868 ui.write(_(b"%s is ignored\n") % uipathfn(f))
1874 1869 else:
1875 1870 ui.write(
1876 1871 _(
1877 1872 b"%s is ignored because of "
1878 1873 b"containing directory %s\n"
1879 1874 )
1880 1875 % (uipathfn(f), ignored)
1881 1876 )
1882 1877 ignorefile, lineno, line = ignoredata
1883 1878 ui.write(
1884 1879 _(b"(ignore rule in %s, line %d: '%s')\n")
1885 1880 % (ignorefile, lineno, line)
1886 1881 )
1887 1882 else:
1888 1883 ui.write(_(b"%s is not ignored\n") % uipathfn(f))
1889 1884
1890 1885
1891 1886 @command(
1892 1887 b'debug-revlog-index|debugindex',
1893 1888 cmdutil.debugrevlogopts + cmdutil.formatteropts,
1894 1889 _(b'-c|-m|FILE'),
1895 1890 )
1896 1891 def debugindex(ui, repo, file_=None, **opts):
1897 1892 """dump index data for a revlog"""
1898 1893 opts = pycompat.byteskwargs(opts)
1899 1894 store = cmdutil.openstorage(repo, b'debugindex', file_, opts)
1900 1895
1901 1896 fm = ui.formatter(b'debugindex', opts)
1902 1897
1903 1898 revlog = getattr(store, b'_revlog', store)
1904 1899
1905 1900 return revlog_debug.debug_index(
1906 1901 ui,
1907 1902 repo,
1908 1903 formatter=fm,
1909 1904 revlog=revlog,
1910 1905 full_node=ui.debugflag,
1911 1906 )
1912 1907
1913 1908
1914 1909 @command(
1915 1910 b'debugindexdot',
1916 1911 cmdutil.debugrevlogopts,
1917 1912 _(b'-c|-m|FILE'),
1918 1913 optionalrepo=True,
1919 1914 )
1920 1915 def debugindexdot(ui, repo, file_=None, **opts):
1921 1916 """dump an index DAG as a graphviz dot file"""
1922 1917 opts = pycompat.byteskwargs(opts)
1923 1918 r = cmdutil.openstorage(repo, b'debugindexdot', file_, opts)
1924 1919 ui.writenoi18n(b"digraph G {\n")
1925 1920 for i in r:
1926 1921 node = r.node(i)
1927 1922 pp = r.parents(node)
1928 1923 ui.write(b"\t%d -> %d\n" % (r.rev(pp[0]), i))
1929 1924 if pp[1] != repo.nullid:
1930 1925 ui.write(b"\t%d -> %d\n" % (r.rev(pp[1]), i))
1931 1926 ui.write(b"}\n")
1932 1927
1933 1928
1934 1929 @command(b'debugindexstats', [])
1935 1930 def debugindexstats(ui, repo):
1936 1931 """show stats related to the changelog index"""
1937 1932 repo.changelog.shortest(repo.nullid, 1)
1938 1933 index = repo.changelog.index
1939 1934 if not util.safehasattr(index, b'stats'):
1940 1935 raise error.Abort(_(b'debugindexstats only works with native code'))
1941 1936 for k, v in sorted(index.stats().items()):
1942 1937 ui.write(b'%s: %d\n' % (k, v))
1943 1938
1944 1939
1945 1940 @command(b'debuginstall', [] + cmdutil.formatteropts, b'', norepo=True)
1946 1941 def debuginstall(ui, **opts):
1947 1942 """test Mercurial installation
1948 1943
1949 1944 Returns 0 on success.
1950 1945 """
1951 1946 opts = pycompat.byteskwargs(opts)
1952 1947
1953 1948 problems = 0
1954 1949
1955 1950 fm = ui.formatter(b'debuginstall', opts)
1956 1951 fm.startitem()
1957 1952
1958 1953 # encoding might be unknown or wrong. don't translate these messages.
1959 1954 fm.write(b'encoding', b"checking encoding (%s)...\n", encoding.encoding)
1960 1955 err = None
1961 1956 try:
1962 1957 codecs.lookup(pycompat.sysstr(encoding.encoding))
1963 1958 except LookupError as inst:
1964 1959 err = stringutil.forcebytestr(inst)
1965 1960 problems += 1
1966 1961 fm.condwrite(
1967 1962 err,
1968 1963 b'encodingerror',
1969 1964 b" %s\n (check that your locale is properly set)\n",
1970 1965 err,
1971 1966 )
1972 1967
1973 1968 # Python
1974 1969 pythonlib = None
1975 1970 if util.safehasattr(os, '__file__'):
1976 1971 pythonlib = os.path.dirname(pycompat.fsencode(os.__file__))
1977 1972 elif getattr(sys, 'oxidized', False):
1978 1973 pythonlib = pycompat.sysexecutable
1979 1974
1980 1975 fm.write(
1981 1976 b'pythonexe',
1982 1977 _(b"checking Python executable (%s)\n"),
1983 1978 pycompat.sysexecutable or _(b"unknown"),
1984 1979 )
1985 1980 fm.write(
1986 1981 b'pythonimplementation',
1987 1982 _(b"checking Python implementation (%s)\n"),
1988 1983 pycompat.sysbytes(platform.python_implementation()),
1989 1984 )
1990 1985 fm.write(
1991 1986 b'pythonver',
1992 1987 _(b"checking Python version (%s)\n"),
1993 1988 (b"%d.%d.%d" % sys.version_info[:3]),
1994 1989 )
1995 1990 fm.write(
1996 1991 b'pythonlib',
1997 1992 _(b"checking Python lib (%s)...\n"),
1998 1993 pythonlib or _(b"unknown"),
1999 1994 )
2000 1995
2001 1996 try:
2002 1997 from . import rustext # pytype: disable=import-error
2003 1998
2004 1999 rustext.__doc__ # trigger lazy import
2005 2000 except ImportError:
2006 2001 rustext = None
2007 2002
2008 2003 security = set(sslutil.supportedprotocols)
2009 2004 if sslutil.hassni:
2010 2005 security.add(b'sni')
2011 2006
2012 2007 fm.write(
2013 2008 b'pythonsecurity',
2014 2009 _(b"checking Python security support (%s)\n"),
2015 2010 fm.formatlist(sorted(security), name=b'protocol', fmt=b'%s', sep=b','),
2016 2011 )
2017 2012
2018 2013 # These are warnings, not errors. So don't increment problem count. This
2019 2014 # may change in the future.
2020 2015 if b'tls1.2' not in security:
2021 2016 fm.plain(
2022 2017 _(
2023 2018 b' TLS 1.2 not supported by Python install; '
2024 2019 b'network connections lack modern security\n'
2025 2020 )
2026 2021 )
2027 2022 if b'sni' not in security:
2028 2023 fm.plain(
2029 2024 _(
2030 2025 b' SNI not supported by Python install; may have '
2031 2026 b'connectivity issues with some servers\n'
2032 2027 )
2033 2028 )
2034 2029
2035 2030 fm.plain(
2036 2031 _(
2037 2032 b"checking Rust extensions (%s)\n"
2038 2033 % (b'missing' if rustext is None else b'installed')
2039 2034 ),
2040 2035 )
2041 2036
2042 2037 # TODO print CA cert info
2043 2038
2044 2039 # hg version
2045 2040 hgver = util.version()
2046 2041 fm.write(
2047 2042 b'hgver', _(b"checking Mercurial version (%s)\n"), hgver.split(b'+')[0]
2048 2043 )
2049 2044 fm.write(
2050 2045 b'hgverextra',
2051 2046 _(b"checking Mercurial custom build (%s)\n"),
2052 2047 b'+'.join(hgver.split(b'+')[1:]),
2053 2048 )
2054 2049
2055 2050 # compiled modules
2056 2051 hgmodules = None
2057 2052 if util.safehasattr(sys.modules[__name__], '__file__'):
2058 2053 hgmodules = os.path.dirname(pycompat.fsencode(__file__))
2059 2054 elif getattr(sys, 'oxidized', False):
2060 2055 hgmodules = pycompat.sysexecutable
2061 2056
2062 2057 fm.write(
2063 2058 b'hgmodulepolicy', _(b"checking module policy (%s)\n"), policy.policy
2064 2059 )
2065 2060 fm.write(
2066 2061 b'hgmodules',
2067 2062 _(b"checking installed modules (%s)...\n"),
2068 2063 hgmodules or _(b"unknown"),
2069 2064 )
2070 2065
2071 2066 rustandc = policy.policy in (b'rust+c', b'rust+c-allow')
2072 2067 rustext = rustandc # for now, that's the only case
2073 2068 cext = policy.policy in (b'c', b'allow') or rustandc
2074 2069 nopure = cext or rustext
2075 2070 if nopure:
2076 2071 err = None
2077 2072 try:
2078 2073 if cext:
2079 2074 from .cext import ( # pytype: disable=import-error
2080 2075 base85,
2081 2076 bdiff,
2082 2077 mpatch,
2083 2078 osutil,
2084 2079 )
2085 2080
2086 2081 # quiet pyflakes
2087 2082 dir(bdiff), dir(mpatch), dir(base85), dir(osutil)
2088 2083 if rustext:
2089 2084 from .rustext import ( # pytype: disable=import-error
2090 2085 ancestor,
2091 2086 dirstate,
2092 2087 )
2093 2088
2094 2089 dir(ancestor), dir(dirstate) # quiet pyflakes
2095 2090 except Exception as inst:
2096 2091 err = stringutil.forcebytestr(inst)
2097 2092 problems += 1
2098 2093 fm.condwrite(err, b'extensionserror', b" %s\n", err)
2099 2094
2100 2095 compengines = util.compengines._engines.values()
2101 2096 fm.write(
2102 2097 b'compengines',
2103 2098 _(b'checking registered compression engines (%s)\n'),
2104 2099 fm.formatlist(
2105 2100 sorted(e.name() for e in compengines),
2106 2101 name=b'compengine',
2107 2102 fmt=b'%s',
2108 2103 sep=b', ',
2109 2104 ),
2110 2105 )
2111 2106 fm.write(
2112 2107 b'compenginesavail',
2113 2108 _(b'checking available compression engines (%s)\n'),
2114 2109 fm.formatlist(
2115 2110 sorted(e.name() for e in compengines if e.available()),
2116 2111 name=b'compengine',
2117 2112 fmt=b'%s',
2118 2113 sep=b', ',
2119 2114 ),
2120 2115 )
2121 2116 wirecompengines = compression.compengines.supportedwireengines(
2122 2117 compression.SERVERROLE
2123 2118 )
2124 2119 fm.write(
2125 2120 b'compenginesserver',
2126 2121 _(
2127 2122 b'checking available compression engines '
2128 2123 b'for wire protocol (%s)\n'
2129 2124 ),
2130 2125 fm.formatlist(
2131 2126 [e.name() for e in wirecompengines if e.wireprotosupport()],
2132 2127 name=b'compengine',
2133 2128 fmt=b'%s',
2134 2129 sep=b', ',
2135 2130 ),
2136 2131 )
2137 2132 re2 = b'missing'
2138 2133 if util._re2:
2139 2134 re2 = b'available'
2140 2135 fm.plain(_(b'checking "re2" regexp engine (%s)\n') % re2)
2141 2136 fm.data(re2=bool(util._re2))
2142 2137
2143 2138 # templates
2144 2139 p = templater.templatedir()
2145 2140 fm.write(b'templatedirs', b'checking templates (%s)...\n', p or b'')
2146 2141 fm.condwrite(not p, b'', _(b" no template directories found\n"))
2147 2142 if p:
2148 2143 (m, fp) = templater.try_open_template(b"map-cmdline.default")
2149 2144 if m:
2150 2145 # template found, check if it is working
2151 2146 err = None
2152 2147 try:
2153 2148 templater.templater.frommapfile(m)
2154 2149 except Exception as inst:
2155 2150 err = stringutil.forcebytestr(inst)
2156 2151 p = None
2157 2152 fm.condwrite(err, b'defaulttemplateerror', b" %s\n", err)
2158 2153 else:
2159 2154 p = None
2160 2155 fm.condwrite(
2161 2156 p, b'defaulttemplate', _(b"checking default template (%s)\n"), m
2162 2157 )
2163 2158 fm.condwrite(
2164 2159 not m,
2165 2160 b'defaulttemplatenotfound',
2166 2161 _(b" template '%s' not found\n"),
2167 2162 b"default",
2168 2163 )
2169 2164 if not p:
2170 2165 problems += 1
2171 2166 fm.condwrite(
2172 2167 not p, b'', _(b" (templates seem to have been installed incorrectly)\n")
2173 2168 )
2174 2169
2175 2170 # editor
2176 2171 editor = ui.geteditor()
2177 2172 editor = util.expandpath(editor)
2178 2173 editorbin = procutil.shellsplit(editor)[0]
2179 2174 fm.write(b'editor', _(b"checking commit editor... (%s)\n"), editorbin)
2180 2175 cmdpath = procutil.findexe(editorbin)
2181 2176 fm.condwrite(
2182 2177 not cmdpath and editor == b'vi',
2183 2178 b'vinotfound',
2184 2179 _(
2185 2180 b" No commit editor set and can't find %s in PATH\n"
2186 2181 b" (specify a commit editor in your configuration"
2187 2182 b" file)\n"
2188 2183 ),
2189 2184 not cmdpath and editor == b'vi' and editorbin,
2190 2185 )
2191 2186 fm.condwrite(
2192 2187 not cmdpath and editor != b'vi',
2193 2188 b'editornotfound',
2194 2189 _(
2195 2190 b" Can't find editor '%s' in PATH\n"
2196 2191 b" (specify a commit editor in your configuration"
2197 2192 b" file)\n"
2198 2193 ),
2199 2194 not cmdpath and editorbin,
2200 2195 )
2201 2196 if not cmdpath and editor != b'vi':
2202 2197 problems += 1
2203 2198
2204 2199 # check username
2205 2200 username = None
2206 2201 err = None
2207 2202 try:
2208 2203 username = ui.username()
2209 2204 except error.Abort as e:
2210 2205 err = e.message
2211 2206 problems += 1
2212 2207
2213 2208 fm.condwrite(
2214 2209 username, b'username', _(b"checking username (%s)\n"), username
2215 2210 )
2216 2211 fm.condwrite(
2217 2212 err,
2218 2213 b'usernameerror',
2219 2214 _(
2220 2215 b"checking username...\n %s\n"
2221 2216 b" (specify a username in your configuration file)\n"
2222 2217 ),
2223 2218 err,
2224 2219 )
2225 2220
2226 2221 for name, mod in extensions.extensions():
2227 2222 handler = getattr(mod, 'debuginstall', None)
2228 2223 if handler is not None:
2229 2224 problems += handler(ui, fm)
2230 2225
2231 2226 fm.condwrite(not problems, b'', _(b"no problems detected\n"))
2232 2227 if not problems:
2233 2228 fm.data(problems=problems)
2234 2229 fm.condwrite(
2235 2230 problems,
2236 2231 b'problems',
2237 2232 _(b"%d problems detected, please check your install!\n"),
2238 2233 problems,
2239 2234 )
2240 2235 fm.end()
2241 2236
2242 2237 return problems
2243 2238
2244 2239
2245 2240 @command(b'debugknown', [], _(b'REPO ID...'), norepo=True)
2246 2241 def debugknown(ui, repopath, *ids, **opts):
2247 2242 """test whether node ids are known to a repo
2248 2243
2249 2244 Every ID must be a full-length hex node id string. Returns a list of 0s
2250 2245 and 1s indicating unknown/known.
2251 2246 """
2252 2247 opts = pycompat.byteskwargs(opts)
2253 2248 repo = hg.peer(ui, opts, repopath)
2254 2249 if not repo.capable(b'known'):
2255 2250 raise error.Abort(b"known() not supported by target repository")
2256 2251 flags = repo.known([bin(s) for s in ids])
2257 2252 ui.write(b"%s\n" % (b"".join([f and b"1" or b"0" for f in flags])))
2258 2253
2259 2254
2260 2255 @command(b'debuglabelcomplete', [], _(b'LABEL...'))
2261 2256 def debuglabelcomplete(ui, repo, *args):
2262 2257 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
2263 2258 debugnamecomplete(ui, repo, *args)
2264 2259
2265 2260
2266 2261 @command(
2267 2262 b'debuglocks',
2268 2263 [
2269 2264 (b'L', b'force-free-lock', None, _(b'free the store lock (DANGEROUS)')),
2270 2265 (
2271 2266 b'W',
2272 2267 b'force-free-wlock',
2273 2268 None,
2274 2269 _(b'free the working state lock (DANGEROUS)'),
2275 2270 ),
2276 2271 (b's', b'set-lock', None, _(b'set the store lock until stopped')),
2277 2272 (
2278 2273 b'S',
2279 2274 b'set-wlock',
2280 2275 None,
2281 2276 _(b'set the working state lock until stopped'),
2282 2277 ),
2283 2278 ],
2284 2279 _(b'[OPTION]...'),
2285 2280 )
2286 2281 def debuglocks(ui, repo, **opts):
2287 2282 """show or modify state of locks
2288 2283
2289 2284 By default, this command will show which locks are held. This
2290 2285 includes the user and process holding the lock, the amount of time
2291 2286 the lock has been held, and the machine name where the process is
2292 2287 running if it's not local.
2293 2288
2294 2289 Locks protect the integrity of Mercurial's data, so should be
2295 2290 treated with care. System crashes or other interruptions may cause
2296 2291 locks to not be properly released, though Mercurial will usually
2297 2292 detect and remove such stale locks automatically.
2298 2293
2299 2294 However, detecting stale locks may not always be possible (for
2300 2295 instance, on a shared filesystem). Removing locks may also be
2301 2296 blocked by filesystem permissions.
2302 2297
2303 2298 Setting a lock will prevent other commands from changing the data.
2304 2299 The command will wait until an interruption (SIGINT, SIGTERM, ...) occurs.
2305 2300 The set locks are removed when the command exits.
2306 2301
2307 2302 Returns 0 if no locks are held.
2308 2303
2309 2304 """
2310 2305
2311 2306 if opts.get('force_free_lock'):
2312 2307 repo.svfs.tryunlink(b'lock')
2313 2308 if opts.get('force_free_wlock'):
2314 2309 repo.vfs.tryunlink(b'wlock')
2315 2310 if opts.get('force_free_lock') or opts.get('force_free_wlock'):
2316 2311 return 0
2317 2312
2318 2313 locks = []
2319 2314 try:
2320 2315 if opts.get('set_wlock'):
2321 2316 try:
2322 2317 locks.append(repo.wlock(False))
2323 2318 except error.LockHeld:
2324 2319 raise error.Abort(_(b'wlock is already held'))
2325 2320 if opts.get('set_lock'):
2326 2321 try:
2327 2322 locks.append(repo.lock(False))
2328 2323 except error.LockHeld:
2329 2324 raise error.Abort(_(b'lock is already held'))
2330 2325 if len(locks):
2331 2326 try:
2332 2327 if ui.interactive():
2333 2328 prompt = _(b"ready to release the lock (y)? $$ &Yes")
2334 2329 ui.promptchoice(prompt)
2335 2330 else:
2336 2331 msg = b"%d locks held, waiting for signal\n"
2337 2332 msg %= len(locks)
2338 2333 ui.status(msg)
2339 2334 while True: # XXX wait for a signal
2340 2335 time.sleep(0.1)
2341 2336 except KeyboardInterrupt:
2342 2337 msg = b"signal-received releasing locks\n"
2343 2338 ui.status(msg)
2344 2339 return 0
2345 2340 finally:
2346 2341 release(*locks)
2347 2342
2348 2343 now = time.time()
2349 2344 held = 0
2350 2345
2351 2346 def report(vfs, name, method):
2352 2347 # this causes stale locks to get reaped for more accurate reporting
2353 2348 try:
2354 2349 l = method(False)
2355 2350 except error.LockHeld:
2356 2351 l = None
2357 2352
2358 2353 if l:
2359 2354 l.release()
2360 2355 else:
2361 2356 try:
2362 2357 st = vfs.lstat(name)
2363 2358 age = now - st[stat.ST_MTIME]
2364 2359 user = util.username(st.st_uid)
2365 2360 locker = vfs.readlock(name)
2366 2361 if b":" in locker:
2367 2362 host, pid = locker.split(b':')
2368 2363 if host == socket.gethostname():
2369 2364 locker = b'user %s, process %s' % (user or b'None', pid)
2370 2365 else:
2371 2366 locker = b'user %s, process %s, host %s' % (
2372 2367 user or b'None',
2373 2368 pid,
2374 2369 host,
2375 2370 )
2376 2371 ui.writenoi18n(b"%-6s %s (%ds)\n" % (name + b":", locker, age))
2377 2372 return 1
2378 2373 except FileNotFoundError:
2379 2374 pass
2380 2375
2381 2376 ui.writenoi18n(b"%-6s free\n" % (name + b":"))
2382 2377 return 0
2383 2378
2384 2379 held += report(repo.svfs, b"lock", repo.lock)
2385 2380 held += report(repo.vfs, b"wlock", repo.wlock)
2386 2381
2387 2382 return held
2388 2383
2389 2384
2390 2385 @command(
2391 2386 b'debugmanifestfulltextcache',
2392 2387 [
2393 2388 (b'', b'clear', False, _(b'clear the cache')),
2394 2389 (
2395 2390 b'a',
2396 2391 b'add',
2397 2392 [],
2398 2393 _(b'add the given manifest nodes to the cache'),
2399 2394 _(b'NODE'),
2400 2395 ),
2401 2396 ],
2402 2397 b'',
2403 2398 )
2404 2399 def debugmanifestfulltextcache(ui, repo, add=(), **opts):
2405 2400 """show, clear or amend the contents of the manifest fulltext cache"""
2406 2401
2407 2402 def getcache():
2408 2403 r = repo.manifestlog.getstorage(b'')
2409 2404 try:
2410 2405 return r._fulltextcache
2411 2406 except AttributeError:
2412 2407 msg = _(
2413 2408 b"Current revlog implementation doesn't appear to have a "
2414 2409 b"manifest fulltext cache\n"
2415 2410 )
2416 2411 raise error.Abort(msg)
2417 2412
2418 2413 if opts.get('clear'):
2419 2414 with repo.wlock():
2420 2415 cache = getcache()
2421 2416 cache.clear(clear_persisted_data=True)
2422 2417 return
2423 2418
2424 2419 if add:
2425 2420 with repo.wlock():
2426 2421 m = repo.manifestlog
2427 2422 store = m.getstorage(b'')
2428 2423 for n in add:
2429 2424 try:
2430 2425 manifest = m[store.lookup(n)]
2431 2426 except error.LookupError as e:
2432 2427 raise error.Abort(
2433 2428 bytes(e), hint=b"Check your manifest node id"
2434 2429 )
2435 2430 manifest.read() # stores revisision in cache too
2436 2431 return
2437 2432
2438 2433 cache = getcache()
2439 2434 if not len(cache):
2440 2435 ui.write(_(b'cache empty\n'))
2441 2436 else:
2442 2437 ui.write(
2443 2438 _(
2444 2439 b'cache contains %d manifest entries, in order of most to '
2445 2440 b'least recent:\n'
2446 2441 )
2447 2442 % (len(cache),)
2448 2443 )
2449 2444 totalsize = 0
2450 2445 for nodeid in cache:
2451 2446 # Use cache.get to not update the LRU order
2452 2447 data = cache.peek(nodeid)
2453 2448 size = len(data)
2454 2449 totalsize += size + 24 # 20 bytes nodeid, 4 bytes size
2455 2450 ui.write(
2456 2451 _(b'id: %s, size %s\n') % (hex(nodeid), util.bytecount(size))
2457 2452 )
2458 2453 ondisk = cache._opener.stat(b'manifestfulltextcache').st_size
2459 2454 ui.write(
2460 2455 _(b'total cache data size %s, on-disk %s\n')
2461 2456 % (util.bytecount(totalsize), util.bytecount(ondisk))
2462 2457 )
2463 2458
2464 2459
2465 2460 @command(b'debugmergestate', [] + cmdutil.templateopts, b'')
2466 2461 def debugmergestate(ui, repo, *args, **opts):
2467 2462 """print merge state
2468 2463
2469 2464 Use --verbose to print out information about whether v1 or v2 merge state
2470 2465 was chosen."""
2471 2466
2472 2467 if ui.verbose:
2473 2468 ms = mergestatemod.mergestate(repo)
2474 2469
2475 2470 # sort so that reasonable information is on top
2476 2471 v1records = ms._readrecordsv1()
2477 2472 v2records = ms._readrecordsv2()
2478 2473
2479 2474 if not v1records and not v2records:
2480 2475 pass
2481 2476 elif not v2records:
2482 2477 ui.writenoi18n(b'no version 2 merge state\n')
2483 2478 elif ms._v1v2match(v1records, v2records):
2484 2479 ui.writenoi18n(b'v1 and v2 states match: using v2\n')
2485 2480 else:
2486 2481 ui.writenoi18n(b'v1 and v2 states mismatch: using v1\n')
2487 2482
2488 2483 opts = pycompat.byteskwargs(opts)
2489 2484 if not opts[b'template']:
2490 2485 opts[b'template'] = (
2491 2486 b'{if(commits, "", "no merge state found\n")}'
2492 2487 b'{commits % "{name}{if(label, " ({label})")}: {node}\n"}'
2493 2488 b'{files % "file: {path} (state \\"{state}\\")\n'
2494 2489 b'{if(local_path, "'
2495 2490 b' local path: {local_path} (hash {local_key}, flags \\"{local_flags}\\")\n'
2496 2491 b' ancestor path: {ancestor_path} (node {ancestor_node})\n'
2497 2492 b' other path: {other_path} (node {other_node})\n'
2498 2493 b'")}'
2499 2494 b'{if(rename_side, "'
2500 2495 b' rename side: {rename_side}\n'
2501 2496 b' renamed path: {renamed_path}\n'
2502 2497 b'")}'
2503 2498 b'{extras % " extra: {key} = {value}\n"}'
2504 2499 b'"}'
2505 2500 b'{extras % "extra: {file} ({key} = {value})\n"}'
2506 2501 )
2507 2502
2508 2503 ms = mergestatemod.mergestate.read(repo)
2509 2504
2510 2505 fm = ui.formatter(b'debugmergestate', opts)
2511 2506 fm.startitem()
2512 2507
2513 2508 fm_commits = fm.nested(b'commits')
2514 2509 if ms.active():
2515 2510 for name, node, label_index in (
2516 2511 (b'local', ms.local, 0),
2517 2512 (b'other', ms.other, 1),
2518 2513 ):
2519 2514 fm_commits.startitem()
2520 2515 fm_commits.data(name=name)
2521 2516 fm_commits.data(node=hex(node))
2522 2517 if ms._labels and len(ms._labels) > label_index:
2523 2518 fm_commits.data(label=ms._labels[label_index])
2524 2519 fm_commits.end()
2525 2520
2526 2521 fm_files = fm.nested(b'files')
2527 2522 if ms.active():
2528 2523 for f in ms:
2529 2524 fm_files.startitem()
2530 2525 fm_files.data(path=f)
2531 2526 state = ms._state[f]
2532 2527 fm_files.data(state=state[0])
2533 2528 if state[0] in (
2534 2529 mergestatemod.MERGE_RECORD_UNRESOLVED,
2535 2530 mergestatemod.MERGE_RECORD_RESOLVED,
2536 2531 ):
2537 2532 fm_files.data(local_key=state[1])
2538 2533 fm_files.data(local_path=state[2])
2539 2534 fm_files.data(ancestor_path=state[3])
2540 2535 fm_files.data(ancestor_node=state[4])
2541 2536 fm_files.data(other_path=state[5])
2542 2537 fm_files.data(other_node=state[6])
2543 2538 fm_files.data(local_flags=state[7])
2544 2539 elif state[0] in (
2545 2540 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH,
2546 2541 mergestatemod.MERGE_RECORD_RESOLVED_PATH,
2547 2542 ):
2548 2543 fm_files.data(renamed_path=state[1])
2549 2544 fm_files.data(rename_side=state[2])
2550 2545 fm_extras = fm_files.nested(b'extras')
2551 2546 for k, v in sorted(ms.extras(f).items()):
2552 2547 fm_extras.startitem()
2553 2548 fm_extras.data(key=k)
2554 2549 fm_extras.data(value=v)
2555 2550 fm_extras.end()
2556 2551
2557 2552 fm_files.end()
2558 2553
2559 2554 fm_extras = fm.nested(b'extras')
2560 2555 for f, d in sorted(ms.allextras().items()):
2561 2556 if f in ms:
2562 2557 # If file is in mergestate, we have already processed it's extras
2563 2558 continue
2564 2559 for k, v in d.items():
2565 2560 fm_extras.startitem()
2566 2561 fm_extras.data(file=f)
2567 2562 fm_extras.data(key=k)
2568 2563 fm_extras.data(value=v)
2569 2564 fm_extras.end()
2570 2565
2571 2566 fm.end()
2572 2567
2573 2568
2574 2569 @command(b'debugnamecomplete', [], _(b'NAME...'))
2575 2570 def debugnamecomplete(ui, repo, *args):
2576 2571 '''complete "names" - tags, open branch names, bookmark names'''
2577 2572
2578 2573 names = set()
2579 2574 # since we previously only listed open branches, we will handle that
2580 2575 # specially (after this for loop)
2581 2576 for name, ns in repo.names.items():
2582 2577 if name != b'branches':
2583 2578 names.update(ns.listnames(repo))
2584 2579 names.update(
2585 2580 tag
2586 2581 for (tag, heads, tip, closed) in repo.branchmap().iterbranches()
2587 2582 if not closed
2588 2583 )
2589 2584 completions = set()
2590 2585 if not args:
2591 2586 args = [b'']
2592 2587 for a in args:
2593 2588 completions.update(n for n in names if n.startswith(a))
2594 2589 ui.write(b'\n'.join(sorted(completions)))
2595 2590 ui.write(b'\n')
2596 2591
2597 2592
2598 2593 @command(
2599 2594 b'debugnodemap',
2600 2595 [
2601 2596 (
2602 2597 b'',
2603 2598 b'dump-new',
2604 2599 False,
2605 2600 _(b'write a (new) persistent binary nodemap on stdout'),
2606 2601 ),
2607 2602 (b'', b'dump-disk', False, _(b'dump on-disk data on stdout')),
2608 2603 (
2609 2604 b'',
2610 2605 b'check',
2611 2606 False,
2612 2607 _(b'check that the data on disk data are correct.'),
2613 2608 ),
2614 2609 (
2615 2610 b'',
2616 2611 b'metadata',
2617 2612 False,
2618 2613 _(b'display the on disk meta data for the nodemap'),
2619 2614 ),
2620 2615 ],
2621 2616 )
2622 2617 def debugnodemap(ui, repo, **opts):
2623 2618 """write and inspect on disk nodemap"""
2624 2619 if opts['dump_new']:
2625 2620 unfi = repo.unfiltered()
2626 2621 cl = unfi.changelog
2627 2622 if util.safehasattr(cl.index, "nodemap_data_all"):
2628 2623 data = cl.index.nodemap_data_all()
2629 2624 else:
2630 2625 data = nodemap.persistent_data(cl.index)
2631 2626 ui.write(data)
2632 2627 elif opts['dump_disk']:
2633 2628 unfi = repo.unfiltered()
2634 2629 cl = unfi.changelog
2635 2630 nm_data = nodemap.persisted_data(cl)
2636 2631 if nm_data is not None:
2637 2632 docket, data = nm_data
2638 2633 ui.write(data[:])
2639 2634 elif opts['check']:
2640 2635 unfi = repo.unfiltered()
2641 2636 cl = unfi.changelog
2642 2637 nm_data = nodemap.persisted_data(cl)
2643 2638 if nm_data is not None:
2644 2639 docket, data = nm_data
2645 2640 return nodemap.check_data(ui, cl.index, data)
2646 2641 elif opts['metadata']:
2647 2642 unfi = repo.unfiltered()
2648 2643 cl = unfi.changelog
2649 2644 nm_data = nodemap.persisted_data(cl)
2650 2645 if nm_data is not None:
2651 2646 docket, data = nm_data
2652 2647 ui.write((b"uid: %s\n") % docket.uid)
2653 2648 ui.write((b"tip-rev: %d\n") % docket.tip_rev)
2654 2649 ui.write((b"tip-node: %s\n") % hex(docket.tip_node))
2655 2650 ui.write((b"data-length: %d\n") % docket.data_length)
2656 2651 ui.write((b"data-unused: %d\n") % docket.data_unused)
2657 2652 unused_perc = docket.data_unused * 100.0 / docket.data_length
2658 2653 ui.write((b"data-unused: %2.3f%%\n") % unused_perc)
2659 2654
2660 2655
2661 2656 @command(
2662 2657 b'debugobsolete',
2663 2658 [
2664 2659 (b'', b'flags', 0, _(b'markers flag')),
2665 2660 (
2666 2661 b'',
2667 2662 b'record-parents',
2668 2663 False,
2669 2664 _(b'record parent information for the precursor'),
2670 2665 ),
2671 2666 (b'r', b'rev', [], _(b'display markers relevant to REV')),
2672 2667 (
2673 2668 b'',
2674 2669 b'exclusive',
2675 2670 False,
2676 2671 _(b'restrict display to markers only relevant to REV'),
2677 2672 ),
2678 2673 (b'', b'index', False, _(b'display index of the marker')),
2679 2674 (b'', b'delete', [], _(b'delete markers specified by indices')),
2680 2675 ]
2681 2676 + cmdutil.commitopts2
2682 2677 + cmdutil.formatteropts,
2683 2678 _(b'[OBSOLETED [REPLACEMENT ...]]'),
2684 2679 )
2685 2680 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2686 2681 """create arbitrary obsolete marker
2687 2682
2688 2683 With no arguments, displays the list of obsolescence markers."""
2689 2684
2690 2685 opts = pycompat.byteskwargs(opts)
2691 2686
2692 2687 def parsenodeid(s):
2693 2688 try:
2694 2689 # We do not use revsingle/revrange functions here to accept
2695 2690 # arbitrary node identifiers, possibly not present in the
2696 2691 # local repository.
2697 2692 n = bin(s)
2698 2693 if len(n) != repo.nodeconstants.nodelen:
2699 2694 raise ValueError
2700 2695 return n
2701 2696 except ValueError:
2702 2697 raise error.InputError(
2703 2698 b'changeset references must be full hexadecimal '
2704 2699 b'node identifiers'
2705 2700 )
2706 2701
2707 2702 if opts.get(b'delete'):
2708 2703 indices = []
2709 2704 for v in opts.get(b'delete'):
2710 2705 try:
2711 2706 indices.append(int(v))
2712 2707 except ValueError:
2713 2708 raise error.InputError(
2714 2709 _(b'invalid index value: %r') % v,
2715 2710 hint=_(b'use integers for indices'),
2716 2711 )
2717 2712
2718 2713 if repo.currenttransaction():
2719 2714 raise error.Abort(
2720 2715 _(b'cannot delete obsmarkers in the middle of transaction.')
2721 2716 )
2722 2717
2723 2718 with repo.lock():
2724 2719 n = repair.deleteobsmarkers(repo.obsstore, indices)
2725 2720 ui.write(_(b'deleted %i obsolescence markers\n') % n)
2726 2721
2727 2722 return
2728 2723
2729 2724 if precursor is not None:
2730 2725 if opts[b'rev']:
2731 2726 raise error.InputError(
2732 2727 b'cannot select revision when creating marker'
2733 2728 )
2734 2729 metadata = {}
2735 2730 metadata[b'user'] = encoding.fromlocal(opts[b'user'] or ui.username())
2736 2731 succs = tuple(parsenodeid(succ) for succ in successors)
2737 2732 l = repo.lock()
2738 2733 try:
2739 2734 tr = repo.transaction(b'debugobsolete')
2740 2735 try:
2741 2736 date = opts.get(b'date')
2742 2737 if date:
2743 2738 date = dateutil.parsedate(date)
2744 2739 else:
2745 2740 date = None
2746 2741 prec = parsenodeid(precursor)
2747 2742 parents = None
2748 2743 if opts[b'record_parents']:
2749 2744 if prec not in repo.unfiltered():
2750 2745 raise error.Abort(
2751 2746 b'cannot used --record-parents on '
2752 2747 b'unknown changesets'
2753 2748 )
2754 2749 parents = repo.unfiltered()[prec].parents()
2755 2750 parents = tuple(p.node() for p in parents)
2756 2751 repo.obsstore.create(
2757 2752 tr,
2758 2753 prec,
2759 2754 succs,
2760 2755 opts[b'flags'],
2761 2756 parents=parents,
2762 2757 date=date,
2763 2758 metadata=metadata,
2764 2759 ui=ui,
2765 2760 )
2766 2761 tr.close()
2767 2762 except ValueError as exc:
2768 2763 raise error.Abort(
2769 2764 _(b'bad obsmarker input: %s') % stringutil.forcebytestr(exc)
2770 2765 )
2771 2766 finally:
2772 2767 tr.release()
2773 2768 finally:
2774 2769 l.release()
2775 2770 else:
2776 2771 if opts[b'rev']:
2777 2772 revs = logcmdutil.revrange(repo, opts[b'rev'])
2778 2773 nodes = [repo[r].node() for r in revs]
2779 2774 markers = list(
2780 2775 obsutil.getmarkers(
2781 2776 repo, nodes=nodes, exclusive=opts[b'exclusive']
2782 2777 )
2783 2778 )
2784 2779 markers.sort(key=lambda x: x._data)
2785 2780 else:
2786 2781 markers = obsutil.getmarkers(repo)
2787 2782
2788 2783 markerstoiter = markers
2789 2784 isrelevant = lambda m: True
2790 2785 if opts.get(b'rev') and opts.get(b'index'):
2791 2786 markerstoiter = obsutil.getmarkers(repo)
2792 2787 markerset = set(markers)
2793 2788 isrelevant = lambda m: m in markerset
2794 2789
2795 2790 fm = ui.formatter(b'debugobsolete', opts)
2796 2791 for i, m in enumerate(markerstoiter):
2797 2792 if not isrelevant(m):
2798 2793 # marker can be irrelevant when we're iterating over a set
2799 2794 # of markers (markerstoiter) which is bigger than the set
2800 2795 # of markers we want to display (markers)
2801 2796 # this can happen if both --index and --rev options are
2802 2797 # provided and thus we need to iterate over all of the markers
2803 2798 # to get the correct indices, but only display the ones that
2804 2799 # are relevant to --rev value
2805 2800 continue
2806 2801 fm.startitem()
2807 2802 ind = i if opts.get(b'index') else None
2808 2803 cmdutil.showmarker(fm, m, index=ind)
2809 2804 fm.end()
2810 2805
2811 2806
2812 2807 @command(
2813 2808 b'debugp1copies',
2814 2809 [(b'r', b'rev', b'', _(b'revision to debug'), _(b'REV'))],
2815 2810 _(b'[-r REV]'),
2816 2811 )
2817 2812 def debugp1copies(ui, repo, **opts):
2818 2813 """dump copy information compared to p1"""
2819 2814
2820 2815 opts = pycompat.byteskwargs(opts)
2821 2816 ctx = scmutil.revsingle(repo, opts.get(b'rev'), default=None)
2822 2817 for dst, src in ctx.p1copies().items():
2823 2818 ui.write(b'%s -> %s\n' % (src, dst))
2824 2819
2825 2820
2826 2821 @command(
2827 2822 b'debugp2copies',
2828 2823 [(b'r', b'rev', b'', _(b'revision to debug'), _(b'REV'))],
2829 2824 _(b'[-r REV]'),
2830 2825 )
2831 2826 def debugp2copies(ui, repo, **opts):
2832 2827 """dump copy information compared to p2"""
2833 2828
2834 2829 opts = pycompat.byteskwargs(opts)
2835 2830 ctx = scmutil.revsingle(repo, opts.get(b'rev'), default=None)
2836 2831 for dst, src in ctx.p2copies().items():
2837 2832 ui.write(b'%s -> %s\n' % (src, dst))
2838 2833
2839 2834
2840 2835 @command(
2841 2836 b'debugpathcomplete',
2842 2837 [
2843 2838 (b'f', b'full', None, _(b'complete an entire path')),
2844 2839 (b'n', b'normal', None, _(b'show only normal files')),
2845 2840 (b'a', b'added', None, _(b'show only added files')),
2846 2841 (b'r', b'removed', None, _(b'show only removed files')),
2847 2842 ],
2848 2843 _(b'FILESPEC...'),
2849 2844 )
2850 2845 def debugpathcomplete(ui, repo, *specs, **opts):
2851 2846 """complete part or all of a tracked path
2852 2847
2853 2848 This command supports shells that offer path name completion. It
2854 2849 currently completes only files already known to the dirstate.
2855 2850
2856 2851 Completion extends only to the next path segment unless
2857 2852 --full is specified, in which case entire paths are used."""
2858 2853
2859 2854 def complete(path, acceptable):
2860 2855 dirstate = repo.dirstate
2861 2856 spec = os.path.normpath(os.path.join(encoding.getcwd(), path))
2862 2857 rootdir = repo.root + pycompat.ossep
2863 2858 if spec != repo.root and not spec.startswith(rootdir):
2864 2859 return [], []
2865 2860 if os.path.isdir(spec):
2866 2861 spec += b'/'
2867 2862 spec = spec[len(rootdir) :]
2868 2863 fixpaths = pycompat.ossep != b'/'
2869 2864 if fixpaths:
2870 2865 spec = spec.replace(pycompat.ossep, b'/')
2871 2866 speclen = len(spec)
2872 2867 fullpaths = opts['full']
2873 2868 files, dirs = set(), set()
2874 2869 adddir, addfile = dirs.add, files.add
2875 2870 for f, st in dirstate.items():
2876 2871 if f.startswith(spec) and st.state in acceptable:
2877 2872 if fixpaths:
2878 2873 f = f.replace(b'/', pycompat.ossep)
2879 2874 if fullpaths:
2880 2875 addfile(f)
2881 2876 continue
2882 2877 s = f.find(pycompat.ossep, speclen)
2883 2878 if s >= 0:
2884 2879 adddir(f[:s])
2885 2880 else:
2886 2881 addfile(f)
2887 2882 return files, dirs
2888 2883
2889 2884 acceptable = b''
2890 2885 if opts['normal']:
2891 2886 acceptable += b'nm'
2892 2887 if opts['added']:
2893 2888 acceptable += b'a'
2894 2889 if opts['removed']:
2895 2890 acceptable += b'r'
2896 2891 cwd = repo.getcwd()
2897 2892 if not specs:
2898 2893 specs = [b'.']
2899 2894
2900 2895 files, dirs = set(), set()
2901 2896 for spec in specs:
2902 2897 f, d = complete(spec, acceptable or b'nmar')
2903 2898 files.update(f)
2904 2899 dirs.update(d)
2905 2900 files.update(dirs)
2906 2901 ui.write(b'\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
2907 2902 ui.write(b'\n')
2908 2903
2909 2904
2910 2905 @command(
2911 2906 b'debugpathcopies',
2912 2907 cmdutil.walkopts,
2913 2908 b'hg debugpathcopies REV1 REV2 [FILE]',
2914 2909 inferrepo=True,
2915 2910 )
2916 2911 def debugpathcopies(ui, repo, rev1, rev2, *pats, **opts):
2917 2912 """show copies between two revisions"""
2918 2913 ctx1 = scmutil.revsingle(repo, rev1)
2919 2914 ctx2 = scmutil.revsingle(repo, rev2)
2920 2915 m = scmutil.match(ctx1, pats, opts)
2921 2916 for dst, src in sorted(copies.pathcopies(ctx1, ctx2, m).items()):
2922 2917 ui.write(b'%s -> %s\n' % (src, dst))
2923 2918
2924 2919
2925 2920 @command(b'debugpeer', [], _(b'PATH'), norepo=True)
2926 2921 def debugpeer(ui, path):
2927 2922 """establish a connection to a peer repository"""
2928 2923 # Always enable peer request logging. Requires --debug to display
2929 2924 # though.
2930 2925 overrides = {
2931 2926 (b'devel', b'debug.peer-request'): True,
2932 2927 }
2933 2928
2934 2929 with ui.configoverride(overrides):
2935 2930 peer = hg.peer(ui, {}, path)
2936 2931
2937 2932 try:
2938 2933 local = peer.local() is not None
2939 2934 canpush = peer.canpush()
2940 2935
2941 2936 ui.write(_(b'url: %s\n') % peer.url())
2942 2937 ui.write(_(b'local: %s\n') % (_(b'yes') if local else _(b'no')))
2943 2938 ui.write(
2944 2939 _(b'pushable: %s\n') % (_(b'yes') if canpush else _(b'no'))
2945 2940 )
2946 2941 finally:
2947 2942 peer.close()
2948 2943
2949 2944
2950 2945 @command(
2951 2946 b'debugpickmergetool',
2952 2947 [
2953 2948 (b'r', b'rev', b'', _(b'check for files in this revision'), _(b'REV')),
2954 2949 (b'', b'changedelete', None, _(b'emulate merging change and delete')),
2955 2950 ]
2956 2951 + cmdutil.walkopts
2957 2952 + cmdutil.mergetoolopts,
2958 2953 _(b'[PATTERN]...'),
2959 2954 inferrepo=True,
2960 2955 )
2961 2956 def debugpickmergetool(ui, repo, *pats, **opts):
2962 2957 """examine which merge tool is chosen for specified file
2963 2958
2964 2959 As described in :hg:`help merge-tools`, Mercurial examines
2965 2960 configurations below in this order to decide which merge tool is
2966 2961 chosen for specified file.
2967 2962
2968 2963 1. ``--tool`` option
2969 2964 2. ``HGMERGE`` environment variable
2970 2965 3. configurations in ``merge-patterns`` section
2971 2966 4. configuration of ``ui.merge``
2972 2967 5. configurations in ``merge-tools`` section
2973 2968 6. ``hgmerge`` tool (for historical reason only)
2974 2969 7. default tool for fallback (``:merge`` or ``:prompt``)
2975 2970
2976 2971 This command writes out examination result in the style below::
2977 2972
2978 2973 FILE = MERGETOOL
2979 2974
2980 2975 By default, all files known in the first parent context of the
2981 2976 working directory are examined. Use file patterns and/or -I/-X
2982 2977 options to limit target files. -r/--rev is also useful to examine
2983 2978 files in another context without actual updating to it.
2984 2979
2985 2980 With --debug, this command shows warning messages while matching
2986 2981 against ``merge-patterns`` and so on, too. It is recommended to
2987 2982 use this option with explicit file patterns and/or -I/-X options,
2988 2983 because this option increases amount of output per file according
2989 2984 to configurations in hgrc.
2990 2985
2991 2986 With -v/--verbose, this command shows configurations below at
2992 2987 first (only if specified).
2993 2988
2994 2989 - ``--tool`` option
2995 2990 - ``HGMERGE`` environment variable
2996 2991 - configuration of ``ui.merge``
2997 2992
2998 2993 If merge tool is chosen before matching against
2999 2994 ``merge-patterns``, this command can't show any helpful
3000 2995 information, even with --debug. In such case, information above is
3001 2996 useful to know why a merge tool is chosen.
3002 2997 """
3003 2998 opts = pycompat.byteskwargs(opts)
3004 2999 overrides = {}
3005 3000 if opts[b'tool']:
3006 3001 overrides[(b'ui', b'forcemerge')] = opts[b'tool']
3007 3002 ui.notenoi18n(b'with --tool %r\n' % (pycompat.bytestr(opts[b'tool'])))
3008 3003
3009 3004 with ui.configoverride(overrides, b'debugmergepatterns'):
3010 3005 hgmerge = encoding.environ.get(b"HGMERGE")
3011 3006 if hgmerge is not None:
3012 3007 ui.notenoi18n(b'with HGMERGE=%r\n' % (pycompat.bytestr(hgmerge)))
3013 3008 uimerge = ui.config(b"ui", b"merge")
3014 3009 if uimerge:
3015 3010 ui.notenoi18n(b'with ui.merge=%r\n' % (pycompat.bytestr(uimerge)))
3016 3011
3017 3012 ctx = scmutil.revsingle(repo, opts.get(b'rev'))
3018 3013 m = scmutil.match(ctx, pats, opts)
3019 3014 changedelete = opts[b'changedelete']
3020 3015 for path in ctx.walk(m):
3021 3016 fctx = ctx[path]
3022 3017 with ui.silent(
3023 3018 error=True
3024 3019 ) if not ui.debugflag else util.nullcontextmanager():
3025 3020 tool, toolpath = filemerge._picktool(
3026 3021 repo,
3027 3022 ui,
3028 3023 path,
3029 3024 fctx.isbinary(),
3030 3025 b'l' in fctx.flags(),
3031 3026 changedelete,
3032 3027 )
3033 3028 ui.write(b'%s = %s\n' % (path, tool))
3034 3029
3035 3030
3036 3031 @command(b'debugpushkey', [], _(b'REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
3037 3032 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
3038 3033 """access the pushkey key/value protocol
3039 3034
3040 3035 With two args, list the keys in the given namespace.
3041 3036
3042 3037 With five args, set a key to new if it currently is set to old.
3043 3038 Reports success or failure.
3044 3039 """
3045 3040
3046 3041 target = hg.peer(ui, {}, repopath)
3047 3042 try:
3048 3043 if keyinfo:
3049 3044 key, old, new = keyinfo
3050 3045 with target.commandexecutor() as e:
3051 3046 r = e.callcommand(
3052 3047 b'pushkey',
3053 3048 {
3054 3049 b'namespace': namespace,
3055 3050 b'key': key,
3056 3051 b'old': old,
3057 3052 b'new': new,
3058 3053 },
3059 3054 ).result()
3060 3055
3061 3056 ui.status(pycompat.bytestr(r) + b'\n')
3062 3057 return not r
3063 3058 else:
3064 3059 for k, v in sorted(target.listkeys(namespace).items()):
3065 3060 ui.write(
3066 3061 b"%s\t%s\n"
3067 3062 % (stringutil.escapestr(k), stringutil.escapestr(v))
3068 3063 )
3069 3064 finally:
3070 3065 target.close()
3071 3066
3072 3067
3073 3068 @command(b'debugpvec', [], _(b'A B'))
3074 3069 def debugpvec(ui, repo, a, b=None):
3075 3070 ca = scmutil.revsingle(repo, a)
3076 3071 cb = scmutil.revsingle(repo, b)
3077 3072 pa = pvec.ctxpvec(ca)
3078 3073 pb = pvec.ctxpvec(cb)
3079 3074 if pa == pb:
3080 3075 rel = b"="
3081 3076 elif pa > pb:
3082 3077 rel = b">"
3083 3078 elif pa < pb:
3084 3079 rel = b"<"
3085 3080 elif pa | pb:
3086 3081 rel = b"|"
3087 3082 ui.write(_(b"a: %s\n") % pa)
3088 3083 ui.write(_(b"b: %s\n") % pb)
3089 3084 ui.write(_(b"depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
3090 3085 ui.write(
3091 3086 _(b"delta: %d hdist: %d distance: %d relation: %s\n")
3092 3087 % (
3093 3088 abs(pa._depth - pb._depth),
3094 3089 pvec._hamming(pa._vec, pb._vec),
3095 3090 pa.distance(pb),
3096 3091 rel,
3097 3092 )
3098 3093 )
3099 3094
3100 3095
3101 3096 @command(
3102 3097 b'debugrebuilddirstate|debugrebuildstate',
3103 3098 [
3104 3099 (b'r', b'rev', b'', _(b'revision to rebuild to'), _(b'REV')),
3105 3100 (
3106 3101 b'',
3107 3102 b'minimal',
3108 3103 None,
3109 3104 _(
3110 3105 b'only rebuild files that are inconsistent with '
3111 3106 b'the working copy parent'
3112 3107 ),
3113 3108 ),
3114 3109 ],
3115 3110 _(b'[-r REV]'),
3116 3111 )
3117 3112 def debugrebuilddirstate(ui, repo, rev, **opts):
3118 3113 """rebuild the dirstate as it would look like for the given revision
3119 3114
3120 3115 If no revision is specified the first current parent will be used.
3121 3116
3122 3117 The dirstate will be set to the files of the given revision.
3123 3118 The actual working directory content or existing dirstate
3124 3119 information such as adds or removes is not considered.
3125 3120
3126 3121 ``minimal`` will only rebuild the dirstate status for files that claim to be
3127 3122 tracked but are not in the parent manifest, or that exist in the parent
3128 3123 manifest but are not in the dirstate. It will not change adds, removes, or
3129 3124 modified files that are in the working copy parent.
3130 3125
3131 3126 One use of this command is to make the next :hg:`status` invocation
3132 3127 check the actual file content.
3133 3128 """
3134 3129 ctx = scmutil.revsingle(repo, rev)
3135 3130 with repo.wlock():
3136 3131 dirstate = repo.dirstate
3137 3132 changedfiles = None
3138 3133 # See command doc for what minimal does.
3139 3134 if opts.get('minimal'):
3140 3135 manifestfiles = set(ctx.manifest().keys())
3141 3136 dirstatefiles = set(dirstate)
3142 3137 manifestonly = manifestfiles - dirstatefiles
3143 3138 dsonly = dirstatefiles - manifestfiles
3144 3139 dsnotadded = {f for f in dsonly if not dirstate.get_entry(f).added}
3145 3140 changedfiles = manifestonly | dsnotadded
3146 3141
3147 3142 dirstate.rebuild(ctx.node(), ctx.manifest(), changedfiles)
3148 3143
3149 3144
3150 3145 @command(
3151 3146 b'debugrebuildfncache',
3152 3147 [
3153 3148 (
3154 3149 b'',
3155 3150 b'only-data',
3156 3151 False,
3157 3152 _(b'only look for wrong .d files (much faster)'),
3158 3153 )
3159 3154 ],
3160 3155 b'',
3161 3156 )
3162 3157 def debugrebuildfncache(ui, repo, **opts):
3163 3158 """rebuild the fncache file"""
3164 3159 opts = pycompat.byteskwargs(opts)
3165 3160 repair.rebuildfncache(ui, repo, opts.get(b"only_data"))
3166 3161
3167 3162
3168 3163 @command(
3169 3164 b'debugrename',
3170 3165 [(b'r', b'rev', b'', _(b'revision to debug'), _(b'REV'))],
3171 3166 _(b'[-r REV] [FILE]...'),
3172 3167 )
3173 3168 def debugrename(ui, repo, *pats, **opts):
3174 3169 """dump rename information"""
3175 3170
3176 3171 opts = pycompat.byteskwargs(opts)
3177 3172 ctx = scmutil.revsingle(repo, opts.get(b'rev'))
3178 3173 m = scmutil.match(ctx, pats, opts)
3179 3174 for abs in ctx.walk(m):
3180 3175 fctx = ctx[abs]
3181 3176 o = fctx.filelog().renamed(fctx.filenode())
3182 3177 rel = repo.pathto(abs)
3183 3178 if o:
3184 3179 ui.write(_(b"%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
3185 3180 else:
3186 3181 ui.write(_(b"%s not renamed\n") % rel)
3187 3182
3188 3183
3189 3184 @command(b'debugrequires|debugrequirements', [], b'')
3190 3185 def debugrequirements(ui, repo):
3191 3186 """print the current repo requirements"""
3192 3187 for r in sorted(repo.requirements):
3193 3188 ui.write(b"%s\n" % r)
3194 3189
3195 3190
3196 3191 @command(
3197 3192 b'debugrevlog',
3198 3193 cmdutil.debugrevlogopts + [(b'd', b'dump', False, _(b'dump index data'))],
3199 3194 _(b'-c|-m|FILE'),
3200 3195 optionalrepo=True,
3201 3196 )
3202 3197 def debugrevlog(ui, repo, file_=None, **opts):
3203 3198 """show data and statistics about a revlog"""
3204 3199 opts = pycompat.byteskwargs(opts)
3205 3200 r = cmdutil.openrevlog(repo, b'debugrevlog', file_, opts)
3206 3201
3207 3202 if opts.get(b"dump"):
3208 3203 revlog_debug.dump(ui, r)
3209 3204 else:
3210 3205 revlog_debug.debug_revlog(ui, r)
3211 3206 return 0
3212 3207
3213 3208
3214 3209 @command(
3215 3210 b'debugrevlogindex',
3216 3211 cmdutil.debugrevlogopts
3217 3212 + [(b'f', b'format', 0, _(b'revlog format'), _(b'FORMAT'))],
3218 3213 _(b'[-f FORMAT] -c|-m|FILE'),
3219 3214 optionalrepo=True,
3220 3215 )
3221 3216 def debugrevlogindex(ui, repo, file_=None, **opts):
3222 3217 """dump the contents of a revlog index"""
3223 3218 opts = pycompat.byteskwargs(opts)
3224 3219 r = cmdutil.openrevlog(repo, b'debugrevlogindex', file_, opts)
3225 3220 format = opts.get(b'format', 0)
3226 3221 if format not in (0, 1):
3227 3222 raise error.Abort(_(b"unknown format %d") % format)
3228 3223
3229 3224 if ui.debugflag:
3230 3225 shortfn = hex
3231 3226 else:
3232 3227 shortfn = short
3233 3228
3234 3229 # There might not be anything in r, so have a sane default
3235 3230 idlen = 12
3236 3231 for i in r:
3237 3232 idlen = len(shortfn(r.node(i)))
3238 3233 break
3239 3234
3240 3235 if format == 0:
3241 3236 if ui.verbose:
3242 3237 ui.writenoi18n(
3243 3238 b" rev offset length linkrev %s %s p2\n"
3244 3239 % (b"nodeid".ljust(idlen), b"p1".ljust(idlen))
3245 3240 )
3246 3241 else:
3247 3242 ui.writenoi18n(
3248 3243 b" rev linkrev %s %s p2\n"
3249 3244 % (b"nodeid".ljust(idlen), b"p1".ljust(idlen))
3250 3245 )
3251 3246 elif format == 1:
3252 3247 if ui.verbose:
3253 3248 ui.writenoi18n(
3254 3249 (
3255 3250 b" rev flag offset length size link p1"
3256 3251 b" p2 %s\n"
3257 3252 )
3258 3253 % b"nodeid".rjust(idlen)
3259 3254 )
3260 3255 else:
3261 3256 ui.writenoi18n(
3262 3257 b" rev flag size link p1 p2 %s\n"
3263 3258 % b"nodeid".rjust(idlen)
3264 3259 )
3265 3260
3266 3261 for i in r:
3267 3262 node = r.node(i)
3268 3263 if format == 0:
3269 3264 try:
3270 3265 pp = r.parents(node)
3271 3266 except Exception:
3272 3267 pp = [repo.nullid, repo.nullid]
3273 3268 if ui.verbose:
3274 3269 ui.write(
3275 3270 b"% 6d % 9d % 7d % 7d %s %s %s\n"
3276 3271 % (
3277 3272 i,
3278 3273 r.start(i),
3279 3274 r.length(i),
3280 3275 r.linkrev(i),
3281 3276 shortfn(node),
3282 3277 shortfn(pp[0]),
3283 3278 shortfn(pp[1]),
3284 3279 )
3285 3280 )
3286 3281 else:
3287 3282 ui.write(
3288 3283 b"% 6d % 7d %s %s %s\n"
3289 3284 % (
3290 3285 i,
3291 3286 r.linkrev(i),
3292 3287 shortfn(node),
3293 3288 shortfn(pp[0]),
3294 3289 shortfn(pp[1]),
3295 3290 )
3296 3291 )
3297 3292 elif format == 1:
3298 3293 pr = r.parentrevs(i)
3299 3294 if ui.verbose:
3300 3295 ui.write(
3301 3296 b"% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d %s\n"
3302 3297 % (
3303 3298 i,
3304 3299 r.flags(i),
3305 3300 r.start(i),
3306 3301 r.length(i),
3307 3302 r.rawsize(i),
3308 3303 r.linkrev(i),
3309 3304 pr[0],
3310 3305 pr[1],
3311 3306 shortfn(node),
3312 3307 )
3313 3308 )
3314 3309 else:
3315 3310 ui.write(
3316 3311 b"% 6d %04x % 8d % 6d % 6d % 6d %s\n"
3317 3312 % (
3318 3313 i,
3319 3314 r.flags(i),
3320 3315 r.rawsize(i),
3321 3316 r.linkrev(i),
3322 3317 pr[0],
3323 3318 pr[1],
3324 3319 shortfn(node),
3325 3320 )
3326 3321 )
3327 3322
3328 3323
3329 3324 @command(
3330 3325 b'debugrevspec',
3331 3326 [
3332 3327 (
3333 3328 b'',
3334 3329 b'optimize',
3335 3330 None,
3336 3331 _(b'print parsed tree after optimizing (DEPRECATED)'),
3337 3332 ),
3338 3333 (
3339 3334 b'',
3340 3335 b'show-revs',
3341 3336 True,
3342 3337 _(b'print list of result revisions (default)'),
3343 3338 ),
3344 3339 (
3345 3340 b's',
3346 3341 b'show-set',
3347 3342 None,
3348 3343 _(b'print internal representation of result set'),
3349 3344 ),
3350 3345 (
3351 3346 b'p',
3352 3347 b'show-stage',
3353 3348 [],
3354 3349 _(b'print parsed tree at the given stage'),
3355 3350 _(b'NAME'),
3356 3351 ),
3357 3352 (b'', b'no-optimized', False, _(b'evaluate tree without optimization')),
3358 3353 (b'', b'verify-optimized', False, _(b'verify optimized result')),
3359 3354 ],
3360 3355 b'REVSPEC',
3361 3356 )
3362 3357 def debugrevspec(ui, repo, expr, **opts):
3363 3358 """parse and apply a revision specification
3364 3359
3365 3360 Use -p/--show-stage option to print the parsed tree at the given stages.
3366 3361 Use -p all to print tree at every stage.
3367 3362
3368 3363 Use --no-show-revs option with -s or -p to print only the set
3369 3364 representation or the parsed tree respectively.
3370 3365
3371 3366 Use --verify-optimized to compare the optimized result with the unoptimized
3372 3367 one. Returns 1 if the optimized result differs.
3373 3368 """
3374 3369 opts = pycompat.byteskwargs(opts)
3375 3370 aliases = ui.configitems(b'revsetalias')
3376 3371 stages = [
3377 3372 (b'parsed', lambda tree: tree),
3378 3373 (
3379 3374 b'expanded',
3380 3375 lambda tree: revsetlang.expandaliases(tree, aliases, ui.warn),
3381 3376 ),
3382 3377 (b'concatenated', revsetlang.foldconcat),
3383 3378 (b'analyzed', revsetlang.analyze),
3384 3379 (b'optimized', revsetlang.optimize),
3385 3380 ]
3386 3381 if opts[b'no_optimized']:
3387 3382 stages = stages[:-1]
3388 3383 if opts[b'verify_optimized'] and opts[b'no_optimized']:
3389 3384 raise error.Abort(
3390 3385 _(b'cannot use --verify-optimized with --no-optimized')
3391 3386 )
3392 3387 stagenames = {n for n, f in stages}
3393 3388
3394 3389 showalways = set()
3395 3390 showchanged = set()
3396 3391 if ui.verbose and not opts[b'show_stage']:
3397 3392 # show parsed tree by --verbose (deprecated)
3398 3393 showalways.add(b'parsed')
3399 3394 showchanged.update([b'expanded', b'concatenated'])
3400 3395 if opts[b'optimize']:
3401 3396 showalways.add(b'optimized')
3402 3397 if opts[b'show_stage'] and opts[b'optimize']:
3403 3398 raise error.Abort(_(b'cannot use --optimize with --show-stage'))
3404 3399 if opts[b'show_stage'] == [b'all']:
3405 3400 showalways.update(stagenames)
3406 3401 else:
3407 3402 for n in opts[b'show_stage']:
3408 3403 if n not in stagenames:
3409 3404 raise error.Abort(_(b'invalid stage name: %s') % n)
3410 3405 showalways.update(opts[b'show_stage'])
3411 3406
3412 3407 treebystage = {}
3413 3408 printedtree = None
3414 3409 tree = revsetlang.parse(expr, lookup=revset.lookupfn(repo))
3415 3410 for n, f in stages:
3416 3411 treebystage[n] = tree = f(tree)
3417 3412 if n in showalways or (n in showchanged and tree != printedtree):
3418 3413 if opts[b'show_stage'] or n != b'parsed':
3419 3414 ui.write(b"* %s:\n" % n)
3420 3415 ui.write(revsetlang.prettyformat(tree), b"\n")
3421 3416 printedtree = tree
3422 3417
3423 3418 if opts[b'verify_optimized']:
3424 3419 arevs = revset.makematcher(treebystage[b'analyzed'])(repo)
3425 3420 brevs = revset.makematcher(treebystage[b'optimized'])(repo)
3426 3421 if opts[b'show_set'] or (opts[b'show_set'] is None and ui.verbose):
3427 3422 ui.writenoi18n(
3428 3423 b"* analyzed set:\n", stringutil.prettyrepr(arevs), b"\n"
3429 3424 )
3430 3425 ui.writenoi18n(
3431 3426 b"* optimized set:\n", stringutil.prettyrepr(brevs), b"\n"
3432 3427 )
3433 3428 arevs = list(arevs)
3434 3429 brevs = list(brevs)
3435 3430 if arevs == brevs:
3436 3431 return 0
3437 3432 ui.writenoi18n(b'--- analyzed\n', label=b'diff.file_a')
3438 3433 ui.writenoi18n(b'+++ optimized\n', label=b'diff.file_b')
3439 3434 sm = difflib.SequenceMatcher(None, arevs, brevs)
3440 3435 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3441 3436 if tag in ('delete', 'replace'):
3442 3437 for c in arevs[alo:ahi]:
3443 3438 ui.write(b'-%d\n' % c, label=b'diff.deleted')
3444 3439 if tag in ('insert', 'replace'):
3445 3440 for c in brevs[blo:bhi]:
3446 3441 ui.write(b'+%d\n' % c, label=b'diff.inserted')
3447 3442 if tag == 'equal':
3448 3443 for c in arevs[alo:ahi]:
3449 3444 ui.write(b' %d\n' % c)
3450 3445 return 1
3451 3446
3452 3447 func = revset.makematcher(tree)
3453 3448 revs = func(repo)
3454 3449 if opts[b'show_set'] or (opts[b'show_set'] is None and ui.verbose):
3455 3450 ui.writenoi18n(b"* set:\n", stringutil.prettyrepr(revs), b"\n")
3456 3451 if not opts[b'show_revs']:
3457 3452 return
3458 3453 for c in revs:
3459 3454 ui.write(b"%d\n" % c)
3460 3455
3461 3456
3462 3457 @command(
3463 3458 b'debugserve',
3464 3459 [
3465 3460 (
3466 3461 b'',
3467 3462 b'sshstdio',
3468 3463 False,
3469 3464 _(b'run an SSH server bound to process handles'),
3470 3465 ),
3471 3466 (b'', b'logiofd', b'', _(b'file descriptor to log server I/O to')),
3472 3467 (b'', b'logiofile', b'', _(b'file to log server I/O to')),
3473 3468 ],
3474 3469 b'',
3475 3470 )
3476 3471 def debugserve(ui, repo, **opts):
3477 3472 """run a server with advanced settings
3478 3473
3479 3474 This command is similar to :hg:`serve`. It exists partially as a
3480 3475 workaround to the fact that ``hg serve --stdio`` must have specific
3481 3476 arguments for security reasons.
3482 3477 """
3483 3478 opts = pycompat.byteskwargs(opts)
3484 3479
3485 3480 if not opts[b'sshstdio']:
3486 3481 raise error.Abort(_(b'only --sshstdio is currently supported'))
3487 3482
3488 3483 logfh = None
3489 3484
3490 3485 if opts[b'logiofd'] and opts[b'logiofile']:
3491 3486 raise error.Abort(_(b'cannot use both --logiofd and --logiofile'))
3492 3487
3493 3488 if opts[b'logiofd']:
3494 3489 # Ideally we would be line buffered. But line buffering in binary
3495 3490 # mode isn't supported and emits a warning in Python 3.8+. Disabling
3496 3491 # buffering could have performance impacts. But since this isn't
3497 3492 # performance critical code, it should be fine.
3498 3493 try:
3499 3494 logfh = os.fdopen(int(opts[b'logiofd']), 'ab', 0)
3500 3495 except OSError as e:
3501 3496 if e.errno != errno.ESPIPE:
3502 3497 raise
3503 3498 # can't seek a pipe, so `ab` mode fails on py3
3504 3499 logfh = os.fdopen(int(opts[b'logiofd']), 'wb', 0)
3505 3500 elif opts[b'logiofile']:
3506 3501 logfh = open(opts[b'logiofile'], b'ab', 0)
3507 3502
3508 3503 s = wireprotoserver.sshserver(ui, repo, logfh=logfh)
3509 3504 s.serve_forever()
3510 3505
3511 3506
3512 3507 @command(b'debugsetparents', [], _(b'REV1 [REV2]'))
3513 3508 def debugsetparents(ui, repo, rev1, rev2=None):
3514 3509 """manually set the parents of the current working directory (DANGEROUS)
3515 3510
3516 3511 This command is not what you are looking for and should not be used. Using
3517 3512 this command will most certainly results in slight corruption of the file
3518 3513 level histories withing your repository. DO NOT USE THIS COMMAND.
3519 3514
3520 3515 The command update the p1 and p2 field in the dirstate, and not touching
3521 3516 anything else. This useful for writing repository conversion tools, but
3522 3517 should be used with extreme care. For example, neither the working
3523 3518 directory nor the dirstate is updated, so file status may be incorrect
3524 3519 after running this command. Only used if you are one of the few people that
3525 3520 deeply unstand both conversion tools and file level histories. If you are
3526 3521 reading this help, you are not one of this people (most of them sailed west
3527 3522 from Mithlond anyway.
3528 3523
3529 3524 So one last time DO NOT USE THIS COMMAND.
3530 3525
3531 3526 Returns 0 on success.
3532 3527 """
3533 3528
3534 3529 node1 = scmutil.revsingle(repo, rev1).node()
3535 3530 node2 = scmutil.revsingle(repo, rev2, b'null').node()
3536 3531
3537 3532 with repo.wlock():
3538 3533 repo.setparents(node1, node2)
3539 3534
3540 3535
3541 3536 @command(b'debugsidedata', cmdutil.debugrevlogopts, _(b'-c|-m|FILE REV'))
3542 3537 def debugsidedata(ui, repo, file_, rev=None, **opts):
3543 3538 """dump the side data for a cl/manifest/file revision
3544 3539
3545 3540 Use --verbose to dump the sidedata content."""
3546 3541 opts = pycompat.byteskwargs(opts)
3547 3542 if opts.get(b'changelog') or opts.get(b'manifest') or opts.get(b'dir'):
3548 3543 if rev is not None:
3549 3544 raise error.CommandError(b'debugdata', _(b'invalid arguments'))
3550 3545 file_, rev = None, file_
3551 3546 elif rev is None:
3552 3547 raise error.CommandError(b'debugdata', _(b'invalid arguments'))
3553 3548 r = cmdutil.openstorage(repo, b'debugdata', file_, opts)
3554 3549 r = getattr(r, '_revlog', r)
3555 3550 try:
3556 3551 sidedata = r.sidedata(r.lookup(rev))
3557 3552 except KeyError:
3558 3553 raise error.Abort(_(b'invalid revision identifier %s') % rev)
3559 3554 if sidedata:
3560 3555 sidedata = list(sidedata.items())
3561 3556 sidedata.sort()
3562 3557 ui.writenoi18n(b'%d sidedata entries\n' % len(sidedata))
3563 3558 for key, value in sidedata:
3564 3559 ui.writenoi18n(b' entry-%04o size %d\n' % (key, len(value)))
3565 3560 if ui.verbose:
3566 3561 ui.writenoi18n(b' %s\n' % stringutil.pprint(value))
3567 3562
3568 3563
3569 3564 @command(b'debugssl', [], b'[SOURCE]', optionalrepo=True)
3570 3565 def debugssl(ui, repo, source=None, **opts):
3571 3566 """test a secure connection to a server
3572 3567
3573 3568 This builds the certificate chain for the server on Windows, installing the
3574 3569 missing intermediates and trusted root via Windows Update if necessary. It
3575 3570 does nothing on other platforms.
3576 3571
3577 3572 If SOURCE is omitted, the 'default' path will be used. If a URL is given,
3578 3573 that server is used. See :hg:`help urls` for more information.
3579 3574
3580 3575 If the update succeeds, retry the original operation. Otherwise, the cause
3581 3576 of the SSL error is likely another issue.
3582 3577 """
3583 3578 if not pycompat.iswindows:
3584 3579 raise error.Abort(
3585 3580 _(b'certificate chain building is only possible on Windows')
3586 3581 )
3587 3582
3588 3583 if not source:
3589 3584 if not repo:
3590 3585 raise error.Abort(
3591 3586 _(
3592 3587 b"there is no Mercurial repository here, and no "
3593 3588 b"server specified"
3594 3589 )
3595 3590 )
3596 3591 source = b"default"
3597 3592
3598 3593 path = urlutil.get_unique_pull_path_obj(b'debugssl', ui, source)
3599 3594 url = path.url
3600 3595
3601 3596 defaultport = {b'https': 443, b'ssh': 22}
3602 3597 if url.scheme in defaultport:
3603 3598 try:
3604 3599 addr = (url.host, int(url.port or defaultport[url.scheme]))
3605 3600 except ValueError:
3606 3601 raise error.Abort(_(b"malformed port number in URL"))
3607 3602 else:
3608 3603 raise error.Abort(_(b"only https and ssh connections are supported"))
3609 3604
3610 3605 from . import win32
3611 3606
3612 3607 s = ssl.wrap_socket(
3613 3608 socket.socket(),
3614 3609 ssl_version=ssl.PROTOCOL_TLS,
3615 3610 cert_reqs=ssl.CERT_NONE,
3616 3611 ca_certs=None,
3617 3612 )
3618 3613
3619 3614 try:
3620 3615 s.connect(addr)
3621 3616 cert = s.getpeercert(True)
3622 3617
3623 3618 ui.status(_(b'checking the certificate chain for %s\n') % url.host)
3624 3619
3625 3620 complete = win32.checkcertificatechain(cert, build=False)
3626 3621
3627 3622 if not complete:
3628 3623 ui.status(_(b'certificate chain is incomplete, updating... '))
3629 3624
3630 3625 if not win32.checkcertificatechain(cert):
3631 3626 ui.status(_(b'failed.\n'))
3632 3627 else:
3633 3628 ui.status(_(b'done.\n'))
3634 3629 else:
3635 3630 ui.status(_(b'full certificate chain is available\n'))
3636 3631 finally:
3637 3632 s.close()
3638 3633
3639 3634
3640 3635 @command(
3641 3636 b"debugbackupbundle",
3642 3637 [
3643 3638 (
3644 3639 b"",
3645 3640 b"recover",
3646 3641 b"",
3647 3642 b"brings the specified changeset back into the repository",
3648 3643 )
3649 3644 ]
3650 3645 + cmdutil.logopts,
3651 3646 _(b"hg debugbackupbundle [--recover HASH]"),
3652 3647 )
3653 3648 def debugbackupbundle(ui, repo, *pats, **opts):
3654 3649 """lists the changesets available in backup bundles
3655 3650
3656 3651 Without any arguments, this command prints a list of the changesets in each
3657 3652 backup bundle.
3658 3653
3659 3654 --recover takes a changeset hash and unbundles the first bundle that
3660 3655 contains that hash, which puts that changeset back in your repository.
3661 3656
3662 3657 --verbose will print the entire commit message and the bundle path for that
3663 3658 backup.
3664 3659 """
3665 3660 backups = list(
3666 3661 filter(
3667 3662 os.path.isfile, glob.glob(repo.vfs.join(b"strip-backup") + b"/*.hg")
3668 3663 )
3669 3664 )
3670 3665 backups.sort(key=lambda x: os.path.getmtime(x), reverse=True)
3671 3666
3672 3667 opts = pycompat.byteskwargs(opts)
3673 3668 opts[b"bundle"] = b""
3674 3669 opts[b"force"] = None
3675 3670 limit = logcmdutil.getlimit(opts)
3676 3671
3677 3672 def display(other, chlist, displayer):
3678 3673 if opts.get(b"newest_first"):
3679 3674 chlist.reverse()
3680 3675 count = 0
3681 3676 for n in chlist:
3682 3677 if limit is not None and count >= limit:
3683 3678 break
3684 3679 parents = [
3685 3680 True for p in other.changelog.parents(n) if p != repo.nullid
3686 3681 ]
3687 3682 if opts.get(b"no_merges") and len(parents) == 2:
3688 3683 continue
3689 3684 count += 1
3690 3685 displayer.show(other[n])
3691 3686
3692 3687 recovernode = opts.get(b"recover")
3693 3688 if recovernode:
3694 3689 if scmutil.isrevsymbol(repo, recovernode):
3695 3690 ui.warn(_(b"%s already exists in the repo\n") % recovernode)
3696 3691 return
3697 3692 elif backups:
3698 3693 msg = _(
3699 3694 b"Recover changesets using: hg debugbackupbundle --recover "
3700 3695 b"<changeset hash>\n\nAvailable backup changesets:"
3701 3696 )
3702 3697 ui.status(msg, label=b"status.removed")
3703 3698 else:
3704 3699 ui.status(_(b"no backup changesets found\n"))
3705 3700 return
3706 3701
3707 3702 for backup in backups:
3708 3703 # Much of this is copied from the hg incoming logic
3709 3704 source = os.path.relpath(backup, encoding.getcwd())
3710 3705 path = urlutil.get_unique_pull_path_obj(
3711 3706 b'debugbackupbundle',
3712 3707 ui,
3713 3708 source,
3714 3709 )
3715 3710 try:
3716 3711 other = hg.peer(repo, opts, path)
3717 3712 except error.LookupError as ex:
3718 3713 msg = _(b"\nwarning: unable to open bundle %s") % path.loc
3719 3714 hint = _(b"\n(missing parent rev %s)\n") % short(ex.name)
3720 3715 ui.warn(msg, hint=hint)
3721 3716 continue
3722 3717 branches = (path.branch, opts.get(b'branch', []))
3723 3718 revs, checkout = hg.addbranchrevs(
3724 3719 repo, other, branches, opts.get(b"rev")
3725 3720 )
3726 3721
3727 3722 if revs:
3728 3723 revs = [other.lookup(rev) for rev in revs]
3729 3724
3730 3725 with ui.silent():
3731 3726 try:
3732 3727 other, chlist, cleanupfn = bundlerepo.getremotechanges(
3733 3728 ui, repo, other, revs, opts[b"bundle"], opts[b"force"]
3734 3729 )
3735 3730 except error.LookupError:
3736 3731 continue
3737 3732
3738 3733 try:
3739 3734 if not chlist:
3740 3735 continue
3741 3736 if recovernode:
3742 3737 with repo.lock(), repo.transaction(b"unbundle") as tr:
3743 3738 if scmutil.isrevsymbol(other, recovernode):
3744 3739 ui.status(_(b"Unbundling %s\n") % (recovernode))
3745 3740 f = hg.openpath(ui, path.loc)
3746 3741 gen = exchange.readbundle(ui, f, path.loc)
3747 3742 if isinstance(gen, bundle2.unbundle20):
3748 3743 bundle2.applybundle(
3749 3744 repo,
3750 3745 gen,
3751 3746 tr,
3752 3747 source=b"unbundle",
3753 3748 url=b"bundle:" + path.loc,
3754 3749 )
3755 3750 else:
3756 3751 gen.apply(repo, b"unbundle", b"bundle:" + path.loc)
3757 3752 break
3758 3753 else:
3759 3754 backupdate = encoding.strtolocal(
3760 3755 time.strftime(
3761 3756 "%a %H:%M, %Y-%m-%d",
3762 3757 time.localtime(os.path.getmtime(path.loc)),
3763 3758 )
3764 3759 )
3765 3760 ui.status(b"\n%s\n" % (backupdate.ljust(50)))
3766 3761 if ui.verbose:
3767 3762 ui.status(b"%s%s\n" % (b"bundle:".ljust(13), path.loc))
3768 3763 else:
3769 3764 opts[
3770 3765 b"template"
3771 3766 ] = b"{label('status.modified', node|short)} {desc|firstline}\n"
3772 3767 displayer = logcmdutil.changesetdisplayer(
3773 3768 ui, other, opts, False
3774 3769 )
3775 3770 display(other, chlist, displayer)
3776 3771 displayer.close()
3777 3772 finally:
3778 3773 cleanupfn()
3779 3774
3780 3775
3781 3776 @command(
3782 3777 b'debugsub',
3783 3778 [(b'r', b'rev', b'', _(b'revision to check'), _(b'REV'))],
3784 3779 _(b'[-r REV] [REV]'),
3785 3780 )
3786 3781 def debugsub(ui, repo, rev=None):
3787 3782 ctx = scmutil.revsingle(repo, rev, None)
3788 3783 for k, v in sorted(ctx.substate.items()):
3789 3784 ui.writenoi18n(b'path %s\n' % k)
3790 3785 ui.writenoi18n(b' source %s\n' % v[0])
3791 3786 ui.writenoi18n(b' revision %s\n' % v[1])
3792 3787
3793 3788
3794 3789 @command(b'debugshell', optionalrepo=True)
3795 3790 def debugshell(ui, repo):
3796 3791 """run an interactive Python interpreter
3797 3792
3798 3793 The local namespace is provided with a reference to the ui and
3799 3794 the repo instance (if available).
3800 3795 """
3801 3796 import code
3802 3797
3803 3798 imported_objects = {
3804 3799 'ui': ui,
3805 3800 'repo': repo,
3806 3801 }
3807 3802
3808 3803 code.interact(local=imported_objects)
3809 3804
3810 3805
3811 3806 @command(
3812 3807 b'debug-revlog-stats',
3813 3808 [
3814 3809 (b'c', b'changelog', None, _(b'Display changelog statistics')),
3815 3810 (b'm', b'manifest', None, _(b'Display manifest statistics')),
3816 3811 (b'f', b'filelogs', None, _(b'Display filelogs statistics')),
3817 3812 ]
3818 3813 + cmdutil.formatteropts,
3819 3814 )
3820 3815 def debug_revlog_stats(ui, repo, **opts):
3821 3816 """display statistics about revlogs in the store"""
3822 3817 opts = pycompat.byteskwargs(opts)
3823 3818 changelog = opts[b"changelog"]
3824 3819 manifest = opts[b"manifest"]
3825 3820 filelogs = opts[b"filelogs"]
3826 3821
3827 3822 if changelog is None and manifest is None and filelogs is None:
3828 3823 changelog = True
3829 3824 manifest = True
3830 3825 filelogs = True
3831 3826
3832 3827 repo = repo.unfiltered()
3833 3828 fm = ui.formatter(b'debug-revlog-stats', opts)
3834 3829 revlog_debug.debug_revlog_stats(repo, fm, changelog, manifest, filelogs)
3835 3830 fm.end()
3836 3831
3837 3832
3838 3833 @command(
3839 3834 b'debugsuccessorssets',
3840 3835 [(b'', b'closest', False, _(b'return closest successors sets only'))],
3841 3836 _(b'[REV]'),
3842 3837 )
3843 3838 def debugsuccessorssets(ui, repo, *revs, **opts):
3844 3839 """show set of successors for revision
3845 3840
3846 3841 A successors set of changeset A is a consistent group of revisions that
3847 3842 succeed A. It contains non-obsolete changesets only unless closests
3848 3843 successors set is set.
3849 3844
3850 3845 In most cases a changeset A has a single successors set containing a single
3851 3846 successor (changeset A replaced by A').
3852 3847
3853 3848 A changeset that is made obsolete with no successors are called "pruned".
3854 3849 Such changesets have no successors sets at all.
3855 3850
3856 3851 A changeset that has been "split" will have a successors set containing
3857 3852 more than one successor.
3858 3853
3859 3854 A changeset that has been rewritten in multiple different ways is called
3860 3855 "divergent". Such changesets have multiple successor sets (each of which
3861 3856 may also be split, i.e. have multiple successors).
3862 3857
3863 3858 Results are displayed as follows::
3864 3859
3865 3860 <rev1>
3866 3861 <successors-1A>
3867 3862 <rev2>
3868 3863 <successors-2A>
3869 3864 <successors-2B1> <successors-2B2> <successors-2B3>
3870 3865
3871 3866 Here rev2 has two possible (i.e. divergent) successors sets. The first
3872 3867 holds one element, whereas the second holds three (i.e. the changeset has
3873 3868 been split).
3874 3869 """
3875 3870 # passed to successorssets caching computation from one call to another
3876 3871 cache = {}
3877 3872 ctx2str = bytes
3878 3873 node2str = short
3879 3874 for rev in logcmdutil.revrange(repo, revs):
3880 3875 ctx = repo[rev]
3881 3876 ui.write(b'%s\n' % ctx2str(ctx))
3882 3877 for succsset in obsutil.successorssets(
3883 3878 repo, ctx.node(), closest=opts['closest'], cache=cache
3884 3879 ):
3885 3880 if succsset:
3886 3881 ui.write(b' ')
3887 3882 ui.write(node2str(succsset[0]))
3888 3883 for node in succsset[1:]:
3889 3884 ui.write(b' ')
3890 3885 ui.write(node2str(node))
3891 3886 ui.write(b'\n')
3892 3887
3893 3888
3894 3889 @command(b'debugtagscache', [])
3895 3890 def debugtagscache(ui, repo):
3896 3891 """display the contents of .hg/cache/hgtagsfnodes1"""
3897 3892 cache = tagsmod.hgtagsfnodescache(repo.unfiltered())
3898 3893 flog = repo.file(b'.hgtags')
3899 3894 for r in repo:
3900 3895 node = repo[r].node()
3901 3896 tagsnode = cache.getfnode(node, computemissing=False)
3902 3897 if tagsnode:
3903 3898 tagsnodedisplay = hex(tagsnode)
3904 3899 if not flog.hasnode(tagsnode):
3905 3900 tagsnodedisplay += b' (unknown node)'
3906 3901 elif tagsnode is None:
3907 3902 tagsnodedisplay = b'missing'
3908 3903 else:
3909 3904 tagsnodedisplay = b'invalid'
3910 3905
3911 3906 ui.write(b'%d %s %s\n' % (r, hex(node), tagsnodedisplay))
3912 3907
3913 3908
3914 3909 @command(
3915 3910 b'debugtemplate',
3916 3911 [
3917 3912 (b'r', b'rev', [], _(b'apply template on changesets'), _(b'REV')),
3918 3913 (b'D', b'define', [], _(b'define template keyword'), _(b'KEY=VALUE')),
3919 3914 ],
3920 3915 _(b'[-r REV]... [-D KEY=VALUE]... TEMPLATE'),
3921 3916 optionalrepo=True,
3922 3917 )
3923 3918 def debugtemplate(ui, repo, tmpl, **opts):
3924 3919 """parse and apply a template
3925 3920
3926 3921 If -r/--rev is given, the template is processed as a log template and
3927 3922 applied to the given changesets. Otherwise, it is processed as a generic
3928 3923 template.
3929 3924
3930 3925 Use --verbose to print the parsed tree.
3931 3926 """
3932 3927 revs = None
3933 3928 if opts['rev']:
3934 3929 if repo is None:
3935 3930 raise error.RepoError(
3936 3931 _(b'there is no Mercurial repository here (.hg not found)')
3937 3932 )
3938 3933 revs = logcmdutil.revrange(repo, opts['rev'])
3939 3934
3940 3935 props = {}
3941 3936 for d in opts['define']:
3942 3937 try:
3943 3938 k, v = (e.strip() for e in d.split(b'=', 1))
3944 3939 if not k or k == b'ui':
3945 3940 raise ValueError
3946 3941 props[k] = v
3947 3942 except ValueError:
3948 3943 raise error.Abort(_(b'malformed keyword definition: %s') % d)
3949 3944
3950 3945 if ui.verbose:
3951 3946 aliases = ui.configitems(b'templatealias')
3952 3947 tree = templater.parse(tmpl)
3953 3948 ui.note(templater.prettyformat(tree), b'\n')
3954 3949 newtree = templater.expandaliases(tree, aliases)
3955 3950 if newtree != tree:
3956 3951 ui.notenoi18n(
3957 3952 b"* expanded:\n", templater.prettyformat(newtree), b'\n'
3958 3953 )
3959 3954
3960 3955 if revs is None:
3961 3956 tres = formatter.templateresources(ui, repo)
3962 3957 t = formatter.maketemplater(ui, tmpl, resources=tres)
3963 3958 if ui.verbose:
3964 3959 kwds, funcs = t.symbolsuseddefault()
3965 3960 ui.writenoi18n(b"* keywords: %s\n" % b', '.join(sorted(kwds)))
3966 3961 ui.writenoi18n(b"* functions: %s\n" % b', '.join(sorted(funcs)))
3967 3962 ui.write(t.renderdefault(props))
3968 3963 else:
3969 3964 displayer = logcmdutil.maketemplater(ui, repo, tmpl)
3970 3965 if ui.verbose:
3971 3966 kwds, funcs = displayer.t.symbolsuseddefault()
3972 3967 ui.writenoi18n(b"* keywords: %s\n" % b', '.join(sorted(kwds)))
3973 3968 ui.writenoi18n(b"* functions: %s\n" % b', '.join(sorted(funcs)))
3974 3969 for r in revs:
3975 3970 displayer.show(repo[r], **pycompat.strkwargs(props))
3976 3971 displayer.close()
3977 3972
3978 3973
3979 3974 @command(
3980 3975 b'debuguigetpass',
3981 3976 [
3982 3977 (b'p', b'prompt', b'', _(b'prompt text'), _(b'TEXT')),
3983 3978 ],
3984 3979 _(b'[-p TEXT]'),
3985 3980 norepo=True,
3986 3981 )
3987 3982 def debuguigetpass(ui, prompt=b''):
3988 3983 """show prompt to type password"""
3989 3984 r = ui.getpass(prompt)
3990 3985 if r is None:
3991 3986 r = b"<default response>"
3992 3987 ui.writenoi18n(b'response: %s\n' % r)
3993 3988
3994 3989
3995 3990 @command(
3996 3991 b'debuguiprompt',
3997 3992 [
3998 3993 (b'p', b'prompt', b'', _(b'prompt text'), _(b'TEXT')),
3999 3994 ],
4000 3995 _(b'[-p TEXT]'),
4001 3996 norepo=True,
4002 3997 )
4003 3998 def debuguiprompt(ui, prompt=b''):
4004 3999 """show plain prompt"""
4005 4000 r = ui.prompt(prompt)
4006 4001 ui.writenoi18n(b'response: %s\n' % r)
4007 4002
4008 4003
4009 4004 @command(b'debugupdatecaches', [])
4010 4005 def debugupdatecaches(ui, repo, *pats, **opts):
4011 4006 """warm all known caches in the repository"""
4012 4007 with repo.wlock(), repo.lock():
4013 4008 repo.updatecaches(caches=repository.CACHES_ALL)
4014 4009
4015 4010
4016 4011 @command(
4017 4012 b'debugupgraderepo',
4018 4013 [
4019 4014 (
4020 4015 b'o',
4021 4016 b'optimize',
4022 4017 [],
4023 4018 _(b'extra optimization to perform'),
4024 4019 _(b'NAME'),
4025 4020 ),
4026 4021 (b'', b'run', False, _(b'performs an upgrade')),
4027 4022 (b'', b'backup', True, _(b'keep the old repository content around')),
4028 4023 (b'', b'changelog', None, _(b'select the changelog for upgrade')),
4029 4024 (b'', b'manifest', None, _(b'select the manifest for upgrade')),
4030 4025 (b'', b'filelogs', None, _(b'select all filelogs for upgrade')),
4031 4026 ],
4032 4027 )
4033 4028 def debugupgraderepo(ui, repo, run=False, optimize=None, backup=True, **opts):
4034 4029 """upgrade a repository to use different features
4035 4030
4036 4031 If no arguments are specified, the repository is evaluated for upgrade
4037 4032 and a list of problems and potential optimizations is printed.
4038 4033
4039 4034 With ``--run``, a repository upgrade is performed. Behavior of the upgrade
4040 4035 can be influenced via additional arguments. More details will be provided
4041 4036 by the command output when run without ``--run``.
4042 4037
4043 4038 During the upgrade, the repository will be locked and no writes will be
4044 4039 allowed.
4045 4040
4046 4041 At the end of the upgrade, the repository may not be readable while new
4047 4042 repository data is swapped in. This window will be as long as it takes to
4048 4043 rename some directories inside the ``.hg`` directory. On most machines, this
4049 4044 should complete almost instantaneously and the chances of a consumer being
4050 4045 unable to access the repository should be low.
4051 4046
4052 4047 By default, all revlogs will be upgraded. You can restrict this using flags
4053 4048 such as `--manifest`:
4054 4049
4055 4050 * `--manifest`: only optimize the manifest
4056 4051 * `--no-manifest`: optimize all revlog but the manifest
4057 4052 * `--changelog`: optimize the changelog only
4058 4053 * `--no-changelog --no-manifest`: optimize filelogs only
4059 4054 * `--filelogs`: optimize the filelogs only
4060 4055 * `--no-changelog --no-manifest --no-filelogs`: skip all revlog optimizations
4061 4056 """
4062 4057 return upgrade.upgraderepo(
4063 4058 ui, repo, run=run, optimize=set(optimize), backup=backup, **opts
4064 4059 )
4065 4060
4066 4061
4067 4062 @command(
4068 4063 b'debugwalk', cmdutil.walkopts, _(b'[OPTION]... [FILE]...'), inferrepo=True
4069 4064 )
4070 4065 def debugwalk(ui, repo, *pats, **opts):
4071 4066 """show how files match on given patterns"""
4072 4067 opts = pycompat.byteskwargs(opts)
4073 4068 m = scmutil.match(repo[None], pats, opts)
4074 4069 if ui.verbose:
4075 4070 ui.writenoi18n(b'* matcher:\n', stringutil.prettyrepr(m), b'\n')
4076 4071 items = list(repo[None].walk(m))
4077 4072 if not items:
4078 4073 return
4079 4074 f = lambda fn: fn
4080 4075 if ui.configbool(b'ui', b'slash') and pycompat.ossep != b'/':
4081 4076 f = lambda fn: util.normpath(fn)
4082 4077 fmt = b'f %%-%ds %%-%ds %%s' % (
4083 4078 max([len(abs) for abs in items]),
4084 4079 max([len(repo.pathto(abs)) for abs in items]),
4085 4080 )
4086 4081 for abs in items:
4087 4082 line = fmt % (
4088 4083 abs,
4089 4084 f(repo.pathto(abs)),
4090 4085 m.exact(abs) and b'exact' or b'',
4091 4086 )
4092 4087 ui.write(b"%s\n" % line.rstrip())
4093 4088
4094 4089
4095 4090 @command(b'debugwhyunstable', [], _(b'REV'))
4096 4091 def debugwhyunstable(ui, repo, rev):
4097 4092 """explain instabilities of a changeset"""
4098 4093 for entry in obsutil.whyunstable(repo, scmutil.revsingle(repo, rev)):
4099 4094 dnodes = b''
4100 4095 if entry.get(b'divergentnodes'):
4101 4096 dnodes = (
4102 4097 b' '.join(
4103 4098 b'%s (%s)' % (ctx.hex(), ctx.phasestr())
4104 4099 for ctx in entry[b'divergentnodes']
4105 4100 )
4106 4101 + b' '
4107 4102 )
4108 4103 ui.write(
4109 4104 b'%s: %s%s %s\n'
4110 4105 % (entry[b'instability'], dnodes, entry[b'reason'], entry[b'node'])
4111 4106 )
4112 4107
4113 4108
4114 4109 @command(
4115 4110 b'debugwireargs',
4116 4111 [
4117 4112 (b'', b'three', b'', b'three'),
4118 4113 (b'', b'four', b'', b'four'),
4119 4114 (b'', b'five', b'', b'five'),
4120 4115 ]
4121 4116 + cmdutil.remoteopts,
4122 4117 _(b'REPO [OPTIONS]... [ONE [TWO]]'),
4123 4118 norepo=True,
4124 4119 )
4125 4120 def debugwireargs(ui, repopath, *vals, **opts):
4126 4121 opts = pycompat.byteskwargs(opts)
4127 4122 repo = hg.peer(ui, opts, repopath)
4128 4123 try:
4129 4124 for opt in cmdutil.remoteopts:
4130 4125 del opts[opt[1]]
4131 4126 args = {}
4132 4127 for k, v in opts.items():
4133 4128 if v:
4134 4129 args[k] = v
4135 4130 args = pycompat.strkwargs(args)
4136 4131 # run twice to check that we don't mess up the stream for the next command
4137 4132 res1 = repo.debugwireargs(*vals, **args)
4138 4133 res2 = repo.debugwireargs(*vals, **args)
4139 4134 ui.write(b"%s\n" % res1)
4140 4135 if res1 != res2:
4141 4136 ui.warn(b"%s\n" % res2)
4142 4137 finally:
4143 4138 repo.close()
4144 4139
4145 4140
4146 4141 def _parsewirelangblocks(fh):
4147 4142 activeaction = None
4148 4143 blocklines = []
4149 4144 lastindent = 0
4150 4145
4151 4146 for line in fh:
4152 4147 line = line.rstrip()
4153 4148 if not line:
4154 4149 continue
4155 4150
4156 4151 if line.startswith(b'#'):
4157 4152 continue
4158 4153
4159 4154 if not line.startswith(b' '):
4160 4155 # New block. Flush previous one.
4161 4156 if activeaction:
4162 4157 yield activeaction, blocklines
4163 4158
4164 4159 activeaction = line
4165 4160 blocklines = []
4166 4161 lastindent = 0
4167 4162 continue
4168 4163
4169 4164 # Else we start with an indent.
4170 4165
4171 4166 if not activeaction:
4172 4167 raise error.Abort(_(b'indented line outside of block'))
4173 4168
4174 4169 indent = len(line) - len(line.lstrip())
4175 4170
4176 4171 # If this line is indented more than the last line, concatenate it.
4177 4172 if indent > lastindent and blocklines:
4178 4173 blocklines[-1] += line.lstrip()
4179 4174 else:
4180 4175 blocklines.append(line)
4181 4176 lastindent = indent
4182 4177
4183 4178 # Flush last block.
4184 4179 if activeaction:
4185 4180 yield activeaction, blocklines
4186 4181
4187 4182
4188 4183 @command(
4189 4184 b'debugwireproto',
4190 4185 [
4191 4186 (b'', b'localssh', False, _(b'start an SSH server for this repo')),
4192 4187 (b'', b'peer', b'', _(b'construct a specific version of the peer')),
4193 4188 (
4194 4189 b'',
4195 4190 b'noreadstderr',
4196 4191 False,
4197 4192 _(b'do not read from stderr of the remote'),
4198 4193 ),
4199 4194 (
4200 4195 b'',
4201 4196 b'nologhandshake',
4202 4197 False,
4203 4198 _(b'do not log I/O related to the peer handshake'),
4204 4199 ),
4205 4200 ]
4206 4201 + cmdutil.remoteopts,
4207 4202 _(b'[PATH]'),
4208 4203 optionalrepo=True,
4209 4204 )
4210 4205 def debugwireproto(ui, repo, path=None, **opts):
4211 4206 """send wire protocol commands to a server
4212 4207
4213 4208 This command can be used to issue wire protocol commands to remote
4214 4209 peers and to debug the raw data being exchanged.
4215 4210
4216 4211 ``--localssh`` will start an SSH server against the current repository
4217 4212 and connect to that. By default, the connection will perform a handshake
4218 4213 and establish an appropriate peer instance.
4219 4214
4220 4215 ``--peer`` can be used to bypass the handshake protocol and construct a
4221 4216 peer instance using the specified class type. Valid values are ``raw``,
4222 4217 ``ssh1``. ``raw`` instances only allow sending raw data payloads and
4223 4218 don't support higher-level command actions.
4224 4219
4225 4220 ``--noreadstderr`` can be used to disable automatic reading from stderr
4226 4221 of the peer (for SSH connections only). Disabling automatic reading of
4227 4222 stderr is useful for making output more deterministic.
4228 4223
4229 4224 Commands are issued via a mini language which is specified via stdin.
4230 4225 The language consists of individual actions to perform. An action is
4231 4226 defined by a block. A block is defined as a line with no leading
4232 4227 space followed by 0 or more lines with leading space. Blocks are
4233 4228 effectively a high-level command with additional metadata.
4234 4229
4235 4230 Lines beginning with ``#`` are ignored.
4236 4231
4237 4232 The following sections denote available actions.
4238 4233
4239 4234 raw
4240 4235 ---
4241 4236
4242 4237 Send raw data to the server.
4243 4238
4244 4239 The block payload contains the raw data to send as one atomic send
4245 4240 operation. The data may not actually be delivered in a single system
4246 4241 call: it depends on the abilities of the transport being used.
4247 4242
4248 4243 Each line in the block is de-indented and concatenated. Then, that
4249 4244 value is evaluated as a Python b'' literal. This allows the use of
4250 4245 backslash escaping, etc.
4251 4246
4252 4247 raw+
4253 4248 ----
4254 4249
4255 4250 Behaves like ``raw`` except flushes output afterwards.
4256 4251
4257 4252 command <X>
4258 4253 -----------
4259 4254
4260 4255 Send a request to run a named command, whose name follows the ``command``
4261 4256 string.
4262 4257
4263 4258 Arguments to the command are defined as lines in this block. The format of
4264 4259 each line is ``<key> <value>``. e.g.::
4265 4260
4266 4261 command listkeys
4267 4262 namespace bookmarks
4268 4263
4269 4264 If the value begins with ``eval:``, it will be interpreted as a Python
4270 4265 literal expression. Otherwise values are interpreted as Python b'' literals.
4271 4266 This allows sending complex types and encoding special byte sequences via
4272 4267 backslash escaping.
4273 4268
4274 4269 The following arguments have special meaning:
4275 4270
4276 4271 ``PUSHFILE``
4277 4272 When defined, the *push* mechanism of the peer will be used instead
4278 4273 of the static request-response mechanism and the content of the
4279 4274 file specified in the value of this argument will be sent as the
4280 4275 command payload.
4281 4276
4282 4277 This can be used to submit a local bundle file to the remote.
4283 4278
4284 4279 batchbegin
4285 4280 ----------
4286 4281
4287 4282 Instruct the peer to begin a batched send.
4288 4283
4289 4284 All ``command`` blocks are queued for execution until the next
4290 4285 ``batchsubmit`` block.
4291 4286
4292 4287 batchsubmit
4293 4288 -----------
4294 4289
4295 4290 Submit previously queued ``command`` blocks as a batch request.
4296 4291
4297 4292 This action MUST be paired with a ``batchbegin`` action.
4298 4293
4299 4294 httprequest <method> <path>
4300 4295 ---------------------------
4301 4296
4302 4297 (HTTP peer only)
4303 4298
4304 4299 Send an HTTP request to the peer.
4305 4300
4306 4301 The HTTP request line follows the ``httprequest`` action. e.g. ``GET /foo``.
4307 4302
4308 4303 Arguments of the form ``<key>: <value>`` are interpreted as HTTP request
4309 4304 headers to add to the request. e.g. ``Accept: foo``.
4310 4305
4311 4306 The following arguments are special:
4312 4307
4313 4308 ``BODYFILE``
4314 4309 The content of the file defined as the value to this argument will be
4315 4310 transferred verbatim as the HTTP request body.
4316 4311
4317 4312 ``frame <type> <flags> <payload>``
4318 4313 Send a unified protocol frame as part of the request body.
4319 4314
4320 4315 All frames will be collected and sent as the body to the HTTP
4321 4316 request.
4322 4317
4323 4318 close
4324 4319 -----
4325 4320
4326 4321 Close the connection to the server.
4327 4322
4328 4323 flush
4329 4324 -----
4330 4325
4331 4326 Flush data written to the server.
4332 4327
4333 4328 readavailable
4334 4329 -------------
4335 4330
4336 4331 Close the write end of the connection and read all available data from
4337 4332 the server.
4338 4333
4339 4334 If the connection to the server encompasses multiple pipes, we poll both
4340 4335 pipes and read available data.
4341 4336
4342 4337 readline
4343 4338 --------
4344 4339
4345 4340 Read a line of output from the server. If there are multiple output
4346 4341 pipes, reads only the main pipe.
4347 4342
4348 4343 ereadline
4349 4344 ---------
4350 4345
4351 4346 Like ``readline``, but read from the stderr pipe, if available.
4352 4347
4353 4348 read <X>
4354 4349 --------
4355 4350
4356 4351 ``read()`` N bytes from the server's main output pipe.
4357 4352
4358 4353 eread <X>
4359 4354 ---------
4360 4355
4361 4356 ``read()`` N bytes from the server's stderr pipe, if available.
4362 4357
4363 4358 Specifying Unified Frame-Based Protocol Frames
4364 4359 ----------------------------------------------
4365 4360
4366 4361 It is possible to emit a *Unified Frame-Based Protocol* by using special
4367 4362 syntax.
4368 4363
4369 4364 A frame is composed as a type, flags, and payload. These can be parsed
4370 4365 from a string of the form:
4371 4366
4372 4367 <request-id> <stream-id> <stream-flags> <type> <flags> <payload>
4373 4368
4374 4369 ``request-id`` and ``stream-id`` are integers defining the request and
4375 4370 stream identifiers.
4376 4371
4377 4372 ``type`` can be an integer value for the frame type or the string name
4378 4373 of the type. The strings are defined in ``wireprotoframing.py``. e.g.
4379 4374 ``command-name``.
4380 4375
4381 4376 ``stream-flags`` and ``flags`` are a ``|`` delimited list of flag
4382 4377 components. Each component (and there can be just one) can be an integer
4383 4378 or a flag name for stream flags or frame flags, respectively. Values are
4384 4379 resolved to integers and then bitwise OR'd together.
4385 4380
4386 4381 ``payload`` represents the raw frame payload. If it begins with
4387 4382 ``cbor:``, the following string is evaluated as Python code and the
4388 4383 resulting object is fed into a CBOR encoder. Otherwise it is interpreted
4389 4384 as a Python byte string literal.
4390 4385 """
4391 4386 opts = pycompat.byteskwargs(opts)
4392 4387
4393 4388 if opts[b'localssh'] and not repo:
4394 4389 raise error.Abort(_(b'--localssh requires a repository'))
4395 4390
4396 4391 if opts[b'peer'] and opts[b'peer'] not in (
4397 4392 b'raw',
4398 4393 b'ssh1',
4399 4394 ):
4400 4395 raise error.Abort(
4401 4396 _(b'invalid value for --peer'),
4402 4397 hint=_(b'valid values are "raw" and "ssh1"'),
4403 4398 )
4404 4399
4405 4400 if path and opts[b'localssh']:
4406 4401 raise error.Abort(_(b'cannot specify --localssh with an explicit path'))
4407 4402
4408 4403 if ui.interactive():
4409 4404 ui.write(_(b'(waiting for commands on stdin)\n'))
4410 4405
4411 4406 blocks = list(_parsewirelangblocks(ui.fin))
4412 4407
4413 4408 proc = None
4414 4409 stdin = None
4415 4410 stdout = None
4416 4411 stderr = None
4417 4412 opener = None
4418 4413
4419 4414 if opts[b'localssh']:
4420 4415 # We start the SSH server in its own process so there is process
4421 4416 # separation. This prevents a whole class of potential bugs around
4422 4417 # shared state from interfering with server operation.
4423 4418 args = procutil.hgcmd() + [
4424 4419 b'-R',
4425 4420 repo.root,
4426 4421 b'debugserve',
4427 4422 b'--sshstdio',
4428 4423 ]
4429 4424 proc = subprocess.Popen(
4430 4425 pycompat.rapply(procutil.tonativestr, args),
4431 4426 stdin=subprocess.PIPE,
4432 4427 stdout=subprocess.PIPE,
4433 4428 stderr=subprocess.PIPE,
4434 4429 bufsize=0,
4435 4430 )
4436 4431
4437 4432 stdin = proc.stdin
4438 4433 stdout = proc.stdout
4439 4434 stderr = proc.stderr
4440 4435
4441 4436 # We turn the pipes into observers so we can log I/O.
4442 4437 if ui.verbose or opts[b'peer'] == b'raw':
4443 4438 stdin = util.makeloggingfileobject(
4444 4439 ui, proc.stdin, b'i', logdata=True
4445 4440 )
4446 4441 stdout = util.makeloggingfileobject(
4447 4442 ui, proc.stdout, b'o', logdata=True
4448 4443 )
4449 4444 stderr = util.makeloggingfileobject(
4450 4445 ui, proc.stderr, b'e', logdata=True
4451 4446 )
4452 4447
4453 4448 # --localssh also implies the peer connection settings.
4454 4449
4455 4450 url = b'ssh://localserver'
4456 4451 autoreadstderr = not opts[b'noreadstderr']
4457 4452
4458 4453 if opts[b'peer'] == b'ssh1':
4459 4454 ui.write(_(b'creating ssh peer for wire protocol version 1\n'))
4460 4455 peer = sshpeer.sshv1peer(
4461 4456 ui,
4462 4457 url,
4463 4458 proc,
4464 4459 stdin,
4465 4460 stdout,
4466 4461 stderr,
4467 4462 None,
4468 4463 autoreadstderr=autoreadstderr,
4469 4464 )
4470 4465 elif opts[b'peer'] == b'raw':
4471 4466 ui.write(_(b'using raw connection to peer\n'))
4472 4467 peer = None
4473 4468 else:
4474 4469 ui.write(_(b'creating ssh peer from handshake results\n'))
4475 4470 peer = sshpeer.makepeer(
4476 4471 ui,
4477 4472 url,
4478 4473 proc,
4479 4474 stdin,
4480 4475 stdout,
4481 4476 stderr,
4482 4477 autoreadstderr=autoreadstderr,
4483 4478 )
4484 4479
4485 4480 elif path:
4486 4481 # We bypass hg.peer() so we can proxy the sockets.
4487 4482 # TODO consider not doing this because we skip
4488 4483 # ``hg.wirepeersetupfuncs`` and potentially other useful functionality.
4489 4484 u = urlutil.url(path)
4490 4485 if u.scheme != b'http':
4491 4486 raise error.Abort(_(b'only http:// paths are currently supported'))
4492 4487
4493 4488 url, authinfo = u.authinfo()
4494 4489 openerargs = {
4495 4490 'useragent': b'Mercurial debugwireproto',
4496 4491 }
4497 4492
4498 4493 # Turn pipes/sockets into observers so we can log I/O.
4499 4494 if ui.verbose:
4500 4495 openerargs.update(
4501 4496 {
4502 4497 'loggingfh': ui,
4503 4498 'loggingname': b's',
4504 4499 'loggingopts': {
4505 4500 'logdata': True,
4506 4501 'logdataapis': False,
4507 4502 },
4508 4503 }
4509 4504 )
4510 4505
4511 4506 if ui.debugflag:
4512 4507 openerargs['loggingopts']['logdataapis'] = True
4513 4508
4514 4509 # Don't send default headers when in raw mode. This allows us to
4515 4510 # bypass most of the behavior of our URL handling code so we can
4516 4511 # have near complete control over what's sent on the wire.
4517 4512 if opts[b'peer'] == b'raw':
4518 4513 openerargs['sendaccept'] = False
4519 4514
4520 4515 opener = urlmod.opener(ui, authinfo, **openerargs)
4521 4516
4522 4517 if opts[b'peer'] == b'raw':
4523 4518 ui.write(_(b'using raw connection to peer\n'))
4524 4519 peer = None
4525 4520 elif opts[b'peer']:
4526 4521 raise error.Abort(
4527 4522 _(b'--peer %s not supported with HTTP peers') % opts[b'peer']
4528 4523 )
4529 4524 else:
4530 4525 peer_path = urlutil.try_path(ui, path)
4531 4526 peer = httppeer.makepeer(ui, peer_path, opener=opener)
4532 4527
4533 4528 # We /could/ populate stdin/stdout with sock.makefile()...
4534 4529 else:
4535 4530 raise error.Abort(_(b'unsupported connection configuration'))
4536 4531
4537 4532 batchedcommands = None
4538 4533
4539 4534 # Now perform actions based on the parsed wire language instructions.
4540 4535 for action, lines in blocks:
4541 4536 if action in (b'raw', b'raw+'):
4542 4537 if not stdin:
4543 4538 raise error.Abort(_(b'cannot call raw/raw+ on this peer'))
4544 4539
4545 4540 # Concatenate the data together.
4546 4541 data = b''.join(l.lstrip() for l in lines)
4547 4542 data = stringutil.unescapestr(data)
4548 4543 stdin.write(data)
4549 4544
4550 4545 if action == b'raw+':
4551 4546 stdin.flush()
4552 4547 elif action == b'flush':
4553 4548 if not stdin:
4554 4549 raise error.Abort(_(b'cannot call flush on this peer'))
4555 4550 stdin.flush()
4556 4551 elif action.startswith(b'command'):
4557 4552 if not peer:
4558 4553 raise error.Abort(
4559 4554 _(
4560 4555 b'cannot send commands unless peer instance '
4561 4556 b'is available'
4562 4557 )
4563 4558 )
4564 4559
4565 4560 command = action.split(b' ', 1)[1]
4566 4561
4567 4562 args = {}
4568 4563 for line in lines:
4569 4564 # We need to allow empty values.
4570 4565 fields = line.lstrip().split(b' ', 1)
4571 4566 if len(fields) == 1:
4572 4567 key = fields[0]
4573 4568 value = b''
4574 4569 else:
4575 4570 key, value = fields
4576 4571
4577 4572 if value.startswith(b'eval:'):
4578 4573 value = stringutil.evalpythonliteral(value[5:])
4579 4574 else:
4580 4575 value = stringutil.unescapestr(value)
4581 4576
4582 4577 args[key] = value
4583 4578
4584 4579 if batchedcommands is not None:
4585 4580 batchedcommands.append((command, args))
4586 4581 continue
4587 4582
4588 4583 ui.status(_(b'sending %s command\n') % command)
4589 4584
4590 4585 if b'PUSHFILE' in args:
4591 4586 with open(args[b'PUSHFILE'], 'rb') as fh:
4592 4587 del args[b'PUSHFILE']
4593 4588 res, output = peer._callpush(
4594 4589 command, fh, **pycompat.strkwargs(args)
4595 4590 )
4596 4591 ui.status(_(b'result: %s\n') % stringutil.escapestr(res))
4597 4592 ui.status(
4598 4593 _(b'remote output: %s\n') % stringutil.escapestr(output)
4599 4594 )
4600 4595 else:
4601 4596 with peer.commandexecutor() as e:
4602 4597 res = e.callcommand(command, args).result()
4603 4598
4604 4599 ui.status(
4605 4600 _(b'response: %s\n')
4606 4601 % stringutil.pprint(res, bprefix=True, indent=2)
4607 4602 )
4608 4603
4609 4604 elif action == b'batchbegin':
4610 4605 if batchedcommands is not None:
4611 4606 raise error.Abort(_(b'nested batchbegin not allowed'))
4612 4607
4613 4608 batchedcommands = []
4614 4609 elif action == b'batchsubmit':
4615 4610 # There is a batching API we could go through. But it would be
4616 4611 # difficult to normalize requests into function calls. It is easier
4617 4612 # to bypass this layer and normalize to commands + args.
4618 4613 ui.status(
4619 4614 _(b'sending batch with %d sub-commands\n')
4620 4615 % len(batchedcommands)
4621 4616 )
4622 4617 assert peer is not None
4623 4618 for i, chunk in enumerate(peer._submitbatch(batchedcommands)):
4624 4619 ui.status(
4625 4620 _(b'response #%d: %s\n') % (i, stringutil.escapestr(chunk))
4626 4621 )
4627 4622
4628 4623 batchedcommands = None
4629 4624
4630 4625 elif action.startswith(b'httprequest '):
4631 4626 if not opener:
4632 4627 raise error.Abort(
4633 4628 _(b'cannot use httprequest without an HTTP peer')
4634 4629 )
4635 4630
4636 4631 request = action.split(b' ', 2)
4637 4632 if len(request) != 3:
4638 4633 raise error.Abort(
4639 4634 _(
4640 4635 b'invalid httprequest: expected format is '
4641 4636 b'"httprequest <method> <path>'
4642 4637 )
4643 4638 )
4644 4639
4645 4640 method, httppath = request[1:]
4646 4641 headers = {}
4647 4642 body = None
4648 4643 frames = []
4649 4644 for line in lines:
4650 4645 line = line.lstrip()
4651 4646 m = re.match(b'^([a-zA-Z0-9_-]+): (.*)$', line)
4652 4647 if m:
4653 4648 # Headers need to use native strings.
4654 4649 key = pycompat.strurl(m.group(1))
4655 4650 value = pycompat.strurl(m.group(2))
4656 4651 headers[key] = value
4657 4652 continue
4658 4653
4659 4654 if line.startswith(b'BODYFILE '):
4660 4655 with open(line.split(b' ', 1), b'rb') as fh:
4661 4656 body = fh.read()
4662 4657 elif line.startswith(b'frame '):
4663 4658 frame = wireprotoframing.makeframefromhumanstring(
4664 4659 line[len(b'frame ') :]
4665 4660 )
4666 4661
4667 4662 frames.append(frame)
4668 4663 else:
4669 4664 raise error.Abort(
4670 4665 _(b'unknown argument to httprequest: %s') % line
4671 4666 )
4672 4667
4673 4668 url = path + httppath
4674 4669
4675 4670 if frames:
4676 4671 body = b''.join(bytes(f) for f in frames)
4677 4672
4678 4673 req = urlmod.urlreq.request(pycompat.strurl(url), body, headers)
4679 4674
4680 4675 # urllib.Request insists on using has_data() as a proxy for
4681 4676 # determining the request method. Override that to use our
4682 4677 # explicitly requested method.
4683 4678 req.get_method = lambda: pycompat.sysstr(method)
4684 4679
4685 4680 try:
4686 4681 res = opener.open(req)
4687 4682 body = res.read()
4688 4683 except util.urlerr.urlerror as e:
4689 4684 # read() method must be called, but only exists in Python 2
4690 4685 getattr(e, 'read', lambda: None)()
4691 4686 continue
4692 4687
4693 4688 ct = res.headers.get('Content-Type')
4694 4689 if ct == 'application/mercurial-cbor':
4695 4690 ui.write(
4696 4691 _(b'cbor> %s\n')
4697 4692 % stringutil.pprint(
4698 4693 cborutil.decodeall(body), bprefix=True, indent=2
4699 4694 )
4700 4695 )
4701 4696
4702 4697 elif action == b'close':
4703 4698 assert peer is not None
4704 4699 peer.close()
4705 4700 elif action == b'readavailable':
4706 4701 if not stdout or not stderr:
4707 4702 raise error.Abort(
4708 4703 _(b'readavailable not available on this peer')
4709 4704 )
4710 4705
4711 4706 stdin.close()
4712 4707 stdout.read()
4713 4708 stderr.read()
4714 4709
4715 4710 elif action == b'readline':
4716 4711 if not stdout:
4717 4712 raise error.Abort(_(b'readline not available on this peer'))
4718 4713 stdout.readline()
4719 4714 elif action == b'ereadline':
4720 4715 if not stderr:
4721 4716 raise error.Abort(_(b'ereadline not available on this peer'))
4722 4717 stderr.readline()
4723 4718 elif action.startswith(b'read '):
4724 4719 count = int(action.split(b' ', 1)[1])
4725 4720 if not stdout:
4726 4721 raise error.Abort(_(b'read not available on this peer'))
4727 4722 stdout.read(count)
4728 4723 elif action.startswith(b'eread '):
4729 4724 count = int(action.split(b' ', 1)[1])
4730 4725 if not stderr:
4731 4726 raise error.Abort(_(b'eread not available on this peer'))
4732 4727 stderr.read(count)
4733 4728 else:
4734 4729 raise error.Abort(_(b'unknown action: %s') % action)
4735 4730
4736 4731 if batchedcommands is not None:
4737 4732 raise error.Abort(_(b'unclosed "batchbegin" request'))
4738 4733
4739 4734 if peer:
4740 4735 peer.close()
4741 4736
4742 4737 if proc:
4743 4738 proc.kill()
@@ -1,587 +1,621 b''
1 1 # verify.py - repository integrity checking for Mercurial
2 2 #
3 3 # Copyright 2006, 2007 Olivia Mackall <olivia@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
9 9 import os
10 10
11 11 from .i18n import _
12 12 from .node import short
13 13 from .utils import stringutil
14 14
15 15 from . import (
16 16 error,
17 17 pycompat,
18 requirements,
18 19 revlog,
19 20 util,
20 21 )
21 22
22 23 VERIFY_DEFAULT = 0
23 24 VERIFY_FULL = 1
24 25
25 26
26 27 def verify(repo, level=None):
27 28 with repo.lock():
28 29 v = verifier(repo, level)
29 30 return v.verify()
30 31
31 32
32 33 def _normpath(f):
33 34 # under hg < 2.4, convert didn't sanitize paths properly, so a
34 35 # converted repo may contain repeated slashes
35 36 while b'//' in f:
36 37 f = f.replace(b'//', b'/')
37 38 return f
38 39
39 40
40 41 HINT_FNCACHE = _(
41 42 b'hint: run "hg debugrebuildfncache" to recover from corrupt fncache\n'
42 43 )
43 44
44 45 WARN_PARENT_DIR_UNKNOWN_REV = _(
45 46 b"parent-directory manifest refers to unknown revision %s"
46 47 )
47 48
48 49 WARN_UNKNOWN_COPY_SOURCE = _(
49 50 b"warning: copy source of '%s' not in parents of %s"
50 51 )
51 52
52 53 WARN_NULLID_COPY_SOURCE = _(
53 54 b"warning: %s@%s: copy source revision is nullid %s:%s\n"
54 55 )
55 56
56 57
57 58 class verifier:
58 59 def __init__(self, repo, level=None):
59 60 self.repo = repo.unfiltered()
60 61 self.ui = repo.ui
61 62 self.match = repo.narrowmatch()
62 63 if level is None:
63 64 level = VERIFY_DEFAULT
64 65 self._level = level
65 66 self.badrevs = set()
66 67 self.errors = 0
67 68 self.warnings = 0
68 69 self.havecl = len(repo.changelog) > 0
69 70 self.havemf = len(repo.manifestlog.getstorage(b'')) > 0
70 71 self.revlogv1 = repo.changelog._format_version != revlog.REVLOGV0
71 72 self.lrugetctx = util.lrucachefunc(repo.unfiltered().__getitem__)
72 73 self.refersmf = False
73 74 self.fncachewarned = False
74 75 # developer config: verify.skipflags
75 76 self.skipflags = repo.ui.configint(b'verify', b'skipflags')
76 77 self.warnorphanstorefiles = True
77 78
78 79 def _warn(self, msg):
79 80 """record a "warning" level issue"""
80 81 self.ui.warn(msg + b"\n")
81 82 self.warnings += 1
82 83
83 84 def _err(self, linkrev, msg, filename=None):
84 85 """record a "error" level issue"""
85 86 if linkrev is not None:
86 87 self.badrevs.add(linkrev)
87 88 linkrev = b"%d" % linkrev
88 89 else:
89 90 linkrev = b'?'
90 91 msg = b"%s: %s" % (linkrev, msg)
91 92 if filename:
92 93 msg = b"%s@%s" % (filename, msg)
93 94 self.ui.warn(b" " + msg + b"\n")
94 95 self.errors += 1
95 96
96 97 def _exc(self, linkrev, msg, inst, filename=None):
97 98 """record exception raised during the verify process"""
98 99 fmsg = stringutil.forcebytestr(inst)
99 100 if not fmsg:
100 101 fmsg = pycompat.byterepr(inst)
101 102 self._err(linkrev, b"%s: %s" % (msg, fmsg), filename)
102 103
103 104 def _checkrevlog(self, obj, name, linkrev):
104 105 """verify high level property of a revlog
105 106
106 107 - revlog is present,
107 108 - revlog is non-empty,
108 109 - sizes (index and data) are correct,
109 110 - revlog's format version is correct.
110 111 """
111 112 if not len(obj) and (self.havecl or self.havemf):
112 113 self._err(linkrev, _(b"empty or missing %s") % name)
113 114 return
114 115
115 116 d = obj.checksize()
116 117 if d[0]:
117 118 self._err(None, _(b"data length off by %d bytes") % d[0], name)
118 119 if d[1]:
119 120 self._err(None, _(b"index contains %d extra bytes") % d[1], name)
120 121
121 122 if obj._format_version != revlog.REVLOGV0:
122 123 if not self.revlogv1:
123 124 self._warn(_(b"warning: `%s' uses revlog format 1") % name)
124 125 elif self.revlogv1:
125 126 self._warn(_(b"warning: `%s' uses revlog format 0") % name)
126 127
127 128 def _checkentry(self, obj, i, node, seen, linkrevs, f):
128 129 """verify a single revlog entry
129 130
130 131 arguments are:
131 132 - obj: the source revlog
132 133 - i: the revision number
133 134 - node: the revision node id
134 135 - seen: nodes previously seen for this revlog
135 136 - linkrevs: [changelog-revisions] introducing "node"
136 137 - f: string label ("changelog", "manifest", or filename)
137 138
138 139 Performs the following checks:
139 140 - linkrev points to an existing changelog revision,
140 141 - linkrev points to a changelog revision that introduces this revision,
141 142 - linkrev points to the lowest of these changesets,
142 143 - both parents exist in the revlog,
143 144 - the revision is not duplicated.
144 145
145 146 Return the linkrev of the revision (or None for changelog's revisions).
146 147 """
147 148 lr = obj.linkrev(obj.rev(node))
148 149 if lr < 0 or (self.havecl and lr not in linkrevs):
149 150 if lr < 0 or lr >= len(self.repo.changelog):
150 151 msg = _(b"rev %d points to nonexistent changeset %d")
151 152 else:
152 153 msg = _(b"rev %d points to unexpected changeset %d")
153 154 self._err(None, msg % (i, lr), f)
154 155 if linkrevs:
155 156 if f and len(linkrevs) > 1:
156 157 try:
157 158 # attempt to filter down to real linkrevs
158 159 linkrevs = []
159 160 for lr in linkrevs:
160 161 if self.lrugetctx(lr)[f].filenode() == node:
161 162 linkrevs.append(lr)
162 163 except Exception:
163 164 pass
164 165 msg = _(b" (expected %s)")
165 166 msg %= b" ".join(map(pycompat.bytestr, linkrevs))
166 167 self._warn(msg)
167 168 lr = None # can't be trusted
168 169
169 170 try:
170 171 p1, p2 = obj.parents(node)
171 172 if p1 not in seen and p1 != self.repo.nullid:
172 173 msg = _(b"unknown parent 1 %s of %s") % (short(p1), short(node))
173 174 self._err(lr, msg, f)
174 175 if p2 not in seen and p2 != self.repo.nullid:
175 176 msg = _(b"unknown parent 2 %s of %s") % (short(p2), short(node))
176 177 self._err(lr, msg, f)
177 178 except Exception as inst:
178 179 self._exc(lr, _(b"checking parents of %s") % short(node), inst, f)
179 180
180 181 if node in seen:
181 182 self._err(lr, _(b"duplicate revision %d (%d)") % (i, seen[node]), f)
182 183 seen[node] = i
183 184 return lr
184 185
185 186 def verify(self):
186 187 """verify the content of the Mercurial repository
187 188
188 189 This method run all verifications, displaying issues as they are found.
189 190
190 191 return 1 if any error have been encountered, 0 otherwise."""
191 192 # initial validation and generic report
192 193 repo = self.repo
193 194 ui = repo.ui
194 195 if not repo.url().startswith(b'file:'):
195 196 raise error.Abort(_(b"cannot verify bundle or remote repos"))
196 197
197 198 if os.path.exists(repo.sjoin(b"journal")):
198 199 ui.warn(_(b"abandoned transaction found - run hg recover\n"))
199 200
200 201 if ui.verbose or not self.revlogv1:
201 202 ui.status(
202 203 _(b"repository uses revlog format %d\n")
203 204 % (self.revlogv1 and 1 or 0)
204 205 )
205 206
206 207 # data verification
207 208 mflinkrevs, filelinkrevs = self._verifychangelog()
208 209 filenodes = self._verifymanifest(mflinkrevs)
209 210 del mflinkrevs
210 211 self._crosscheckfiles(filelinkrevs, filenodes)
211 212 totalfiles, filerevisions = self._verifyfiles(filenodes, filelinkrevs)
212 213
214 if self.errors:
215 ui.warn(_(b"not checking dirstate because of previous errors\n"))
216 dirstate_errors = 0
217 else:
218 dirstate_errors = self._verify_dirstate()
219
213 220 # final report
214 221 ui.status(
215 222 _(b"checked %d changesets with %d changes to %d files\n")
216 223 % (len(repo.changelog), filerevisions, totalfiles)
217 224 )
218 225 if self.warnings:
219 226 ui.warn(_(b"%d warnings encountered!\n") % self.warnings)
220 227 if self.fncachewarned:
221 228 ui.warn(HINT_FNCACHE)
222 229 if self.errors:
223 230 ui.warn(_(b"%d integrity errors encountered!\n") % self.errors)
224 231 if self.badrevs:
225 232 msg = _(b"(first damaged changeset appears to be %d)\n")
226 233 msg %= min(self.badrevs)
227 234 ui.warn(msg)
235 if dirstate_errors:
236 ui.warn(
237 _(b"dirstate inconsistent with current parent's manifest\n")
238 )
239 ui.warn(_(b"%d dirstate errors\n") % dirstate_errors)
228 240 return 1
229 241 return 0
230 242
231 243 def _verifychangelog(self):
232 244 """verify the changelog of a repository
233 245
234 246 The following checks are performed:
235 247 - all of `_checkrevlog` checks,
236 248 - all of `_checkentry` checks (for each revisions),
237 249 - each revision can be read.
238 250
239 251 The function returns some of the data observed in the changesets as a
240 252 (mflinkrevs, filelinkrevs) tuples:
241 253 - mflinkrevs: is a { manifest-node -> [changelog-rev] } mapping
242 254 - filelinkrevs: is a { file-path -> [changelog-rev] } mapping
243 255
244 256 If a matcher was specified, filelinkrevs will only contains matched
245 257 files.
246 258 """
247 259 ui = self.ui
248 260 repo = self.repo
249 261 match = self.match
250 262 cl = repo.changelog
251 263
252 264 ui.status(_(b"checking changesets\n"))
253 265 mflinkrevs = {}
254 266 filelinkrevs = {}
255 267 seen = {}
256 268 self._checkrevlog(cl, b"changelog", 0)
257 269 progress = ui.makeprogress(
258 270 _(b'checking'), unit=_(b'changesets'), total=len(repo)
259 271 )
260 272 for i in repo:
261 273 progress.update(i)
262 274 n = cl.node(i)
263 275 self._checkentry(cl, i, n, seen, [i], b"changelog")
264 276
265 277 try:
266 278 changes = cl.read(n)
267 279 if changes[0] != self.repo.nullid:
268 280 mflinkrevs.setdefault(changes[0], []).append(i)
269 281 self.refersmf = True
270 282 for f in changes[3]:
271 283 if match(f):
272 284 filelinkrevs.setdefault(_normpath(f), []).append(i)
273 285 except Exception as inst:
274 286 self.refersmf = True
275 287 self._exc(i, _(b"unpacking changeset %s") % short(n), inst)
276 288 progress.complete()
277 289 return mflinkrevs, filelinkrevs
278 290
279 291 def _verifymanifest(
280 292 self, mflinkrevs, dir=b"", storefiles=None, subdirprogress=None
281 293 ):
282 294 """verify the manifestlog content
283 295
284 296 Inputs:
285 297 - mflinkrevs: a {manifest-node -> [changelog-revisions]} mapping
286 298 - dir: a subdirectory to check (for tree manifest repo)
287 299 - storefiles: set of currently "orphan" files.
288 300 - subdirprogress: a progress object
289 301
290 302 This function checks:
291 303 * all of `_checkrevlog` checks (for all manifest related revlogs)
292 304 * all of `_checkentry` checks (for all manifest related revisions)
293 305 * nodes for subdirectory exists in the sub-directory manifest
294 306 * each manifest entries have a file path
295 307 * each manifest node refered in mflinkrevs exist in the manifest log
296 308
297 309 If tree manifest is in use and a matchers is specified, only the
298 310 sub-directories matching it will be verified.
299 311
300 312 return a two level mapping:
301 313 {"path" -> { filenode -> changelog-revision}}
302 314
303 315 This mapping primarily contains entries for every files in the
304 316 repository. In addition, when tree-manifest is used, it also contains
305 317 sub-directory entries.
306 318
307 319 If a matcher is provided, only matching paths will be included.
308 320 """
309 321 repo = self.repo
310 322 ui = self.ui
311 323 match = self.match
312 324 mfl = self.repo.manifestlog
313 325 mf = mfl.getstorage(dir)
314 326
315 327 if not dir:
316 328 self.ui.status(_(b"checking manifests\n"))
317 329
318 330 filenodes = {}
319 331 subdirnodes = {}
320 332 seen = {}
321 333 label = b"manifest"
322 334 if dir:
323 335 label = dir
324 336 revlogfiles = mf.files()
325 337 storefiles.difference_update(revlogfiles)
326 338 if subdirprogress: # should be true since we're in a subdirectory
327 339 subdirprogress.increment()
328 340 if self.refersmf:
329 341 # Do not check manifest if there are only changelog entries with
330 342 # null manifests.
331 343 self._checkrevlog(mf._revlog, label, 0)
332 344 progress = ui.makeprogress(
333 345 _(b'checking'), unit=_(b'manifests'), total=len(mf)
334 346 )
335 347 for i in mf:
336 348 if not dir:
337 349 progress.update(i)
338 350 n = mf.node(i)
339 351 lr = self._checkentry(mf, i, n, seen, mflinkrevs.get(n, []), label)
340 352 if n in mflinkrevs:
341 353 del mflinkrevs[n]
342 354 elif dir:
343 355 msg = _(b"%s not in parent-directory manifest") % short(n)
344 356 self._err(lr, msg, label)
345 357 else:
346 358 self._err(lr, _(b"%s not in changesets") % short(n), label)
347 359
348 360 try:
349 361 mfdelta = mfl.get(dir, n).readdelta(shallow=True)
350 362 for f, fn, fl in mfdelta.iterentries():
351 363 if not f:
352 364 self._err(lr, _(b"entry without name in manifest"))
353 365 elif f == b"/dev/null": # ignore this in very old repos
354 366 continue
355 367 fullpath = dir + _normpath(f)
356 368 if fl == b't':
357 369 if not match.visitdir(fullpath):
358 370 continue
359 371 sdn = subdirnodes.setdefault(fullpath + b'/', {})
360 372 sdn.setdefault(fn, []).append(lr)
361 373 else:
362 374 if not match(fullpath):
363 375 continue
364 376 filenodes.setdefault(fullpath, {}).setdefault(fn, lr)
365 377 except Exception as inst:
366 378 self._exc(lr, _(b"reading delta %s") % short(n), inst, label)
367 379 if self._level >= VERIFY_FULL:
368 380 try:
369 381 # Various issues can affect manifest. So we read each full
370 382 # text from storage. This triggers the checks from the core
371 383 # code (eg: hash verification, filename are ordered, etc.)
372 384 mfdelta = mfl.get(dir, n).read()
373 385 except Exception as inst:
374 386 msg = _(b"reading full manifest %s") % short(n)
375 387 self._exc(lr, msg, inst, label)
376 388
377 389 if not dir:
378 390 progress.complete()
379 391
380 392 if self.havemf:
381 393 # since we delete entry in `mflinkrevs` during iteration, any
382 394 # remaining entries are "missing". We need to issue errors for them.
383 395 changesetpairs = [(c, m) for m in mflinkrevs for c in mflinkrevs[m]]
384 396 for c, m in sorted(changesetpairs):
385 397 if dir:
386 398 self._err(c, WARN_PARENT_DIR_UNKNOWN_REV % short(m), label)
387 399 else:
388 400 msg = _(b"changeset refers to unknown revision %s")
389 401 msg %= short(m)
390 402 self._err(c, msg, label)
391 403
392 404 if not dir and subdirnodes:
393 405 self.ui.status(_(b"checking directory manifests\n"))
394 406 storefiles = set()
395 407 subdirs = set()
396 408 revlogv1 = self.revlogv1
397 409 undecodable = []
398 410 for t, f, size in repo.store.datafiles(undecodable=undecodable):
399 411 if (size > 0 or not revlogv1) and f.startswith(b'meta/'):
400 412 storefiles.add(_normpath(f))
401 413 subdirs.add(os.path.dirname(f))
402 414 for f in undecodable:
403 415 self._err(None, _(b"cannot decode filename '%s'") % f)
404 416 subdirprogress = ui.makeprogress(
405 417 _(b'checking'), unit=_(b'manifests'), total=len(subdirs)
406 418 )
407 419
408 420 for subdir, linkrevs in subdirnodes.items():
409 421 subdirfilenodes = self._verifymanifest(
410 422 linkrevs, subdir, storefiles, subdirprogress
411 423 )
412 424 for f, onefilenodes in subdirfilenodes.items():
413 425 filenodes.setdefault(f, {}).update(onefilenodes)
414 426
415 427 if not dir and subdirnodes:
416 428 assert subdirprogress is not None # help pytype
417 429 subdirprogress.complete()
418 430 if self.warnorphanstorefiles:
419 431 for f in sorted(storefiles):
420 432 self._warn(_(b"warning: orphan data file '%s'") % f)
421 433
422 434 return filenodes
423 435
424 436 def _crosscheckfiles(self, filelinkrevs, filenodes):
425 437 repo = self.repo
426 438 ui = self.ui
427 439 ui.status(_(b"crosschecking files in changesets and manifests\n"))
428 440
429 441 total = len(filelinkrevs) + len(filenodes)
430 442 progress = ui.makeprogress(
431 443 _(b'crosschecking'), unit=_(b'files'), total=total
432 444 )
433 445 if self.havemf:
434 446 for f in sorted(filelinkrevs):
435 447 progress.increment()
436 448 if f not in filenodes:
437 449 lr = filelinkrevs[f][0]
438 450 self._err(lr, _(b"in changeset but not in manifest"), f)
439 451
440 452 if self.havecl:
441 453 for f in sorted(filenodes):
442 454 progress.increment()
443 455 if f not in filelinkrevs:
444 456 try:
445 457 fl = repo.file(f)
446 458 lr = min([fl.linkrev(fl.rev(n)) for n in filenodes[f]])
447 459 except Exception:
448 460 lr = None
449 461 self._err(lr, _(b"in manifest but not in changeset"), f)
450 462
451 463 progress.complete()
452 464
453 465 def _verifyfiles(self, filenodes, filelinkrevs):
454 466 repo = self.repo
455 467 ui = self.ui
456 468 lrugetctx = self.lrugetctx
457 469 revlogv1 = self.revlogv1
458 470 havemf = self.havemf
459 471 ui.status(_(b"checking files\n"))
460 472
461 473 storefiles = set()
462 474 undecodable = []
463 475 for t, f, size in repo.store.datafiles(undecodable=undecodable):
464 476 if (size > 0 or not revlogv1) and f.startswith(b'data/'):
465 477 storefiles.add(_normpath(f))
466 478 for f in undecodable:
467 479 self._err(None, _(b"cannot decode filename '%s'") % f)
468 480
469 481 state = {
470 482 # TODO this assumes revlog storage for changelog.
471 483 b'expectedversion': self.repo.changelog._format_version,
472 484 b'skipflags': self.skipflags,
473 485 # experimental config: censor.policy
474 486 b'erroroncensored': ui.config(b'censor', b'policy') == b'abort',
475 487 }
476 488
477 489 files = sorted(set(filenodes) | set(filelinkrevs))
478 490 revisions = 0
479 491 progress = ui.makeprogress(
480 492 _(b'checking'), unit=_(b'files'), total=len(files)
481 493 )
482 494 for i, f in enumerate(files):
483 495 progress.update(i, item=f)
484 496 try:
485 497 linkrevs = filelinkrevs[f]
486 498 except KeyError:
487 499 # in manifest but not in changelog
488 500 linkrevs = []
489 501
490 502 if linkrevs:
491 503 lr = linkrevs[0]
492 504 else:
493 505 lr = None
494 506
495 507 try:
496 508 fl = repo.file(f)
497 509 except error.StorageError as e:
498 510 self._err(lr, _(b"broken revlog! (%s)") % e, f)
499 511 continue
500 512
501 513 for ff in fl.files():
502 514 try:
503 515 storefiles.remove(ff)
504 516 except KeyError:
505 517 if self.warnorphanstorefiles:
506 518 msg = _(b" warning: revlog '%s' not in fncache!")
507 519 self._warn(msg % ff)
508 520 self.fncachewarned = True
509 521
510 522 if not len(fl) and (self.havecl or self.havemf):
511 523 self._err(lr, _(b"empty or missing %s") % f)
512 524 else:
513 525 # Guard against implementations not setting this.
514 526 state[b'skipread'] = set()
515 527 state[b'safe_renamed'] = set()
516 528
517 529 for problem in fl.verifyintegrity(state):
518 530 if problem.node is not None:
519 531 linkrev = fl.linkrev(fl.rev(problem.node))
520 532 else:
521 533 linkrev = None
522 534
523 535 if problem.warning:
524 536 self._warn(problem.warning)
525 537 elif problem.error:
526 538 linkrev_msg = linkrev if linkrev is not None else lr
527 539 self._err(linkrev_msg, problem.error, f)
528 540 else:
529 541 raise error.ProgrammingError(
530 542 b'problem instance does not set warning or error '
531 543 b'attribute: %s' % problem.msg
532 544 )
533 545
534 546 seen = {}
535 547 for i in fl:
536 548 revisions += 1
537 549 n = fl.node(i)
538 550 lr = self._checkentry(fl, i, n, seen, linkrevs, f)
539 551 if f in filenodes:
540 552 if havemf and n not in filenodes[f]:
541 553 self._err(lr, _(b"%s not in manifests") % (short(n)), f)
542 554 else:
543 555 del filenodes[f][n]
544 556
545 557 if n in state[b'skipread'] and n not in state[b'safe_renamed']:
546 558 continue
547 559
548 560 # check renames
549 561 try:
550 562 # This requires resolving fulltext (at least on revlogs,
551 563 # though not with LFS revisions). We may want
552 564 # ``verifyintegrity()`` to pass a set of nodes with
553 565 # rename metadata as an optimization.
554 566 rp = fl.renamed(n)
555 567 if rp:
556 568 if lr is not None and ui.verbose:
557 569 ctx = lrugetctx(lr)
558 570 if not any(rp[0] in pctx for pctx in ctx.parents()):
559 571 self._warn(WARN_UNKNOWN_COPY_SOURCE % (f, ctx))
560 572 fl2 = repo.file(rp[0])
561 573 if not len(fl2):
562 574 m = _(b"empty or missing copy source revlog %s:%s")
563 575 self._err(lr, m % (rp[0], short(rp[1])), f)
564 576 elif rp[1] == self.repo.nullid:
565 577 msg = WARN_NULLID_COPY_SOURCE
566 578 msg %= (f, lr, rp[0], short(rp[1]))
567 579 ui.note(msg)
568 580 else:
569 581 fl2.rev(rp[1])
570 582 except Exception as inst:
571 583 self._exc(
572 584 lr, _(b"checking rename of %s") % short(n), inst, f
573 585 )
574 586
575 587 # cross-check
576 588 if f in filenodes:
577 589 fns = [(v, k) for k, v in filenodes[f].items()]
578 590 for lr, node in sorted(fns):
579 591 msg = _(b"manifest refers to unknown revision %s")
580 592 self._err(lr, msg % short(node), f)
581 593 progress.complete()
582 594
583 595 if self.warnorphanstorefiles:
584 596 for f in sorted(storefiles):
585 597 self._warn(_(b"warning: orphan data file '%s'") % f)
586 598
587 599 return len(files), revisions
600
601 def _verify_dirstate(self):
602 """Check that the dirstate is consistent with the parent's manifest"""
603 repo = self.repo
604 ui = self.ui
605 ui.status(_(b"checking dirstate\n"))
606
607 parent1, parent2 = repo.dirstate.parents()
608 m1 = repo[parent1].manifest()
609 m2 = repo[parent2].manifest()
610 dirstate_errors = 0
611
612 is_narrow = requirements.NARROW_REQUIREMENT in repo.requirements
613 narrow_matcher = repo.narrowmatch() if is_narrow else None
614
615 for err in repo.dirstate.verify(m1, m2, narrow_matcher):
616 ui.error(err)
617 dirstate_errors += 1
618
619 if dirstate_errors:
620 self.errors += dirstate_errors
621 return dirstate_errors
@@ -1,198 +1,199 b''
1 1 #testcases obsstore-off obsstore-on
2 2
3 3 $ cat << EOF >> $HGRCPATH
4 4 > [extensions]
5 5 > amend =
6 6 > EOF
7 7
8 8 #if obsstore-on
9 9 $ cat << EOF >> $HGRCPATH
10 10 > [experimental]
11 11 > evolution.createmarkers = True
12 12 > EOF
13 13 #endif
14 14
15 15 Prepare parent repo
16 16 -------------------
17 17
18 18 $ hg init r
19 19 $ cd r
20 20
21 21 $ echo a > a
22 22 $ hg ci -Am0
23 23 adding a
24 24
25 25 Link first subrepo
26 26 ------------------
27 27
28 28 $ echo 's = s' >> .hgsub
29 29 $ hg add .hgsub
30 30 $ hg init s
31 31
32 32 amend without .hgsub
33 33
34 34 $ hg amend s
35 35 abort: can't commit subrepos without .hgsub
36 36 [255]
37 37
38 38 amend with subrepo
39 39
40 40 $ hg amend
41 41 saved backup bundle to * (glob) (obsstore-off !)
42 42 $ hg status --change .
43 43 A .hgsub
44 44 A .hgsubstate
45 45 A a
46 46 $ cat .hgsubstate
47 47 0000000000000000000000000000000000000000 s
48 48
49 49 Update subrepo
50 50 --------------
51 51
52 52 add new commit to be amended
53 53
54 54 $ echo a >> a
55 55 $ hg ci -m1
56 56
57 57 amend with dirty subrepo
58 58
59 59 $ echo a >> s/a
60 60 $ hg add -R s
61 61 adding s/a
62 62 $ hg amend
63 63 abort: uncommitted changes in subrepository "s"
64 64 (use --subrepos for recursive commit)
65 65 [255]
66 66
67 67 amend with modified subrepo
68 68
69 69 $ hg ci -R s -m0
70 70 $ hg amend
71 71 saved backup bundle to * (glob) (obsstore-off !)
72 72 $ hg status --change .
73 73 M .hgsubstate
74 74 M a
75 75 $ cat .hgsubstate
76 76 f7b1eb17ad24730a1651fccd46c43826d1bbc2ac s
77 77
78 78 revert subrepo change
79 79
80 80 $ hg up -R s -q null
81 81 $ hg amend
82 82 saved backup bundle to * (glob) (obsstore-off !)
83 83 $ hg status --change .
84 84 M a
85 85
86 86 Link another subrepo
87 87 --------------------
88 88
89 89 add new commit to be amended
90 90
91 91 $ echo b >> b
92 92 $ hg ci -qAm2
93 93
94 94 also checks if non-subrepo change is included
95 95
96 96 $ echo a >> a
97 97
98 98 amend with another subrepo
99 99
100 100 $ hg init t
101 101 $ echo b >> t/b
102 102 $ hg ci -R t -Am0
103 103 adding b
104 104 $ echo 't = t' >> .hgsub
105 105 $ hg amend
106 106 saved backup bundle to * (glob) (obsstore-off !)
107 107 $ hg status --change .
108 108 M .hgsub
109 109 M .hgsubstate
110 110 M a
111 111 A b
112 112 $ cat .hgsubstate
113 113 0000000000000000000000000000000000000000 s
114 114 bfb1a4fb358498a9533dabf4f2043d94162f1fcd t
115 115
116 116 Unlink one subrepo
117 117 ------------------
118 118
119 119 add new commit to be amended
120 120
121 121 $ echo a >> a
122 122 $ hg ci -m3
123 123
124 124 $ echo 't = t' > .hgsub
125 125
126 126 --interactive won't silently ignore dirty subrepos
127 127
128 128 $ echo modified > t/b
129 129 $ hg amend --interactive --config ui.interactive=True
130 130 abort: uncommitted changes in subrepository "t"
131 131 [255]
132 132 $ hg amend --interactive --config ui.interactive=True --config ui.commitsubrepos=True
133 133 abort: uncommitted changes in subrepository "t"
134 134 [255]
135 135
136 136 $ hg -R t revert -q --all --no-backup
137 137
138 138 amend with one subrepo dropped
139 139
140 140 $ hg amend
141 141 saved backup bundle to * (glob) (obsstore-off !)
142 142 $ hg status --change .
143 143 M .hgsub
144 144 M .hgsubstate
145 145 M a
146 146 $ cat .hgsubstate
147 147 bfb1a4fb358498a9533dabf4f2043d94162f1fcd t
148 148
149 149 Unlink subrepos completely
150 150 --------------------------
151 151
152 152 add new commit to be amended
153 153
154 154 $ echo a >> a
155 155 $ hg ci -m3
156 156
157 157 amend with .hgsub removed
158 158
159 159 $ hg rm .hgsub
160 160 $ hg amend
161 161 saved backup bundle to * (glob) (obsstore-off !)
162 162 $ hg status --change .
163 163 M a
164 164 R .hgsub
165 165 R .hgsubstate
166 166
167 167 broken repositories will refuse to push
168 168
169 169 #if obsstore-off
170 170 $ hg up -q -C 2
171 171 #else
172 172 $ hg up -q -C 6
173 173 #endif
174 174 $ echo c >> t/b
175 175 $ hg amend -q -R t
176 176
177 177 $ hg init ../dest
178 178 $ hg init ../dest/t
179 179 $ hg init ../dest/s
180 180 $ hg push -q ../dest
181 181 abort: subrepo 't' is hidden in revision 04aa62396ec6 (obsstore-on !)
182 182 abort: subrepo 't' not found in revision 04aa62396ec6 (obsstore-off !)
183 183 [255]
184 184
185 185 ... unless forced
186 186
187 187 $ hg push --force -q ../dest
188 188 $ hg verify -R ../dest
189 189 checking changesets
190 190 checking manifests
191 191 crosschecking files in changesets and manifests
192 192 checking files
193 checking dirstate
193 194 checked 5 changesets with 12 changes to 4 files
194 195 checking subrepo links
195 196 subrepo 't' not found in revision 04aa62396ec6
196 197 subrepo 't' not found in revision 6bce99600681
197 198
198 199 $ cd ..
@@ -1,259 +1,260 b''
1 1 Create a repository:
2 2
3 3 #if no-extraextensions
4 4 $ hg config
5 5 chgserver.idletimeout=60
6 6 devel.all-warnings=true
7 7 devel.default-date=0 0
8 8 extensions.fsmonitor= (fsmonitor !)
9 9 format.use-dirstate-v2=1 (dirstate-v2 !)
10 10 largefiles.usercache=$TESTTMP/.cache/largefiles
11 11 lfs.usercache=$TESTTMP/.cache/lfs
12 12 ui.slash=True
13 13 ui.interactive=False
14 14 ui.detailed-exit-code=True
15 15 ui.merge=internal:merge
16 16 ui.mergemarkers=detailed
17 17 ui.promptecho=True
18 18 ui.ssh=* (glob)
19 19 ui.timeout.warn=15
20 20 web.address=localhost
21 21 web\.ipv6=(?:True|False) (re)
22 22 web.server-header=testing stub value
23 23 #endif
24 24
25 25 $ hg init t
26 26 $ cd t
27 27
28 28 Prepare a changeset:
29 29
30 30 $ echo a > a
31 31 $ hg add a
32 32
33 33 $ hg status
34 34 A a
35 35
36 36 Writes to stdio succeed and fail appropriately
37 37
38 38 #if devfull
39 39 $ hg status 2>/dev/full
40 40 A a
41 41
42 42 $ hg status >/dev/full
43 43 abort: No space left on device* (glob)
44 44 [255]
45 45 #endif
46 46
47 47 #if devfull
48 48 $ hg status >/dev/full 2>&1
49 49 [255]
50 50
51 51 $ hg status ENOENT 2>/dev/full
52 52 [255]
53 53 #endif
54 54
55 55 On Python 3, stdio may be None:
56 56
57 57 $ hg debuguiprompt --config ui.interactive=true 0<&-
58 58 abort: Bad file descriptor (no-rhg !)
59 59 abort: response expected (rhg !)
60 60 [255]
61 61 $ hg version -q 0<&-
62 62 Mercurial Distributed SCM * (glob)
63 63
64 64 #if py3 no-rhg
65 65 $ hg version -q 1>&-
66 66 abort: Bad file descriptor
67 67 [255]
68 68 #else
69 69 $ hg version -q 1>&-
70 70 #endif
71 71 $ hg unknown -q 1>&-
72 72 hg: unknown command 'unknown'
73 73 (did you mean debugknown?)
74 74 [10]
75 75
76 76 $ hg version -q 2>&-
77 77 Mercurial Distributed SCM * (glob)
78 78 $ hg unknown -q 2>&-
79 79 [10]
80 80
81 81 $ hg commit -m test
82 82
83 83 This command is ancient:
84 84
85 85 $ hg history
86 86 changeset: 0:acb14030fe0a
87 87 tag: tip
88 88 user: test
89 89 date: Thu Jan 01 00:00:00 1970 +0000
90 90 summary: test
91 91
92 92
93 93 Verify that updating to revision 0 via commands.update() works properly
94 94
95 95 $ cat <<EOF > update_to_rev0.py
96 96 > from mercurial import commands, hg, ui as uimod
97 97 > myui = uimod.ui.load()
98 98 > repo = hg.repository(myui, path=b'.')
99 99 > commands.update(myui, repo, rev=b"0")
100 100 > EOF
101 101 $ hg up null
102 102 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
103 103 $ "$PYTHON" ./update_to_rev0.py
104 104 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
105 105 $ hg identify -n
106 106 0
107 107
108 108
109 109 Poke around at hashes:
110 110
111 111 $ hg manifest --debug
112 112 b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3 644 a
113 113
114 114 $ hg cat a
115 115 a
116 116
117 117 Verify should succeed:
118 118
119 119 $ hg verify
120 120 checking changesets
121 121 checking manifests
122 122 crosschecking files in changesets and manifests
123 123 checking files
124 checking dirstate
124 125 checked 1 changesets with 1 changes to 1 files
125 126
126 127 Repository root:
127 128
128 129 $ hg root
129 130 $TESTTMP/t
130 131 $ hg log -l1 -T '{reporoot}\n'
131 132 $TESTTMP/t
132 133 $ hg root -Tjson | sed 's|\\\\|\\|g'
133 134 [
134 135 {
135 136 "hgpath": "$TESTTMP/t/.hg",
136 137 "reporoot": "$TESTTMP/t",
137 138 "storepath": "$TESTTMP/t/.hg/store"
138 139 }
139 140 ]
140 141
141 142 At the end...
142 143
143 144 $ cd ..
144 145
145 146 Status message redirection:
146 147
147 148 $ hg init empty
148 149
149 150 status messages are sent to stdout by default:
150 151
151 152 $ hg outgoing -R t empty -Tjson 2>/dev/null
152 153 comparing with empty
153 154 searching for changes
154 155 [
155 156 {
156 157 "bookmarks": [],
157 158 "branch": "default",
158 159 "date": [0, 0],
159 160 "desc": "test",
160 161 "node": "acb14030fe0a21b60322c440ad2d20cf7685a376",
161 162 "parents": ["0000000000000000000000000000000000000000"],
162 163 "phase": "draft",
163 164 "rev": 0,
164 165 "tags": ["tip"],
165 166 "user": "test"
166 167 }
167 168 ]
168 169
169 170 which can be configured to send to stderr, so the output wouldn't be
170 171 interleaved:
171 172
172 173 $ cat <<'EOF' >> "$HGRCPATH"
173 174 > [ui]
174 175 > message-output = stderr
175 176 > EOF
176 177 $ hg outgoing -R t empty -Tjson 2>/dev/null
177 178 [
178 179 {
179 180 "bookmarks": [],
180 181 "branch": "default",
181 182 "date": [0, 0],
182 183 "desc": "test",
183 184 "node": "acb14030fe0a21b60322c440ad2d20cf7685a376",
184 185 "parents": ["0000000000000000000000000000000000000000"],
185 186 "phase": "draft",
186 187 "rev": 0,
187 188 "tags": ["tip"],
188 189 "user": "test"
189 190 }
190 191 ]
191 192 $ hg outgoing -R t empty -Tjson >/dev/null
192 193 comparing with empty
193 194 searching for changes
194 195
195 196 this option should be turned off by HGPLAIN= since it may break scripting use:
196 197
197 198 $ HGPLAIN= hg outgoing -R t empty -Tjson 2>/dev/null
198 199 comparing with empty
199 200 searching for changes
200 201 [
201 202 {
202 203 "bookmarks": [],
203 204 "branch": "default",
204 205 "date": [0, 0],
205 206 "desc": "test",
206 207 "node": "acb14030fe0a21b60322c440ad2d20cf7685a376",
207 208 "parents": ["0000000000000000000000000000000000000000"],
208 209 "phase": "draft",
209 210 "rev": 0,
210 211 "tags": ["tip"],
211 212 "user": "test"
212 213 }
213 214 ]
214 215
215 216 but still overridden by --config:
216 217
217 218 $ HGPLAIN= hg outgoing -R t empty -Tjson --config ui.message-output=stderr \
218 219 > 2>/dev/null
219 220 [
220 221 {
221 222 "bookmarks": [],
222 223 "branch": "default",
223 224 "date": [0, 0],
224 225 "desc": "test",
225 226 "node": "acb14030fe0a21b60322c440ad2d20cf7685a376",
226 227 "parents": ["0000000000000000000000000000000000000000"],
227 228 "phase": "draft",
228 229 "rev": 0,
229 230 "tags": ["tip"],
230 231 "user": "test"
231 232 }
232 233 ]
233 234
234 235 Invalid ui.message-output option:
235 236
236 237 $ hg log -R t --config ui.message-output=bad
237 238 abort: invalid ui.message-output destination: bad
238 239 [255]
239 240
240 241 Underlying message streams should be updated when ui.fout/ferr are set:
241 242
242 243 $ cat <<'EOF' > capui.py
243 244 > import io
244 245 > from mercurial import registrar
245 246 > cmdtable = {}
246 247 > command = registrar.command(cmdtable)
247 248 > @command(b'capui', norepo=True)
248 249 > def capui(ui):
249 250 > out = ui.fout
250 251 > ui.fout = io.BytesIO()
251 252 > ui.status(b'status\n')
252 253 > ui.ferr = io.BytesIO()
253 254 > ui.warn(b'warn\n')
254 255 > out.write(b'stdout: %s' % ui.fout.getvalue())
255 256 > out.write(b'stderr: %s' % ui.ferr.getvalue())
256 257 > EOF
257 258 $ hg --config extensions.capui=capui.py --config ui.message-output=stdio capui
258 259 stdout: status
259 260 stderr: warn
@@ -1,531 +1,532 b''
1 1 #require no-reposimplestore
2 2 #testcases revlogv1 revlogv2
3 3
4 4 #if revlogv2
5 5
6 6 $ cat >> $HGRCPATH <<EOF
7 7 > [experimental]
8 8 > revlogv2=enable-unstable-format-and-corrupt-my-data
9 9 > EOF
10 10
11 11 #endif
12 12
13 13 $ cp $HGRCPATH $HGRCPATH.orig
14 14
15 15 Create repo with unimpeachable content
16 16
17 17 $ hg init r
18 18 $ cd r
19 19 $ echo 'Initially untainted file' > target
20 20 $ echo 'Normal file here' > bystander
21 21 $ hg add target bystander
22 22 $ hg ci -m init
23 23
24 24 Clone repo so we can test pull later
25 25
26 26 $ cd ..
27 27 $ hg clone r rpull
28 28 updating to branch default
29 29 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
30 30 $ cd r
31 31
32 32 Introduce content which will ultimately require censorship. Name the first
33 33 censored node C1, second C2, and so on
34 34
35 35 $ echo 'Tainted file' > target
36 36 $ echo 'Passwords: hunter2' >> target
37 37 $ hg ci -m taint target
38 38 $ C1=`hg id --debug -i`
39 39
40 40 $ echo 'hunter3' >> target
41 41 $ echo 'Normal file v2' > bystander
42 42 $ hg ci -m moretaint target bystander
43 43 $ C2=`hg id --debug -i`
44 44
45 45 Add a new sanitized versions to correct our mistake. Name the first head H1,
46 46 the second head H2, and so on
47 47
48 48 $ echo 'Tainted file is now sanitized' > target
49 49 $ hg ci -m sanitized target
50 50 $ H1=`hg id --debug -i`
51 51
52 52 $ hg update -r $C2
53 53 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
54 54 $ echo 'Tainted file now super sanitized' > target
55 55 $ hg ci -m 'super sanitized' target
56 56 created new head
57 57 $ H2=`hg id --debug -i`
58 58
59 59 Verify target contents before censorship at each revision
60 60
61 61 $ hg cat -r $H1 target | head -n 10
62 62 Tainted file is now sanitized
63 63 $ hg cat -r $H2 target | head -n 10
64 64 Tainted file now super sanitized
65 65 $ hg cat -r $C2 target | head -n 10
66 66 Tainted file
67 67 Passwords: hunter2
68 68 hunter3
69 69 $ hg cat -r $C1 target | head -n 10
70 70 Tainted file
71 71 Passwords: hunter2
72 72 $ hg cat -r 0 target | head -n 10
73 73 Initially untainted file
74 74
75 75 Censor revision with 2 offenses
76 76
77 77 (this also tests file pattern matching: path relative to cwd case)
78 78
79 79 $ mkdir -p foo/bar/baz
80 80 $ hg --config extensions.censor= --cwd foo/bar/baz censor -r $C2 -t "remove password" ../../../target
81 81 $ hg cat -r $H1 target | head -n 10
82 82 Tainted file is now sanitized
83 83 $ hg cat -r $H2 target | head -n 10
84 84 Tainted file now super sanitized
85 85 $ hg cat -r $C2 target | head -n 10
86 86 abort: censored node: 1e0247a9a4b7
87 87 (set censor.policy to ignore errors)
88 88 $ hg cat -r $C1 target | head -n 10
89 89 Tainted file
90 90 Passwords: hunter2
91 91 $ hg cat -r 0 target | head -n 10
92 92 Initially untainted file
93 93
94 94 Censor revision with 1 offense
95 95
96 96 (this also tests file pattern matching: with 'path:' scheme)
97 97
98 98 $ hg --config extensions.censor= --cwd foo/bar/baz censor -r $C1 path:target
99 99 $ hg cat -r $H1 target | head -n 10
100 100 Tainted file is now sanitized
101 101 $ hg cat -r $H2 target | head -n 10
102 102 Tainted file now super sanitized
103 103 $ hg cat -r $C2 target | head -n 10
104 104 abort: censored node: 1e0247a9a4b7
105 105 (set censor.policy to ignore errors)
106 106 $ hg cat -r $C1 target | head -n 10
107 107 abort: censored node: 613bc869fceb
108 108 (set censor.policy to ignore errors)
109 109 $ hg cat -r 0 target | head -n 10
110 110 Initially untainted file
111 111
112 112 Can only checkout target at uncensored revisions, -X is workaround for --all
113 113
114 114 $ hg revert -r $C2 target | head -n 10
115 115 abort: censored node: 1e0247a9a4b7
116 116 (set censor.policy to ignore errors)
117 117 $ hg revert -r $C1 target | head -n 10
118 118 abort: censored node: 613bc869fceb
119 119 (set censor.policy to ignore errors)
120 120 $ hg revert -r $C1 --all
121 121 reverting bystander
122 122 reverting target
123 123 abort: censored node: 613bc869fceb
124 124 (set censor.policy to ignore errors)
125 125 [255]
126 126 $ hg revert -r $C1 --all -X target
127 127 $ cat target | head -n 10
128 128 Tainted file now super sanitized
129 129 $ hg revert -r 0 --all
130 130 reverting target
131 131 $ cat target | head -n 10
132 132 Initially untainted file
133 133 $ hg revert -r $H2 --all
134 134 reverting bystander
135 135 reverting target
136 136 $ cat target | head -n 10
137 137 Tainted file now super sanitized
138 138
139 139 Uncensored file can be viewed at any revision
140 140
141 141 $ hg cat -r $H1 bystander | head -n 10
142 142 Normal file v2
143 143 $ hg cat -r $C2 bystander | head -n 10
144 144 Normal file v2
145 145 $ hg cat -r $C1 bystander | head -n 10
146 146 Normal file here
147 147 $ hg cat -r 0 bystander | head -n 10
148 148 Normal file here
149 149
150 150 Can update to children of censored revision
151 151
152 152 $ hg update -r $H1
153 153 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
154 154 $ cat target | head -n 10
155 155 Tainted file is now sanitized
156 156 $ hg update -r $H2
157 157 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
158 158 $ cat target | head -n 10
159 159 Tainted file now super sanitized
160 160
161 161 Set censor policy to abort in trusted $HGRC so hg verify fails
162 162
163 163 $ cp $HGRCPATH.orig $HGRCPATH
164 164 $ cat >> $HGRCPATH <<EOF
165 165 > [censor]
166 166 > policy = abort
167 167 > EOF
168 168
169 169 Repo fails verification due to censorship
170 170
171 171 $ hg verify
172 172 checking changesets
173 173 checking manifests
174 174 crosschecking files in changesets and manifests
175 175 checking files
176 176 target@1: censored file data
177 177 target@2: censored file data
178 not checking dirstate because of previous errors
178 179 checked 5 changesets with 7 changes to 2 files
179 180 2 integrity errors encountered!
180 181 (first damaged changeset appears to be 1)
181 182 [1]
182 183
183 184 Cannot update to revision with censored data
184 185
185 186 $ hg update -r $C2
186 187 abort: censored node: 1e0247a9a4b7
187 188 (set censor.policy to ignore errors)
188 189 [255]
189 190 $ hg update -r $C1
190 191 abort: censored node: 613bc869fceb
191 192 (set censor.policy to ignore errors)
192 193 [255]
193 194 $ hg update -r 0
194 195 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
195 196 $ hg update -r $H2
196 197 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
197 198
198 199 Set censor policy to ignore in trusted $HGRC so hg verify passes
199 200
200 201 $ cp $HGRCPATH.orig $HGRCPATH
201 202 $ cat >> $HGRCPATH <<EOF
202 203 > [censor]
203 204 > policy = ignore
204 205 > EOF
205 206
206 207 Repo passes verification with warnings with explicit config
207 208
208 209 $ hg verify -q
209 210
210 211 May update to revision with censored data with explicit config
211 212
212 213 $ hg update -r $C2
213 214 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
214 215 $ cat target | head -n 10
215 216 $ hg update -r $C1
216 217 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
217 218 $ cat target | head -n 10
218 219 $ hg update -r 0
219 220 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
220 221 $ cat target | head -n 10
221 222 Initially untainted file
222 223 $ hg update -r $H2
223 224 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
224 225 $ cat target | head -n 10
225 226 Tainted file now super sanitized
226 227
227 228 Can merge in revision with censored data. Test requires one branch of history
228 229 with the file censored, but we can't censor at a head, so advance H1.
229 230
230 231 $ hg update -r $H1
231 232 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
232 233 $ C3=$H1
233 234 $ echo 'advanced head H1' > target
234 235 $ hg ci -m 'advance head H1' target
235 236 $ H1=`hg id --debug -i`
236 237 $ hg --config extensions.censor= censor -r $C3 target
237 238 $ hg update -r $H2
238 239 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
239 240 $ hg merge -r $C3
240 241 merging target
241 242 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
242 243 (branch merge, don't forget to commit)
243 244
244 245 Revisions present in repository heads may not be censored
245 246
246 247 $ hg update -C -r $H2
247 248 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
248 249 $ hg --config extensions.censor= censor -r $H2 target
249 250 abort: cannot censor file in heads (78a8fc215e79)
250 251 (clean/delete and commit first)
251 252 [255]
252 253 $ echo 'twiddling thumbs' > bystander
253 254 $ hg ci -m 'bystander commit'
254 255 $ H2=`hg id --debug -i`
255 256 $ hg --config extensions.censor= censor -r "$H2^" target
256 257 abort: cannot censor file in heads (efbe78065929)
257 258 (clean/delete and commit first)
258 259 [255]
259 260
260 261 Cannot censor working directory
261 262
262 263 $ echo 'seriously no passwords' > target
263 264 $ hg ci -m 'extend second head arbitrarily' target
264 265 $ H2=`hg id --debug -i`
265 266 $ hg update -r "$H2^"
266 267 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
267 268 $ hg --config extensions.censor= censor -r . target
268 269 abort: cannot censor working directory
269 270 (clean/delete/update first)
270 271 [255]
271 272 $ hg update -r $H2
272 273 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
273 274
274 275 Can re-add file after being deleted + censored
275 276
276 277 $ C4=$H2
277 278 $ hg rm target
278 279 $ hg ci -m 'delete target so it may be censored'
279 280 $ H2=`hg id --debug -i`
280 281 $ hg --config extensions.censor= censor -r $C4 target
281 282 $ hg cat -r $C4 target | head -n 10
282 283 $ hg cat -r "$H2^^" target | head -n 10
283 284 Tainted file now super sanitized
284 285 $ echo 'fresh start' > target
285 286 $ hg add target
286 287 $ hg ci -m reincarnated target
287 288 $ H2=`hg id --debug -i`
288 289 $ hg cat -r $H2 target | head -n 10
289 290 fresh start
290 291 $ hg cat -r "$H2^" target | head -n 10
291 292 target: no such file in rev 452ec1762369
292 293 $ hg cat -r $C4 target | head -n 10
293 294 $ hg cat -r "$H2^^^" target | head -n 10
294 295 Tainted file now super sanitized
295 296
296 297 Can censor after revlog has expanded to no longer permit inline storage
297 298
298 299 $ for x in `"$PYTHON" $TESTDIR/seq.py 0 50000`
299 300 > do
300 301 > echo "Password: hunter$x" >> target
301 302 > done
302 303 $ hg ci -m 'add 100k passwords'
303 304 $ H2=`hg id --debug -i`
304 305 $ C5=$H2
305 306 $ hg revert -r "$H2^" target
306 307 $ hg ci -m 'cleaned 100k passwords'
307 308 $ H2=`hg id --debug -i`
308 309 $ hg --config extensions.censor= censor -r $C5 target
309 310 $ hg cat -r $C5 target | head -n 10
310 311 $ hg cat -r $H2 target | head -n 10
311 312 fresh start
312 313
313 314 Repo with censored nodes can be cloned and cloned nodes are censored
314 315
315 316 $ cd ..
316 317 $ hg clone r rclone
317 318 updating to branch default
318 319 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
319 320 $ cd rclone
320 321 $ hg cat -r $H1 target | head -n 10
321 322 advanced head H1
322 323 $ hg cat -r $H2~5 target | head -n 10
323 324 Tainted file now super sanitized
324 325 $ hg cat -r $C2 target | head -n 10
325 326 $ hg cat -r $C1 target | head -n 10
326 327 $ hg cat -r 0 target | head -n 10
327 328 Initially untainted file
328 329 $ hg verify -q
329 330
330 331 Repo cloned before tainted content introduced can pull censored nodes
331 332
332 333 $ cd ../rpull
333 334 $ hg cat -r tip target | head -n 10
334 335 Initially untainted file
335 336 $ hg verify -q
336 337 $ hg pull -r $H1 -r $H2
337 338 pulling from $TESTTMP/r
338 339 searching for changes
339 340 adding changesets
340 341 adding manifests
341 342 adding file changes
342 343 added 11 changesets with 11 changes to 2 files (+1 heads)
343 344 new changesets 186fb27560c3:683e4645fded
344 345 (run 'hg heads' to see heads, 'hg merge' to merge)
345 346 $ hg update 4
346 347 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
347 348 $ cat target | head -n 10
348 349 Tainted file now super sanitized
349 350 $ hg cat -r $H1 target | head -n 10
350 351 advanced head H1
351 352 $ hg cat -r $H2~5 target | head -n 10
352 353 Tainted file now super sanitized
353 354 $ hg cat -r $C2 target | head -n 10
354 355 $ hg cat -r $C1 target | head -n 10
355 356 $ hg cat -r 0 target | head -n 10
356 357 Initially untainted file
357 358 $ hg verify -q
358 359
359 360 Censored nodes can be pushed if they censor previously unexchanged nodes
360 361
361 362 $ echo 'Passwords: hunter2hunter2' > target
362 363 $ hg ci -m 're-add password from clone' target
363 364 created new head
364 365 $ H3=`hg id --debug -i`
365 366 $ REV=$H3
366 367 $ echo 'Re-sanitized; nothing to see here' > target
367 368 $ hg ci -m 're-sanitized' target
368 369 $ H2=`hg id --debug -i`
369 370 $ CLEANREV=$H2
370 371 $ hg cat -r $REV target | head -n 10
371 372 Passwords: hunter2hunter2
372 373 $ hg --config extensions.censor= censor -r $REV target
373 374 $ hg cat -r $REV target | head -n 10
374 375 $ hg cat -r $CLEANREV target | head -n 10
375 376 Re-sanitized; nothing to see here
376 377 $ hg push -f -r $H2
377 378 pushing to $TESTTMP/r
378 379 searching for changes
379 380 adding changesets
380 381 adding manifests
381 382 adding file changes
382 383 added 2 changesets with 2 changes to 1 files (+1 heads)
383 384
384 385 $ cd ../r
385 386 $ hg cat -r $REV target | head -n 10
386 387 $ hg cat -r $CLEANREV target | head -n 10
387 388 Re-sanitized; nothing to see here
388 389 $ hg update $CLEANREV
389 390 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
390 391 $ cat target | head -n 10
391 392 Re-sanitized; nothing to see here
392 393
393 394 Censored nodes can be bundled up and unbundled in another repo
394 395
395 396 $ hg bundle --base 0 ../pwbundle
396 397 13 changesets found
397 398 $ cd ../rclone
398 399 $ hg unbundle ../pwbundle
399 400 adding changesets
400 401 adding manifests
401 402 adding file changes
402 403 added 2 changesets with 2 changes to 2 files (+1 heads)
403 404 new changesets 075be80ac777:dcbaf17bf3a1 (2 drafts)
404 405 (run 'hg heads .' to see heads, 'hg merge' to merge)
405 406 $ hg cat -r $REV target | head -n 10
406 407 $ hg cat -r $CLEANREV target | head -n 10
407 408 Re-sanitized; nothing to see here
408 409 $ hg update $CLEANREV
409 410 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
410 411 $ cat target | head -n 10
411 412 Re-sanitized; nothing to see here
412 413 $ hg verify -q
413 414
414 415 Grepping only warns, doesn't error out
415 416
416 417 $ cd ../rpull
417 418 $ hg grep 'Normal file'
418 419 bystander:Normal file v2
419 420 $ hg grep nothing
420 421 target:Re-sanitized; nothing to see here
421 422 $ hg grep --diff 'Normal file'
422 423 cannot search in censored file: target:7
423 424 cannot search in censored file: target:10
424 425 cannot search in censored file: target:12
425 426 bystander:6:-:Normal file v2
426 427 cannot search in censored file: target:1
427 428 cannot search in censored file: target:2
428 429 cannot search in censored file: target:3
429 430 bystander:2:-:Normal file here
430 431 bystander:2:+:Normal file v2
431 432 bystander:0:+:Normal file here
432 433 $ hg grep --diff nothing
433 434 cannot search in censored file: target:7
434 435 cannot search in censored file: target:10
435 436 cannot search in censored file: target:12
436 437 target:13:+:Re-sanitized; nothing to see here
437 438 cannot search in censored file: target:1
438 439 cannot search in censored file: target:2
439 440 cannot search in censored file: target:3
440 441
441 442 Censored nodes can be imported on top of censored nodes, consecutively
442 443
443 444 $ hg init ../rimport
444 445 $ hg bundle --base 1 ../rimport/splitbundle
445 446 12 changesets found
446 447 $ cd ../rimport
447 448 $ hg pull -r $H1 -r $H2 ../r
448 449 pulling from ../r
449 450 adding changesets
450 451 adding manifests
451 452 adding file changes
452 453 added 8 changesets with 10 changes to 2 files (+1 heads)
453 454 new changesets e97f55b2665a:dcbaf17bf3a1
454 455 (run 'hg heads' to see heads, 'hg merge' to merge)
455 456 $ hg unbundle splitbundle
456 457 adding changesets
457 458 adding manifests
458 459 adding file changes
459 460 added 6 changesets with 5 changes to 2 files (+1 heads)
460 461 new changesets efbe78065929:683e4645fded (6 drafts)
461 462 (run 'hg heads .' to see heads, 'hg merge' to merge)
462 463 $ hg update $H2
463 464 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
464 465 $ cat target | head -n 10
465 466 Re-sanitized; nothing to see here
466 467 $ hg verify -q
467 468 $ cd ../r
468 469
469 470 Can import bundle where first revision of a file is censored
470 471
471 472 $ hg init ../rinit
472 473 $ hg --config extensions.censor= censor -r 0 target
473 474 $ hg bundle -r 0 --base null ../rinit/initbundle
474 475 1 changesets found
475 476 $ cd ../rinit
476 477 $ hg unbundle initbundle
477 478 adding changesets
478 479 adding manifests
479 480 adding file changes
480 481 added 1 changesets with 2 changes to 2 files
481 482 new changesets e97f55b2665a (1 drafts)
482 483 (run 'hg update' to get a working copy)
483 484 $ hg cat -r 0 target | head -n 10
484 485
485 486 #if revlogv2
486 487
487 488 Testing feature that does not work in revlog v1
488 489 ===============================================
489 490
490 491 Censoring a revision that is used as delta base
491 492 -----------------------------------------------
492 493
493 494 $ cd ..
494 495 $ hg init censor-with-delta
495 496 $ cd censor-with-delta
496 497 $ echo root > target
497 498 $ hg add target
498 499 $ hg commit -m root
499 500 $ B0=`hg id --debug -i`
500 501 $ for x in `"$PYTHON" $TESTDIR/seq.py 0 50000`
501 502 > do
502 503 > echo "Password: hunter$x" >> target
503 504 > done
504 505 $ hg ci -m 'write a long file'
505 506 $ B1=`hg id --debug -i`
506 507 $ echo 'small change (should create a delta)' >> target
507 508 $ hg ci -m 'create a delta over the password'
508 509 (should show that the last revision is a delta, not a snapshot)
509 510 $ B2=`hg id --debug -i`
510 511
511 512 Make sure the last revision is a delta against the revision we will censor
512 513
513 514 $ hg debugdeltachain target -T '{rev} {chainid} {chainlen} {prevrev}\n'
514 515 0 1 1 -1
515 516 1 2 1 -1
516 517 2 2 2 1
517 518
518 519 Censor the file
519 520
520 521 $ hg cat -r $B1 target | wc -l
521 522 *50002 (re)
522 523 $ hg --config extensions.censor= censor -r $B1 target
523 524 $ hg cat -r $B1 target | wc -l
524 525 *0 (re)
525 526
526 527 Check the children is fine
527 528
528 529 $ hg cat -r $B2 target | wc -l
529 530 *50003 (re)
530 531
531 532 #endif
@@ -1,1173 +1,1175 b''
1 1 #require no-rhg no-chg
2 2
3 3 XXX-RHG this test hangs if `hg` is really `rhg`. This was hidden by the use of
4 4 `alias hg=rhg` by run-tests.py. With such alias removed, this test is revealed
5 5 buggy. This need to be resolved sooner than later.
6 6
7 7 XXX-CHG this test hangs if `hg` is really `chg`. This was hidden by the use of
8 8 `alias hg=chg` by run-tests.py. With such alias removed, this test is revealed
9 9 buggy. This need to be resolved sooner than later.
10 10
11 11 #if windows
12 12 $ PYTHONPATH="$TESTDIR/../contrib;$PYTHONPATH"
13 13 #else
14 14 $ PYTHONPATH="$TESTDIR/../contrib:$PYTHONPATH"
15 15 #endif
16 16 $ export PYTHONPATH
17 17
18 18 typical client does not want echo-back messages, so test without it:
19 19
20 20 $ grep -v '^promptecho ' < $HGRCPATH >> $HGRCPATH.new
21 21 $ mv $HGRCPATH.new $HGRCPATH
22 22
23 23 $ hg init repo
24 24 $ cd repo
25 25
26 26 >>> import os
27 27 >>> import sys
28 28 >>> from hgclient import bprint, check, readchannel, runcommand
29 29 >>> @check
30 30 ... def hellomessage(server):
31 31 ... ch, data = readchannel(server)
32 32 ... bprint(b'%c, %r' % (ch, data))
33 33 ... # run an arbitrary command to make sure the next thing the server
34 34 ... # sends isn't part of the hello message
35 35 ... runcommand(server, [b'id'])
36 36 o, 'capabilities: getencoding runcommand\nencoding: *\npid: *' (glob)
37 37 *** runcommand id
38 38 000000000000 tip
39 39
40 40 >>> from hgclient import check
41 41 >>> @check
42 42 ... def unknowncommand(server):
43 43 ... server.stdin.write(b'unknowncommand\n')
44 44 abort: unknown command unknowncommand
45 45
46 46 >>> from hgclient import check, readchannel, runcommand
47 47 >>> @check
48 48 ... def checkruncommand(server):
49 49 ... # hello block
50 50 ... readchannel(server)
51 51 ...
52 52 ... # no args
53 53 ... runcommand(server, [])
54 54 ...
55 55 ... # global options
56 56 ... runcommand(server, [b'id', b'--quiet'])
57 57 ...
58 58 ... # make sure global options don't stick through requests
59 59 ... runcommand(server, [b'id'])
60 60 ...
61 61 ... # --config
62 62 ... runcommand(server, [b'id', b'--config', b'ui.quiet=True'])
63 63 ...
64 64 ... # make sure --config doesn't stick
65 65 ... runcommand(server, [b'id'])
66 66 ...
67 67 ... # negative return code should be masked
68 68 ... runcommand(server, [b'id', b'-runknown'])
69 69 *** runcommand
70 70 Mercurial Distributed SCM
71 71
72 72 basic commands:
73 73
74 74 add add the specified files on the next commit
75 75 annotate show changeset information by line for each file
76 76 clone make a copy of an existing repository
77 77 commit commit the specified files or all outstanding changes
78 78 diff diff repository (or selected files)
79 79 export dump the header and diffs for one or more changesets
80 80 forget forget the specified files on the next commit
81 81 init create a new repository in the given directory
82 82 log show revision history of entire repository or files
83 83 merge merge another revision into working directory
84 84 pull pull changes from the specified source
85 85 push push changes to the specified destination
86 86 remove remove the specified files on the next commit
87 87 serve start stand-alone webserver
88 88 status show changed files in the working directory
89 89 summary summarize working directory state
90 90 update update working directory (or switch revisions)
91 91
92 92 (use 'hg help' for the full list of commands or 'hg -v' for details)
93 93 *** runcommand id --quiet
94 94 000000000000
95 95 *** runcommand id
96 96 000000000000 tip
97 97 *** runcommand id --config ui.quiet=True
98 98 000000000000
99 99 *** runcommand id
100 100 000000000000 tip
101 101 *** runcommand id -runknown
102 102 abort: unknown revision 'unknown'
103 103 [10]
104 104
105 105 >>> from hgclient import bprint, check, readchannel
106 106 >>> @check
107 107 ... def inputeof(server):
108 108 ... readchannel(server)
109 109 ... server.stdin.write(b'runcommand\n')
110 110 ... # close stdin while server is waiting for input
111 111 ... server.stdin.close()
112 112 ...
113 113 ... # server exits with 1 if the pipe closed while reading the command
114 114 ... bprint(b'server exit code =', b'%d' % server.wait())
115 115 server exit code = 1
116 116
117 117 >>> from hgclient import check, readchannel, runcommand, stringio
118 118 >>> @check
119 119 ... def serverinput(server):
120 120 ... readchannel(server)
121 121 ...
122 122 ... patch = b"""
123 123 ... # HG changeset patch
124 124 ... # User test
125 125 ... # Date 0 0
126 126 ... # Node ID c103a3dec114d882c98382d684d8af798d09d857
127 127 ... # Parent 0000000000000000000000000000000000000000
128 128 ... 1
129 129 ...
130 130 ... diff -r 000000000000 -r c103a3dec114 a
131 131 ... --- /dev/null Thu Jan 01 00:00:00 1970 +0000
132 132 ... +++ b/a Thu Jan 01 00:00:00 1970 +0000
133 133 ... @@ -0,0 +1,1 @@
134 134 ... +1
135 135 ... """
136 136 ...
137 137 ... runcommand(server, [b'import', b'-'], input=stringio(patch))
138 138 ... runcommand(server, [b'log'])
139 139 *** runcommand import -
140 140 applying patch from stdin
141 141 *** runcommand log
142 142 changeset: 0:eff892de26ec
143 143 tag: tip
144 144 user: test
145 145 date: Thu Jan 01 00:00:00 1970 +0000
146 146 summary: 1
147 147
148 148
149 149 check strict parsing of early options:
150 150
151 151 >>> import os
152 152 >>> from hgclient import check, readchannel, runcommand
153 153 >>> os.environ['HGPLAIN'] = '+strictflags'
154 154 >>> @check
155 155 ... def cwd(server):
156 156 ... readchannel(server)
157 157 ... runcommand(server, [b'log', b'-b', b'--config=alias.log=!echo pwned',
158 158 ... b'default'])
159 159 *** runcommand log -b --config=alias.log=!echo pwned default
160 160 abort: unknown revision '--config=alias.log=!echo pwned'
161 161 [10]
162 162
163 163 check that "histedit --commands=-" can read rules from the input channel:
164 164
165 165 >>> from hgclient import check, readchannel, runcommand, stringio
166 166 >>> @check
167 167 ... def serverinput(server):
168 168 ... readchannel(server)
169 169 ... rules = b'pick eff892de26ec\n'
170 170 ... runcommand(server, [b'histedit', b'0', b'--commands=-',
171 171 ... b'--config', b'extensions.histedit='],
172 172 ... input=stringio(rules))
173 173 *** runcommand histedit 0 --commands=- --config extensions.histedit=
174 174
175 175 check that --cwd doesn't persist between requests:
176 176
177 177 $ mkdir foo
178 178 $ touch foo/bar
179 179 >>> from hgclient import check, readchannel, runcommand
180 180 >>> @check
181 181 ... def cwd(server):
182 182 ... readchannel(server)
183 183 ... runcommand(server, [b'--cwd', b'foo', b'st', b'bar'])
184 184 ... runcommand(server, [b'st', b'foo/bar'])
185 185 *** runcommand --cwd foo st bar
186 186 ? bar
187 187 *** runcommand st foo/bar
188 188 ? foo/bar
189 189
190 190 $ rm foo/bar
191 191
192 192
193 193 check that local configs for the cached repo aren't inherited when -R is used:
194 194
195 195 $ cat <<EOF >> .hg/hgrc
196 196 > [ui]
197 197 > foo = bar
198 198 > EOF
199 199
200 200 #if no-extraextensions
201 201
202 202 >>> from hgclient import check, readchannel, runcommand, sep
203 203 >>> @check
204 204 ... def localhgrc(server):
205 205 ... readchannel(server)
206 206 ...
207 207 ... # the cached repo local hgrc contains ui.foo=bar, so showconfig should
208 208 ... # show it
209 209 ... runcommand(server, [b'showconfig'], outfilter=sep)
210 210 ...
211 211 ... # but not for this repo
212 212 ... runcommand(server, [b'init', b'foo'])
213 213 ... runcommand(server, [b'-R', b'foo', b'showconfig', b'ui', b'defaults'])
214 214 *** runcommand showconfig
215 215 bundle.mainreporoot=$TESTTMP/repo
216 216 chgserver.idletimeout=60
217 217 devel.all-warnings=true
218 218 devel.default-date=0 0
219 219 extensions.fsmonitor= (fsmonitor !)
220 220 format.use-dirstate-v2=1 (dirstate-v2 !)
221 221 largefiles.usercache=$TESTTMP/.cache/largefiles
222 222 lfs.usercache=$TESTTMP/.cache/lfs
223 223 ui.slash=True
224 224 ui.interactive=False
225 225 ui.detailed-exit-code=True
226 226 ui.merge=internal:merge
227 227 ui.mergemarkers=detailed
228 228 ui.ssh=* (glob)
229 229 ui.timeout.warn=15
230 230 ui.foo=bar
231 231 ui.nontty=true
232 232 web.address=localhost
233 233 web\.ipv6=(?:True|False) (re)
234 234 web.server-header=testing stub value
235 235 *** runcommand init foo
236 236 *** runcommand -R foo showconfig ui defaults
237 237 ui.slash=True
238 238 ui.interactive=False
239 239 ui.detailed-exit-code=True
240 240 ui.merge=internal:merge
241 241 ui.mergemarkers=detailed
242 242 ui.ssh=* (glob)
243 243 ui.timeout.warn=15
244 244 ui.nontty=true
245 245 #endif
246 246
247 247 $ rm -R foo
248 248
249 249 #if windows
250 250 $ PYTHONPATH="$TESTTMP/repo;$PYTHONPATH"
251 251 #else
252 252 $ PYTHONPATH="$TESTTMP/repo:$PYTHONPATH"
253 253 #endif
254 254
255 255 $ cat <<EOF > hook.py
256 256 > import sys
257 257 > from hgclient import bprint
258 258 > def hook(**args):
259 259 > bprint(b'hook talking')
260 260 > bprint(b'now try to read something: %r' % sys.stdin.read())
261 261 > EOF
262 262
263 263 >>> from hgclient import check, readchannel, runcommand, stringio
264 264 >>> @check
265 265 ... def hookoutput(server):
266 266 ... readchannel(server)
267 267 ... runcommand(server, [b'--config',
268 268 ... b'hooks.pre-identify=python:hook.hook',
269 269 ... b'id'],
270 270 ... input=stringio(b'some input'))
271 271 *** runcommand --config hooks.pre-identify=python:hook.hook id
272 272 eff892de26ec tip
273 273 hook talking
274 274 now try to read something: ''
275 275
276 276 Clean hook cached version
277 277 $ rm hook.py*
278 278 $ rm -Rf __pycache__
279 279
280 280 $ echo a >> a
281 281 >>> import os
282 282 >>> from hgclient import check, readchannel, runcommand
283 283 >>> @check
284 284 ... def outsidechanges(server):
285 285 ... readchannel(server)
286 286 ... runcommand(server, [b'status'])
287 287 ... os.system('hg ci -Am2')
288 288 ... runcommand(server, [b'tip'])
289 289 ... runcommand(server, [b'status'])
290 290 *** runcommand status
291 291 M a
292 292 *** runcommand tip
293 293 changeset: 1:d3a0a68be6de
294 294 tag: tip
295 295 user: test
296 296 date: Thu Jan 01 00:00:00 1970 +0000
297 297 summary: 2
298 298
299 299 *** runcommand status
300 300
301 301 >>> import os
302 302 >>> from hgclient import bprint, check, readchannel, runcommand
303 303 >>> @check
304 304 ... def bookmarks(server):
305 305 ... readchannel(server)
306 306 ... runcommand(server, [b'bookmarks'])
307 307 ...
308 308 ... # changes .hg/bookmarks
309 309 ... os.system('hg bookmark -i bm1')
310 310 ... os.system('hg bookmark -i bm2')
311 311 ... runcommand(server, [b'bookmarks'])
312 312 ...
313 313 ... # changes .hg/bookmarks.current
314 314 ... os.system('hg upd bm1 -q')
315 315 ... runcommand(server, [b'bookmarks'])
316 316 ...
317 317 ... runcommand(server, [b'bookmarks', b'bm3'])
318 318 ... f = open('a', 'ab')
319 319 ... f.write(b'a\n') and None
320 320 ... f.close()
321 321 ... runcommand(server, [b'commit', b'-Amm'])
322 322 ... runcommand(server, [b'bookmarks'])
323 323 ... bprint(b'')
324 324 *** runcommand bookmarks
325 325 no bookmarks set
326 326 *** runcommand bookmarks
327 327 bm1 1:d3a0a68be6de
328 328 bm2 1:d3a0a68be6de
329 329 *** runcommand bookmarks
330 330 * bm1 1:d3a0a68be6de
331 331 bm2 1:d3a0a68be6de
332 332 *** runcommand bookmarks bm3
333 333 *** runcommand commit -Amm
334 334 *** runcommand bookmarks
335 335 bm1 1:d3a0a68be6de
336 336 bm2 1:d3a0a68be6de
337 337 * bm3 2:aef17e88f5f0
338 338
339 339
340 340 >>> import os
341 341 >>> from hgclient import check, readchannel, runcommand
342 342 >>> @check
343 343 ... def tagscache(server):
344 344 ... readchannel(server)
345 345 ... runcommand(server, [b'id', b'-t', b'-r', b'0'])
346 346 ... os.system('hg tag -r 0 foo')
347 347 ... runcommand(server, [b'id', b'-t', b'-r', b'0'])
348 348 *** runcommand id -t -r 0
349 349
350 350 *** runcommand id -t -r 0
351 351 foo
352 352
353 353 >>> import os
354 354 >>> from hgclient import check, readchannel, runcommand
355 355 >>> @check
356 356 ... def setphase(server):
357 357 ... readchannel(server)
358 358 ... runcommand(server, [b'phase', b'-r', b'.'])
359 359 ... os.system('hg phase -r . -p')
360 360 ... runcommand(server, [b'phase', b'-r', b'.'])
361 361 *** runcommand phase -r .
362 362 3: draft
363 363 *** runcommand phase -r .
364 364 3: public
365 365
366 366 $ echo a >> a
367 367 >>> from hgclient import bprint, check, readchannel, runcommand
368 368 >>> @check
369 369 ... def rollback(server):
370 370 ... readchannel(server)
371 371 ... runcommand(server, [b'phase', b'-r', b'.', b'-p'])
372 372 ... runcommand(server, [b'commit', b'-Am.'])
373 373 ... runcommand(server, [b'rollback'])
374 374 ... runcommand(server, [b'phase', b'-r', b'.'])
375 375 ... bprint(b'')
376 376 *** runcommand phase -r . -p
377 377 no phases changed
378 378 *** runcommand commit -Am.
379 379 *** runcommand rollback
380 380 repository tip rolled back to revision 3 (undo commit)
381 381 working directory now based on revision 3
382 382 *** runcommand phase -r .
383 383 3: public
384 384
385 385
386 386 >>> import os
387 387 >>> from hgclient import check, readchannel, runcommand
388 388 >>> @check
389 389 ... def branch(server):
390 390 ... readchannel(server)
391 391 ... runcommand(server, [b'branch'])
392 392 ... os.system('hg branch foo')
393 393 ... runcommand(server, [b'branch'])
394 394 ... os.system('hg branch default')
395 395 *** runcommand branch
396 396 default
397 397 marked working directory as branch foo
398 398 (branches are permanent and global, did you want a bookmark?)
399 399 *** runcommand branch
400 400 foo
401 401 marked working directory as branch default
402 402 (branches are permanent and global, did you want a bookmark?)
403 403
404 404 $ touch .hgignore
405 405 >>> import os
406 406 >>> from hgclient import bprint, check, readchannel, runcommand
407 407 >>> @check
408 408 ... def hgignore(server):
409 409 ... readchannel(server)
410 410 ... runcommand(server, [b'commit', b'-Am.'])
411 411 ... f = open('ignored-file', 'ab')
412 412 ... f.write(b'') and None
413 413 ... f.close()
414 414 ... f = open('.hgignore', 'ab')
415 415 ... f.write(b'ignored-file')
416 416 ... f.close()
417 417 ... runcommand(server, [b'status', b'-i', b'-u'])
418 418 ... bprint(b'')
419 419 *** runcommand commit -Am.
420 420 adding .hgignore
421 421 *** runcommand status -i -u
422 422 I ignored-file
423 423
424 424
425 425 cache of non-public revisions should be invalidated on repository change
426 426 (issue4855):
427 427
428 428 >>> import os
429 429 >>> from hgclient import bprint, check, readchannel, runcommand
430 430 >>> @check
431 431 ... def phasesetscacheaftercommit(server):
432 432 ... readchannel(server)
433 433 ... # load _phasecache._phaserevs and _phasesets
434 434 ... runcommand(server, [b'log', b'-qr', b'draft()'])
435 435 ... # create draft commits by another process
436 436 ... for i in range(5, 7):
437 437 ... f = open('a', 'ab')
438 438 ... f.seek(0, os.SEEK_END)
439 439 ... f.write(b'a\n') and None
440 440 ... f.close()
441 441 ... os.system('hg commit -Aqm%d' % i)
442 442 ... # new commits should be listed as draft revisions
443 443 ... runcommand(server, [b'log', b'-qr', b'draft()'])
444 444 ... bprint(b'')
445 445 *** runcommand log -qr draft()
446 446 4:7966c8e3734d
447 447 *** runcommand log -qr draft()
448 448 4:7966c8e3734d
449 449 5:41f6602d1c4f
450 450 6:10501e202c35
451 451
452 452
453 453 >>> import os
454 454 >>> from hgclient import bprint, check, readchannel, runcommand
455 455 >>> @check
456 456 ... def phasesetscacheafterstrip(server):
457 457 ... readchannel(server)
458 458 ... # load _phasecache._phaserevs and _phasesets
459 459 ... runcommand(server, [b'log', b'-qr', b'draft()'])
460 460 ... # strip cached revisions by another process
461 461 ... os.system('hg --config extensions.strip= strip -q 5')
462 462 ... # shouldn't abort by "unknown revision '6'"
463 463 ... runcommand(server, [b'log', b'-qr', b'draft()'])
464 464 ... bprint(b'')
465 465 *** runcommand log -qr draft()
466 466 4:7966c8e3734d
467 467 5:41f6602d1c4f
468 468 6:10501e202c35
469 469 *** runcommand log -qr draft()
470 470 4:7966c8e3734d
471 471
472 472
473 473 cache of phase roots should be invalidated on strip (issue3827):
474 474
475 475 >>> import os
476 476 >>> from hgclient import check, readchannel, runcommand, sep
477 477 >>> @check
478 478 ... def phasecacheafterstrip(server):
479 479 ... readchannel(server)
480 480 ...
481 481 ... # create new head, 5:731265503d86
482 482 ... runcommand(server, [b'update', b'-C', b'0'])
483 483 ... f = open('a', 'ab')
484 484 ... f.write(b'a\n') and None
485 485 ... f.close()
486 486 ... runcommand(server, [b'commit', b'-Am.', b'a'])
487 487 ... runcommand(server, [b'log', b'-Gq'])
488 488 ...
489 489 ... # make it public; draft marker moves to 4:7966c8e3734d
490 490 ... runcommand(server, [b'phase', b'-p', b'.'])
491 491 ... # load _phasecache.phaseroots
492 492 ... runcommand(server, [b'phase', b'.'], outfilter=sep)
493 493 ...
494 494 ... # strip 1::4 outside server
495 495 ... os.system('hg -q --config extensions.mq= strip 1')
496 496 ...
497 497 ... # shouldn't raise "7966c8e3734d: no node!"
498 498 ... runcommand(server, [b'branches'])
499 499 *** runcommand update -C 0
500 500 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
501 501 (leaving bookmark bm3)
502 502 *** runcommand commit -Am. a
503 503 created new head
504 504 *** runcommand log -Gq
505 505 @ 5:731265503d86
506 506 |
507 507 | o 4:7966c8e3734d
508 508 | |
509 509 | o 3:b9b85890c400
510 510 | |
511 511 | o 2:aef17e88f5f0
512 512 | |
513 513 | o 1:d3a0a68be6de
514 514 |/
515 515 o 0:eff892de26ec
516 516
517 517 *** runcommand phase -p .
518 518 *** runcommand phase .
519 519 5: public
520 520 *** runcommand branches
521 521 default 1:731265503d86
522 522
523 523 in-memory cache must be reloaded if transaction is aborted. otherwise
524 524 changelog and manifest would have invalid node:
525 525
526 526 $ echo a >> a
527 527 >>> from hgclient import check, readchannel, runcommand
528 528 >>> @check
529 529 ... def txabort(server):
530 530 ... readchannel(server)
531 531 ... runcommand(server, [b'commit', b'--config', b'hooks.pretxncommit=false',
532 532 ... b'-mfoo'])
533 533 ... runcommand(server, [b'verify'])
534 534 *** runcommand commit --config hooks.pretxncommit=false -mfoo
535 535 transaction abort!
536 536 rollback completed
537 537 abort: pretxncommit hook exited with status 1
538 538 [40]
539 539 *** runcommand verify
540 540 checking changesets
541 541 checking manifests
542 542 crosschecking files in changesets and manifests
543 543 checking files
544 checking dirstate
544 545 checked 2 changesets with 2 changes to 1 files
545 546 $ hg revert --no-backup -aq
546 547
547 548 $ cat >> .hg/hgrc << EOF
548 549 > [experimental]
549 550 > evolution.createmarkers=True
550 551 > EOF
551 552
552 553 >>> import os
553 554 >>> from hgclient import check, readchannel, runcommand
554 555 >>> @check
555 556 ... def obsolete(server):
556 557 ... readchannel(server)
557 558 ...
558 559 ... runcommand(server, [b'up', b'null'])
559 560 ... runcommand(server, [b'phase', b'-df', b'tip'])
560 561 ... cmd = 'hg debugobsolete `hg log -r tip --template {node}`'
561 562 ... if os.name == 'nt':
562 563 ... cmd = 'sh -c "%s"' % cmd # run in sh, not cmd.exe
563 564 ... os.system(cmd)
564 565 ... runcommand(server, [b'log', b'--hidden'])
565 566 ... runcommand(server, [b'log'])
566 567 *** runcommand up null
567 568 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
568 569 *** runcommand phase -df tip
569 570 1 new obsolescence markers
570 571 obsoleted 1 changesets
571 572 *** runcommand log --hidden
572 573 changeset: 1:731265503d86
573 574 tag: tip
574 575 user: test
575 576 date: Thu Jan 01 00:00:00 1970 +0000
576 577 obsolete: pruned
577 578 summary: .
578 579
579 580 changeset: 0:eff892de26ec
580 581 bookmark: bm1
581 582 bookmark: bm2
582 583 bookmark: bm3
583 584 user: test
584 585 date: Thu Jan 01 00:00:00 1970 +0000
585 586 summary: 1
586 587
587 588 *** runcommand log
588 589 changeset: 0:eff892de26ec
589 590 bookmark: bm1
590 591 bookmark: bm2
591 592 bookmark: bm3
592 593 tag: tip
593 594 user: test
594 595 date: Thu Jan 01 00:00:00 1970 +0000
595 596 summary: 1
596 597
597 598
598 599 $ cat <<EOF >> .hg/hgrc
599 600 > [extensions]
600 601 > mq =
601 602 > EOF
602 603
603 604 >>> import os
604 605 >>> from hgclient import check, readchannel, runcommand
605 606 >>> @check
606 607 ... def mqoutsidechanges(server):
607 608 ... readchannel(server)
608 609 ...
609 610 ... # load repo.mq
610 611 ... runcommand(server, [b'qapplied'])
611 612 ... os.system('hg qnew 0.diff')
612 613 ... # repo.mq should be invalidated
613 614 ... runcommand(server, [b'qapplied'])
614 615 ...
615 616 ... runcommand(server, [b'qpop', b'--all'])
616 617 ... os.system('hg qqueue --create foo')
617 618 ... # repo.mq should be recreated to point to new queue
618 619 ... runcommand(server, [b'qqueue', b'--active'])
619 620 *** runcommand qapplied
620 621 *** runcommand qapplied
621 622 0.diff
622 623 *** runcommand qpop --all
623 624 popping 0.diff
624 625 patch queue now empty
625 626 *** runcommand qqueue --active
626 627 foo
627 628
628 629 $ cat <<'EOF' > ../dbgui.py
629 630 > import os
630 631 > import sys
631 632 > from mercurial import commands, registrar
632 633 > cmdtable = {}
633 634 > command = registrar.command(cmdtable)
634 635 > @command(b"debuggetpass", norepo=True)
635 636 > def debuggetpass(ui):
636 637 > ui.write(b"%s\n" % ui.getpass())
637 638 > @command(b"debugprompt", norepo=True)
638 639 > def debugprompt(ui):
639 640 > ui.write(b"%s\n" % ui.prompt(b"prompt:"))
640 641 > @command(b"debugpromptchoice", norepo=True)
641 642 > def debugpromptchoice(ui):
642 643 > msg = b"promptchoice (y/n)? $$ &Yes $$ &No"
643 644 > ui.write(b"%d\n" % ui.promptchoice(msg))
644 645 > @command(b"debugreadstdin", norepo=True)
645 646 > def debugreadstdin(ui):
646 647 > ui.write(b"read: %r\n" % sys.stdin.read(1))
647 648 > @command(b"debugwritestdout", norepo=True)
648 649 > def debugwritestdout(ui):
649 650 > os.write(1, b"low-level stdout fd and\n")
650 651 > sys.stdout.write("stdout should be redirected to stderr\n")
651 652 > sys.stdout.flush()
652 653 > EOF
653 654 $ cat <<EOF >> .hg/hgrc
654 655 > [extensions]
655 656 > dbgui = ../dbgui.py
656 657 > EOF
657 658
658 659 >>> from hgclient import check, readchannel, runcommand, stringio
659 660 >>> @check
660 661 ... def getpass(server):
661 662 ... readchannel(server)
662 663 ... runcommand(server, [b'debuggetpass', b'--config',
663 664 ... b'ui.interactive=True'],
664 665 ... input=stringio(b'1234\n'))
665 666 ... runcommand(server, [b'debuggetpass', b'--config',
666 667 ... b'ui.interactive=True'],
667 668 ... input=stringio(b'\n'))
668 669 ... runcommand(server, [b'debuggetpass', b'--config',
669 670 ... b'ui.interactive=True'],
670 671 ... input=stringio(b''))
671 672 ... runcommand(server, [b'debugprompt', b'--config',
672 673 ... b'ui.interactive=True'],
673 674 ... input=stringio(b'5678\n'))
674 675 ... runcommand(server, [b'debugprompt', b'--config',
675 676 ... b'ui.interactive=True'],
676 677 ... input=stringio(b'\nremainder\nshould\nnot\nbe\nread\n'))
677 678 ... runcommand(server, [b'debugreadstdin'])
678 679 ... runcommand(server, [b'debugwritestdout'])
679 680 *** runcommand debuggetpass --config ui.interactive=True
680 681 password: 1234
681 682 *** runcommand debuggetpass --config ui.interactive=True
682 683 password:
683 684 *** runcommand debuggetpass --config ui.interactive=True
684 685 password: abort: response expected
685 686 [255]
686 687 *** runcommand debugprompt --config ui.interactive=True
687 688 prompt: 5678
688 689 *** runcommand debugprompt --config ui.interactive=True
689 690 prompt: y
690 691 *** runcommand debugreadstdin
691 692 read: ''
692 693 *** runcommand debugwritestdout
693 694 low-level stdout fd and
694 695 stdout should be redirected to stderr
695 696
696 697
697 698 run commandserver in commandserver, which is silly but should work:
698 699
699 700 >>> from hgclient import bprint, check, readchannel, runcommand, stringio
700 701 >>> @check
701 702 ... def nested(server):
702 703 ... bprint(b'%c, %r' % readchannel(server))
703 704 ... class nestedserver(object):
704 705 ... stdin = stringio(b'getencoding\n')
705 706 ... stdout = stringio()
706 707 ... runcommand(server, [b'serve', b'--cmdserver', b'pipe'],
707 708 ... output=nestedserver.stdout, input=nestedserver.stdin)
708 709 ... nestedserver.stdout.seek(0)
709 710 ... bprint(b'%c, %r' % readchannel(nestedserver)) # hello
710 711 ... bprint(b'%c, %r' % readchannel(nestedserver)) # getencoding
711 712 o, 'capabilities: getencoding runcommand\nencoding: *\npid: *' (glob)
712 713 *** runcommand serve --cmdserver pipe
713 714 o, 'capabilities: getencoding runcommand\nencoding: *\npid: *' (glob)
714 715 r, '*' (glob)
715 716
716 717
717 718 start without repository:
718 719
719 720 $ cd ..
720 721
721 722 >>> from hgclient import bprint, check, readchannel, runcommand
722 723 >>> @check
723 724 ... def hellomessage(server):
724 725 ... ch, data = readchannel(server)
725 726 ... bprint(b'%c, %r' % (ch, data))
726 727 ... # run an arbitrary command to make sure the next thing the server
727 728 ... # sends isn't part of the hello message
728 729 ... runcommand(server, [b'id'])
729 730 o, 'capabilities: getencoding runcommand\nencoding: *\npid: *' (glob)
730 731 *** runcommand id
731 732 abort: there is no Mercurial repository here (.hg not found)
732 733 [10]
733 734
734 735 >>> from hgclient import check, readchannel, runcommand
735 736 >>> @check
736 737 ... def startwithoutrepo(server):
737 738 ... readchannel(server)
738 739 ... runcommand(server, [b'init', b'repo2'])
739 740 ... runcommand(server, [b'id', b'-R', b'repo2'])
740 741 *** runcommand init repo2
741 742 *** runcommand id -R repo2
742 743 000000000000 tip
743 744
744 745
745 746 don't fall back to cwd if invalid -R path is specified (issue4805):
746 747
747 748 $ cd repo
748 749 $ hg serve --cmdserver pipe -R ../nonexistent
749 750 abort: repository ../nonexistent not found
750 751 [255]
751 752 $ cd ..
752 753
753 754
754 755 #if no-windows
755 756
756 757 option to not shutdown on SIGINT:
757 758
758 759 $ cat <<'EOF' > dbgint.py
759 760 > import os
760 761 > import signal
761 762 > import time
762 763 > from mercurial import commands, registrar
763 764 > cmdtable = {}
764 765 > command = registrar.command(cmdtable)
765 766 > @command(b"debugsleep", norepo=True)
766 767 > def debugsleep(ui):
767 768 > time.sleep(1)
768 769 > @command(b"debugsuicide", norepo=True)
769 770 > def debugsuicide(ui):
770 771 > os.kill(os.getpid(), signal.SIGINT)
771 772 > time.sleep(1)
772 773 > EOF
773 774
774 775 >>> import signal
775 776 >>> import time
776 777 >>> from hgclient import checkwith, readchannel, runcommand
777 778 >>> @checkwith(extraargs=[b'--config', b'cmdserver.shutdown-on-interrupt=False',
778 779 ... b'--config', b'extensions.dbgint=dbgint.py'])
779 780 ... def nointr(server):
780 781 ... readchannel(server)
781 782 ... server.send_signal(signal.SIGINT) # server won't be terminated
782 783 ... time.sleep(1)
783 784 ... runcommand(server, [b'debugsleep'])
784 785 ... server.send_signal(signal.SIGINT) # server won't be terminated
785 786 ... runcommand(server, [b'debugsleep'])
786 787 ... runcommand(server, [b'debugsuicide']) # command can be interrupted
787 788 ... server.send_signal(signal.SIGTERM) # server will be terminated
788 789 ... time.sleep(1)
789 790 *** runcommand debugsleep
790 791 *** runcommand debugsleep
791 792 *** runcommand debugsuicide
792 793 interrupted!
793 794 killed!
794 795 [255]
795 796
796 797 #endif
797 798
798 799
799 800 structured message channel:
800 801
801 802 $ cat <<'EOF' >> repo2/.hg/hgrc
802 803 > [ui]
803 804 > # server --config should precede repository option
804 805 > message-output = stdio
805 806 > EOF
806 807
807 808 >>> from hgclient import bprint, checkwith, readchannel, runcommand
808 809 >>> @checkwith(extraargs=[b'--config', b'ui.message-output=channel',
809 810 ... b'--config', b'cmdserver.message-encodings=foo cbor'])
810 811 ... def verify(server):
811 812 ... _ch, data = readchannel(server)
812 813 ... bprint(data)
813 814 ... runcommand(server, [b'-R', b'repo2', b'verify'])
814 815 capabilities: getencoding runcommand
815 816 encoding: ascii
816 817 message-encoding: cbor
817 818 pid: * (glob)
818 819 pgid: * (glob) (no-windows !)
819 820 *** runcommand -R repo2 verify
820 821 message: '\xa2DdataTchecking changesets\nDtypeFstatus'
821 822 message: '\xa6Ditem@Cpos\xf6EtopicHcheckingEtotal\xf6DtypeHprogressDunit@'
822 823 message: '\xa2DdataSchecking manifests\nDtypeFstatus'
823 824 message: '\xa6Ditem@Cpos\xf6EtopicHcheckingEtotal\xf6DtypeHprogressDunit@'
824 825 message: '\xa2DdataX0crosschecking files in changesets and manifests\nDtypeFstatus'
825 826 message: '\xa6Ditem@Cpos\xf6EtopicMcrosscheckingEtotal\xf6DtypeHprogressDunit@'
826 827 message: '\xa2DdataOchecking files\nDtypeFstatus'
827 828 message: '\xa6Ditem@Cpos\xf6EtopicHcheckingEtotal\xf6DtypeHprogressDunit@'
829 message: '\xa2DdataRchecking dirstate\nDtypeFstatus'
828 830 message: '\xa2DdataX/checked 0 changesets with 0 changes to 0 files\nDtypeFstatus'
829 831
830 832 >>> from hgclient import checkwith, readchannel, runcommand, stringio
831 833 >>> @checkwith(extraargs=[b'--config', b'ui.message-output=channel',
832 834 ... b'--config', b'cmdserver.message-encodings=cbor',
833 835 ... b'--config', b'extensions.dbgui=dbgui.py'])
834 836 ... def prompt(server):
835 837 ... readchannel(server)
836 838 ... interactive = [b'--config', b'ui.interactive=True']
837 839 ... runcommand(server, [b'debuggetpass'] + interactive,
838 840 ... input=stringio(b'1234\n'))
839 841 ... runcommand(server, [b'debugprompt'] + interactive,
840 842 ... input=stringio(b'5678\n'))
841 843 ... runcommand(server, [b'debugpromptchoice'] + interactive,
842 844 ... input=stringio(b'n\n'))
843 845 *** runcommand debuggetpass --config ui.interactive=True
844 846 message: '\xa3DdataJpassword: Hpassword\xf5DtypeFprompt'
845 847 1234
846 848 *** runcommand debugprompt --config ui.interactive=True
847 849 message: '\xa3DdataGprompt:GdefaultAyDtypeFprompt'
848 850 5678
849 851 *** runcommand debugpromptchoice --config ui.interactive=True
850 852 message: '\xa4Gchoices\x82\x82AyCYes\x82AnBNoDdataTpromptchoice (y/n)? GdefaultAyDtypeFprompt'
851 853 1
852 854
853 855 bad message encoding:
854 856
855 857 $ hg serve --cmdserver pipe --config ui.message-output=channel
856 858 abort: no supported message encodings:
857 859 [255]
858 860 $ hg serve --cmdserver pipe --config ui.message-output=channel \
859 861 > --config cmdserver.message-encodings='foo bar'
860 862 abort: no supported message encodings: foo bar
861 863 [255]
862 864
863 865 unix domain socket:
864 866
865 867 $ cd repo
866 868 $ hg update -q
867 869
868 870 #if unix-socket unix-permissions
869 871
870 872 >>> from hgclient import bprint, check, readchannel, runcommand, stringio, unixserver
871 873 >>> server = unixserver(b'.hg/server.sock', b'.hg/server.log')
872 874 >>> def hellomessage(conn):
873 875 ... ch, data = readchannel(conn)
874 876 ... bprint(b'%c, %r' % (ch, data))
875 877 ... runcommand(conn, [b'id'])
876 878 >>> check(hellomessage, server.connect)
877 879 o, 'capabilities: getencoding runcommand\nencoding: *\npid: *' (glob)
878 880 *** runcommand id
879 881 eff892de26ec tip bm1/bm2/bm3
880 882 >>> def unknowncommand(conn):
881 883 ... readchannel(conn)
882 884 ... conn.stdin.write(b'unknowncommand\n')
883 885 >>> check(unknowncommand, server.connect) # error sent to server.log
884 886 >>> def serverinput(conn):
885 887 ... readchannel(conn)
886 888 ... patch = b"""
887 889 ... # HG changeset patch
888 890 ... # User test
889 891 ... # Date 0 0
890 892 ... 2
891 893 ...
892 894 ... diff -r eff892de26ec -r 1ed24be7e7a0 a
893 895 ... --- a/a
894 896 ... +++ b/a
895 897 ... @@ -1,1 +1,2 @@
896 898 ... 1
897 899 ... +2
898 900 ... """
899 901 ... runcommand(conn, [b'import', b'-'], input=stringio(patch))
900 902 ... runcommand(conn, [b'log', b'-rtip', b'-q'])
901 903 >>> check(serverinput, server.connect)
902 904 *** runcommand import -
903 905 applying patch from stdin
904 906 *** runcommand log -rtip -q
905 907 2:1ed24be7e7a0
906 908 >>> server.shutdown()
907 909
908 910 $ cat .hg/server.log
909 911 listening at .hg/server.sock
910 912 abort: unknown command unknowncommand
911 913 killed!
912 914 $ rm .hg/server.log
913 915
914 916 if server crashed before hello, traceback will be sent to 'e' channel as
915 917 last ditch:
916 918
917 919 $ cat <<'EOF' > ../earlycrasher.py
918 920 > from mercurial import commandserver, extensions
919 921 > def _serverequest(orig, ui, repo, conn, createcmdserver, prereposetups):
920 922 > def createcmdserver(*args, **kwargs):
921 923 > raise Exception('crash')
922 924 > return orig(ui, repo, conn, createcmdserver, prereposetups)
923 925 > def extsetup(ui):
924 926 > extensions.wrapfunction(commandserver, b'_serverequest', _serverequest)
925 927 > EOF
926 928 $ cat <<EOF >> .hg/hgrc
927 929 > [extensions]
928 930 > earlycrasher = ../earlycrasher.py
929 931 > EOF
930 932 >>> from hgclient import bprint, check, readchannel, unixserver
931 933 >>> server = unixserver(b'.hg/server.sock', b'.hg/server.log')
932 934 >>> def earlycrash(conn):
933 935 ... while True:
934 936 ... try:
935 937 ... ch, data = readchannel(conn)
936 938 ... for l in data.splitlines(True):
937 939 ... if not l.startswith(b' '):
938 940 ... bprint(b'%c, %r' % (ch, l))
939 941 ... except EOFError:
940 942 ... break
941 943 >>> check(earlycrash, server.connect)
942 944 e, 'Traceback (most recent call last):\n'
943 945 e, 'Exception: crash\n'
944 946 >>> server.shutdown()
945 947
946 948 $ cat .hg/server.log | grep -v '^ '
947 949 listening at .hg/server.sock
948 950 Traceback (most recent call last):
949 951 Exception: crash
950 952 killed!
951 953 #endif
952 954 #if no-unix-socket
953 955
954 956 $ hg serve --cmdserver unix -a .hg/server.sock
955 957 abort: unsupported platform
956 958 [255]
957 959
958 960 #endif
959 961
960 962 $ cd ..
961 963
962 964 Test that accessing to invalid changelog cache is avoided at
963 965 subsequent operations even if repo object is reused even after failure
964 966 of transaction (see 0a7610758c42 also)
965 967
966 968 "hg log" after failure of transaction is needed to detect invalid
967 969 cache in repoview: this can't detect by "hg verify" only.
968 970
969 971 Combination of "finalization" and "empty-ness of changelog" (2 x 2 =
970 972 4) are tested, because '00changelog.i' are differently changed in each
971 973 cases.
972 974
973 975 $ cat > $TESTTMP/failafterfinalize.py <<EOF
974 976 > # extension to abort transaction after finalization forcibly
975 977 > from mercurial import commands, error, extensions, lock as lockmod
976 978 > from mercurial import registrar
977 979 > cmdtable = {}
978 980 > command = registrar.command(cmdtable)
979 981 > configtable = {}
980 982 > configitem = registrar.configitem(configtable)
981 983 > configitem(b'failafterfinalize', b'fail',
982 984 > default=None,
983 985 > )
984 986 > def fail(tr):
985 987 > raise error.Abort(b'fail after finalization')
986 988 > def reposetup(ui, repo):
987 989 > class failrepo(repo.__class__):
988 990 > def commitctx(self, ctx, error=False, origctx=None):
989 991 > if self.ui.configbool(b'failafterfinalize', b'fail'):
990 992 > # 'sorted()' by ASCII code on category names causes
991 993 > # invoking 'fail' after finalization of changelog
992 994 > # using "'cl-%i' % id(self)" as category name
993 995 > self.currenttransaction().addfinalize(b'zzzzzzzz', fail)
994 996 > return super(failrepo, self).commitctx(ctx, error, origctx)
995 997 > repo.__class__ = failrepo
996 998 > EOF
997 999
998 1000 $ hg init repo3
999 1001 $ cd repo3
1000 1002
1001 1003 $ cat <<EOF >> $HGRCPATH
1002 1004 > [command-templates]
1003 1005 > log = {rev} {desc|firstline} ({files})\n
1004 1006 >
1005 1007 > [extensions]
1006 1008 > failafterfinalize = $TESTTMP/failafterfinalize.py
1007 1009 > EOF
1008 1010
1009 1011 - test failure with "empty changelog"
1010 1012
1011 1013 $ echo foo > foo
1012 1014 $ hg add foo
1013 1015
1014 1016 (failure before finalization)
1015 1017
1016 1018 >>> from hgclient import check, readchannel, runcommand
1017 1019 >>> @check
1018 1020 ... def abort(server):
1019 1021 ... readchannel(server)
1020 1022 ... runcommand(server, [b'commit',
1021 1023 ... b'--config', b'hooks.pretxncommit=false',
1022 1024 ... b'-mfoo'])
1023 1025 ... runcommand(server, [b'log'])
1024 1026 ... runcommand(server, [b'verify', b'-q'])
1025 1027 *** runcommand commit --config hooks.pretxncommit=false -mfoo
1026 1028 transaction abort!
1027 1029 rollback completed
1028 1030 abort: pretxncommit hook exited with status 1
1029 1031 [40]
1030 1032 *** runcommand log
1031 1033 *** runcommand verify -q
1032 1034
1033 1035 (failure after finalization)
1034 1036
1035 1037 >>> from hgclient import check, readchannel, runcommand
1036 1038 >>> @check
1037 1039 ... def abort(server):
1038 1040 ... readchannel(server)
1039 1041 ... runcommand(server, [b'commit',
1040 1042 ... b'--config', b'failafterfinalize.fail=true',
1041 1043 ... b'-mfoo'])
1042 1044 ... runcommand(server, [b'log'])
1043 1045 ... runcommand(server, [b'verify', b'-q'])
1044 1046 *** runcommand commit --config failafterfinalize.fail=true -mfoo
1045 1047 transaction abort!
1046 1048 rollback completed
1047 1049 abort: fail after finalization
1048 1050 [255]
1049 1051 *** runcommand log
1050 1052 *** runcommand verify -q
1051 1053
1052 1054 - test failure with "not-empty changelog"
1053 1055
1054 1056 $ echo bar > bar
1055 1057 $ hg add bar
1056 1058 $ hg commit -mbar bar
1057 1059
1058 1060 (failure before finalization)
1059 1061
1060 1062 >>> from hgclient import check, readchannel, runcommand
1061 1063 >>> @check
1062 1064 ... def abort(server):
1063 1065 ... readchannel(server)
1064 1066 ... runcommand(server, [b'commit',
1065 1067 ... b'--config', b'hooks.pretxncommit=false',
1066 1068 ... b'-mfoo', b'foo'])
1067 1069 ... runcommand(server, [b'log'])
1068 1070 ... runcommand(server, [b'verify', b'-q'])
1069 1071 *** runcommand commit --config hooks.pretxncommit=false -mfoo foo
1070 1072 transaction abort!
1071 1073 rollback completed
1072 1074 abort: pretxncommit hook exited with status 1
1073 1075 [40]
1074 1076 *** runcommand log
1075 1077 0 bar (bar)
1076 1078 *** runcommand verify -q
1077 1079
1078 1080 (failure after finalization)
1079 1081
1080 1082 >>> from hgclient import check, readchannel, runcommand
1081 1083 >>> @check
1082 1084 ... def abort(server):
1083 1085 ... readchannel(server)
1084 1086 ... runcommand(server, [b'commit',
1085 1087 ... b'--config', b'failafterfinalize.fail=true',
1086 1088 ... b'-mfoo', b'foo'])
1087 1089 ... runcommand(server, [b'log'])
1088 1090 ... runcommand(server, [b'verify', b'-q'])
1089 1091 *** runcommand commit --config failafterfinalize.fail=true -mfoo foo
1090 1092 transaction abort!
1091 1093 rollback completed
1092 1094 abort: fail after finalization
1093 1095 [255]
1094 1096 *** runcommand log
1095 1097 0 bar (bar)
1096 1098 *** runcommand verify -q
1097 1099
1098 1100 $ cd ..
1099 1101
1100 1102 Test symlink traversal over cached audited paths:
1101 1103 -------------------------------------------------
1102 1104
1103 1105 #if symlink
1104 1106
1105 1107 set up symlink hell
1106 1108
1107 1109 $ mkdir merge-symlink-out
1108 1110 $ hg init merge-symlink
1109 1111 $ cd merge-symlink
1110 1112 $ touch base
1111 1113 $ hg commit -qAm base
1112 1114 $ ln -s ../merge-symlink-out a
1113 1115 $ hg commit -qAm 'symlink a -> ../merge-symlink-out'
1114 1116 $ hg up -q 0
1115 1117 $ mkdir a
1116 1118 $ touch a/poisoned
1117 1119 $ hg commit -qAm 'file a/poisoned'
1118 1120 $ hg log -G -T '{rev}: {desc}\n'
1119 1121 @ 2: file a/poisoned
1120 1122 |
1121 1123 | o 1: symlink a -> ../merge-symlink-out
1122 1124 |/
1123 1125 o 0: base
1124 1126
1125 1127
1126 1128 try trivial merge after update: cache of audited paths should be discarded,
1127 1129 and the merge should fail (issue5628)
1128 1130
1129 1131 $ hg up -q null
1130 1132 >>> from hgclient import check, readchannel, runcommand
1131 1133 >>> @check
1132 1134 ... def merge(server):
1133 1135 ... readchannel(server)
1134 1136 ... # audit a/poisoned as a good path
1135 1137 ... runcommand(server, [b'up', b'-qC', b'2'])
1136 1138 ... runcommand(server, [b'up', b'-qC', b'1'])
1137 1139 ... # here a is a symlink, so a/poisoned is bad
1138 1140 ... runcommand(server, [b'merge', b'2'])
1139 1141 *** runcommand up -qC 2
1140 1142 *** runcommand up -qC 1
1141 1143 *** runcommand merge 2
1142 1144 abort: path 'a/poisoned' traverses symbolic link 'a'
1143 1145 [255]
1144 1146 $ ls ../merge-symlink-out
1145 1147
1146 1148 cache of repo.auditor should be discarded, so matcher would never traverse
1147 1149 symlinks:
1148 1150
1149 1151 $ hg up -qC 0
1150 1152 $ touch ../merge-symlink-out/poisoned
1151 1153 >>> from hgclient import check, readchannel, runcommand
1152 1154 >>> @check
1153 1155 ... def files(server):
1154 1156 ... readchannel(server)
1155 1157 ... runcommand(server, [b'up', b'-qC', b'2'])
1156 1158 ... # audit a/poisoned as a good path
1157 1159 ... runcommand(server, [b'files', b'a/poisoned'])
1158 1160 ... runcommand(server, [b'up', b'-qC', b'0'])
1159 1161 ... runcommand(server, [b'up', b'-qC', b'1'])
1160 1162 ... # here 'a' is a symlink, so a/poisoned should be warned
1161 1163 ... runcommand(server, [b'files', b'a/poisoned'])
1162 1164 *** runcommand up -qC 2
1163 1165 *** runcommand files a/poisoned
1164 1166 a/poisoned
1165 1167 *** runcommand up -qC 0
1166 1168 *** runcommand up -qC 1
1167 1169 *** runcommand files a/poisoned
1168 1170 abort: path 'a/poisoned' traverses symbolic link 'a'
1169 1171 [255]
1170 1172
1171 1173 $ cd ..
1172 1174
1173 1175 #endif
@@ -1,535 +1,537 b''
1 1 #require repofncache
2 2
3 3 An extension which will set fncache chunksize to 1 byte to make sure that logic
4 4 does not break
5 5
6 6 $ cat > chunksize.py <<EOF
7 7 > from mercurial import store
8 8 > store.fncache_chunksize = 1
9 9 > EOF
10 10
11 11 $ cat >> $HGRCPATH <<EOF
12 12 > [extensions]
13 13 > chunksize = $TESTTMP/chunksize.py
14 14 > EOF
15 15
16 16 Init repo1:
17 17
18 18 $ hg init repo1
19 19 $ cd repo1
20 20 $ echo "some text" > a
21 21 $ hg add
22 22 adding a
23 23 $ hg ci -m first
24 24 $ cat .hg/store/fncache | sort
25 25 data/a.i
26 26
27 27 Testing a.i/b:
28 28
29 29 $ mkdir a.i
30 30 $ echo "some other text" > a.i/b
31 31 $ hg add
32 32 adding a.i/b
33 33 $ hg ci -m second
34 34 $ cat .hg/store/fncache | sort
35 35 data/a.i
36 36 data/a.i.hg/b.i
37 37
38 38 Testing a.i.hg/c:
39 39
40 40 $ mkdir a.i.hg
41 41 $ echo "yet another text" > a.i.hg/c
42 42 $ hg add
43 43 adding a.i.hg/c
44 44 $ hg ci -m third
45 45 $ cat .hg/store/fncache | sort
46 46 data/a.i
47 47 data/a.i.hg.hg/c.i
48 48 data/a.i.hg/b.i
49 49
50 50 Testing verify:
51 51
52 52 $ hg verify -q
53 53
54 54 $ rm .hg/store/fncache
55 55
56 56 $ hg verify
57 57 checking changesets
58 58 checking manifests
59 59 crosschecking files in changesets and manifests
60 60 checking files
61 61 warning: revlog 'data/a.i' not in fncache!
62 62 warning: revlog 'data/a.i.hg/c.i' not in fncache!
63 63 warning: revlog 'data/a.i/b.i' not in fncache!
64 checking dirstate
64 65 checked 3 changesets with 3 changes to 3 files
65 66 3 warnings encountered!
66 67 hint: run "hg debugrebuildfncache" to recover from corrupt fncache
67 68
68 69 Follow the hint to make sure it works
69 70
70 71 $ hg debugrebuildfncache
71 72 adding data/a.i
72 73 adding data/a.i.hg/c.i
73 74 adding data/a.i/b.i
74 75 3 items added, 0 removed from fncache
75 76
76 77 $ hg verify -q
77 78
78 79 $ cd ..
79 80
80 81 Non store repo:
81 82
82 83 $ hg --config format.usestore=False init foo
83 84 $ cd foo
84 85 $ mkdir tst.d
85 86 $ echo foo > tst.d/foo
86 87 $ hg ci -Amfoo
87 88 adding tst.d/foo
88 89 $ find .hg | sort
89 90 .hg
90 91 .hg/00changelog.i
91 92 .hg/00manifest.i
92 93 .hg/cache
93 94 .hg/cache/branch2-served
94 95 .hg/cache/rbc-names-v1
95 96 .hg/cache/rbc-revs-v1
96 97 .hg/data
97 98 .hg/data/tst.d.hg
98 99 .hg/data/tst.d.hg/foo.i
99 100 .hg/dirstate
100 101 .hg/fsmonitor.state (fsmonitor !)
101 102 .hg/last-message.txt
102 103 .hg/phaseroots
103 104 .hg/requires
104 105 .hg/undo
105 106 .hg/undo.backup.dirstate
106 107 .hg/undo.backupfiles
107 108 .hg/undo.bookmarks
108 109 .hg/undo.branch
109 110 .hg/undo.desc
110 111 .hg/undo.dirstate
111 112 .hg/undo.phaseroots
112 113 .hg/wcache
113 114 .hg/wcache/checkisexec (execbit !)
114 115 .hg/wcache/checklink (symlink !)
115 116 .hg/wcache/checklink-target (symlink !)
116 117 .hg/wcache/manifestfulltextcache (reporevlogstore !)
117 118 $ cd ..
118 119
119 120 Non fncache repo:
120 121
121 122 $ hg --config format.usefncache=False init bar
122 123 $ cd bar
123 124 $ mkdir tst.d
124 125 $ echo foo > tst.d/Foo
125 126 $ hg ci -Amfoo
126 127 adding tst.d/Foo
127 128 $ find .hg | sort
128 129 .hg
129 130 .hg/00changelog.i
130 131 .hg/cache
131 132 .hg/cache/branch2-served
132 133 .hg/cache/rbc-names-v1
133 134 .hg/cache/rbc-revs-v1
134 135 .hg/dirstate
135 136 .hg/fsmonitor.state (fsmonitor !)
136 137 .hg/last-message.txt
137 138 .hg/requires
138 139 .hg/store
139 140 .hg/store/00changelog.i
140 141 .hg/store/00manifest.i
141 142 .hg/store/data
142 143 .hg/store/data/tst.d.hg
143 144 .hg/store/data/tst.d.hg/_foo.i
144 145 .hg/store/phaseroots
145 146 .hg/store/requires
146 147 .hg/store/undo
147 148 .hg/store/undo.backupfiles
148 149 .hg/store/undo.phaseroots
149 150 .hg/undo.backup.dirstate
150 151 .hg/undo.bookmarks
151 152 .hg/undo.branch
152 153 .hg/undo.desc
153 154 .hg/undo.dirstate
154 155 .hg/wcache
155 156 .hg/wcache/checkisexec (execbit !)
156 157 .hg/wcache/checklink (symlink !)
157 158 .hg/wcache/checklink-target (symlink !)
158 159 .hg/wcache/manifestfulltextcache (reporevlogstore !)
159 160 $ cd ..
160 161
161 162 Encoding of reserved / long paths in the store
162 163
163 164 $ hg init r2
164 165 $ cd r2
165 166 $ cat <<EOF > .hg/hgrc
166 167 > [ui]
167 168 > portablefilenames = ignore
168 169 > EOF
169 170
170 171 $ hg import -q --bypass - <<EOF
171 172 > # HG changeset patch
172 173 > # User test
173 174 > # Date 0 0
174 175 > # Node ID 1c7a2f7cb77be1a0def34e4c7cabc562ad98fbd7
175 176 > # Parent 0000000000000000000000000000000000000000
176 177 > 1
177 178 >
178 179 > diff --git a/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-12345-ABCDEFGHIJKLMNOPRSTUVWXYZ-abcdefghjiklmnopqrstuvwxyz b/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-12345-ABCDEFGHIJKLMNOPRSTUVWXYZ-abcdefghjiklmnopqrstuvwxyz
179 180 > new file mode 100644
180 181 > --- /dev/null
181 182 > +++ b/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-123456789-12.3456789-12345-ABCDEFGHIJKLMNOPRSTUVWXYZ-abcdefghjiklmnopqrstuvwxyz
182 183 > @@ -0,0 +1,1 @@
183 184 > +foo
184 185 > diff --git a/AUX/SECOND/X.PRN/FOURTH/FI:FTH/SIXTH/SEVENTH/EIGHTH/NINETH/TENTH/ELEVENTH/LOREMIPSUM.TXT b/AUX/SECOND/X.PRN/FOURTH/FI:FTH/SIXTH/SEVENTH/EIGHTH/NINETH/TENTH/ELEVENTH/LOREMIPSUM.TXT
185 186 > new file mode 100644
186 187 > --- /dev/null
187 188 > +++ b/AUX/SECOND/X.PRN/FOURTH/FI:FTH/SIXTH/SEVENTH/EIGHTH/NINETH/TENTH/ELEVENTH/LOREMIPSUM.TXT
188 189 > @@ -0,0 +1,1 @@
189 190 > +foo
190 191 > diff --git a/Project Planning/Resources/AnotherLongDirectoryName/Followedbyanother/AndAnother/AndThenAnExtremelyLongFileName.txt b/Project Planning/Resources/AnotherLongDirectoryName/Followedbyanother/AndAnother/AndThenAnExtremelyLongFileName.txt
191 192 > new file mode 100644
192 193 > --- /dev/null
193 194 > +++ b/Project Planning/Resources/AnotherLongDirectoryName/Followedbyanother/AndAnother/AndThenAnExtremelyLongFileName.txt
194 195 > @@ -0,0 +1,1 @@
195 196 > +foo
196 197 > diff --git a/bla.aux/prn/PRN/lpt/com3/nul/coma/foo.NUL/normal.c b/bla.aux/prn/PRN/lpt/com3/nul/coma/foo.NUL/normal.c
197 198 > new file mode 100644
198 199 > --- /dev/null
199 200 > +++ b/bla.aux/prn/PRN/lpt/com3/nul/coma/foo.NUL/normal.c
200 201 > @@ -0,0 +1,1 @@
201 202 > +foo
202 203 > diff --git a/enterprise/openesbaddons/contrib-imola/corba-bc/netbeansplugin/wsdlExtension/src/main/java/META-INF/services/org.netbeans.modules.xml.wsdl.bindingsupport.spi.ExtensibilityElementTemplateProvider b/enterprise/openesbaddons/contrib-imola/corba-bc/netbeansplugin/wsdlExtension/src/main/java/META-INF/services/org.netbeans.modules.xml.wsdl.bindingsupport.spi.ExtensibilityElementTemplateProvider
203 204 > new file mode 100644
204 205 > --- /dev/null
205 206 > +++ b/enterprise/openesbaddons/contrib-imola/corba-bc/netbeansplugin/wsdlExtension/src/main/java/META-INF/services/org.netbeans.modules.xml.wsdl.bindingsupport.spi.ExtensibilityElementTemplateProvider
206 207 > @@ -0,0 +1,1 @@
207 208 > +foo
208 209 > EOF
209 210
210 211 $ find .hg/store -name *.i | sort
211 212 .hg/store/00changelog.i
212 213 .hg/store/00manifest.i
213 214 .hg/store/data/bla.aux/pr~6e/_p_r_n/lpt/co~6d3/nu~6c/coma/foo._n_u_l/normal.c.i
214 215 .hg/store/dh/12345678/12345678/12345678/12345678/12345678/12345678/12345678/12345/xxxxxx168e07b38e65eff86ab579afaaa8e30bfbe0f35f.i
215 216 .hg/store/dh/au~78/second/x.prn/fourth/fi~3afth/sixth/seventh/eighth/nineth/tenth/loremia20419e358ddff1bf8751e38288aff1d7c32ec05.i
216 217 .hg/store/dh/enterpri/openesba/contrib-/corba-bc/netbeans/wsdlexte/src/main/java/org.net7018f27961fdf338a598a40c4683429e7ffb9743.i
217 218 .hg/store/dh/project_/resource/anotherl/followed/andanoth/andthenanextremelylongfilename0d8e1f4187c650e2f1fdca9fd90f786bc0976b6b.i
218 219
219 220 $ cd ..
220 221
221 222 Aborting lock does not prevent fncache writes
222 223
223 224 $ cat > exceptionext.py <<EOF
224 225 > import os
225 226 > from mercurial import commands, error, extensions
226 227 >
227 228 > def lockexception(orig, vfs, lockname, wait, releasefn, *args, **kwargs):
228 229 > def releasewrap():
229 230 > l.held = False # ensure __del__ is a noop
230 231 > raise error.Abort(b"forced lock failure")
231 232 > l = orig(vfs, lockname, wait, releasewrap, *args, **kwargs)
232 233 > return l
233 234 >
234 235 > def reposetup(ui, repo):
235 236 > extensions.wrapfunction(repo, '_lock', lockexception)
236 237 >
237 238 > cmdtable = {}
238 239 >
239 240 > # wrap "commit" command to prevent wlock from being '__del__()'-ed
240 241 > # at the end of dispatching (for intentional "forced lcok failure")
241 242 > def commitwrap(orig, ui, repo, *pats, **opts):
242 243 > repo = repo.unfiltered() # to use replaced repo._lock certainly
243 244 > wlock = repo.wlock()
244 245 > try:
245 246 > return orig(ui, repo, *pats, **opts)
246 247 > finally:
247 248 > # multiple 'relase()' is needed for complete releasing wlock,
248 249 > # because "forced" abort at last releasing store lock
249 250 > # prevents wlock from being released at same 'lockmod.release()'
250 251 > for i in range(wlock.held):
251 252 > wlock.release()
252 253 >
253 254 > def extsetup(ui):
254 255 > extensions.wrapcommand(commands.table, b"commit", commitwrap)
255 256 > EOF
256 257 $ extpath=`pwd`/exceptionext.py
257 258 $ hg init fncachetxn
258 259 $ cd fncachetxn
259 260 $ printf "[extensions]\nexceptionext=$extpath\n" >> .hg/hgrc
260 261 $ touch y
261 262 $ hg ci -qAm y
262 263 abort: forced lock failure
263 264 [255]
264 265 $ cat .hg/store/fncache
265 266 data/y.i
266 267
267 268 Aborting transaction prevents fncache change
268 269
269 270 $ cat > ../exceptionext.py <<EOF
270 271 > import os
271 272 > from mercurial import commands, error, extensions, localrepo
272 273 >
273 274 > def wrapper(orig, self, *args, **kwargs):
274 275 > tr = orig(self, *args, **kwargs)
275 276 > def fail(tr):
276 277 > raise error.Abort(b"forced transaction failure")
277 278 > # zzz prefix to ensure it sorted after store.write
278 279 > tr.addfinalize(b'zzz-forcefails', fail)
279 280 > return tr
280 281 >
281 282 > def uisetup(ui):
282 283 > extensions.wrapfunction(
283 284 > localrepo.localrepository, b'transaction', wrapper)
284 285 >
285 286 > cmdtable = {}
286 287 >
287 288 > EOF
288 289
289 290 Clean cached version
290 291 $ rm -f "${extpath}c"
291 292 $ rm -Rf "`dirname $extpath`/__pycache__"
292 293
293 294 $ touch z
294 295 $ hg ci -qAm z
295 296 transaction abort!
296 297 rollback completed
297 298 abort: forced transaction failure
298 299 [255]
299 300 $ cat .hg/store/fncache
300 301 data/y.i
301 302
302 303 Aborted transactions can be recovered later
303 304
304 305 $ cat > ../exceptionext.py <<EOF
305 306 > import os
306 307 > from mercurial import (
307 308 > commands,
308 309 > error,
309 310 > extensions,
310 311 > localrepo,
311 312 > transaction,
312 313 > )
313 314 >
314 315 > def trwrapper(orig, self, *args, **kwargs):
315 316 > tr = orig(self, *args, **kwargs)
316 317 > def fail(tr):
317 318 > raise error.Abort(b"forced transaction failure")
318 319 > # zzz prefix to ensure it sorted after store.write
319 320 > tr.addfinalize(b'zzz-forcefails', fail)
320 321 > return tr
321 322 >
322 323 > def abortwrapper(orig, self, *args, **kwargs):
323 324 > raise error.Abort(b"forced transaction failure")
324 325 >
325 326 > def uisetup(ui):
326 327 > extensions.wrapfunction(localrepo.localrepository, 'transaction',
327 328 > trwrapper)
328 329 > extensions.wrapfunction(transaction.transaction, '_abort',
329 330 > abortwrapper)
330 331 >
331 332 > cmdtable = {}
332 333 >
333 334 > EOF
334 335
335 336 Clean cached versions
336 337 $ rm -f "${extpath}c"
337 338 $ rm -Rf "`dirname $extpath`/__pycache__"
338 339
339 340 $ hg up -q 1
340 341 $ touch z
341 342 $ hg ci -qAm z 2>/dev/null
342 343 [255]
343 344 $ cat .hg/store/fncache | sort
344 345 data/y.i
345 346 data/z.i
346 347 $ hg recover --verify
347 348 rolling back interrupted transaction
348 349 checking changesets
349 350 checking manifests
350 351 crosschecking files in changesets and manifests
351 352 checking files
353 checking dirstate
352 354 checked 1 changesets with 1 changes to 1 files
353 355 $ cat .hg/store/fncache
354 356 data/y.i
355 357
356 358 $ cd ..
357 359
358 360 debugrebuildfncache does nothing unless repo has fncache requirement
359 361
360 362 $ hg --config format.usefncache=false init nofncache
361 363 $ cd nofncache
362 364 $ hg debugrebuildfncache
363 365 (not rebuilding fncache because repository does not support fncache)
364 366
365 367 $ cd ..
366 368
367 369 debugrebuildfncache works on empty repository
368 370
369 371 $ hg init empty
370 372 $ cd empty
371 373 $ hg debugrebuildfncache
372 374 fncache already up to date
373 375 $ cd ..
374 376
375 377 debugrebuildfncache on an up to date repository no-ops
376 378
377 379 $ hg init repo
378 380 $ cd repo
379 381 $ echo initial > foo
380 382 $ echo initial > .bar
381 383 $ hg commit -A -m initial
382 384 adding .bar
383 385 adding foo
384 386
385 387 $ cat .hg/store/fncache | sort
386 388 data/.bar.i
387 389 data/foo.i
388 390
389 391 $ hg debugrebuildfncache
390 392 fncache already up to date
391 393
392 394 debugrebuildfncache restores deleted fncache file
393 395
394 396 $ rm -f .hg/store/fncache
395 397 $ hg debugrebuildfncache
396 398 adding data/.bar.i
397 399 adding data/foo.i
398 400 2 items added, 0 removed from fncache
399 401
400 402 $ cat .hg/store/fncache | sort
401 403 data/.bar.i
402 404 data/foo.i
403 405
404 406 Rebuild after rebuild should no-op
405 407
406 408 $ hg debugrebuildfncache
407 409 fncache already up to date
408 410
409 411 A single missing file should get restored, an extra file should be removed
410 412
411 413 $ cat > .hg/store/fncache << EOF
412 414 > data/foo.i
413 415 > data/bad-entry.i
414 416 > EOF
415 417
416 418 $ hg debugrebuildfncache
417 419 removing data/bad-entry.i
418 420 adding data/.bar.i
419 421 1 items added, 1 removed from fncache
420 422
421 423 $ cat .hg/store/fncache | sort
422 424 data/.bar.i
423 425 data/foo.i
424 426
425 427 debugrebuildfncache recovers from truncated line in fncache
426 428
427 429 $ printf a > .hg/store/fncache
428 430 $ hg debugrebuildfncache
429 431 fncache does not ends with a newline
430 432 adding data/.bar.i
431 433 adding data/foo.i
432 434 2 items added, 0 removed from fncache
433 435
434 436 $ cat .hg/store/fncache | sort
435 437 data/.bar.i
436 438 data/foo.i
437 439
438 440 $ cd ..
439 441
440 442 Try a simple variation without dotencode to ensure fncache is ignorant of encoding
441 443
442 444 $ hg --config format.dotencode=false init nodotencode
443 445 $ cd nodotencode
444 446 $ echo initial > foo
445 447 $ echo initial > .bar
446 448 $ hg commit -A -m initial
447 449 adding .bar
448 450 adding foo
449 451
450 452 $ cat .hg/store/fncache | sort
451 453 data/.bar.i
452 454 data/foo.i
453 455
454 456 $ rm .hg/store/fncache
455 457 $ hg debugrebuildfncache
456 458 adding data/.bar.i
457 459 adding data/foo.i
458 460 2 items added, 0 removed from fncache
459 461
460 462 $ cat .hg/store/fncache | sort
461 463 data/.bar.i
462 464 data/foo.i
463 465
464 466 $ cd ..
465 467
466 468 In repositories that have accumulated a large number of files over time, the
467 469 fncache file is going to be large. If we possibly can avoid loading it, so much the better.
468 470 The cache should not loaded when committing changes to existing files, or when unbundling
469 471 changesets that only contain changes to existing files:
470 472
471 473 $ cat > fncacheloadwarn.py << EOF
472 474 > from mercurial import extensions, localrepo
473 475 >
474 476 > def extsetup(ui):
475 477 > def wrapstore(orig, requirements, *args):
476 478 > store = orig(requirements, *args)
477 479 > if b'store' in requirements and b'fncache' in requirements:
478 480 > instrumentfncachestore(store, ui)
479 481 > return store
480 482 > extensions.wrapfunction(localrepo, 'makestore', wrapstore)
481 483 >
482 484 > def instrumentfncachestore(fncachestore, ui):
483 485 > class instrumentedfncache(type(fncachestore.fncache)):
484 486 > def _load(self):
485 487 > ui.warn(b'fncache load triggered!\n')
486 488 > super(instrumentedfncache, self)._load()
487 489 > fncachestore.fncache.__class__ = instrumentedfncache
488 490 > EOF
489 491
490 492 $ fncachextpath=`pwd`/fncacheloadwarn.py
491 493 $ hg init nofncacheload
492 494 $ cd nofncacheload
493 495 $ printf "[extensions]\nfncacheloadwarn=$fncachextpath\n" >> .hg/hgrc
494 496
495 497 A new file should trigger a load, as we'd want to update the fncache set in that case:
496 498
497 499 $ touch foo
498 500 $ hg ci -qAm foo
499 501 fncache load triggered!
500 502
501 503 But modifying that file should not:
502 504
503 505 $ echo bar >> foo
504 506 $ hg ci -qm foo
505 507
506 508 If a transaction has been aborted, the zero-size truncated index file will
507 509 not prevent the fncache from being loaded; rather than actually abort
508 510 a transaction, we simulate the situation by creating a zero-size index file:
509 511
510 512 $ touch .hg/store/data/bar.i
511 513 $ touch bar
512 514 $ hg ci -qAm bar
513 515 fncache load triggered!
514 516
515 517 Unbundling should follow the same rules; existing files should not cause a load:
516 518
517 519 (loading during the clone is expected)
518 520 $ hg clone -q . tobundle
519 521 fncache load triggered!
520 522 fncache load triggered!
521 523
522 524 $ echo 'new line' > tobundle/bar
523 525 $ hg -R tobundle ci -qm bar
524 526 $ hg -R tobundle bundle -q barupdated.hg
525 527 $ hg unbundle -q barupdated.hg
526 528
527 529 but adding new files should:
528 530
529 531 $ touch tobundle/newfile
530 532 $ hg -R tobundle ci -qAm newfile
531 533 $ hg -R tobundle bundle -q newfile.hg
532 534 $ hg unbundle -q newfile.hg
533 535 fncache load triggered!
534 536
535 537 $ cd ..
@@ -1,414 +1,415 b''
1 1 This file contains testcases that tend to be related to the wire protocol part
2 2 of largefiles.
3 3
4 4 $ USERCACHE="$TESTTMP/cache"; export USERCACHE
5 5 $ mkdir "${USERCACHE}"
6 6 $ cat >> $HGRCPATH <<EOF
7 7 > [extensions]
8 8 > largefiles=
9 9 > purge=
10 10 > rebase=
11 11 > transplant=
12 12 > [phases]
13 13 > publish=False
14 14 > [largefiles]
15 15 > minsize=2
16 16 > patterns=glob:**.dat
17 17 > usercache=${USERCACHE}
18 18 > [web]
19 19 > allow-archive = zip
20 20 > [hooks]
21 21 > precommit=sh -c "echo \\"Invoking status precommit hook\\"; hg status"
22 22 > EOF
23 23
24 24
25 25 #if serve
26 26 vanilla clients not locked out from largefiles servers on vanilla repos
27 27 $ mkdir r1
28 28 $ cd r1
29 29 $ hg init
30 30 $ echo c1 > f1
31 31 $ hg add f1
32 32 $ hg commit -m "m1"
33 33 Invoking status precommit hook
34 34 A f1
35 35 $ cd ..
36 36 $ hg serve -R r1 -d -p $HGPORT --pid-file hg.pid
37 37 $ cat hg.pid >> $DAEMON_PIDS
38 38 $ hg --config extensions.largefiles=! clone http://localhost:$HGPORT r2
39 39 requesting all changes
40 40 adding changesets
41 41 adding manifests
42 42 adding file changes
43 43 added 1 changesets with 1 changes to 1 files
44 44 new changesets b6eb3a2e2efe (1 drafts)
45 45 updating to branch default
46 46 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
47 47
48 48 largefiles clients still work with vanilla servers
49 49 $ hg serve --config extensions.largefiles=! -R r1 -d -p $HGPORT1 --pid-file hg.pid
50 50 $ cat hg.pid >> $DAEMON_PIDS
51 51 $ hg clone http://localhost:$HGPORT1 r3
52 52 requesting all changes
53 53 adding changesets
54 54 adding manifests
55 55 adding file changes
56 56 added 1 changesets with 1 changes to 1 files
57 57 new changesets b6eb3a2e2efe (1 drafts)
58 58 updating to branch default
59 59 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
60 60 #endif
61 61
62 62 vanilla clients locked out from largefiles http repos
63 63 $ mkdir r4
64 64 $ cd r4
65 65 $ hg init
66 66 $ echo c1 > f1
67 67 $ hg add --large f1
68 68 $ hg commit -m "m1"
69 69 Invoking status precommit hook
70 70 A f1
71 71 $ cd ..
72 72
73 73 largefiles can be pushed locally (issue3583)
74 74 $ hg init dest
75 75 $ cd r4
76 76 $ hg outgoing ../dest
77 77 comparing with ../dest
78 78 searching for changes
79 79 changeset: 0:639881c12b4c
80 80 tag: tip
81 81 user: test
82 82 date: Thu Jan 01 00:00:00 1970 +0000
83 83 summary: m1
84 84
85 85 $ hg push ../dest
86 86 pushing to ../dest
87 87 searching for changes
88 88 adding changesets
89 89 adding manifests
90 90 adding file changes
91 91 added 1 changesets with 1 changes to 1 files
92 92
93 93 exit code with nothing outgoing (issue3611)
94 94 $ hg outgoing ../dest
95 95 comparing with ../dest
96 96 searching for changes
97 97 no changes found
98 98 [1]
99 99 $ cd ..
100 100
101 101 #if serve
102 102 $ hg serve -R r4 -d -p $HGPORT2 --pid-file hg.pid
103 103 $ cat hg.pid >> $DAEMON_PIDS
104 104 $ hg --config extensions.largefiles=! clone http://localhost:$HGPORT2 r5
105 105 abort: remote error:
106 106
107 107 This repository uses the largefiles extension.
108 108
109 109 Please enable it in your Mercurial config file.
110 110 [100]
111 111
112 112 used all HGPORTs, kill all daemons
113 113 $ killdaemons.py
114 114 #endif
115 115
116 116 vanilla clients locked out from largefiles ssh repos
117 117 $ hg --config extensions.largefiles=! clone ssh://user@dummy/r4 r5
118 118 remote:
119 119 remote: This repository uses the largefiles extension.
120 120 remote:
121 121 remote: Please enable it in your Mercurial config file.
122 122 remote:
123 123 remote: -
124 124 abort: remote error
125 125 (check previous remote output)
126 126 [100]
127 127
128 128 #if serve
129 129
130 130 largefiles clients refuse to push largefiles repos to vanilla servers
131 131 $ mkdir r6
132 132 $ cd r6
133 133 $ hg init
134 134 $ echo c1 > f1
135 135 $ hg add f1
136 136 $ hg commit -m "m1"
137 137 Invoking status precommit hook
138 138 A f1
139 139 $ cat >> .hg/hgrc <<!
140 140 > [web]
141 141 > push_ssl = false
142 142 > allow_push = *
143 143 > !
144 144 $ cd ..
145 145 $ hg clone r6 r7
146 146 updating to branch default
147 147 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
148 148 $ cd r7
149 149 $ echo c2 > f2
150 150 $ hg add --large f2
151 151 $ hg commit -m "m2"
152 152 Invoking status precommit hook
153 153 A f2
154 154 $ hg verify --large -q
155 155 $ hg serve --config extensions.largefiles=! -R ../r6 -d -p $HGPORT --pid-file ../hg.pid
156 156 $ cat ../hg.pid >> $DAEMON_PIDS
157 157 $ hg push http://localhost:$HGPORT
158 158 pushing to http://localhost:$HGPORT/
159 159 searching for changes
160 160 abort: http://localhost:$HGPORT/ does not appear to be a largefile store
161 161 [255]
162 162 $ cd ..
163 163
164 164 putlfile errors are shown (issue3123)
165 165 Corrupt the cached largefile in r7 and move it out of the servers usercache
166 166 $ mv r7/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8 .
167 167 $ echo 'client side corruption' > r7/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8
168 168 $ rm "$USERCACHE/4cdac4d8b084d0b599525cf732437fb337d422a8"
169 169 $ hg init empty
170 170 $ hg serve -R empty -d -p $HGPORT1 --pid-file hg.pid \
171 171 > --config 'web.allow_push=*' --config web.push_ssl=False
172 172 $ cat hg.pid >> $DAEMON_PIDS
173 173 $ hg push -R r7 http://localhost:$HGPORT1
174 174 pushing to http://localhost:$HGPORT1/
175 175 searching for changes
176 176 remote: largefiles: failed to put 4cdac4d8b084d0b599525cf732437fb337d422a8 into store: largefile contents do not match hash
177 177 abort: remotestore: could not put $TESTTMP/r7/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8 to remote store http://localhost:$HGPORT1/
178 178 [255]
179 179 $ mv 4cdac4d8b084d0b599525cf732437fb337d422a8 r7/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8
180 180 Push of file that exists on server but is corrupted - magic healing would be nice ... but too magic
181 181 $ echo "server side corruption" > empty/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8
182 182 $ hg push -R r7 http://localhost:$HGPORT1
183 183 pushing to http://localhost:$HGPORT1/
184 184 searching for changes
185 185 remote: adding changesets
186 186 remote: adding manifests
187 187 remote: adding file changes
188 188 remote: added 2 changesets with 2 changes to 2 files
189 189 $ cat empty/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8
190 190 server side corruption
191 191 $ rm -rf empty
192 192
193 193 Push a largefiles repository to a served empty repository
194 194 $ hg init r8
195 195 $ echo c3 > r8/f1
196 196 $ hg add --large r8/f1 -R r8
197 197 $ hg commit -m "m1" -R r8
198 198 Invoking status precommit hook
199 199 A f1
200 200 $ hg init empty
201 201 $ hg serve -R empty -d -p $HGPORT2 --pid-file hg.pid \
202 202 > --config 'web.allow_push=*' --config web.push_ssl=False
203 203 $ cat hg.pid >> $DAEMON_PIDS
204 204 $ rm "${USERCACHE}"/*
205 205 $ hg push -R r8 http://localhost:$HGPORT2/#default
206 206 pushing to http://localhost:$HGPORT2/
207 207 searching for changes
208 208 remote: adding changesets
209 209 remote: adding manifests
210 210 remote: adding file changes
211 211 remote: added 1 changesets with 1 changes to 1 files
212 212 $ [ -f "${USERCACHE}"/02a439e5c31c526465ab1a0ca1f431f76b827b90 ]
213 213 $ [ -f empty/.hg/largefiles/02a439e5c31c526465ab1a0ca1f431f76b827b90 ]
214 214
215 215 Clone over http, no largefiles pulled on clone.
216 216
217 217 $ hg clone http://localhost:$HGPORT2/#default http-clone -U
218 218 adding changesets
219 219 adding manifests
220 220 adding file changes
221 221 added 1 changesets with 1 changes to 1 files
222 222 new changesets cf03e5bb9936 (1 drafts)
223 223
224 224 Archive contains largefiles
225 225 >>> import os
226 226 >>> from mercurial import urllibcompat
227 227 >>> u = 'http://localhost:%s/archive/default.zip' % os.environ['HGPORT2']
228 228 >>> with open('archive.zip', 'wb') as f:
229 229 ... f.write(urllibcompat.urlreq.urlopen(u).read()) and None
230 230 $ unzip -t archive.zip
231 231 Archive: archive.zip
232 232 testing: empty-default/.hg_archival.txt*OK (glob)
233 233 testing: empty-default/f1*OK (glob)
234 234 No errors detected in compressed data of archive.zip.
235 235
236 236 test 'verify' with remotestore:
237 237
238 238 $ rm "${USERCACHE}"/02a439e5c31c526465ab1a0ca1f431f76b827b90
239 239 $ mv empty/.hg/largefiles/02a439e5c31c526465ab1a0ca1f431f76b827b90 .
240 240 $ hg -R http-clone verify --large --lfa
241 241 checking changesets
242 242 checking manifests
243 243 crosschecking files in changesets and manifests
244 244 checking files
245 checking dirstate
245 246 checked 1 changesets with 1 changes to 1 files
246 247 searching 1 changesets for largefiles
247 248 changeset 0:cf03e5bb9936: f1 missing
248 249 verified existence of 1 revisions of 1 largefiles
249 250 [1]
250 251 $ mv 02a439e5c31c526465ab1a0ca1f431f76b827b90 empty/.hg/largefiles/
251 252 $ hg -R http-clone -q verify --large --lfa
252 253
253 254 largefiles pulled on update - a largefile missing on the server:
254 255 $ mv empty/.hg/largefiles/02a439e5c31c526465ab1a0ca1f431f76b827b90 .
255 256 $ hg -R http-clone up --config largefiles.usercache=http-clone-usercache
256 257 getting changed largefiles
257 258 f1: largefile 02a439e5c31c526465ab1a0ca1f431f76b827b90 not available from http://localhost:$HGPORT2/
258 259 0 largefiles updated, 0 removed
259 260 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
260 261 $ hg -R http-clone st
261 262 ! f1
262 263 $ hg -R http-clone up -Cqr null
263 264
264 265 largefiles pulled on update - a largefile corrupted on the server:
265 266 $ echo corruption > empty/.hg/largefiles/02a439e5c31c526465ab1a0ca1f431f76b827b90
266 267 $ hg -R http-clone up --config largefiles.usercache=http-clone-usercache
267 268 getting changed largefiles
268 269 f1: data corruption (expected 02a439e5c31c526465ab1a0ca1f431f76b827b90, got 6a7bb2556144babe3899b25e5428123735bb1e27)
269 270 0 largefiles updated, 0 removed
270 271 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
271 272 $ hg -R http-clone st
272 273 ! f1
273 274 $ [ ! -f http-clone/.hg/largefiles/02a439e5c31c526465ab1a0ca1f431f76b827b90 ]
274 275 $ [ ! -f http-clone/f1 ]
275 276 $ [ ! -f http-clone-usercache ]
276 277 $ hg -R http-clone verify --large --lfc -q
277 278 $ hg -R http-clone up -Cqr null
278 279
279 280 largefiles pulled on update - no server side problems:
280 281 $ mv 02a439e5c31c526465ab1a0ca1f431f76b827b90 empty/.hg/largefiles/
281 282 $ hg -R http-clone --debug up --config largefiles.usercache=http-clone-usercache --config progress.debug=true
282 283 resolving manifests
283 284 branchmerge: False, force: False, partial: False
284 285 ancestor: 000000000000, local: 000000000000+, remote: cf03e5bb9936
285 286 .hglf/f1: remote created -> g
286 287 getting .hglf/f1
287 288 updating: .hglf/f1 1/1 files (100.00%)
288 289 getting changed largefiles
289 290 using http://localhost:$HGPORT2/
290 291 sending capabilities command
291 292 sending statlfile command
292 293 getting largefiles: 0/1 files (0.00%)
293 294 getting f1:02a439e5c31c526465ab1a0ca1f431f76b827b90
294 295 sending getlfile command
295 296 found 02a439e5c31c526465ab1a0ca1f431f76b827b90 in store
296 297 1 largefiles updated, 0 removed
297 298 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
298 299
299 300 $ ls http-clone-usercache/*
300 301 http-clone-usercache/02a439e5c31c526465ab1a0ca1f431f76b827b90
301 302
302 303 $ rm -rf empty http-clone*
303 304
304 305 used all HGPORTs, kill all daemons
305 306 $ killdaemons.py
306 307
307 308 largefiles should batch verify remote calls
308 309
309 310 $ hg init batchverifymain
310 311 $ cd batchverifymain
311 312 $ echo "aaa" >> a
312 313 $ hg add --large a
313 314 $ hg commit -m "a"
314 315 Invoking status precommit hook
315 316 A a
316 317 $ echo "bbb" >> b
317 318 $ hg add --large b
318 319 $ hg commit -m "b"
319 320 Invoking status precommit hook
320 321 A b
321 322 $ cd ..
322 323 $ hg serve -R batchverifymain -d -p $HGPORT --pid-file hg.pid \
323 324 > -A access.log
324 325 $ cat hg.pid >> $DAEMON_PIDS
325 326 $ hg clone --noupdate http://localhost:$HGPORT batchverifyclone
326 327 requesting all changes
327 328 adding changesets
328 329 adding manifests
329 330 adding file changes
330 331 added 2 changesets with 2 changes to 2 files
331 332 new changesets 567253b0f523:04d19c27a332 (2 drafts)
332 333 $ hg -R batchverifyclone verify --large --lfa -q
333 334 $ tail -1 access.log
334 335 $LOCALIP - - [$LOGDATE$] "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=statlfile+sha%3D972a1a11f19934401291cc99117ec614933374ce%3Bstatlfile+sha%3Dc801c9cfe94400963fcb683246217d5db77f9a9a x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
335 336 $ hg -R batchverifyclone update
336 337 getting changed largefiles
337 338 2 largefiles updated, 0 removed
338 339 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
339 340
340 341 Clear log file before next test
341 342
342 343 $ printf "" > access.log
343 344
344 345 Verify should check file on remote server only when file is not
345 346 available locally.
346 347
347 348 $ echo "ccc" >> batchverifymain/c
348 349 $ hg -R batchverifymain status
349 350 ? c
350 351 $ hg -R batchverifymain add --large batchverifymain/c
351 352 $ hg -R batchverifymain commit -m "c"
352 353 Invoking status precommit hook
353 354 A c
354 355 $ hg -R batchverifyclone pull
355 356 pulling from http://localhost:$HGPORT/
356 357 searching for changes
357 358 adding changesets
358 359 adding manifests
359 360 adding file changes
360 361 added 1 changesets with 1 changes to 1 files
361 362 new changesets 6bba8cb6935d (1 drafts)
362 363 (run 'hg update' to get a working copy)
363 364 $ hg -R batchverifyclone verify --lfa -q
364 365 $ tail -1 access.log
365 366 $LOCALIP - - [$LOGDATE$] "GET /?cmd=statlfile HTTP/1.1" 200 - x-hgarg-1:sha=c8559c3c9cfb42131794b7d8009230403b9b454c x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
366 367
367 368 $ killdaemons.py
368 369
369 370 largefiles should not ask for password again after successful authorization
370 371
371 372 $ hg init credentialmain
372 373 $ cd credentialmain
373 374 $ echo "aaa" >> a
374 375 $ hg add --large a
375 376 $ hg commit -m "a"
376 377 Invoking status precommit hook
377 378 A a
378 379
379 380 Before running server clear the user cache to force clone to download
380 381 a large file from the server rather than to get it from the cache
381 382
382 383 $ rm "${USERCACHE}"/*
383 384
384 385 $ cd ..
385 386
386 387 $ hg serve --config extensions.x=$TESTDIR/httpserverauth.py -R credentialmain \
387 388 > -d -p $HGPORT --pid-file hg.pid -A access.log
388 389 $ cat hg.pid >> $DAEMON_PIDS
389 390 $ cat << EOF > get_pass.py
390 391 > from mercurial import util
391 392 > def newgetpass():
392 393 > return "pass"
393 394 > util.get_password = newgetpass
394 395 > EOF
395 396 $ hg clone --config ui.interactive=true --config extensions.getpass=get_pass.py \
396 397 > http://user@localhost:$HGPORT credentialclone
397 398 http authorization required for http://localhost:$HGPORT/
398 399 realm: mercurial
399 400 user: user
400 401 password: requesting all changes
401 402 adding changesets
402 403 adding manifests
403 404 adding file changes
404 405 added 1 changesets with 1 changes to 1 files
405 406 new changesets 567253b0f523 (1 drafts)
406 407 updating to branch default
407 408 getting changed largefiles
408 409 1 largefiles updated, 0 removed
409 410 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
410 411
411 412 $ killdaemons.py
412 413 $ rm hg.pid access.log
413 414
414 415 #endif
@@ -1,1878 +1,1879 b''
1 1 This file used to contains all largefile tests.
2 2 Do not add any new tests in this file as it his already far too long to run.
3 3
4 4 It contains all the testing of the basic concepts of large file in a single block.
5 5
6 6 $ USERCACHE="$TESTTMP/cache"; export USERCACHE
7 7 $ mkdir "${USERCACHE}"
8 8 $ cat >> $HGRCPATH <<EOF
9 9 > [extensions]
10 10 > largefiles=
11 11 > purge=
12 12 > rebase=
13 13 > transplant=
14 14 > [phases]
15 15 > publish=False
16 16 > [largefiles]
17 17 > minsize=2
18 18 > patterns=glob:**.dat
19 19 > usercache=${USERCACHE}
20 20 > [hooks]
21 21 > precommit=sh -c "echo \\"Invoking status precommit hook\\"; hg status"
22 22 > EOF
23 23
24 24 Create the repo with a couple of revisions of both large and normal
25 25 files.
26 26 Test status and dirstate of largefiles and that summary output is correct.
27 27
28 28 $ hg init a
29 29 $ cd a
30 30 $ mkdir sub
31 31 $ echo normal1 > normal1
32 32 $ echo normal2 > sub/normal2
33 33 $ echo large1 > large1
34 34 $ echo large2 > sub/large2
35 35 $ hg add normal1 sub/normal2
36 36 $ hg add --large large1 sub/large2
37 37 $ hg commit -m "add files"
38 38 Invoking status precommit hook
39 39 A large1
40 40 A normal1
41 41 A sub/large2
42 42 A sub/normal2
43 43 $ touch large1 sub/large2
44 44 $ sleep 1
45 45 $ hg st
46 46 $ hg debugstate --no-dates
47 47 n 644 41 set .hglf/large1
48 48 n 644 41 set .hglf/sub/large2
49 49 n 644 8 set normal1
50 50 n 644 8 set sub/normal2
51 51 $ hg debugstate --large --no-dates
52 52 n 644 7 set large1
53 53 n 644 7 set sub/large2
54 54 $ echo normal11 > normal1
55 55 $ echo normal22 > sub/normal2
56 56 $ echo large11 > large1
57 57 $ echo large22 > sub/large2
58 58 $ hg commit -m "edit files"
59 59 Invoking status precommit hook
60 60 M large1
61 61 M normal1
62 62 M sub/large2
63 63 M sub/normal2
64 64 $ hg sum --large
65 65 parent: 1:ce8896473775 tip
66 66 edit files
67 67 branch: default
68 68 commit: (clean)
69 69 update: (current)
70 70 phases: 2 draft
71 71 largefiles: (no remote repo)
72 72
73 73 Commit preserved largefile contents.
74 74
75 75 $ cat normal1
76 76 normal11
77 77 $ cat large1
78 78 large11
79 79 $ cat sub/normal2
80 80 normal22
81 81 $ cat sub/large2
82 82 large22
83 83
84 84 Test status, subdir and unknown files
85 85
86 86 $ echo unknown > sub/unknown
87 87 $ hg st --all
88 88 ? sub/unknown
89 89 C large1
90 90 C normal1
91 91 C sub/large2
92 92 C sub/normal2
93 93 $ hg st --all sub
94 94 ? sub/unknown
95 95 C sub/large2
96 96 C sub/normal2
97 97 $ rm sub/unknown
98 98
99 99 Test messages and exit codes for remove warning cases
100 100
101 101 $ hg remove -A large1
102 102 not removing large1: file still exists
103 103 [1]
104 104 $ echo 'modified' > large1
105 105 $ hg remove large1
106 106 not removing large1: file is modified (use -f to force removal)
107 107 [1]
108 108 $ echo 'new' > normalnew
109 109 $ hg add normalnew
110 110 $ echo 'new' > largenew
111 111 $ hg add --large normalnew
112 112 normalnew already tracked!
113 113 $ hg remove normalnew largenew
114 114 not removing largenew: file is untracked
115 115 not removing normalnew: file has been marked for add (use 'hg forget' to undo add)
116 116 [1]
117 117 $ rm normalnew largenew
118 118 $ hg up -Cq
119 119
120 120 Remove both largefiles and normal files.
121 121
122 122 $ hg remove normal1 large1
123 123 $ hg status large1
124 124 R large1
125 125 $ hg commit -m "remove files"
126 126 Invoking status precommit hook
127 127 R large1
128 128 R normal1
129 129 $ ls -A
130 130 .hg
131 131 .hglf
132 132 sub
133 133 $ echo "testlargefile" > large1-test
134 134 $ hg add --large large1-test
135 135 $ hg st
136 136 A large1-test
137 137 $ hg rm large1-test
138 138 not removing large1-test: file has been marked for add (use forget to undo)
139 139 [1]
140 140 $ hg st
141 141 A large1-test
142 142 $ hg forget large1-test
143 143 $ hg st
144 144 ? large1-test
145 145 $ hg remove large1-test
146 146 not removing large1-test: file is untracked
147 147 [1]
148 148 $ hg forget large1-test
149 149 not removing large1-test: file is already untracked
150 150 [1]
151 151 $ rm large1-test
152 152
153 153 Copy both largefiles and normal files (testing that status output is correct).
154 154
155 155 $ hg cp sub/normal2 normal1
156 156 $ hg cp sub/large2 large1
157 157 $ hg commit -m "copy files"
158 158 Invoking status precommit hook
159 159 A large1
160 160 A normal1
161 161 $ cat normal1
162 162 normal22
163 163 $ cat large1
164 164 large22
165 165
166 166 Test moving largefiles and verify that normal files are also unaffected.
167 167
168 168 $ hg mv normal1 normal3
169 169 $ hg mv large1 large3
170 170 $ hg mv sub/normal2 sub/normal4
171 171 $ hg mv sub/large2 sub/large4
172 172 $ hg commit -m "move files"
173 173 Invoking status precommit hook
174 174 A large3
175 175 A normal3
176 176 A sub/large4
177 177 A sub/normal4
178 178 R large1
179 179 R normal1
180 180 R sub/large2
181 181 R sub/normal2
182 182 $ cat normal3
183 183 normal22
184 184 $ cat large3
185 185 large22
186 186 $ cat sub/normal4
187 187 normal22
188 188 $ cat sub/large4
189 189 large22
190 190
191 191
192 192 #if serve
193 193 Test display of largefiles in hgweb
194 194
195 195 $ hg serve -d -p $HGPORT --pid-file ../hg.pid
196 196 $ cat ../hg.pid >> $DAEMON_PIDS
197 197 $ get-with-headers.py $LOCALIP:$HGPORT 'file/tip/?style=raw'
198 198 200 Script output follows
199 199
200 200
201 201 drwxr-xr-x sub
202 202 -rw-r--r-- 41 large3
203 203 -rw-r--r-- 9 normal3
204 204
205 205
206 206 $ get-with-headers.py $LOCALIP:$HGPORT 'file/tip/sub/?style=raw'
207 207 200 Script output follows
208 208
209 209
210 210 -rw-r--r-- 41 large4
211 211 -rw-r--r-- 9 normal4
212 212
213 213
214 214 $ killdaemons.py
215 215 #endif
216 216
217 217 Test largefiles can be loaded in hgweb (wrapcommand() shouldn't fail)
218 218
219 219 $ cat <<EOF > "$TESTTMP/hgweb.cgi"
220 220 > #!$PYTHON
221 221 > from mercurial import demandimport; demandimport.enable()
222 222 > from mercurial.hgweb import hgweb
223 223 > from mercurial.hgweb import wsgicgi
224 224 > application = hgweb(b'.', b'test repo')
225 225 > wsgicgi.launch(application)
226 226 > EOF
227 227 $ . "$TESTDIR/cgienv"
228 228
229 229 $ SCRIPT_NAME='' \
230 230 > "$PYTHON" "$TESTTMP/hgweb.cgi" > /dev/null
231 231
232 232 Test archiving the various revisions. These hit corner cases known with
233 233 archiving.
234 234
235 235 $ hg archive -r 0 ../archive0
236 236 $ hg archive -r 1 ../archive1
237 237 $ hg archive -r 2 ../archive2
238 238 $ hg archive -r 3 ../archive3
239 239 $ hg archive -r 4 ../archive4
240 240 $ cd ../archive0
241 241 $ cat normal1
242 242 normal1
243 243 $ cat large1
244 244 large1
245 245 $ cat sub/normal2
246 246 normal2
247 247 $ cat sub/large2
248 248 large2
249 249 $ cd ../archive1
250 250 $ cat normal1
251 251 normal11
252 252 $ cat large1
253 253 large11
254 254 $ cat sub/normal2
255 255 normal22
256 256 $ cat sub/large2
257 257 large22
258 258 $ cd ../archive2
259 259 $ ls -A
260 260 .hg_archival.txt
261 261 sub
262 262 $ cat sub/normal2
263 263 normal22
264 264 $ cat sub/large2
265 265 large22
266 266 $ cd ../archive3
267 267 $ cat normal1
268 268 normal22
269 269 $ cat large1
270 270 large22
271 271 $ cat sub/normal2
272 272 normal22
273 273 $ cat sub/large2
274 274 large22
275 275 $ cd ../archive4
276 276 $ cat normal3
277 277 normal22
278 278 $ cat large3
279 279 large22
280 280 $ cat sub/normal4
281 281 normal22
282 282 $ cat sub/large4
283 283 large22
284 284
285 285 Commit corner case: specify files to commit.
286 286
287 287 $ cd ../a
288 288 $ echo normal3 > normal3
289 289 $ echo large3 > large3
290 290 $ echo normal4 > sub/normal4
291 291 $ echo large4 > sub/large4
292 292 $ hg commit normal3 large3 sub/normal4 sub/large4 -m "edit files again"
293 293 Invoking status precommit hook
294 294 M large3
295 295 M normal3
296 296 M sub/large4
297 297 M sub/normal4
298 298 $ cat normal3
299 299 normal3
300 300 $ cat large3
301 301 large3
302 302 $ cat sub/normal4
303 303 normal4
304 304 $ cat sub/large4
305 305 large4
306 306
307 307 One more commit corner case: commit from a subdirectory.
308 308
309 309 $ cd ../a
310 310 $ echo normal33 > normal3
311 311 $ echo large33 > large3
312 312 $ echo normal44 > sub/normal4
313 313 $ echo large44 > sub/large4
314 314 $ cd sub
315 315 $ hg commit -m "edit files yet again"
316 316 Invoking status precommit hook
317 317 M large3
318 318 M normal3
319 319 M sub/large4
320 320 M sub/normal4
321 321 $ cat ../normal3
322 322 normal33
323 323 $ cat ../large3
324 324 large33
325 325 $ cat normal4
326 326 normal44
327 327 $ cat large4
328 328 large44
329 329
330 330 Committing standins is not allowed.
331 331
332 332 $ cd ..
333 333 $ echo large3 > large3
334 334 $ hg commit .hglf/large3 -m "try to commit standin"
335 335 abort: file ".hglf/large3" is a largefile standin
336 336 (commit the largefile itself instead)
337 337 [255]
338 338
339 339 Corner cases for adding largefiles.
340 340
341 341 $ echo large5 > large5
342 342 $ hg add --large large5
343 343 $ hg add --large large5
344 344 large5 already a largefile
345 345 $ mkdir sub2
346 346 $ echo large6 > sub2/large6
347 347 $ echo large7 > sub2/large7
348 348 $ hg add --large sub2
349 349 adding sub2/large6 as a largefile
350 350 adding sub2/large7 as a largefile
351 351 $ hg st
352 352 M large3
353 353 A large5
354 354 A sub2/large6
355 355 A sub2/large7
356 356
357 357 Committing directories containing only largefiles.
358 358
359 359 $ mkdir -p z/y/x/m
360 360 $ touch z/y/x/m/large1
361 361 $ touch z/y/x/large2
362 362 $ hg add --large z/y/x/m/large1 z/y/x/large2
363 363 $ hg commit -m "Subdir with directory only containing largefiles" z
364 364 Invoking status precommit hook
365 365 M large3
366 366 A large5
367 367 A sub2/large6
368 368 A sub2/large7
369 369 A z/y/x/large2
370 370 A z/y/x/m/large1
371 371
372 372 (and a bit of log testing)
373 373
374 374 $ hg log -T '{rev}\n' z/y/x/m/large1
375 375 7
376 376 $ hg log -T '{rev}\n' z/y/x/m # with only a largefile
377 377 7
378 378
379 379 $ hg rollback --quiet
380 380 $ touch z/y/x/m/normal
381 381 $ hg add z/y/x/m/normal
382 382 $ hg commit -m "Subdir with mixed contents" z
383 383 Invoking status precommit hook
384 384 M large3
385 385 A large5
386 386 A sub2/large6
387 387 A sub2/large7
388 388 A z/y/x/large2
389 389 A z/y/x/m/large1
390 390 A z/y/x/m/normal
391 391 $ hg st
392 392 M large3
393 393 A large5
394 394 A sub2/large6
395 395 A sub2/large7
396 396 $ hg rollback --quiet
397 397 $ hg revert z/y/x/large2 z/y/x/m/large1
398 398 $ rm z/y/x/large2 z/y/x/m/large1
399 399 $ hg commit -m "Subdir with normal contents" z
400 400 Invoking status precommit hook
401 401 M large3
402 402 A large5
403 403 A sub2/large6
404 404 A sub2/large7
405 405 A z/y/x/m/normal
406 406 $ hg st
407 407 M large3
408 408 A large5
409 409 A sub2/large6
410 410 A sub2/large7
411 411 $ hg rollback --quiet
412 412 $ hg revert --quiet z
413 413 $ hg commit -m "Empty subdir" z
414 414 abort: z: no match under directory!
415 415 [10]
416 416 $ rm -rf z
417 417 $ hg ci -m "standin" .hglf
418 418 abort: file ".hglf" is a largefile standin
419 419 (commit the largefile itself instead)
420 420 [255]
421 421
422 422 Test "hg status" with combination of 'file pattern' and 'directory
423 423 pattern' for largefiles:
424 424
425 425 $ hg status sub2/large6 sub2
426 426 A sub2/large6
427 427 A sub2/large7
428 428
429 429 Config settings (pattern **.dat, minsize 2 MB) are respected.
430 430
431 431 $ echo testdata > test.dat
432 432 $ dd bs=1k count=2k if=/dev/zero of=reallylarge > /dev/null 2> /dev/null
433 433 $ hg add
434 434 adding reallylarge as a largefile
435 435 adding test.dat as a largefile
436 436
437 437 Test that minsize and --lfsize handle float values;
438 438 also tests that --lfsize overrides largefiles.minsize.
439 439 (0.250 MB = 256 kB = 262144 B)
440 440
441 441 $ dd if=/dev/zero of=ratherlarge bs=1024 count=256 > /dev/null 2> /dev/null
442 442 $ dd if=/dev/zero of=medium bs=1024 count=128 > /dev/null 2> /dev/null
443 443 $ hg --config largefiles.minsize=.25 add
444 444 adding ratherlarge as a largefile
445 445 adding medium
446 446 $ hg forget medium
447 447 $ hg --config largefiles.minsize=.25 add --lfsize=.125
448 448 adding medium as a largefile
449 449 $ dd if=/dev/zero of=notlarge bs=1024 count=127 > /dev/null 2> /dev/null
450 450 $ hg --config largefiles.minsize=.25 add --lfsize=.125
451 451 adding notlarge
452 452 $ hg forget notlarge
453 453
454 454 Test forget on largefiles.
455 455
456 456 $ hg forget large3 large5 test.dat reallylarge ratherlarge medium
457 457 $ hg commit -m "add/edit more largefiles"
458 458 Invoking status precommit hook
459 459 A sub2/large6
460 460 A sub2/large7
461 461 R large3
462 462 ? large5
463 463 ? medium
464 464 ? notlarge
465 465 ? ratherlarge
466 466 ? reallylarge
467 467 ? test.dat
468 468 $ hg st
469 469 ? large3
470 470 ? large5
471 471 ? medium
472 472 ? notlarge
473 473 ? ratherlarge
474 474 ? reallylarge
475 475 ? test.dat
476 476
477 477 Purge with largefiles: verify that largefiles are still in the working
478 478 dir after a purge.
479 479
480 480 $ hg purge --all
481 481 $ cat sub/large4
482 482 large44
483 483 $ cat sub2/large6
484 484 large6
485 485 $ cat sub2/large7
486 486 large7
487 487
488 488 Test addremove: verify that files that should be added as largefiles are added as
489 489 such and that already-existing largefiles are not added as normal files by
490 490 accident.
491 491
492 492 $ rm normal3
493 493 $ rm sub/large4
494 494 $ echo "testing addremove with patterns" > testaddremove.dat
495 495 $ echo "normaladdremove" > normaladdremove
496 496 $ hg addremove
497 497 removing sub/large4
498 498 adding testaddremove.dat as a largefile
499 499 removing normal3
500 500 adding normaladdremove
501 501
502 502 Test addremove with -R
503 503
504 504 $ hg up -C
505 505 getting changed largefiles
506 506 1 largefiles updated, 0 removed
507 507 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
508 508 $ rm normal3
509 509 $ rm sub/large4
510 510 $ echo "testing addremove with patterns" > testaddremove.dat
511 511 $ echo "normaladdremove" > normaladdremove
512 512 $ cd ..
513 513 $ hg -R a -v addremove
514 514 removing sub/large4
515 515 adding testaddremove.dat as a largefile
516 516 removing normal3
517 517 adding normaladdremove
518 518 $ cd a
519 519
520 520 Test 3364
521 521 $ hg clone . ../addrm
522 522 updating to branch default
523 523 getting changed largefiles
524 524 3 largefiles updated, 0 removed
525 525 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
526 526 $ cd ../addrm
527 527 $ cat >> .hg/hgrc <<EOF
528 528 > [hooks]
529 529 > post-commit.stat=sh -c "echo \\"Invoking status postcommit hook\\"; hg status -A"
530 530 > EOF
531 531 $ touch foo
532 532 $ hg add --large foo
533 533 $ hg ci -m "add foo"
534 534 Invoking status precommit hook
535 535 A foo
536 536 Invoking status postcommit hook
537 537 C foo
538 538 C normal3
539 539 C sub/large4
540 540 C sub/normal4
541 541 C sub2/large6
542 542 C sub2/large7
543 543 $ rm foo
544 544 $ hg st
545 545 ! foo
546 546 hmm.. no precommit invoked, but there is a postcommit??
547 547 $ hg ci -m "will not checkin"
548 548 nothing changed (1 missing files, see 'hg status')
549 549 Invoking status postcommit hook
550 550 ! foo
551 551 C normal3
552 552 C sub/large4
553 553 C sub/normal4
554 554 C sub2/large6
555 555 C sub2/large7
556 556 [1]
557 557 $ hg addremove
558 558 removing foo
559 559 $ hg st
560 560 R foo
561 561 $ hg ci -m "used to say nothing changed"
562 562 Invoking status precommit hook
563 563 R foo
564 564 Invoking status postcommit hook
565 565 C normal3
566 566 C sub/large4
567 567 C sub/normal4
568 568 C sub2/large6
569 569 C sub2/large7
570 570 $ hg st
571 571
572 572 Test 3507 (both normal files and largefiles were a problem)
573 573
574 574 $ touch normal
575 575 $ touch large
576 576 $ hg add normal
577 577 $ hg add --large large
578 578 $ hg ci -m "added"
579 579 Invoking status precommit hook
580 580 A large
581 581 A normal
582 582 Invoking status postcommit hook
583 583 C large
584 584 C normal
585 585 C normal3
586 586 C sub/large4
587 587 C sub/normal4
588 588 C sub2/large6
589 589 C sub2/large7
590 590 $ hg remove normal
591 591 $ hg addremove --traceback
592 592 $ hg ci -m "addremoved normal"
593 593 Invoking status precommit hook
594 594 R normal
595 595 Invoking status postcommit hook
596 596 C large
597 597 C normal3
598 598 C sub/large4
599 599 C sub/normal4
600 600 C sub2/large6
601 601 C sub2/large7
602 602 $ hg up -C '.^'
603 603 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
604 604 $ hg remove large
605 605 $ hg addremove --traceback
606 606 $ hg ci -m "removed large"
607 607 Invoking status precommit hook
608 608 R large
609 609 created new head
610 610 Invoking status postcommit hook
611 611 C normal
612 612 C normal3
613 613 C sub/large4
614 614 C sub/normal4
615 615 C sub2/large6
616 616 C sub2/large7
617 617
618 618 Test commit -A (issue3542)
619 619 $ echo large8 > large8
620 620 $ hg add --large large8
621 621 $ hg ci -Am 'this used to add large8 as normal and commit both'
622 622 Invoking status precommit hook
623 623 A large8
624 624 Invoking status postcommit hook
625 625 C large8
626 626 C normal
627 627 C normal3
628 628 C sub/large4
629 629 C sub/normal4
630 630 C sub2/large6
631 631 C sub2/large7
632 632 $ rm large8
633 633 $ hg ci -Am 'this used to not notice the rm'
634 634 removing large8
635 635 Invoking status precommit hook
636 636 R large8
637 637 Invoking status postcommit hook
638 638 C normal
639 639 C normal3
640 640 C sub/large4
641 641 C sub/normal4
642 642 C sub2/large6
643 643 C sub2/large7
644 644
645 645 Test that a standin can't be added as a large file
646 646
647 647 $ touch large
648 648 $ hg add --large large
649 649 $ hg ci -m "add"
650 650 Invoking status precommit hook
651 651 A large
652 652 Invoking status postcommit hook
653 653 C large
654 654 C normal
655 655 C normal3
656 656 C sub/large4
657 657 C sub/normal4
658 658 C sub2/large6
659 659 C sub2/large7
660 660 $ hg remove large
661 661 $ touch large
662 662 $ hg addremove --config largefiles.patterns=**large --traceback
663 663 adding large as a largefile
664 664
665 665 Test that outgoing --large works (with revsets too)
666 666 $ hg outgoing --rev '.^' --large
667 667 comparing with $TESTTMP/a
668 668 searching for changes
669 669 changeset: 8:c02fd3b77ec4
670 670 user: test
671 671 date: Thu Jan 01 00:00:00 1970 +0000
672 672 summary: add foo
673 673
674 674 changeset: 9:289dd08c9bbb
675 675 user: test
676 676 date: Thu Jan 01 00:00:00 1970 +0000
677 677 summary: used to say nothing changed
678 678
679 679 changeset: 10:34f23ac6ac12
680 680 user: test
681 681 date: Thu Jan 01 00:00:00 1970 +0000
682 682 summary: added
683 683
684 684 changeset: 12:710c1b2f523c
685 685 parent: 10:34f23ac6ac12
686 686 user: test
687 687 date: Thu Jan 01 00:00:00 1970 +0000
688 688 summary: removed large
689 689
690 690 changeset: 13:0a3e75774479
691 691 user: test
692 692 date: Thu Jan 01 00:00:00 1970 +0000
693 693 summary: this used to add large8 as normal and commit both
694 694
695 695 changeset: 14:84f3d378175c
696 696 user: test
697 697 date: Thu Jan 01 00:00:00 1970 +0000
698 698 summary: this used to not notice the rm
699 699
700 700 largefiles to upload (1 entities):
701 701 large8
702 702
703 703 $ cd ../a
704 704
705 705 Clone a largefiles repo.
706 706
707 707 $ hg clone . ../b
708 708 updating to branch default
709 709 getting changed largefiles
710 710 3 largefiles updated, 0 removed
711 711 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
712 712 $ cd ../b
713 713 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
714 714 7:daea875e9014 add/edit more largefiles
715 715 6:4355d653f84f edit files yet again
716 716 5:9d5af5072dbd edit files again
717 717 4:74c02385b94c move files
718 718 3:9e8fbc4bce62 copy files
719 719 2:51a0ae4d5864 remove files
720 720 1:ce8896473775 edit files
721 721 0:30d30fe6a5be add files
722 722 $ cat normal3
723 723 normal33
724 724
725 725 Test graph log
726 726
727 727 $ hg log -G --template '{rev}:{node|short} {desc|firstline}\n'
728 728 @ 7:daea875e9014 add/edit more largefiles
729 729 |
730 730 o 6:4355d653f84f edit files yet again
731 731 |
732 732 o 5:9d5af5072dbd edit files again
733 733 |
734 734 o 4:74c02385b94c move files
735 735 |
736 736 o 3:9e8fbc4bce62 copy files
737 737 |
738 738 o 2:51a0ae4d5864 remove files
739 739 |
740 740 o 1:ce8896473775 edit files
741 741 |
742 742 o 0:30d30fe6a5be add files
743 743
744 744
745 745 Test log with --patch
746 746
747 747 $ hg log --patch -r 6::7
748 748 changeset: 6:4355d653f84f
749 749 user: test
750 750 date: Thu Jan 01 00:00:00 1970 +0000
751 751 summary: edit files yet again
752 752
753 753 diff -r 9d5af5072dbd -r 4355d653f84f .hglf/large3
754 754 --- a/.hglf/large3 Thu Jan 01 00:00:00 1970 +0000
755 755 +++ b/.hglf/large3 Thu Jan 01 00:00:00 1970 +0000
756 756 @@ -1,1 +1,1 @@
757 757 -baaf12afde9d8d67f25dab6dced0d2bf77dba47c
758 758 +7838695e10da2bb75ac1156565f40a2595fa2fa0
759 759 diff -r 9d5af5072dbd -r 4355d653f84f .hglf/sub/large4
760 760 --- a/.hglf/sub/large4 Thu Jan 01 00:00:00 1970 +0000
761 761 +++ b/.hglf/sub/large4 Thu Jan 01 00:00:00 1970 +0000
762 762 @@ -1,1 +1,1 @@
763 763 -aeb2210d19f02886dde00dac279729a48471e2f9
764 764 +971fb41e78fea4f8e0ba5244784239371cb00591
765 765 diff -r 9d5af5072dbd -r 4355d653f84f normal3
766 766 --- a/normal3 Thu Jan 01 00:00:00 1970 +0000
767 767 +++ b/normal3 Thu Jan 01 00:00:00 1970 +0000
768 768 @@ -1,1 +1,1 @@
769 769 -normal3
770 770 +normal33
771 771 diff -r 9d5af5072dbd -r 4355d653f84f sub/normal4
772 772 --- a/sub/normal4 Thu Jan 01 00:00:00 1970 +0000
773 773 +++ b/sub/normal4 Thu Jan 01 00:00:00 1970 +0000
774 774 @@ -1,1 +1,1 @@
775 775 -normal4
776 776 +normal44
777 777
778 778 changeset: 7:daea875e9014
779 779 tag: tip
780 780 user: test
781 781 date: Thu Jan 01 00:00:00 1970 +0000
782 782 summary: add/edit more largefiles
783 783
784 784 diff -r 4355d653f84f -r daea875e9014 .hglf/large3
785 785 --- a/.hglf/large3 Thu Jan 01 00:00:00 1970 +0000
786 786 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
787 787 @@ -1,1 +0,0 @@
788 788 -7838695e10da2bb75ac1156565f40a2595fa2fa0
789 789 diff -r 4355d653f84f -r daea875e9014 .hglf/sub2/large6
790 790 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
791 791 +++ b/.hglf/sub2/large6 Thu Jan 01 00:00:00 1970 +0000
792 792 @@ -0,0 +1,1 @@
793 793 +0d6d75887db61b2c7e6c74b5dd8fc6ad50c0cc30
794 794 diff -r 4355d653f84f -r daea875e9014 .hglf/sub2/large7
795 795 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
796 796 +++ b/.hglf/sub2/large7 Thu Jan 01 00:00:00 1970 +0000
797 797 @@ -0,0 +1,1 @@
798 798 +bb3151689acb10f0c3125c560d5e63df914bc1af
799 799
800 800
801 801 $ hg log --patch -r 6::7 sub/
802 802 changeset: 6:4355d653f84f
803 803 user: test
804 804 date: Thu Jan 01 00:00:00 1970 +0000
805 805 summary: edit files yet again
806 806
807 807 diff -r 9d5af5072dbd -r 4355d653f84f .hglf/sub/large4
808 808 --- a/.hglf/sub/large4 Thu Jan 01 00:00:00 1970 +0000
809 809 +++ b/.hglf/sub/large4 Thu Jan 01 00:00:00 1970 +0000
810 810 @@ -1,1 +1,1 @@
811 811 -aeb2210d19f02886dde00dac279729a48471e2f9
812 812 +971fb41e78fea4f8e0ba5244784239371cb00591
813 813 diff -r 9d5af5072dbd -r 4355d653f84f sub/normal4
814 814 --- a/sub/normal4 Thu Jan 01 00:00:00 1970 +0000
815 815 +++ b/sub/normal4 Thu Jan 01 00:00:00 1970 +0000
816 816 @@ -1,1 +1,1 @@
817 817 -normal4
818 818 +normal44
819 819
820 820
821 821 log with both --follow and --patch
822 822
823 823 $ hg log --follow --patch --limit 2
824 824 changeset: 7:daea875e9014
825 825 tag: tip
826 826 user: test
827 827 date: Thu Jan 01 00:00:00 1970 +0000
828 828 summary: add/edit more largefiles
829 829
830 830 diff -r 4355d653f84f -r daea875e9014 .hglf/large3
831 831 --- a/.hglf/large3 Thu Jan 01 00:00:00 1970 +0000
832 832 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
833 833 @@ -1,1 +0,0 @@
834 834 -7838695e10da2bb75ac1156565f40a2595fa2fa0
835 835 diff -r 4355d653f84f -r daea875e9014 .hglf/sub2/large6
836 836 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
837 837 +++ b/.hglf/sub2/large6 Thu Jan 01 00:00:00 1970 +0000
838 838 @@ -0,0 +1,1 @@
839 839 +0d6d75887db61b2c7e6c74b5dd8fc6ad50c0cc30
840 840 diff -r 4355d653f84f -r daea875e9014 .hglf/sub2/large7
841 841 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
842 842 +++ b/.hglf/sub2/large7 Thu Jan 01 00:00:00 1970 +0000
843 843 @@ -0,0 +1,1 @@
844 844 +bb3151689acb10f0c3125c560d5e63df914bc1af
845 845
846 846 changeset: 6:4355d653f84f
847 847 user: test
848 848 date: Thu Jan 01 00:00:00 1970 +0000
849 849 summary: edit files yet again
850 850
851 851 diff -r 9d5af5072dbd -r 4355d653f84f .hglf/large3
852 852 --- a/.hglf/large3 Thu Jan 01 00:00:00 1970 +0000
853 853 +++ b/.hglf/large3 Thu Jan 01 00:00:00 1970 +0000
854 854 @@ -1,1 +1,1 @@
855 855 -baaf12afde9d8d67f25dab6dced0d2bf77dba47c
856 856 +7838695e10da2bb75ac1156565f40a2595fa2fa0
857 857 diff -r 9d5af5072dbd -r 4355d653f84f .hglf/sub/large4
858 858 --- a/.hglf/sub/large4 Thu Jan 01 00:00:00 1970 +0000
859 859 +++ b/.hglf/sub/large4 Thu Jan 01 00:00:00 1970 +0000
860 860 @@ -1,1 +1,1 @@
861 861 -aeb2210d19f02886dde00dac279729a48471e2f9
862 862 +971fb41e78fea4f8e0ba5244784239371cb00591
863 863 diff -r 9d5af5072dbd -r 4355d653f84f normal3
864 864 --- a/normal3 Thu Jan 01 00:00:00 1970 +0000
865 865 +++ b/normal3 Thu Jan 01 00:00:00 1970 +0000
866 866 @@ -1,1 +1,1 @@
867 867 -normal3
868 868 +normal33
869 869 diff -r 9d5af5072dbd -r 4355d653f84f sub/normal4
870 870 --- a/sub/normal4 Thu Jan 01 00:00:00 1970 +0000
871 871 +++ b/sub/normal4 Thu Jan 01 00:00:00 1970 +0000
872 872 @@ -1,1 +1,1 @@
873 873 -normal4
874 874 +normal44
875 875
876 876 $ hg log --follow --patch sub/large4
877 877 changeset: 6:4355d653f84f
878 878 user: test
879 879 date: Thu Jan 01 00:00:00 1970 +0000
880 880 summary: edit files yet again
881 881
882 882 diff -r 9d5af5072dbd -r 4355d653f84f .hglf/sub/large4
883 883 --- a/.hglf/sub/large4 Thu Jan 01 00:00:00 1970 +0000
884 884 +++ b/.hglf/sub/large4 Thu Jan 01 00:00:00 1970 +0000
885 885 @@ -1,1 +1,1 @@
886 886 -aeb2210d19f02886dde00dac279729a48471e2f9
887 887 +971fb41e78fea4f8e0ba5244784239371cb00591
888 888
889 889 changeset: 5:9d5af5072dbd
890 890 user: test
891 891 date: Thu Jan 01 00:00:00 1970 +0000
892 892 summary: edit files again
893 893
894 894 diff -r 74c02385b94c -r 9d5af5072dbd .hglf/sub/large4
895 895 --- a/.hglf/sub/large4 Thu Jan 01 00:00:00 1970 +0000
896 896 +++ b/.hglf/sub/large4 Thu Jan 01 00:00:00 1970 +0000
897 897 @@ -1,1 +1,1 @@
898 898 -eb7338044dc27f9bc59b8dd5a246b065ead7a9c4
899 899 +aeb2210d19f02886dde00dac279729a48471e2f9
900 900
901 901 changeset: 4:74c02385b94c
902 902 user: test
903 903 date: Thu Jan 01 00:00:00 1970 +0000
904 904 summary: move files
905 905
906 906 diff -r 9e8fbc4bce62 -r 74c02385b94c .hglf/sub/large4
907 907 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
908 908 +++ b/.hglf/sub/large4 Thu Jan 01 00:00:00 1970 +0000
909 909 @@ -0,0 +1,1 @@
910 910 +eb7338044dc27f9bc59b8dd5a246b065ead7a9c4
911 911
912 912 changeset: 1:ce8896473775
913 913 user: test
914 914 date: Thu Jan 01 00:00:00 1970 +0000
915 915 summary: edit files
916 916
917 917 diff -r 30d30fe6a5be -r ce8896473775 .hglf/sub/large2
918 918 --- a/.hglf/sub/large2 Thu Jan 01 00:00:00 1970 +0000
919 919 +++ b/.hglf/sub/large2 Thu Jan 01 00:00:00 1970 +0000
920 920 @@ -1,1 +1,1 @@
921 921 -1deebade43c8c498a3c8daddac0244dc55d1331d
922 922 +eb7338044dc27f9bc59b8dd5a246b065ead7a9c4
923 923
924 924 changeset: 0:30d30fe6a5be
925 925 user: test
926 926 date: Thu Jan 01 00:00:00 1970 +0000
927 927 summary: add files
928 928
929 929 diff -r 000000000000 -r 30d30fe6a5be .hglf/sub/large2
930 930 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
931 931 +++ b/.hglf/sub/large2 Thu Jan 01 00:00:00 1970 +0000
932 932 @@ -0,0 +1,1 @@
933 933 +1deebade43c8c498a3c8daddac0244dc55d1331d
934 934
935 935 $ cat sub/normal4
936 936 normal44
937 937 $ cat sub/large4
938 938 large44
939 939 $ cat sub2/large6
940 940 large6
941 941 $ cat sub2/large7
942 942 large7
943 943 $ hg log -qf sub2/large7
944 944 7:daea875e9014
945 945 $ hg log -Gqf sub2/large7
946 946 @ 7:daea875e9014
947 947 |
948 948 ~
949 949 $ cd ..
950 950
951 951 Test log from outside repo
952 952
953 953 $ hg log b/sub -T '{rev}:{node|short} {desc|firstline}\n'
954 954 6:4355d653f84f edit files yet again
955 955 5:9d5af5072dbd edit files again
956 956 4:74c02385b94c move files
957 957 1:ce8896473775 edit files
958 958 0:30d30fe6a5be add files
959 959
960 960 Test clone at revision
961 961
962 962 $ hg clone a -r 3 c
963 963 adding changesets
964 964 adding manifests
965 965 adding file changes
966 966 added 4 changesets with 10 changes to 4 files
967 967 new changesets 30d30fe6a5be:9e8fbc4bce62 (4 drafts)
968 968 updating to branch default
969 969 getting changed largefiles
970 970 2 largefiles updated, 0 removed
971 971 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
972 972 $ cd c
973 973 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
974 974 3:9e8fbc4bce62 copy files
975 975 2:51a0ae4d5864 remove files
976 976 1:ce8896473775 edit files
977 977 0:30d30fe6a5be add files
978 978 $ cat normal1
979 979 normal22
980 980 $ cat large1
981 981 large22
982 982 $ cat sub/normal2
983 983 normal22
984 984 $ cat sub/large2
985 985 large22
986 986
987 987 Old revisions of a clone have correct largefiles content (this also
988 988 tests update).
989 989
990 990 $ hg update -r 1
991 991 getting changed largefiles
992 992 1 largefiles updated, 0 removed
993 993 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
994 994 $ cat large1
995 995 large11
996 996 $ cat sub/large2
997 997 large22
998 998 $ cd ..
999 999
1000 1000 Test cloning with --all-largefiles flag
1001 1001
1002 1002 $ rm "${USERCACHE}"/*
1003 1003 $ hg clone --all-largefiles a a-backup
1004 1004 updating to branch default
1005 1005 getting changed largefiles
1006 1006 3 largefiles updated, 0 removed
1007 1007 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
1008 1008 7 additional largefiles cached
1009 1009
1010 1010 $ rm "${USERCACHE}"/*
1011 1011 $ hg clone --all-largefiles -u 0 a a-clone0
1012 1012 updating to branch default
1013 1013 getting changed largefiles
1014 1014 2 largefiles updated, 0 removed
1015 1015 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
1016 1016 8 additional largefiles cached
1017 1017 $ hg -R a-clone0 sum
1018 1018 parent: 0:30d30fe6a5be
1019 1019 add files
1020 1020 branch: default
1021 1021 commit: (clean)
1022 1022 update: 7 new changesets (update)
1023 1023 phases: 8 draft
1024 1024
1025 1025 $ rm "${USERCACHE}"/*
1026 1026 $ hg clone --all-largefiles -u 1 a a-clone1
1027 1027 updating to branch default
1028 1028 getting changed largefiles
1029 1029 2 largefiles updated, 0 removed
1030 1030 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
1031 1031 8 additional largefiles cached
1032 1032 $ hg -R a-clone1 verify --large --lfa --lfc -q
1033 1033 $ hg -R a-clone1 sum
1034 1034 parent: 1:ce8896473775
1035 1035 edit files
1036 1036 branch: default
1037 1037 commit: (clean)
1038 1038 update: 6 new changesets (update)
1039 1039 phases: 8 draft
1040 1040
1041 1041 $ rm "${USERCACHE}"/*
1042 1042 $ hg clone --all-largefiles -U a a-clone-u
1043 1043 10 additional largefiles cached
1044 1044 $ hg -R a-clone-u sum
1045 1045 parent: -1:000000000000 (no revision checked out)
1046 1046 branch: default
1047 1047 commit: (clean)
1048 1048 update: 8 new changesets (update)
1049 1049 phases: 8 draft
1050 1050
1051 1051 Show computed destination directory:
1052 1052
1053 1053 $ mkdir xyz
1054 1054 $ cd xyz
1055 1055 $ hg clone ../a
1056 1056 destination directory: a
1057 1057 updating to branch default
1058 1058 getting changed largefiles
1059 1059 3 largefiles updated, 0 removed
1060 1060 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
1061 1061 $ cd ..
1062 1062
1063 1063 Clone URL without path:
1064 1064
1065 1065 $ hg clone file://
1066 1066 abort: repository / not found
1067 1067 [255]
1068 1068
1069 1069 Ensure base clone command argument validation
1070 1070
1071 1071 $ hg clone -U -u 0 a a-clone-failure
1072 1072 abort: cannot specify both --noupdate and --updaterev
1073 1073 [10]
1074 1074
1075 1075 $ hg clone --all-largefiles a ssh://localhost/a
1076 1076 abort: --all-largefiles is incompatible with non-local destination ssh://localhost/a
1077 1077 [255]
1078 1078
1079 1079 Test pulling with --all-largefiles flag. Also test that the largefiles are
1080 1080 downloaded from 'default' instead of 'default-push' when no source is specified
1081 1081 (issue3584)
1082 1082
1083 1083 $ rm -Rf a-backup
1084 1084 $ hg clone -r 1 a a-backup
1085 1085 adding changesets
1086 1086 adding manifests
1087 1087 adding file changes
1088 1088 added 2 changesets with 8 changes to 4 files
1089 1089 new changesets 30d30fe6a5be:ce8896473775 (2 drafts)
1090 1090 updating to branch default
1091 1091 getting changed largefiles
1092 1092 2 largefiles updated, 0 removed
1093 1093 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
1094 1094 $ rm "${USERCACHE}"/*
1095 1095 $ cd a-backup
1096 1096 $ hg pull --all-largefiles --config paths.default-push=bogus/path
1097 1097 pulling from $TESTTMP/a
1098 1098 searching for changes
1099 1099 adding changesets
1100 1100 adding manifests
1101 1101 adding file changes
1102 1102 added 6 changesets with 16 changes to 8 files
1103 1103 new changesets 51a0ae4d5864:daea875e9014 (6 drafts)
1104 1104 (run 'hg update' to get a working copy)
1105 1105 6 largefiles cached
1106 1106
1107 1107 redo pull with --lfrev and check it pulls largefiles for the right revs
1108 1108
1109 1109 $ hg rollback
1110 1110 repository tip rolled back to revision 1 (undo pull)
1111 1111 $ hg pull -v --lfrev 'heads(pulled())+min(pulled())'
1112 1112 pulling from $TESTTMP/a
1113 1113 searching for changes
1114 1114 all local changesets known remotely
1115 1115 6 changesets found
1116 1116 uncompressed size of bundle content:
1117 1117 1389 (changelog)
1118 1118 1698 (manifests)
1119 1119 254 .hglf/large1
1120 1120 564 .hglf/large3
1121 1121 572 .hglf/sub/large4
1122 1122 182 .hglf/sub2/large6
1123 1123 182 .hglf/sub2/large7
1124 1124 212 normal1
1125 1125 457 normal3
1126 1126 465 sub/normal4
1127 1127 adding changesets
1128 1128 adding manifests
1129 1129 adding file changes
1130 1130 added 6 changesets with 16 changes to 8 files
1131 1131 new changesets 51a0ae4d5864:daea875e9014 (6 drafts)
1132 1132 calling hook changegroup.lfiles: hgext.largefiles.reposetup.checkrequireslfiles
1133 1133 (run 'hg update' to get a working copy)
1134 1134 pulling largefiles for revision 7
1135 1135 found 971fb41e78fea4f8e0ba5244784239371cb00591 in store
1136 1136 found 0d6d75887db61b2c7e6c74b5dd8fc6ad50c0cc30 in store
1137 1137 found bb3151689acb10f0c3125c560d5e63df914bc1af in store
1138 1138 pulling largefiles for revision 2
1139 1139 found eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 in store
1140 1140 0 largefiles cached
1141 1141
1142 1142 lfpull
1143 1143
1144 1144 $ hg lfpull -r : --config largefiles.usercache=usercache-lfpull
1145 1145 2 largefiles cached
1146 1146 $ hg lfpull -v -r 4+2 --config largefiles.usercache=usercache-lfpull
1147 1147 pulling largefiles for revision 4
1148 1148 found eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 in store
1149 1149 found eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 in store
1150 1150 pulling largefiles for revision 2
1151 1151 found eb7338044dc27f9bc59b8dd5a246b065ead7a9c4 in store
1152 1152 0 largefiles cached
1153 1153
1154 1154 $ ls usercache-lfpull/* | sort
1155 1155 usercache-lfpull/1deebade43c8c498a3c8daddac0244dc55d1331d
1156 1156 usercache-lfpull/4669e532d5b2c093a78eca010077e708a071bb64
1157 1157
1158 1158 $ cd ..
1159 1159
1160 1160 Rebasing between two repositories does not revert largefiles to old
1161 1161 revisions (this was a very bad bug that took a lot of work to fix).
1162 1162
1163 1163 $ hg clone a d
1164 1164 updating to branch default
1165 1165 getting changed largefiles
1166 1166 3 largefiles updated, 0 removed
1167 1167 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
1168 1168 $ cd b
1169 1169 $ echo large4-modified > sub/large4
1170 1170 $ echo normal3-modified > normal3
1171 1171 $ hg commit -m "modify normal file and largefile in repo b"
1172 1172 Invoking status precommit hook
1173 1173 M normal3
1174 1174 M sub/large4
1175 1175 $ cd ../d
1176 1176 $ echo large6-modified > sub2/large6
1177 1177 $ echo normal4-modified > sub/normal4
1178 1178 $ hg commit -m "modify normal file largefile in repo d"
1179 1179 Invoking status precommit hook
1180 1180 M sub/normal4
1181 1181 M sub2/large6
1182 1182 $ cd ..
1183 1183 $ hg clone d e
1184 1184 updating to branch default
1185 1185 getting changed largefiles
1186 1186 3 largefiles updated, 0 removed
1187 1187 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
1188 1188 $ cd d
1189 1189
1190 1190 More rebase testing, but also test that the largefiles are downloaded from
1191 1191 'default-push' when no source is specified (issue3584). (The largefile from the
1192 1192 pulled revision is however not downloaded but found in the local cache.)
1193 1193 Largefiles are fetched for the new pulled revision, not for existing revisions,
1194 1194 rebased or not.
1195 1195
1196 1196 $ [ ! -f .hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928 ]
1197 1197 $ hg pull --rebase --all-largefiles --config paths.default-push=bogus/path --config paths.default=../b
1198 1198 pulling from $TESTTMP/b
1199 1199 searching for changes
1200 1200 adding changesets
1201 1201 adding manifests
1202 1202 adding file changes
1203 1203 added 1 changesets with 2 changes to 2 files (+1 heads)
1204 1204 new changesets a381d2c8c80e (1 drafts)
1205 1205 0 largefiles cached
1206 1206 rebasing 8:f574fb32bb45 "modify normal file largefile in repo d"
1207 1207 Invoking status precommit hook
1208 1208 M sub/normal4
1209 1209 M sub2/large6
1210 1210 saved backup bundle to $TESTTMP/d/.hg/strip-backup/f574fb32bb45-dd1d9f80-rebase.hg
1211 1211 $ [ -f .hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928 ]
1212 1212 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
1213 1213 9:598410d3eb9a modify normal file largefile in repo d
1214 1214 8:a381d2c8c80e modify normal file and largefile in repo b
1215 1215 7:daea875e9014 add/edit more largefiles
1216 1216 6:4355d653f84f edit files yet again
1217 1217 5:9d5af5072dbd edit files again
1218 1218 4:74c02385b94c move files
1219 1219 3:9e8fbc4bce62 copy files
1220 1220 2:51a0ae4d5864 remove files
1221 1221 1:ce8896473775 edit files
1222 1222 0:30d30fe6a5be add files
1223 1223 $ hg log -G --template '{rev}:{node|short} {desc|firstline}\n'
1224 1224 @ 9:598410d3eb9a modify normal file largefile in repo d
1225 1225 |
1226 1226 o 8:a381d2c8c80e modify normal file and largefile in repo b
1227 1227 |
1228 1228 o 7:daea875e9014 add/edit more largefiles
1229 1229 |
1230 1230 o 6:4355d653f84f edit files yet again
1231 1231 |
1232 1232 o 5:9d5af5072dbd edit files again
1233 1233 |
1234 1234 o 4:74c02385b94c move files
1235 1235 |
1236 1236 o 3:9e8fbc4bce62 copy files
1237 1237 |
1238 1238 o 2:51a0ae4d5864 remove files
1239 1239 |
1240 1240 o 1:ce8896473775 edit files
1241 1241 |
1242 1242 o 0:30d30fe6a5be add files
1243 1243
1244 1244 $ cat normal3
1245 1245 normal3-modified
1246 1246 $ cat sub/normal4
1247 1247 normal4-modified
1248 1248 $ cat sub/large4
1249 1249 large4-modified
1250 1250 $ cat sub2/large6
1251 1251 large6-modified
1252 1252 $ cat sub2/large7
1253 1253 large7
1254 1254 $ cd ../e
1255 1255 $ hg pull ../b
1256 1256 pulling from ../b
1257 1257 searching for changes
1258 1258 adding changesets
1259 1259 adding manifests
1260 1260 adding file changes
1261 1261 added 1 changesets with 2 changes to 2 files (+1 heads)
1262 1262 new changesets a381d2c8c80e (1 drafts)
1263 1263 (run 'hg heads' to see heads, 'hg merge' to merge)
1264 1264 $ hg rebase
1265 1265 rebasing 8:f574fb32bb45 "modify normal file largefile in repo d"
1266 1266 Invoking status precommit hook
1267 1267 M sub/normal4
1268 1268 M sub2/large6
1269 1269 saved backup bundle to $TESTTMP/e/.hg/strip-backup/f574fb32bb45-dd1d9f80-rebase.hg
1270 1270 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
1271 1271 9:598410d3eb9a modify normal file largefile in repo d
1272 1272 8:a381d2c8c80e modify normal file and largefile in repo b
1273 1273 7:daea875e9014 add/edit more largefiles
1274 1274 6:4355d653f84f edit files yet again
1275 1275 5:9d5af5072dbd edit files again
1276 1276 4:74c02385b94c move files
1277 1277 3:9e8fbc4bce62 copy files
1278 1278 2:51a0ae4d5864 remove files
1279 1279 1:ce8896473775 edit files
1280 1280 0:30d30fe6a5be add files
1281 1281 $ cat normal3
1282 1282 normal3-modified
1283 1283 $ cat sub/normal4
1284 1284 normal4-modified
1285 1285 $ cat sub/large4
1286 1286 large4-modified
1287 1287 $ cat sub2/large6
1288 1288 large6-modified
1289 1289 $ cat sub2/large7
1290 1290 large7
1291 1291
1292 1292 Log on largefiles
1293 1293
1294 1294 - same output
1295 1295 $ hg log --template '{rev}:{node|short} {desc|firstline}\n' .hglf/sub/large4
1296 1296 8:a381d2c8c80e modify normal file and largefile in repo b
1297 1297 6:4355d653f84f edit files yet again
1298 1298 5:9d5af5072dbd edit files again
1299 1299 4:74c02385b94c move files
1300 1300 $ hg log -G --template '{rev}:{node|short} {desc|firstline}\n' .hglf/sub/large4
1301 1301 o 8:a381d2c8c80e modify normal file and largefile in repo b
1302 1302 :
1303 1303 o 6:4355d653f84f edit files yet again
1304 1304 |
1305 1305 o 5:9d5af5072dbd edit files again
1306 1306 |
1307 1307 o 4:74c02385b94c move files
1308 1308 |
1309 1309 ~
1310 1310 $ hg log --template '{rev}:{node|short} {desc|firstline}\n' sub/large4
1311 1311 8:a381d2c8c80e modify normal file and largefile in repo b
1312 1312 6:4355d653f84f edit files yet again
1313 1313 5:9d5af5072dbd edit files again
1314 1314 4:74c02385b94c move files
1315 1315 $ hg log -G --template '{rev}:{node|short} {desc|firstline}\n' .hglf/sub/large4
1316 1316 o 8:a381d2c8c80e modify normal file and largefile in repo b
1317 1317 :
1318 1318 o 6:4355d653f84f edit files yet again
1319 1319 |
1320 1320 o 5:9d5af5072dbd edit files again
1321 1321 |
1322 1322 o 4:74c02385b94c move files
1323 1323 |
1324 1324 ~
1325 1325
1326 1326 - .hglf only matches largefiles, without .hglf it matches 9 bco sub/normal
1327 1327 $ hg log --template '{rev}:{node|short} {desc|firstline}\n' .hglf/sub
1328 1328 8:a381d2c8c80e modify normal file and largefile in repo b
1329 1329 6:4355d653f84f edit files yet again
1330 1330 5:9d5af5072dbd edit files again
1331 1331 4:74c02385b94c move files
1332 1332 1:ce8896473775 edit files
1333 1333 0:30d30fe6a5be add files
1334 1334 $ hg log -G --template '{rev}:{node|short} {desc|firstline}\n' .hglf/sub
1335 1335 o 8:a381d2c8c80e modify normal file and largefile in repo b
1336 1336 :
1337 1337 o 6:4355d653f84f edit files yet again
1338 1338 |
1339 1339 o 5:9d5af5072dbd edit files again
1340 1340 |
1341 1341 o 4:74c02385b94c move files
1342 1342 :
1343 1343 o 1:ce8896473775 edit files
1344 1344 |
1345 1345 o 0:30d30fe6a5be add files
1346 1346
1347 1347 $ hg log --template '{rev}:{node|short} {desc|firstline}\n' sub
1348 1348 9:598410d3eb9a modify normal file largefile in repo d
1349 1349 8:a381d2c8c80e modify normal file and largefile in repo b
1350 1350 6:4355d653f84f edit files yet again
1351 1351 5:9d5af5072dbd edit files again
1352 1352 4:74c02385b94c move files
1353 1353 1:ce8896473775 edit files
1354 1354 0:30d30fe6a5be add files
1355 1355 $ hg log -G --template '{rev}:{node|short} {desc|firstline}\n' sub
1356 1356 @ 9:598410d3eb9a modify normal file largefile in repo d
1357 1357 |
1358 1358 o 8:a381d2c8c80e modify normal file and largefile in repo b
1359 1359 :
1360 1360 o 6:4355d653f84f edit files yet again
1361 1361 |
1362 1362 o 5:9d5af5072dbd edit files again
1363 1363 |
1364 1364 o 4:74c02385b94c move files
1365 1365 :
1366 1366 o 1:ce8896473775 edit files
1367 1367 |
1368 1368 o 0:30d30fe6a5be add files
1369 1369
1370 1370 - globbing gives same result
1371 1371 $ hg log --template '{rev}:{node|short} {desc|firstline}\n' 'glob:sub/*'
1372 1372 9:598410d3eb9a modify normal file largefile in repo d
1373 1373 8:a381d2c8c80e modify normal file and largefile in repo b
1374 1374 6:4355d653f84f edit files yet again
1375 1375 5:9d5af5072dbd edit files again
1376 1376 4:74c02385b94c move files
1377 1377 1:ce8896473775 edit files
1378 1378 0:30d30fe6a5be add files
1379 1379 $ hg log -G --template '{rev}:{node|short} {desc|firstline}\n' 'glob:sub/*'
1380 1380 @ 9:598410d3eb9a modify normal file largefile in repo d
1381 1381 |
1382 1382 o 8:a381d2c8c80e modify normal file and largefile in repo b
1383 1383 :
1384 1384 o 6:4355d653f84f edit files yet again
1385 1385 |
1386 1386 o 5:9d5af5072dbd edit files again
1387 1387 |
1388 1388 o 4:74c02385b94c move files
1389 1389 :
1390 1390 o 1:ce8896473775 edit files
1391 1391 |
1392 1392 o 0:30d30fe6a5be add files
1393 1393
1394 1394 Rollback on largefiles.
1395 1395
1396 1396 $ echo large4-modified-again > sub/large4
1397 1397 $ hg commit -m "Modify large4 again"
1398 1398 Invoking status precommit hook
1399 1399 M sub/large4
1400 1400 $ hg rollback
1401 1401 repository tip rolled back to revision 9 (undo commit)
1402 1402 working directory now based on revision 9
1403 1403 $ hg st
1404 1404 M sub/large4
1405 1405 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
1406 1406 9:598410d3eb9a modify normal file largefile in repo d
1407 1407 8:a381d2c8c80e modify normal file and largefile in repo b
1408 1408 7:daea875e9014 add/edit more largefiles
1409 1409 6:4355d653f84f edit files yet again
1410 1410 5:9d5af5072dbd edit files again
1411 1411 4:74c02385b94c move files
1412 1412 3:9e8fbc4bce62 copy files
1413 1413 2:51a0ae4d5864 remove files
1414 1414 1:ce8896473775 edit files
1415 1415 0:30d30fe6a5be add files
1416 1416 $ cat sub/large4
1417 1417 large4-modified-again
1418 1418
1419 1419 "update --check" refuses to update with uncommitted changes.
1420 1420 $ hg update --check 8
1421 1421 abort: uncommitted changes
1422 1422 [255]
1423 1423
1424 1424 "update --clean" leaves correct largefiles in working copy, even when there is
1425 1425 .orig files from revert in .hglf.
1426 1426
1427 1427 $ echo mistake > sub2/large7
1428 1428 $ hg revert sub2/large7
1429 1429 $ cat sub2/large7
1430 1430 large7
1431 1431 $ cat sub2/large7.orig
1432 1432 mistake
1433 1433 $ test ! -f .hglf/sub2/large7.orig
1434 1434
1435 1435 $ hg -q update --clean -r null
1436 1436 $ hg update --clean
1437 1437 getting changed largefiles
1438 1438 3 largefiles updated, 0 removed
1439 1439 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
1440 1440 $ cat normal3
1441 1441 normal3-modified
1442 1442 $ cat sub/normal4
1443 1443 normal4-modified
1444 1444 $ cat sub/large4
1445 1445 large4-modified
1446 1446 $ cat sub2/large6
1447 1447 large6-modified
1448 1448 $ cat sub2/large7
1449 1449 large7
1450 1450 $ cat sub2/large7.orig
1451 1451 mistake
1452 1452 $ test ! -f .hglf/sub2/large7.orig
1453 1453
1454 1454 verify that largefile .orig file no longer is overwritten on every update -C:
1455 1455 $ hg update --clean
1456 1456 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1457 1457 $ cat sub2/large7.orig
1458 1458 mistake
1459 1459 $ rm sub2/large7.orig
1460 1460
1461 1461 Now "update check" is happy.
1462 1462 $ hg update --check 8
1463 1463 getting changed largefiles
1464 1464 1 largefiles updated, 0 removed
1465 1465 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1466 1466 $ hg update --check
1467 1467 getting changed largefiles
1468 1468 1 largefiles updated, 0 removed
1469 1469 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1470 1470
1471 1471 Test removing empty largefiles directories on update
1472 1472 $ test -d sub2 && echo "sub2 exists"
1473 1473 sub2 exists
1474 1474 $ hg update -q null
1475 1475 $ test -d sub2 && echo "error: sub2 should not exist anymore"
1476 1476 [1]
1477 1477 $ hg update -q
1478 1478
1479 1479 Test hg remove removes empty largefiles directories
1480 1480 $ test -d sub2 && echo "sub2 exists"
1481 1481 sub2 exists
1482 1482 $ hg remove sub2/*
1483 1483 $ test -d sub2 && echo "error: sub2 should not exist anymore"
1484 1484 [1]
1485 1485 $ hg revert sub2/large6 sub2/large7
1486 1486
1487 1487 "revert" works on largefiles (and normal files too).
1488 1488 $ echo hack3 >> normal3
1489 1489 $ echo hack4 >> sub/normal4
1490 1490 $ echo hack4 >> sub/large4
1491 1491 $ rm sub2/large6
1492 1492 $ hg revert sub2/large6
1493 1493 $ hg rm sub2/large6
1494 1494 $ echo new >> sub2/large8
1495 1495 $ hg add --large sub2/large8
1496 1496 # XXX we don't really want to report that we're reverting the standin;
1497 1497 # that's just an implementation detail. But I don't see an obvious fix. ;-(
1498 1498 $ hg revert sub
1499 1499 reverting .hglf/sub/large4
1500 1500 reverting sub/normal4
1501 1501 $ hg status
1502 1502 M normal3
1503 1503 A sub2/large8
1504 1504 R sub2/large6
1505 1505 ? sub/large4.orig
1506 1506 ? sub/normal4.orig
1507 1507 $ cat sub/normal4
1508 1508 normal4-modified
1509 1509 $ cat sub/large4
1510 1510 large4-modified
1511 1511 $ hg revert -a --no-backup
1512 1512 forgetting .hglf/sub2/large8
1513 1513 reverting normal3
1514 1514 undeleting .hglf/sub2/large6
1515 1515 $ hg status
1516 1516 ? sub/large4.orig
1517 1517 ? sub/normal4.orig
1518 1518 ? sub2/large8
1519 1519 $ cat normal3
1520 1520 normal3-modified
1521 1521 $ cat sub2/large6
1522 1522 large6-modified
1523 1523 $ rm sub/*.orig sub2/large8
1524 1524
1525 1525 revert some files to an older revision
1526 1526 $ hg revert --no-backup -r 8 sub2
1527 1527 reverting .hglf/sub2/large6
1528 1528 $ cat sub2/large6
1529 1529 large6
1530 1530 $ hg revert --no-backup -C -r '.^' sub2
1531 1531 $ hg revert --no-backup sub2
1532 1532 reverting .hglf/sub2/large6
1533 1533 $ hg status
1534 1534
1535 1535 "verify --large" actually verifies largefiles
1536 1536
1537 1537 - Where Do We Come From? What Are We? Where Are We Going?
1538 1538 $ pwd
1539 1539 $TESTTMP/e
1540 1540 $ hg paths
1541 1541 default = $TESTTMP/d
1542 1542
1543 1543 $ hg verify --large
1544 1544 checking changesets
1545 1545 checking manifests
1546 1546 crosschecking files in changesets and manifests
1547 1547 checking files
1548 checking dirstate
1548 1549 checked 10 changesets with 28 changes to 10 files
1549 1550 searching 1 changesets for largefiles
1550 1551 verified existence of 3 revisions of 3 largefiles
1551 1552
1552 1553 - introduce missing blob in local store repo and remote store
1553 1554 and make sure that this is caught:
1554 1555
1555 1556 $ mv $TESTTMP/d/.hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928 .
1556 1557 $ rm .hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928
1557 1558 $ hg verify --large -q
1558 1559 changeset 9:598410d3eb9a: sub/large4 references missing $TESTTMP/d/.hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928
1559 1560 [1]
1560 1561
1561 1562 - introduce corruption and make sure that it is caught when checking content:
1562 1563 $ echo '5 cents' > $TESTTMP/d/.hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928
1563 1564 $ hg verify -q --large --lfc
1564 1565 changeset 9:598410d3eb9a: sub/large4 references corrupted $TESTTMP/d/.hg/largefiles/e166e74c7303192238d60af5a9c4ce9bef0b7928
1565 1566 [1]
1566 1567
1567 1568 - cleanup
1568 1569 $ cp e166e74c7303192238d60af5a9c4ce9bef0b7928 $TESTTMP/d/.hg/largefiles/
1569 1570 $ mv e166e74c7303192238d60af5a9c4ce9bef0b7928 .hg/largefiles/
1570 1571
1571 1572 - verifying all revisions will fail because we didn't clone all largefiles to d:
1572 1573 $ echo 'T-shirt' > $TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4
1573 1574 $ hg verify -q --lfa --lfc
1574 1575 changeset 0:30d30fe6a5be: large1 references missing $TESTTMP/d/.hg/largefiles/4669e532d5b2c093a78eca010077e708a071bb64
1575 1576 changeset 0:30d30fe6a5be: sub/large2 references missing $TESTTMP/d/.hg/largefiles/1deebade43c8c498a3c8daddac0244dc55d1331d
1576 1577 changeset 1:ce8896473775: large1 references missing $TESTTMP/d/.hg/largefiles/5f78770c0e77ba4287ad6ef3071c9bf9c379742f
1577 1578 changeset 1:ce8896473775: sub/large2 references corrupted $TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4
1578 1579 changeset 3:9e8fbc4bce62: large1 references corrupted $TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4
1579 1580 changeset 4:74c02385b94c: large3 references corrupted $TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4
1580 1581 changeset 4:74c02385b94c: sub/large4 references corrupted $TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4
1581 1582 changeset 5:9d5af5072dbd: large3 references missing $TESTTMP/d/.hg/largefiles/baaf12afde9d8d67f25dab6dced0d2bf77dba47c
1582 1583 changeset 5:9d5af5072dbd: sub/large4 references missing $TESTTMP/d/.hg/largefiles/aeb2210d19f02886dde00dac279729a48471e2f9
1583 1584 changeset 6:4355d653f84f: large3 references missing $TESTTMP/d/.hg/largefiles/7838695e10da2bb75ac1156565f40a2595fa2fa0
1584 1585 [1]
1585 1586
1586 1587 - cleanup
1587 1588 $ rm $TESTTMP/d/.hg/largefiles/eb7338044dc27f9bc59b8dd5a246b065ead7a9c4
1588 1589 $ rm -f .hglf/sub/*.orig
1589 1590
1590 1591 Update to revision with missing largefile - and make sure it really is missing
1591 1592
1592 1593 $ rm ${USERCACHE}/7838695e10da2bb75ac1156565f40a2595fa2fa0
1593 1594 $ hg up -r 6
1594 1595 getting changed largefiles
1595 1596 large3: largefile 7838695e10da2bb75ac1156565f40a2595fa2fa0 not available from file:/*/$TESTTMP/d (glob)
1596 1597 1 largefiles updated, 2 removed
1597 1598 4 files updated, 0 files merged, 2 files removed, 0 files unresolved
1598 1599 $ rm normal3
1599 1600 $ echo >> sub/normal4
1600 1601 $ hg ci -m 'commit with missing files'
1601 1602 Invoking status precommit hook
1602 1603 M sub/normal4
1603 1604 ! large3
1604 1605 ! normal3
1605 1606 created new head
1606 1607 $ hg st
1607 1608 ! large3
1608 1609 ! normal3
1609 1610 $ hg up -r.
1610 1611 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1611 1612 $ hg st
1612 1613 ! large3
1613 1614 ! normal3
1614 1615 $ hg up -Cr.
1615 1616 getting changed largefiles
1616 1617 large3: largefile 7838695e10da2bb75ac1156565f40a2595fa2fa0 not available from file:/*/$TESTTMP/d (glob)
1617 1618 0 largefiles updated, 0 removed
1618 1619 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1619 1620 $ hg st
1620 1621 ! large3
1621 1622 $ hg rollback
1622 1623 repository tip rolled back to revision 9 (undo commit)
1623 1624 working directory now based on revision 6
1624 1625
1625 1626 Merge with revision with missing largefile - and make sure it tries to fetch it.
1626 1627
1627 1628 $ hg up -Cqr null
1628 1629 $ echo f > f
1629 1630 $ hg ci -Am branch
1630 1631 adding f
1631 1632 Invoking status precommit hook
1632 1633 A f
1633 1634 created new head
1634 1635 $ hg merge -r 6
1635 1636 getting changed largefiles
1636 1637 large3: largefile 7838695e10da2bb75ac1156565f40a2595fa2fa0 not available from file:/*/$TESTTMP/d (glob)
1637 1638 1 largefiles updated, 0 removed
1638 1639 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
1639 1640 (branch merge, don't forget to commit)
1640 1641
1641 1642 $ hg rollback -q
1642 1643 $ hg up -Cq
1643 1644
1644 1645 Pulling 0 revisions with --all-largefiles should not fetch for all revisions
1645 1646
1646 1647 $ hg pull --all-largefiles
1647 1648 pulling from $TESTTMP/d
1648 1649 searching for changes
1649 1650 no changes found
1650 1651
1651 1652 Merging does not revert to old versions of largefiles and also check
1652 1653 that merging after having pulled from a non-default remote works
1653 1654 correctly.
1654 1655
1655 1656 $ cd ..
1656 1657 $ hg clone -r 7 e temp
1657 1658 adding changesets
1658 1659 adding manifests
1659 1660 adding file changes
1660 1661 added 8 changesets with 24 changes to 10 files
1661 1662 new changesets 30d30fe6a5be:daea875e9014 (8 drafts)
1662 1663 updating to branch default
1663 1664 getting changed largefiles
1664 1665 3 largefiles updated, 0 removed
1665 1666 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
1666 1667 $ hg clone temp f
1667 1668 updating to branch default
1668 1669 getting changed largefiles
1669 1670 3 largefiles updated, 0 removed
1670 1671 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
1671 1672 # Delete the largefiles in the largefiles system cache so that we have an
1672 1673 # opportunity to test that caching after a pull works.
1673 1674 $ rm "${USERCACHE}"/*
1674 1675 $ cd f
1675 1676 $ echo "large4-merge-test" > sub/large4
1676 1677 $ hg commit -m "Modify large4 to test merge"
1677 1678 Invoking status precommit hook
1678 1679 M sub/large4
1679 1680 # Test --cache-largefiles flag
1680 1681 $ hg pull --lfrev 'heads(pulled())' ../e
1681 1682 pulling from ../e
1682 1683 searching for changes
1683 1684 adding changesets
1684 1685 adding manifests
1685 1686 adding file changes
1686 1687 added 2 changesets with 4 changes to 4 files (+1 heads)
1687 1688 new changesets a381d2c8c80e:598410d3eb9a (2 drafts)
1688 1689 (run 'hg heads' to see heads, 'hg merge' to merge)
1689 1690 2 largefiles cached
1690 1691 $ hg merge
1691 1692 largefile sub/large4 has a merge conflict
1692 1693 ancestor was 971fb41e78fea4f8e0ba5244784239371cb00591
1693 1694 you can keep (l)ocal d846f26643bfa8ec210be40cc93cc6b7ff1128ea or take (o)ther e166e74c7303192238d60af5a9c4ce9bef0b7928.
1694 1695 what do you want to do? l
1695 1696 getting changed largefiles
1696 1697 1 largefiles updated, 0 removed
1697 1698 3 files updated, 1 files merged, 0 files removed, 0 files unresolved
1698 1699 (branch merge, don't forget to commit)
1699 1700 $ hg commit -m "Merge repos e and f"
1700 1701 Invoking status precommit hook
1701 1702 M normal3
1702 1703 M sub/normal4
1703 1704 M sub2/large6
1704 1705 $ cat normal3
1705 1706 normal3-modified
1706 1707 $ cat sub/normal4
1707 1708 normal4-modified
1708 1709 $ cat sub/large4
1709 1710 large4-merge-test
1710 1711 $ cat sub2/large6
1711 1712 large6-modified
1712 1713 $ cat sub2/large7
1713 1714 large7
1714 1715
1715 1716 Test status after merging with a branch that introduces a new largefile:
1716 1717
1717 1718 $ echo large > large
1718 1719 $ hg add --large large
1719 1720 $ hg commit -m 'add largefile'
1720 1721 Invoking status precommit hook
1721 1722 A large
1722 1723 $ hg update -q ".^"
1723 1724 $ echo change >> normal3
1724 1725 $ hg commit -m 'some change'
1725 1726 Invoking status precommit hook
1726 1727 M normal3
1727 1728 created new head
1728 1729 $ hg merge
1729 1730 getting changed largefiles
1730 1731 1 largefiles updated, 0 removed
1731 1732 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1732 1733 (branch merge, don't forget to commit)
1733 1734 $ hg status
1734 1735 M large
1735 1736
1736 1737 - make sure update of merge with removed largefiles fails as expected
1737 1738 $ hg rm sub2/large6
1738 1739 $ hg up -r.
1739 1740 abort: outstanding uncommitted merge
1740 1741 [20]
1741 1742
1742 1743 - revert should be able to revert files introduced in a pending merge
1743 1744 $ hg revert --all -r .
1744 1745 removing .hglf/large
1745 1746 undeleting .hglf/sub2/large6
1746 1747
1747 1748 Test that a normal file and a largefile with the same name and path cannot
1748 1749 coexist.
1749 1750
1750 1751 $ rm sub2/large7
1751 1752 $ echo "largeasnormal" > sub2/large7
1752 1753 $ hg add sub2/large7
1753 1754 sub2/large7 already a largefile
1754 1755
1755 1756 Test that transplanting a largefile change works correctly.
1756 1757
1757 1758 $ cd ..
1758 1759 $ hg clone -r 8 d g
1759 1760 adding changesets
1760 1761 adding manifests
1761 1762 adding file changes
1762 1763 added 9 changesets with 26 changes to 10 files
1763 1764 new changesets 30d30fe6a5be:a381d2c8c80e (9 drafts)
1764 1765 updating to branch default
1765 1766 getting changed largefiles
1766 1767 3 largefiles updated, 0 removed
1767 1768 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
1768 1769 $ cd g
1769 1770 $ hg transplant -s ../d 598410d3eb9a
1770 1771 searching for changes
1771 1772 searching for changes
1772 1773 adding changesets
1773 1774 adding manifests
1774 1775 adding file changes
1775 1776 added 1 changesets with 2 changes to 2 files
1776 1777 new changesets 598410d3eb9a (1 drafts)
1777 1778 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
1778 1779 9:598410d3eb9a modify normal file largefile in repo d
1779 1780 8:a381d2c8c80e modify normal file and largefile in repo b
1780 1781 7:daea875e9014 add/edit more largefiles
1781 1782 6:4355d653f84f edit files yet again
1782 1783 5:9d5af5072dbd edit files again
1783 1784 4:74c02385b94c move files
1784 1785 3:9e8fbc4bce62 copy files
1785 1786 2:51a0ae4d5864 remove files
1786 1787 1:ce8896473775 edit files
1787 1788 0:30d30fe6a5be add files
1788 1789 $ cat normal3
1789 1790 normal3-modified
1790 1791 $ cat sub/normal4
1791 1792 normal4-modified
1792 1793 $ cat sub/large4
1793 1794 large4-modified
1794 1795 $ cat sub2/large6
1795 1796 large6-modified
1796 1797 $ cat sub2/large7
1797 1798 large7
1798 1799
1799 1800 Cat a largefile
1800 1801 $ hg cat normal3
1801 1802 normal3-modified
1802 1803 $ hg cat sub/large4
1803 1804 large4-modified
1804 1805 $ rm "${USERCACHE}"/*
1805 1806 $ hg cat -r a381d2c8c80e -o cat.out sub/large4
1806 1807 $ cat cat.out
1807 1808 large4-modified
1808 1809 $ rm cat.out
1809 1810 $ hg cat -r a381d2c8c80e normal3
1810 1811 normal3-modified
1811 1812 $ hg cat -r '.^' normal3
1812 1813 normal3-modified
1813 1814 $ hg cat -r '.^' sub/large4 doesntexist
1814 1815 large4-modified
1815 1816 doesntexist: no such file in rev a381d2c8c80e
1816 1817 $ hg --cwd sub cat -r '.^' large4
1817 1818 large4-modified
1818 1819 $ hg --cwd sub cat -r '.^' ../normal3
1819 1820 normal3-modified
1820 1821 Cat a standin
1821 1822 $ hg cat .hglf/sub/large4
1822 1823 e166e74c7303192238d60af5a9c4ce9bef0b7928
1823 1824 $ hg cat .hglf/normal3
1824 1825 .hglf/normal3: no such file in rev 598410d3eb9a
1825 1826 [1]
1826 1827
1827 1828 Test that renaming a largefile results in correct output for status
1828 1829
1829 1830 $ hg rename sub/large4 large4-renamed
1830 1831 $ hg commit -m "test rename output"
1831 1832 Invoking status precommit hook
1832 1833 A large4-renamed
1833 1834 R sub/large4
1834 1835 $ cat large4-renamed
1835 1836 large4-modified
1836 1837 $ cd sub2
1837 1838 $ hg rename large6 large6-renamed
1838 1839 $ hg st
1839 1840 A sub2/large6-renamed
1840 1841 R sub2/large6
1841 1842 $ cd ..
1842 1843
1843 1844 Test --normal flag
1844 1845
1845 1846 $ dd if=/dev/zero bs=2k count=11k > new-largefile 2> /dev/null
1846 1847 $ hg add --normal --large new-largefile
1847 1848 abort: --normal cannot be used with --large
1848 1849 [255]
1849 1850 $ hg add --normal new-largefile
1850 1851 new-largefile: up to 69 MB of RAM may be required to manage this file
1851 1852 (use 'hg revert new-largefile' to cancel the pending addition)
1852 1853 $ hg revert new-largefile
1853 1854 $ hg --config ui.large-file-limit=22M add --normal new-largefile
1854 1855
1855 1856 Test explicit commit of switch between normal and largefile - make sure both
1856 1857 the add and the remove is committed.
1857 1858
1858 1859 $ hg up -qC
1859 1860 $ hg forget normal3 large4-renamed
1860 1861 $ hg add --large normal3
1861 1862 $ hg add large4-renamed
1862 1863 $ hg commit -m 'swap' normal3 large4-renamed
1863 1864 Invoking status precommit hook
1864 1865 A large4-renamed
1865 1866 A normal3
1866 1867 ? new-largefile
1867 1868 ? sub2/large6-renamed
1868 1869 $ hg mani
1869 1870 .hglf/normal3
1870 1871 .hglf/sub2/large6
1871 1872 .hglf/sub2/large7
1872 1873 large4-renamed
1873 1874 sub/normal4
1874 1875
1875 1876 $ cd ..
1876 1877
1877 1878
1878 1879
@@ -1,412 +1,413 b''
1 1 $ USERCACHE="$TESTTMP/cache"; export USERCACHE
2 2 $ mkdir "${USERCACHE}"
3 3 $ cat >> $HGRCPATH <<EOF
4 4 > [extensions]
5 5 > largefiles =
6 6 > share =
7 7 > strip =
8 8 > convert =
9 9 > [largefiles]
10 10 > minsize = 0.5
11 11 > patterns = **.other
12 12 > **.dat
13 13 > usercache=${USERCACHE}
14 14 > EOF
15 15
16 16 "lfconvert" works
17 17 $ hg init bigfile-repo
18 18 $ cd bigfile-repo
19 19 $ cat >> .hg/hgrc <<EOF
20 20 > [extensions]
21 21 > largefiles = !
22 22 > EOF
23 23 $ mkdir sub
24 24 $ dd if=/dev/zero bs=1k count=256 > large 2> /dev/null
25 25 $ dd if=/dev/zero bs=1k count=256 > large2 2> /dev/null
26 26 $ echo normal > normal1
27 27 $ echo alsonormal > sub/normal2
28 28 $ dd if=/dev/zero bs=1k count=10 > sub/maybelarge.dat 2> /dev/null
29 29 $ hg addremove
30 30 adding large
31 31 adding large2
32 32 adding normal1
33 33 adding sub/maybelarge.dat
34 34 adding sub/normal2
35 35 $ hg commit -m"add large, normal1" large normal1
36 36 $ hg commit -m"add sub/*" sub
37 37
38 38 Test tag parsing
39 39 $ cat >> .hgtags <<EOF
40 40 > IncorrectlyFormattedTag!
41 41 > invalidhash sometag
42 42 > 0123456789abcdef anothertag
43 43 > EOF
44 44 $ hg add .hgtags
45 45 $ hg commit -m"add large2" large2 .hgtags
46 46
47 47 Test link+rename largefile codepath
48 48 $ [ -d .hg/largefiles ] && echo fail || echo pass
49 49 pass
50 50 $ cd ..
51 51 $ hg lfconvert --size 0.2 bigfile-repo largefiles-repo
52 52 initializing destination largefiles-repo
53 53 skipping incorrectly formatted tag IncorrectlyFormattedTag!
54 54 skipping incorrectly formatted id invalidhash
55 55 no mapping for id 0123456789abcdef
56 56 #if symlink
57 57 $ hg --cwd bigfile-repo rename large2 large3
58 58 $ ln -sf large bigfile-repo/large3
59 59 $ hg --cwd bigfile-repo commit -m"make large2 a symlink" large2 large3
60 60 $ hg lfconvert --size 0.2 bigfile-repo largefiles-repo-symlink
61 61 initializing destination largefiles-repo-symlink
62 62 skipping incorrectly formatted tag IncorrectlyFormattedTag!
63 63 skipping incorrectly formatted id invalidhash
64 64 no mapping for id 0123456789abcdef
65 65 abort: renamed/copied largefile large3 becomes symlink
66 66 [255]
67 67 #endif
68 68 $ cd bigfile-repo
69 69 $ hg strip --no-backup 2
70 70 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
71 71 $ cd ..
72 72 $ rm -rf largefiles-repo largefiles-repo-symlink
73 73
74 74 $ hg lfconvert --size 0.2 bigfile-repo largefiles-repo
75 75 initializing destination largefiles-repo
76 76
77 77 "lfconvert" converts content correctly
78 78 $ cd largefiles-repo
79 79 $ hg up
80 80 getting changed largefiles
81 81 2 largefiles updated, 0 removed
82 82 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
83 83 $ hg locate
84 84 .hglf/large
85 85 .hglf/sub/maybelarge.dat
86 86 normal1
87 87 sub/normal2
88 88 $ cat normal1
89 89 normal
90 90 $ cat sub/normal2
91 91 alsonormal
92 92 $ md5sum.py large sub/maybelarge.dat
93 93 ec87a838931d4d5d2e94a04644788a55 large
94 94 1276481102f218c981e0324180bafd9f sub/maybelarge.dat
95 95
96 96 "lfconvert" adds 'largefiles' to .hg/requires.
97 97 $ hg debugrequires
98 98 dotencode
99 99 dirstate-v2 (dirstate-v2 !)
100 100 fncache
101 101 generaldelta
102 102 largefiles
103 103 persistent-nodemap (rust !)
104 104 revlog-compression-zstd (zstd !)
105 105 revlogv1
106 106 share-safe
107 107 sparserevlog
108 108 store
109 109 testonly-simplestore (reposimplestore !)
110 110
111 111 "lfconvert" includes a newline at the end of the standin files.
112 112 $ cat .hglf/large .hglf/sub/maybelarge.dat
113 113 2e000fa7e85759c7f4c254d4d9c33ef481e459a7
114 114 34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c
115 115 $ cd ..
116 116
117 117 add some changesets to rename/remove/merge
118 118 $ cd bigfile-repo
119 119 $ hg mv -q sub stuff
120 120 $ hg commit -m"rename sub/ to stuff/"
121 121 $ hg update -q 1
122 122 $ echo blah >> normal3
123 123 $ echo blah >> sub/normal2
124 124 $ echo blah >> sub/maybelarge.dat
125 125 $ md5sum.py sub/maybelarge.dat
126 126 1dd0b99ff80e19cff409702a1d3f5e15 sub/maybelarge.dat
127 127 $ hg commit -A -m"add normal3, modify sub/*"
128 128 adding normal3
129 129 created new head
130 130 $ hg rm large normal3
131 131 $ hg commit -q -m"remove large, normal3"
132 132 $ hg merge
133 133 tool internal:merge (for pattern stuff/maybelarge.dat) can't handle binary
134 134 no tool found to merge stuff/maybelarge.dat
135 135 file 'stuff/maybelarge.dat' needs to be resolved.
136 136 You can keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved.
137 137 What do you want to do? u
138 138 merging sub/normal2 and stuff/normal2 to stuff/normal2
139 139 0 files updated, 1 files merged, 0 files removed, 1 files unresolved
140 140 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
141 141 [1]
142 142 $ hg cat -r . sub/maybelarge.dat > stuff/maybelarge.dat
143 143 $ hg resolve -m stuff/maybelarge.dat
144 144 (no more unresolved files)
145 145 $ hg commit -m"merge"
146 146 $ hg log -G --template "{rev}:{node|short} {desc|firstline}\n"
147 147 @ 5:4884f215abda merge
148 148 |\
149 149 | o 4:7285f817b77e remove large, normal3
150 150 | |
151 151 | o 3:67e3892e3534 add normal3, modify sub/*
152 152 | |
153 153 o | 2:c96c8beb5d56 rename sub/ to stuff/
154 154 |/
155 155 o 1:020c65d24e11 add sub/*
156 156 |
157 157 o 0:117b8328f97a add large, normal1
158 158
159 159 $ cd ..
160 160
161 161 lfconvert with rename, merge, and remove
162 162 $ rm -rf largefiles-repo
163 163 $ hg lfconvert --size 0.2 bigfile-repo largefiles-repo
164 164 initializing destination largefiles-repo
165 165 $ cd largefiles-repo
166 166 $ hg log -G --template "{rev}:{node|short} {desc|firstline}\n"
167 167 o 5:9cc5aa7204f0 merge
168 168 |\
169 169 | o 4:a5a02de7a8e4 remove large, normal3
170 170 | |
171 171 | o 3:55759520c76f add normal3, modify sub/*
172 172 | |
173 173 o | 2:261ad3f3f037 rename sub/ to stuff/
174 174 |/
175 175 o 1:334e5237836d add sub/*
176 176 |
177 177 o 0:d4892ec57ce2 add large, normal1
178 178
179 179 $ hg locate -r 2
180 180 .hglf/large
181 181 .hglf/stuff/maybelarge.dat
182 182 normal1
183 183 stuff/normal2
184 184 $ hg locate -r 3
185 185 .hglf/large
186 186 .hglf/sub/maybelarge.dat
187 187 normal1
188 188 normal3
189 189 sub/normal2
190 190 $ hg locate -r 4
191 191 .hglf/sub/maybelarge.dat
192 192 normal1
193 193 sub/normal2
194 194 $ hg locate -r 5
195 195 .hglf/stuff/maybelarge.dat
196 196 normal1
197 197 stuff/normal2
198 198 $ hg update
199 199 getting changed largefiles
200 200 1 largefiles updated, 0 removed
201 201 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
202 202 $ cat stuff/normal2
203 203 alsonormal
204 204 blah
205 205 $ md5sum.py stuff/maybelarge.dat
206 206 1dd0b99ff80e19cff409702a1d3f5e15 stuff/maybelarge.dat
207 207 $ cat .hglf/stuff/maybelarge.dat
208 208 76236b6a2c6102826c61af4297dd738fb3b1de38
209 209 $ cd ..
210 210
211 211 "lfconvert" error cases
212 212 $ hg lfconvert http://localhost/foo foo
213 213 abort: http://localhost/foo is not a local Mercurial repo
214 214 [255]
215 215 $ hg lfconvert foo ssh://localhost/foo
216 216 abort: ssh://localhost/foo is not a local Mercurial repo
217 217 [255]
218 218 $ hg lfconvert nosuchrepo foo
219 219 abort: repository nosuchrepo not found
220 220 [255]
221 221 $ hg share -q -U bigfile-repo shared
222 222 $ printf 'bogus' > shared/.hg/sharedpath
223 223 $ hg lfconvert shared foo
224 224 abort: .hg/sharedpath points to nonexistent directory $TESTTMP/bogus
225 225 [255]
226 226 $ hg lfconvert bigfile-repo largefiles-repo
227 227 initializing destination largefiles-repo
228 228 abort: repository largefiles-repo already exists
229 229 [255]
230 230
231 231 add another largefile to the new largefiles repo
232 232 $ cd largefiles-repo
233 233 $ dd if=/dev/zero bs=1k count=1k > anotherlarge 2> /dev/null
234 234 $ hg add --lfsize=1 anotherlarge
235 235 $ hg commit -m "add anotherlarge (should be a largefile)"
236 236 $ cat .hglf/anotherlarge
237 237 3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3
238 238 $ hg tag mytag
239 239 $ cd ..
240 240
241 241 round-trip: converting back to a normal (non-largefiles) repo with
242 242 "lfconvert --to-normal" should give the same as ../bigfile-repo. The
243 243 convert extension is disabled to show config items can be loaded without it.
244 244 $ cd largefiles-repo
245 245 $ hg --config extensions.convert=! lfconvert --to-normal . ../normal-repo
246 246 initializing destination ../normal-repo
247 247 0 additional largefiles cached
248 248 scanning source...
249 249 sorting...
250 250 converting...
251 251 7 add large, normal1
252 252 6 add sub/*
253 253 5 rename sub/ to stuff/
254 254 4 add normal3, modify sub/*
255 255 3 remove large, normal3
256 256 2 merge
257 257 1 add anotherlarge (should be a largefile)
258 258 0 Added tag mytag for changeset 17126745edfd
259 259 $ cd ../normal-repo
260 260 $ cat >> .hg/hgrc <<EOF
261 261 > [extensions]
262 262 > largefiles = !
263 263 > EOF
264 264
265 265 $ hg log -G --template "{rev}:{node|short} {desc|firstline}\n"
266 266 o 7:b5fedc110b9d Added tag mytag for changeset 867ab992ecf4
267 267 |
268 268 o 6:867ab992ecf4 add anotherlarge (should be a largefile)
269 269 |
270 270 o 5:4884f215abda merge
271 271 |\
272 272 | o 4:7285f817b77e remove large, normal3
273 273 | |
274 274 | o 3:67e3892e3534 add normal3, modify sub/*
275 275 | |
276 276 o | 2:c96c8beb5d56 rename sub/ to stuff/
277 277 |/
278 278 o 1:020c65d24e11 add sub/*
279 279 |
280 280 o 0:117b8328f97a add large, normal1
281 281
282 282 $ hg update
283 283 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
284 284 $ hg locate
285 285 .hgtags
286 286 anotherlarge
287 287 normal1
288 288 stuff/maybelarge.dat
289 289 stuff/normal2
290 290 $ [ -d .hg/largefiles ] && echo fail || echo pass
291 291 pass
292 292
293 293 $ cd ..
294 294
295 295 Clearing the usercache ensures that commitctx doesn't try to cache largefiles
296 296 from the working dir on a convert.
297 297 $ rm "${USERCACHE}"/*
298 298 $ hg convert largefiles-repo
299 299 assuming destination largefiles-repo-hg
300 300 initializing destination largefiles-repo-hg repository
301 301 scanning source...
302 302 sorting...
303 303 converting...
304 304 7 add large, normal1
305 305 6 add sub/*
306 306 5 rename sub/ to stuff/
307 307 4 add normal3, modify sub/*
308 308 3 remove large, normal3
309 309 2 merge
310 310 1 add anotherlarge (should be a largefile)
311 311 0 Added tag mytag for changeset 17126745edfd
312 312
313 313 $ hg -R largefiles-repo-hg log -G --template "{rev}:{node|short} {desc|firstline}\n"
314 314 o 7:2f08f66459b7 Added tag mytag for changeset 17126745edfd
315 315 |
316 316 o 6:17126745edfd add anotherlarge (should be a largefile)
317 317 |
318 318 o 5:9cc5aa7204f0 merge
319 319 |\
320 320 | o 4:a5a02de7a8e4 remove large, normal3
321 321 | |
322 322 | o 3:55759520c76f add normal3, modify sub/*
323 323 | |
324 324 o | 2:261ad3f3f037 rename sub/ to stuff/
325 325 |/
326 326 o 1:334e5237836d add sub/*
327 327 |
328 328 o 0:d4892ec57ce2 add large, normal1
329 329
330 330 Verify will fail (for now) if the usercache is purged before converting, since
331 331 largefiles are not cached in the converted repo's local store by the conversion
332 332 process.
333 333 $ cd largefiles-repo-hg
334 334 $ cat >> .hg/hgrc <<EOF
335 335 > [experimental]
336 336 > evolution.createmarkers=True
337 337 > EOF
338 338 $ hg debugobsolete `hg log -r tip -T "{node}"`
339 339 1 new obsolescence markers
340 340 obsoleted 1 changesets
341 341 $ cd ..
342 342
343 343 $ hg -R largefiles-repo-hg verify --large --lfa
344 344 checking changesets
345 345 checking manifests
346 346 crosschecking files in changesets and manifests
347 347 checking files
348 checking dirstate
348 349 checked 8 changesets with 13 changes to 9 files
349 350 searching 7 changesets for largefiles
350 351 changeset 0:d4892ec57ce2: large references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/2e000fa7e85759c7f4c254d4d9c33ef481e459a7
351 352 changeset 1:334e5237836d: sub/maybelarge.dat references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c
352 353 changeset 2:261ad3f3f037: stuff/maybelarge.dat references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c
353 354 changeset 3:55759520c76f: sub/maybelarge.dat references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/76236b6a2c6102826c61af4297dd738fb3b1de38
354 355 changeset 5:9cc5aa7204f0: stuff/maybelarge.dat references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/76236b6a2c6102826c61af4297dd738fb3b1de38
355 356 changeset 6:17126745edfd: anotherlarge references missing $TESTTMP/largefiles-repo-hg/.hg/largefiles/3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3
356 357 verified existence of 6 revisions of 4 largefiles
357 358 [1]
358 359 $ hg -R largefiles-repo-hg showconfig paths
359 360 [1]
360 361
361 362
362 363 Avoid a traceback if a largefile isn't available (issue3519)
363 364
364 365 Ensure the largefile can be cached in the source if necessary
365 366 $ hg clone -U largefiles-repo issue3519
366 367 $ rm -f "${USERCACHE}"/*
367 368 $ hg -R issue3519 branch -q mybranch
368 369 $ hg -R issue3519 ci -m 'change branch name only'
369 370 $ hg lfconvert --to-normal issue3519 normalized3519
370 371 initializing destination normalized3519
371 372 4 additional largefiles cached
372 373 scanning source...
373 374 sorting...
374 375 converting...
375 376 8 add large, normal1
376 377 7 add sub/*
377 378 6 rename sub/ to stuff/
378 379 5 add normal3, modify sub/*
379 380 4 remove large, normal3
380 381 3 merge
381 382 2 add anotherlarge (should be a largefile)
382 383 1 Added tag mytag for changeset 17126745edfd
383 384 0 change branch name only
384 385
385 386 Ensure empty commits aren't lost in the conversion
386 387 $ hg -R normalized3519 log -r tip -T '{desc}\n'
387 388 change branch name only
388 389
389 390 Ensure the abort message is useful if a largefile is entirely unavailable
390 391 $ rm -rf normalized3519
391 392 $ rm "${USERCACHE}"/*
392 393 $ rm issue3519/.hg/largefiles/*
393 394 $ rm largefiles-repo/.hg/largefiles/*
394 395 $ hg lfconvert --to-normal issue3519 normalized3519
395 396 initializing destination normalized3519
396 397 large: largefile 2e000fa7e85759c7f4c254d4d9c33ef481e459a7 not available from file:/*/$TESTTMP/largefiles-repo (glob)
397 398 large: largefile 2e000fa7e85759c7f4c254d4d9c33ef481e459a7 not available from file:/*/$TESTTMP/largefiles-repo (glob)
398 399 sub/maybelarge.dat: largefile 34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c not available from file:/*/$TESTTMP/largefiles-repo (glob)
399 400 large: largefile 2e000fa7e85759c7f4c254d4d9c33ef481e459a7 not available from file:/*/$TESTTMP/largefiles-repo (glob)
400 401 stuff/maybelarge.dat: largefile 34e163be8e43c5631d8b92e9c43ab0bf0fa62b9c not available from file:/*/$TESTTMP/largefiles-repo (glob)
401 402 large: largefile 2e000fa7e85759c7f4c254d4d9c33ef481e459a7 not available from file:/*/$TESTTMP/largefiles-repo (glob)
402 403 sub/maybelarge.dat: largefile 76236b6a2c6102826c61af4297dd738fb3b1de38 not available from file:/*/$TESTTMP/largefiles-repo (glob)
403 404 sub/maybelarge.dat: largefile 76236b6a2c6102826c61af4297dd738fb3b1de38 not available from file:/*/$TESTTMP/largefiles-repo (glob)
404 405 stuff/maybelarge.dat: largefile 76236b6a2c6102826c61af4297dd738fb3b1de38 not available from file:/*/$TESTTMP/largefiles-repo (glob)
405 406 anotherlarge: largefile 3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3 not available from file:/*/$TESTTMP/largefiles-repo (glob)
406 407 stuff/maybelarge.dat: largefile 76236b6a2c6102826c61af4297dd738fb3b1de38 not available from file:/*/$TESTTMP/largefiles-repo (glob)
407 408 0 additional largefiles cached
408 409 11 largefiles failed to download
409 410 abort: all largefiles must be present locally
410 411 [255]
411 412
412 413
@@ -1,1240 +1,1248 b''
1 1 #require no-reposimplestore no-chg
2 2
3 3 $ hg init requirements
4 4 $ cd requirements
5 5
6 6 # LFS not loaded by default.
7 7
8 8 $ hg config extensions
9 9 [1]
10 10
11 11 # Adding lfs to requires file will auto-load lfs extension.
12 12
13 13 $ echo lfs >> .hg/requires
14 14 $ hg config extensions
15 15 extensions.lfs=
16 16
17 17 # But only if there is no config entry for the extension already.
18 18
19 19 $ cat > .hg/hgrc << EOF
20 20 > [extensions]
21 21 > lfs=!
22 22 > EOF
23 23
24 24 $ hg config extensions
25 25 abort: repository requires features unknown to this Mercurial: lfs
26 26 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
27 27 [255]
28 28
29 29 $ cat > .hg/hgrc << EOF
30 30 > [extensions]
31 31 > lfs=
32 32 > EOF
33 33
34 34 $ hg config extensions
35 35 extensions.lfs=
36 36
37 37 $ cat > .hg/hgrc << EOF
38 38 > [extensions]
39 39 > lfs = missing.py
40 40 > EOF
41 41
42 42 $ hg config extensions
43 43 \*\*\* failed to import extension "lfs" from missing.py: [Errno *] $ENOENT$: 'missing.py' (glob)
44 44 abort: repository requires features unknown to this Mercurial: lfs
45 45 (see https://mercurial-scm.org/wiki/MissingRequirement for more information)
46 46 [255]
47 47
48 48 $ cd ..
49 49
50 50 # Initial setup
51 51
52 52 $ cat >> $HGRCPATH << EOF
53 53 > [extensions]
54 54 > lfs=
55 55 > [lfs]
56 56 > # Test deprecated config
57 57 > threshold=1000B
58 58 > EOF
59 59
60 60 $ LONG=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
61 61
62 62 # Prepare server and enable extension
63 63 $ hg init server
64 64 $ hg clone -q server client
65 65 $ cd client
66 66
67 67 # Commit small file
68 68 $ echo s > smallfile
69 69 $ echo '**.py = LF' > .hgeol
70 70 $ hg --config lfs.track='"size(\">1000B\")"' commit -Aqm "add small file"
71 71 hg: parse error: unsupported file pattern: size(">1000B")
72 72 (paths must be prefixed with "path:")
73 73 [10]
74 74 $ hg --config lfs.track='size(">1000B")' commit -Aqm "add small file"
75 75
76 76 # Commit large file
77 77 $ echo $LONG > largefile
78 78 $ hg debugrequires | grep lfs
79 79 [1]
80 80 $ hg commit --traceback -Aqm "add large file"
81 81 $ hg debugrequires | grep lfs
82 82 lfs
83 83
84 84 # Ensure metadata is stored
85 85 $ hg debugdata largefile 0
86 86 version https://git-lfs.github.com/spec/v1
87 87 oid sha256:f11e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
88 88 size 1501
89 89 x-is-binary 0
90 90
91 91 # Check the blobstore is populated
92 92 $ find .hg/store/lfs/objects | sort
93 93 .hg/store/lfs/objects
94 94 .hg/store/lfs/objects/f1
95 95 .hg/store/lfs/objects/f1/1e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
96 96
97 97 # Check the blob stored contains the actual contents of the file
98 98 $ cat .hg/store/lfs/objects/f1/1e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
99 99 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
100 100
101 101 # Push changes to the server
102 102
103 103 $ hg push
104 104 pushing to $TESTTMP/server
105 105 searching for changes
106 106 abort: lfs.url needs to be configured
107 107 [255]
108 108
109 109 $ cat >> $HGRCPATH << EOF
110 110 > [lfs]
111 111 > url=file:$TESTTMP/dummy-remote/
112 112 > EOF
113 113
114 114 Push to a local non-lfs repo with the extension enabled will add the
115 115 lfs requirement
116 116
117 117 $ hg debugrequires -R $TESTTMP/server/ | grep lfs
118 118 [1]
119 119 $ hg push -v | egrep -v '^(uncompressed| )'
120 120 pushing to $TESTTMP/server
121 121 searching for changes
122 122 lfs: found f11e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b in the local lfs store
123 123 2 changesets found
124 124 adding changesets
125 125 adding manifests
126 126 adding file changes
127 127 calling hook pretxnchangegroup.lfs: hgext.lfs.checkrequireslfs
128 128 added 2 changesets with 3 changes to 3 files
129 129 $ hg debugrequires -R $TESTTMP/server/ | grep lfs
130 130 lfs
131 131
132 132 # Unknown URL scheme
133 133
134 134 $ hg push --config lfs.url=ftp://foobar
135 135 abort: lfs: unknown url scheme: ftp
136 136 [255]
137 137
138 138 $ cd ../
139 139
140 140 # Initialize new client (not cloning) and setup extension
141 141 $ hg init client2
142 142 $ cd client2
143 143 $ cat >> .hg/hgrc <<EOF
144 144 > [paths]
145 145 > default = $TESTTMP/server
146 146 > EOF
147 147
148 148 # Pull from server
149 149
150 150 Pulling a local lfs repo into a local non-lfs repo with the extension
151 151 enabled adds the lfs requirement
152 152
153 153 $ hg debugrequires | grep lfs || true
154 154 $ hg debugrequires -R $TESTTMP/server/ | grep lfs
155 155 lfs
156 156 $ hg pull default
157 157 pulling from $TESTTMP/server
158 158 requesting all changes
159 159 adding changesets
160 160 adding manifests
161 161 adding file changes
162 162 added 2 changesets with 3 changes to 3 files
163 163 new changesets 0ead593177f7:b88141481348
164 164 (run 'hg update' to get a working copy)
165 165 $ hg debugrequires | grep lfs
166 166 lfs
167 167 $ hg debugrequires -R $TESTTMP/server/ | grep lfs
168 168 lfs
169 169
170 170 # Check the blobstore is not yet populated
171 171 $ [ -d .hg/store/lfs/objects ]
172 172 [1]
173 173
174 174 # Update to the last revision containing the large file
175 175 $ hg update
176 176 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
177 177
178 178 # Check the blobstore has been populated on update
179 179 $ find .hg/store/lfs/objects | sort
180 180 .hg/store/lfs/objects
181 181 .hg/store/lfs/objects/f1
182 182 .hg/store/lfs/objects/f1/1e77c257047a398492d8d6cb9f6acf3aa7c4384bb23080b43546053e183e4b
183 183
184 184 # Check the contents of the file are fetched from blobstore when requested
185 185 $ hg cat -r . largefile
186 186 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
187 187
188 188 # Check the file has been copied in the working copy
189 189 $ cat largefile
190 190 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
191 191
192 192 $ cd ..
193 193
194 194 # Check rename, and switch between large and small files
195 195
196 196 $ hg init repo3
197 197 $ cd repo3
198 198 $ cat >> .hg/hgrc << EOF
199 199 > [lfs]
200 200 > track=size(">10B")
201 201 > EOF
202 202
203 203 $ echo LONGER-THAN-TEN-BYTES-WILL-TRIGGER-LFS > large
204 204 $ echo SHORTER > small
205 205 $ hg add . -q
206 206 $ hg commit -m 'commit with lfs content'
207 207
208 208 $ hg files -r . 'set:added()'
209 209 large
210 210 small
211 211 $ hg files -r . 'set:added() & lfs()'
212 212 large
213 213
214 214 $ hg mv large l
215 215 $ hg mv small s
216 216 $ hg status 'set:removed()'
217 217 R large
218 218 R small
219 219 $ hg status 'set:removed() & lfs()'
220 220 R large
221 221 $ hg commit -m 'renames'
222 222
223 223 $ hg cat -r . l -T '{rawdata}\n'
224 224 version https://git-lfs.github.com/spec/v1
225 225 oid sha256:66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
226 226 size 39
227 227 x-hg-copy large
228 228 x-hg-copyrev 2c531e0992ff3107c511b53cb82a91b6436de8b2
229 229 x-is-binary 0
230 230
231 231
232 232 $ hg files -r . 'set:copied()'
233 233 l
234 234 s
235 235 $ hg files -r . 'set:copied() & lfs()'
236 236 l
237 237 $ hg status --change . 'set:removed()'
238 238 R large
239 239 R small
240 240 $ hg status --change . 'set:removed() & lfs()'
241 241 R large
242 242
243 243 $ echo SHORT > l
244 244 $ echo BECOME-LARGER-FROM-SHORTER > s
245 245 $ hg commit -m 'large to small, small to large'
246 246
247 247 $ echo 1 >> l
248 248 $ echo 2 >> s
249 249 $ hg commit -m 'random modifications'
250 250
251 251 $ echo RESTORE-TO-BE-LARGE > l
252 252 $ echo SHORTER > s
253 253 $ hg commit -m 'switch large and small again'
254 254
255 255 # Test lfs_files template
256 256
257 257 $ hg log -r 'all()' -T '{rev} {join(lfs_files, ", ")}\n'
258 258 0 large
259 259 1 l, large
260 260 2 s
261 261 3 s
262 262 4 l
263 263
264 264 # Push and pull the above repo
265 265
266 266 $ hg --cwd .. init repo4
267 267 $ hg push ../repo4
268 268 pushing to ../repo4
269 269 searching for changes
270 270 adding changesets
271 271 adding manifests
272 272 adding file changes
273 273 added 5 changesets with 10 changes to 4 files
274 274
275 275 $ hg --cwd .. init repo5
276 276 $ hg --cwd ../repo5 pull ../repo3
277 277 pulling from ../repo3
278 278 requesting all changes
279 279 adding changesets
280 280 adding manifests
281 281 adding file changes
282 282 added 5 changesets with 10 changes to 4 files
283 283 new changesets fd47a419c4f7:5adf850972b9
284 284 (run 'hg update' to get a working copy)
285 285
286 286 $ cd ..
287 287
288 288 # Test clone
289 289
290 290 $ hg init repo6
291 291 $ cd repo6
292 292 $ cat >> .hg/hgrc << EOF
293 293 > [lfs]
294 294 > track=size(">30B")
295 295 > EOF
296 296
297 297 $ echo LARGE-BECAUSE-IT-IS-MORE-THAN-30-BYTES > large
298 298 $ echo SMALL > small
299 299 $ hg commit -Aqm 'create a lfs file' large small
300 300 $ hg debuglfsupload -r 'all()' -v
301 301 lfs: found 8e92251415339ae9b148c8da89ed5ec665905166a1ab11b09dca8fad83344738 in the local lfs store
302 302
303 303 $ cd ..
304 304
305 305 $ hg clone repo6 repo7
306 306 updating to branch default
307 307 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
308 308 $ cd repo7
309 309 $ cat large
310 310 LARGE-BECAUSE-IT-IS-MORE-THAN-30-BYTES
311 311 $ cat small
312 312 SMALL
313 313
314 314 $ cd ..
315 315
316 316 $ hg --config extensions.share= share repo7 sharedrepo
317 317 updating working directory
318 318 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
319 319 $ hg debugrequires -R sharedrepo/ | grep lfs
320 320 lfs
321 321
322 322 # Test rename and status
323 323
324 324 $ hg init repo8
325 325 $ cd repo8
326 326 $ cat >> .hg/hgrc << EOF
327 327 > [lfs]
328 328 > track=size(">10B")
329 329 > EOF
330 330
331 331 $ echo THIS-IS-LFS-BECAUSE-10-BYTES > a1
332 332 $ echo SMALL > a2
333 333 $ hg commit -m a -A a1 a2
334 334 $ hg status
335 335 $ hg mv a1 b1
336 336 $ hg mv a2 a1
337 337 $ hg mv b1 a2
338 338 $ hg commit -m b
339 339 $ hg status
340 340 >>> with open('a2', 'wb') as f:
341 341 ... f.write(b'\1\nSTART-WITH-HG-FILELOG-METADATA') and None
342 342 >>> with open('a1', 'wb') as f:
343 343 ... f.write(b'\1\nMETA\n') and None
344 344 $ hg commit -m meta
345 345 $ hg status
346 346 $ hg log -T '{rev}: {file_copies} | {file_dels} | {file_adds}\n'
347 347 2: | |
348 348 1: a1 (a2)a2 (a1) | |
349 349 0: | | a1 a2
350 350
351 351 $ for n in a1 a2; do
352 352 > for r in 0 1 2; do
353 353 > printf '\n%s @ %s\n' $n $r
354 354 > hg debugdata $n $r
355 355 > done
356 356 > done
357 357
358 358 a1 @ 0
359 359 version https://git-lfs.github.com/spec/v1
360 360 oid sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
361 361 size 29
362 362 x-is-binary 0
363 363
364 364 a1 @ 1
365 365 \x01 (esc)
366 366 copy: a2
367 367 copyrev: 50470ad23cf937b1f4b9f80bfe54df38e65b50d9
368 368 \x01 (esc)
369 369 SMALL
370 370
371 371 a1 @ 2
372 372 \x01 (esc)
373 373 \x01 (esc)
374 374 \x01 (esc)
375 375 META
376 376
377 377 a2 @ 0
378 378 SMALL
379 379
380 380 a2 @ 1
381 381 version https://git-lfs.github.com/spec/v1
382 382 oid sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
383 383 size 29
384 384 x-hg-copy a1
385 385 x-hg-copyrev be23af27908a582af43e5cda209a5a9b319de8d4
386 386 x-is-binary 0
387 387
388 388 a2 @ 2
389 389 version https://git-lfs.github.com/spec/v1
390 390 oid sha256:876dadc86a8542f9798048f2c47f51dbf8e4359aed883e8ec80c5db825f0d943
391 391 size 32
392 392 x-is-binary 0
393 393
394 394 # Verify commit hashes include rename metadata
395 395
396 396 $ hg log -T '{rev}:{node|short} {desc}\n'
397 397 2:0fae949de7fa meta
398 398 1:9cd6bdffdac0 b
399 399 0:7f96794915f7 a
400 400
401 401 $ cd ..
402 402
403 403 # Test bundle
404 404
405 405 $ hg init repo9
406 406 $ cd repo9
407 407 $ cat >> .hg/hgrc << EOF
408 408 > [lfs]
409 409 > track=size(">10B")
410 410 > [diff]
411 411 > git=1
412 412 > EOF
413 413
414 414 $ for i in 0 single two three 4; do
415 415 > echo 'THIS-IS-LFS-'$i > a
416 416 > hg commit -m a-$i -A a
417 417 > done
418 418
419 419 $ hg update 2 -q
420 420 $ echo 'THIS-IS-LFS-2-CHILD' > a
421 421 $ hg commit -m branching -q
422 422
423 423 $ hg bundle --base 1 bundle.hg -v
424 424 lfs: found 5ab7a3739a5feec94a562d070a14f36dba7cad17e5484a4a89eea8e5f3166888 in the local lfs store
425 425 lfs: found a9c7d1cd6ce2b9bbdf46ed9a862845228717b921c089d0d42e3bcaed29eb612e in the local lfs store
426 426 lfs: found f693890c49c409ec33673b71e53f297681f76c1166daf33b2ad7ebf8b1d3237e in the local lfs store
427 427 lfs: found fda198fea753eb66a252e9856915e1f5cddbe41723bd4b695ece2604ad3c9f75 in the local lfs store
428 428 4 changesets found
429 429 uncompressed size of bundle content:
430 430 * (changelog) (glob)
431 431 * (manifests) (glob)
432 432 * a (glob)
433 433 $ hg --config extensions.strip= strip -r 2 --no-backup --force -q
434 434 $ hg -R bundle.hg log -p -T '{rev} {desc}\n' a
435 435 5 branching
436 436 diff --git a/a b/a
437 437 --- a/a
438 438 +++ b/a
439 439 @@ -1,1 +1,1 @@
440 440 -THIS-IS-LFS-two
441 441 +THIS-IS-LFS-2-CHILD
442 442
443 443 4 a-4
444 444 diff --git a/a b/a
445 445 --- a/a
446 446 +++ b/a
447 447 @@ -1,1 +1,1 @@
448 448 -THIS-IS-LFS-three
449 449 +THIS-IS-LFS-4
450 450
451 451 3 a-three
452 452 diff --git a/a b/a
453 453 --- a/a
454 454 +++ b/a
455 455 @@ -1,1 +1,1 @@
456 456 -THIS-IS-LFS-two
457 457 +THIS-IS-LFS-three
458 458
459 459 2 a-two
460 460 diff --git a/a b/a
461 461 --- a/a
462 462 +++ b/a
463 463 @@ -1,1 +1,1 @@
464 464 -THIS-IS-LFS-single
465 465 +THIS-IS-LFS-two
466 466
467 467 1 a-single
468 468 diff --git a/a b/a
469 469 --- a/a
470 470 +++ b/a
471 471 @@ -1,1 +1,1 @@
472 472 -THIS-IS-LFS-0
473 473 +THIS-IS-LFS-single
474 474
475 475 0 a-0
476 476 diff --git a/a b/a
477 477 new file mode 100644
478 478 --- /dev/null
479 479 +++ b/a
480 480 @@ -0,0 +1,1 @@
481 481 +THIS-IS-LFS-0
482 482
483 483 $ hg bundle -R bundle.hg --base 1 bundle-again.hg -q
484 484 $ hg -R bundle-again.hg log -p -T '{rev} {desc}\n' a
485 485 5 branching
486 486 diff --git a/a b/a
487 487 --- a/a
488 488 +++ b/a
489 489 @@ -1,1 +1,1 @@
490 490 -THIS-IS-LFS-two
491 491 +THIS-IS-LFS-2-CHILD
492 492
493 493 4 a-4
494 494 diff --git a/a b/a
495 495 --- a/a
496 496 +++ b/a
497 497 @@ -1,1 +1,1 @@
498 498 -THIS-IS-LFS-three
499 499 +THIS-IS-LFS-4
500 500
501 501 3 a-three
502 502 diff --git a/a b/a
503 503 --- a/a
504 504 +++ b/a
505 505 @@ -1,1 +1,1 @@
506 506 -THIS-IS-LFS-two
507 507 +THIS-IS-LFS-three
508 508
509 509 2 a-two
510 510 diff --git a/a b/a
511 511 --- a/a
512 512 +++ b/a
513 513 @@ -1,1 +1,1 @@
514 514 -THIS-IS-LFS-single
515 515 +THIS-IS-LFS-two
516 516
517 517 1 a-single
518 518 diff --git a/a b/a
519 519 --- a/a
520 520 +++ b/a
521 521 @@ -1,1 +1,1 @@
522 522 -THIS-IS-LFS-0
523 523 +THIS-IS-LFS-single
524 524
525 525 0 a-0
526 526 diff --git a/a b/a
527 527 new file mode 100644
528 528 --- /dev/null
529 529 +++ b/a
530 530 @@ -0,0 +1,1 @@
531 531 +THIS-IS-LFS-0
532 532
533 533 $ cd ..
534 534
535 535 # Test isbinary
536 536
537 537 $ hg init repo10
538 538 $ cd repo10
539 539 $ cat >> .hg/hgrc << EOF
540 540 > [extensions]
541 541 > lfs=
542 542 > [lfs]
543 543 > track=all()
544 544 > EOF
545 545 $ "$PYTHON" <<'EOF'
546 546 > def write(path, content):
547 547 > with open(path, 'wb') as f:
548 548 > f.write(content)
549 549 > write('a', b'\0\0')
550 550 > write('b', b'\1\n')
551 551 > write('c', b'\1\n\0')
552 552 > write('d', b'xx')
553 553 > EOF
554 554 $ hg add a b c d
555 555 $ hg diff --stat
556 556 a | Bin
557 557 b | 1 +
558 558 c | Bin
559 559 d | 1 +
560 560 4 files changed, 2 insertions(+), 0 deletions(-)
561 561 $ hg commit -m binarytest
562 562 $ cat > $TESTTMP/dumpbinary.py << EOF
563 563 > from mercurial.utils import (
564 564 > stringutil,
565 565 > )
566 566 > def reposetup(ui, repo):
567 567 > for n in (b'a', b'b', b'c', b'd'):
568 568 > ui.write((b'%s: binary=%s\n')
569 569 > % (n, stringutil.pprint(repo[b'.'][n].isbinary())))
570 570 > EOF
571 571 $ hg --config extensions.dumpbinary=$TESTTMP/dumpbinary.py id --trace
572 572 a: binary=True
573 573 b: binary=False
574 574 c: binary=True
575 575 d: binary=False
576 576 b55353847f02 tip
577 577
578 578 Binary blobs don't need to be present to be skipped in filesets. (And their
579 579 absence doesn't cause an abort.)
580 580
581 581 $ rm .hg/store/lfs/objects/96/a296d224f285c67bee93c30f8a309157f0daa35dc5b87e410b78630a09cfc7
582 582 $ rm .hg/store/lfs/objects/92/f76135a4baf4faccb8586a60faf830c2bdfce147cefa188aaf4b790bd01b7e
583 583
584 584 $ hg files --debug -r . 'set:eol("unix")' --config 'experimental.lfs.disableusercache=True'
585 585 lfs: found c04b5bb1a5b2eb3e9cd4805420dba5a9d133da5b7adeeafb5474c4adae9faa80 in the local lfs store
586 586 2 b
587 587 lfs: found 5dde896887f6754c9b15bfe3a441ae4806df2fde94001311e08bf110622e0bbe in the local lfs store
588 588
589 589 $ hg files --debug -r . 'set:binary()' --config 'experimental.lfs.disableusercache=True'
590 590 2 a
591 591 3 c
592 592
593 593 $ cd ..
594 594
595 595 # Test fctx.cmp fastpath - diff without LFS blobs
596 596
597 597 $ hg init repo12
598 598 $ cd repo12
599 599 $ cat >> .hg/hgrc <<EOF
600 600 > [lfs]
601 601 > threshold=1
602 602 > EOF
603 603 $ cat > ../patch.diff <<EOF
604 604 > # HG changeset patch
605 605 > 2
606 606 >
607 607 > diff --git a/a b/a
608 608 > old mode 100644
609 609 > new mode 100755
610 610 > EOF
611 611
612 612 $ for i in 1 2 3; do
613 613 > cp ../repo10/a a
614 614 > if [ $i = 3 ]; then
615 615 > # make a content-only change
616 616 > hg import -q --bypass ../patch.diff
617 617 > hg update -q
618 618 > rm ../patch.diff
619 619 > else
620 620 > echo $i >> a
621 621 > hg commit -m $i -A a
622 622 > fi
623 623 > done
624 624 $ [ -d .hg/store/lfs/objects ]
625 625
626 626 $ cd ..
627 627
628 628 $ hg clone repo12 repo13 --noupdate
629 629 $ cd repo13
630 630 $ hg log --removed -p a -T '{desc}\n' --config diff.nobinary=1 --git
631 631 2
632 632 diff --git a/a b/a
633 633 old mode 100644
634 634 new mode 100755
635 635
636 636 2
637 637 diff --git a/a b/a
638 638 Binary file a has changed
639 639
640 640 1
641 641 diff --git a/a b/a
642 642 new file mode 100644
643 643 Binary file a has changed
644 644
645 645 $ [ -d .hg/store/lfs/objects ]
646 646 [1]
647 647
648 648 $ cd ..
649 649
650 650 # Test filter
651 651
652 652 $ hg init repo11
653 653 $ cd repo11
654 654 $ cat >> .hg/hgrc << EOF
655 655 > [lfs]
656 656 > track=(**.a & size(">5B")) | (**.b & !size(">5B"))
657 657 > | (**.c & "path:d" & !"path:d/c.c") | size(">10B")
658 658 > EOF
659 659
660 660 $ mkdir a
661 661 $ echo aaaaaa > a/1.a
662 662 $ echo a > a/2.a
663 663 $ echo aaaaaa > 1.b
664 664 $ echo a > 2.b
665 665 $ echo a > 1.c
666 666 $ mkdir d
667 667 $ echo a > d/c.c
668 668 $ echo a > d/d.c
669 669 $ echo aaaaaaaaaaaa > x
670 670 $ hg add . -q
671 671 $ hg commit -m files
672 672
673 673 $ for p in a/1.a a/2.a 1.b 2.b 1.c d/c.c d/d.c x; do
674 674 > if hg debugdata $p 0 2>&1 | grep git-lfs >/dev/null; then
675 675 > echo "${p}: is lfs"
676 676 > else
677 677 > echo "${p}: not lfs"
678 678 > fi
679 679 > done
680 680 a/1.a: is lfs
681 681 a/2.a: not lfs
682 682 1.b: not lfs
683 683 2.b: is lfs
684 684 1.c: not lfs
685 685 d/c.c: not lfs
686 686 d/d.c: is lfs
687 687 x: is lfs
688 688
689 689 $ cd ..
690 690
691 691 # Verify the repos
692 692
693 693 $ cat > $TESTTMP/dumpflog.py << EOF
694 694 > # print raw revision sizes, flags, and hashes for certain files
695 695 > import hashlib
696 696 > from mercurial.node import short
697 697 > from mercurial import (
698 698 > pycompat,
699 699 > revlog,
700 700 > )
701 701 > from mercurial.utils import (
702 702 > procutil,
703 703 > stringutil,
704 704 > )
705 705 > def hash(rawtext):
706 706 > h = hashlib.sha512()
707 707 > h.update(rawtext)
708 708 > return pycompat.sysbytes(h.hexdigest()[:4])
709 709 > def reposetup(ui, repo):
710 710 > # these 2 files are interesting
711 711 > for name in [b'l', b's']:
712 712 > fl = repo.file(name)
713 713 > if len(fl) == 0:
714 714 > continue
715 715 > sizes = [fl._revlog.rawsize(i) for i in fl]
716 716 > texts = [fl.rawdata(i) for i in fl]
717 717 > flags = [int(fl._revlog.flags(i)) for i in fl]
718 718 > hashes = [hash(t) for t in texts]
719 719 > procutil.stdout.write(b' %s: rawsizes=%r flags=%r hashes=%s\n'
720 720 > % (name, sizes, flags, stringutil.pprint(hashes)))
721 721 > EOF
722 722
723 723 $ for i in client client2 server repo3 repo4 repo5 repo6 repo7 repo8 repo9 \
724 724 > repo10; do
725 725 > echo 'repo:' $i
726 726 > hg --cwd $i verify --config extensions.dumpflog=$TESTTMP/dumpflog.py -q
727 727 > done
728 728 repo: client
729 729 repo: client2
730 730 repo: server
731 731 repo: repo3
732 732 l: rawsizes=[211, 6, 8, 141] flags=[8192, 0, 0, 8192] hashes=['d2b8', '948c', 'cc88', '724d']
733 733 s: rawsizes=[74, 141, 141, 8] flags=[0, 8192, 8192, 0] hashes=['3c80', 'fce0', '874a', '826b']
734 734 repo: repo4
735 735 l: rawsizes=[211, 6, 8, 141] flags=[8192, 0, 0, 8192] hashes=['d2b8', '948c', 'cc88', '724d']
736 736 s: rawsizes=[74, 141, 141, 8] flags=[0, 8192, 8192, 0] hashes=['3c80', 'fce0', '874a', '826b']
737 737 repo: repo5
738 738 l: rawsizes=[211, 6, 8, 141] flags=[8192, 0, 0, 8192] hashes=['d2b8', '948c', 'cc88', '724d']
739 739 s: rawsizes=[74, 141, 141, 8] flags=[0, 8192, 8192, 0] hashes=['3c80', 'fce0', '874a', '826b']
740 740 repo: repo6
741 741 repo: repo7
742 742 repo: repo8
743 743 repo: repo9
744 744 repo: repo10
745 745
746 746 repo13 doesn't have any cached lfs files and its source never pushed its
747 747 files. Therefore, the files don't exist in the remote store. Use the files in
748 748 the user cache.
749 749
750 750 $ test -d $TESTTMP/repo13/.hg/store/lfs/objects
751 751 [1]
752 752
753 753 $ hg --config extensions.share= share repo13 repo14
754 754 updating working directory
755 755 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
756 756 $ hg -R repo14 -q verify
757 757
758 758 $ hg clone repo13 repo15
759 759 updating to branch default
760 760 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
761 761 $ hg -R repo15 -q verify
762 762
763 763 If the source repo doesn't have the blob (maybe it was pulled or cloned with
764 764 --noupdate), the blob is still accessible via the global cache to send to the
765 765 remote store.
766 766
767 767 $ rm -rf $TESTTMP/repo15/.hg/store/lfs
768 768 $ hg init repo16
769 769 $ hg -R repo15 push repo16
770 770 pushing to repo16
771 771 searching for changes
772 772 adding changesets
773 773 adding manifests
774 774 adding file changes
775 775 added 3 changesets with 2 changes to 1 files
776 776 $ hg -R repo15 -q verify
777 777
778 778 Test damaged file scenarios. (This also damages the usercache because of the
779 779 hardlinks.)
780 780
781 781 $ echo 'damage' >> repo5/.hg/store/lfs/objects/66/100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
782 782
783 783 Repo with damaged lfs objects in any revision will fail verification.
784 784
785 785 $ hg -R repo5 verify
786 786 checking changesets
787 787 checking manifests
788 788 crosschecking files in changesets and manifests
789 789 checking files
790 790 l@1: unpacking 46a2f24864bc: integrity check failed on l:0
791 791 large@0: unpacking 2c531e0992ff: integrity check failed on large:0
792 not checking dirstate because of previous errors
792 793 checked 5 changesets with 10 changes to 4 files
793 794 2 integrity errors encountered!
794 795 (first damaged changeset appears to be 0)
795 796 [1]
796 797
797 798 Updates work after cloning a damaged repo, if the damaged lfs objects aren't in
798 799 the update destination. Those objects won't be added to the new repo's store
799 800 because they aren't accessed.
800 801
801 802 $ hg clone -v repo5 fromcorrupt
802 803 updating to branch default
803 804 resolving manifests
804 805 getting l
805 806 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the usercache
806 807 getting s
807 808 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
808 809 $ test -f fromcorrupt/.hg/store/lfs/objects/66/100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
809 810 [1]
810 811
811 812 Verify will not try to download lfs blobs, if told not to process lfs content.
812 813 The extension makes sure that the filelog.renamed() path is taken on a missing
813 814 blob, and the output shows that it isn't fetched.
814 815
815 816 $ cat > $TESTTMP/lfsrename.py <<EOF
816 817 > import sys
817 818 >
818 819 > from mercurial import (
819 820 > exthelper,
820 821 > pycompat,
821 822 > )
822 823 >
823 824 > from hgext.lfs import (
824 825 > pointer,
825 826 > wrapper,
826 827 > )
827 828 >
828 829 > eh = exthelper.exthelper()
829 830 > uisetup = eh.finaluisetup
830 831 >
831 832 > @eh.wrapfunction(wrapper, b'filelogrenamed')
832 833 > def filelogrenamed(orig, orig1, self, node):
833 834 > ret = orig(orig1, self, node)
834 835 > if wrapper._islfs(self._revlog, node) and ret:
835 836 > rawtext = self._revlog.rawdata(node)
836 837 > metadata = pointer.deserialize(rawtext)
837 838 > print('lfs blob %s renamed %s -> %s'
838 839 > % (pycompat.sysstr(metadata[b'oid']),
839 840 > pycompat.sysstr(ret[0]),
840 841 > pycompat.fsdecode(self._revlog.filename)))
841 842 > sys.stdout.flush()
842 843 > return ret
843 844 > EOF
844 845
845 846 $ hg -R fromcorrupt --config lfs.usercache=emptycache verify -v --no-lfs \
846 847 > --config extensions.x=$TESTTMP/lfsrename.py
847 848 repository uses revlog format 1
848 849 checking changesets
849 850 checking manifests
850 851 crosschecking files in changesets and manifests
851 852 checking files
852 853 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the local lfs store
853 854 lfs blob sha256:66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e renamed large -> l
855 checking dirstate
854 856 checked 5 changesets with 10 changes to 4 files
855 857
856 858 Verify will not try to download lfs blobs, if told not to by the config option
857 859
858 860 $ hg -R fromcorrupt --config lfs.usercache=emptycache verify -v \
859 861 > --config verify.skipflags=8192 \
860 862 > --config extensions.x=$TESTTMP/lfsrename.py
861 863 repository uses revlog format 1
862 864 checking changesets
863 865 checking manifests
864 866 crosschecking files in changesets and manifests
865 867 checking files
866 868 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the local lfs store
867 869 lfs blob sha256:66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e renamed large -> l
870 checking dirstate
868 871 checked 5 changesets with 10 changes to 4 files
869 872
870 873 Verify will copy/link all lfs objects into the local store that aren't already
871 874 present. Bypass the corrupted usercache to show that verify works when fed by
872 875 the (uncorrupted) remote store.
873 876
874 877 $ hg -R fromcorrupt --config lfs.usercache=emptycache verify -v
875 878 repository uses revlog format 1
876 879 checking changesets
877 880 checking manifests
878 881 crosschecking files in changesets and manifests
879 882 checking files
880 883 lfs: adding 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e to the usercache
881 884 lfs: found 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e in the local lfs store
882 885 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the local lfs store
883 886 lfs: found 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e in the local lfs store
884 887 lfs: adding 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 to the usercache
885 888 lfs: found 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 in the local lfs store
886 889 lfs: adding b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c to the usercache
887 890 lfs: found b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c in the local lfs store
891 checking dirstate
888 892 checked 5 changesets with 10 changes to 4 files
889 893
890 894 Verify will not copy/link a corrupted file from the usercache into the local
891 895 store, and poison it. (The verify with a good remote now works.)
892 896
893 897 $ rm -r fromcorrupt/.hg/store/lfs/objects/66/100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
894 898 $ hg -R fromcorrupt verify -v
895 899 repository uses revlog format 1
896 900 checking changesets
897 901 checking manifests
898 902 crosschecking files in changesets and manifests
899 903 checking files
900 904 l@1: unpacking 46a2f24864bc: integrity check failed on l:0
901 905 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the local lfs store
902 906 large@0: unpacking 2c531e0992ff: integrity check failed on large:0
903 907 lfs: found 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 in the local lfs store
904 908 lfs: found b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c in the local lfs store
909 not checking dirstate because of previous errors
905 910 checked 5 changesets with 10 changes to 4 files
906 911 2 integrity errors encountered!
907 912 (first damaged changeset appears to be 0)
908 913 [1]
909 914 $ hg -R fromcorrupt --config lfs.usercache=emptycache verify -v
910 915 repository uses revlog format 1
911 916 checking changesets
912 917 checking manifests
913 918 crosschecking files in changesets and manifests
914 919 checking files
915 920 lfs: found 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e in the usercache
916 921 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the local lfs store
917 922 lfs: found 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e in the local lfs store
918 923 lfs: found 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 in the local lfs store
919 924 lfs: found b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c in the local lfs store
925 checking dirstate
920 926 checked 5 changesets with 10 changes to 4 files
921 927
922 928 Damaging a file required by the update destination fails the update.
923 929
924 930 $ echo 'damage' >> $TESTTMP/dummy-remote/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
925 931 $ hg --config lfs.usercache=emptycache clone -v repo5 fromcorrupt2
926 932 updating to branch default
927 933 resolving manifests
928 934 abort: corrupt remote lfs object: 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
929 935 [255]
930 936
931 937 A corrupted lfs blob is not transferred from a file://remotestore to the
932 938 usercache or local store.
933 939
934 940 $ test -f emptycache/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
935 941 [1]
936 942 $ test -f fromcorrupt2/.hg/store/lfs/objects/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
937 943 [1]
938 944
939 945 $ hg -R fromcorrupt2 verify
940 946 checking changesets
941 947 checking manifests
942 948 crosschecking files in changesets and manifests
943 949 checking files
944 950 l@1: unpacking 46a2f24864bc: integrity check failed on l:0
945 951 large@0: unpacking 2c531e0992ff: integrity check failed on large:0
952 not checking dirstate because of previous errors
946 953 checked 5 changesets with 10 changes to 4 files
947 954 2 integrity errors encountered!
948 955 (first damaged changeset appears to be 0)
949 956 [1]
950 957
951 958 Corrupt local files are not sent upstream. (The alternate dummy remote
952 959 avoids the corrupt lfs object in the original remote.)
953 960
954 961 $ mkdir $TESTTMP/dummy-remote2
955 962 $ hg init dest
956 963 $ hg -R fromcorrupt2 --config lfs.url=file:///$TESTTMP/dummy-remote2 push -v dest
957 964 pushing to dest
958 965 searching for changes
959 966 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the local lfs store
960 967 abort: detected corrupt lfs object: 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
961 968 (run hg verify)
962 969 [255]
963 970
964 971 $ hg -R fromcorrupt2 --config lfs.url=file:///$TESTTMP/dummy-remote2 verify -v
965 972 repository uses revlog format 1
966 973 checking changesets
967 974 checking manifests
968 975 crosschecking files in changesets and manifests
969 976 checking files
970 977 l@1: unpacking 46a2f24864bc: integrity check failed on l:0
971 978 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the local lfs store
972 979 large@0: unpacking 2c531e0992ff: integrity check failed on large:0
973 980 lfs: found 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 in the local lfs store
974 981 lfs: found b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c in the local lfs store
982 not checking dirstate because of previous errors
975 983 checked 5 changesets with 10 changes to 4 files
976 984 2 integrity errors encountered!
977 985 (first damaged changeset appears to be 0)
978 986 [1]
979 987
980 988 $ cat $TESTTMP/dummy-remote2/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b | $TESTDIR/f --sha256
981 989 sha256=22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
982 990 $ cat fromcorrupt2/.hg/store/lfs/objects/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b | $TESTDIR/f --sha256
983 991 sha256=22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
984 992 $ test -f $TESTTMP/dummy-remote2/66/100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
985 993 [1]
986 994
987 995 Accessing a corrupt file will complain
988 996
989 997 $ hg --cwd fromcorrupt2 cat -r 0 large
990 998 abort: integrity check failed on large:0
991 999 [50]
992 1000
993 1001 lfs -> normal -> lfs round trip conversions are possible. The 'none()'
994 1002 predicate on the command line will override whatever is configured globally and
995 1003 locally, and ensures everything converts to a regular file. For lfs -> normal,
996 1004 there's no 'lfs' destination repo requirement. For normal -> lfs, there is.
997 1005
998 1006 $ hg --config extensions.convert= --config 'lfs.track=none()' \
999 1007 > convert repo8 convert_normal
1000 1008 initializing destination convert_normal repository
1001 1009 scanning source...
1002 1010 sorting...
1003 1011 converting...
1004 1012 2 a
1005 1013 1 b
1006 1014 0 meta
1007 1015 $ hg debugrequires -R convert_normal | grep 'lfs'
1008 1016 [1]
1009 1017 $ hg --cwd convert_normal cat a1 -r 0 -T '{rawdata}'
1010 1018 THIS-IS-LFS-BECAUSE-10-BYTES
1011 1019
1012 1020 $ hg --config extensions.convert= --config lfs.threshold=10B \
1013 1021 > convert convert_normal convert_lfs
1014 1022 initializing destination convert_lfs repository
1015 1023 scanning source...
1016 1024 sorting...
1017 1025 converting...
1018 1026 2 a
1019 1027 1 b
1020 1028 0 meta
1021 1029
1022 1030 $ hg --cwd convert_lfs cat -r 0 a1 -T '{rawdata}'
1023 1031 version https://git-lfs.github.com/spec/v1
1024 1032 oid sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
1025 1033 size 29
1026 1034 x-is-binary 0
1027 1035 $ hg --cwd convert_lfs debugdata a1 0
1028 1036 version https://git-lfs.github.com/spec/v1
1029 1037 oid sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
1030 1038 size 29
1031 1039 x-is-binary 0
1032 1040 $ hg --cwd convert_lfs log -r 0 -T "{lfs_files % '{lfspointer % '{key}={value}\n'}'}"
1033 1041 version=https://git-lfs.github.com/spec/v1
1034 1042 oid=sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
1035 1043 size=29
1036 1044 x-is-binary=0
1037 1045 $ hg --cwd convert_lfs log -r 0 \
1038 1046 > -T '{lfs_files % "{get(lfspointer, "oid")}\n"}{lfs_files % "{lfspointer.oid}\n"}'
1039 1047 sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
1040 1048 sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
1041 1049 $ hg --cwd convert_lfs log -r 0 -T '{lfs_files % "{lfspointer}\n"}'
1042 1050 version=https://git-lfs.github.com/spec/v1 oid=sha256:5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024 size=29 x-is-binary=0
1043 1051 $ hg --cwd convert_lfs \
1044 1052 > log -r 'all()' -T '{rev}: {lfs_files % "{file}: {lfsoid}\n"}'
1045 1053 0: a1: 5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
1046 1054 1: a2: 5bb8341bee63b3649f222b2215bde37322bea075a30575aa685d8f8d21c77024
1047 1055 2: a2: 876dadc86a8542f9798048f2c47f51dbf8e4359aed883e8ec80c5db825f0d943
1048 1056
1049 1057 $ hg debugrequires -R convert_lfs | grep 'lfs'
1050 1058 lfs
1051 1059
1052 1060 The hashes in all stages of the conversion are unchanged.
1053 1061
1054 1062 $ hg -R repo8 log -T '{node|short}\n'
1055 1063 0fae949de7fa
1056 1064 9cd6bdffdac0
1057 1065 7f96794915f7
1058 1066 $ hg -R convert_normal log -T '{node|short}\n'
1059 1067 0fae949de7fa
1060 1068 9cd6bdffdac0
1061 1069 7f96794915f7
1062 1070 $ hg -R convert_lfs log -T '{node|short}\n'
1063 1071 0fae949de7fa
1064 1072 9cd6bdffdac0
1065 1073 7f96794915f7
1066 1074
1067 1075 This convert is trickier, because it contains deleted files (via `hg mv`)
1068 1076
1069 1077 $ hg --config extensions.convert= --config lfs.threshold=1000M \
1070 1078 > convert repo3 convert_normal2
1071 1079 initializing destination convert_normal2 repository
1072 1080 scanning source...
1073 1081 sorting...
1074 1082 converting...
1075 1083 4 commit with lfs content
1076 1084 3 renames
1077 1085 2 large to small, small to large
1078 1086 1 random modifications
1079 1087 0 switch large and small again
1080 1088 $ hg debugrequires -R convert_normal2 | grep 'lfs'
1081 1089 [1]
1082 1090 $ hg --cwd convert_normal2 debugdata large 0
1083 1091 LONGER-THAN-TEN-BYTES-WILL-TRIGGER-LFS
1084 1092
1085 1093 $ hg --config extensions.convert= --config lfs.threshold=10B \
1086 1094 > convert convert_normal2 convert_lfs2
1087 1095 initializing destination convert_lfs2 repository
1088 1096 scanning source...
1089 1097 sorting...
1090 1098 converting...
1091 1099 4 commit with lfs content
1092 1100 3 renames
1093 1101 2 large to small, small to large
1094 1102 1 random modifications
1095 1103 0 switch large and small again
1096 1104 $ hg debugrequires -R convert_lfs2 | grep 'lfs'
1097 1105 lfs
1098 1106 $ hg --cwd convert_lfs2 debugdata large 0
1099 1107 version https://git-lfs.github.com/spec/v1
1100 1108 oid sha256:66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
1101 1109 size 39
1102 1110 x-is-binary 0
1103 1111
1104 1112 Committing deleted files works:
1105 1113
1106 1114 $ hg init $TESTTMP/repo-del
1107 1115 $ cd $TESTTMP/repo-del
1108 1116 $ echo 1 > A
1109 1117 $ hg commit -m 'add A' -A A
1110 1118 $ hg rm A
1111 1119 $ hg commit -m 'rm A'
1112 1120
1113 1121 Bad .hglfs files will block the commit with a useful message
1114 1122
1115 1123 $ cat > .hglfs << EOF
1116 1124 > [track]
1117 1125 > **.test = size(">5B")
1118 1126 > bad file ... no commit
1119 1127 > EOF
1120 1128
1121 1129 $ echo x > file.txt
1122 1130 $ hg ci -Aqm 'should fail'
1123 1131 config error at .hglfs:3: bad file ... no commit
1124 1132 [30]
1125 1133
1126 1134 $ cat > .hglfs << EOF
1127 1135 > [track]
1128 1136 > **.test = size(">5B")
1129 1137 > ** = nonexistent()
1130 1138 > EOF
1131 1139
1132 1140 $ hg ci -Aqm 'should fail'
1133 1141 abort: parse error in .hglfs: unknown identifier: nonexistent
1134 1142 [255]
1135 1143
1136 1144 '**' works out to mean all files.
1137 1145
1138 1146 $ cat > .hglfs << EOF
1139 1147 > [track]
1140 1148 > path:.hglfs = none()
1141 1149 > **.test = size(">5B")
1142 1150 > **.exclude = none()
1143 1151 > ** = size(">10B")
1144 1152 > EOF
1145 1153
1146 1154 The LFS policy takes effect without tracking the .hglfs file
1147 1155
1148 1156 $ echo 'largefile' > lfs.test
1149 1157 $ echo '012345678901234567890' > nolfs.exclude
1150 1158 $ echo '01234567890123456' > lfs.catchall
1151 1159 $ hg add *
1152 1160 $ hg ci -qm 'before add .hglfs'
1153 1161 $ hg log -r . -T '{rev}: {lfs_files % "{file}: {lfsoid}\n"}\n'
1154 1162 2: lfs.catchall: d4ec46c2869ba22eceb42a729377432052d9dd75d82fc40390ebaadecee87ee9
1155 1163 lfs.test: 5489e6ced8c36a7b267292bde9fd5242a5f80a7482e8f23fa0477393dfaa4d6c
1156 1164
1157 1165 The .hglfs file works when tracked
1158 1166
1159 1167 $ echo 'largefile2' > lfs.test
1160 1168 $ echo '012345678901234567890a' > nolfs.exclude
1161 1169 $ echo '01234567890123456a' > lfs.catchall
1162 1170 $ hg ci -Aqm 'after adding .hglfs'
1163 1171 $ hg log -r . -T '{rev}: {lfs_files % "{file}: {lfsoid}\n"}\n'
1164 1172 3: lfs.catchall: 31f43b9c62b540126b0ad5884dc013d21a61c9329b77de1fceeae2fc58511573
1165 1173 lfs.test: 8acd23467967bc7b8cc5a280056589b0ba0b17ff21dbd88a7b6474d6290378a6
1166 1174
1167 1175 The LFS policy stops when the .hglfs is gone
1168 1176
1169 1177 $ mv .hglfs .hglfs_
1170 1178 $ echo 'largefile3' > lfs.test
1171 1179 $ echo '012345678901234567890abc' > nolfs.exclude
1172 1180 $ echo '01234567890123456abc' > lfs.catchall
1173 1181 $ hg ci -qm 'file test' -X .hglfs
1174 1182 $ hg log -r . -T '{rev}: {lfs_files % "{file}: {lfsoid}\n"}\n'
1175 1183 4:
1176 1184
1177 1185 $ mv .hglfs_ .hglfs
1178 1186 $ echo '012345678901234567890abc' > lfs.test
1179 1187 $ hg ci -m 'back to lfs'
1180 1188 $ hg rm lfs.test
1181 1189 $ hg ci -qm 'remove lfs'
1182 1190
1183 1191 {lfs_files} will list deleted files too
1184 1192
1185 1193 $ hg log -T "{lfs_files % '{rev} {file}: {lfspointer.oid}\n'}"
1186 1194 6 lfs.test:
1187 1195 5 lfs.test: sha256:43f8f41171b6f62a6b61ba4ce98a8a6c1649240a47ebafd43120aa215ac9e7f6
1188 1196 3 lfs.catchall: sha256:31f43b9c62b540126b0ad5884dc013d21a61c9329b77de1fceeae2fc58511573
1189 1197 3 lfs.test: sha256:8acd23467967bc7b8cc5a280056589b0ba0b17ff21dbd88a7b6474d6290378a6
1190 1198 2 lfs.catchall: sha256:d4ec46c2869ba22eceb42a729377432052d9dd75d82fc40390ebaadecee87ee9
1191 1199 2 lfs.test: sha256:5489e6ced8c36a7b267292bde9fd5242a5f80a7482e8f23fa0477393dfaa4d6c
1192 1200
1193 1201 $ hg log -r 'file("set:lfs()")' -T '{rev} {join(lfs_files, ", ")}\n'
1194 1202 2 lfs.catchall, lfs.test
1195 1203 3 lfs.catchall, lfs.test
1196 1204 5 lfs.test
1197 1205 6 lfs.test
1198 1206
1199 1207 $ cd ..
1200 1208
1201 1209 Unbundling adds a requirement to a non-lfs repo, if necessary.
1202 1210
1203 1211 $ hg bundle -R $TESTTMP/repo-del -qr 0 --base null nolfs.hg
1204 1212 $ hg bundle -R convert_lfs2 -qr tip --base null lfs.hg
1205 1213 $ hg init unbundle
1206 1214 $ hg pull -R unbundle -q nolfs.hg
1207 1215 $ hg debugrequires -R unbundle | grep lfs
1208 1216 [1]
1209 1217 $ hg pull -R unbundle -q lfs.hg
1210 1218 $ hg debugrequires -R unbundle | grep lfs
1211 1219 lfs
1212 1220
1213 1221 $ hg init no_lfs
1214 1222 $ cat >> no_lfs/.hg/hgrc <<EOF
1215 1223 > [experimental]
1216 1224 > changegroup3 = True
1217 1225 > [extensions]
1218 1226 > lfs=!
1219 1227 > EOF
1220 1228 $ cp -R no_lfs no_lfs2
1221 1229
1222 1230 Pushing from a local lfs repo to a local repo without an lfs requirement and
1223 1231 with lfs disabled, fails.
1224 1232
1225 1233 $ hg push -R convert_lfs2 no_lfs
1226 1234 pushing to no_lfs
1227 1235 abort: required features are not supported in the destination: lfs
1228 1236 [255]
1229 1237 $ hg debugrequires -R no_lfs/ | grep lfs
1230 1238 [1]
1231 1239
1232 1240 Pulling from a local lfs repo to a local repo without an lfs requirement and
1233 1241 with lfs disabled, fails.
1234 1242
1235 1243 $ hg pull -R no_lfs2 convert_lfs2
1236 1244 pulling from convert_lfs2
1237 1245 abort: required features are not supported in the destination: lfs
1238 1246 [255]
1239 1247 $ hg debugrequires -R no_lfs2/ | grep lfs
1240 1248 [1]
@@ -1,104 +1,105 b''
1 1 #testcases tree flat-fncache flat-nofncache
2 2
3 3 Tests narrow stream clones
4 4
5 5 $ . "$TESTDIR/narrow-library.sh"
6 6
7 7 #if tree
8 8 $ cat << EOF >> $HGRCPATH
9 9 > [experimental]
10 10 > treemanifest = 1
11 11 > EOF
12 12 #endif
13 13
14 14 #if flat-nofncache
15 15 $ cat << EOF >> $HGRCPATH
16 16 > [format]
17 17 > usefncache = 0
18 18 > EOF
19 19 #endif
20 20
21 21 Server setup
22 22
23 23 $ hg init master
24 24 $ cd master
25 25 $ mkdir dir
26 26 $ mkdir dir/src
27 27 $ cd dir/src
28 28 $ for x in `$TESTDIR/seq.py 20`; do echo $x > "F$x"; hg add "F$x"; hg commit -m "Commit src $x"; done
29 29
30 30 $ cd ..
31 31 $ mkdir tests
32 32 $ cd tests
33 33 $ for x in `$TESTDIR/seq.py 20`; do echo $x > "F$x"; hg add "F$x"; hg commit -m "Commit src $x"; done
34 34 $ cd ../../..
35 35
36 36 Trying to stream clone when the server does not support it
37 37
38 38 $ hg clone --narrow ssh://user@dummy/master narrow --noupdate --include "dir/src/F10" --stream
39 39 streaming all changes
40 40 remote: abort: server does not support narrow stream clones
41 41 abort: pull failed on remote
42 42 [100]
43 43
44 44 Enable stream clone on the server
45 45
46 46 $ echo "[experimental]" >> master/.hg/hgrc
47 47 $ echo "server.stream-narrow-clones=True" >> master/.hg/hgrc
48 48
49 49 Cloning a specific file when stream clone is supported
50 50
51 51 $ hg clone --narrow ssh://user@dummy/master narrow --noupdate --include "dir/src/F10" --stream
52 52 streaming all changes
53 53 * files to transfer, * KB of data (glob)
54 54 transferred * KB in * seconds (* */sec) (glob)
55 55
56 56 $ cd narrow
57 57 $ ls -A
58 58 .hg
59 59 $ hg tracked
60 60 I path:dir/src/F10
61 61
62 62 Making sure we have the correct set of requirements
63 63
64 64 $ hg debugrequires
65 65 dotencode (tree !)
66 66 dotencode (flat-fncache !)
67 67 dirstate-v2 (dirstate-v2 !)
68 68 fncache (tree !)
69 69 fncache (flat-fncache !)
70 70 generaldelta
71 71 narrowhg-experimental
72 72 persistent-nodemap (rust !)
73 73 revlog-compression-zstd (zstd !)
74 74 revlogv1
75 75 share-safe
76 76 sparserevlog
77 77 store
78 78 treemanifest (tree !)
79 79
80 80 Making sure store has the required files
81 81
82 82 $ ls .hg/store/
83 83 00changelog.i
84 84 00manifest.i
85 85 data
86 86 fncache (tree !)
87 87 fncache (flat-fncache !)
88 88 meta (tree !)
89 89 narrowspec
90 90 requires
91 91 undo
92 92 undo.backupfiles
93 93 undo.narrowspec
94 94 undo.phaseroots
95 95
96 96 Checking that repository has all the required data and not broken
97 97
98 98 $ hg verify
99 99 checking changesets
100 100 checking manifests
101 101 checking directory manifests (tree !)
102 102 crosschecking files in changesets and manifests
103 103 checking files
104 checking dirstate
104 105 checked 40 changesets with 1 changes to 1 files
@@ -1,342 +1,344 b''
1 1 ==================================
2 2 Basic testing for the push command
3 3 ==================================
4 4
5 5 Testing of the '--rev' flag
6 6 ===========================
7 7
8 8 $ hg init test-revflag
9 9 $ hg -R test-revflag unbundle "$TESTDIR/bundles/remote.hg"
10 10 adding changesets
11 11 adding manifests
12 12 adding file changes
13 13 added 9 changesets with 7 changes to 4 files (+1 heads)
14 14 new changesets bfaf4b5cbf01:916f1afdef90 (9 drafts)
15 15 (run 'hg heads' to see heads, 'hg merge' to merge)
16 16
17 17 $ for i in 0 1 2 3 4 5 6 7 8; do
18 18 > echo
19 19 > hg init test-revflag-"$i"
20 20 > hg -R test-revflag push -r "$i" test-revflag-"$i"
21 21 > hg -R test-revflag-"$i" verify -q
22 22 > done
23 23
24 24 pushing to test-revflag-0
25 25 searching for changes
26 26 adding changesets
27 27 adding manifests
28 28 adding file changes
29 29 added 1 changesets with 1 changes to 1 files
30 30
31 31 pushing to test-revflag-1
32 32 searching for changes
33 33 adding changesets
34 34 adding manifests
35 35 adding file changes
36 36 added 2 changesets with 2 changes to 1 files
37 37
38 38 pushing to test-revflag-2
39 39 searching for changes
40 40 adding changesets
41 41 adding manifests
42 42 adding file changes
43 43 added 3 changesets with 3 changes to 1 files
44 44
45 45 pushing to test-revflag-3
46 46 searching for changes
47 47 adding changesets
48 48 adding manifests
49 49 adding file changes
50 50 added 4 changesets with 4 changes to 1 files
51 51
52 52 pushing to test-revflag-4
53 53 searching for changes
54 54 adding changesets
55 55 adding manifests
56 56 adding file changes
57 57 added 2 changesets with 2 changes to 1 files
58 58
59 59 pushing to test-revflag-5
60 60 searching for changes
61 61 adding changesets
62 62 adding manifests
63 63 adding file changes
64 64 added 3 changesets with 3 changes to 1 files
65 65
66 66 pushing to test-revflag-6
67 67 searching for changes
68 68 adding changesets
69 69 adding manifests
70 70 adding file changes
71 71 added 4 changesets with 5 changes to 2 files
72 72
73 73 pushing to test-revflag-7
74 74 searching for changes
75 75 adding changesets
76 76 adding manifests
77 77 adding file changes
78 78 added 5 changesets with 6 changes to 3 files
79 79
80 80 pushing to test-revflag-8
81 81 searching for changes
82 82 adding changesets
83 83 adding manifests
84 84 adding file changes
85 85 added 5 changesets with 5 changes to 2 files
86 86
87 87 $ cd test-revflag-8
88 88
89 89 $ hg pull ../test-revflag-7
90 90 pulling from ../test-revflag-7
91 91 searching for changes
92 92 adding changesets
93 93 adding manifests
94 94 adding file changes
95 95 added 4 changesets with 2 changes to 3 files (+1 heads)
96 96 new changesets c70afb1ee985:faa2e4234c7a
97 97 (run 'hg heads' to see heads, 'hg merge' to merge)
98 98
99 99 $ hg verify -q
100 100
101 101 $ cd ..
102 102
103 103 Test server side validation during push
104 104 =======================================
105 105
106 106 $ hg init test-validation
107 107 $ cd test-validation
108 108
109 109 $ cat > .hg/hgrc <<EOF
110 110 > [server]
111 111 > validate=1
112 112 > EOF
113 113
114 114 $ echo alpha > alpha
115 115 $ echo beta > beta
116 116 $ hg addr
117 117 adding alpha
118 118 adding beta
119 119 $ hg ci -m 1
120 120
121 121 $ cd ..
122 122 $ hg clone test-validation test-validation-clone
123 123 updating to branch default
124 124 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
125 125
126 126 #if reporevlogstore
127 127
128 128 Test spurious filelog entries:
129 129
130 130 $ cd test-validation-clone
131 131 $ echo blah >> beta
132 132 $ cp .hg/store/data/beta.i tmp1
133 133 $ hg ci -m 2
134 134 $ cp .hg/store/data/beta.i tmp2
135 135 $ hg -q rollback
136 136 $ mv tmp2 .hg/store/data/beta.i
137 137 $ echo blah >> beta
138 138 $ hg ci -m '2 (corrupt)'
139 139
140 140 Expected to fail:
141 141
142 142 $ hg verify -q
143 143 beta@1: dddc47b3ba30 not in manifests
144 not checking dirstate because of previous errors
144 145 1 integrity errors encountered!
145 146 (first damaged changeset appears to be 1)
146 147 [1]
147 148
148 149 $ hg push
149 150 pushing to $TESTTMP/test-validation
150 151 searching for changes
151 152 adding changesets
152 153 adding manifests
153 154 adding file changes
154 155 transaction abort!
155 156 rollback completed
156 157 abort: received spurious file revlog entry
157 158 [255]
158 159
159 160 $ hg -q rollback
160 161 $ mv tmp1 .hg/store/data/beta.i
161 162 $ echo beta > beta
162 163
163 164 Test missing filelog entries:
164 165
165 166 $ cp .hg/store/data/beta.i tmp
166 167 $ echo blah >> beta
167 168 $ hg ci -m '2 (corrupt)'
168 169 $ mv tmp .hg/store/data/beta.i
169 170
170 171 Expected to fail:
171 172
172 173 $ hg verify -q
173 174 beta@1: manifest refers to unknown revision dddc47b3ba30
175 not checking dirstate because of previous errors
174 176 1 integrity errors encountered!
175 177 (first damaged changeset appears to be 1)
176 178 [1]
177 179
178 180 $ hg push
179 181 pushing to $TESTTMP/test-validation
180 182 searching for changes
181 183 adding changesets
182 184 adding manifests
183 185 adding file changes
184 186 transaction abort!
185 187 rollback completed
186 188 abort: missing file data for beta:dddc47b3ba30e54484720ce0f4f768a0f4b6efb9 - run hg verify
187 189 [255]
188 190
189 191 $ cd ..
190 192
191 193 #endif
192 194
193 195 Test push hook locking
194 196 =====================
195 197
196 198 $ hg init 1
197 199
198 200 $ echo '[ui]' >> 1/.hg/hgrc
199 201 $ echo 'timeout = 10' >> 1/.hg/hgrc
200 202
201 203 $ echo foo > 1/foo
202 204 $ hg --cwd 1 ci -A -m foo
203 205 adding foo
204 206
205 207 $ hg clone 1 2
206 208 updating to branch default
207 209 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
208 210
209 211 $ hg clone 2 3
210 212 updating to branch default
211 213 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
212 214
213 215 $ cat <<EOF > $TESTTMP/debuglocks-pretxn-hook.sh
214 216 > hg debuglocks
215 217 > true
216 218 > EOF
217 219 $ echo '[hooks]' >> 2/.hg/hgrc
218 220 $ echo "pretxnchangegroup.a = sh $TESTTMP/debuglocks-pretxn-hook.sh" >> 2/.hg/hgrc
219 221 $ echo 'changegroup.push = hg push -qf ../1' >> 2/.hg/hgrc
220 222
221 223 $ echo bar >> 3/foo
222 224 $ hg --cwd 3 ci -m bar
223 225
224 226 $ hg --cwd 3 push ../2 --config devel.legacy.exchange=bundle1
225 227 pushing to ../2
226 228 searching for changes
227 229 adding changesets
228 230 adding manifests
229 231 adding file changes
230 232 lock: user *, process * (*s) (glob)
231 233 wlock: free
232 234 added 1 changesets with 1 changes to 1 files
233 235
234 236 $ hg --cwd 1 --config extensions.strip= strip tip -q
235 237 $ hg --cwd 2 --config extensions.strip= strip tip -q
236 238 $ hg --cwd 3 push ../2 # bundle2+
237 239 pushing to ../2
238 240 searching for changes
239 241 adding changesets
240 242 adding manifests
241 243 adding file changes
242 244 lock: user *, process * (*s) (glob)
243 245 wlock: user *, process * (*s) (glob)
244 246 added 1 changesets with 1 changes to 1 files
245 247
246 248 Test bare push with multiple race checking options
247 249 --------------------------------------------------
248 250
249 251 $ hg init test-bare-push-no-concurrency
250 252 $ hg init test-bare-push-unrelated-concurrency
251 253 $ hg -R test-revflag push -r 0 test-bare-push-no-concurrency --config server.concurrent-push-mode=strict
252 254 pushing to test-bare-push-no-concurrency
253 255 searching for changes
254 256 adding changesets
255 257 adding manifests
256 258 adding file changes
257 259 added 1 changesets with 1 changes to 1 files
258 260 $ hg -R test-revflag push -r 0 test-bare-push-unrelated-concurrency --config server.concurrent-push-mode=check-related
259 261 pushing to test-bare-push-unrelated-concurrency
260 262 searching for changes
261 263 adding changesets
262 264 adding manifests
263 265 adding file changes
264 266 added 1 changesets with 1 changes to 1 files
265 267
266 268 SEC: check for unsafe ssh url
267 269
268 270 $ cat >> $HGRCPATH << EOF
269 271 > [ui]
270 272 > ssh = sh -c "read l; read l; read l"
271 273 > EOF
272 274
273 275 $ hg -R test-revflag push 'ssh://-oProxyCommand=touch${IFS}owned/path'
274 276 pushing to ssh://-oProxyCommand%3Dtouch%24%7BIFS%7Downed/path
275 277 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
276 278 [255]
277 279 $ hg -R test-revflag push 'ssh://%2DoProxyCommand=touch${IFS}owned/path'
278 280 pushing to ssh://-oProxyCommand%3Dtouch%24%7BIFS%7Downed/path
279 281 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
280 282 [255]
281 283 $ hg -R test-revflag push 'ssh://fakehost|touch${IFS}owned/path'
282 284 pushing to ssh://fakehost%7Ctouch%24%7BIFS%7Downed/path
283 285 abort: no suitable response from remote hg
284 286 [255]
285 287 $ hg -R test-revflag push 'ssh://fakehost%7Ctouch%20owned/path'
286 288 pushing to ssh://fakehost%7Ctouch%20owned/path
287 289 abort: no suitable response from remote hg
288 290 [255]
289 291
290 292 $ [ ! -f owned ] || echo 'you got owned'
291 293
292 294 Test `commands.push.require-revs`
293 295 ---------------------------------
294 296
295 297 $ hg clone -q test-revflag test-require-revs-source
296 298 $ hg init test-require-revs-dest
297 299 $ cd test-require-revs-source
298 300 $ cat >> .hg/hgrc << EOF
299 301 > [paths]
300 302 > default = ../test-require-revs-dest
301 303 > [commands]
302 304 > push.require-revs=1
303 305 > EOF
304 306 $ hg push
305 307 pushing to $TESTTMP/test-require-revs-dest
306 308 abort: no revisions specified to push
307 309 (did you mean "hg push -r ."?)
308 310 [10]
309 311 $ hg push -r 0
310 312 pushing to $TESTTMP/test-require-revs-dest
311 313 searching for changes
312 314 adding changesets
313 315 adding manifests
314 316 adding file changes
315 317 added 1 changesets with 1 changes to 1 files
316 318 $ hg bookmark -r 0 push-this-bookmark
317 319 (test that -B (bookmark) works for specifying "revs")
318 320 $ hg push -B push-this-bookmark
319 321 pushing to $TESTTMP/test-require-revs-dest
320 322 searching for changes
321 323 no changes found
322 324 exporting bookmark push-this-bookmark
323 325 [1]
324 326 (test that -b (branch) works for specifying "revs")
325 327 $ hg push -b default
326 328 pushing to $TESTTMP/test-require-revs-dest
327 329 searching for changes
328 330 abort: push creates new remote head [0-9a-f]+ (re)
329 331 (merge or see 'hg help push' for details about pushing new heads)
330 332 [20]
331 333 (demonstrate that even though we don't have anything to exchange, we're still
332 334 showing the error)
333 335 $ hg push
334 336 pushing to $TESTTMP/test-require-revs-dest
335 337 abort: no revisions specified to push
336 338 (did you mean "hg push -r ."?)
337 339 [10]
338 340 $ hg push --config paths.default:pushrev=0
339 341 pushing to $TESTTMP/test-require-revs-dest
340 342 searching for changes
341 343 no changes found
342 344 [1]
@@ -1,143 +1,148 b''
1 1 #require unix-permissions no-root reporevlogstore
2 2
3 3 $ cat > $TESTTMP/dumpjournal.py <<EOF
4 4 > import sys
5 5 > for entry in sys.stdin.read().split('\n'):
6 6 > if entry:
7 7 > print(entry.split('\x00')[0])
8 8 > EOF
9 9
10 10 $ echo "[extensions]" >> $HGRCPATH
11 11 $ echo "mq=">> $HGRCPATH
12 12
13 13 $ teststrip() {
14 14 > hg -q up -C $1
15 15 > echo % before update $1, strip $2
16 16 > hg parents
17 17 > chmod -$3 $4
18 18 > hg strip $2 2>&1 | sed 's/\(bundle\).*/\1/' | sed 's/Permission denied.*\.hg\/store\/\(.*\)/Permission denied \.hg\/store\/\1/'
19 19 > echo % after update $1, strip $2
20 20 > chmod +$3 $4
21 21 > hg verify
22 22 > echo % journal contents
23 23 > if [ -f .hg/store/journal ]; then
24 24 > cat .hg/store/journal | "$PYTHON" $TESTTMP/dumpjournal.py
25 25 > else
26 26 > echo "(no journal)"
27 27 > fi
28 28 > if ls .hg/store/journal >/dev/null 2>&1; then
29 29 > hg recover --verify
30 30 > fi
31 31 > ls .hg/strip-backup/* >/dev/null 2>&1 && hg unbundle -q .hg/strip-backup/*
32 32 > rm -rf .hg/strip-backup
33 33 > }
34 34
35 35 $ hg init test
36 36 $ cd test
37 37 $ echo a > a
38 38 $ hg -q ci -m "a" -A
39 39 $ echo b > b
40 40 $ hg -q ci -m "b" -A
41 41 $ echo b2 >> b
42 42 $ hg -q ci -m "b2" -A
43 43 $ echo c > c
44 44 $ hg -q ci -m "c" -A
45 45 $ teststrip 0 2 w .hg/store/data/b.i
46 46 % before update 0, strip 2
47 47 changeset: 0:cb9a9f314b8b
48 48 user: test
49 49 date: Thu Jan 01 00:00:00 1970 +0000
50 50 summary: a
51 51
52 52 saved backup bundle
53 53 transaction abort!
54 54 failed to truncate data/b.i
55 55 rollback failed - please run hg recover
56 56 (failure reason: [Errno *] Permission denied .hg/store/data/b.i') (glob)
57 57 strip failed, backup bundle
58 58 abort: Permission denied .hg/store/data/b.i'
59 59 % after update 0, strip 2
60 60 abandoned transaction found - run hg recover
61 61 checking changesets
62 62 checking manifests
63 63 crosschecking files in changesets and manifests
64 64 checking files
65 65 b@?: rev 1 points to nonexistent changeset 2
66 66 (expected 1)
67 67 b@?: 736c29771fba not in manifests
68 68 warning: orphan data file 'data/c.i'
69 not checking dirstate because of previous errors
69 70 checked 2 changesets with 3 changes to 2 files
70 71 2 warnings encountered!
71 72 2 integrity errors encountered!
72 73 % journal contents
73 74 00changelog.i
74 75 00manifest.i
75 76 data/b.i
76 77 data/c.i
77 78 rolling back interrupted transaction
78 79 checking changesets
79 80 checking manifests
80 81 crosschecking files in changesets and manifests
81 82 checking files
83 checking dirstate
82 84 checked 2 changesets with 2 changes to 2 files
83 85 $ teststrip 0 2 r .hg/store/data/b.i
84 86 % before update 0, strip 2
85 87 changeset: 0:cb9a9f314b8b
86 88 user: test
87 89 date: Thu Jan 01 00:00:00 1970 +0000
88 90 summary: a
89 91
90 92 abort: Permission denied .hg/store/data/b.i'
91 93 % after update 0, strip 2
92 94 checking changesets
93 95 checking manifests
94 96 crosschecking files in changesets and manifests
95 97 checking files
98 checking dirstate
96 99 checked 4 changesets with 4 changes to 3 files
97 100 % journal contents
98 101 (no journal)
99 102 $ teststrip 0 2 w .hg/store/00manifest.i
100 103 % before update 0, strip 2
101 104 changeset: 0:cb9a9f314b8b
102 105 user: test
103 106 date: Thu Jan 01 00:00:00 1970 +0000
104 107 summary: a
105 108
106 109 saved backup bundle
107 110 transaction abort!
108 111 failed to truncate 00manifest.i
109 112 rollback failed - please run hg recover
110 113 (failure reason: [Errno *] Permission denied .hg/store/00manifest.i') (glob)
111 114 strip failed, backup bundle
112 115 abort: Permission denied .hg/store/00manifest.i'
113 116 % after update 0, strip 2
114 117 abandoned transaction found - run hg recover
115 118 checking changesets
116 119 checking manifests
117 120 manifest@?: rev 2 points to nonexistent changeset 2
118 121 manifest@?: 3362547cdf64 not in changesets
119 122 manifest@?: rev 3 points to nonexistent changeset 3
120 123 manifest@?: 265a85892ecb not in changesets
121 124 crosschecking files in changesets and manifests
122 125 c@3: in manifest but not in changeset
123 126 checking files
124 127 b@?: rev 1 points to nonexistent changeset 2
125 128 (expected 1)
126 129 c@?: rev 0 points to nonexistent changeset 3
130 not checking dirstate because of previous errors
127 131 checked 2 changesets with 4 changes to 3 files
128 132 1 warnings encountered!
129 133 7 integrity errors encountered!
130 134 (first damaged changeset appears to be 3)
131 135 % journal contents
132 136 00changelog.i
133 137 00manifest.i
134 138 data/b.i
135 139 data/c.i
136 140 rolling back interrupted transaction
137 141 checking changesets
138 142 checking manifests
139 143 crosschecking files in changesets and manifests
140 144 checking files
145 checking dirstate
141 146 checked 2 changesets with 2 changes to 2 files
142 147
143 148 $ cd ..
@@ -1,842 +1,844 b''
1 1 Set up repo
2 2
3 3 $ hg --config experimental.treemanifest=True init repo
4 4 $ cd repo
5 5
6 6 Requirements get set on init
7 7
8 8 $ hg debugrequires | grep treemanifest
9 9 treemanifest
10 10
11 11 Without directories, looks like any other repo
12 12
13 13 $ echo 0 > a
14 14 $ echo 0 > b
15 15 $ hg ci -Aqm initial
16 16 $ hg debugdata -m 0
17 17 a\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
18 18 b\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
19 19
20 20 Submanifest is stored in separate revlog
21 21
22 22 $ mkdir dir1
23 23 $ echo 1 > dir1/a
24 24 $ echo 1 > dir1/b
25 25 $ echo 1 > e
26 26 $ hg ci -Aqm 'add dir1'
27 27 $ hg debugdata -m 1
28 28 a\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
29 29 b\x00362fef284ce2ca02aecc8de6d5e8a1c3af0556fe (esc)
30 30 dir1\x008b3ffd73f901e83304c83d33132c8e774ceac44et (esc)
31 31 e\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
32 32 $ hg debugdata --dir dir1 0
33 33 a\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
34 34 b\x00b8e02f6433738021a065f94175c7cd23db5f05be (esc)
35 35
36 36 Can add nested directories
37 37
38 38 $ mkdir dir1/dir1
39 39 $ echo 2 > dir1/dir1/a
40 40 $ echo 2 > dir1/dir1/b
41 41 $ mkdir dir1/dir2
42 42 $ echo 2 > dir1/dir2/a
43 43 $ echo 2 > dir1/dir2/b
44 44 $ hg ci -Aqm 'add dir1/dir1'
45 45 $ hg files -r .
46 46 a
47 47 b
48 48 dir1/a
49 49 dir1/b
50 50 dir1/dir1/a
51 51 dir1/dir1/b
52 52 dir1/dir2/a
53 53 dir1/dir2/b
54 54 e
55 55
56 56 The manifest command works
57 57
58 58 $ hg manifest
59 59 a
60 60 b
61 61 dir1/a
62 62 dir1/b
63 63 dir1/dir1/a
64 64 dir1/dir1/b
65 65 dir1/dir2/a
66 66 dir1/dir2/b
67 67 e
68 68
69 69 Revision is not created for unchanged directory
70 70
71 71 $ mkdir dir2
72 72 $ echo 3 > dir2/a
73 73 $ hg add dir2
74 74 adding dir2/a
75 75 $ hg debugindex --dir dir1 > before
76 76 $ hg ci -qm 'add dir2'
77 77 $ hg debugindex --dir dir1 > after
78 78 $ diff before after
79 79 $ rm before after
80 80
81 81 Removing directory does not create an revlog entry
82 82
83 83 $ hg rm dir1/dir1
84 84 removing dir1/dir1/a
85 85 removing dir1/dir1/b
86 86 $ hg debugindex --dir dir1/dir1 > before
87 87 $ hg ci -qm 'remove dir1/dir1'
88 88 $ hg debugindex --dir dir1/dir1 > after
89 89 $ diff before after
90 90 $ rm before after
91 91
92 92 Check that hg files (calls treemanifest.walk()) works
93 93 without loading all directory revlogs
94 94
95 95 $ hg co 'desc("add dir2")'
96 96 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
97 97 $ mv .hg/store/meta/dir2 .hg/store/meta/dir2-backup
98 98 $ hg files -r . dir1
99 99 dir1/a
100 100 dir1/b
101 101 dir1/dir1/a
102 102 dir1/dir1/b
103 103 dir1/dir2/a
104 104 dir1/dir2/b
105 105
106 106 Check that status between revisions works (calls treemanifest.matches())
107 107 without loading all directory revlogs
108 108
109 109 $ hg status --rev 'desc("add dir1")' --rev . dir1
110 110 A dir1/dir1/a
111 111 A dir1/dir1/b
112 112 A dir1/dir2/a
113 113 A dir1/dir2/b
114 114 $ mv .hg/store/meta/dir2-backup .hg/store/meta/dir2
115 115
116 116 Merge creates 2-parent revision of directory revlog
117 117
118 118 $ echo 5 > dir1/a
119 119 $ hg ci -Aqm 'modify dir1/a'
120 120 $ hg co '.^'
121 121 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
122 122 $ echo 6 > dir1/b
123 123 $ hg ci -Aqm 'modify dir1/b'
124 124 $ hg merge 'desc("modify dir1/a")'
125 125 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
126 126 (branch merge, don't forget to commit)
127 127 $ hg ci -m 'conflict-free merge involving dir1/'
128 128 $ cat dir1/a
129 129 5
130 130 $ cat dir1/b
131 131 6
132 132 $ hg debugindex --dir dir1
133 133 rev linkrev nodeid p1-nodeid p2-nodeid
134 134 0 1 8b3ffd73f901 000000000000 000000000000
135 135 1 2 68e9d057c5a8 8b3ffd73f901 000000000000
136 136 2 4 4698198d2624 68e9d057c5a8 000000000000
137 137 3 5 44844058ccce 68e9d057c5a8 000000000000
138 138 4 6 bf3d9b744927 68e9d057c5a8 000000000000
139 139 5 7 dde7c0af2a03 bf3d9b744927 44844058ccce
140 140
141 141 Merge keeping directory from parent 1 does not create revlog entry. (Note that
142 142 dir1's manifest does change, but only because dir1/a's filelog changes.)
143 143
144 144 $ hg co 'desc("add dir2")'
145 145 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
146 146 $ echo 8 > dir2/a
147 147 $ hg ci -m 'modify dir2/a'
148 148 created new head
149 149
150 150 $ hg debugindex --dir dir2 > before
151 151 $ hg merge 'desc("modify dir1/a")'
152 152 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
153 153 (branch merge, don't forget to commit)
154 154 $ hg revert -r 'desc("modify dir2/a")' .
155 155 reverting dir1/a
156 156 $ hg ci -m 'merge, keeping parent 1'
157 157 $ hg debugindex --dir dir2 > after
158 158 $ diff before after
159 159 $ rm before after
160 160
161 161 Merge keeping directory from parent 2 does not create revlog entry. (Note that
162 162 dir2's manifest does change, but only because dir2/a's filelog changes.)
163 163
164 164 $ hg co 'desc("modify dir2/a")'
165 165 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
166 166 $ hg debugindex --dir dir1 > before
167 167 $ hg merge 'desc("modify dir1/a")'
168 168 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
169 169 (branch merge, don't forget to commit)
170 170 $ hg revert -r 'desc("modify dir1/a")' .
171 171 reverting dir2/a
172 172 $ hg ci -m 'merge, keeping parent 2'
173 173 created new head
174 174 $ hg debugindex --dir dir1 > after
175 175 $ diff before after
176 176 $ rm before after
177 177
178 178 Create flat source repo for tests with mixed flat/tree manifests
179 179
180 180 $ cd ..
181 181 $ hg init repo-flat
182 182 $ cd repo-flat
183 183
184 184 Create a few commits with flat manifest
185 185
186 186 $ echo 0 > a
187 187 $ echo 0 > b
188 188 $ echo 0 > e
189 189 $ for d in dir1 dir1/dir1 dir1/dir2 dir2
190 190 > do
191 191 > mkdir $d
192 192 > echo 0 > $d/a
193 193 > echo 0 > $d/b
194 194 > done
195 195 $ hg ci -Aqm initial
196 196
197 197 $ echo 1 > a
198 198 $ echo 1 > dir1/a
199 199 $ echo 1 > dir1/dir1/a
200 200 $ hg ci -Aqm 'modify on branch 1'
201 201
202 202 $ hg co 0
203 203 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
204 204 $ echo 2 > b
205 205 $ echo 2 > dir1/b
206 206 $ echo 2 > dir1/dir1/b
207 207 $ hg ci -Aqm 'modify on branch 2'
208 208
209 209 $ hg merge 1
210 210 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
211 211 (branch merge, don't forget to commit)
212 212 $ hg ci -m 'merge of flat manifests to new flat manifest'
213 213
214 214 $ hg serve -p $HGPORT -d --pid-file=hg.pid --errorlog=errors.log
215 215 $ cat hg.pid >> $DAEMON_PIDS
216 216
217 217 Create clone with tree manifests enabled
218 218
219 219 $ cd ..
220 220 $ hg clone --config experimental.treemanifest=1 \
221 221 > http://localhost:$HGPORT repo-mixed -r 1
222 222 adding changesets
223 223 adding manifests
224 224 adding file changes
225 225 added 2 changesets with 14 changes to 11 files
226 226 new changesets 5b02a3e8db7e:581ef6037d8b
227 227 updating to branch default
228 228 11 files updated, 0 files merged, 0 files removed, 0 files unresolved
229 229 $ cd repo-mixed
230 230 $ test -d .hg/store/meta
231 231 [1]
232 232 $ hg debugrequires | grep treemanifest
233 233 treemanifest
234 234
235 235 Should be possible to push updates from flat to tree manifest repo
236 236
237 237 $ hg -R ../repo-flat push ssh://user@dummy/repo-mixed
238 238 pushing to ssh://user@dummy/repo-mixed
239 239 searching for changes
240 240 remote: adding changesets
241 241 remote: adding manifests
242 242 remote: adding file changes
243 243 remote: added 2 changesets with 3 changes to 3 files
244 244
245 245 Commit should store revlog per directory
246 246
247 247 $ hg co 1
248 248 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
249 249 $ echo 3 > a
250 250 $ echo 3 > dir1/a
251 251 $ echo 3 > dir1/dir1/a
252 252 $ hg ci -m 'first tree'
253 253 created new head
254 254 $ find .hg/store/meta | sort
255 255 .hg/store/meta
256 256 .hg/store/meta/dir1
257 257 .hg/store/meta/dir1/00manifest.i
258 258 .hg/store/meta/dir1/dir1
259 259 .hg/store/meta/dir1/dir1/00manifest.i
260 260 .hg/store/meta/dir1/dir2
261 261 .hg/store/meta/dir1/dir2/00manifest.i
262 262 .hg/store/meta/dir2
263 263 .hg/store/meta/dir2/00manifest.i
264 264
265 265 Merge of two trees
266 266
267 267 $ hg co 2
268 268 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
269 269 $ hg merge 1
270 270 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
271 271 (branch merge, don't forget to commit)
272 272 $ hg ci -m 'merge of flat manifests to new tree manifest'
273 273 created new head
274 274 $ hg diff -r 3
275 275
276 276 Parent of tree root manifest should be flat manifest, and two for merge
277 277
278 278 $ hg debugindex -m
279 279 rev linkrev nodeid p1-nodeid p2-nodeid
280 280 0 0 40536115ed9e 000000000000 000000000000
281 281 1 1 f3376063c255 40536115ed9e 000000000000
282 282 2 2 5d9b9da231a2 40536115ed9e 000000000000
283 283 3 3 d17d663cbd8a 5d9b9da231a2 f3376063c255
284 284 4 4 51e32a8c60ee f3376063c255 000000000000
285 285 5 5 cc5baa78b230 5d9b9da231a2 f3376063c255
286 286
287 287
288 288 Status across flat/tree boundary should work
289 289
290 290 $ hg status --rev '.^' --rev .
291 291 M a
292 292 M dir1/a
293 293 M dir1/dir1/a
294 294
295 295
296 296 Turning off treemanifest config has no effect
297 297
298 298 $ hg debugindex --dir dir1
299 299 rev linkrev nodeid p1-nodeid p2-nodeid
300 300 0 4 064927a0648a 000000000000 000000000000
301 301 1 5 25ecb8cb8618 000000000000 000000000000
302 302 $ echo 2 > dir1/a
303 303 $ hg --config experimental.treemanifest=False ci -qm 'modify dir1/a'
304 304 $ hg debugindex --dir dir1
305 305 rev linkrev nodeid p1-nodeid p2-nodeid
306 306 0 4 064927a0648a 000000000000 000000000000
307 307 1 5 25ecb8cb8618 000000000000 000000000000
308 308 2 6 5b16163a30c6 25ecb8cb8618 000000000000
309 309
310 310 Stripping and recovering changes should work
311 311
312 312 $ hg st --change tip
313 313 M dir1/a
314 314 $ hg --config extensions.strip= strip tip
315 315 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
316 316 saved backup bundle to $TESTTMP/repo-mixed/.hg/strip-backup/51cfd7b1e13b-78a2f3ed-backup.hg
317 317 $ hg debugindex --dir dir1
318 318 rev linkrev nodeid p1-nodeid p2-nodeid
319 319 0 4 064927a0648a 000000000000 000000000000
320 320 1 5 25ecb8cb8618 000000000000 000000000000
321 321
322 322 #if repobundlerepo
323 323 $ hg incoming .hg/strip-backup/*
324 324 comparing with .hg/strip-backup/*-backup.hg (glob)
325 325 searching for changes
326 326 changeset: 6:51cfd7b1e13b
327 327 tag: tip
328 328 user: test
329 329 date: Thu Jan 01 00:00:00 1970 +0000
330 330 summary: modify dir1/a
331 331
332 332 #endif
333 333
334 334 $ hg unbundle .hg/strip-backup/*
335 335 adding changesets
336 336 adding manifests
337 337 adding file changes
338 338 added 1 changesets with 1 changes to 1 files
339 339 new changesets 51cfd7b1e13b (1 drafts)
340 340 (run 'hg update' to get a working copy)
341 341 $ hg --config extensions.strip= strip tip
342 342 saved backup bundle to $TESTTMP/repo-mixed/.hg/strip-backup/*-backup.hg (glob)
343 343 $ hg unbundle -q .hg/strip-backup/*
344 344 $ hg debugindex --dir dir1
345 345 rev linkrev nodeid p1-nodeid p2-nodeid
346 346 0 4 064927a0648a 000000000000 000000000000
347 347 1 5 25ecb8cb8618 000000000000 000000000000
348 348 2 6 5b16163a30c6 25ecb8cb8618 000000000000
349 349 $ hg st --change tip
350 350 M dir1/a
351 351
352 352 Shelving and unshelving should work
353 353
354 354 $ echo foo >> dir1/a
355 355 $ hg shelve
356 356 shelved as default
357 357 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
358 358 $ hg unshelve
359 359 unshelving change 'default'
360 360 $ hg diff --nodates
361 361 diff -r 708a273da119 dir1/a
362 362 --- a/dir1/a
363 363 +++ b/dir1/a
364 364 @@ -1,1 +1,2 @@
365 365 1
366 366 +foo
367 367
368 368 Pushing from treemanifest repo to an empty repo makes that a treemanifest repo
369 369
370 370 $ cd ..
371 371 $ hg init empty-repo
372 372 $ cat << EOF >> empty-repo/.hg/hgrc
373 373 > [experimental]
374 374 > changegroup3=yes
375 375 > EOF
376 376 $ hg debugrequires -R empty-repo | grep treemanifest
377 377 [1]
378 378 $ hg push -R repo -r 0 empty-repo
379 379 pushing to empty-repo
380 380 searching for changes
381 381 adding changesets
382 382 adding manifests
383 383 adding file changes
384 384 added 1 changesets with 2 changes to 2 files
385 385 $ hg debugrequires -R empty-repo | grep treemanifest
386 386 treemanifest
387 387
388 388 Pushing to an empty repo works
389 389
390 390 $ hg --config experimental.treemanifest=1 init clone
391 391 $ hg debugrequires -R clone | grep treemanifest
392 392 treemanifest
393 393 $ hg push -R repo clone
394 394 pushing to clone
395 395 searching for changes
396 396 adding changesets
397 397 adding manifests
398 398 adding file changes
399 399 added 11 changesets with 15 changes to 10 files (+3 heads)
400 400 $ hg debugrequires -R clone | grep treemanifest
401 401 treemanifest
402 402 $ hg -R clone verify -q
403 403
404 404 Create deeper repo with tree manifests.
405 405
406 406 $ hg --config experimental.treemanifest=True init deeprepo
407 407 $ cd deeprepo
408 408
409 409 $ mkdir .A
410 410 $ mkdir b
411 411 $ mkdir b/bar
412 412 $ mkdir b/bar/orange
413 413 $ mkdir b/bar/orange/fly
414 414 $ mkdir b/foo
415 415 $ mkdir b/foo/apple
416 416 $ mkdir b/foo/apple/bees
417 417
418 418 $ touch .A/one.txt
419 419 $ touch .A/two.txt
420 420 $ touch b/bar/fruits.txt
421 421 $ touch b/bar/orange/fly/gnat.py
422 422 $ touch b/bar/orange/fly/housefly.txt
423 423 $ touch b/foo/apple/bees/flower.py
424 424 $ touch c.txt
425 425 $ touch d.py
426 426
427 427 $ hg ci -Aqm 'initial'
428 428
429 429 $ echo >> .A/one.txt
430 430 $ echo >> .A/two.txt
431 431 $ echo >> b/bar/fruits.txt
432 432 $ echo >> b/bar/orange/fly/gnat.py
433 433 $ echo >> b/bar/orange/fly/housefly.txt
434 434 $ echo >> b/foo/apple/bees/flower.py
435 435 $ echo >> c.txt
436 436 $ echo >> d.py
437 437 $ hg ci -Aqm 'second'
438 438
439 439 We'll see that visitdir works by removing some treemanifest revlogs and running
440 440 the files command with various parameters.
441 441
442 442 Test files from the root.
443 443
444 444 $ hg files -r .
445 445 .A/one.txt
446 446 .A/two.txt
447 447 b/bar/fruits.txt
448 448 b/bar/orange/fly/gnat.py
449 449 b/bar/orange/fly/housefly.txt
450 450 b/foo/apple/bees/flower.py
451 451 c.txt
452 452 d.py
453 453
454 454 Excludes with a glob should not exclude everything from the glob's root
455 455
456 456 $ hg files -r . -X 'b/fo?' b
457 457 b/bar/fruits.txt
458 458 b/bar/orange/fly/gnat.py
459 459 b/bar/orange/fly/housefly.txt
460 460 $ cp -R .hg/store .hg/store-copy
461 461
462 462 Test files for a subdirectory.
463 463
464 464 #if reporevlogstore
465 465 $ rm -r .hg/store/meta/~2e_a
466 466 #endif
467 467 #if reposimplestore
468 468 $ rm -r .hg/store/meta/._a
469 469 #endif
470 470 $ hg files -r . b
471 471 b/bar/fruits.txt
472 472 b/bar/orange/fly/gnat.py
473 473 b/bar/orange/fly/housefly.txt
474 474 b/foo/apple/bees/flower.py
475 475 $ hg diff -r '.^' -r . --stat b
476 476 b/bar/fruits.txt | 1 +
477 477 b/bar/orange/fly/gnat.py | 1 +
478 478 b/bar/orange/fly/housefly.txt | 1 +
479 479 b/foo/apple/bees/flower.py | 1 +
480 480 4 files changed, 4 insertions(+), 0 deletions(-)
481 481 $ cp -R .hg/store-copy/. .hg/store
482 482
483 483 Test files with just includes and excludes.
484 484
485 485 #if reporevlogstore
486 486 $ rm -r .hg/store/meta/~2e_a
487 487 #endif
488 488 #if reposimplestore
489 489 $ rm -r .hg/store/meta/._a
490 490 #endif
491 491 $ rm -r .hg/store/meta/b/bar/orange/fly
492 492 $ rm -r .hg/store/meta/b/foo/apple/bees
493 493 $ hg files -r . -I path:b/bar -X path:b/bar/orange/fly -I path:b/foo -X path:b/foo/apple/bees
494 494 b/bar/fruits.txt
495 495 $ hg diff -r '.^' -r . --stat -I path:b/bar -X path:b/bar/orange/fly -I path:b/foo -X path:b/foo/apple/bees
496 496 b/bar/fruits.txt | 1 +
497 497 1 files changed, 1 insertions(+), 0 deletions(-)
498 498 $ cp -R .hg/store-copy/. .hg/store
499 499
500 500 Test files for a subdirectory, excluding a directory within it.
501 501
502 502 #if reporevlogstore
503 503 $ rm -r .hg/store/meta/~2e_a
504 504 #endif
505 505 #if reposimplestore
506 506 $ rm -r .hg/store/meta/._a
507 507 #endif
508 508 $ rm -r .hg/store/meta/b/foo
509 509 $ hg files -r . -X path:b/foo b
510 510 b/bar/fruits.txt
511 511 b/bar/orange/fly/gnat.py
512 512 b/bar/orange/fly/housefly.txt
513 513 $ hg diff -r '.^' -r . --stat -X path:b/foo b
514 514 b/bar/fruits.txt | 1 +
515 515 b/bar/orange/fly/gnat.py | 1 +
516 516 b/bar/orange/fly/housefly.txt | 1 +
517 517 3 files changed, 3 insertions(+), 0 deletions(-)
518 518 $ cp -R .hg/store-copy/. .hg/store
519 519
520 520 Test files for a sub directory, including only a directory within it, and
521 521 including an unrelated directory.
522 522
523 523 #if reporevlogstore
524 524 $ rm -r .hg/store/meta/~2e_a
525 525 #endif
526 526 #if reposimplestore
527 527 $ rm -r .hg/store/meta/._a
528 528 #endif
529 529 $ rm -r .hg/store/meta/b/foo
530 530 $ hg files -r . -I path:b/bar/orange -I path:a b
531 531 b/bar/orange/fly/gnat.py
532 532 b/bar/orange/fly/housefly.txt
533 533 $ hg diff -r '.^' -r . --stat -I path:b/bar/orange -I path:a b
534 534 b/bar/orange/fly/gnat.py | 1 +
535 535 b/bar/orange/fly/housefly.txt | 1 +
536 536 2 files changed, 2 insertions(+), 0 deletions(-)
537 537 $ cp -R .hg/store-copy/. .hg/store
538 538
539 539 Test files for a pattern, including a directory, and excluding a directory
540 540 within that.
541 541
542 542 #if reporevlogstore
543 543 $ rm -r .hg/store/meta/~2e_a
544 544 #endif
545 545 #if reposimplestore
546 546 $ rm -r .hg/store/meta/._a
547 547 #endif
548 548 $ rm -r .hg/store/meta/b/foo
549 549 $ rm -r .hg/store/meta/b/bar/orange
550 550 $ hg files -r . glob:**.txt -I path:b/bar -X path:b/bar/orange
551 551 b/bar/fruits.txt
552 552 $ hg diff -r '.^' -r . --stat glob:**.txt -I path:b/bar -X path:b/bar/orange
553 553 b/bar/fruits.txt | 1 +
554 554 1 files changed, 1 insertions(+), 0 deletions(-)
555 555 $ cp -R .hg/store-copy/. .hg/store
556 556
557 557 Add some more changes to the deep repo
558 558 $ echo narf >> b/bar/fruits.txt
559 559 $ hg ci -m narf
560 560 $ echo troz >> b/bar/orange/fly/gnat.py
561 561 $ hg ci -m troz
562 562
563 563 Verify works
564 564 $ hg verify -q
565 565
566 566 #if repofncache
567 567 Dirlogs are included in fncache
568 568 $ grep meta/.A/00manifest.i .hg/store/fncache
569 569 meta/.A/00manifest.i
570 570
571 571 Rebuilt fncache includes dirlogs
572 572 $ rm .hg/store/fncache
573 573 $ hg debugrebuildfncache
574 574 adding data/.A/one.txt.i
575 575 adding data/.A/two.txt.i
576 576 adding data/b/bar/fruits.txt.i
577 577 adding data/b/bar/orange/fly/gnat.py.i
578 578 adding data/b/bar/orange/fly/housefly.txt.i
579 579 adding data/b/foo/apple/bees/flower.py.i
580 580 adding data/c.txt.i
581 581 adding data/d.py.i
582 582 adding meta/.A/00manifest.i
583 583 adding meta/b/00manifest.i
584 584 adding meta/b/bar/00manifest.i
585 585 adding meta/b/bar/orange/00manifest.i
586 586 adding meta/b/bar/orange/fly/00manifest.i
587 587 adding meta/b/foo/00manifest.i
588 588 adding meta/b/foo/apple/00manifest.i
589 589 adding meta/b/foo/apple/bees/00manifest.i
590 590 16 items added, 0 removed from fncache
591 591 #endif
592 592
593 593 Finish first server
594 594 $ killdaemons.py
595 595
596 596 Back up the recently added revlogs
597 597 $ cp -R .hg/store .hg/store-newcopy
598 598
599 599 Verify reports missing dirlog
600 600 $ rm .hg/store/meta/b/00manifest.*
601 601 $ hg verify
602 602 checking changesets
603 603 checking manifests
604 604 checking directory manifests
605 605 0: empty or missing b/
606 606 b/@0: parent-directory manifest refers to unknown revision 67688a370455
607 607 b/@1: parent-directory manifest refers to unknown revision f065da70369e
608 608 b/@2: parent-directory manifest refers to unknown revision ac0d30948e0b
609 609 b/@3: parent-directory manifest refers to unknown revision 367152e6af28
610 610 warning: orphan data file 'meta/b/bar/00manifest.i' (reporevlogstore !)
611 611 warning: orphan data file 'meta/b/bar/orange/00manifest.i' (reporevlogstore !)
612 612 warning: orphan data file 'meta/b/bar/orange/fly/00manifest.i' (reporevlogstore !)
613 613 warning: orphan data file 'meta/b/foo/00manifest.i' (reporevlogstore !)
614 614 warning: orphan data file 'meta/b/foo/apple/00manifest.i' (reporevlogstore !)
615 615 warning: orphan data file 'meta/b/foo/apple/bees/00manifest.i' (reporevlogstore !)
616 616 crosschecking files in changesets and manifests
617 617 b/bar/fruits.txt@0: in changeset but not in manifest
618 618 b/bar/orange/fly/gnat.py@0: in changeset but not in manifest
619 619 b/bar/orange/fly/housefly.txt@0: in changeset but not in manifest
620 620 b/foo/apple/bees/flower.py@0: in changeset but not in manifest
621 621 checking files
622 not checking dirstate because of previous errors
622 623 checked 4 changesets with 18 changes to 8 files
623 624 6 warnings encountered! (reporevlogstore !)
624 625 9 integrity errors encountered!
625 626 (first damaged changeset appears to be 0)
626 627 [1]
627 628 $ cp -R .hg/store-newcopy/. .hg/store
628 629
629 630 Verify reports missing dirlog entry
630 631 $ mv -f .hg/store-copy/meta/b/00manifest.* .hg/store/meta/b/
631 632 $ hg verify
632 633 checking changesets
633 634 checking manifests
634 635 checking directory manifests
635 636 b/@2: parent-directory manifest refers to unknown revision ac0d30948e0b
636 637 b/@3: parent-directory manifest refers to unknown revision 367152e6af28
637 638 b/bar/@?: rev 2 points to unexpected changeset 2
638 639 b/bar/@?: 44d7e1146e0d not in parent-directory manifest
639 640 b/bar/@?: rev 3 points to unexpected changeset 3
640 641 b/bar/@?: 70b10c6b17b7 not in parent-directory manifest
641 642 b/bar/orange/@?: rev 2 points to unexpected changeset 3
642 643 (expected None)
643 644 b/bar/orange/fly/@?: rev 2 points to unexpected changeset 3
644 645 (expected None)
645 646 crosschecking files in changesets and manifests
646 647 checking files
648 not checking dirstate because of previous errors
647 649 checked 4 changesets with 18 changes to 8 files
648 650 2 warnings encountered!
649 651 8 integrity errors encountered!
650 652 (first damaged changeset appears to be 2)
651 653 [1]
652 654 $ cp -R .hg/store-newcopy/. .hg/store
653 655
654 656 Test cloning a treemanifest repo over http.
655 657 $ hg serve -p $HGPORT -d --pid-file=hg.pid --errorlog=errors.log
656 658 $ cat hg.pid >> $DAEMON_PIDS
657 659 $ cd ..
658 660 We can clone even with the knob turned off and we'll get a treemanifest repo.
659 661 $ hg clone --config experimental.treemanifest=False \
660 662 > --config experimental.changegroup3=True \
661 663 > http://localhost:$HGPORT deepclone
662 664 requesting all changes
663 665 adding changesets
664 666 adding manifests
665 667 adding file changes
666 668 added 4 changesets with 18 changes to 8 files
667 669 new changesets 775704be6f52:523e5c631710
668 670 updating to branch default
669 671 8 files updated, 0 files merged, 0 files removed, 0 files unresolved
670 672 No server errors.
671 673 $ cat deeprepo/errors.log
672 674 requires got updated to include treemanifest
673 675 $ hg debugrequires -R deepclone | grep treemanifest
674 676 treemanifest
675 677 Tree manifest revlogs exist.
676 678 $ find deepclone/.hg/store/meta | sort
677 679 deepclone/.hg/store/meta
678 680 deepclone/.hg/store/meta/._a (reposimplestore !)
679 681 deepclone/.hg/store/meta/._a/00manifest.i (reposimplestore !)
680 682 deepclone/.hg/store/meta/b
681 683 deepclone/.hg/store/meta/b/00manifest.i
682 684 deepclone/.hg/store/meta/b/bar
683 685 deepclone/.hg/store/meta/b/bar/00manifest.i
684 686 deepclone/.hg/store/meta/b/bar/orange
685 687 deepclone/.hg/store/meta/b/bar/orange/00manifest.i
686 688 deepclone/.hg/store/meta/b/bar/orange/fly
687 689 deepclone/.hg/store/meta/b/bar/orange/fly/00manifest.i
688 690 deepclone/.hg/store/meta/b/foo
689 691 deepclone/.hg/store/meta/b/foo/00manifest.i
690 692 deepclone/.hg/store/meta/b/foo/apple
691 693 deepclone/.hg/store/meta/b/foo/apple/00manifest.i
692 694 deepclone/.hg/store/meta/b/foo/apple/bees
693 695 deepclone/.hg/store/meta/b/foo/apple/bees/00manifest.i
694 696 deepclone/.hg/store/meta/~2e_a (reporevlogstore !)
695 697 deepclone/.hg/store/meta/~2e_a/00manifest.i (reporevlogstore !)
696 698 Verify passes.
697 699 $ cd deepclone
698 700 $ hg verify -q
699 701 $ cd ..
700 702
701 703 #if reporevlogstore
702 704 Create clones using old repo formats to use in later tests
703 705 $ hg clone --config format.usestore=False \
704 706 > --config experimental.changegroup3=True \
705 707 > http://localhost:$HGPORT deeprepo-basicstore
706 708 requesting all changes
707 709 adding changesets
708 710 adding manifests
709 711 adding file changes
710 712 added 4 changesets with 18 changes to 8 files
711 713 new changesets 775704be6f52:523e5c631710
712 714 updating to branch default
713 715 8 files updated, 0 files merged, 0 files removed, 0 files unresolved
714 716 $ cd deeprepo-basicstore
715 717 $ hg debugrequires | grep store
716 718 [1]
717 719 $ hg serve -p $HGPORT1 -d --pid-file=hg.pid --errorlog=errors.log
718 720 $ cat hg.pid >> $DAEMON_PIDS
719 721 $ cd ..
720 722 $ hg clone --config format.usefncache=False \
721 723 > --config experimental.changegroup3=True \
722 724 > http://localhost:$HGPORT deeprepo-encodedstore
723 725 requesting all changes
724 726 adding changesets
725 727 adding manifests
726 728 adding file changes
727 729 added 4 changesets with 18 changes to 8 files
728 730 new changesets 775704be6f52:523e5c631710
729 731 updating to branch default
730 732 8 files updated, 0 files merged, 0 files removed, 0 files unresolved
731 733 $ cd deeprepo-encodedstore
732 734 $ hg debugrequires | grep fncache
733 735 [1]
734 736 $ hg serve -p $HGPORT2 -d --pid-file=hg.pid --errorlog=errors.log
735 737 $ cat hg.pid >> $DAEMON_PIDS
736 738 $ cd ..
737 739
738 740 Local clone with basicstore
739 741 $ hg clone -U deeprepo-basicstore local-clone-basicstore
740 742 $ hg -R local-clone-basicstore verify -q
741 743
742 744 Local clone with encodedstore
743 745 $ hg clone -U deeprepo-encodedstore local-clone-encodedstore
744 746 $ hg -R local-clone-encodedstore verify -q
745 747
746 748 Local clone with fncachestore
747 749 $ hg clone -U deeprepo local-clone-fncachestore
748 750 $ hg -R local-clone-fncachestore verify -q
749 751
750 752 Stream clone with basicstore
751 753 $ hg clone --config experimental.changegroup3=True --stream -U \
752 754 > http://localhost:$HGPORT1 stream-clone-basicstore
753 755 streaming all changes
754 756 28 files to transfer, * of data (glob)
755 757 transferred * in * seconds (*) (glob)
756 758 $ hg -R stream-clone-basicstore verify -q
757 759
758 760 Stream clone with encodedstore
759 761 $ hg clone --config experimental.changegroup3=True --stream -U \
760 762 > http://localhost:$HGPORT2 stream-clone-encodedstore
761 763 streaming all changes
762 764 28 files to transfer, * of data (glob)
763 765 transferred * in * seconds (*) (glob)
764 766 $ hg -R stream-clone-encodedstore verify -q
765 767
766 768 Stream clone with fncachestore
767 769 $ hg clone --config experimental.changegroup3=True --stream -U \
768 770 > http://localhost:$HGPORT stream-clone-fncachestore
769 771 streaming all changes
770 772 22 files to transfer, * of data (glob)
771 773 transferred * in * seconds (*) (glob)
772 774 $ hg -R stream-clone-fncachestore verify -q
773 775
774 776 Packed bundle
775 777 $ hg -R deeprepo debugcreatestreamclonebundle repo-packed.hg
776 778 writing 5330 bytes for 18 files (no-zstd !)
777 779 writing 5400 bytes for 18 files (zstd !)
778 780 bundle requirements:.* treemanifest(,.*)? (re)
779 781 $ hg debugbundle --spec repo-packed.hg
780 782 none-packed1;requirements%3D(.*%2C)?treemanifest(%2C.*)? (re)
781 783
782 784 #endif
783 785
784 786 Bundle with changegroup2 is not supported
785 787
786 788 $ hg -R deeprepo bundle --all -t v2 deeprepo.bundle
787 789 abort: repository does not support bundle version 02
788 790 [255]
789 791
790 792 Pull does not include changegroup for manifest the client already has from
791 793 other branch
792 794
793 795 $ mkdir grafted-dir-repo
794 796 $ cd grafted-dir-repo
795 797 $ hg --config experimental.treemanifest=1 init
796 798 $ mkdir dir
797 799 $ echo a > dir/file
798 800 $ echo a > file
799 801 $ hg ci -Am initial
800 802 adding dir/file
801 803 adding file
802 804 $ echo b > dir/file
803 805 $ hg ci -m updated
804 806 $ hg co '.^'
805 807 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
806 808 $ hg revert -r tip dir/
807 809 reverting dir/file
808 810 $ echo b > file # to make sure root manifest is sent
809 811 $ hg ci -m grafted
810 812 created new head
811 813 $ cd ..
812 814
813 815 $ hg --config experimental.treemanifest=1 clone --pull -r 1 \
814 816 > grafted-dir-repo grafted-dir-repo-clone
815 817 adding changesets
816 818 adding manifests
817 819 adding file changes
818 820 added 2 changesets with 3 changes to 2 files
819 821 new changesets d84f4c419457:09ab742f3b0f
820 822 updating to branch default
821 823 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
822 824 $ cd grafted-dir-repo-clone
823 825 $ hg pull -r 2
824 826 pulling from $TESTTMP/grafted-dir-repo
825 827 searching for changes
826 828 adding changesets
827 829 adding manifests
828 830 adding file changes
829 831 added 1 changesets with 1 changes to 1 files (+1 heads)
830 832 new changesets 73699489fb7c
831 833 (run 'hg heads' to see heads, 'hg merge' to merge)
832 834
833 835 Committing a empty commit does not duplicate root treemanifest
834 836 $ echo z >> z
835 837 $ hg commit -Aqm 'pre-empty commit'
836 838 $ hg rm z
837 839 $ hg commit --amend -m 'empty commit'
838 840 saved backup bundle to $TESTTMP/grafted-dir-repo-clone/.hg/strip-backup/cb99d5717cea-9e3b6b02-amend.hg
839 841 $ hg log -r 'tip + tip^' -T '{manifest}\n'
840 842 1:678d3574b88c
841 843 1:678d3574b88c
842 844 $ hg --config extensions.strip= strip -r . -q
@@ -1,339 +1,365 b''
1 1 #require reporevlogstore
2 2
3 3 prepare repo
4 4
5 5 $ hg init a
6 6 $ cd a
7 7 $ echo "some text" > FOO.txt
8 8 $ echo "another text" > bar.txt
9 9 $ echo "more text" > QUICK.txt
10 10 $ hg add
11 11 adding FOO.txt
12 12 adding QUICK.txt
13 13 adding bar.txt
14 14 $ hg ci -mtest1
15 15
16 16 verify
17 17
18 18 $ hg verify
19 19 checking changesets
20 20 checking manifests
21 21 crosschecking files in changesets and manifests
22 22 checking files
23 checking dirstate
23 24 checked 1 changesets with 3 changes to 3 files
24 25
25 26 verify with journal
26 27
27 28 $ touch .hg/store/journal
28 29 $ hg verify
29 30 abandoned transaction found - run hg recover
30 31 checking changesets
31 32 checking manifests
32 33 crosschecking files in changesets and manifests
33 34 checking files
35 checking dirstate
34 36 checked 1 changesets with 3 changes to 3 files
35 37 $ rm .hg/store/journal
36 38
37 39 introduce some bugs in repo
38 40
39 41 $ cd .hg/store/data
40 42 $ mv _f_o_o.txt.i X_f_o_o.txt.i
41 43 $ mv bar.txt.i xbar.txt.i
42 44 $ rm _q_u_i_c_k.txt.i
43 45
44 46 $ hg verify
45 47 checking changesets
46 48 checking manifests
47 49 crosschecking files in changesets and manifests
48 50 checking files
49 51 warning: revlog 'data/FOO.txt.i' not in fncache!
50 52 0: empty or missing FOO.txt
51 53 FOO.txt@0: manifest refers to unknown revision f62022d3d590
52 54 warning: revlog 'data/QUICK.txt.i' not in fncache!
53 55 0: empty or missing QUICK.txt
54 56 QUICK.txt@0: manifest refers to unknown revision 88b857db8eba
55 57 warning: revlog 'data/bar.txt.i' not in fncache!
56 58 0: empty or missing bar.txt
57 59 bar.txt@0: manifest refers to unknown revision 256559129457
60 not checking dirstate because of previous errors
58 61 checked 1 changesets with 0 changes to 3 files
59 62 3 warnings encountered!
60 63 hint: run "hg debugrebuildfncache" to recover from corrupt fncache
61 64 6 integrity errors encountered!
62 65 (first damaged changeset appears to be 0)
63 66 [1]
64 67
65 68 $ cd ../../..
66 69 $ cd ..
67 70
68 71 Set up a repo for testing missing revlog entries
69 72
70 73 $ hg init missing-entries
71 74 $ cd missing-entries
72 75 $ echo 0 > file
73 76 $ hg ci -Aqm0
74 77 $ cp -R .hg/store .hg/store-partial
75 78 $ echo 1 > file
76 79 $ hg ci -Aqm1
77 80 $ cp -R .hg/store .hg/store-full
78 81
79 82 Entire changelog missing
80 83
81 84 $ rm .hg/store/00changelog.*
82 85 $ hg verify -q
83 86 0: empty or missing changelog
84 87 manifest@0: d0b6632564d4 not in changesets
85 88 manifest@1: 941fc4534185 not in changesets
89 not checking dirstate because of previous errors
86 90 3 integrity errors encountered!
87 91 (first damaged changeset appears to be 0)
88 92 [1]
89 93 $ cp -R .hg/store-full/. .hg/store
90 94
91 95 Entire manifest log missing
92 96
93 97 $ rm .hg/store/00manifest.*
94 98 $ hg verify -q
95 99 0: empty or missing manifest
100 not checking dirstate because of previous errors
96 101 1 integrity errors encountered!
97 102 (first damaged changeset appears to be 0)
98 103 [1]
99 104 $ cp -R .hg/store-full/. .hg/store
100 105
101 106 Entire filelog missing
102 107
103 108 $ rm .hg/store/data/file.*
104 109 $ hg verify -q
105 110 warning: revlog 'data/file.i' not in fncache!
106 111 0: empty or missing file
107 112 file@0: manifest refers to unknown revision 362fef284ce2
108 113 file@1: manifest refers to unknown revision c10f2164107d
114 not checking dirstate because of previous errors
109 115 1 warnings encountered!
110 116 hint: run "hg debugrebuildfncache" to recover from corrupt fncache
111 117 3 integrity errors encountered!
112 118 (first damaged changeset appears to be 0)
113 119 [1]
114 120 $ cp -R .hg/store-full/. .hg/store
115 121
116 122 Entire changelog and manifest log missing
117 123
118 124 $ rm .hg/store/00changelog.*
119 125 $ rm .hg/store/00manifest.*
120 126 $ hg verify -q
121 127 warning: orphan data file 'data/file.i'
128 warning: ignoring unknown working parent c5ddb05ab828!
129 file marked as tracked in p1 but not in manifest1
122 130 1 warnings encountered!
131 1 integrity errors encountered!
132 dirstate inconsistent with current parent's manifest
133 1 dirstate errors
134 [1]
123 135 $ cp -R .hg/store-full/. .hg/store
124 136
125 137 Entire changelog and filelog missing
126 138
127 139 $ rm .hg/store/00changelog.*
128 140 $ rm .hg/store/data/file.*
129 141 $ hg verify -q
130 142 0: empty or missing changelog
131 143 manifest@0: d0b6632564d4 not in changesets
132 144 manifest@1: 941fc4534185 not in changesets
133 145 warning: revlog 'data/file.i' not in fncache!
134 146 ?: empty or missing file
135 147 file@0: manifest refers to unknown revision 362fef284ce2
136 148 file@1: manifest refers to unknown revision c10f2164107d
149 not checking dirstate because of previous errors
137 150 1 warnings encountered!
138 151 hint: run "hg debugrebuildfncache" to recover from corrupt fncache
139 152 6 integrity errors encountered!
140 153 (first damaged changeset appears to be 0)
141 154 [1]
142 155 $ cp -R .hg/store-full/. .hg/store
143 156
144 157 Entire manifest log and filelog missing
145 158
146 159 $ rm .hg/store/00manifest.*
147 160 $ rm .hg/store/data/file.*
148 161 $ hg verify -q
149 162 0: empty or missing manifest
150 163 warning: revlog 'data/file.i' not in fncache!
151 164 0: empty or missing file
165 not checking dirstate because of previous errors
152 166 1 warnings encountered!
153 167 hint: run "hg debugrebuildfncache" to recover from corrupt fncache
154 168 2 integrity errors encountered!
155 169 (first damaged changeset appears to be 0)
156 170 [1]
157 171 $ cp -R .hg/store-full/. .hg/store
158 172
159 173 Changelog missing entry
160 174
161 175 $ cp -f .hg/store-partial/00changelog.* .hg/store
162 176 $ hg verify -q
163 177 manifest@?: rev 1 points to nonexistent changeset 1
164 178 manifest@?: 941fc4534185 not in changesets
165 179 file@?: rev 1 points to nonexistent changeset 1
166 180 (expected 0)
181 not checking dirstate because of previous errors
167 182 1 warnings encountered!
168 183 3 integrity errors encountered!
169 184 [1]
170 185 $ cp -R .hg/store-full/. .hg/store
171 186
172 187 Manifest log missing entry
173 188
174 189 $ cp -f .hg/store-partial/00manifest.* .hg/store
175 190 $ hg verify -q
176 191 manifest@1: changeset refers to unknown revision 941fc4534185
177 192 file@1: c10f2164107d not in manifests
193 not checking dirstate because of previous errors
178 194 2 integrity errors encountered!
179 195 (first damaged changeset appears to be 1)
180 196 [1]
181 197 $ cp -R .hg/store-full/. .hg/store
182 198
183 199 Filelog missing entry
184 200
185 201 $ cp -f .hg/store-partial/data/file.* .hg/store/data
186 202 $ hg verify -q
187 203 file@1: manifest refers to unknown revision c10f2164107d
204 not checking dirstate because of previous errors
188 205 1 integrity errors encountered!
189 206 (first damaged changeset appears to be 1)
190 207 [1]
191 208 $ cp -R .hg/store-full/. .hg/store
192 209
193 210 Changelog and manifest log missing entry
194 211
195 212 $ cp -f .hg/store-partial/00changelog.* .hg/store
196 213 $ cp -f .hg/store-partial/00manifest.* .hg/store
197 214 $ hg verify -q
198 215 file@?: rev 1 points to nonexistent changeset 1
199 216 (expected 0)
200 217 file@?: c10f2164107d not in manifests
218 not checking dirstate because of previous errors
201 219 1 warnings encountered!
202 220 2 integrity errors encountered!
203 221 [1]
204 222 $ cp -R .hg/store-full/. .hg/store
205 223
206 224 Changelog and filelog missing entry
207 225
208 226 $ cp -f .hg/store-partial/00changelog.* .hg/store
209 227 $ cp -f .hg/store-partial/data/file.* .hg/store/data
210 228 $ hg verify -q
211 229 manifest@?: rev 1 points to nonexistent changeset 1
212 230 manifest@?: 941fc4534185 not in changesets
213 231 file@?: manifest refers to unknown revision c10f2164107d
232 not checking dirstate because of previous errors
214 233 3 integrity errors encountered!
215 234 [1]
216 235 $ cp -R .hg/store-full/. .hg/store
217 236
218 237 Manifest and filelog missing entry
219 238
220 239 $ cp -f .hg/store-partial/00manifest.* .hg/store
221 240 $ cp -f .hg/store-partial/data/file.* .hg/store/data
222 241 $ hg verify -q
223 242 manifest@1: changeset refers to unknown revision 941fc4534185
243 not checking dirstate because of previous errors
224 244 1 integrity errors encountered!
225 245 (first damaged changeset appears to be 1)
226 246 [1]
227 247 $ cp -R .hg/store-full/. .hg/store
228 248
229 249 Corrupt changelog base node to cause failure to read revision
230 250
231 251 $ printf abcd | dd conv=notrunc of=.hg/store/00changelog.i bs=1 seek=16 \
232 252 > 2> /dev/null
233 253 $ hg verify -q
234 254 0: unpacking changeset 08b1860757c2: * (glob)
235 255 manifest@?: rev 0 points to unexpected changeset 0
236 256 manifest@?: d0b6632564d4 not in changesets
237 257 file@?: rev 0 points to unexpected changeset 0
238 258 (expected 1)
259 not checking dirstate because of previous errors
239 260 1 warnings encountered!
240 261 4 integrity errors encountered!
241 262 (first damaged changeset appears to be 0)
242 263 [1]
243 264 $ cp -R .hg/store-full/. .hg/store
244 265
245 266 Corrupt manifest log base node to cause failure to read revision
246 267
247 268 $ printf abcd | dd conv=notrunc of=.hg/store/00manifest.i bs=1 seek=16 \
248 269 > 2> /dev/null
249 270 $ hg verify -q
250 271 manifest@0: reading delta d0b6632564d4: * (glob)
251 272 file@0: 362fef284ce2 not in manifests
273 not checking dirstate because of previous errors
252 274 2 integrity errors encountered!
253 275 (first damaged changeset appears to be 0)
254 276 [1]
255 277 $ cp -R .hg/store-full/. .hg/store
256 278
257 279 Corrupt filelog base node to cause failure to read revision
258 280
259 281 $ printf abcd | dd conv=notrunc of=.hg/store/data/file.i bs=1 seek=16 \
260 282 > 2> /dev/null
261 283 $ hg verify -q
262 284 file@0: unpacking 362fef284ce2: * (glob)
285 not checking dirstate because of previous errors
263 286 1 integrity errors encountered!
264 287 (first damaged changeset appears to be 0)
265 288 [1]
266 289 $ cp -R .hg/store-full/. .hg/store
267 290
268 291 $ cd ..
269 292
270 293 test changelog without a manifest
271 294
272 295 $ hg init b
273 296 $ cd b
274 297 $ hg branch foo
275 298 marked working directory as branch foo
276 299 (branches are permanent and global, did you want a bookmark?)
277 300 $ hg ci -m branchfoo
278 301 $ hg verify -q
279 302
280 303 test revlog corruption
281 304
282 305 $ touch a
283 306 $ hg add a
284 307 $ hg ci -m a
285 308
286 309 $ echo 'corrupted' > b
287 310 $ dd if=.hg/store/data/a.i of=start bs=1 count=20 2>/dev/null
288 311 $ cat start b > .hg/store/data/a.i
289 312
290 313 $ hg verify -q
291 314 a@1: broken revlog! (index a is corrupted)
292 315 warning: orphan data file 'data/a.i'
316 not checking dirstate because of previous errors
293 317 1 warnings encountered!
294 318 1 integrity errors encountered!
295 319 (first damaged changeset appears to be 1)
296 320 [1]
297 321
298 322 $ cd ..
299 323
300 324 test revlog format 0
301 325
302 326 $ revlog-formatv0.py
303 327 $ cd formatv0
304 328 $ hg verify
305 329 repository uses revlog format 0
306 330 checking changesets
307 331 checking manifests
308 332 crosschecking files in changesets and manifests
309 333 checking files
334 checking dirstate
310 335 checked 1 changesets with 1 changes to 1 files
311 336 $ cd ..
312 337
313 338 test flag processor and skipflags
314 339
315 340 $ hg init skipflags
316 341 $ cd skipflags
317 342 $ cat >> .hg/hgrc <<EOF
318 343 > [extensions]
319 344 > flagprocessor=$RUNTESTDIR/flagprocessorext.py
320 345 > EOF
321 346 $ echo '[BASE64]content' > base64
322 347 $ hg commit -Aqm 'flag processor content' base64
323 348 $ hg verify -q
324 349
325 350 $ cat >> $TESTTMP/break-base64.py <<EOF
326 351 > import base64
327 352 > base64.b64decode=lambda x: x
328 353 > EOF
329 354 $ cat >> .hg/hgrc <<EOF
330 355 > breakbase64=$TESTTMP/break-base64.py
331 356 > EOF
332 357
333 358 $ hg verify -q
334 359 base64@0: unpacking 794cee7777cb: integrity check failed on base64:0
360 not checking dirstate because of previous errors
335 361 1 integrity errors encountered!
336 362 (first damaged changeset appears to be 0)
337 363 [1]
338 364 $ hg verify --config verify.skipflags=2147483647 -q
339 365
General Comments 0
You need to be logged in to leave comments. Login now