##// END OF EJS Templates
obsolete: disable other evolution config options if createmarkers is off...
av6 -
r48579:b1e1559f default
parent child Browse files
Show More
@@ -1,1154 +1,1150
1 1 # obsolete.py - obsolete markers handling
2 2 #
3 3 # Copyright 2012 Pierre-Yves David <pierre-yves.david@ens-lyon.org>
4 4 # Logilab SA <contact@logilab.fr>
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9 """Obsolete marker handling
10 10
11 11 An obsolete marker maps an old changeset to a list of new
12 12 changesets. If the list of new changesets is empty, the old changeset
13 13 is said to be "killed". Otherwise, the old changeset is being
14 14 "replaced" by the new changesets.
15 15
16 16 Obsolete markers can be used to record and distribute changeset graph
17 17 transformations performed by history rewrite operations, and help
18 18 building new tools to reconcile conflicting rewrite actions. To
19 19 facilitate conflict resolution, markers include various annotations
20 20 besides old and news changeset identifiers, such as creation date or
21 21 author name.
22 22
23 23 The old obsoleted changeset is called a "predecessor" and possible
24 24 replacements are called "successors". Markers that used changeset X as
25 25 a predecessor are called "successor markers of X" because they hold
26 26 information about the successors of X. Markers that use changeset Y as
27 27 a successors are call "predecessor markers of Y" because they hold
28 28 information about the predecessors of Y.
29 29
30 30 Examples:
31 31
32 32 - When changeset A is replaced by changeset A', one marker is stored:
33 33
34 34 (A, (A',))
35 35
36 36 - When changesets A and B are folded into a new changeset C, two markers are
37 37 stored:
38 38
39 39 (A, (C,)) and (B, (C,))
40 40
41 41 - When changeset A is simply "pruned" from the graph, a marker is created:
42 42
43 43 (A, ())
44 44
45 45 - When changeset A is split into B and C, a single marker is used:
46 46
47 47 (A, (B, C))
48 48
49 49 We use a single marker to distinguish the "split" case from the "divergence"
50 50 case. If two independent operations rewrite the same changeset A in to A' and
51 51 A'', we have an error case: divergent rewriting. We can detect it because
52 52 two markers will be created independently:
53 53
54 54 (A, (B,)) and (A, (C,))
55 55
56 56 Format
57 57 ------
58 58
59 59 Markers are stored in an append-only file stored in
60 60 '.hg/store/obsstore'.
61 61
62 62 The file starts with a version header:
63 63
64 64 - 1 unsigned byte: version number, starting at zero.
65 65
66 66 The header is followed by the markers. Marker format depend of the version. See
67 67 comment associated with each format for details.
68 68
69 69 """
70 70 from __future__ import absolute_import
71 71
72 72 import errno
73 73 import struct
74 74
75 75 from .i18n import _
76 76 from .node import (
77 77 bin,
78 78 hex,
79 79 )
80 80 from .pycompat import getattr
81 81 from .node import (
82 82 bin,
83 83 hex,
84 84 )
85 85 from . import (
86 86 encoding,
87 87 error,
88 88 obsutil,
89 89 phases,
90 90 policy,
91 91 pycompat,
92 92 util,
93 93 )
94 94 from .utils import (
95 95 dateutil,
96 96 hashutil,
97 97 )
98 98
99 99 parsers = policy.importmod('parsers')
100 100
101 101 _pack = struct.pack
102 102 _unpack = struct.unpack
103 103 _calcsize = struct.calcsize
104 104 propertycache = util.propertycache
105 105
106 106 # Options for obsolescence
107 107 createmarkersopt = b'createmarkers'
108 108 allowunstableopt = b'allowunstable'
109 109 allowdivergenceopt = b'allowdivergence'
110 110 exchangeopt = b'exchange'
111 111
112 112
113 113 def _getoptionvalue(repo, option):
114 114 """Returns True if the given repository has the given obsolete option
115 115 enabled.
116 116 """
117 117 configkey = b'evolution.%s' % option
118 118 newconfig = repo.ui.configbool(b'experimental', configkey)
119 119
120 120 # Return the value only if defined
121 121 if newconfig is not None:
122 122 return newconfig
123 123
124 124 # Fallback on generic option
125 125 try:
126 126 return repo.ui.configbool(b'experimental', b'evolution')
127 127 except (error.ConfigError, AttributeError):
128 128 # Fallback on old-fashion config
129 129 # inconsistent config: experimental.evolution
130 130 result = set(repo.ui.configlist(b'experimental', b'evolution'))
131 131
132 132 if b'all' in result:
133 133 return True
134 134
135 135 # Temporary hack for next check
136 136 newconfig = repo.ui.config(b'experimental', b'evolution.createmarkers')
137 137 if newconfig:
138 138 result.add(b'createmarkers')
139 139
140 140 return option in result
141 141
142 142
143 143 def getoptions(repo):
144 144 """Returns dicts showing state of obsolescence features."""
145 145
146 146 createmarkersvalue = _getoptionvalue(repo, createmarkersopt)
147 unstablevalue = _getoptionvalue(repo, allowunstableopt)
148 divergencevalue = _getoptionvalue(repo, allowdivergenceopt)
149 exchangevalue = _getoptionvalue(repo, exchangeopt)
150
151 # createmarkers must be enabled if other options are enabled
152 if (
153 unstablevalue or divergencevalue or exchangevalue
154 ) and not createmarkersvalue:
155 raise error.Abort(
156 _(
157 b"'createmarkers' obsolete option must be enabled "
158 b"if other obsolete options are enabled"
159 )
160 )
147 if createmarkersvalue:
148 unstablevalue = _getoptionvalue(repo, allowunstableopt)
149 divergencevalue = _getoptionvalue(repo, allowdivergenceopt)
150 exchangevalue = _getoptionvalue(repo, exchangeopt)
151 else:
152 # if we cannot create obsolescence markers, we shouldn't exchange them
153 # or perform operations that lead to instability or divergence
154 unstablevalue = False
155 divergencevalue = False
156 exchangevalue = False
161 157
162 158 return {
163 159 createmarkersopt: createmarkersvalue,
164 160 allowunstableopt: unstablevalue,
165 161 allowdivergenceopt: divergencevalue,
166 162 exchangeopt: exchangevalue,
167 163 }
168 164
169 165
170 166 def isenabled(repo, option):
171 167 """Returns True if the given repository has the given obsolete option
172 168 enabled.
173 169 """
174 170 return getoptions(repo)[option]
175 171
176 172
177 173 # Creating aliases for marker flags because evolve extension looks for
178 174 # bumpedfix in obsolete.py
179 175 bumpedfix = obsutil.bumpedfix
180 176 usingsha256 = obsutil.usingsha256
181 177
182 178 ## Parsing and writing of version "0"
183 179 #
184 180 # The header is followed by the markers. Each marker is made of:
185 181 #
186 182 # - 1 uint8 : number of new changesets "N", can be zero.
187 183 #
188 184 # - 1 uint32: metadata size "M" in bytes.
189 185 #
190 186 # - 1 byte: a bit field. It is reserved for flags used in common
191 187 # obsolete marker operations, to avoid repeated decoding of metadata
192 188 # entries.
193 189 #
194 190 # - 20 bytes: obsoleted changeset identifier.
195 191 #
196 192 # - N*20 bytes: new changesets identifiers.
197 193 #
198 194 # - M bytes: metadata as a sequence of nul-terminated strings. Each
199 195 # string contains a key and a value, separated by a colon ':', without
200 196 # additional encoding. Keys cannot contain '\0' or ':' and values
201 197 # cannot contain '\0'.
202 198 _fm0version = 0
203 199 _fm0fixed = b'>BIB20s'
204 200 _fm0node = b'20s'
205 201 _fm0fsize = _calcsize(_fm0fixed)
206 202 _fm0fnodesize = _calcsize(_fm0node)
207 203
208 204
209 205 def _fm0readmarkers(data, off, stop):
210 206 # Loop on markers
211 207 while off < stop:
212 208 # read fixed part
213 209 cur = data[off : off + _fm0fsize]
214 210 off += _fm0fsize
215 211 numsuc, mdsize, flags, pre = _unpack(_fm0fixed, cur)
216 212 # read replacement
217 213 sucs = ()
218 214 if numsuc:
219 215 s = _fm0fnodesize * numsuc
220 216 cur = data[off : off + s]
221 217 sucs = _unpack(_fm0node * numsuc, cur)
222 218 off += s
223 219 # read metadata
224 220 # (metadata will be decoded on demand)
225 221 metadata = data[off : off + mdsize]
226 222 if len(metadata) != mdsize:
227 223 raise error.Abort(
228 224 _(
229 225 b'parsing obsolete marker: metadata is too '
230 226 b'short, %d bytes expected, got %d'
231 227 )
232 228 % (mdsize, len(metadata))
233 229 )
234 230 off += mdsize
235 231 metadata = _fm0decodemeta(metadata)
236 232 try:
237 233 when, offset = metadata.pop(b'date', b'0 0').split(b' ')
238 234 date = float(when), int(offset)
239 235 except ValueError:
240 236 date = (0.0, 0)
241 237 parents = None
242 238 if b'p2' in metadata:
243 239 parents = (metadata.pop(b'p1', None), metadata.pop(b'p2', None))
244 240 elif b'p1' in metadata:
245 241 parents = (metadata.pop(b'p1', None),)
246 242 elif b'p0' in metadata:
247 243 parents = ()
248 244 if parents is not None:
249 245 try:
250 246 parents = tuple(bin(p) for p in parents)
251 247 # if parent content is not a nodeid, drop the data
252 248 for p in parents:
253 249 if len(p) != 20:
254 250 parents = None
255 251 break
256 252 except TypeError:
257 253 # if content cannot be translated to nodeid drop the data.
258 254 parents = None
259 255
260 256 metadata = tuple(sorted(pycompat.iteritems(metadata)))
261 257
262 258 yield (pre, sucs, flags, metadata, date, parents)
263 259
264 260
265 261 def _fm0encodeonemarker(marker):
266 262 pre, sucs, flags, metadata, date, parents = marker
267 263 if flags & usingsha256:
268 264 raise error.Abort(_(b'cannot handle sha256 with old obsstore format'))
269 265 metadata = dict(metadata)
270 266 time, tz = date
271 267 metadata[b'date'] = b'%r %i' % (time, tz)
272 268 if parents is not None:
273 269 if not parents:
274 270 # mark that we explicitly recorded no parents
275 271 metadata[b'p0'] = b''
276 272 for i, p in enumerate(parents, 1):
277 273 metadata[b'p%i' % i] = hex(p)
278 274 metadata = _fm0encodemeta(metadata)
279 275 numsuc = len(sucs)
280 276 format = _fm0fixed + (_fm0node * numsuc)
281 277 data = [numsuc, len(metadata), flags, pre]
282 278 data.extend(sucs)
283 279 return _pack(format, *data) + metadata
284 280
285 281
286 282 def _fm0encodemeta(meta):
287 283 """Return encoded metadata string to string mapping.
288 284
289 285 Assume no ':' in key and no '\0' in both key and value."""
290 286 for key, value in pycompat.iteritems(meta):
291 287 if b':' in key or b'\0' in key:
292 288 raise ValueError(b"':' and '\0' are forbidden in metadata key'")
293 289 if b'\0' in value:
294 290 raise ValueError(b"':' is forbidden in metadata value'")
295 291 return b'\0'.join([b'%s:%s' % (k, meta[k]) for k in sorted(meta)])
296 292
297 293
298 294 def _fm0decodemeta(data):
299 295 """Return string to string dictionary from encoded version."""
300 296 d = {}
301 297 for l in data.split(b'\0'):
302 298 if l:
303 299 key, value = l.split(b':', 1)
304 300 d[key] = value
305 301 return d
306 302
307 303
308 304 ## Parsing and writing of version "1"
309 305 #
310 306 # The header is followed by the markers. Each marker is made of:
311 307 #
312 308 # - uint32: total size of the marker (including this field)
313 309 #
314 310 # - float64: date in seconds since epoch
315 311 #
316 312 # - int16: timezone offset in minutes
317 313 #
318 314 # - uint16: a bit field. It is reserved for flags used in common
319 315 # obsolete marker operations, to avoid repeated decoding of metadata
320 316 # entries.
321 317 #
322 318 # - uint8: number of successors "N", can be zero.
323 319 #
324 320 # - uint8: number of parents "P", can be zero.
325 321 #
326 322 # 0: parents data stored but no parent,
327 323 # 1: one parent stored,
328 324 # 2: two parents stored,
329 325 # 3: no parent data stored
330 326 #
331 327 # - uint8: number of metadata entries M
332 328 #
333 329 # - 20 or 32 bytes: predecessor changeset identifier.
334 330 #
335 331 # - N*(20 or 32) bytes: successors changesets identifiers.
336 332 #
337 333 # - P*(20 or 32) bytes: parents of the predecessors changesets.
338 334 #
339 335 # - M*(uint8, uint8): size of all metadata entries (key and value)
340 336 #
341 337 # - remaining bytes: the metadata, each (key, value) pair after the other.
342 338 _fm1version = 1
343 339 _fm1fixed = b'>IdhHBBB'
344 340 _fm1nodesha1 = b'20s'
345 341 _fm1nodesha256 = b'32s'
346 342 _fm1nodesha1size = _calcsize(_fm1nodesha1)
347 343 _fm1nodesha256size = _calcsize(_fm1nodesha256)
348 344 _fm1fsize = _calcsize(_fm1fixed)
349 345 _fm1parentnone = 3
350 346 _fm1parentshift = 14
351 347 _fm1parentmask = _fm1parentnone << _fm1parentshift
352 348 _fm1metapair = b'BB'
353 349 _fm1metapairsize = _calcsize(_fm1metapair)
354 350
355 351
356 352 def _fm1purereadmarkers(data, off, stop):
357 353 # make some global constants local for performance
358 354 noneflag = _fm1parentnone
359 355 sha2flag = usingsha256
360 356 sha1size = _fm1nodesha1size
361 357 sha2size = _fm1nodesha256size
362 358 sha1fmt = _fm1nodesha1
363 359 sha2fmt = _fm1nodesha256
364 360 metasize = _fm1metapairsize
365 361 metafmt = _fm1metapair
366 362 fsize = _fm1fsize
367 363 unpack = _unpack
368 364
369 365 # Loop on markers
370 366 ufixed = struct.Struct(_fm1fixed).unpack
371 367
372 368 while off < stop:
373 369 # read fixed part
374 370 o1 = off + fsize
375 371 t, secs, tz, flags, numsuc, numpar, nummeta = ufixed(data[off:o1])
376 372
377 373 if flags & sha2flag:
378 374 nodefmt = sha2fmt
379 375 nodesize = sha2size
380 376 else:
381 377 nodefmt = sha1fmt
382 378 nodesize = sha1size
383 379
384 380 (prec,) = unpack(nodefmt, data[o1 : o1 + nodesize])
385 381 o1 += nodesize
386 382
387 383 # read 0 or more successors
388 384 if numsuc == 1:
389 385 o2 = o1 + nodesize
390 386 sucs = (data[o1:o2],)
391 387 else:
392 388 o2 = o1 + nodesize * numsuc
393 389 sucs = unpack(nodefmt * numsuc, data[o1:o2])
394 390
395 391 # read parents
396 392 if numpar == noneflag:
397 393 o3 = o2
398 394 parents = None
399 395 elif numpar == 1:
400 396 o3 = o2 + nodesize
401 397 parents = (data[o2:o3],)
402 398 else:
403 399 o3 = o2 + nodesize * numpar
404 400 parents = unpack(nodefmt * numpar, data[o2:o3])
405 401
406 402 # read metadata
407 403 off = o3 + metasize * nummeta
408 404 metapairsize = unpack(b'>' + (metafmt * nummeta), data[o3:off])
409 405 metadata = []
410 406 for idx in pycompat.xrange(0, len(metapairsize), 2):
411 407 o1 = off + metapairsize[idx]
412 408 o2 = o1 + metapairsize[idx + 1]
413 409 metadata.append((data[off:o1], data[o1:o2]))
414 410 off = o2
415 411
416 412 yield (prec, sucs, flags, tuple(metadata), (secs, tz * 60), parents)
417 413
418 414
419 415 def _fm1encodeonemarker(marker):
420 416 pre, sucs, flags, metadata, date, parents = marker
421 417 # determine node size
422 418 _fm1node = _fm1nodesha1
423 419 if flags & usingsha256:
424 420 _fm1node = _fm1nodesha256
425 421 numsuc = len(sucs)
426 422 numextranodes = 1 + numsuc
427 423 if parents is None:
428 424 numpar = _fm1parentnone
429 425 else:
430 426 numpar = len(parents)
431 427 numextranodes += numpar
432 428 formatnodes = _fm1node * numextranodes
433 429 formatmeta = _fm1metapair * len(metadata)
434 430 format = _fm1fixed + formatnodes + formatmeta
435 431 # tz is stored in minutes so we divide by 60
436 432 tz = date[1] // 60
437 433 data = [None, date[0], tz, flags, numsuc, numpar, len(metadata), pre]
438 434 data.extend(sucs)
439 435 if parents is not None:
440 436 data.extend(parents)
441 437 totalsize = _calcsize(format)
442 438 for key, value in metadata:
443 439 lk = len(key)
444 440 lv = len(value)
445 441 if lk > 255:
446 442 msg = (
447 443 b'obsstore metadata key cannot be longer than 255 bytes'
448 444 b' (key "%s" is %u bytes)'
449 445 ) % (key, lk)
450 446 raise error.ProgrammingError(msg)
451 447 if lv > 255:
452 448 msg = (
453 449 b'obsstore metadata value cannot be longer than 255 bytes'
454 450 b' (value "%s" for key "%s" is %u bytes)'
455 451 ) % (value, key, lv)
456 452 raise error.ProgrammingError(msg)
457 453 data.append(lk)
458 454 data.append(lv)
459 455 totalsize += lk + lv
460 456 data[0] = totalsize
461 457 data = [_pack(format, *data)]
462 458 for key, value in metadata:
463 459 data.append(key)
464 460 data.append(value)
465 461 return b''.join(data)
466 462
467 463
468 464 def _fm1readmarkers(data, off, stop):
469 465 native = getattr(parsers, 'fm1readmarkers', None)
470 466 if not native:
471 467 return _fm1purereadmarkers(data, off, stop)
472 468 return native(data, off, stop)
473 469
474 470
475 471 # mapping to read/write various marker formats
476 472 # <version> -> (decoder, encoder)
477 473 formats = {
478 474 _fm0version: (_fm0readmarkers, _fm0encodeonemarker),
479 475 _fm1version: (_fm1readmarkers, _fm1encodeonemarker),
480 476 }
481 477
482 478
483 479 def _readmarkerversion(data):
484 480 return _unpack(b'>B', data[0:1])[0]
485 481
486 482
487 483 @util.nogc
488 484 def _readmarkers(data, off=None, stop=None):
489 485 """Read and enumerate markers from raw data"""
490 486 diskversion = _readmarkerversion(data)
491 487 if not off:
492 488 off = 1 # skip 1 byte version number
493 489 if stop is None:
494 490 stop = len(data)
495 491 if diskversion not in formats:
496 492 msg = _(b'parsing obsolete marker: unknown version %r') % diskversion
497 493 raise error.UnknownVersion(msg, version=diskversion)
498 494 return diskversion, formats[diskversion][0](data, off, stop)
499 495
500 496
501 497 def encodeheader(version=_fm0version):
502 498 return _pack(b'>B', version)
503 499
504 500
505 501 def encodemarkers(markers, addheader=False, version=_fm0version):
506 502 # Kept separate from flushmarkers(), it will be reused for
507 503 # markers exchange.
508 504 encodeone = formats[version][1]
509 505 if addheader:
510 506 yield encodeheader(version)
511 507 for marker in markers:
512 508 yield encodeone(marker)
513 509
514 510
515 511 @util.nogc
516 512 def _addsuccessors(successors, markers):
517 513 for mark in markers:
518 514 successors.setdefault(mark[0], set()).add(mark)
519 515
520 516
521 517 @util.nogc
522 518 def _addpredecessors(predecessors, markers):
523 519 for mark in markers:
524 520 for suc in mark[1]:
525 521 predecessors.setdefault(suc, set()).add(mark)
526 522
527 523
528 524 @util.nogc
529 525 def _addchildren(children, markers):
530 526 for mark in markers:
531 527 parents = mark[5]
532 528 if parents is not None:
533 529 for p in parents:
534 530 children.setdefault(p, set()).add(mark)
535 531
536 532
537 533 def _checkinvalidmarkers(repo, markers):
538 534 """search for marker with invalid data and raise error if needed
539 535
540 536 Exist as a separated function to allow the evolve extension for a more
541 537 subtle handling.
542 538 """
543 539 for mark in markers:
544 540 if repo.nullid in mark[1]:
545 541 raise error.Abort(
546 542 _(
547 543 b'bad obsolescence marker detected: '
548 544 b'invalid successors nullid'
549 545 )
550 546 )
551 547
552 548
553 549 class obsstore(object):
554 550 """Store obsolete markers
555 551
556 552 Markers can be accessed with two mappings:
557 553 - predecessors[x] -> set(markers on predecessors edges of x)
558 554 - successors[x] -> set(markers on successors edges of x)
559 555 - children[x] -> set(markers on predecessors edges of children(x)
560 556 """
561 557
562 558 fields = (b'prec', b'succs', b'flag', b'meta', b'date', b'parents')
563 559 # prec: nodeid, predecessors changesets
564 560 # succs: tuple of nodeid, successor changesets (0-N length)
565 561 # flag: integer, flag field carrying modifier for the markers (see doc)
566 562 # meta: binary blob in UTF-8, encoded metadata dictionary
567 563 # date: (float, int) tuple, date of marker creation
568 564 # parents: (tuple of nodeid) or None, parents of predecessors
569 565 # None is used when no data has been recorded
570 566
571 567 def __init__(self, repo, svfs, defaultformat=_fm1version, readonly=False):
572 568 # caches for various obsolescence related cache
573 569 self.caches = {}
574 570 self.svfs = svfs
575 571 self.repo = repo
576 572 self._defaultformat = defaultformat
577 573 self._readonly = readonly
578 574
579 575 def __iter__(self):
580 576 return iter(self._all)
581 577
582 578 def __len__(self):
583 579 return len(self._all)
584 580
585 581 def __nonzero__(self):
586 582 if not self._cached('_all'):
587 583 try:
588 584 return self.svfs.stat(b'obsstore').st_size > 1
589 585 except OSError as inst:
590 586 if inst.errno != errno.ENOENT:
591 587 raise
592 588 # just build an empty _all list if no obsstore exists, which
593 589 # avoids further stat() syscalls
594 590 return bool(self._all)
595 591
596 592 __bool__ = __nonzero__
597 593
598 594 @property
599 595 def readonly(self):
600 596 """True if marker creation is disabled
601 597
602 598 Remove me in the future when obsolete marker is always on."""
603 599 return self._readonly
604 600
605 601 def create(
606 602 self,
607 603 transaction,
608 604 prec,
609 605 succs=(),
610 606 flag=0,
611 607 parents=None,
612 608 date=None,
613 609 metadata=None,
614 610 ui=None,
615 611 ):
616 612 """obsolete: add a new obsolete marker
617 613
618 614 * ensuring it is hashable
619 615 * check mandatory metadata
620 616 * encode metadata
621 617
622 618 If you are a human writing code creating marker you want to use the
623 619 `createmarkers` function in this module instead.
624 620
625 621 return True if a new marker have been added, False if the markers
626 622 already existed (no op).
627 623 """
628 624 flag = int(flag)
629 625 if metadata is None:
630 626 metadata = {}
631 627 if date is None:
632 628 if b'date' in metadata:
633 629 # as a courtesy for out-of-tree extensions
634 630 date = dateutil.parsedate(metadata.pop(b'date'))
635 631 elif ui is not None:
636 632 date = ui.configdate(b'devel', b'default-date')
637 633 if date is None:
638 634 date = dateutil.makedate()
639 635 else:
640 636 date = dateutil.makedate()
641 637 if flag & usingsha256:
642 638 if len(prec) != 32:
643 639 raise ValueError(prec)
644 640 for succ in succs:
645 641 if len(succ) != 32:
646 642 raise ValueError(succ)
647 643 else:
648 644 if len(prec) != 20:
649 645 raise ValueError(prec)
650 646 for succ in succs:
651 647 if len(succ) != 20:
652 648 raise ValueError(succ)
653 649 if prec in succs:
654 650 raise ValueError(
655 651 'in-marker cycle with %s' % pycompat.sysstr(hex(prec))
656 652 )
657 653
658 654 metadata = tuple(sorted(pycompat.iteritems(metadata)))
659 655 for k, v in metadata:
660 656 try:
661 657 # might be better to reject non-ASCII keys
662 658 k.decode('utf-8')
663 659 v.decode('utf-8')
664 660 except UnicodeDecodeError:
665 661 raise error.ProgrammingError(
666 662 b'obsstore metadata must be valid UTF-8 sequence '
667 663 b'(key = %r, value = %r)'
668 664 % (pycompat.bytestr(k), pycompat.bytestr(v))
669 665 )
670 666
671 667 marker = (bytes(prec), tuple(succs), flag, metadata, date, parents)
672 668 return bool(self.add(transaction, [marker]))
673 669
674 670 def add(self, transaction, markers):
675 671 """Add new markers to the store
676 672
677 673 Take care of filtering duplicate.
678 674 Return the number of new marker."""
679 675 if self._readonly:
680 676 raise error.Abort(
681 677 _(b'creating obsolete markers is not enabled on this repo')
682 678 )
683 679 known = set()
684 680 getsuccessors = self.successors.get
685 681 new = []
686 682 for m in markers:
687 683 if m not in getsuccessors(m[0], ()) and m not in known:
688 684 known.add(m)
689 685 new.append(m)
690 686 if new:
691 687 f = self.svfs(b'obsstore', b'ab')
692 688 try:
693 689 offset = f.tell()
694 690 transaction.add(b'obsstore', offset)
695 691 # offset == 0: new file - add the version header
696 692 data = b''.join(encodemarkers(new, offset == 0, self._version))
697 693 f.write(data)
698 694 finally:
699 695 # XXX: f.close() == filecache invalidation == obsstore rebuilt.
700 696 # call 'filecacheentry.refresh()' here
701 697 f.close()
702 698 addedmarkers = transaction.changes.get(b'obsmarkers')
703 699 if addedmarkers is not None:
704 700 addedmarkers.update(new)
705 701 self._addmarkers(new, data)
706 702 # new marker *may* have changed several set. invalidate the cache.
707 703 self.caches.clear()
708 704 # records the number of new markers for the transaction hooks
709 705 previous = int(transaction.hookargs.get(b'new_obsmarkers', b'0'))
710 706 transaction.hookargs[b'new_obsmarkers'] = b'%d' % (previous + len(new))
711 707 return len(new)
712 708
713 709 def mergemarkers(self, transaction, data):
714 710 """merge a binary stream of markers inside the obsstore
715 711
716 712 Returns the number of new markers added."""
717 713 version, markers = _readmarkers(data)
718 714 return self.add(transaction, markers)
719 715
720 716 @propertycache
721 717 def _data(self):
722 718 return self.svfs.tryread(b'obsstore')
723 719
724 720 @propertycache
725 721 def _version(self):
726 722 if len(self._data) >= 1:
727 723 return _readmarkerversion(self._data)
728 724 else:
729 725 return self._defaultformat
730 726
731 727 @propertycache
732 728 def _all(self):
733 729 data = self._data
734 730 if not data:
735 731 return []
736 732 self._version, markers = _readmarkers(data)
737 733 markers = list(markers)
738 734 _checkinvalidmarkers(self.repo, markers)
739 735 return markers
740 736
741 737 @propertycache
742 738 def successors(self):
743 739 successors = {}
744 740 _addsuccessors(successors, self._all)
745 741 return successors
746 742
747 743 @propertycache
748 744 def predecessors(self):
749 745 predecessors = {}
750 746 _addpredecessors(predecessors, self._all)
751 747 return predecessors
752 748
753 749 @propertycache
754 750 def children(self):
755 751 children = {}
756 752 _addchildren(children, self._all)
757 753 return children
758 754
759 755 def _cached(self, attr):
760 756 return attr in self.__dict__
761 757
762 758 def _addmarkers(self, markers, rawdata):
763 759 markers = list(markers) # to allow repeated iteration
764 760 self._data = self._data + rawdata
765 761 self._all.extend(markers)
766 762 if self._cached('successors'):
767 763 _addsuccessors(self.successors, markers)
768 764 if self._cached('predecessors'):
769 765 _addpredecessors(self.predecessors, markers)
770 766 if self._cached('children'):
771 767 _addchildren(self.children, markers)
772 768 _checkinvalidmarkers(self.repo, markers)
773 769
774 770 def relevantmarkers(self, nodes):
775 771 """return a set of all obsolescence markers relevant to a set of nodes.
776 772
777 773 "relevant" to a set of nodes mean:
778 774
779 775 - marker that use this changeset as successor
780 776 - prune marker of direct children on this changeset
781 777 - recursive application of the two rules on predecessors of these
782 778 markers
783 779
784 780 It is a set so you cannot rely on order."""
785 781
786 782 pendingnodes = set(nodes)
787 783 seenmarkers = set()
788 784 seennodes = set(pendingnodes)
789 785 precursorsmarkers = self.predecessors
790 786 succsmarkers = self.successors
791 787 children = self.children
792 788 while pendingnodes:
793 789 direct = set()
794 790 for current in pendingnodes:
795 791 direct.update(precursorsmarkers.get(current, ()))
796 792 pruned = [m for m in children.get(current, ()) if not m[1]]
797 793 direct.update(pruned)
798 794 pruned = [m for m in succsmarkers.get(current, ()) if not m[1]]
799 795 direct.update(pruned)
800 796 direct -= seenmarkers
801 797 pendingnodes = {m[0] for m in direct}
802 798 seenmarkers |= direct
803 799 pendingnodes -= seennodes
804 800 seennodes |= pendingnodes
805 801 return seenmarkers
806 802
807 803
808 804 def makestore(ui, repo):
809 805 """Create an obsstore instance from a repo."""
810 806 # read default format for new obsstore.
811 807 # developer config: format.obsstore-version
812 808 defaultformat = ui.configint(b'format', b'obsstore-version')
813 809 # rely on obsstore class default when possible.
814 810 kwargs = {}
815 811 if defaultformat is not None:
816 812 kwargs['defaultformat'] = defaultformat
817 813 readonly = not isenabled(repo, createmarkersopt)
818 814 store = obsstore(repo, repo.svfs, readonly=readonly, **kwargs)
819 815 if store and readonly:
820 816 ui.warn(
821 817 _(b'obsolete feature not enabled but %i markers found!\n')
822 818 % len(list(store))
823 819 )
824 820 return store
825 821
826 822
827 823 def commonversion(versions):
828 824 """Return the newest version listed in both versions and our local formats.
829 825
830 826 Returns None if no common version exists.
831 827 """
832 828 versions.sort(reverse=True)
833 829 # search for highest version known on both side
834 830 for v in versions:
835 831 if v in formats:
836 832 return v
837 833 return None
838 834
839 835
840 836 # arbitrary picked to fit into 8K limit from HTTP server
841 837 # you have to take in account:
842 838 # - the version header
843 839 # - the base85 encoding
844 840 _maxpayload = 5300
845 841
846 842
847 843 def _pushkeyescape(markers):
848 844 """encode markers into a dict suitable for pushkey exchange
849 845
850 846 - binary data is base85 encoded
851 847 - split in chunks smaller than 5300 bytes"""
852 848 keys = {}
853 849 parts = []
854 850 currentlen = _maxpayload * 2 # ensure we create a new part
855 851 for marker in markers:
856 852 nextdata = _fm0encodeonemarker(marker)
857 853 if len(nextdata) + currentlen > _maxpayload:
858 854 currentpart = []
859 855 currentlen = 0
860 856 parts.append(currentpart)
861 857 currentpart.append(nextdata)
862 858 currentlen += len(nextdata)
863 859 for idx, part in enumerate(reversed(parts)):
864 860 data = b''.join([_pack(b'>B', _fm0version)] + part)
865 861 keys[b'dump%i' % idx] = util.b85encode(data)
866 862 return keys
867 863
868 864
869 865 def listmarkers(repo):
870 866 """List markers over pushkey"""
871 867 if not repo.obsstore:
872 868 return {}
873 869 return _pushkeyescape(sorted(repo.obsstore))
874 870
875 871
876 872 def pushmarker(repo, key, old, new):
877 873 """Push markers over pushkey"""
878 874 if not key.startswith(b'dump'):
879 875 repo.ui.warn(_(b'unknown key: %r') % key)
880 876 return False
881 877 if old:
882 878 repo.ui.warn(_(b'unexpected old value for %r') % key)
883 879 return False
884 880 data = util.b85decode(new)
885 881 with repo.lock(), repo.transaction(b'pushkey: obsolete markers') as tr:
886 882 repo.obsstore.mergemarkers(tr, data)
887 883 repo.invalidatevolatilesets()
888 884 return True
889 885
890 886
891 887 # mapping of 'set-name' -> <function to compute this set>
892 888 cachefuncs = {}
893 889
894 890
895 891 def cachefor(name):
896 892 """Decorator to register a function as computing the cache for a set"""
897 893
898 894 def decorator(func):
899 895 if name in cachefuncs:
900 896 msg = b"duplicated registration for volatileset '%s' (existing: %r)"
901 897 raise error.ProgrammingError(msg % (name, cachefuncs[name]))
902 898 cachefuncs[name] = func
903 899 return func
904 900
905 901 return decorator
906 902
907 903
908 904 def getrevs(repo, name):
909 905 """Return the set of revision that belong to the <name> set
910 906
911 907 Such access may compute the set and cache it for future use"""
912 908 repo = repo.unfiltered()
913 909 with util.timedcm('getrevs %s', name):
914 910 if not repo.obsstore:
915 911 return frozenset()
916 912 if name not in repo.obsstore.caches:
917 913 repo.obsstore.caches[name] = cachefuncs[name](repo)
918 914 return repo.obsstore.caches[name]
919 915
920 916
921 917 # To be simple we need to invalidate obsolescence cache when:
922 918 #
923 919 # - new changeset is added:
924 920 # - public phase is changed
925 921 # - obsolescence marker are added
926 922 # - strip is used a repo
927 923 def clearobscaches(repo):
928 924 """Remove all obsolescence related cache from a repo
929 925
930 926 This remove all cache in obsstore is the obsstore already exist on the
931 927 repo.
932 928
933 929 (We could be smarter here given the exact event that trigger the cache
934 930 clearing)"""
935 931 # only clear cache is there is obsstore data in this repo
936 932 if b'obsstore' in repo._filecache:
937 933 repo.obsstore.caches.clear()
938 934
939 935
940 936 def _mutablerevs(repo):
941 937 """the set of mutable revision in the repository"""
942 938 return repo._phasecache.getrevset(repo, phases.mutablephases)
943 939
944 940
945 941 @cachefor(b'obsolete')
946 942 def _computeobsoleteset(repo):
947 943 """the set of obsolete revisions"""
948 944 getnode = repo.changelog.node
949 945 notpublic = _mutablerevs(repo)
950 946 isobs = repo.obsstore.successors.__contains__
951 947 obs = {r for r in notpublic if isobs(getnode(r))}
952 948 return obs
953 949
954 950
955 951 @cachefor(b'orphan')
956 952 def _computeorphanset(repo):
957 953 """the set of non obsolete revisions with obsolete parents"""
958 954 pfunc = repo.changelog.parentrevs
959 955 mutable = _mutablerevs(repo)
960 956 obsolete = getrevs(repo, b'obsolete')
961 957 others = mutable - obsolete
962 958 unstable = set()
963 959 for r in sorted(others):
964 960 # A rev is unstable if one of its parent is obsolete or unstable
965 961 # this works since we traverse following growing rev order
966 962 for p in pfunc(r):
967 963 if p in obsolete or p in unstable:
968 964 unstable.add(r)
969 965 break
970 966 return unstable
971 967
972 968
973 969 @cachefor(b'suspended')
974 970 def _computesuspendedset(repo):
975 971 """the set of obsolete parents with non obsolete descendants"""
976 972 suspended = repo.changelog.ancestors(getrevs(repo, b'orphan'))
977 973 return {r for r in getrevs(repo, b'obsolete') if r in suspended}
978 974
979 975
980 976 @cachefor(b'extinct')
981 977 def _computeextinctset(repo):
982 978 """the set of obsolete parents without non obsolete descendants"""
983 979 return getrevs(repo, b'obsolete') - getrevs(repo, b'suspended')
984 980
985 981
986 982 @cachefor(b'phasedivergent')
987 983 def _computephasedivergentset(repo):
988 984 """the set of revs trying to obsolete public revisions"""
989 985 bumped = set()
990 986 # util function (avoid attribute lookup in the loop)
991 987 phase = repo._phasecache.phase # would be faster to grab the full list
992 988 public = phases.public
993 989 cl = repo.changelog
994 990 torev = cl.index.get_rev
995 991 tonode = cl.node
996 992 obsstore = repo.obsstore
997 993 for rev in repo.revs(b'(not public()) and (not obsolete())'):
998 994 # We only evaluate mutable, non-obsolete revision
999 995 node = tonode(rev)
1000 996 # (future) A cache of predecessors may worth if split is very common
1001 997 for pnode in obsutil.allpredecessors(
1002 998 obsstore, [node], ignoreflags=bumpedfix
1003 999 ):
1004 1000 prev = torev(pnode) # unfiltered! but so is phasecache
1005 1001 if (prev is not None) and (phase(repo, prev) <= public):
1006 1002 # we have a public predecessor
1007 1003 bumped.add(rev)
1008 1004 break # Next draft!
1009 1005 return bumped
1010 1006
1011 1007
1012 1008 @cachefor(b'contentdivergent')
1013 1009 def _computecontentdivergentset(repo):
1014 1010 """the set of rev that compete to be the final successors of some revision."""
1015 1011 divergent = set()
1016 1012 obsstore = repo.obsstore
1017 1013 newermap = {}
1018 1014 tonode = repo.changelog.node
1019 1015 for rev in repo.revs(b'(not public()) - obsolete()'):
1020 1016 node = tonode(rev)
1021 1017 mark = obsstore.predecessors.get(node, ())
1022 1018 toprocess = set(mark)
1023 1019 seen = set()
1024 1020 while toprocess:
1025 1021 prec = toprocess.pop()[0]
1026 1022 if prec in seen:
1027 1023 continue # emergency cycle hanging prevention
1028 1024 seen.add(prec)
1029 1025 if prec not in newermap:
1030 1026 obsutil.successorssets(repo, prec, cache=newermap)
1031 1027 newer = [n for n in newermap[prec] if n]
1032 1028 if len(newer) > 1:
1033 1029 divergent.add(rev)
1034 1030 break
1035 1031 toprocess.update(obsstore.predecessors.get(prec, ()))
1036 1032 return divergent
1037 1033
1038 1034
1039 1035 def makefoldid(relation, user):
1040 1036
1041 1037 folddigest = hashutil.sha1(user)
1042 1038 for p in relation[0] + relation[1]:
1043 1039 folddigest.update(b'%d' % p.rev())
1044 1040 folddigest.update(p.node())
1045 1041 # Since fold only has to compete against fold for the same successors, it
1046 1042 # seems fine to use a small ID. Smaller ID save space.
1047 1043 return hex(folddigest.digest())[:8]
1048 1044
1049 1045
1050 1046 def createmarkers(
1051 1047 repo, relations, flag=0, date=None, metadata=None, operation=None
1052 1048 ):
1053 1049 """Add obsolete markers between changesets in a repo
1054 1050
1055 1051 <relations> must be an iterable of ((<old>,...), (<new>, ...)[,{metadata}])
1056 1052 tuple. `old` and `news` are changectx. metadata is an optional dictionary
1057 1053 containing metadata for this marker only. It is merged with the global
1058 1054 metadata specified through the `metadata` argument of this function.
1059 1055 Any string values in metadata must be UTF-8 bytes.
1060 1056
1061 1057 Trying to obsolete a public changeset will raise an exception.
1062 1058
1063 1059 Current user and date are used except if specified otherwise in the
1064 1060 metadata attribute.
1065 1061
1066 1062 This function operates within a transaction of its own, but does
1067 1063 not take any lock on the repo.
1068 1064 """
1069 1065 # prepare metadata
1070 1066 if metadata is None:
1071 1067 metadata = {}
1072 1068 if b'user' not in metadata:
1073 1069 luser = (
1074 1070 repo.ui.config(b'devel', b'user.obsmarker') or repo.ui.username()
1075 1071 )
1076 1072 metadata[b'user'] = encoding.fromlocal(luser)
1077 1073
1078 1074 # Operation metadata handling
1079 1075 useoperation = repo.ui.configbool(
1080 1076 b'experimental', b'evolution.track-operation'
1081 1077 )
1082 1078 if useoperation and operation:
1083 1079 metadata[b'operation'] = operation
1084 1080
1085 1081 # Effect flag metadata handling
1086 1082 saveeffectflag = repo.ui.configbool(
1087 1083 b'experimental', b'evolution.effect-flags'
1088 1084 )
1089 1085
1090 1086 with repo.transaction(b'add-obsolescence-marker') as tr:
1091 1087 markerargs = []
1092 1088 for rel in relations:
1093 1089 predecessors = rel[0]
1094 1090 if not isinstance(predecessors, tuple):
1095 1091 # preserve compat with old API until all caller are migrated
1096 1092 predecessors = (predecessors,)
1097 1093 if len(predecessors) > 1 and len(rel[1]) != 1:
1098 1094 msg = b'Fold markers can only have 1 successors, not %d'
1099 1095 raise error.ProgrammingError(msg % len(rel[1]))
1100 1096 foldid = None
1101 1097 foldsize = len(predecessors)
1102 1098 if 1 < foldsize:
1103 1099 foldid = makefoldid(rel, metadata[b'user'])
1104 1100 for foldidx, prec in enumerate(predecessors, 1):
1105 1101 sucs = rel[1]
1106 1102 localmetadata = metadata.copy()
1107 1103 if len(rel) > 2:
1108 1104 localmetadata.update(rel[2])
1109 1105 if foldid is not None:
1110 1106 localmetadata[b'fold-id'] = foldid
1111 1107 localmetadata[b'fold-idx'] = b'%d' % foldidx
1112 1108 localmetadata[b'fold-size'] = b'%d' % foldsize
1113 1109
1114 1110 if not prec.mutable():
1115 1111 raise error.Abort(
1116 1112 _(b"cannot obsolete public changeset: %s") % prec,
1117 1113 hint=b"see 'hg help phases' for details",
1118 1114 )
1119 1115 nprec = prec.node()
1120 1116 nsucs = tuple(s.node() for s in sucs)
1121 1117 npare = None
1122 1118 if not nsucs:
1123 1119 npare = tuple(p.node() for p in prec.parents())
1124 1120 if nprec in nsucs:
1125 1121 raise error.Abort(
1126 1122 _(b"changeset %s cannot obsolete itself") % prec
1127 1123 )
1128 1124
1129 1125 # Effect flag can be different by relation
1130 1126 if saveeffectflag:
1131 1127 # The effect flag is saved in a versioned field name for
1132 1128 # future evolution
1133 1129 effectflag = obsutil.geteffectflag(prec, sucs)
1134 1130 localmetadata[obsutil.EFFECTFLAGFIELD] = b"%d" % effectflag
1135 1131
1136 1132 # Creating the marker causes the hidden cache to become
1137 1133 # invalid, which causes recomputation when we ask for
1138 1134 # prec.parents() above. Resulting in n^2 behavior. So let's
1139 1135 # prepare all of the args first, then create the markers.
1140 1136 markerargs.append((nprec, nsucs, npare, localmetadata))
1141 1137
1142 1138 for args in markerargs:
1143 1139 nprec, nsucs, npare, localmetadata = args
1144 1140 repo.obsstore.create(
1145 1141 tr,
1146 1142 nprec,
1147 1143 nsucs,
1148 1144 flag,
1149 1145 parents=npare,
1150 1146 date=date,
1151 1147 metadata=localmetadata,
1152 1148 ui=repo.ui,
1153 1149 )
1154 1150 repo.filteredrevcache.clear()
@@ -1,1308 +1,1326
1 1 #testcases sshv1 sshv2
2 2
3 3 #if sshv2
4 4 $ cat >> $HGRCPATH << EOF
5 5 > [experimental]
6 6 > sshpeer.advertise-v2 = true
7 7 > sshserver.support-v2 = true
8 8 > EOF
9 9 #endif
10 10
11 11 Prepare repo a:
12 12
13 13 $ hg init a
14 14 $ cd a
15 15 $ echo a > a
16 16 $ hg add a
17 17 $ hg commit -m test
18 18 $ echo first line > b
19 19 $ hg add b
20 20
21 21 Create a non-inlined filelog:
22 22
23 23 $ "$PYTHON" -c 'open("data1", "wb").write(b"".join(b"%d\n" % x for x in range(10000)))'
24 24 $ for j in 0 1 2 3 4 5 6 7 8 9; do
25 25 > cat data1 >> b
26 26 > hg commit -m test
27 27 > done
28 28
29 29 List files in store/data (should show a 'b.d'):
30 30
31 31 #if reporevlogstore
32 32 $ for i in .hg/store/data/*; do
33 33 > echo $i
34 34 > done
35 35 .hg/store/data/a.i
36 36 .hg/store/data/b.d
37 37 .hg/store/data/b.i
38 38 #endif
39 39
40 40 Trigger branchcache creation:
41 41
42 42 $ hg branches
43 43 default 10:a7949464abda
44 44 $ ls .hg/cache
45 45 branch2-served
46 46 rbc-names-v1
47 47 rbc-revs-v1
48 48
49 49 Default operation:
50 50
51 51 $ hg clone . ../b
52 52 updating to branch default
53 53 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
54 54 $ cd ../b
55 55
56 56 Ensure branchcache got copied over:
57 57
58 58 $ ls .hg/cache
59 59 branch2-base
60 60 branch2-immutable
61 61 branch2-served
62 62 branch2-served.hidden
63 63 branch2-visible
64 64 branch2-visible-hidden
65 65 rbc-names-v1
66 66 rbc-revs-v1
67 67 tags2
68 68 tags2-served
69 69
70 70 $ cat a
71 71 a
72 72 $ hg verify
73 73 checking changesets
74 74 checking manifests
75 75 crosschecking files in changesets and manifests
76 76 checking files
77 77 checked 11 changesets with 11 changes to 2 files
78 78
79 79 Invalid dest '' must abort:
80 80
81 81 $ hg clone . ''
82 82 abort: empty destination path is not valid
83 83 [10]
84 84
85 85 No update, with debug option:
86 86
87 87 #if hardlink
88 88 $ hg --debug clone -U . ../c --config progress.debug=true
89 89 linking: 1/15 files (6.67%)
90 90 linking: 2/15 files (13.33%)
91 91 linking: 3/15 files (20.00%)
92 92 linking: 4/15 files (26.67%)
93 93 linking: 5/15 files (33.33%)
94 94 linking: 6/15 files (40.00%)
95 95 linking: 7/15 files (46.67%)
96 96 linking: 8/15 files (53.33%)
97 97 linking: 9/15 files (60.00%)
98 98 linking: 10/15 files (66.67%)
99 99 linking: 11/15 files (73.33%)
100 100 linking: 12/15 files (80.00%)
101 101 linking: 13/15 files (86.67%)
102 102 linking: 14/15 files (93.33%)
103 103 linking: 15/15 files (100.00%)
104 104 linked 15 files
105 105 updating the branch cache
106 106 #else
107 107 $ hg --debug clone -U . ../c --config progress.debug=true
108 108 linking: 1 files
109 109 copying: 2 files
110 110 copying: 3 files
111 111 copying: 4 files
112 112 copying: 5 files
113 113 copying: 6 files
114 114 copying: 7 files
115 115 copying: 8 files
116 116 #endif
117 117 $ cd ../c
118 118
119 119 Ensure branchcache got copied over:
120 120
121 121 $ ls .hg/cache
122 122 branch2-base
123 123 branch2-immutable
124 124 branch2-served
125 125 branch2-served.hidden
126 126 branch2-visible
127 127 branch2-visible-hidden
128 128 rbc-names-v1
129 129 rbc-revs-v1
130 130 tags2
131 131 tags2-served
132 132
133 133 $ cat a 2>/dev/null || echo "a not present"
134 134 a not present
135 135 $ hg verify
136 136 checking changesets
137 137 checking manifests
138 138 crosschecking files in changesets and manifests
139 139 checking files
140 140 checked 11 changesets with 11 changes to 2 files
141 141
142 142 Default destination:
143 143
144 144 $ mkdir ../d
145 145 $ cd ../d
146 146 $ hg clone ../a
147 147 destination directory: a
148 148 updating to branch default
149 149 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
150 150 $ cd a
151 151 $ hg cat a
152 152 a
153 153 $ cd ../..
154 154
155 155 Check that we drop the 'file:' from the path before writing the .hgrc:
156 156
157 157 $ hg clone file:a e
158 158 updating to branch default
159 159 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
160 160 $ grep 'file:' e/.hg/hgrc
161 161 [1]
162 162
163 163 Check that path aliases are expanded:
164 164
165 165 $ hg clone -q -U --config 'paths.foobar=a#0' foobar f
166 166 $ hg -R f showconfig paths.default
167 167 $TESTTMP/a#0
168 168
169 169 Use --pull:
170 170
171 171 $ hg clone --pull a g
172 172 requesting all changes
173 173 adding changesets
174 174 adding manifests
175 175 adding file changes
176 176 added 11 changesets with 11 changes to 2 files
177 177 new changesets acb14030fe0a:a7949464abda
178 178 updating to branch default
179 179 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
180 180 $ hg -R g verify
181 181 checking changesets
182 182 checking manifests
183 183 crosschecking files in changesets and manifests
184 184 checking files
185 185 checked 11 changesets with 11 changes to 2 files
186 186
187 187 Invalid dest '' with --pull must abort (issue2528):
188 188
189 189 $ hg clone --pull a ''
190 190 abort: empty destination path is not valid
191 191 [10]
192 192
193 193 Clone to '.':
194 194
195 195 $ mkdir h
196 196 $ cd h
197 197 $ hg clone ../a .
198 198 updating to branch default
199 199 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
200 200 $ cd ..
201 201
202 202
203 203 *** Tests for option -u ***
204 204
205 205 Adding some more history to repo a:
206 206
207 207 $ cd a
208 208 $ hg tag ref1
209 209 $ echo the quick brown fox >a
210 210 $ hg ci -m "hacked default"
211 211 $ hg up ref1
212 212 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
213 213 $ hg branch stable
214 214 marked working directory as branch stable
215 215 (branches are permanent and global, did you want a bookmark?)
216 216 $ echo some text >a
217 217 $ hg ci -m "starting branch stable"
218 218 $ hg tag ref2
219 219 $ echo some more text >a
220 220 $ hg ci -m "another change for branch stable"
221 221 $ hg up ref2
222 222 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
223 223 $ hg parents
224 224 changeset: 13:e8ece76546a6
225 225 branch: stable
226 226 tag: ref2
227 227 parent: 10:a7949464abda
228 228 user: test
229 229 date: Thu Jan 01 00:00:00 1970 +0000
230 230 summary: starting branch stable
231 231
232 232
233 233 Repo a has two heads:
234 234
235 235 $ hg heads
236 236 changeset: 15:0aae7cf88f0d
237 237 branch: stable
238 238 tag: tip
239 239 user: test
240 240 date: Thu Jan 01 00:00:00 1970 +0000
241 241 summary: another change for branch stable
242 242
243 243 changeset: 12:f21241060d6a
244 244 user: test
245 245 date: Thu Jan 01 00:00:00 1970 +0000
246 246 summary: hacked default
247 247
248 248
249 249 $ cd ..
250 250
251 251
252 252 Testing --noupdate with --updaterev (must abort):
253 253
254 254 $ hg clone --noupdate --updaterev 1 a ua
255 255 abort: cannot specify both --noupdate and --updaterev
256 256 [10]
257 257
258 258
259 259 Testing clone -u:
260 260
261 261 $ hg clone -u . a ua
262 262 updating to branch stable
263 263 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
264 264
265 265 Repo ua has both heads:
266 266
267 267 $ hg -R ua heads
268 268 changeset: 15:0aae7cf88f0d
269 269 branch: stable
270 270 tag: tip
271 271 user: test
272 272 date: Thu Jan 01 00:00:00 1970 +0000
273 273 summary: another change for branch stable
274 274
275 275 changeset: 12:f21241060d6a
276 276 user: test
277 277 date: Thu Jan 01 00:00:00 1970 +0000
278 278 summary: hacked default
279 279
280 280
281 281 Same revision checked out in repo a and ua:
282 282
283 283 $ hg -R a parents --template "{node|short}\n"
284 284 e8ece76546a6
285 285 $ hg -R ua parents --template "{node|short}\n"
286 286 e8ece76546a6
287 287
288 288 $ rm -r ua
289 289
290 290
291 291 Testing clone --pull -u:
292 292
293 293 $ hg clone --pull -u . a ua
294 294 requesting all changes
295 295 adding changesets
296 296 adding manifests
297 297 adding file changes
298 298 added 16 changesets with 16 changes to 3 files (+1 heads)
299 299 new changesets acb14030fe0a:0aae7cf88f0d
300 300 updating to branch stable
301 301 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
302 302
303 303 Repo ua has both heads:
304 304
305 305 $ hg -R ua heads
306 306 changeset: 15:0aae7cf88f0d
307 307 branch: stable
308 308 tag: tip
309 309 user: test
310 310 date: Thu Jan 01 00:00:00 1970 +0000
311 311 summary: another change for branch stable
312 312
313 313 changeset: 12:f21241060d6a
314 314 user: test
315 315 date: Thu Jan 01 00:00:00 1970 +0000
316 316 summary: hacked default
317 317
318 318
319 319 Same revision checked out in repo a and ua:
320 320
321 321 $ hg -R a parents --template "{node|short}\n"
322 322 e8ece76546a6
323 323 $ hg -R ua parents --template "{node|short}\n"
324 324 e8ece76546a6
325 325
326 326 $ rm -r ua
327 327
328 328
329 329 Testing clone -u <branch>:
330 330
331 331 $ hg clone -u stable a ua
332 332 updating to branch stable
333 333 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
334 334
335 335 Repo ua has both heads:
336 336
337 337 $ hg -R ua heads
338 338 changeset: 15:0aae7cf88f0d
339 339 branch: stable
340 340 tag: tip
341 341 user: test
342 342 date: Thu Jan 01 00:00:00 1970 +0000
343 343 summary: another change for branch stable
344 344
345 345 changeset: 12:f21241060d6a
346 346 user: test
347 347 date: Thu Jan 01 00:00:00 1970 +0000
348 348 summary: hacked default
349 349
350 350
351 351 Branch 'stable' is checked out:
352 352
353 353 $ hg -R ua parents
354 354 changeset: 15:0aae7cf88f0d
355 355 branch: stable
356 356 tag: tip
357 357 user: test
358 358 date: Thu Jan 01 00:00:00 1970 +0000
359 359 summary: another change for branch stable
360 360
361 361
362 362 $ rm -r ua
363 363
364 364
365 365 Testing default checkout:
366 366
367 367 $ hg clone a ua
368 368 updating to branch default
369 369 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
370 370
371 371 Repo ua has both heads:
372 372
373 373 $ hg -R ua heads
374 374 changeset: 15:0aae7cf88f0d
375 375 branch: stable
376 376 tag: tip
377 377 user: test
378 378 date: Thu Jan 01 00:00:00 1970 +0000
379 379 summary: another change for branch stable
380 380
381 381 changeset: 12:f21241060d6a
382 382 user: test
383 383 date: Thu Jan 01 00:00:00 1970 +0000
384 384 summary: hacked default
385 385
386 386
387 387 Branch 'default' is checked out:
388 388
389 389 $ hg -R ua parents
390 390 changeset: 12:f21241060d6a
391 391 user: test
392 392 date: Thu Jan 01 00:00:00 1970 +0000
393 393 summary: hacked default
394 394
395 395 Test clone with a branch named "@" (issue3677)
396 396
397 397 $ hg -R ua branch @
398 398 marked working directory as branch @
399 399 $ hg -R ua commit -m 'created branch @'
400 400 $ hg clone ua atbranch
401 401 updating to branch default
402 402 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
403 403 $ hg -R atbranch heads
404 404 changeset: 16:798b6d97153e
405 405 branch: @
406 406 tag: tip
407 407 parent: 12:f21241060d6a
408 408 user: test
409 409 date: Thu Jan 01 00:00:00 1970 +0000
410 410 summary: created branch @
411 411
412 412 changeset: 15:0aae7cf88f0d
413 413 branch: stable
414 414 user: test
415 415 date: Thu Jan 01 00:00:00 1970 +0000
416 416 summary: another change for branch stable
417 417
418 418 changeset: 12:f21241060d6a
419 419 user: test
420 420 date: Thu Jan 01 00:00:00 1970 +0000
421 421 summary: hacked default
422 422
423 423 $ hg -R atbranch parents
424 424 changeset: 12:f21241060d6a
425 425 user: test
426 426 date: Thu Jan 01 00:00:00 1970 +0000
427 427 summary: hacked default
428 428
429 429
430 430 $ rm -r ua atbranch
431 431
432 432
433 433 Testing #<branch>:
434 434
435 435 $ hg clone -u . a#stable ua
436 436 adding changesets
437 437 adding manifests
438 438 adding file changes
439 439 added 14 changesets with 14 changes to 3 files
440 440 new changesets acb14030fe0a:0aae7cf88f0d
441 441 updating to branch stable
442 442 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
443 443
444 444 Repo ua has branch 'stable' and 'default' (was changed in fd511e9eeea6):
445 445
446 446 $ hg -R ua heads
447 447 changeset: 13:0aae7cf88f0d
448 448 branch: stable
449 449 tag: tip
450 450 user: test
451 451 date: Thu Jan 01 00:00:00 1970 +0000
452 452 summary: another change for branch stable
453 453
454 454 changeset: 10:a7949464abda
455 455 user: test
456 456 date: Thu Jan 01 00:00:00 1970 +0000
457 457 summary: test
458 458
459 459
460 460 Same revision checked out in repo a and ua:
461 461
462 462 $ hg -R a parents --template "{node|short}\n"
463 463 e8ece76546a6
464 464 $ hg -R ua parents --template "{node|short}\n"
465 465 e8ece76546a6
466 466
467 467 $ rm -r ua
468 468
469 469
470 470 Testing -u -r <branch>:
471 471
472 472 $ hg clone -u . -r stable a ua
473 473 adding changesets
474 474 adding manifests
475 475 adding file changes
476 476 added 14 changesets with 14 changes to 3 files
477 477 new changesets acb14030fe0a:0aae7cf88f0d
478 478 updating to branch stable
479 479 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
480 480
481 481 Repo ua has branch 'stable' and 'default' (was changed in fd511e9eeea6):
482 482
483 483 $ hg -R ua heads
484 484 changeset: 13:0aae7cf88f0d
485 485 branch: stable
486 486 tag: tip
487 487 user: test
488 488 date: Thu Jan 01 00:00:00 1970 +0000
489 489 summary: another change for branch stable
490 490
491 491 changeset: 10:a7949464abda
492 492 user: test
493 493 date: Thu Jan 01 00:00:00 1970 +0000
494 494 summary: test
495 495
496 496
497 497 Same revision checked out in repo a and ua:
498 498
499 499 $ hg -R a parents --template "{node|short}\n"
500 500 e8ece76546a6
501 501 $ hg -R ua parents --template "{node|short}\n"
502 502 e8ece76546a6
503 503
504 504 $ rm -r ua
505 505
506 506
507 507 Testing -r <branch>:
508 508
509 509 $ hg clone -r stable a ua
510 510 adding changesets
511 511 adding manifests
512 512 adding file changes
513 513 added 14 changesets with 14 changes to 3 files
514 514 new changesets acb14030fe0a:0aae7cf88f0d
515 515 updating to branch stable
516 516 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
517 517
518 518 Repo ua has branch 'stable' and 'default' (was changed in fd511e9eeea6):
519 519
520 520 $ hg -R ua heads
521 521 changeset: 13:0aae7cf88f0d
522 522 branch: stable
523 523 tag: tip
524 524 user: test
525 525 date: Thu Jan 01 00:00:00 1970 +0000
526 526 summary: another change for branch stable
527 527
528 528 changeset: 10:a7949464abda
529 529 user: test
530 530 date: Thu Jan 01 00:00:00 1970 +0000
531 531 summary: test
532 532
533 533
534 534 Branch 'stable' is checked out:
535 535
536 536 $ hg -R ua parents
537 537 changeset: 13:0aae7cf88f0d
538 538 branch: stable
539 539 tag: tip
540 540 user: test
541 541 date: Thu Jan 01 00:00:00 1970 +0000
542 542 summary: another change for branch stable
543 543
544 544
545 545 $ rm -r ua
546 546
547 547
548 548 Issue2267: Error in 1.6 hg.py: TypeError: 'NoneType' object is not
549 549 iterable in addbranchrevs()
550 550
551 551 $ cat <<EOF > simpleclone.py
552 552 > from mercurial import hg, ui as uimod
553 553 > myui = uimod.ui.load()
554 554 > repo = hg.repository(myui, b'a')
555 555 > hg.clone(myui, {}, repo, dest=b"ua")
556 556 > EOF
557 557
558 558 $ "$PYTHON" simpleclone.py
559 559 updating to branch default
560 560 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
561 561
562 562 $ rm -r ua
563 563
564 564 $ cat <<EOF > branchclone.py
565 565 > from mercurial import extensions, hg, ui as uimod
566 566 > myui = uimod.ui.load()
567 567 > extensions.loadall(myui)
568 568 > extensions.populateui(myui)
569 569 > repo = hg.repository(myui, b'a')
570 570 > hg.clone(myui, {}, repo, dest=b"ua", branch=[b"stable"])
571 571 > EOF
572 572
573 573 $ "$PYTHON" branchclone.py
574 574 adding changesets
575 575 adding manifests
576 576 adding file changes
577 577 added 14 changesets with 14 changes to 3 files
578 578 new changesets acb14030fe0a:0aae7cf88f0d
579 579 updating to branch stable
580 580 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
581 581 $ rm -r ua
582 582
583 Local clones don't get confused by unusual experimental.evolution options
584
585 $ hg clone \
586 > --config experimental.evolution=allowunstable,allowdivergence,exchange \
587 > a ua
588 updating to branch default
589 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
590 $ rm -r ua
591
592 $ hg clone \
593 > --config experimental.evolution.createmarkers=no \
594 > --config experimental.evolution.allowunstable=yes \
595 > --config experimental.evolution.allowdivergence=yes \
596 > --config experimental.evolution.exchange=yes \
597 > a ua
598 updating to branch default
599 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
600 $ rm -r ua
583 601
584 602 Test clone with special '@' bookmark:
585 603 $ cd a
586 604 $ hg bookmark -r a7949464abda @ # branch point of stable from default
587 605 $ hg clone . ../i
588 606 updating to bookmark @
589 607 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
590 608 $ hg id -i ../i
591 609 a7949464abda
592 610 $ rm -r ../i
593 611
594 612 $ hg bookmark -f -r stable @
595 613 $ hg bookmarks
596 614 @ 15:0aae7cf88f0d
597 615 $ hg clone . ../i
598 616 updating to bookmark @ on branch stable
599 617 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
600 618 $ hg id -i ../i
601 619 0aae7cf88f0d
602 620 $ cd "$TESTTMP"
603 621
604 622
605 623 Testing failures:
606 624
607 625 $ mkdir fail
608 626 $ cd fail
609 627
610 628 No local source
611 629
612 630 $ hg clone a b
613 631 abort: repository a not found
614 632 [255]
615 633
616 634 Invalid URL
617 635
618 636 $ hg clone http://invalid:url/a b
619 637 abort: error: nonnumeric port: 'url'
620 638 [100]
621 639
622 640 No remote source
623 641
624 642 #if windows
625 643 $ hg clone http://$LOCALIP:3121/a b
626 644 abort: error: * (glob)
627 645 [100]
628 646 #else
629 647 $ hg clone http://$LOCALIP:3121/a b
630 648 abort: error: *refused* (glob)
631 649 [100]
632 650 #endif
633 651 $ rm -rf b # work around bug with http clone
634 652
635 653
636 654 #if unix-permissions no-root
637 655
638 656 Inaccessible source
639 657
640 658 $ mkdir a
641 659 $ chmod 000 a
642 660 $ hg clone a b
643 661 abort: Permission denied: *$TESTTMP/fail/a/.hg* (glob)
644 662 [255]
645 663
646 664 Inaccessible destination
647 665
648 666 $ hg init b
649 667 $ cd b
650 668 $ hg clone . ../a
651 669 abort: Permission denied: *../a* (glob)
652 670 [255]
653 671 $ cd ..
654 672 $ chmod 700 a
655 673 $ rm -r a b
656 674
657 675 #endif
658 676
659 677
660 678 #if fifo
661 679
662 680 Source of wrong type
663 681
664 682 $ mkfifo a
665 683 $ hg clone a b
666 684 abort: $ENOTDIR$: *$TESTTMP/fail/a/.hg* (glob)
667 685 [255]
668 686 $ rm a
669 687
670 688 #endif
671 689
672 690 Default destination, same directory
673 691
674 692 $ hg init q
675 693 $ hg clone q
676 694 destination directory: q
677 695 abort: destination 'q' is not empty
678 696 [10]
679 697
680 698 destination directory not empty
681 699
682 700 $ mkdir a
683 701 $ echo stuff > a/a
684 702 $ hg clone q a
685 703 abort: destination 'a' is not empty
686 704 [10]
687 705
688 706
689 707 #if unix-permissions no-root
690 708
691 709 leave existing directory in place after clone failure
692 710
693 711 $ hg init c
694 712 $ cd c
695 713 $ echo c > c
696 714 $ hg commit -A -m test
697 715 adding c
698 716 $ chmod -rx .hg/store/data
699 717 $ cd ..
700 718 $ mkdir d
701 719 $ hg clone c d 2> err
702 720 [255]
703 721 $ test -d d
704 722 $ test -d d/.hg
705 723 [1]
706 724
707 725 re-enable perm to allow deletion
708 726
709 727 $ chmod +rx c/.hg/store/data
710 728
711 729 #endif
712 730
713 731 $ cd ..
714 732
715 733 Test clone from the repository in (emulated) revlog format 0 (issue4203):
716 734
717 735 $ mkdir issue4203
718 736 $ mkdir -p src/.hg
719 737 $ echo foo > src/foo
720 738 $ hg -R src add src/foo
721 739 $ hg -R src commit -m '#0'
722 740 $ hg -R src log -q
723 741 0:e1bab28bca43
724 742 $ hg -R src debugrevlog -c | egrep 'format|flags'
725 743 format : 0
726 744 flags : (none)
727 745 $ hg root -R src -T json | sed 's|\\\\|\\|g'
728 746 [
729 747 {
730 748 "hgpath": "$TESTTMP/src/.hg",
731 749 "reporoot": "$TESTTMP/src",
732 750 "storepath": "$TESTTMP/src/.hg"
733 751 }
734 752 ]
735 753 $ hg clone -U -q src dst
736 754 $ hg -R dst log -q
737 755 0:e1bab28bca43
738 756
739 757 Create repositories to test auto sharing functionality
740 758
741 759 $ cat >> $HGRCPATH << EOF
742 760 > [extensions]
743 761 > share=
744 762 > EOF
745 763
746 764 $ hg init empty
747 765 $ hg init source1a
748 766 $ cd source1a
749 767 $ echo initial1 > foo
750 768 $ hg -q commit -A -m initial
751 769 $ echo second > foo
752 770 $ hg commit -m second
753 771 $ cd ..
754 772
755 773 $ hg init filteredrev0
756 774 $ cd filteredrev0
757 775 $ cat >> .hg/hgrc << EOF
758 776 > [experimental]
759 777 > evolution.createmarkers=True
760 778 > EOF
761 779 $ echo initial1 > foo
762 780 $ hg -q commit -A -m initial0
763 781 $ hg -q up -r null
764 782 $ echo initial2 > foo
765 783 $ hg -q commit -A -m initial1
766 784 $ hg debugobsolete c05d5c47a5cf81401869999f3d05f7d699d2b29a e082c1832e09a7d1e78b7fd49a592d372de854c8
767 785 1 new obsolescence markers
768 786 obsoleted 1 changesets
769 787 $ cd ..
770 788
771 789 $ hg -q clone --pull source1a source1b
772 790 $ cd source1a
773 791 $ hg bookmark bookA
774 792 $ echo 1a > foo
775 793 $ hg commit -m 1a
776 794 $ cd ../source1b
777 795 $ hg -q up -r 0
778 796 $ echo head1 > foo
779 797 $ hg commit -m head1
780 798 created new head
781 799 $ hg bookmark head1
782 800 $ hg -q up -r 0
783 801 $ echo head2 > foo
784 802 $ hg commit -m head2
785 803 created new head
786 804 $ hg bookmark head2
787 805 $ hg -q up -r 0
788 806 $ hg branch branch1
789 807 marked working directory as branch branch1
790 808 (branches are permanent and global, did you want a bookmark?)
791 809 $ echo branch1 > foo
792 810 $ hg commit -m branch1
793 811 $ hg -q up -r 0
794 812 $ hg branch branch2
795 813 marked working directory as branch branch2
796 814 $ echo branch2 > foo
797 815 $ hg commit -m branch2
798 816 $ cd ..
799 817 $ hg init source2
800 818 $ cd source2
801 819 $ echo initial2 > foo
802 820 $ hg -q commit -A -m initial2
803 821 $ echo second > foo
804 822 $ hg commit -m second
805 823 $ cd ..
806 824
807 825 Clone with auto share from an empty repo should not result in share
808 826
809 827 $ mkdir share
810 828 $ hg --config share.pool=share clone empty share-empty
811 829 (not using pooled storage: remote appears to be empty)
812 830 updating to branch default
813 831 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
814 832 $ ls share
815 833 $ test -d share-empty/.hg/store
816 834 $ test -f share-empty/.hg/sharedpath
817 835 [1]
818 836
819 837 Clone with auto share from a repo with filtered revision 0 should not result in share
820 838
821 839 $ hg --config share.pool=share clone filteredrev0 share-filtered
822 840 (not using pooled storage: unable to resolve identity of remote)
823 841 requesting all changes
824 842 adding changesets
825 843 adding manifests
826 844 adding file changes
827 845 added 1 changesets with 1 changes to 1 files
828 846 new changesets e082c1832e09
829 847 updating to branch default
830 848 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
831 849
832 850 Clone from repo with content should result in shared store being created
833 851
834 852 $ hg --config share.pool=share clone source1a share-dest1a
835 853 (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
836 854 requesting all changes
837 855 adding changesets
838 856 adding manifests
839 857 adding file changes
840 858 added 3 changesets with 3 changes to 1 files
841 859 new changesets b5f04eac9d8f:e5bfe23c0b47
842 860 searching for changes
843 861 no changes found
844 862 adding remote bookmark bookA
845 863 updating working directory
846 864 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
847 865
848 866 The shared repo should have been created
849 867
850 868 $ ls share
851 869 b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1
852 870
853 871 The destination should point to it
854 872
855 873 $ cat share-dest1a/.hg/sharedpath; echo
856 874 $TESTTMP/share/b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1/.hg
857 875
858 876 The destination should have bookmarks
859 877
860 878 $ hg -R share-dest1a bookmarks
861 879 bookA 2:e5bfe23c0b47
862 880
863 881 The default path should be the remote, not the share
864 882
865 883 $ hg -R share-dest1a config paths.default
866 884 $TESTTMP/source1a
867 885
868 886 Clone with existing share dir should result in pull + share
869 887
870 888 $ hg --config share.pool=share clone source1b share-dest1b
871 889 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
872 890 searching for changes
873 891 adding changesets
874 892 adding manifests
875 893 adding file changes
876 894 adding remote bookmark head1
877 895 adding remote bookmark head2
878 896 added 4 changesets with 4 changes to 1 files (+4 heads)
879 897 new changesets 4a8dc1ab4c13:6bacf4683960
880 898 updating working directory
881 899 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
882 900
883 901 $ ls share
884 902 b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1
885 903
886 904 $ cat share-dest1b/.hg/sharedpath; echo
887 905 $TESTTMP/share/b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1/.hg
888 906
889 907 We only get bookmarks from the remote, not everything in the share
890 908
891 909 $ hg -R share-dest1b bookmarks
892 910 head1 3:4a8dc1ab4c13
893 911 head2 4:99f71071f117
894 912
895 913 Default path should be source, not share.
896 914
897 915 $ hg -R share-dest1b config paths.default
898 916 $TESTTMP/source1b
899 917
900 918 Checked out revision should be head of default branch
901 919
902 920 $ hg -R share-dest1b log -r .
903 921 changeset: 4:99f71071f117
904 922 bookmark: head2
905 923 parent: 0:b5f04eac9d8f
906 924 user: test
907 925 date: Thu Jan 01 00:00:00 1970 +0000
908 926 summary: head2
909 927
910 928
911 929 Clone from unrelated repo should result in new share
912 930
913 931 $ hg --config share.pool=share clone source2 share-dest2
914 932 (sharing from new pooled repository 22aeff664783fd44c6d9b435618173c118c3448e)
915 933 requesting all changes
916 934 adding changesets
917 935 adding manifests
918 936 adding file changes
919 937 added 2 changesets with 2 changes to 1 files
920 938 new changesets 22aeff664783:63cf6c3dba4a
921 939 searching for changes
922 940 no changes found
923 941 updating working directory
924 942 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
925 943
926 944 $ ls share
927 945 22aeff664783fd44c6d9b435618173c118c3448e
928 946 b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1
929 947
930 948 remote naming mode works as advertised
931 949
932 950 $ hg --config share.pool=shareremote --config share.poolnaming=remote clone source1a share-remote1a
933 951 (sharing from new pooled repository 195bb1fcdb595c14a6c13e0269129ed78f6debde)
934 952 requesting all changes
935 953 adding changesets
936 954 adding manifests
937 955 adding file changes
938 956 added 3 changesets with 3 changes to 1 files
939 957 new changesets b5f04eac9d8f:e5bfe23c0b47
940 958 searching for changes
941 959 no changes found
942 960 adding remote bookmark bookA
943 961 updating working directory
944 962 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
945 963
946 964 $ ls shareremote
947 965 195bb1fcdb595c14a6c13e0269129ed78f6debde
948 966
949 967 $ hg --config share.pool=shareremote --config share.poolnaming=remote clone source1b share-remote1b
950 968 (sharing from new pooled repository c0d4f83847ca2a873741feb7048a45085fd47c46)
951 969 requesting all changes
952 970 adding changesets
953 971 adding manifests
954 972 adding file changes
955 973 added 6 changesets with 6 changes to 1 files (+4 heads)
956 974 new changesets b5f04eac9d8f:6bacf4683960
957 975 searching for changes
958 976 no changes found
959 977 adding remote bookmark head1
960 978 adding remote bookmark head2
961 979 updating working directory
962 980 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
963 981
964 982 $ ls shareremote
965 983 195bb1fcdb595c14a6c13e0269129ed78f6debde
966 984 c0d4f83847ca2a873741feb7048a45085fd47c46
967 985
968 986 request to clone a single revision is respected in sharing mode
969 987
970 988 $ hg --config share.pool=sharerevs clone -r 4a8dc1ab4c13 source1b share-1arev
971 989 (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
972 990 adding changesets
973 991 adding manifests
974 992 adding file changes
975 993 added 2 changesets with 2 changes to 1 files
976 994 new changesets b5f04eac9d8f:4a8dc1ab4c13
977 995 no changes found
978 996 adding remote bookmark head1
979 997 updating working directory
980 998 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
981 999
982 1000 $ hg -R share-1arev log -G
983 1001 @ changeset: 1:4a8dc1ab4c13
984 1002 | bookmark: head1
985 1003 | tag: tip
986 1004 | user: test
987 1005 | date: Thu Jan 01 00:00:00 1970 +0000
988 1006 | summary: head1
989 1007 |
990 1008 o changeset: 0:b5f04eac9d8f
991 1009 user: test
992 1010 date: Thu Jan 01 00:00:00 1970 +0000
993 1011 summary: initial
994 1012
995 1013
996 1014 making another clone should only pull down requested rev
997 1015
998 1016 $ hg --config share.pool=sharerevs clone -r 99f71071f117 source1b share-1brev
999 1017 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1000 1018 searching for changes
1001 1019 adding changesets
1002 1020 adding manifests
1003 1021 adding file changes
1004 1022 adding remote bookmark head1
1005 1023 adding remote bookmark head2
1006 1024 added 1 changesets with 1 changes to 1 files (+1 heads)
1007 1025 new changesets 99f71071f117
1008 1026 updating working directory
1009 1027 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1010 1028
1011 1029 $ hg -R share-1brev log -G
1012 1030 @ changeset: 2:99f71071f117
1013 1031 | bookmark: head2
1014 1032 | tag: tip
1015 1033 | parent: 0:b5f04eac9d8f
1016 1034 | user: test
1017 1035 | date: Thu Jan 01 00:00:00 1970 +0000
1018 1036 | summary: head2
1019 1037 |
1020 1038 | o changeset: 1:4a8dc1ab4c13
1021 1039 |/ bookmark: head1
1022 1040 | user: test
1023 1041 | date: Thu Jan 01 00:00:00 1970 +0000
1024 1042 | summary: head1
1025 1043 |
1026 1044 o changeset: 0:b5f04eac9d8f
1027 1045 user: test
1028 1046 date: Thu Jan 01 00:00:00 1970 +0000
1029 1047 summary: initial
1030 1048
1031 1049
1032 1050 Request to clone a single branch is respected in sharing mode
1033 1051
1034 1052 $ hg --config share.pool=sharebranch clone -b branch1 source1b share-1bbranch1
1035 1053 (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1036 1054 adding changesets
1037 1055 adding manifests
1038 1056 adding file changes
1039 1057 added 2 changesets with 2 changes to 1 files
1040 1058 new changesets b5f04eac9d8f:5f92a6c1a1b1
1041 1059 no changes found
1042 1060 updating working directory
1043 1061 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1044 1062
1045 1063 $ hg -R share-1bbranch1 log -G
1046 1064 o changeset: 1:5f92a6c1a1b1
1047 1065 | branch: branch1
1048 1066 | tag: tip
1049 1067 | user: test
1050 1068 | date: Thu Jan 01 00:00:00 1970 +0000
1051 1069 | summary: branch1
1052 1070 |
1053 1071 @ changeset: 0:b5f04eac9d8f
1054 1072 user: test
1055 1073 date: Thu Jan 01 00:00:00 1970 +0000
1056 1074 summary: initial
1057 1075
1058 1076
1059 1077 $ hg --config share.pool=sharebranch clone -b branch2 source1b share-1bbranch2
1060 1078 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1061 1079 searching for changes
1062 1080 adding changesets
1063 1081 adding manifests
1064 1082 adding file changes
1065 1083 added 1 changesets with 1 changes to 1 files (+1 heads)
1066 1084 new changesets 6bacf4683960
1067 1085 updating working directory
1068 1086 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1069 1087
1070 1088 $ hg -R share-1bbranch2 log -G
1071 1089 o changeset: 2:6bacf4683960
1072 1090 | branch: branch2
1073 1091 | tag: tip
1074 1092 | parent: 0:b5f04eac9d8f
1075 1093 | user: test
1076 1094 | date: Thu Jan 01 00:00:00 1970 +0000
1077 1095 | summary: branch2
1078 1096 |
1079 1097 | o changeset: 1:5f92a6c1a1b1
1080 1098 |/ branch: branch1
1081 1099 | user: test
1082 1100 | date: Thu Jan 01 00:00:00 1970 +0000
1083 1101 | summary: branch1
1084 1102 |
1085 1103 @ changeset: 0:b5f04eac9d8f
1086 1104 user: test
1087 1105 date: Thu Jan 01 00:00:00 1970 +0000
1088 1106 summary: initial
1089 1107
1090 1108
1091 1109 -U is respected in share clone mode
1092 1110
1093 1111 $ hg --config share.pool=share clone -U source1a share-1anowc
1094 1112 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1095 1113 searching for changes
1096 1114 no changes found
1097 1115 adding remote bookmark bookA
1098 1116
1099 1117 $ ls -A share-1anowc
1100 1118 .hg
1101 1119
1102 1120 Test that auto sharing doesn't cause failure of "hg clone local remote"
1103 1121
1104 1122 $ cd $TESTTMP
1105 1123 $ hg -R a id -r 0
1106 1124 acb14030fe0a
1107 1125 $ hg id -R remote -r 0
1108 1126 abort: repository remote not found
1109 1127 [255]
1110 1128 $ hg --config share.pool=share -q clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" a ssh://user@dummy/remote
1111 1129 $ hg -R remote id -r 0
1112 1130 acb14030fe0a
1113 1131
1114 1132 Cloning into pooled storage doesn't race (issue5104)
1115 1133
1116 1134 $ HGPOSTLOCKDELAY=2.0 hg --config share.pool=racepool --config extensions.lockdelay=$TESTDIR/lockdelay.py clone source1a share-destrace1 > race1.log 2>&1 &
1117 1135 $ HGPRELOCKDELAY=1.0 hg --config share.pool=racepool --config extensions.lockdelay=$TESTDIR/lockdelay.py clone source1a share-destrace2 > race2.log 2>&1
1118 1136 $ wait
1119 1137
1120 1138 $ hg -R share-destrace1 log -r tip
1121 1139 changeset: 2:e5bfe23c0b47
1122 1140 bookmark: bookA
1123 1141 tag: tip
1124 1142 user: test
1125 1143 date: Thu Jan 01 00:00:00 1970 +0000
1126 1144 summary: 1a
1127 1145
1128 1146
1129 1147 $ hg -R share-destrace2 log -r tip
1130 1148 changeset: 2:e5bfe23c0b47
1131 1149 bookmark: bookA
1132 1150 tag: tip
1133 1151 user: test
1134 1152 date: Thu Jan 01 00:00:00 1970 +0000
1135 1153 summary: 1a
1136 1154
1137 1155 One repo should be new, the other should be shared from the pool. We
1138 1156 don't care which is which, so we just make sure we always print the
1139 1157 one containing "new pooled" first, then one one containing "existing
1140 1158 pooled".
1141 1159
1142 1160 $ (grep 'new pooled' race1.log > /dev/null && cat race1.log || cat race2.log) | grep -v lock
1143 1161 (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1144 1162 requesting all changes
1145 1163 adding changesets
1146 1164 adding manifests
1147 1165 adding file changes
1148 1166 added 3 changesets with 3 changes to 1 files
1149 1167 new changesets b5f04eac9d8f:e5bfe23c0b47
1150 1168 searching for changes
1151 1169 no changes found
1152 1170 adding remote bookmark bookA
1153 1171 updating working directory
1154 1172 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1155 1173
1156 1174 $ (grep 'existing pooled' race1.log > /dev/null && cat race1.log || cat race2.log) | grep -v lock
1157 1175 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1)
1158 1176 searching for changes
1159 1177 no changes found
1160 1178 adding remote bookmark bookA
1161 1179 updating working directory
1162 1180 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1163 1181
1164 1182 SEC: check for unsafe ssh url
1165 1183
1166 1184 $ cat >> $HGRCPATH << EOF
1167 1185 > [ui]
1168 1186 > ssh = sh -c "read l; read l; read l"
1169 1187 > EOF
1170 1188
1171 1189 $ hg clone 'ssh://-oProxyCommand=touch${IFS}owned/path'
1172 1190 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
1173 1191 [255]
1174 1192 $ hg clone 'ssh://%2DoProxyCommand=touch${IFS}owned/path'
1175 1193 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch${IFS}owned/path'
1176 1194 [255]
1177 1195 $ hg clone 'ssh://fakehost|touch%20owned/path'
1178 1196 abort: no suitable response from remote hg
1179 1197 [255]
1180 1198 $ hg clone 'ssh://fakehost%7Ctouch%20owned/path'
1181 1199 abort: no suitable response from remote hg
1182 1200 [255]
1183 1201
1184 1202 $ hg clone 'ssh://-oProxyCommand=touch owned%20foo@example.com/nonexistent/path'
1185 1203 abort: potentially unsafe url: 'ssh://-oProxyCommand=touch owned foo@example.com/nonexistent/path'
1186 1204 [255]
1187 1205
1188 1206 #if windows
1189 1207 $ hg clone "ssh://%26touch%20owned%20/" --debug
1190 1208 running sh -c "read l; read l; read l" "&touch owned " "hg -R . serve --stdio"
1191 1209 sending upgrade request: * proto=exp-ssh-v2-0003 (glob) (sshv2 !)
1192 1210 sending hello command
1193 1211 sending between command
1194 1212 abort: no suitable response from remote hg
1195 1213 [255]
1196 1214 $ hg clone "ssh://example.com:%26touch%20owned%20/" --debug
1197 1215 running sh -c "read l; read l; read l" -p "&touch owned " example.com "hg -R . serve --stdio"
1198 1216 sending upgrade request: * proto=exp-ssh-v2-0003 (glob) (sshv2 !)
1199 1217 sending hello command
1200 1218 sending between command
1201 1219 abort: no suitable response from remote hg
1202 1220 [255]
1203 1221 #else
1204 1222 $ hg clone "ssh://%3btouch%20owned%20/" --debug
1205 1223 running sh -c "read l; read l; read l" ';touch owned ' 'hg -R . serve --stdio'
1206 1224 sending upgrade request: * proto=exp-ssh-v2-0003 (glob) (sshv2 !)
1207 1225 sending hello command
1208 1226 sending between command
1209 1227 abort: no suitable response from remote hg
1210 1228 [255]
1211 1229 $ hg clone "ssh://example.com:%3btouch%20owned%20/" --debug
1212 1230 running sh -c "read l; read l; read l" -p ';touch owned ' example.com 'hg -R . serve --stdio'
1213 1231 sending upgrade request: * proto=exp-ssh-v2-0003 (glob) (sshv2 !)
1214 1232 sending hello command
1215 1233 sending between command
1216 1234 abort: no suitable response from remote hg
1217 1235 [255]
1218 1236 #endif
1219 1237
1220 1238 $ hg clone "ssh://v-alid.example.com/" --debug
1221 1239 running sh -c "read l; read l; read l" v-alid\.example\.com ['"]hg -R \. serve --stdio['"] (re)
1222 1240 sending upgrade request: * proto=exp-ssh-v2-0003 (glob) (sshv2 !)
1223 1241 sending hello command
1224 1242 sending between command
1225 1243 abort: no suitable response from remote hg
1226 1244 [255]
1227 1245
1228 1246 We should not have created a file named owned - if it exists, the
1229 1247 attack succeeded.
1230 1248 $ if test -f owned; then echo 'you got owned'; fi
1231 1249
1232 1250 Cloning without fsmonitor enabled does not print a warning for small repos
1233 1251
1234 1252 $ hg clone a fsmonitor-default
1235 1253 updating to bookmark @ on branch stable
1236 1254 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1237 1255
1238 1256 Lower the warning threshold to simulate a large repo
1239 1257
1240 1258 $ cat >> $HGRCPATH << EOF
1241 1259 > [fsmonitor]
1242 1260 > warn_update_file_count = 2
1243 1261 > warn_update_file_count_rust = 2
1244 1262 > EOF
1245 1263
1246 1264 We should see a warning about no fsmonitor on supported platforms
1247 1265
1248 1266 #if linuxormacos no-fsmonitor
1249 1267 $ hg clone a nofsmonitor
1250 1268 updating to bookmark @ on branch stable
1251 1269 (warning: large working directory being used without fsmonitor enabled; enable fsmonitor to improve performance; see "hg help -e fsmonitor")
1252 1270 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1253 1271 #else
1254 1272 $ hg clone a nofsmonitor
1255 1273 updating to bookmark @ on branch stable
1256 1274 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1257 1275 #endif
1258 1276
1259 1277 We should not see warning about fsmonitor when it is enabled
1260 1278
1261 1279 #if fsmonitor
1262 1280 $ hg clone a fsmonitor-enabled
1263 1281 updating to bookmark @ on branch stable
1264 1282 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1265 1283 #endif
1266 1284
1267 1285 We can disable the fsmonitor warning
1268 1286
1269 1287 $ hg --config fsmonitor.warn_when_unused=false clone a fsmonitor-disable-warning
1270 1288 updating to bookmark @ on branch stable
1271 1289 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1272 1290
1273 1291 Loaded fsmonitor but disabled in config should still print warning
1274 1292
1275 1293 #if linuxormacos fsmonitor
1276 1294 $ hg --config fsmonitor.mode=off clone a fsmonitor-mode-off
1277 1295 updating to bookmark @ on branch stable
1278 1296 (warning: large working directory being used without fsmonitor enabled; enable fsmonitor to improve performance; see "hg help -e fsmonitor") (fsmonitor !)
1279 1297 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1280 1298 #endif
1281 1299
1282 1300 Warning not printed if working directory isn't empty
1283 1301
1284 1302 $ hg -q clone a fsmonitor-update
1285 1303 (warning: large working directory being used without fsmonitor enabled; enable fsmonitor to improve performance; see "hg help -e fsmonitor") (?)
1286 1304 $ cd fsmonitor-update
1287 1305 $ hg up acb14030fe0a
1288 1306 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
1289 1307 (leaving bookmark @)
1290 1308 $ hg up cf0fe1914066
1291 1309 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1292 1310
1293 1311 `hg update` from null revision also prints
1294 1312
1295 1313 $ hg up null
1296 1314 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1297 1315
1298 1316 #if linuxormacos no-fsmonitor
1299 1317 $ hg up cf0fe1914066
1300 1318 (warning: large working directory being used without fsmonitor enabled; enable fsmonitor to improve performance; see "hg help -e fsmonitor")
1301 1319 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1302 1320 #else
1303 1321 $ hg up cf0fe1914066
1304 1322 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1305 1323 #endif
1306 1324
1307 1325 $ cd ..
1308 1326
General Comments 0
You need to be logged in to leave comments. Login now