##// END OF EJS Templates
util: implement a deterministic __repr__ on sortdict...
Gregory Szorc -
r29592:37cccad5 default
parent child Browse files
Show More
@@ -1,2859 +1,2863 b''
1 1 # util.py - Mercurial utility functions and platform specific implementations
2 2 #
3 3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 6 #
7 7 # This software may be used and distributed according to the terms of the
8 8 # GNU General Public License version 2 or any later version.
9 9
10 10 """Mercurial utility functions and platform specific implementations.
11 11
12 12 This contains helper routines that are independent of the SCM core and
13 13 hide platform-specific details from the core.
14 14 """
15 15
16 16 from __future__ import absolute_import
17 17
18 18 import bz2
19 19 import calendar
20 20 import collections
21 21 import datetime
22 22 import errno
23 23 import gc
24 24 import hashlib
25 25 import imp
26 26 import os
27 27 import re as remod
28 28 import shutil
29 29 import signal
30 30 import socket
31 31 import subprocess
32 32 import sys
33 33 import tempfile
34 34 import textwrap
35 35 import time
36 36 import traceback
37 37 import zlib
38 38
39 39 from . import (
40 40 encoding,
41 41 error,
42 42 i18n,
43 43 osutil,
44 44 parsers,
45 45 pycompat,
46 46 )
47 47
48 48 for attr in (
49 49 'empty',
50 50 'httplib',
51 51 'httpserver',
52 52 'pickle',
53 53 'queue',
54 54 'urlerr',
55 55 'urlparse',
56 56 # we do import urlreq, but we do it outside the loop
57 57 #'urlreq',
58 58 'stringio',
59 59 'socketserver',
60 60 'xmlrpclib',
61 61 ):
62 62 globals()[attr] = getattr(pycompat, attr)
63 63
64 64 # This line is to make pyflakes happy:
65 65 urlreq = pycompat.urlreq
66 66
67 67 if os.name == 'nt':
68 68 from . import windows as platform
69 69 else:
70 70 from . import posix as platform
71 71
72 72 _ = i18n._
73 73
74 74 bindunixsocket = platform.bindunixsocket
75 75 cachestat = platform.cachestat
76 76 checkexec = platform.checkexec
77 77 checklink = platform.checklink
78 78 copymode = platform.copymode
79 79 executablepath = platform.executablepath
80 80 expandglobs = platform.expandglobs
81 81 explainexit = platform.explainexit
82 82 findexe = platform.findexe
83 83 gethgcmd = platform.gethgcmd
84 84 getuser = platform.getuser
85 85 getpid = os.getpid
86 86 groupmembers = platform.groupmembers
87 87 groupname = platform.groupname
88 88 hidewindow = platform.hidewindow
89 89 isexec = platform.isexec
90 90 isowner = platform.isowner
91 91 localpath = platform.localpath
92 92 lookupreg = platform.lookupreg
93 93 makedir = platform.makedir
94 94 nlinks = platform.nlinks
95 95 normpath = platform.normpath
96 96 normcase = platform.normcase
97 97 normcasespec = platform.normcasespec
98 98 normcasefallback = platform.normcasefallback
99 99 openhardlinks = platform.openhardlinks
100 100 oslink = platform.oslink
101 101 parsepatchoutput = platform.parsepatchoutput
102 102 pconvert = platform.pconvert
103 103 poll = platform.poll
104 104 popen = platform.popen
105 105 posixfile = platform.posixfile
106 106 quotecommand = platform.quotecommand
107 107 readpipe = platform.readpipe
108 108 rename = platform.rename
109 109 removedirs = platform.removedirs
110 110 samedevice = platform.samedevice
111 111 samefile = platform.samefile
112 112 samestat = platform.samestat
113 113 setbinary = platform.setbinary
114 114 setflags = platform.setflags
115 115 setsignalhandler = platform.setsignalhandler
116 116 shellquote = platform.shellquote
117 117 spawndetached = platform.spawndetached
118 118 split = platform.split
119 119 sshargs = platform.sshargs
120 120 statfiles = getattr(osutil, 'statfiles', platform.statfiles)
121 121 statisexec = platform.statisexec
122 122 statislink = platform.statislink
123 123 termwidth = platform.termwidth
124 124 testpid = platform.testpid
125 125 umask = platform.umask
126 126 unlink = platform.unlink
127 127 unlinkpath = platform.unlinkpath
128 128 username = platform.username
129 129
130 130 # Python compatibility
131 131
132 132 _notset = object()
133 133
134 134 # disable Python's problematic floating point timestamps (issue4836)
135 135 # (Python hypocritically says you shouldn't change this behavior in
136 136 # libraries, and sure enough Mercurial is not a library.)
137 137 os.stat_float_times(False)
138 138
139 139 def safehasattr(thing, attr):
140 140 return getattr(thing, attr, _notset) is not _notset
141 141
142 142 DIGESTS = {
143 143 'md5': hashlib.md5,
144 144 'sha1': hashlib.sha1,
145 145 'sha512': hashlib.sha512,
146 146 }
147 147 # List of digest types from strongest to weakest
148 148 DIGESTS_BY_STRENGTH = ['sha512', 'sha1', 'md5']
149 149
150 150 for k in DIGESTS_BY_STRENGTH:
151 151 assert k in DIGESTS
152 152
153 153 class digester(object):
154 154 """helper to compute digests.
155 155
156 156 This helper can be used to compute one or more digests given their name.
157 157
158 158 >>> d = digester(['md5', 'sha1'])
159 159 >>> d.update('foo')
160 160 >>> [k for k in sorted(d)]
161 161 ['md5', 'sha1']
162 162 >>> d['md5']
163 163 'acbd18db4cc2f85cedef654fccc4a4d8'
164 164 >>> d['sha1']
165 165 '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'
166 166 >>> digester.preferred(['md5', 'sha1'])
167 167 'sha1'
168 168 """
169 169
170 170 def __init__(self, digests, s=''):
171 171 self._hashes = {}
172 172 for k in digests:
173 173 if k not in DIGESTS:
174 174 raise Abort(_('unknown digest type: %s') % k)
175 175 self._hashes[k] = DIGESTS[k]()
176 176 if s:
177 177 self.update(s)
178 178
179 179 def update(self, data):
180 180 for h in self._hashes.values():
181 181 h.update(data)
182 182
183 183 def __getitem__(self, key):
184 184 if key not in DIGESTS:
185 185 raise Abort(_('unknown digest type: %s') % k)
186 186 return self._hashes[key].hexdigest()
187 187
188 188 def __iter__(self):
189 189 return iter(self._hashes)
190 190
191 191 @staticmethod
192 192 def preferred(supported):
193 193 """returns the strongest digest type in both supported and DIGESTS."""
194 194
195 195 for k in DIGESTS_BY_STRENGTH:
196 196 if k in supported:
197 197 return k
198 198 return None
199 199
200 200 class digestchecker(object):
201 201 """file handle wrapper that additionally checks content against a given
202 202 size and digests.
203 203
204 204 d = digestchecker(fh, size, {'md5': '...'})
205 205
206 206 When multiple digests are given, all of them are validated.
207 207 """
208 208
209 209 def __init__(self, fh, size, digests):
210 210 self._fh = fh
211 211 self._size = size
212 212 self._got = 0
213 213 self._digests = dict(digests)
214 214 self._digester = digester(self._digests.keys())
215 215
216 216 def read(self, length=-1):
217 217 content = self._fh.read(length)
218 218 self._digester.update(content)
219 219 self._got += len(content)
220 220 return content
221 221
222 222 def validate(self):
223 223 if self._size != self._got:
224 224 raise Abort(_('size mismatch: expected %d, got %d') %
225 225 (self._size, self._got))
226 226 for k, v in self._digests.items():
227 227 if v != self._digester[k]:
228 228 # i18n: first parameter is a digest name
229 229 raise Abort(_('%s mismatch: expected %s, got %s') %
230 230 (k, v, self._digester[k]))
231 231
232 232 try:
233 233 buffer = buffer
234 234 except NameError:
235 235 if sys.version_info[0] < 3:
236 236 def buffer(sliceable, offset=0):
237 237 return sliceable[offset:]
238 238 else:
239 239 def buffer(sliceable, offset=0):
240 240 return memoryview(sliceable)[offset:]
241 241
242 242 closefds = os.name == 'posix'
243 243
244 244 _chunksize = 4096
245 245
246 246 class bufferedinputpipe(object):
247 247 """a manually buffered input pipe
248 248
249 249 Python will not let us use buffered IO and lazy reading with 'polling' at
250 250 the same time. We cannot probe the buffer state and select will not detect
251 251 that data are ready to read if they are already buffered.
252 252
253 253 This class let us work around that by implementing its own buffering
254 254 (allowing efficient readline) while offering a way to know if the buffer is
255 255 empty from the output (allowing collaboration of the buffer with polling).
256 256
257 257 This class lives in the 'util' module because it makes use of the 'os'
258 258 module from the python stdlib.
259 259 """
260 260
261 261 def __init__(self, input):
262 262 self._input = input
263 263 self._buffer = []
264 264 self._eof = False
265 265 self._lenbuf = 0
266 266
267 267 @property
268 268 def hasbuffer(self):
269 269 """True is any data is currently buffered
270 270
271 271 This will be used externally a pre-step for polling IO. If there is
272 272 already data then no polling should be set in place."""
273 273 return bool(self._buffer)
274 274
275 275 @property
276 276 def closed(self):
277 277 return self._input.closed
278 278
279 279 def fileno(self):
280 280 return self._input.fileno()
281 281
282 282 def close(self):
283 283 return self._input.close()
284 284
285 285 def read(self, size):
286 286 while (not self._eof) and (self._lenbuf < size):
287 287 self._fillbuffer()
288 288 return self._frombuffer(size)
289 289
290 290 def readline(self, *args, **kwargs):
291 291 if 1 < len(self._buffer):
292 292 # this should not happen because both read and readline end with a
293 293 # _frombuffer call that collapse it.
294 294 self._buffer = [''.join(self._buffer)]
295 295 self._lenbuf = len(self._buffer[0])
296 296 lfi = -1
297 297 if self._buffer:
298 298 lfi = self._buffer[-1].find('\n')
299 299 while (not self._eof) and lfi < 0:
300 300 self._fillbuffer()
301 301 if self._buffer:
302 302 lfi = self._buffer[-1].find('\n')
303 303 size = lfi + 1
304 304 if lfi < 0: # end of file
305 305 size = self._lenbuf
306 306 elif 1 < len(self._buffer):
307 307 # we need to take previous chunks into account
308 308 size += self._lenbuf - len(self._buffer[-1])
309 309 return self._frombuffer(size)
310 310
311 311 def _frombuffer(self, size):
312 312 """return at most 'size' data from the buffer
313 313
314 314 The data are removed from the buffer."""
315 315 if size == 0 or not self._buffer:
316 316 return ''
317 317 buf = self._buffer[0]
318 318 if 1 < len(self._buffer):
319 319 buf = ''.join(self._buffer)
320 320
321 321 data = buf[:size]
322 322 buf = buf[len(data):]
323 323 if buf:
324 324 self._buffer = [buf]
325 325 self._lenbuf = len(buf)
326 326 else:
327 327 self._buffer = []
328 328 self._lenbuf = 0
329 329 return data
330 330
331 331 def _fillbuffer(self):
332 332 """read data to the buffer"""
333 333 data = os.read(self._input.fileno(), _chunksize)
334 334 if not data:
335 335 self._eof = True
336 336 else:
337 337 self._lenbuf += len(data)
338 338 self._buffer.append(data)
339 339
340 340 def popen2(cmd, env=None, newlines=False):
341 341 # Setting bufsize to -1 lets the system decide the buffer size.
342 342 # The default for bufsize is 0, meaning unbuffered. This leads to
343 343 # poor performance on Mac OS X: http://bugs.python.org/issue4194
344 344 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
345 345 close_fds=closefds,
346 346 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
347 347 universal_newlines=newlines,
348 348 env=env)
349 349 return p.stdin, p.stdout
350 350
351 351 def popen3(cmd, env=None, newlines=False):
352 352 stdin, stdout, stderr, p = popen4(cmd, env, newlines)
353 353 return stdin, stdout, stderr
354 354
355 355 def popen4(cmd, env=None, newlines=False, bufsize=-1):
356 356 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
357 357 close_fds=closefds,
358 358 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
359 359 stderr=subprocess.PIPE,
360 360 universal_newlines=newlines,
361 361 env=env)
362 362 return p.stdin, p.stdout, p.stderr, p
363 363
364 364 def version():
365 365 """Return version information if available."""
366 366 try:
367 367 from . import __version__
368 368 return __version__.version
369 369 except ImportError:
370 370 return 'unknown'
371 371
372 372 def versiontuple(v=None, n=4):
373 373 """Parses a Mercurial version string into an N-tuple.
374 374
375 375 The version string to be parsed is specified with the ``v`` argument.
376 376 If it isn't defined, the current Mercurial version string will be parsed.
377 377
378 378 ``n`` can be 2, 3, or 4. Here is how some version strings map to
379 379 returned values:
380 380
381 381 >>> v = '3.6.1+190-df9b73d2d444'
382 382 >>> versiontuple(v, 2)
383 383 (3, 6)
384 384 >>> versiontuple(v, 3)
385 385 (3, 6, 1)
386 386 >>> versiontuple(v, 4)
387 387 (3, 6, 1, '190-df9b73d2d444')
388 388
389 389 >>> versiontuple('3.6.1+190-df9b73d2d444+20151118')
390 390 (3, 6, 1, '190-df9b73d2d444+20151118')
391 391
392 392 >>> v = '3.6'
393 393 >>> versiontuple(v, 2)
394 394 (3, 6)
395 395 >>> versiontuple(v, 3)
396 396 (3, 6, None)
397 397 >>> versiontuple(v, 4)
398 398 (3, 6, None, None)
399 399 """
400 400 if not v:
401 401 v = version()
402 402 parts = v.split('+', 1)
403 403 if len(parts) == 1:
404 404 vparts, extra = parts[0], None
405 405 else:
406 406 vparts, extra = parts
407 407
408 408 vints = []
409 409 for i in vparts.split('.'):
410 410 try:
411 411 vints.append(int(i))
412 412 except ValueError:
413 413 break
414 414 # (3, 6) -> (3, 6, None)
415 415 while len(vints) < 3:
416 416 vints.append(None)
417 417
418 418 if n == 2:
419 419 return (vints[0], vints[1])
420 420 if n == 3:
421 421 return (vints[0], vints[1], vints[2])
422 422 if n == 4:
423 423 return (vints[0], vints[1], vints[2], extra)
424 424
425 425 # used by parsedate
426 426 defaultdateformats = (
427 427 '%Y-%m-%d %H:%M:%S',
428 428 '%Y-%m-%d %I:%M:%S%p',
429 429 '%Y-%m-%d %H:%M',
430 430 '%Y-%m-%d %I:%M%p',
431 431 '%Y-%m-%d',
432 432 '%m-%d',
433 433 '%m/%d',
434 434 '%m/%d/%y',
435 435 '%m/%d/%Y',
436 436 '%a %b %d %H:%M:%S %Y',
437 437 '%a %b %d %I:%M:%S%p %Y',
438 438 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
439 439 '%b %d %H:%M:%S %Y',
440 440 '%b %d %I:%M:%S%p %Y',
441 441 '%b %d %H:%M:%S',
442 442 '%b %d %I:%M:%S%p',
443 443 '%b %d %H:%M',
444 444 '%b %d %I:%M%p',
445 445 '%b %d %Y',
446 446 '%b %d',
447 447 '%H:%M:%S',
448 448 '%I:%M:%S%p',
449 449 '%H:%M',
450 450 '%I:%M%p',
451 451 )
452 452
453 453 extendeddateformats = defaultdateformats + (
454 454 "%Y",
455 455 "%Y-%m",
456 456 "%b",
457 457 "%b %Y",
458 458 )
459 459
460 460 def cachefunc(func):
461 461 '''cache the result of function calls'''
462 462 # XXX doesn't handle keywords args
463 463 if func.__code__.co_argcount == 0:
464 464 cache = []
465 465 def f():
466 466 if len(cache) == 0:
467 467 cache.append(func())
468 468 return cache[0]
469 469 return f
470 470 cache = {}
471 471 if func.__code__.co_argcount == 1:
472 472 # we gain a small amount of time because
473 473 # we don't need to pack/unpack the list
474 474 def f(arg):
475 475 if arg not in cache:
476 476 cache[arg] = func(arg)
477 477 return cache[arg]
478 478 else:
479 479 def f(*args):
480 480 if args not in cache:
481 481 cache[args] = func(*args)
482 482 return cache[args]
483 483
484 484 return f
485 485
486 486 class sortdict(dict):
487 487 '''a simple sorted dictionary'''
488 488 def __init__(self, data=None):
489 489 self._list = []
490 490 if data:
491 491 self.update(data)
492 492 def copy(self):
493 493 return sortdict(self)
494 494 def __setitem__(self, key, val):
495 495 if key in self:
496 496 self._list.remove(key)
497 497 self._list.append(key)
498 498 dict.__setitem__(self, key, val)
499 499 def __iter__(self):
500 500 return self._list.__iter__()
501 501 def update(self, src):
502 502 if isinstance(src, dict):
503 503 src = src.iteritems()
504 504 for k, v in src:
505 505 self[k] = v
506 506 def clear(self):
507 507 dict.clear(self)
508 508 self._list = []
509 509 def items(self):
510 510 return [(k, self[k]) for k in self._list]
511 511 def __delitem__(self, key):
512 512 dict.__delitem__(self, key)
513 513 self._list.remove(key)
514 514 def pop(self, key, *args, **kwargs):
515 515 dict.pop(self, key, *args, **kwargs)
516 516 try:
517 517 self._list.remove(key)
518 518 except ValueError:
519 519 pass
520 520 def keys(self):
521 521 return self._list
522 522 def iterkeys(self):
523 523 return self._list.__iter__()
524 524 def iteritems(self):
525 525 for k in self._list:
526 526 yield k, self[k]
527 527 def insert(self, index, key, val):
528 528 self._list.insert(index, key)
529 529 dict.__setitem__(self, key, val)
530 def __repr__(self):
531 if not self:
532 return '%s()' % self.__class__.__name__
533 return '%s(%r)' % (self.__class__.__name__, self.items())
530 534
531 535 class _lrucachenode(object):
532 536 """A node in a doubly linked list.
533 537
534 538 Holds a reference to nodes on either side as well as a key-value
535 539 pair for the dictionary entry.
536 540 """
537 541 __slots__ = ('next', 'prev', 'key', 'value')
538 542
539 543 def __init__(self):
540 544 self.next = None
541 545 self.prev = None
542 546
543 547 self.key = _notset
544 548 self.value = None
545 549
546 550 def markempty(self):
547 551 """Mark the node as emptied."""
548 552 self.key = _notset
549 553
550 554 class lrucachedict(object):
551 555 """Dict that caches most recent accesses and sets.
552 556
553 557 The dict consists of an actual backing dict - indexed by original
554 558 key - and a doubly linked circular list defining the order of entries in
555 559 the cache.
556 560
557 561 The head node is the newest entry in the cache. If the cache is full,
558 562 we recycle head.prev and make it the new head. Cache accesses result in
559 563 the node being moved to before the existing head and being marked as the
560 564 new head node.
561 565 """
562 566 def __init__(self, max):
563 567 self._cache = {}
564 568
565 569 self._head = head = _lrucachenode()
566 570 head.prev = head
567 571 head.next = head
568 572 self._size = 1
569 573 self._capacity = max
570 574
571 575 def __len__(self):
572 576 return len(self._cache)
573 577
574 578 def __contains__(self, k):
575 579 return k in self._cache
576 580
577 581 def __iter__(self):
578 582 # We don't have to iterate in cache order, but why not.
579 583 n = self._head
580 584 for i in range(len(self._cache)):
581 585 yield n.key
582 586 n = n.next
583 587
584 588 def __getitem__(self, k):
585 589 node = self._cache[k]
586 590 self._movetohead(node)
587 591 return node.value
588 592
589 593 def __setitem__(self, k, v):
590 594 node = self._cache.get(k)
591 595 # Replace existing value and mark as newest.
592 596 if node is not None:
593 597 node.value = v
594 598 self._movetohead(node)
595 599 return
596 600
597 601 if self._size < self._capacity:
598 602 node = self._addcapacity()
599 603 else:
600 604 # Grab the last/oldest item.
601 605 node = self._head.prev
602 606
603 607 # At capacity. Kill the old entry.
604 608 if node.key is not _notset:
605 609 del self._cache[node.key]
606 610
607 611 node.key = k
608 612 node.value = v
609 613 self._cache[k] = node
610 614 # And mark it as newest entry. No need to adjust order since it
611 615 # is already self._head.prev.
612 616 self._head = node
613 617
614 618 def __delitem__(self, k):
615 619 node = self._cache.pop(k)
616 620 node.markempty()
617 621
618 622 # Temporarily mark as newest item before re-adjusting head to make
619 623 # this node the oldest item.
620 624 self._movetohead(node)
621 625 self._head = node.next
622 626
623 627 # Additional dict methods.
624 628
625 629 def get(self, k, default=None):
626 630 try:
627 631 return self._cache[k]
628 632 except KeyError:
629 633 return default
630 634
631 635 def clear(self):
632 636 n = self._head
633 637 while n.key is not _notset:
634 638 n.markempty()
635 639 n = n.next
636 640
637 641 self._cache.clear()
638 642
639 643 def copy(self):
640 644 result = lrucachedict(self._capacity)
641 645 n = self._head.prev
642 646 # Iterate in oldest-to-newest order, so the copy has the right ordering
643 647 for i in range(len(self._cache)):
644 648 result[n.key] = n.value
645 649 n = n.prev
646 650 return result
647 651
648 652 def _movetohead(self, node):
649 653 """Mark a node as the newest, making it the new head.
650 654
651 655 When a node is accessed, it becomes the freshest entry in the LRU
652 656 list, which is denoted by self._head.
653 657
654 658 Visually, let's make ``N`` the new head node (* denotes head):
655 659
656 660 previous/oldest <-> head <-> next/next newest
657 661
658 662 ----<->--- A* ---<->-----
659 663 | |
660 664 E <-> D <-> N <-> C <-> B
661 665
662 666 To:
663 667
664 668 ----<->--- N* ---<->-----
665 669 | |
666 670 E <-> D <-> C <-> B <-> A
667 671
668 672 This requires the following moves:
669 673
670 674 C.next = D (node.prev.next = node.next)
671 675 D.prev = C (node.next.prev = node.prev)
672 676 E.next = N (head.prev.next = node)
673 677 N.prev = E (node.prev = head.prev)
674 678 N.next = A (node.next = head)
675 679 A.prev = N (head.prev = node)
676 680 """
677 681 head = self._head
678 682 # C.next = D
679 683 node.prev.next = node.next
680 684 # D.prev = C
681 685 node.next.prev = node.prev
682 686 # N.prev = E
683 687 node.prev = head.prev
684 688 # N.next = A
685 689 # It is tempting to do just "head" here, however if node is
686 690 # adjacent to head, this will do bad things.
687 691 node.next = head.prev.next
688 692 # E.next = N
689 693 node.next.prev = node
690 694 # A.prev = N
691 695 node.prev.next = node
692 696
693 697 self._head = node
694 698
695 699 def _addcapacity(self):
696 700 """Add a node to the circular linked list.
697 701
698 702 The new node is inserted before the head node.
699 703 """
700 704 head = self._head
701 705 node = _lrucachenode()
702 706 head.prev.next = node
703 707 node.prev = head.prev
704 708 node.next = head
705 709 head.prev = node
706 710 self._size += 1
707 711 return node
708 712
709 713 def lrucachefunc(func):
710 714 '''cache most recent results of function calls'''
711 715 cache = {}
712 716 order = collections.deque()
713 717 if func.__code__.co_argcount == 1:
714 718 def f(arg):
715 719 if arg not in cache:
716 720 if len(cache) > 20:
717 721 del cache[order.popleft()]
718 722 cache[arg] = func(arg)
719 723 else:
720 724 order.remove(arg)
721 725 order.append(arg)
722 726 return cache[arg]
723 727 else:
724 728 def f(*args):
725 729 if args not in cache:
726 730 if len(cache) > 20:
727 731 del cache[order.popleft()]
728 732 cache[args] = func(*args)
729 733 else:
730 734 order.remove(args)
731 735 order.append(args)
732 736 return cache[args]
733 737
734 738 return f
735 739
736 740 class propertycache(object):
737 741 def __init__(self, func):
738 742 self.func = func
739 743 self.name = func.__name__
740 744 def __get__(self, obj, type=None):
741 745 result = self.func(obj)
742 746 self.cachevalue(obj, result)
743 747 return result
744 748
745 749 def cachevalue(self, obj, value):
746 750 # __dict__ assignment required to bypass __setattr__ (eg: repoview)
747 751 obj.__dict__[self.name] = value
748 752
749 753 def pipefilter(s, cmd):
750 754 '''filter string S through command CMD, returning its output'''
751 755 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
752 756 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
753 757 pout, perr = p.communicate(s)
754 758 return pout
755 759
756 760 def tempfilter(s, cmd):
757 761 '''filter string S through a pair of temporary files with CMD.
758 762 CMD is used as a template to create the real command to be run,
759 763 with the strings INFILE and OUTFILE replaced by the real names of
760 764 the temporary files generated.'''
761 765 inname, outname = None, None
762 766 try:
763 767 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
764 768 fp = os.fdopen(infd, 'wb')
765 769 fp.write(s)
766 770 fp.close()
767 771 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
768 772 os.close(outfd)
769 773 cmd = cmd.replace('INFILE', inname)
770 774 cmd = cmd.replace('OUTFILE', outname)
771 775 code = os.system(cmd)
772 776 if sys.platform == 'OpenVMS' and code & 1:
773 777 code = 0
774 778 if code:
775 779 raise Abort(_("command '%s' failed: %s") %
776 780 (cmd, explainexit(code)))
777 781 return readfile(outname)
778 782 finally:
779 783 try:
780 784 if inname:
781 785 os.unlink(inname)
782 786 except OSError:
783 787 pass
784 788 try:
785 789 if outname:
786 790 os.unlink(outname)
787 791 except OSError:
788 792 pass
789 793
790 794 filtertable = {
791 795 'tempfile:': tempfilter,
792 796 'pipe:': pipefilter,
793 797 }
794 798
795 799 def filter(s, cmd):
796 800 "filter a string through a command that transforms its input to its output"
797 801 for name, fn in filtertable.iteritems():
798 802 if cmd.startswith(name):
799 803 return fn(s, cmd[len(name):].lstrip())
800 804 return pipefilter(s, cmd)
801 805
802 806 def binary(s):
803 807 """return true if a string is binary data"""
804 808 return bool(s and '\0' in s)
805 809
806 810 def increasingchunks(source, min=1024, max=65536):
807 811 '''return no less than min bytes per chunk while data remains,
808 812 doubling min after each chunk until it reaches max'''
809 813 def log2(x):
810 814 if not x:
811 815 return 0
812 816 i = 0
813 817 while x:
814 818 x >>= 1
815 819 i += 1
816 820 return i - 1
817 821
818 822 buf = []
819 823 blen = 0
820 824 for chunk in source:
821 825 buf.append(chunk)
822 826 blen += len(chunk)
823 827 if blen >= min:
824 828 if min < max:
825 829 min = min << 1
826 830 nmin = 1 << log2(blen)
827 831 if nmin > min:
828 832 min = nmin
829 833 if min > max:
830 834 min = max
831 835 yield ''.join(buf)
832 836 blen = 0
833 837 buf = []
834 838 if buf:
835 839 yield ''.join(buf)
836 840
837 841 Abort = error.Abort
838 842
839 843 def always(fn):
840 844 return True
841 845
842 846 def never(fn):
843 847 return False
844 848
845 849 def nogc(func):
846 850 """disable garbage collector
847 851
848 852 Python's garbage collector triggers a GC each time a certain number of
849 853 container objects (the number being defined by gc.get_threshold()) are
850 854 allocated even when marked not to be tracked by the collector. Tracking has
851 855 no effect on when GCs are triggered, only on what objects the GC looks
852 856 into. As a workaround, disable GC while building complex (huge)
853 857 containers.
854 858
855 859 This garbage collector issue have been fixed in 2.7.
856 860 """
857 861 def wrapper(*args, **kwargs):
858 862 gcenabled = gc.isenabled()
859 863 gc.disable()
860 864 try:
861 865 return func(*args, **kwargs)
862 866 finally:
863 867 if gcenabled:
864 868 gc.enable()
865 869 return wrapper
866 870
867 871 def pathto(root, n1, n2):
868 872 '''return the relative path from one place to another.
869 873 root should use os.sep to separate directories
870 874 n1 should use os.sep to separate directories
871 875 n2 should use "/" to separate directories
872 876 returns an os.sep-separated path.
873 877
874 878 If n1 is a relative path, it's assumed it's
875 879 relative to root.
876 880 n2 should always be relative to root.
877 881 '''
878 882 if not n1:
879 883 return localpath(n2)
880 884 if os.path.isabs(n1):
881 885 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
882 886 return os.path.join(root, localpath(n2))
883 887 n2 = '/'.join((pconvert(root), n2))
884 888 a, b = splitpath(n1), n2.split('/')
885 889 a.reverse()
886 890 b.reverse()
887 891 while a and b and a[-1] == b[-1]:
888 892 a.pop()
889 893 b.pop()
890 894 b.reverse()
891 895 return os.sep.join((['..'] * len(a)) + b) or '.'
892 896
893 897 def mainfrozen():
894 898 """return True if we are a frozen executable.
895 899
896 900 The code supports py2exe (most common, Windows only) and tools/freeze
897 901 (portable, not much used).
898 902 """
899 903 return (safehasattr(sys, "frozen") or # new py2exe
900 904 safehasattr(sys, "importers") or # old py2exe
901 905 imp.is_frozen("__main__")) # tools/freeze
902 906
903 907 # the location of data files matching the source code
904 908 if mainfrozen() and getattr(sys, 'frozen', None) != 'macosx_app':
905 909 # executable version (py2exe) doesn't support __file__
906 910 datapath = os.path.dirname(sys.executable)
907 911 else:
908 912 datapath = os.path.dirname(__file__)
909 913
910 914 i18n.setdatapath(datapath)
911 915
912 916 _hgexecutable = None
913 917
914 918 def hgexecutable():
915 919 """return location of the 'hg' executable.
916 920
917 921 Defaults to $HG or 'hg' in the search path.
918 922 """
919 923 if _hgexecutable is None:
920 924 hg = os.environ.get('HG')
921 925 mainmod = sys.modules['__main__']
922 926 if hg:
923 927 _sethgexecutable(hg)
924 928 elif mainfrozen():
925 929 if getattr(sys, 'frozen', None) == 'macosx_app':
926 930 # Env variable set by py2app
927 931 _sethgexecutable(os.environ['EXECUTABLEPATH'])
928 932 else:
929 933 _sethgexecutable(sys.executable)
930 934 elif os.path.basename(getattr(mainmod, '__file__', '')) == 'hg':
931 935 _sethgexecutable(mainmod.__file__)
932 936 else:
933 937 exe = findexe('hg') or os.path.basename(sys.argv[0])
934 938 _sethgexecutable(exe)
935 939 return _hgexecutable
936 940
937 941 def _sethgexecutable(path):
938 942 """set location of the 'hg' executable"""
939 943 global _hgexecutable
940 944 _hgexecutable = path
941 945
942 946 def _isstdout(f):
943 947 fileno = getattr(f, 'fileno', None)
944 948 return fileno and fileno() == sys.__stdout__.fileno()
945 949
946 950 def system(cmd, environ=None, cwd=None, onerr=None, errprefix=None, out=None):
947 951 '''enhanced shell command execution.
948 952 run with environment maybe modified, maybe in different dir.
949 953
950 954 if command fails and onerr is None, return status, else raise onerr
951 955 object as exception.
952 956
953 957 if out is specified, it is assumed to be a file-like object that has a
954 958 write() method. stdout and stderr will be redirected to out.'''
955 959 if environ is None:
956 960 environ = {}
957 961 try:
958 962 sys.stdout.flush()
959 963 except Exception:
960 964 pass
961 965 def py2shell(val):
962 966 'convert python object into string that is useful to shell'
963 967 if val is None or val is False:
964 968 return '0'
965 969 if val is True:
966 970 return '1'
967 971 return str(val)
968 972 origcmd = cmd
969 973 cmd = quotecommand(cmd)
970 974 if sys.platform == 'plan9' and (sys.version_info[0] == 2
971 975 and sys.version_info[1] < 7):
972 976 # subprocess kludge to work around issues in half-baked Python
973 977 # ports, notably bichued/python:
974 978 if not cwd is None:
975 979 os.chdir(cwd)
976 980 rc = os.system(cmd)
977 981 else:
978 982 env = dict(os.environ)
979 983 env.update((k, py2shell(v)) for k, v in environ.iteritems())
980 984 env['HG'] = hgexecutable()
981 985 if out is None or _isstdout(out):
982 986 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
983 987 env=env, cwd=cwd)
984 988 else:
985 989 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
986 990 env=env, cwd=cwd, stdout=subprocess.PIPE,
987 991 stderr=subprocess.STDOUT)
988 992 while True:
989 993 line = proc.stdout.readline()
990 994 if not line:
991 995 break
992 996 out.write(line)
993 997 proc.wait()
994 998 rc = proc.returncode
995 999 if sys.platform == 'OpenVMS' and rc & 1:
996 1000 rc = 0
997 1001 if rc and onerr:
998 1002 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
999 1003 explainexit(rc)[0])
1000 1004 if errprefix:
1001 1005 errmsg = '%s: %s' % (errprefix, errmsg)
1002 1006 raise onerr(errmsg)
1003 1007 return rc
1004 1008
1005 1009 def checksignature(func):
1006 1010 '''wrap a function with code to check for calling errors'''
1007 1011 def check(*args, **kwargs):
1008 1012 try:
1009 1013 return func(*args, **kwargs)
1010 1014 except TypeError:
1011 1015 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
1012 1016 raise error.SignatureError
1013 1017 raise
1014 1018
1015 1019 return check
1016 1020
1017 1021 def copyfile(src, dest, hardlink=False, copystat=False, checkambig=False):
1018 1022 '''copy a file, preserving mode and optionally other stat info like
1019 1023 atime/mtime
1020 1024
1021 1025 checkambig argument is used with filestat, and is useful only if
1022 1026 destination file is guarded by any lock (e.g. repo.lock or
1023 1027 repo.wlock).
1024 1028
1025 1029 copystat and checkambig should be exclusive.
1026 1030 '''
1027 1031 assert not (copystat and checkambig)
1028 1032 oldstat = None
1029 1033 if os.path.lexists(dest):
1030 1034 if checkambig:
1031 1035 oldstat = checkambig and filestat(dest)
1032 1036 unlink(dest)
1033 1037 # hardlinks are problematic on CIFS, quietly ignore this flag
1034 1038 # until we find a way to work around it cleanly (issue4546)
1035 1039 if False and hardlink:
1036 1040 try:
1037 1041 oslink(src, dest)
1038 1042 return
1039 1043 except (IOError, OSError):
1040 1044 pass # fall back to normal copy
1041 1045 if os.path.islink(src):
1042 1046 os.symlink(os.readlink(src), dest)
1043 1047 # copytime is ignored for symlinks, but in general copytime isn't needed
1044 1048 # for them anyway
1045 1049 else:
1046 1050 try:
1047 1051 shutil.copyfile(src, dest)
1048 1052 if copystat:
1049 1053 # copystat also copies mode
1050 1054 shutil.copystat(src, dest)
1051 1055 else:
1052 1056 shutil.copymode(src, dest)
1053 1057 if oldstat and oldstat.stat:
1054 1058 newstat = filestat(dest)
1055 1059 if newstat.isambig(oldstat):
1056 1060 # stat of copied file is ambiguous to original one
1057 1061 advanced = (oldstat.stat.st_mtime + 1) & 0x7fffffff
1058 1062 os.utime(dest, (advanced, advanced))
1059 1063 except shutil.Error as inst:
1060 1064 raise Abort(str(inst))
1061 1065
1062 1066 def copyfiles(src, dst, hardlink=None, progress=lambda t, pos: None):
1063 1067 """Copy a directory tree using hardlinks if possible."""
1064 1068 num = 0
1065 1069
1066 1070 if hardlink is None:
1067 1071 hardlink = (os.stat(src).st_dev ==
1068 1072 os.stat(os.path.dirname(dst)).st_dev)
1069 1073 if hardlink:
1070 1074 topic = _('linking')
1071 1075 else:
1072 1076 topic = _('copying')
1073 1077
1074 1078 if os.path.isdir(src):
1075 1079 os.mkdir(dst)
1076 1080 for name, kind in osutil.listdir(src):
1077 1081 srcname = os.path.join(src, name)
1078 1082 dstname = os.path.join(dst, name)
1079 1083 def nprog(t, pos):
1080 1084 if pos is not None:
1081 1085 return progress(t, pos + num)
1082 1086 hardlink, n = copyfiles(srcname, dstname, hardlink, progress=nprog)
1083 1087 num += n
1084 1088 else:
1085 1089 if hardlink:
1086 1090 try:
1087 1091 oslink(src, dst)
1088 1092 except (IOError, OSError):
1089 1093 hardlink = False
1090 1094 shutil.copy(src, dst)
1091 1095 else:
1092 1096 shutil.copy(src, dst)
1093 1097 num += 1
1094 1098 progress(topic, num)
1095 1099 progress(topic, None)
1096 1100
1097 1101 return hardlink, num
1098 1102
1099 1103 _winreservednames = '''con prn aux nul
1100 1104 com1 com2 com3 com4 com5 com6 com7 com8 com9
1101 1105 lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
1102 1106 _winreservedchars = ':*?"<>|'
1103 1107 def checkwinfilename(path):
1104 1108 r'''Check that the base-relative path is a valid filename on Windows.
1105 1109 Returns None if the path is ok, or a UI string describing the problem.
1106 1110
1107 1111 >>> checkwinfilename("just/a/normal/path")
1108 1112 >>> checkwinfilename("foo/bar/con.xml")
1109 1113 "filename contains 'con', which is reserved on Windows"
1110 1114 >>> checkwinfilename("foo/con.xml/bar")
1111 1115 "filename contains 'con', which is reserved on Windows"
1112 1116 >>> checkwinfilename("foo/bar/xml.con")
1113 1117 >>> checkwinfilename("foo/bar/AUX/bla.txt")
1114 1118 "filename contains 'AUX', which is reserved on Windows"
1115 1119 >>> checkwinfilename("foo/bar/bla:.txt")
1116 1120 "filename contains ':', which is reserved on Windows"
1117 1121 >>> checkwinfilename("foo/bar/b\07la.txt")
1118 1122 "filename contains '\\x07', which is invalid on Windows"
1119 1123 >>> checkwinfilename("foo/bar/bla ")
1120 1124 "filename ends with ' ', which is not allowed on Windows"
1121 1125 >>> checkwinfilename("../bar")
1122 1126 >>> checkwinfilename("foo\\")
1123 1127 "filename ends with '\\', which is invalid on Windows"
1124 1128 >>> checkwinfilename("foo\\/bar")
1125 1129 "directory name ends with '\\', which is invalid on Windows"
1126 1130 '''
1127 1131 if path.endswith('\\'):
1128 1132 return _("filename ends with '\\', which is invalid on Windows")
1129 1133 if '\\/' in path:
1130 1134 return _("directory name ends with '\\', which is invalid on Windows")
1131 1135 for n in path.replace('\\', '/').split('/'):
1132 1136 if not n:
1133 1137 continue
1134 1138 for c in n:
1135 1139 if c in _winreservedchars:
1136 1140 return _("filename contains '%s', which is reserved "
1137 1141 "on Windows") % c
1138 1142 if ord(c) <= 31:
1139 1143 return _("filename contains %r, which is invalid "
1140 1144 "on Windows") % c
1141 1145 base = n.split('.')[0]
1142 1146 if base and base.lower() in _winreservednames:
1143 1147 return _("filename contains '%s', which is reserved "
1144 1148 "on Windows") % base
1145 1149 t = n[-1]
1146 1150 if t in '. ' and n not in '..':
1147 1151 return _("filename ends with '%s', which is not allowed "
1148 1152 "on Windows") % t
1149 1153
1150 1154 if os.name == 'nt':
1151 1155 checkosfilename = checkwinfilename
1152 1156 else:
1153 1157 checkosfilename = platform.checkosfilename
1154 1158
1155 1159 def makelock(info, pathname):
1156 1160 try:
1157 1161 return os.symlink(info, pathname)
1158 1162 except OSError as why:
1159 1163 if why.errno == errno.EEXIST:
1160 1164 raise
1161 1165 except AttributeError: # no symlink in os
1162 1166 pass
1163 1167
1164 1168 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
1165 1169 os.write(ld, info)
1166 1170 os.close(ld)
1167 1171
1168 1172 def readlock(pathname):
1169 1173 try:
1170 1174 return os.readlink(pathname)
1171 1175 except OSError as why:
1172 1176 if why.errno not in (errno.EINVAL, errno.ENOSYS):
1173 1177 raise
1174 1178 except AttributeError: # no symlink in os
1175 1179 pass
1176 1180 fp = posixfile(pathname)
1177 1181 r = fp.read()
1178 1182 fp.close()
1179 1183 return r
1180 1184
1181 1185 def fstat(fp):
1182 1186 '''stat file object that may not have fileno method.'''
1183 1187 try:
1184 1188 return os.fstat(fp.fileno())
1185 1189 except AttributeError:
1186 1190 return os.stat(fp.name)
1187 1191
1188 1192 # File system features
1189 1193
1190 1194 def checkcase(path):
1191 1195 """
1192 1196 Return true if the given path is on a case-sensitive filesystem
1193 1197
1194 1198 Requires a path (like /foo/.hg) ending with a foldable final
1195 1199 directory component.
1196 1200 """
1197 1201 s1 = os.lstat(path)
1198 1202 d, b = os.path.split(path)
1199 1203 b2 = b.upper()
1200 1204 if b == b2:
1201 1205 b2 = b.lower()
1202 1206 if b == b2:
1203 1207 return True # no evidence against case sensitivity
1204 1208 p2 = os.path.join(d, b2)
1205 1209 try:
1206 1210 s2 = os.lstat(p2)
1207 1211 if s2 == s1:
1208 1212 return False
1209 1213 return True
1210 1214 except OSError:
1211 1215 return True
1212 1216
1213 1217 try:
1214 1218 import re2
1215 1219 _re2 = None
1216 1220 except ImportError:
1217 1221 _re2 = False
1218 1222
1219 1223 class _re(object):
1220 1224 def _checkre2(self):
1221 1225 global _re2
1222 1226 try:
1223 1227 # check if match works, see issue3964
1224 1228 _re2 = bool(re2.match(r'\[([^\[]+)\]', '[ui]'))
1225 1229 except ImportError:
1226 1230 _re2 = False
1227 1231
1228 1232 def compile(self, pat, flags=0):
1229 1233 '''Compile a regular expression, using re2 if possible
1230 1234
1231 1235 For best performance, use only re2-compatible regexp features. The
1232 1236 only flags from the re module that are re2-compatible are
1233 1237 IGNORECASE and MULTILINE.'''
1234 1238 if _re2 is None:
1235 1239 self._checkre2()
1236 1240 if _re2 and (flags & ~(remod.IGNORECASE | remod.MULTILINE)) == 0:
1237 1241 if flags & remod.IGNORECASE:
1238 1242 pat = '(?i)' + pat
1239 1243 if flags & remod.MULTILINE:
1240 1244 pat = '(?m)' + pat
1241 1245 try:
1242 1246 return re2.compile(pat)
1243 1247 except re2.error:
1244 1248 pass
1245 1249 return remod.compile(pat, flags)
1246 1250
1247 1251 @propertycache
1248 1252 def escape(self):
1249 1253 '''Return the version of escape corresponding to self.compile.
1250 1254
1251 1255 This is imperfect because whether re2 or re is used for a particular
1252 1256 function depends on the flags, etc, but it's the best we can do.
1253 1257 '''
1254 1258 global _re2
1255 1259 if _re2 is None:
1256 1260 self._checkre2()
1257 1261 if _re2:
1258 1262 return re2.escape
1259 1263 else:
1260 1264 return remod.escape
1261 1265
1262 1266 re = _re()
1263 1267
1264 1268 _fspathcache = {}
1265 1269 def fspath(name, root):
1266 1270 '''Get name in the case stored in the filesystem
1267 1271
1268 1272 The name should be relative to root, and be normcase-ed for efficiency.
1269 1273
1270 1274 Note that this function is unnecessary, and should not be
1271 1275 called, for case-sensitive filesystems (simply because it's expensive).
1272 1276
1273 1277 The root should be normcase-ed, too.
1274 1278 '''
1275 1279 def _makefspathcacheentry(dir):
1276 1280 return dict((normcase(n), n) for n in os.listdir(dir))
1277 1281
1278 1282 seps = os.sep
1279 1283 if os.altsep:
1280 1284 seps = seps + os.altsep
1281 1285 # Protect backslashes. This gets silly very quickly.
1282 1286 seps.replace('\\','\\\\')
1283 1287 pattern = remod.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
1284 1288 dir = os.path.normpath(root)
1285 1289 result = []
1286 1290 for part, sep in pattern.findall(name):
1287 1291 if sep:
1288 1292 result.append(sep)
1289 1293 continue
1290 1294
1291 1295 if dir not in _fspathcache:
1292 1296 _fspathcache[dir] = _makefspathcacheentry(dir)
1293 1297 contents = _fspathcache[dir]
1294 1298
1295 1299 found = contents.get(part)
1296 1300 if not found:
1297 1301 # retry "once per directory" per "dirstate.walk" which
1298 1302 # may take place for each patches of "hg qpush", for example
1299 1303 _fspathcache[dir] = contents = _makefspathcacheentry(dir)
1300 1304 found = contents.get(part)
1301 1305
1302 1306 result.append(found or part)
1303 1307 dir = os.path.join(dir, part)
1304 1308
1305 1309 return ''.join(result)
1306 1310
1307 1311 def checknlink(testfile):
1308 1312 '''check whether hardlink count reporting works properly'''
1309 1313
1310 1314 # testfile may be open, so we need a separate file for checking to
1311 1315 # work around issue2543 (or testfile may get lost on Samba shares)
1312 1316 f1 = testfile + ".hgtmp1"
1313 1317 if os.path.lexists(f1):
1314 1318 return False
1315 1319 try:
1316 1320 posixfile(f1, 'w').close()
1317 1321 except IOError:
1318 1322 return False
1319 1323
1320 1324 f2 = testfile + ".hgtmp2"
1321 1325 fd = None
1322 1326 try:
1323 1327 oslink(f1, f2)
1324 1328 # nlinks() may behave differently for files on Windows shares if
1325 1329 # the file is open.
1326 1330 fd = posixfile(f2)
1327 1331 return nlinks(f2) > 1
1328 1332 except OSError:
1329 1333 return False
1330 1334 finally:
1331 1335 if fd is not None:
1332 1336 fd.close()
1333 1337 for f in (f1, f2):
1334 1338 try:
1335 1339 os.unlink(f)
1336 1340 except OSError:
1337 1341 pass
1338 1342
1339 1343 def endswithsep(path):
1340 1344 '''Check path ends with os.sep or os.altsep.'''
1341 1345 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
1342 1346
1343 1347 def splitpath(path):
1344 1348 '''Split path by os.sep.
1345 1349 Note that this function does not use os.altsep because this is
1346 1350 an alternative of simple "xxx.split(os.sep)".
1347 1351 It is recommended to use os.path.normpath() before using this
1348 1352 function if need.'''
1349 1353 return path.split(os.sep)
1350 1354
1351 1355 def gui():
1352 1356 '''Are we running in a GUI?'''
1353 1357 if sys.platform == 'darwin':
1354 1358 if 'SSH_CONNECTION' in os.environ:
1355 1359 # handle SSH access to a box where the user is logged in
1356 1360 return False
1357 1361 elif getattr(osutil, 'isgui', None):
1358 1362 # check if a CoreGraphics session is available
1359 1363 return osutil.isgui()
1360 1364 else:
1361 1365 # pure build; use a safe default
1362 1366 return True
1363 1367 else:
1364 1368 return os.name == "nt" or os.environ.get("DISPLAY")
1365 1369
1366 1370 def mktempcopy(name, emptyok=False, createmode=None):
1367 1371 """Create a temporary file with the same contents from name
1368 1372
1369 1373 The permission bits are copied from the original file.
1370 1374
1371 1375 If the temporary file is going to be truncated immediately, you
1372 1376 can use emptyok=True as an optimization.
1373 1377
1374 1378 Returns the name of the temporary file.
1375 1379 """
1376 1380 d, fn = os.path.split(name)
1377 1381 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1378 1382 os.close(fd)
1379 1383 # Temporary files are created with mode 0600, which is usually not
1380 1384 # what we want. If the original file already exists, just copy
1381 1385 # its mode. Otherwise, manually obey umask.
1382 1386 copymode(name, temp, createmode)
1383 1387 if emptyok:
1384 1388 return temp
1385 1389 try:
1386 1390 try:
1387 1391 ifp = posixfile(name, "rb")
1388 1392 except IOError as inst:
1389 1393 if inst.errno == errno.ENOENT:
1390 1394 return temp
1391 1395 if not getattr(inst, 'filename', None):
1392 1396 inst.filename = name
1393 1397 raise
1394 1398 ofp = posixfile(temp, "wb")
1395 1399 for chunk in filechunkiter(ifp):
1396 1400 ofp.write(chunk)
1397 1401 ifp.close()
1398 1402 ofp.close()
1399 1403 except: # re-raises
1400 1404 try: os.unlink(temp)
1401 1405 except OSError: pass
1402 1406 raise
1403 1407 return temp
1404 1408
1405 1409 class filestat(object):
1406 1410 """help to exactly detect change of a file
1407 1411
1408 1412 'stat' attribute is result of 'os.stat()' if specified 'path'
1409 1413 exists. Otherwise, it is None. This can avoid preparative
1410 1414 'exists()' examination on client side of this class.
1411 1415 """
1412 1416 def __init__(self, path):
1413 1417 try:
1414 1418 self.stat = os.stat(path)
1415 1419 except OSError as err:
1416 1420 if err.errno != errno.ENOENT:
1417 1421 raise
1418 1422 self.stat = None
1419 1423
1420 1424 __hash__ = object.__hash__
1421 1425
1422 1426 def __eq__(self, old):
1423 1427 try:
1424 1428 # if ambiguity between stat of new and old file is
1425 1429 # avoided, comparision of size, ctime and mtime is enough
1426 1430 # to exactly detect change of a file regardless of platform
1427 1431 return (self.stat.st_size == old.stat.st_size and
1428 1432 self.stat.st_ctime == old.stat.st_ctime and
1429 1433 self.stat.st_mtime == old.stat.st_mtime)
1430 1434 except AttributeError:
1431 1435 return False
1432 1436
1433 1437 def isambig(self, old):
1434 1438 """Examine whether new (= self) stat is ambiguous against old one
1435 1439
1436 1440 "S[N]" below means stat of a file at N-th change:
1437 1441
1438 1442 - S[n-1].ctime < S[n].ctime: can detect change of a file
1439 1443 - S[n-1].ctime == S[n].ctime
1440 1444 - S[n-1].ctime < S[n].mtime: means natural advancing (*1)
1441 1445 - S[n-1].ctime == S[n].mtime: is ambiguous (*2)
1442 1446 - S[n-1].ctime > S[n].mtime: never occurs naturally (don't care)
1443 1447 - S[n-1].ctime > S[n].ctime: never occurs naturally (don't care)
1444 1448
1445 1449 Case (*2) above means that a file was changed twice or more at
1446 1450 same time in sec (= S[n-1].ctime), and comparison of timestamp
1447 1451 is ambiguous.
1448 1452
1449 1453 Base idea to avoid such ambiguity is "advance mtime 1 sec, if
1450 1454 timestamp is ambiguous".
1451 1455
1452 1456 But advancing mtime only in case (*2) doesn't work as
1453 1457 expected, because naturally advanced S[n].mtime in case (*1)
1454 1458 might be equal to manually advanced S[n-1 or earlier].mtime.
1455 1459
1456 1460 Therefore, all "S[n-1].ctime == S[n].ctime" cases should be
1457 1461 treated as ambiguous regardless of mtime, to avoid overlooking
1458 1462 by confliction between such mtime.
1459 1463
1460 1464 Advancing mtime "if isambig(oldstat)" ensures "S[n-1].mtime !=
1461 1465 S[n].mtime", even if size of a file isn't changed.
1462 1466 """
1463 1467 try:
1464 1468 return (self.stat.st_ctime == old.stat.st_ctime)
1465 1469 except AttributeError:
1466 1470 return False
1467 1471
1468 1472 def __ne__(self, other):
1469 1473 return not self == other
1470 1474
1471 1475 class atomictempfile(object):
1472 1476 '''writable file object that atomically updates a file
1473 1477
1474 1478 All writes will go to a temporary copy of the original file. Call
1475 1479 close() when you are done writing, and atomictempfile will rename
1476 1480 the temporary copy to the original name, making the changes
1477 1481 visible. If the object is destroyed without being closed, all your
1478 1482 writes are discarded.
1479 1483
1480 1484 checkambig argument of constructor is used with filestat, and is
1481 1485 useful only if target file is guarded by any lock (e.g. repo.lock
1482 1486 or repo.wlock).
1483 1487 '''
1484 1488 def __init__(self, name, mode='w+b', createmode=None, checkambig=False):
1485 1489 self.__name = name # permanent name
1486 1490 self._tempname = mktempcopy(name, emptyok=('w' in mode),
1487 1491 createmode=createmode)
1488 1492 self._fp = posixfile(self._tempname, mode)
1489 1493 self._checkambig = checkambig
1490 1494
1491 1495 # delegated methods
1492 1496 self.read = self._fp.read
1493 1497 self.write = self._fp.write
1494 1498 self.seek = self._fp.seek
1495 1499 self.tell = self._fp.tell
1496 1500 self.fileno = self._fp.fileno
1497 1501
1498 1502 def close(self):
1499 1503 if not self._fp.closed:
1500 1504 self._fp.close()
1501 1505 filename = localpath(self.__name)
1502 1506 oldstat = self._checkambig and filestat(filename)
1503 1507 if oldstat and oldstat.stat:
1504 1508 rename(self._tempname, filename)
1505 1509 newstat = filestat(filename)
1506 1510 if newstat.isambig(oldstat):
1507 1511 # stat of changed file is ambiguous to original one
1508 1512 advanced = (oldstat.stat.st_mtime + 1) & 0x7fffffff
1509 1513 os.utime(filename, (advanced, advanced))
1510 1514 else:
1511 1515 rename(self._tempname, filename)
1512 1516
1513 1517 def discard(self):
1514 1518 if not self._fp.closed:
1515 1519 try:
1516 1520 os.unlink(self._tempname)
1517 1521 except OSError:
1518 1522 pass
1519 1523 self._fp.close()
1520 1524
1521 1525 def __del__(self):
1522 1526 if safehasattr(self, '_fp'): # constructor actually did something
1523 1527 self.discard()
1524 1528
1525 1529 def __enter__(self):
1526 1530 return self
1527 1531
1528 1532 def __exit__(self, exctype, excvalue, traceback):
1529 1533 if exctype is not None:
1530 1534 self.discard()
1531 1535 else:
1532 1536 self.close()
1533 1537
1534 1538 def makedirs(name, mode=None, notindexed=False):
1535 1539 """recursive directory creation with parent mode inheritance
1536 1540
1537 1541 Newly created directories are marked as "not to be indexed by
1538 1542 the content indexing service", if ``notindexed`` is specified
1539 1543 for "write" mode access.
1540 1544 """
1541 1545 try:
1542 1546 makedir(name, notindexed)
1543 1547 except OSError as err:
1544 1548 if err.errno == errno.EEXIST:
1545 1549 return
1546 1550 if err.errno != errno.ENOENT or not name:
1547 1551 raise
1548 1552 parent = os.path.dirname(os.path.abspath(name))
1549 1553 if parent == name:
1550 1554 raise
1551 1555 makedirs(parent, mode, notindexed)
1552 1556 try:
1553 1557 makedir(name, notindexed)
1554 1558 except OSError as err:
1555 1559 # Catch EEXIST to handle races
1556 1560 if err.errno == errno.EEXIST:
1557 1561 return
1558 1562 raise
1559 1563 if mode is not None:
1560 1564 os.chmod(name, mode)
1561 1565
1562 1566 def readfile(path):
1563 1567 with open(path, 'rb') as fp:
1564 1568 return fp.read()
1565 1569
1566 1570 def writefile(path, text):
1567 1571 with open(path, 'wb') as fp:
1568 1572 fp.write(text)
1569 1573
1570 1574 def appendfile(path, text):
1571 1575 with open(path, 'ab') as fp:
1572 1576 fp.write(text)
1573 1577
1574 1578 class chunkbuffer(object):
1575 1579 """Allow arbitrary sized chunks of data to be efficiently read from an
1576 1580 iterator over chunks of arbitrary size."""
1577 1581
1578 1582 def __init__(self, in_iter):
1579 1583 """in_iter is the iterator that's iterating over the input chunks.
1580 1584 targetsize is how big a buffer to try to maintain."""
1581 1585 def splitbig(chunks):
1582 1586 for chunk in chunks:
1583 1587 if len(chunk) > 2**20:
1584 1588 pos = 0
1585 1589 while pos < len(chunk):
1586 1590 end = pos + 2 ** 18
1587 1591 yield chunk[pos:end]
1588 1592 pos = end
1589 1593 else:
1590 1594 yield chunk
1591 1595 self.iter = splitbig(in_iter)
1592 1596 self._queue = collections.deque()
1593 1597 self._chunkoffset = 0
1594 1598
1595 1599 def read(self, l=None):
1596 1600 """Read L bytes of data from the iterator of chunks of data.
1597 1601 Returns less than L bytes if the iterator runs dry.
1598 1602
1599 1603 If size parameter is omitted, read everything"""
1600 1604 if l is None:
1601 1605 return ''.join(self.iter)
1602 1606
1603 1607 left = l
1604 1608 buf = []
1605 1609 queue = self._queue
1606 1610 while left > 0:
1607 1611 # refill the queue
1608 1612 if not queue:
1609 1613 target = 2**18
1610 1614 for chunk in self.iter:
1611 1615 queue.append(chunk)
1612 1616 target -= len(chunk)
1613 1617 if target <= 0:
1614 1618 break
1615 1619 if not queue:
1616 1620 break
1617 1621
1618 1622 # The easy way to do this would be to queue.popleft(), modify the
1619 1623 # chunk (if necessary), then queue.appendleft(). However, for cases
1620 1624 # where we read partial chunk content, this incurs 2 dequeue
1621 1625 # mutations and creates a new str for the remaining chunk in the
1622 1626 # queue. Our code below avoids this overhead.
1623 1627
1624 1628 chunk = queue[0]
1625 1629 chunkl = len(chunk)
1626 1630 offset = self._chunkoffset
1627 1631
1628 1632 # Use full chunk.
1629 1633 if offset == 0 and left >= chunkl:
1630 1634 left -= chunkl
1631 1635 queue.popleft()
1632 1636 buf.append(chunk)
1633 1637 # self._chunkoffset remains at 0.
1634 1638 continue
1635 1639
1636 1640 chunkremaining = chunkl - offset
1637 1641
1638 1642 # Use all of unconsumed part of chunk.
1639 1643 if left >= chunkremaining:
1640 1644 left -= chunkremaining
1641 1645 queue.popleft()
1642 1646 # offset == 0 is enabled by block above, so this won't merely
1643 1647 # copy via ``chunk[0:]``.
1644 1648 buf.append(chunk[offset:])
1645 1649 self._chunkoffset = 0
1646 1650
1647 1651 # Partial chunk needed.
1648 1652 else:
1649 1653 buf.append(chunk[offset:offset + left])
1650 1654 self._chunkoffset += left
1651 1655 left -= chunkremaining
1652 1656
1653 1657 return ''.join(buf)
1654 1658
1655 1659 def filechunkiter(f, size=65536, limit=None):
1656 1660 """Create a generator that produces the data in the file size
1657 1661 (default 65536) bytes at a time, up to optional limit (default is
1658 1662 to read all data). Chunks may be less than size bytes if the
1659 1663 chunk is the last chunk in the file, or the file is a socket or
1660 1664 some other type of file that sometimes reads less data than is
1661 1665 requested."""
1662 1666 assert size >= 0
1663 1667 assert limit is None or limit >= 0
1664 1668 while True:
1665 1669 if limit is None:
1666 1670 nbytes = size
1667 1671 else:
1668 1672 nbytes = min(limit, size)
1669 1673 s = nbytes and f.read(nbytes)
1670 1674 if not s:
1671 1675 break
1672 1676 if limit:
1673 1677 limit -= len(s)
1674 1678 yield s
1675 1679
1676 1680 def makedate(timestamp=None):
1677 1681 '''Return a unix timestamp (or the current time) as a (unixtime,
1678 1682 offset) tuple based off the local timezone.'''
1679 1683 if timestamp is None:
1680 1684 timestamp = time.time()
1681 1685 if timestamp < 0:
1682 1686 hint = _("check your clock")
1683 1687 raise Abort(_("negative timestamp: %d") % timestamp, hint=hint)
1684 1688 delta = (datetime.datetime.utcfromtimestamp(timestamp) -
1685 1689 datetime.datetime.fromtimestamp(timestamp))
1686 1690 tz = delta.days * 86400 + delta.seconds
1687 1691 return timestamp, tz
1688 1692
1689 1693 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1690 1694 """represent a (unixtime, offset) tuple as a localized time.
1691 1695 unixtime is seconds since the epoch, and offset is the time zone's
1692 1696 number of seconds away from UTC.
1693 1697
1694 1698 >>> datestr((0, 0))
1695 1699 'Thu Jan 01 00:00:00 1970 +0000'
1696 1700 >>> datestr((42, 0))
1697 1701 'Thu Jan 01 00:00:42 1970 +0000'
1698 1702 >>> datestr((-42, 0))
1699 1703 'Wed Dec 31 23:59:18 1969 +0000'
1700 1704 >>> datestr((0x7fffffff, 0))
1701 1705 'Tue Jan 19 03:14:07 2038 +0000'
1702 1706 >>> datestr((-0x80000000, 0))
1703 1707 'Fri Dec 13 20:45:52 1901 +0000'
1704 1708 """
1705 1709 t, tz = date or makedate()
1706 1710 if "%1" in format or "%2" in format or "%z" in format:
1707 1711 sign = (tz > 0) and "-" or "+"
1708 1712 minutes = abs(tz) // 60
1709 1713 q, r = divmod(minutes, 60)
1710 1714 format = format.replace("%z", "%1%2")
1711 1715 format = format.replace("%1", "%c%02d" % (sign, q))
1712 1716 format = format.replace("%2", "%02d" % r)
1713 1717 d = t - tz
1714 1718 if d > 0x7fffffff:
1715 1719 d = 0x7fffffff
1716 1720 elif d < -0x80000000:
1717 1721 d = -0x80000000
1718 1722 # Never use time.gmtime() and datetime.datetime.fromtimestamp()
1719 1723 # because they use the gmtime() system call which is buggy on Windows
1720 1724 # for negative values.
1721 1725 t = datetime.datetime(1970, 1, 1) + datetime.timedelta(seconds=d)
1722 1726 s = t.strftime(format)
1723 1727 return s
1724 1728
1725 1729 def shortdate(date=None):
1726 1730 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1727 1731 return datestr(date, format='%Y-%m-%d')
1728 1732
1729 1733 def parsetimezone(tz):
1730 1734 """parse a timezone string and return an offset integer"""
1731 1735 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1732 1736 sign = (tz[0] == "+") and 1 or -1
1733 1737 hours = int(tz[1:3])
1734 1738 minutes = int(tz[3:5])
1735 1739 return -sign * (hours * 60 + minutes) * 60
1736 1740 if tz == "GMT" or tz == "UTC":
1737 1741 return 0
1738 1742 return None
1739 1743
1740 1744 def strdate(string, format, defaults=[]):
1741 1745 """parse a localized time string and return a (unixtime, offset) tuple.
1742 1746 if the string cannot be parsed, ValueError is raised."""
1743 1747 # NOTE: unixtime = localunixtime + offset
1744 1748 offset, date = parsetimezone(string.split()[-1]), string
1745 1749 if offset is not None:
1746 1750 date = " ".join(string.split()[:-1])
1747 1751
1748 1752 # add missing elements from defaults
1749 1753 usenow = False # default to using biased defaults
1750 1754 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
1751 1755 found = [True for p in part if ("%"+p) in format]
1752 1756 if not found:
1753 1757 date += "@" + defaults[part][usenow]
1754 1758 format += "@%" + part[0]
1755 1759 else:
1756 1760 # We've found a specific time element, less specific time
1757 1761 # elements are relative to today
1758 1762 usenow = True
1759 1763
1760 1764 timetuple = time.strptime(date, format)
1761 1765 localunixtime = int(calendar.timegm(timetuple))
1762 1766 if offset is None:
1763 1767 # local timezone
1764 1768 unixtime = int(time.mktime(timetuple))
1765 1769 offset = unixtime - localunixtime
1766 1770 else:
1767 1771 unixtime = localunixtime + offset
1768 1772 return unixtime, offset
1769 1773
1770 1774 def parsedate(date, formats=None, bias=None):
1771 1775 """parse a localized date/time and return a (unixtime, offset) tuple.
1772 1776
1773 1777 The date may be a "unixtime offset" string or in one of the specified
1774 1778 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1775 1779
1776 1780 >>> parsedate(' today ') == parsedate(\
1777 1781 datetime.date.today().strftime('%b %d'))
1778 1782 True
1779 1783 >>> parsedate( 'yesterday ') == parsedate((datetime.date.today() -\
1780 1784 datetime.timedelta(days=1)\
1781 1785 ).strftime('%b %d'))
1782 1786 True
1783 1787 >>> now, tz = makedate()
1784 1788 >>> strnow, strtz = parsedate('now')
1785 1789 >>> (strnow - now) < 1
1786 1790 True
1787 1791 >>> tz == strtz
1788 1792 True
1789 1793 """
1790 1794 if bias is None:
1791 1795 bias = {}
1792 1796 if not date:
1793 1797 return 0, 0
1794 1798 if isinstance(date, tuple) and len(date) == 2:
1795 1799 return date
1796 1800 if not formats:
1797 1801 formats = defaultdateformats
1798 1802 date = date.strip()
1799 1803
1800 1804 if date == 'now' or date == _('now'):
1801 1805 return makedate()
1802 1806 if date == 'today' or date == _('today'):
1803 1807 date = datetime.date.today().strftime('%b %d')
1804 1808 elif date == 'yesterday' or date == _('yesterday'):
1805 1809 date = (datetime.date.today() -
1806 1810 datetime.timedelta(days=1)).strftime('%b %d')
1807 1811
1808 1812 try:
1809 1813 when, offset = map(int, date.split(' '))
1810 1814 except ValueError:
1811 1815 # fill out defaults
1812 1816 now = makedate()
1813 1817 defaults = {}
1814 1818 for part in ("d", "mb", "yY", "HI", "M", "S"):
1815 1819 # this piece is for rounding the specific end of unknowns
1816 1820 b = bias.get(part)
1817 1821 if b is None:
1818 1822 if part[0] in "HMS":
1819 1823 b = "00"
1820 1824 else:
1821 1825 b = "0"
1822 1826
1823 1827 # this piece is for matching the generic end to today's date
1824 1828 n = datestr(now, "%" + part[0])
1825 1829
1826 1830 defaults[part] = (b, n)
1827 1831
1828 1832 for format in formats:
1829 1833 try:
1830 1834 when, offset = strdate(date, format, defaults)
1831 1835 except (ValueError, OverflowError):
1832 1836 pass
1833 1837 else:
1834 1838 break
1835 1839 else:
1836 1840 raise Abort(_('invalid date: %r') % date)
1837 1841 # validate explicit (probably user-specified) date and
1838 1842 # time zone offset. values must fit in signed 32 bits for
1839 1843 # current 32-bit linux runtimes. timezones go from UTC-12
1840 1844 # to UTC+14
1841 1845 if when < -0x80000000 or when > 0x7fffffff:
1842 1846 raise Abort(_('date exceeds 32 bits: %d') % when)
1843 1847 if offset < -50400 or offset > 43200:
1844 1848 raise Abort(_('impossible time zone offset: %d') % offset)
1845 1849 return when, offset
1846 1850
1847 1851 def matchdate(date):
1848 1852 """Return a function that matches a given date match specifier
1849 1853
1850 1854 Formats include:
1851 1855
1852 1856 '{date}' match a given date to the accuracy provided
1853 1857
1854 1858 '<{date}' on or before a given date
1855 1859
1856 1860 '>{date}' on or after a given date
1857 1861
1858 1862 >>> p1 = parsedate("10:29:59")
1859 1863 >>> p2 = parsedate("10:30:00")
1860 1864 >>> p3 = parsedate("10:30:59")
1861 1865 >>> p4 = parsedate("10:31:00")
1862 1866 >>> p5 = parsedate("Sep 15 10:30:00 1999")
1863 1867 >>> f = matchdate("10:30")
1864 1868 >>> f(p1[0])
1865 1869 False
1866 1870 >>> f(p2[0])
1867 1871 True
1868 1872 >>> f(p3[0])
1869 1873 True
1870 1874 >>> f(p4[0])
1871 1875 False
1872 1876 >>> f(p5[0])
1873 1877 False
1874 1878 """
1875 1879
1876 1880 def lower(date):
1877 1881 d = {'mb': "1", 'd': "1"}
1878 1882 return parsedate(date, extendeddateformats, d)[0]
1879 1883
1880 1884 def upper(date):
1881 1885 d = {'mb': "12", 'HI': "23", 'M': "59", 'S': "59"}
1882 1886 for days in ("31", "30", "29"):
1883 1887 try:
1884 1888 d["d"] = days
1885 1889 return parsedate(date, extendeddateformats, d)[0]
1886 1890 except Abort:
1887 1891 pass
1888 1892 d["d"] = "28"
1889 1893 return parsedate(date, extendeddateformats, d)[0]
1890 1894
1891 1895 date = date.strip()
1892 1896
1893 1897 if not date:
1894 1898 raise Abort(_("dates cannot consist entirely of whitespace"))
1895 1899 elif date[0] == "<":
1896 1900 if not date[1:]:
1897 1901 raise Abort(_("invalid day spec, use '<DATE'"))
1898 1902 when = upper(date[1:])
1899 1903 return lambda x: x <= when
1900 1904 elif date[0] == ">":
1901 1905 if not date[1:]:
1902 1906 raise Abort(_("invalid day spec, use '>DATE'"))
1903 1907 when = lower(date[1:])
1904 1908 return lambda x: x >= when
1905 1909 elif date[0] == "-":
1906 1910 try:
1907 1911 days = int(date[1:])
1908 1912 except ValueError:
1909 1913 raise Abort(_("invalid day spec: %s") % date[1:])
1910 1914 if days < 0:
1911 1915 raise Abort(_('%s must be nonnegative (see "hg help dates")')
1912 1916 % date[1:])
1913 1917 when = makedate()[0] - days * 3600 * 24
1914 1918 return lambda x: x >= when
1915 1919 elif " to " in date:
1916 1920 a, b = date.split(" to ")
1917 1921 start, stop = lower(a), upper(b)
1918 1922 return lambda x: x >= start and x <= stop
1919 1923 else:
1920 1924 start, stop = lower(date), upper(date)
1921 1925 return lambda x: x >= start and x <= stop
1922 1926
1923 1927 def stringmatcher(pattern):
1924 1928 """
1925 1929 accepts a string, possibly starting with 're:' or 'literal:' prefix.
1926 1930 returns the matcher name, pattern, and matcher function.
1927 1931 missing or unknown prefixes are treated as literal matches.
1928 1932
1929 1933 helper for tests:
1930 1934 >>> def test(pattern, *tests):
1931 1935 ... kind, pattern, matcher = stringmatcher(pattern)
1932 1936 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
1933 1937
1934 1938 exact matching (no prefix):
1935 1939 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
1936 1940 ('literal', 'abcdefg', [False, False, True])
1937 1941
1938 1942 regex matching ('re:' prefix)
1939 1943 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
1940 1944 ('re', 'a.+b', [False, False, True])
1941 1945
1942 1946 force exact matches ('literal:' prefix)
1943 1947 >>> test('literal:re:foobar', 'foobar', 're:foobar')
1944 1948 ('literal', 're:foobar', [False, True])
1945 1949
1946 1950 unknown prefixes are ignored and treated as literals
1947 1951 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
1948 1952 ('literal', 'foo:bar', [False, False, True])
1949 1953 """
1950 1954 if pattern.startswith('re:'):
1951 1955 pattern = pattern[3:]
1952 1956 try:
1953 1957 regex = remod.compile(pattern)
1954 1958 except remod.error as e:
1955 1959 raise error.ParseError(_('invalid regular expression: %s')
1956 1960 % e)
1957 1961 return 're', pattern, regex.search
1958 1962 elif pattern.startswith('literal:'):
1959 1963 pattern = pattern[8:]
1960 1964 return 'literal', pattern, pattern.__eq__
1961 1965
1962 1966 def shortuser(user):
1963 1967 """Return a short representation of a user name or email address."""
1964 1968 f = user.find('@')
1965 1969 if f >= 0:
1966 1970 user = user[:f]
1967 1971 f = user.find('<')
1968 1972 if f >= 0:
1969 1973 user = user[f + 1:]
1970 1974 f = user.find(' ')
1971 1975 if f >= 0:
1972 1976 user = user[:f]
1973 1977 f = user.find('.')
1974 1978 if f >= 0:
1975 1979 user = user[:f]
1976 1980 return user
1977 1981
1978 1982 def emailuser(user):
1979 1983 """Return the user portion of an email address."""
1980 1984 f = user.find('@')
1981 1985 if f >= 0:
1982 1986 user = user[:f]
1983 1987 f = user.find('<')
1984 1988 if f >= 0:
1985 1989 user = user[f + 1:]
1986 1990 return user
1987 1991
1988 1992 def email(author):
1989 1993 '''get email of author.'''
1990 1994 r = author.find('>')
1991 1995 if r == -1:
1992 1996 r = None
1993 1997 return author[author.find('<') + 1:r]
1994 1998
1995 1999 def ellipsis(text, maxlength=400):
1996 2000 """Trim string to at most maxlength (default: 400) columns in display."""
1997 2001 return encoding.trim(text, maxlength, ellipsis='...')
1998 2002
1999 2003 def unitcountfn(*unittable):
2000 2004 '''return a function that renders a readable count of some quantity'''
2001 2005
2002 2006 def go(count):
2003 2007 for multiplier, divisor, format in unittable:
2004 2008 if count >= divisor * multiplier:
2005 2009 return format % (count / float(divisor))
2006 2010 return unittable[-1][2] % count
2007 2011
2008 2012 return go
2009 2013
2010 2014 bytecount = unitcountfn(
2011 2015 (100, 1 << 30, _('%.0f GB')),
2012 2016 (10, 1 << 30, _('%.1f GB')),
2013 2017 (1, 1 << 30, _('%.2f GB')),
2014 2018 (100, 1 << 20, _('%.0f MB')),
2015 2019 (10, 1 << 20, _('%.1f MB')),
2016 2020 (1, 1 << 20, _('%.2f MB')),
2017 2021 (100, 1 << 10, _('%.0f KB')),
2018 2022 (10, 1 << 10, _('%.1f KB')),
2019 2023 (1, 1 << 10, _('%.2f KB')),
2020 2024 (1, 1, _('%.0f bytes')),
2021 2025 )
2022 2026
2023 2027 def uirepr(s):
2024 2028 # Avoid double backslash in Windows path repr()
2025 2029 return repr(s).replace('\\\\', '\\')
2026 2030
2027 2031 # delay import of textwrap
2028 2032 def MBTextWrapper(**kwargs):
2029 2033 class tw(textwrap.TextWrapper):
2030 2034 """
2031 2035 Extend TextWrapper for width-awareness.
2032 2036
2033 2037 Neither number of 'bytes' in any encoding nor 'characters' is
2034 2038 appropriate to calculate terminal columns for specified string.
2035 2039
2036 2040 Original TextWrapper implementation uses built-in 'len()' directly,
2037 2041 so overriding is needed to use width information of each characters.
2038 2042
2039 2043 In addition, characters classified into 'ambiguous' width are
2040 2044 treated as wide in East Asian area, but as narrow in other.
2041 2045
2042 2046 This requires use decision to determine width of such characters.
2043 2047 """
2044 2048 def _cutdown(self, ucstr, space_left):
2045 2049 l = 0
2046 2050 colwidth = encoding.ucolwidth
2047 2051 for i in xrange(len(ucstr)):
2048 2052 l += colwidth(ucstr[i])
2049 2053 if space_left < l:
2050 2054 return (ucstr[:i], ucstr[i:])
2051 2055 return ucstr, ''
2052 2056
2053 2057 # overriding of base class
2054 2058 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
2055 2059 space_left = max(width - cur_len, 1)
2056 2060
2057 2061 if self.break_long_words:
2058 2062 cut, res = self._cutdown(reversed_chunks[-1], space_left)
2059 2063 cur_line.append(cut)
2060 2064 reversed_chunks[-1] = res
2061 2065 elif not cur_line:
2062 2066 cur_line.append(reversed_chunks.pop())
2063 2067
2064 2068 # this overriding code is imported from TextWrapper of Python 2.6
2065 2069 # to calculate columns of string by 'encoding.ucolwidth()'
2066 2070 def _wrap_chunks(self, chunks):
2067 2071 colwidth = encoding.ucolwidth
2068 2072
2069 2073 lines = []
2070 2074 if self.width <= 0:
2071 2075 raise ValueError("invalid width %r (must be > 0)" % self.width)
2072 2076
2073 2077 # Arrange in reverse order so items can be efficiently popped
2074 2078 # from a stack of chucks.
2075 2079 chunks.reverse()
2076 2080
2077 2081 while chunks:
2078 2082
2079 2083 # Start the list of chunks that will make up the current line.
2080 2084 # cur_len is just the length of all the chunks in cur_line.
2081 2085 cur_line = []
2082 2086 cur_len = 0
2083 2087
2084 2088 # Figure out which static string will prefix this line.
2085 2089 if lines:
2086 2090 indent = self.subsequent_indent
2087 2091 else:
2088 2092 indent = self.initial_indent
2089 2093
2090 2094 # Maximum width for this line.
2091 2095 width = self.width - len(indent)
2092 2096
2093 2097 # First chunk on line is whitespace -- drop it, unless this
2094 2098 # is the very beginning of the text (i.e. no lines started yet).
2095 2099 if self.drop_whitespace and chunks[-1].strip() == '' and lines:
2096 2100 del chunks[-1]
2097 2101
2098 2102 while chunks:
2099 2103 l = colwidth(chunks[-1])
2100 2104
2101 2105 # Can at least squeeze this chunk onto the current line.
2102 2106 if cur_len + l <= width:
2103 2107 cur_line.append(chunks.pop())
2104 2108 cur_len += l
2105 2109
2106 2110 # Nope, this line is full.
2107 2111 else:
2108 2112 break
2109 2113
2110 2114 # The current line is full, and the next chunk is too big to
2111 2115 # fit on *any* line (not just this one).
2112 2116 if chunks and colwidth(chunks[-1]) > width:
2113 2117 self._handle_long_word(chunks, cur_line, cur_len, width)
2114 2118
2115 2119 # If the last chunk on this line is all whitespace, drop it.
2116 2120 if (self.drop_whitespace and
2117 2121 cur_line and cur_line[-1].strip() == ''):
2118 2122 del cur_line[-1]
2119 2123
2120 2124 # Convert current line back to a string and store it in list
2121 2125 # of all lines (return value).
2122 2126 if cur_line:
2123 2127 lines.append(indent + ''.join(cur_line))
2124 2128
2125 2129 return lines
2126 2130
2127 2131 global MBTextWrapper
2128 2132 MBTextWrapper = tw
2129 2133 return tw(**kwargs)
2130 2134
2131 2135 def wrap(line, width, initindent='', hangindent=''):
2132 2136 maxindent = max(len(hangindent), len(initindent))
2133 2137 if width <= maxindent:
2134 2138 # adjust for weird terminal size
2135 2139 width = max(78, maxindent + 1)
2136 2140 line = line.decode(encoding.encoding, encoding.encodingmode)
2137 2141 initindent = initindent.decode(encoding.encoding, encoding.encodingmode)
2138 2142 hangindent = hangindent.decode(encoding.encoding, encoding.encodingmode)
2139 2143 wrapper = MBTextWrapper(width=width,
2140 2144 initial_indent=initindent,
2141 2145 subsequent_indent=hangindent)
2142 2146 return wrapper.fill(line).encode(encoding.encoding)
2143 2147
2144 2148 def iterlines(iterator):
2145 2149 for chunk in iterator:
2146 2150 for line in chunk.splitlines():
2147 2151 yield line
2148 2152
2149 2153 def expandpath(path):
2150 2154 return os.path.expanduser(os.path.expandvars(path))
2151 2155
2152 2156 def hgcmd():
2153 2157 """Return the command used to execute current hg
2154 2158
2155 2159 This is different from hgexecutable() because on Windows we want
2156 2160 to avoid things opening new shell windows like batch files, so we
2157 2161 get either the python call or current executable.
2158 2162 """
2159 2163 if mainfrozen():
2160 2164 if getattr(sys, 'frozen', None) == 'macosx_app':
2161 2165 # Env variable set by py2app
2162 2166 return [os.environ['EXECUTABLEPATH']]
2163 2167 else:
2164 2168 return [sys.executable]
2165 2169 return gethgcmd()
2166 2170
2167 2171 def rundetached(args, condfn):
2168 2172 """Execute the argument list in a detached process.
2169 2173
2170 2174 condfn is a callable which is called repeatedly and should return
2171 2175 True once the child process is known to have started successfully.
2172 2176 At this point, the child process PID is returned. If the child
2173 2177 process fails to start or finishes before condfn() evaluates to
2174 2178 True, return -1.
2175 2179 """
2176 2180 # Windows case is easier because the child process is either
2177 2181 # successfully starting and validating the condition or exiting
2178 2182 # on failure. We just poll on its PID. On Unix, if the child
2179 2183 # process fails to start, it will be left in a zombie state until
2180 2184 # the parent wait on it, which we cannot do since we expect a long
2181 2185 # running process on success. Instead we listen for SIGCHLD telling
2182 2186 # us our child process terminated.
2183 2187 terminated = set()
2184 2188 def handler(signum, frame):
2185 2189 terminated.add(os.wait())
2186 2190 prevhandler = None
2187 2191 SIGCHLD = getattr(signal, 'SIGCHLD', None)
2188 2192 if SIGCHLD is not None:
2189 2193 prevhandler = signal.signal(SIGCHLD, handler)
2190 2194 try:
2191 2195 pid = spawndetached(args)
2192 2196 while not condfn():
2193 2197 if ((pid in terminated or not testpid(pid))
2194 2198 and not condfn()):
2195 2199 return -1
2196 2200 time.sleep(0.1)
2197 2201 return pid
2198 2202 finally:
2199 2203 if prevhandler is not None:
2200 2204 signal.signal(signal.SIGCHLD, prevhandler)
2201 2205
2202 2206 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
2203 2207 """Return the result of interpolating items in the mapping into string s.
2204 2208
2205 2209 prefix is a single character string, or a two character string with
2206 2210 a backslash as the first character if the prefix needs to be escaped in
2207 2211 a regular expression.
2208 2212
2209 2213 fn is an optional function that will be applied to the replacement text
2210 2214 just before replacement.
2211 2215
2212 2216 escape_prefix is an optional flag that allows using doubled prefix for
2213 2217 its escaping.
2214 2218 """
2215 2219 fn = fn or (lambda s: s)
2216 2220 patterns = '|'.join(mapping.keys())
2217 2221 if escape_prefix:
2218 2222 patterns += '|' + prefix
2219 2223 if len(prefix) > 1:
2220 2224 prefix_char = prefix[1:]
2221 2225 else:
2222 2226 prefix_char = prefix
2223 2227 mapping[prefix_char] = prefix_char
2224 2228 r = remod.compile(r'%s(%s)' % (prefix, patterns))
2225 2229 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
2226 2230
2227 2231 def getport(port):
2228 2232 """Return the port for a given network service.
2229 2233
2230 2234 If port is an integer, it's returned as is. If it's a string, it's
2231 2235 looked up using socket.getservbyname(). If there's no matching
2232 2236 service, error.Abort is raised.
2233 2237 """
2234 2238 try:
2235 2239 return int(port)
2236 2240 except ValueError:
2237 2241 pass
2238 2242
2239 2243 try:
2240 2244 return socket.getservbyname(port)
2241 2245 except socket.error:
2242 2246 raise Abort(_("no port number associated with service '%s'") % port)
2243 2247
2244 2248 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
2245 2249 '0': False, 'no': False, 'false': False, 'off': False,
2246 2250 'never': False}
2247 2251
2248 2252 def parsebool(s):
2249 2253 """Parse s into a boolean.
2250 2254
2251 2255 If s is not a valid boolean, returns None.
2252 2256 """
2253 2257 return _booleans.get(s.lower(), None)
2254 2258
2255 2259 _hexdig = '0123456789ABCDEFabcdef'
2256 2260 _hextochr = dict((a + b, chr(int(a + b, 16)))
2257 2261 for a in _hexdig for b in _hexdig)
2258 2262
2259 2263 def _urlunquote(s):
2260 2264 """Decode HTTP/HTML % encoding.
2261 2265
2262 2266 >>> _urlunquote('abc%20def')
2263 2267 'abc def'
2264 2268 """
2265 2269 res = s.split('%')
2266 2270 # fastpath
2267 2271 if len(res) == 1:
2268 2272 return s
2269 2273 s = res[0]
2270 2274 for item in res[1:]:
2271 2275 try:
2272 2276 s += _hextochr[item[:2]] + item[2:]
2273 2277 except KeyError:
2274 2278 s += '%' + item
2275 2279 except UnicodeDecodeError:
2276 2280 s += unichr(int(item[:2], 16)) + item[2:]
2277 2281 return s
2278 2282
2279 2283 class url(object):
2280 2284 r"""Reliable URL parser.
2281 2285
2282 2286 This parses URLs and provides attributes for the following
2283 2287 components:
2284 2288
2285 2289 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
2286 2290
2287 2291 Missing components are set to None. The only exception is
2288 2292 fragment, which is set to '' if present but empty.
2289 2293
2290 2294 If parsefragment is False, fragment is included in query. If
2291 2295 parsequery is False, query is included in path. If both are
2292 2296 False, both fragment and query are included in path.
2293 2297
2294 2298 See http://www.ietf.org/rfc/rfc2396.txt for more information.
2295 2299
2296 2300 Note that for backward compatibility reasons, bundle URLs do not
2297 2301 take host names. That means 'bundle://../' has a path of '../'.
2298 2302
2299 2303 Examples:
2300 2304
2301 2305 >>> url('http://www.ietf.org/rfc/rfc2396.txt')
2302 2306 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
2303 2307 >>> url('ssh://[::1]:2200//home/joe/repo')
2304 2308 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
2305 2309 >>> url('file:///home/joe/repo')
2306 2310 <url scheme: 'file', path: '/home/joe/repo'>
2307 2311 >>> url('file:///c:/temp/foo/')
2308 2312 <url scheme: 'file', path: 'c:/temp/foo/'>
2309 2313 >>> url('bundle:foo')
2310 2314 <url scheme: 'bundle', path: 'foo'>
2311 2315 >>> url('bundle://../foo')
2312 2316 <url scheme: 'bundle', path: '../foo'>
2313 2317 >>> url(r'c:\foo\bar')
2314 2318 <url path: 'c:\\foo\\bar'>
2315 2319 >>> url(r'\\blah\blah\blah')
2316 2320 <url path: '\\\\blah\\blah\\blah'>
2317 2321 >>> url(r'\\blah\blah\blah#baz')
2318 2322 <url path: '\\\\blah\\blah\\blah', fragment: 'baz'>
2319 2323 >>> url(r'file:///C:\users\me')
2320 2324 <url scheme: 'file', path: 'C:\\users\\me'>
2321 2325
2322 2326 Authentication credentials:
2323 2327
2324 2328 >>> url('ssh://joe:xyz@x/repo')
2325 2329 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
2326 2330 >>> url('ssh://joe@x/repo')
2327 2331 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
2328 2332
2329 2333 Query strings and fragments:
2330 2334
2331 2335 >>> url('http://host/a?b#c')
2332 2336 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
2333 2337 >>> url('http://host/a?b#c', parsequery=False, parsefragment=False)
2334 2338 <url scheme: 'http', host: 'host', path: 'a?b#c'>
2335 2339 """
2336 2340
2337 2341 _safechars = "!~*'()+"
2338 2342 _safepchars = "/!~*'()+:\\"
2339 2343 _matchscheme = remod.compile(r'^[a-zA-Z0-9+.\-]+:').match
2340 2344
2341 2345 def __init__(self, path, parsequery=True, parsefragment=True):
2342 2346 # We slowly chomp away at path until we have only the path left
2343 2347 self.scheme = self.user = self.passwd = self.host = None
2344 2348 self.port = self.path = self.query = self.fragment = None
2345 2349 self._localpath = True
2346 2350 self._hostport = ''
2347 2351 self._origpath = path
2348 2352
2349 2353 if parsefragment and '#' in path:
2350 2354 path, self.fragment = path.split('#', 1)
2351 2355 if not path:
2352 2356 path = None
2353 2357
2354 2358 # special case for Windows drive letters and UNC paths
2355 2359 if hasdriveletter(path) or path.startswith(r'\\'):
2356 2360 self.path = path
2357 2361 return
2358 2362
2359 2363 # For compatibility reasons, we can't handle bundle paths as
2360 2364 # normal URLS
2361 2365 if path.startswith('bundle:'):
2362 2366 self.scheme = 'bundle'
2363 2367 path = path[7:]
2364 2368 if path.startswith('//'):
2365 2369 path = path[2:]
2366 2370 self.path = path
2367 2371 return
2368 2372
2369 2373 if self._matchscheme(path):
2370 2374 parts = path.split(':', 1)
2371 2375 if parts[0]:
2372 2376 self.scheme, path = parts
2373 2377 self._localpath = False
2374 2378
2375 2379 if not path:
2376 2380 path = None
2377 2381 if self._localpath:
2378 2382 self.path = ''
2379 2383 return
2380 2384 else:
2381 2385 if self._localpath:
2382 2386 self.path = path
2383 2387 return
2384 2388
2385 2389 if parsequery and '?' in path:
2386 2390 path, self.query = path.split('?', 1)
2387 2391 if not path:
2388 2392 path = None
2389 2393 if not self.query:
2390 2394 self.query = None
2391 2395
2392 2396 # // is required to specify a host/authority
2393 2397 if path and path.startswith('//'):
2394 2398 parts = path[2:].split('/', 1)
2395 2399 if len(parts) > 1:
2396 2400 self.host, path = parts
2397 2401 else:
2398 2402 self.host = parts[0]
2399 2403 path = None
2400 2404 if not self.host:
2401 2405 self.host = None
2402 2406 # path of file:///d is /d
2403 2407 # path of file:///d:/ is d:/, not /d:/
2404 2408 if path and not hasdriveletter(path):
2405 2409 path = '/' + path
2406 2410
2407 2411 if self.host and '@' in self.host:
2408 2412 self.user, self.host = self.host.rsplit('@', 1)
2409 2413 if ':' in self.user:
2410 2414 self.user, self.passwd = self.user.split(':', 1)
2411 2415 if not self.host:
2412 2416 self.host = None
2413 2417
2414 2418 # Don't split on colons in IPv6 addresses without ports
2415 2419 if (self.host and ':' in self.host and
2416 2420 not (self.host.startswith('[') and self.host.endswith(']'))):
2417 2421 self._hostport = self.host
2418 2422 self.host, self.port = self.host.rsplit(':', 1)
2419 2423 if not self.host:
2420 2424 self.host = None
2421 2425
2422 2426 if (self.host and self.scheme == 'file' and
2423 2427 self.host not in ('localhost', '127.0.0.1', '[::1]')):
2424 2428 raise Abort(_('file:// URLs can only refer to localhost'))
2425 2429
2426 2430 self.path = path
2427 2431
2428 2432 # leave the query string escaped
2429 2433 for a in ('user', 'passwd', 'host', 'port',
2430 2434 'path', 'fragment'):
2431 2435 v = getattr(self, a)
2432 2436 if v is not None:
2433 2437 setattr(self, a, _urlunquote(v))
2434 2438
2435 2439 def __repr__(self):
2436 2440 attrs = []
2437 2441 for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
2438 2442 'query', 'fragment'):
2439 2443 v = getattr(self, a)
2440 2444 if v is not None:
2441 2445 attrs.append('%s: %r' % (a, v))
2442 2446 return '<url %s>' % ', '.join(attrs)
2443 2447
2444 2448 def __str__(self):
2445 2449 r"""Join the URL's components back into a URL string.
2446 2450
2447 2451 Examples:
2448 2452
2449 2453 >>> str(url('http://user:pw@host:80/c:/bob?fo:oo#ba:ar'))
2450 2454 'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'
2451 2455 >>> str(url('http://user:pw@host:80/?foo=bar&baz=42'))
2452 2456 'http://user:pw@host:80/?foo=bar&baz=42'
2453 2457 >>> str(url('http://user:pw@host:80/?foo=bar%3dbaz'))
2454 2458 'http://user:pw@host:80/?foo=bar%3dbaz'
2455 2459 >>> str(url('ssh://user:pw@[::1]:2200//home/joe#'))
2456 2460 'ssh://user:pw@[::1]:2200//home/joe#'
2457 2461 >>> str(url('http://localhost:80//'))
2458 2462 'http://localhost:80//'
2459 2463 >>> str(url('http://localhost:80/'))
2460 2464 'http://localhost:80/'
2461 2465 >>> str(url('http://localhost:80'))
2462 2466 'http://localhost:80/'
2463 2467 >>> str(url('bundle:foo'))
2464 2468 'bundle:foo'
2465 2469 >>> str(url('bundle://../foo'))
2466 2470 'bundle:../foo'
2467 2471 >>> str(url('path'))
2468 2472 'path'
2469 2473 >>> str(url('file:///tmp/foo/bar'))
2470 2474 'file:///tmp/foo/bar'
2471 2475 >>> str(url('file:///c:/tmp/foo/bar'))
2472 2476 'file:///c:/tmp/foo/bar'
2473 2477 >>> print url(r'bundle:foo\bar')
2474 2478 bundle:foo\bar
2475 2479 >>> print url(r'file:///D:\data\hg')
2476 2480 file:///D:\data\hg
2477 2481 """
2478 2482 if self._localpath:
2479 2483 s = self.path
2480 2484 if self.scheme == 'bundle':
2481 2485 s = 'bundle:' + s
2482 2486 if self.fragment:
2483 2487 s += '#' + self.fragment
2484 2488 return s
2485 2489
2486 2490 s = self.scheme + ':'
2487 2491 if self.user or self.passwd or self.host:
2488 2492 s += '//'
2489 2493 elif self.scheme and (not self.path or self.path.startswith('/')
2490 2494 or hasdriveletter(self.path)):
2491 2495 s += '//'
2492 2496 if hasdriveletter(self.path):
2493 2497 s += '/'
2494 2498 if self.user:
2495 2499 s += urlreq.quote(self.user, safe=self._safechars)
2496 2500 if self.passwd:
2497 2501 s += ':' + urlreq.quote(self.passwd, safe=self._safechars)
2498 2502 if self.user or self.passwd:
2499 2503 s += '@'
2500 2504 if self.host:
2501 2505 if not (self.host.startswith('[') and self.host.endswith(']')):
2502 2506 s += urlreq.quote(self.host)
2503 2507 else:
2504 2508 s += self.host
2505 2509 if self.port:
2506 2510 s += ':' + urlreq.quote(self.port)
2507 2511 if self.host:
2508 2512 s += '/'
2509 2513 if self.path:
2510 2514 # TODO: similar to the query string, we should not unescape the
2511 2515 # path when we store it, the path might contain '%2f' = '/',
2512 2516 # which we should *not* escape.
2513 2517 s += urlreq.quote(self.path, safe=self._safepchars)
2514 2518 if self.query:
2515 2519 # we store the query in escaped form.
2516 2520 s += '?' + self.query
2517 2521 if self.fragment is not None:
2518 2522 s += '#' + urlreq.quote(self.fragment, safe=self._safepchars)
2519 2523 return s
2520 2524
2521 2525 def authinfo(self):
2522 2526 user, passwd = self.user, self.passwd
2523 2527 try:
2524 2528 self.user, self.passwd = None, None
2525 2529 s = str(self)
2526 2530 finally:
2527 2531 self.user, self.passwd = user, passwd
2528 2532 if not self.user:
2529 2533 return (s, None)
2530 2534 # authinfo[1] is passed to urllib2 password manager, and its
2531 2535 # URIs must not contain credentials. The host is passed in the
2532 2536 # URIs list because Python < 2.4.3 uses only that to search for
2533 2537 # a password.
2534 2538 return (s, (None, (s, self.host),
2535 2539 self.user, self.passwd or ''))
2536 2540
2537 2541 def isabs(self):
2538 2542 if self.scheme and self.scheme != 'file':
2539 2543 return True # remote URL
2540 2544 if hasdriveletter(self.path):
2541 2545 return True # absolute for our purposes - can't be joined()
2542 2546 if self.path.startswith(r'\\'):
2543 2547 return True # Windows UNC path
2544 2548 if self.path.startswith('/'):
2545 2549 return True # POSIX-style
2546 2550 return False
2547 2551
2548 2552 def localpath(self):
2549 2553 if self.scheme == 'file' or self.scheme == 'bundle':
2550 2554 path = self.path or '/'
2551 2555 # For Windows, we need to promote hosts containing drive
2552 2556 # letters to paths with drive letters.
2553 2557 if hasdriveletter(self._hostport):
2554 2558 path = self._hostport + '/' + self.path
2555 2559 elif (self.host is not None and self.path
2556 2560 and not hasdriveletter(path)):
2557 2561 path = '/' + path
2558 2562 return path
2559 2563 return self._origpath
2560 2564
2561 2565 def islocal(self):
2562 2566 '''whether localpath will return something that posixfile can open'''
2563 2567 return (not self.scheme or self.scheme == 'file'
2564 2568 or self.scheme == 'bundle')
2565 2569
2566 2570 def hasscheme(path):
2567 2571 return bool(url(path).scheme)
2568 2572
2569 2573 def hasdriveletter(path):
2570 2574 return path and path[1:2] == ':' and path[0:1].isalpha()
2571 2575
2572 2576 def urllocalpath(path):
2573 2577 return url(path, parsequery=False, parsefragment=False).localpath()
2574 2578
2575 2579 def hidepassword(u):
2576 2580 '''hide user credential in a url string'''
2577 2581 u = url(u)
2578 2582 if u.passwd:
2579 2583 u.passwd = '***'
2580 2584 return str(u)
2581 2585
2582 2586 def removeauth(u):
2583 2587 '''remove all authentication information from a url string'''
2584 2588 u = url(u)
2585 2589 u.user = u.passwd = None
2586 2590 return str(u)
2587 2591
2588 2592 def isatty(fp):
2589 2593 try:
2590 2594 return fp.isatty()
2591 2595 except AttributeError:
2592 2596 return False
2593 2597
2594 2598 timecount = unitcountfn(
2595 2599 (1, 1e3, _('%.0f s')),
2596 2600 (100, 1, _('%.1f s')),
2597 2601 (10, 1, _('%.2f s')),
2598 2602 (1, 1, _('%.3f s')),
2599 2603 (100, 0.001, _('%.1f ms')),
2600 2604 (10, 0.001, _('%.2f ms')),
2601 2605 (1, 0.001, _('%.3f ms')),
2602 2606 (100, 0.000001, _('%.1f us')),
2603 2607 (10, 0.000001, _('%.2f us')),
2604 2608 (1, 0.000001, _('%.3f us')),
2605 2609 (100, 0.000000001, _('%.1f ns')),
2606 2610 (10, 0.000000001, _('%.2f ns')),
2607 2611 (1, 0.000000001, _('%.3f ns')),
2608 2612 )
2609 2613
2610 2614 _timenesting = [0]
2611 2615
2612 2616 def timed(func):
2613 2617 '''Report the execution time of a function call to stderr.
2614 2618
2615 2619 During development, use as a decorator when you need to measure
2616 2620 the cost of a function, e.g. as follows:
2617 2621
2618 2622 @util.timed
2619 2623 def foo(a, b, c):
2620 2624 pass
2621 2625 '''
2622 2626
2623 2627 def wrapper(*args, **kwargs):
2624 2628 start = time.time()
2625 2629 indent = 2
2626 2630 _timenesting[0] += indent
2627 2631 try:
2628 2632 return func(*args, **kwargs)
2629 2633 finally:
2630 2634 elapsed = time.time() - start
2631 2635 _timenesting[0] -= indent
2632 2636 sys.stderr.write('%s%s: %s\n' %
2633 2637 (' ' * _timenesting[0], func.__name__,
2634 2638 timecount(elapsed)))
2635 2639 return wrapper
2636 2640
2637 2641 _sizeunits = (('m', 2**20), ('k', 2**10), ('g', 2**30),
2638 2642 ('kb', 2**10), ('mb', 2**20), ('gb', 2**30), ('b', 1))
2639 2643
2640 2644 def sizetoint(s):
2641 2645 '''Convert a space specifier to a byte count.
2642 2646
2643 2647 >>> sizetoint('30')
2644 2648 30
2645 2649 >>> sizetoint('2.2kb')
2646 2650 2252
2647 2651 >>> sizetoint('6M')
2648 2652 6291456
2649 2653 '''
2650 2654 t = s.strip().lower()
2651 2655 try:
2652 2656 for k, u in _sizeunits:
2653 2657 if t.endswith(k):
2654 2658 return int(float(t[:-len(k)]) * u)
2655 2659 return int(t)
2656 2660 except ValueError:
2657 2661 raise error.ParseError(_("couldn't parse size: %s") % s)
2658 2662
2659 2663 class hooks(object):
2660 2664 '''A collection of hook functions that can be used to extend a
2661 2665 function's behavior. Hooks are called in lexicographic order,
2662 2666 based on the names of their sources.'''
2663 2667
2664 2668 def __init__(self):
2665 2669 self._hooks = []
2666 2670
2667 2671 def add(self, source, hook):
2668 2672 self._hooks.append((source, hook))
2669 2673
2670 2674 def __call__(self, *args):
2671 2675 self._hooks.sort(key=lambda x: x[0])
2672 2676 results = []
2673 2677 for source, hook in self._hooks:
2674 2678 results.append(hook(*args))
2675 2679 return results
2676 2680
2677 2681 def getstackframes(skip=0, line=' %-*s in %s\n', fileline='%s:%s'):
2678 2682 '''Yields lines for a nicely formatted stacktrace.
2679 2683 Skips the 'skip' last entries.
2680 2684 Each file+linenumber is formatted according to fileline.
2681 2685 Each line is formatted according to line.
2682 2686 If line is None, it yields:
2683 2687 length of longest filepath+line number,
2684 2688 filepath+linenumber,
2685 2689 function
2686 2690
2687 2691 Not be used in production code but very convenient while developing.
2688 2692 '''
2689 2693 entries = [(fileline % (fn, ln), func)
2690 2694 for fn, ln, func, _text in traceback.extract_stack()[:-skip - 1]]
2691 2695 if entries:
2692 2696 fnmax = max(len(entry[0]) for entry in entries)
2693 2697 for fnln, func in entries:
2694 2698 if line is None:
2695 2699 yield (fnmax, fnln, func)
2696 2700 else:
2697 2701 yield line % (fnmax, fnln, func)
2698 2702
2699 2703 def debugstacktrace(msg='stacktrace', skip=0, f=sys.stderr, otherf=sys.stdout):
2700 2704 '''Writes a message to f (stderr) with a nicely formatted stacktrace.
2701 2705 Skips the 'skip' last entries. By default it will flush stdout first.
2702 2706 It can be used everywhere and intentionally does not require an ui object.
2703 2707 Not be used in production code but very convenient while developing.
2704 2708 '''
2705 2709 if otherf:
2706 2710 otherf.flush()
2707 2711 f.write('%s at:\n' % msg)
2708 2712 for line in getstackframes(skip + 1):
2709 2713 f.write(line)
2710 2714 f.flush()
2711 2715
2712 2716 class dirs(object):
2713 2717 '''a multiset of directory names from a dirstate or manifest'''
2714 2718
2715 2719 def __init__(self, map, skip=None):
2716 2720 self._dirs = {}
2717 2721 addpath = self.addpath
2718 2722 if safehasattr(map, 'iteritems') and skip is not None:
2719 2723 for f, s in map.iteritems():
2720 2724 if s[0] != skip:
2721 2725 addpath(f)
2722 2726 else:
2723 2727 for f in map:
2724 2728 addpath(f)
2725 2729
2726 2730 def addpath(self, path):
2727 2731 dirs = self._dirs
2728 2732 for base in finddirs(path):
2729 2733 if base in dirs:
2730 2734 dirs[base] += 1
2731 2735 return
2732 2736 dirs[base] = 1
2733 2737
2734 2738 def delpath(self, path):
2735 2739 dirs = self._dirs
2736 2740 for base in finddirs(path):
2737 2741 if dirs[base] > 1:
2738 2742 dirs[base] -= 1
2739 2743 return
2740 2744 del dirs[base]
2741 2745
2742 2746 def __iter__(self):
2743 2747 return self._dirs.iterkeys()
2744 2748
2745 2749 def __contains__(self, d):
2746 2750 return d in self._dirs
2747 2751
2748 2752 if safehasattr(parsers, 'dirs'):
2749 2753 dirs = parsers.dirs
2750 2754
2751 2755 def finddirs(path):
2752 2756 pos = path.rfind('/')
2753 2757 while pos != -1:
2754 2758 yield path[:pos]
2755 2759 pos = path.rfind('/', 0, pos)
2756 2760
2757 2761 # compression utility
2758 2762
2759 2763 class nocompress(object):
2760 2764 def compress(self, x):
2761 2765 return x
2762 2766 def flush(self):
2763 2767 return ""
2764 2768
2765 2769 compressors = {
2766 2770 None: nocompress,
2767 2771 # lambda to prevent early import
2768 2772 'BZ': lambda: bz2.BZ2Compressor(),
2769 2773 'GZ': lambda: zlib.compressobj(),
2770 2774 }
2771 2775 # also support the old form by courtesies
2772 2776 compressors['UN'] = compressors[None]
2773 2777
2774 2778 def _makedecompressor(decompcls):
2775 2779 def generator(f):
2776 2780 d = decompcls()
2777 2781 for chunk in filechunkiter(f):
2778 2782 yield d.decompress(chunk)
2779 2783 def func(fh):
2780 2784 return chunkbuffer(generator(fh))
2781 2785 return func
2782 2786
2783 2787 class ctxmanager(object):
2784 2788 '''A context manager for use in 'with' blocks to allow multiple
2785 2789 contexts to be entered at once. This is both safer and more
2786 2790 flexible than contextlib.nested.
2787 2791
2788 2792 Once Mercurial supports Python 2.7+, this will become mostly
2789 2793 unnecessary.
2790 2794 '''
2791 2795
2792 2796 def __init__(self, *args):
2793 2797 '''Accepts a list of no-argument functions that return context
2794 2798 managers. These will be invoked at __call__ time.'''
2795 2799 self._pending = args
2796 2800 self._atexit = []
2797 2801
2798 2802 def __enter__(self):
2799 2803 return self
2800 2804
2801 2805 def enter(self):
2802 2806 '''Create and enter context managers in the order in which they were
2803 2807 passed to the constructor.'''
2804 2808 values = []
2805 2809 for func in self._pending:
2806 2810 obj = func()
2807 2811 values.append(obj.__enter__())
2808 2812 self._atexit.append(obj.__exit__)
2809 2813 del self._pending
2810 2814 return values
2811 2815
2812 2816 def atexit(self, func, *args, **kwargs):
2813 2817 '''Add a function to call when this context manager exits. The
2814 2818 ordering of multiple atexit calls is unspecified, save that
2815 2819 they will happen before any __exit__ functions.'''
2816 2820 def wrapper(exc_type, exc_val, exc_tb):
2817 2821 func(*args, **kwargs)
2818 2822 self._atexit.append(wrapper)
2819 2823 return func
2820 2824
2821 2825 def __exit__(self, exc_type, exc_val, exc_tb):
2822 2826 '''Context managers are exited in the reverse order from which
2823 2827 they were created.'''
2824 2828 received = exc_type is not None
2825 2829 suppressed = False
2826 2830 pending = None
2827 2831 self._atexit.reverse()
2828 2832 for exitfunc in self._atexit:
2829 2833 try:
2830 2834 if exitfunc(exc_type, exc_val, exc_tb):
2831 2835 suppressed = True
2832 2836 exc_type = None
2833 2837 exc_val = None
2834 2838 exc_tb = None
2835 2839 except BaseException:
2836 2840 pending = sys.exc_info()
2837 2841 exc_type, exc_val, exc_tb = pending = sys.exc_info()
2838 2842 del self._atexit
2839 2843 if pending:
2840 2844 raise exc_val
2841 2845 return received and suppressed
2842 2846
2843 2847 def _bz2():
2844 2848 d = bz2.BZ2Decompressor()
2845 2849 # Bzip2 stream start with BZ, but we stripped it.
2846 2850 # we put it back for good measure.
2847 2851 d.decompress('BZ')
2848 2852 return d
2849 2853
2850 2854 decompressors = {None: lambda fh: fh,
2851 2855 '_truncatedBZ': _makedecompressor(_bz2),
2852 2856 'BZ': _makedecompressor(lambda: bz2.BZ2Decompressor()),
2853 2857 'GZ': _makedecompressor(lambda: zlib.decompressobj()),
2854 2858 }
2855 2859 # also support the old form by courtesies
2856 2860 decompressors['UN'] = decompressors[None]
2857 2861
2858 2862 # convenient shortcut
2859 2863 dst = debugstacktrace
@@ -1,128 +1,128 b''
1 1
2 2 $ cat << EOF >> $HGRCPATH
3 3 > [format]
4 4 > usegeneraldelta=yes
5 5 > EOF
6 6
7 7 bundle w/o type option
8 8
9 9 $ hg init t1
10 10 $ hg init t2
11 11 $ cd t1
12 12 $ echo blablablablabla > file.txt
13 13 $ hg ci -Ama
14 14 adding file.txt
15 15 $ hg log | grep summary
16 16 summary: a
17 17 $ hg bundle ../b1 ../t2
18 18 searching for changes
19 19 1 changesets found
20 20
21 21 $ cd ../t2
22 22 $ hg pull ../b1
23 23 pulling from ../b1
24 24 requesting all changes
25 25 adding changesets
26 26 adding manifests
27 27 adding file changes
28 28 added 1 changesets with 1 changes to 1 files
29 29 (run 'hg update' to get a working copy)
30 30 $ hg up
31 31 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
32 32 $ hg log | grep summary
33 33 summary: a
34 34 $ cd ..
35 35
36 36 test bundle types
37 37
38 38 $ for t in "None" "bzip2" "gzip" "none-v2" "v2" "v1" "gzip-v1"; do
39 39 > echo % test bundle type $t
40 40 > hg init t$t
41 41 > cd t1
42 42 > hg bundle -t $t ../b$t ../t$t
43 43 > f -q -B6 -D ../b$t; echo
44 44 > cd ../t$t
45 45 > hg debugbundle ../b$t
46 46 > hg debugbundle --spec ../b$t
47 47 > echo
48 48 > cd ..
49 49 > done
50 50 % test bundle type None
51 51 searching for changes
52 52 1 changesets found
53 53 HG20\x00\x00 (esc)
54 54 Stream params: {}
55 changegroup -- "{'version': '02'}"
55 changegroup -- "sortdict([('version', '02')])"
56 56 c35a0f9217e65d1fdb90c936ffa7dbe679f83ddf
57 57 none-v2
58 58
59 59 % test bundle type bzip2
60 60 searching for changes
61 61 1 changesets found
62 62 HG20\x00\x00 (esc)
63 Stream params: {'Compression': 'BZ'}
64 changegroup -- "{'version': '02'}"
63 Stream params: sortdict([('Compression', 'BZ')])
64 changegroup -- "sortdict([('version', '02')])"
65 65 c35a0f9217e65d1fdb90c936ffa7dbe679f83ddf
66 66 bzip2-v2
67 67
68 68 % test bundle type gzip
69 69 searching for changes
70 70 1 changesets found
71 71 HG20\x00\x00 (esc)
72 Stream params: {'Compression': 'GZ'}
73 changegroup -- "{'version': '02'}"
72 Stream params: sortdict([('Compression', 'GZ')])
73 changegroup -- "sortdict([('version', '02')])"
74 74 c35a0f9217e65d1fdb90c936ffa7dbe679f83ddf
75 75 gzip-v2
76 76
77 77 % test bundle type none-v2
78 78 searching for changes
79 79 1 changesets found
80 80 HG20\x00\x00 (esc)
81 81 Stream params: {}
82 changegroup -- "{'version': '02'}"
82 changegroup -- "sortdict([('version', '02')])"
83 83 c35a0f9217e65d1fdb90c936ffa7dbe679f83ddf
84 84 none-v2
85 85
86 86 % test bundle type v2
87 87 searching for changes
88 88 1 changesets found
89 89 HG20\x00\x00 (esc)
90 Stream params: {'Compression': 'BZ'}
91 changegroup -- "{'version': '02'}"
90 Stream params: sortdict([('Compression', 'BZ')])
91 changegroup -- "sortdict([('version', '02')])"
92 92 c35a0f9217e65d1fdb90c936ffa7dbe679f83ddf
93 93 bzip2-v2
94 94
95 95 % test bundle type v1
96 96 searching for changes
97 97 1 changesets found
98 98 HG10BZ
99 99 c35a0f9217e65d1fdb90c936ffa7dbe679f83ddf
100 100 bzip2-v1
101 101
102 102 % test bundle type gzip-v1
103 103 searching for changes
104 104 1 changesets found
105 105 HG10GZ
106 106 c35a0f9217e65d1fdb90c936ffa7dbe679f83ddf
107 107 gzip-v1
108 108
109 109
110 110 test garbage file
111 111
112 112 $ echo garbage > bgarbage
113 113 $ hg init tgarbage
114 114 $ cd tgarbage
115 115 $ hg pull ../bgarbage
116 116 pulling from ../bgarbage
117 117 abort: ../bgarbage: not a Mercurial bundle
118 118 [255]
119 119 $ cd ..
120 120
121 121 test invalid bundle type
122 122
123 123 $ cd t1
124 124 $ hg bundle -a -t garbage ../bgarbage
125 125 abort: garbage is not a recognized bundle specification
126 126 (see "hg help bundle" for supported values for --type)
127 127 [255]
128 128 $ cd ..
@@ -1,1233 +1,1233 b''
1 1 This test is dedicated to test the bundle2 container format
2 2
3 3 It test multiple existing parts to test different feature of the container. You
4 4 probably do not need to touch this test unless you change the binary encoding
5 5 of the bundle2 format itself.
6 6
7 7 Create an extension to test bundle2 API
8 8
9 9 $ cat > bundle2.py << EOF
10 10 > """A small extension to test bundle2 implementation
11 11 >
12 12 > This extension allows detailed testing of the various bundle2 API and
13 13 > behaviors.
14 14 > """
15 15 >
16 16 > import sys, os, gc
17 17 > from mercurial import cmdutil
18 18 > from mercurial import util
19 19 > from mercurial import bundle2
20 20 > from mercurial import scmutil
21 21 > from mercurial import discovery
22 22 > from mercurial import changegroup
23 23 > from mercurial import error
24 24 > from mercurial import obsolete
25 25 >
26 26 >
27 27 > try:
28 28 > import msvcrt
29 29 > msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
30 30 > msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
31 31 > msvcrt.setmode(sys.stderr.fileno(), os.O_BINARY)
32 32 > except ImportError:
33 33 > pass
34 34 >
35 35 > cmdtable = {}
36 36 > command = cmdutil.command(cmdtable)
37 37 >
38 38 > ELEPHANTSSONG = """Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
39 39 > Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
40 40 > Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko."""
41 41 > assert len(ELEPHANTSSONG) == 178 # future test say 178 bytes, trust it.
42 42 >
43 43 > @bundle2.parthandler('test:song')
44 44 > def songhandler(op, part):
45 45 > """handle a "test:song" bundle2 part, printing the lyrics on stdin"""
46 46 > op.ui.write('The choir starts singing:\n')
47 47 > verses = 0
48 48 > for line in part.read().split('\n'):
49 49 > op.ui.write(' %s\n' % line)
50 50 > verses += 1
51 51 > op.records.add('song', {'verses': verses})
52 52 >
53 53 > @bundle2.parthandler('test:ping')
54 54 > def pinghandler(op, part):
55 55 > op.ui.write('received ping request (id %i)\n' % part.id)
56 56 > if op.reply is not None and 'ping-pong' in op.reply.capabilities:
57 57 > op.ui.write_err('replying to ping request (id %i)\n' % part.id)
58 58 > op.reply.newpart('test:pong', [('in-reply-to', str(part.id))],
59 59 > mandatory=False)
60 60 >
61 61 > @bundle2.parthandler('test:debugreply')
62 62 > def debugreply(op, part):
63 63 > """print data about the capacity of the bundle reply"""
64 64 > if op.reply is None:
65 65 > op.ui.write('debugreply: no reply\n')
66 66 > else:
67 67 > op.ui.write('debugreply: capabilities:\n')
68 68 > for cap in sorted(op.reply.capabilities):
69 69 > op.ui.write('debugreply: %r\n' % cap)
70 70 > for val in op.reply.capabilities[cap]:
71 71 > op.ui.write('debugreply: %r\n' % val)
72 72 >
73 73 > @command('bundle2',
74 74 > [('', 'param', [], 'stream level parameter'),
75 75 > ('', 'unknown', False, 'include an unknown mandatory part in the bundle'),
76 76 > ('', 'unknownparams', False, 'include an unknown part parameters in the bundle'),
77 77 > ('', 'parts', False, 'include some arbitrary parts to the bundle'),
78 78 > ('', 'reply', False, 'produce a reply bundle'),
79 79 > ('', 'pushrace', False, 'includes a check:head part with unknown nodes'),
80 80 > ('', 'genraise', False, 'includes a part that raise an exception during generation'),
81 81 > ('', 'timeout', False, 'emulate a timeout during bundle generation'),
82 82 > ('r', 'rev', [], 'includes those changeset in the bundle'),
83 83 > ('', 'compress', '', 'compress the stream'),],
84 84 > '[OUTPUTFILE]')
85 85 > def cmdbundle2(ui, repo, path=None, **opts):
86 86 > """write a bundle2 container on standard output"""
87 87 > bundler = bundle2.bundle20(ui)
88 88 > for p in opts['param']:
89 89 > p = p.split('=', 1)
90 90 > try:
91 91 > bundler.addparam(*p)
92 92 > except ValueError, exc:
93 93 > raise error.Abort('%s' % exc)
94 94 >
95 95 > if opts['compress']:
96 96 > bundler.setcompression(opts['compress'])
97 97 >
98 98 > if opts['reply']:
99 99 > capsstring = 'ping-pong\nelephants=babar,celeste\ncity%3D%21=celeste%2Cville'
100 100 > bundler.newpart('replycaps', data=capsstring)
101 101 >
102 102 > if opts['pushrace']:
103 103 > # also serve to test the assignement of data outside of init
104 104 > part = bundler.newpart('check:heads')
105 105 > part.data = '01234567890123456789'
106 106 >
107 107 > revs = opts['rev']
108 108 > if 'rev' in opts:
109 109 > revs = scmutil.revrange(repo, opts['rev'])
110 110 > if revs:
111 111 > # very crude version of a changegroup part creation
112 112 > bundled = repo.revs('%ld::%ld', revs, revs)
113 113 > headmissing = [c.node() for c in repo.set('heads(%ld)', revs)]
114 114 > headcommon = [c.node() for c in repo.set('parents(%ld) - %ld', revs, revs)]
115 115 > outgoing = discovery.outgoing(repo.changelog, headcommon, headmissing)
116 116 > cg = changegroup.getlocalchangegroup(repo, 'test:bundle2', outgoing, None)
117 117 > bundler.newpart('changegroup', data=cg.getchunks(),
118 118 > mandatory=False)
119 119 >
120 120 > if opts['parts']:
121 121 > bundler.newpart('test:empty', mandatory=False)
122 122 > # add a second one to make sure we handle multiple parts
123 123 > bundler.newpart('test:empty', mandatory=False)
124 124 > bundler.newpart('test:song', data=ELEPHANTSSONG, mandatory=False)
125 125 > bundler.newpart('test:debugreply', mandatory=False)
126 126 > mathpart = bundler.newpart('test:math')
127 127 > mathpart.addparam('pi', '3.14')
128 128 > mathpart.addparam('e', '2.72')
129 129 > mathpart.addparam('cooking', 'raw', mandatory=False)
130 130 > mathpart.data = '42'
131 131 > mathpart.mandatory = False
132 132 > # advisory known part with unknown mandatory param
133 133 > bundler.newpart('test:song', [('randomparam','')], mandatory=False)
134 134 > if opts['unknown']:
135 135 > bundler.newpart('test:unknown', data='some random content')
136 136 > if opts['unknownparams']:
137 137 > bundler.newpart('test:song', [('randomparams', '')])
138 138 > if opts['parts']:
139 139 > bundler.newpart('test:ping', mandatory=False)
140 140 > if opts['genraise']:
141 141 > def genraise():
142 142 > yield 'first line\n'
143 143 > raise RuntimeError('Someone set up us the bomb!')
144 144 > bundler.newpart('output', data=genraise(), mandatory=False)
145 145 >
146 146 > if path is None:
147 147 > file = sys.stdout
148 148 > else:
149 149 > file = open(path, 'wb')
150 150 >
151 151 > if opts['timeout']:
152 152 > bundler.newpart('test:song', data=ELEPHANTSSONG, mandatory=False)
153 153 > for idx, junk in enumerate(bundler.getchunks()):
154 154 > ui.write('%d chunk\n' % idx)
155 155 > if idx > 4:
156 156 > # This throws a GeneratorExit inside the generator, which
157 157 > # can cause problems if the exception-recovery code is
158 158 > # too zealous. It's important for this test that the break
159 159 > # occur while we're in the middle of a part.
160 160 > break
161 161 > gc.collect()
162 162 > ui.write('fake timeout complete.\n')
163 163 > return
164 164 > try:
165 165 > for chunk in bundler.getchunks():
166 166 > file.write(chunk)
167 167 > except RuntimeError, exc:
168 168 > raise error.Abort(exc)
169 169 > finally:
170 170 > file.flush()
171 171 >
172 172 > @command('unbundle2', [], '')
173 173 > def cmdunbundle2(ui, repo, replypath=None):
174 174 > """process a bundle2 stream from stdin on the current repo"""
175 175 > try:
176 176 > tr = None
177 177 > lock = repo.lock()
178 178 > tr = repo.transaction('processbundle')
179 179 > try:
180 180 > unbundler = bundle2.getunbundler(ui, sys.stdin)
181 181 > op = bundle2.processbundle(repo, unbundler, lambda: tr)
182 182 > tr.close()
183 183 > except error.BundleValueError, exc:
184 184 > raise error.Abort('missing support for %s' % exc)
185 185 > except error.PushRaced, exc:
186 186 > raise error.Abort('push race: %s' % exc)
187 187 > finally:
188 188 > if tr is not None:
189 189 > tr.release()
190 190 > lock.release()
191 191 > remains = sys.stdin.read()
192 192 > ui.write('%i unread bytes\n' % len(remains))
193 193 > if op.records['song']:
194 194 > totalverses = sum(r['verses'] for r in op.records['song'])
195 195 > ui.write('%i total verses sung\n' % totalverses)
196 196 > for rec in op.records['changegroup']:
197 197 > ui.write('addchangegroup return: %i\n' % rec['return'])
198 198 > if op.reply is not None and replypath is not None:
199 199 > with open(replypath, 'wb') as file:
200 200 > for chunk in op.reply.getchunks():
201 201 > file.write(chunk)
202 202 >
203 203 > @command('statbundle2', [], '')
204 204 > def cmdstatbundle2(ui, repo):
205 205 > """print statistic on the bundle2 container read from stdin"""
206 206 > unbundler = bundle2.getunbundler(ui, sys.stdin)
207 207 > try:
208 208 > params = unbundler.params
209 209 > except error.BundleValueError, exc:
210 210 > raise error.Abort('unknown parameters: %s' % exc)
211 211 > ui.write('options count: %i\n' % len(params))
212 212 > for key in sorted(params):
213 213 > ui.write('- %s\n' % key)
214 214 > value = params[key]
215 215 > if value is not None:
216 216 > ui.write(' %s\n' % value)
217 217 > count = 0
218 218 > for p in unbundler.iterparts():
219 219 > count += 1
220 220 > ui.write(' :%s:\n' % p.type)
221 221 > ui.write(' mandatory: %i\n' % len(p.mandatoryparams))
222 222 > ui.write(' advisory: %i\n' % len(p.advisoryparams))
223 223 > ui.write(' payload: %i bytes\n' % len(p.read()))
224 224 > ui.write('parts count: %i\n' % count)
225 225 > EOF
226 226 $ cat >> $HGRCPATH << EOF
227 227 > [extensions]
228 228 > bundle2=$TESTTMP/bundle2.py
229 229 > [experimental]
230 230 > bundle2-exp=True
231 231 > evolution=createmarkers
232 232 > [ui]
233 233 > ssh=python "$TESTDIR/dummyssh"
234 234 > logtemplate={rev}:{node|short} {phase} {author} {bookmarks} {desc|firstline}
235 235 > [web]
236 236 > push_ssl = false
237 237 > allow_push = *
238 238 > [phases]
239 239 > publish=False
240 240 > EOF
241 241
242 242 The extension requires a repo (currently unused)
243 243
244 244 $ hg init main
245 245 $ cd main
246 246 $ touch a
247 247 $ hg add a
248 248 $ hg commit -m 'a'
249 249
250 250
251 251 Empty bundle
252 252 =================
253 253
254 254 - no option
255 255 - no parts
256 256
257 257 Test bundling
258 258
259 259 $ hg bundle2 | f --hexdump
260 260
261 261 0000: 48 47 32 30 00 00 00 00 00 00 00 00 |HG20........|
262 262
263 263 Test timeouts during bundling
264 264 $ hg bundle2 --timeout --debug --config devel.bundle2.debug=yes
265 265 bundle2-output-bundle: "HG20", 1 parts total
266 266 bundle2-output: start emission of HG20 stream
267 267 0 chunk
268 268 bundle2-output: bundle parameter:
269 269 1 chunk
270 270 bundle2-output: start of parts
271 271 bundle2-output: bundle part: "test:song"
272 272 bundle2-output-part: "test:song" (advisory) 178 bytes payload
273 273 bundle2-output: part 0: "test:song"
274 274 bundle2-output: header chunk size: 16
275 275 2 chunk
276 276 3 chunk
277 277 bundle2-output: payload chunk size: 178
278 278 4 chunk
279 279 5 chunk
280 280 bundle2-generatorexit
281 281 fake timeout complete.
282 282
283 283 Test unbundling
284 284
285 285 $ hg bundle2 | hg statbundle2
286 286 options count: 0
287 287 parts count: 0
288 288
289 289 Test old style bundle are detected and refused
290 290
291 291 $ hg bundle --all --type v1 ../bundle.hg
292 292 1 changesets found
293 293 $ hg statbundle2 < ../bundle.hg
294 294 abort: unknown bundle version 10
295 295 [255]
296 296
297 297 Test parameters
298 298 =================
299 299
300 300 - some options
301 301 - no parts
302 302
303 303 advisory parameters, no value
304 304 -------------------------------
305 305
306 306 Simplest possible parameters form
307 307
308 308 Test generation simple option
309 309
310 310 $ hg bundle2 --param 'caution' | f --hexdump
311 311
312 312 0000: 48 47 32 30 00 00 00 07 63 61 75 74 69 6f 6e 00 |HG20....caution.|
313 313 0010: 00 00 00 |...|
314 314
315 315 Test unbundling
316 316
317 317 $ hg bundle2 --param 'caution' | hg statbundle2
318 318 options count: 1
319 319 - caution
320 320 parts count: 0
321 321
322 322 Test generation multiple option
323 323
324 324 $ hg bundle2 --param 'caution' --param 'meal' | f --hexdump
325 325
326 326 0000: 48 47 32 30 00 00 00 0c 63 61 75 74 69 6f 6e 20 |HG20....caution |
327 327 0010: 6d 65 61 6c 00 00 00 00 |meal....|
328 328
329 329 Test unbundling
330 330
331 331 $ hg bundle2 --param 'caution' --param 'meal' | hg statbundle2
332 332 options count: 2
333 333 - caution
334 334 - meal
335 335 parts count: 0
336 336
337 337 advisory parameters, with value
338 338 -------------------------------
339 339
340 340 Test generation
341 341
342 342 $ hg bundle2 --param 'caution' --param 'meal=vegan' --param 'elephants' | f --hexdump
343 343
344 344 0000: 48 47 32 30 00 00 00 1c 63 61 75 74 69 6f 6e 20 |HG20....caution |
345 345 0010: 6d 65 61 6c 3d 76 65 67 61 6e 20 65 6c 65 70 68 |meal=vegan eleph|
346 346 0020: 61 6e 74 73 00 00 00 00 |ants....|
347 347
348 348 Test unbundling
349 349
350 350 $ hg bundle2 --param 'caution' --param 'meal=vegan' --param 'elephants' | hg statbundle2
351 351 options count: 3
352 352 - caution
353 353 - elephants
354 354 - meal
355 355 vegan
356 356 parts count: 0
357 357
358 358 parameter with special char in value
359 359 ---------------------------------------------------
360 360
361 361 Test generation
362 362
363 363 $ hg bundle2 --param 'e|! 7/=babar%#==tutu' --param simple | f --hexdump
364 364
365 365 0000: 48 47 32 30 00 00 00 29 65 25 37 43 25 32 31 25 |HG20...)e%7C%21%|
366 366 0010: 32 30 37 2f 3d 62 61 62 61 72 25 32 35 25 32 33 |207/=babar%25%23|
367 367 0020: 25 33 44 25 33 44 74 75 74 75 20 73 69 6d 70 6c |%3D%3Dtutu simpl|
368 368 0030: 65 00 00 00 00 |e....|
369 369
370 370 Test unbundling
371 371
372 372 $ hg bundle2 --param 'e|! 7/=babar%#==tutu' --param simple | hg statbundle2
373 373 options count: 2
374 374 - e|! 7/
375 375 babar%#==tutu
376 376 - simple
377 377 parts count: 0
378 378
379 379 Test unknown mandatory option
380 380 ---------------------------------------------------
381 381
382 382 $ hg bundle2 --param 'Gravity' | hg statbundle2
383 383 abort: unknown parameters: Stream Parameter - Gravity
384 384 [255]
385 385
386 386 Test debug output
387 387 ---------------------------------------------------
388 388
389 389 bundling debug
390 390
391 391 $ hg bundle2 --debug --param 'e|! 7/=babar%#==tutu' --param simple ../out.hg2 --config progress.debug=true --config devel.bundle2.debug=true
392 392 bundle2-output-bundle: "HG20", (2 params) 0 parts total
393 393 bundle2-output: start emission of HG20 stream
394 394 bundle2-output: bundle parameter: e%7C%21%207/=babar%25%23%3D%3Dtutu simple
395 395 bundle2-output: start of parts
396 396 bundle2-output: end of bundle
397 397
398 398 file content is ok
399 399
400 400 $ f --hexdump ../out.hg2
401 401 ../out.hg2:
402 402 0000: 48 47 32 30 00 00 00 29 65 25 37 43 25 32 31 25 |HG20...)e%7C%21%|
403 403 0010: 32 30 37 2f 3d 62 61 62 61 72 25 32 35 25 32 33 |207/=babar%25%23|
404 404 0020: 25 33 44 25 33 44 74 75 74 75 20 73 69 6d 70 6c |%3D%3Dtutu simpl|
405 405 0030: 65 00 00 00 00 |e....|
406 406
407 407 unbundling debug
408 408
409 409 $ hg statbundle2 --debug --config progress.debug=true --config devel.bundle2.debug=true < ../out.hg2
410 410 bundle2-input: start processing of HG20 stream
411 411 bundle2-input: reading bundle2 stream parameters
412 412 bundle2-input: ignoring unknown parameter 'e|! 7/'
413 413 bundle2-input: ignoring unknown parameter 'simple'
414 414 options count: 2
415 415 - e|! 7/
416 416 babar%#==tutu
417 417 - simple
418 418 bundle2-input: start extraction of bundle2 parts
419 419 bundle2-input: part header size: 0
420 420 bundle2-input: end of bundle2 stream
421 421 parts count: 0
422 422
423 423
424 424 Test buggy input
425 425 ---------------------------------------------------
426 426
427 427 empty parameter name
428 428
429 429 $ hg bundle2 --param '' --quiet
430 430 abort: empty parameter name
431 431 [255]
432 432
433 433 bad parameter name
434 434
435 435 $ hg bundle2 --param 42babar
436 436 abort: non letter first character: '42babar'
437 437 [255]
438 438
439 439
440 440 Test part
441 441 =================
442 442
443 443 $ hg bundle2 --parts ../parts.hg2 --debug --config progress.debug=true --config devel.bundle2.debug=true
444 444 bundle2-output-bundle: "HG20", 7 parts total
445 445 bundle2-output: start emission of HG20 stream
446 446 bundle2-output: bundle parameter:
447 447 bundle2-output: start of parts
448 448 bundle2-output: bundle part: "test:empty"
449 449 bundle2-output-part: "test:empty" (advisory) empty payload
450 450 bundle2-output: part 0: "test:empty"
451 451 bundle2-output: header chunk size: 17
452 452 bundle2-output: closing payload chunk
453 453 bundle2-output: bundle part: "test:empty"
454 454 bundle2-output-part: "test:empty" (advisory) empty payload
455 455 bundle2-output: part 1: "test:empty"
456 456 bundle2-output: header chunk size: 17
457 457 bundle2-output: closing payload chunk
458 458 bundle2-output: bundle part: "test:song"
459 459 bundle2-output-part: "test:song" (advisory) 178 bytes payload
460 460 bundle2-output: part 2: "test:song"
461 461 bundle2-output: header chunk size: 16
462 462 bundle2-output: payload chunk size: 178
463 463 bundle2-output: closing payload chunk
464 464 bundle2-output: bundle part: "test:debugreply"
465 465 bundle2-output-part: "test:debugreply" (advisory) empty payload
466 466 bundle2-output: part 3: "test:debugreply"
467 467 bundle2-output: header chunk size: 22
468 468 bundle2-output: closing payload chunk
469 469 bundle2-output: bundle part: "test:math"
470 470 bundle2-output-part: "test:math" (advisory) (params: 2 mandatory 2 advisory) 2 bytes payload
471 471 bundle2-output: part 4: "test:math"
472 472 bundle2-output: header chunk size: 43
473 473 bundle2-output: payload chunk size: 2
474 474 bundle2-output: closing payload chunk
475 475 bundle2-output: bundle part: "test:song"
476 476 bundle2-output-part: "test:song" (advisory) (params: 1 mandatory) empty payload
477 477 bundle2-output: part 5: "test:song"
478 478 bundle2-output: header chunk size: 29
479 479 bundle2-output: closing payload chunk
480 480 bundle2-output: bundle part: "test:ping"
481 481 bundle2-output-part: "test:ping" (advisory) empty payload
482 482 bundle2-output: part 6: "test:ping"
483 483 bundle2-output: header chunk size: 16
484 484 bundle2-output: closing payload chunk
485 485 bundle2-output: end of bundle
486 486
487 487 $ f --hexdump ../parts.hg2
488 488 ../parts.hg2:
489 489 0000: 48 47 32 30 00 00 00 00 00 00 00 11 0a 74 65 73 |HG20.........tes|
490 490 0010: 74 3a 65 6d 70 74 79 00 00 00 00 00 00 00 00 00 |t:empty.........|
491 491 0020: 00 00 00 00 11 0a 74 65 73 74 3a 65 6d 70 74 79 |......test:empty|
492 492 0030: 00 00 00 01 00 00 00 00 00 00 00 00 00 10 09 74 |...............t|
493 493 0040: 65 73 74 3a 73 6f 6e 67 00 00 00 02 00 00 00 00 |est:song........|
494 494 0050: 00 b2 50 61 74 61 6c 69 20 44 69 72 61 70 61 74 |..Patali Dirapat|
495 495 0060: 61 2c 20 43 72 6f 6d 64 61 20 43 72 6f 6d 64 61 |a, Cromda Cromda|
496 496 0070: 20 52 69 70 61 6c 6f 2c 20 50 61 74 61 20 50 61 | Ripalo, Pata Pa|
497 497 0080: 74 61 2c 20 4b 6f 20 4b 6f 20 4b 6f 0a 42 6f 6b |ta, Ko Ko Ko.Bok|
498 498 0090: 6f 72 6f 20 44 69 70 6f 75 6c 69 74 6f 2c 20 52 |oro Dipoulito, R|
499 499 00a0: 6f 6e 64 69 20 52 6f 6e 64 69 20 50 65 70 69 6e |ondi Rondi Pepin|
500 500 00b0: 6f 2c 20 50 61 74 61 20 50 61 74 61 2c 20 4b 6f |o, Pata Pata, Ko|
501 501 00c0: 20 4b 6f 20 4b 6f 0a 45 6d 61 6e 61 20 4b 61 72 | Ko Ko.Emana Kar|
502 502 00d0: 61 73 73 6f 6c 69 2c 20 4c 6f 75 63 72 61 20 4c |assoli, Loucra L|
503 503 00e0: 6f 75 63 72 61 20 50 6f 6e 70 6f 6e 74 6f 2c 20 |oucra Ponponto, |
504 504 00f0: 50 61 74 61 20 50 61 74 61 2c 20 4b 6f 20 4b 6f |Pata Pata, Ko Ko|
505 505 0100: 20 4b 6f 2e 00 00 00 00 00 00 00 16 0f 74 65 73 | Ko..........tes|
506 506 0110: 74 3a 64 65 62 75 67 72 65 70 6c 79 00 00 00 03 |t:debugreply....|
507 507 0120: 00 00 00 00 00 00 00 00 00 2b 09 74 65 73 74 3a |.........+.test:|
508 508 0130: 6d 61 74 68 00 00 00 04 02 01 02 04 01 04 07 03 |math............|
509 509 0140: 70 69 33 2e 31 34 65 32 2e 37 32 63 6f 6f 6b 69 |pi3.14e2.72cooki|
510 510 0150: 6e 67 72 61 77 00 00 00 02 34 32 00 00 00 00 00 |ngraw....42.....|
511 511 0160: 00 00 1d 09 74 65 73 74 3a 73 6f 6e 67 00 00 00 |....test:song...|
512 512 0170: 05 01 00 0b 00 72 61 6e 64 6f 6d 70 61 72 61 6d |.....randomparam|
513 513 0180: 00 00 00 00 00 00 00 10 09 74 65 73 74 3a 70 69 |.........test:pi|
514 514 0190: 6e 67 00 00 00 06 00 00 00 00 00 00 00 00 00 00 |ng..............|
515 515
516 516
517 517 $ hg statbundle2 < ../parts.hg2
518 518 options count: 0
519 519 :test:empty:
520 520 mandatory: 0
521 521 advisory: 0
522 522 payload: 0 bytes
523 523 :test:empty:
524 524 mandatory: 0
525 525 advisory: 0
526 526 payload: 0 bytes
527 527 :test:song:
528 528 mandatory: 0
529 529 advisory: 0
530 530 payload: 178 bytes
531 531 :test:debugreply:
532 532 mandatory: 0
533 533 advisory: 0
534 534 payload: 0 bytes
535 535 :test:math:
536 536 mandatory: 2
537 537 advisory: 1
538 538 payload: 2 bytes
539 539 :test:song:
540 540 mandatory: 1
541 541 advisory: 0
542 542 payload: 0 bytes
543 543 :test:ping:
544 544 mandatory: 0
545 545 advisory: 0
546 546 payload: 0 bytes
547 547 parts count: 7
548 548
549 549 $ hg statbundle2 --debug --config progress.debug=true --config devel.bundle2.debug=true < ../parts.hg2
550 550 bundle2-input: start processing of HG20 stream
551 551 bundle2-input: reading bundle2 stream parameters
552 552 options count: 0
553 553 bundle2-input: start extraction of bundle2 parts
554 554 bundle2-input: part header size: 17
555 555 bundle2-input: part type: "test:empty"
556 556 bundle2-input: part id: "0"
557 557 bundle2-input: part parameters: 0
558 558 :test:empty:
559 559 mandatory: 0
560 560 advisory: 0
561 561 bundle2-input: payload chunk size: 0
562 562 payload: 0 bytes
563 563 bundle2-input: part header size: 17
564 564 bundle2-input: part type: "test:empty"
565 565 bundle2-input: part id: "1"
566 566 bundle2-input: part parameters: 0
567 567 :test:empty:
568 568 mandatory: 0
569 569 advisory: 0
570 570 bundle2-input: payload chunk size: 0
571 571 payload: 0 bytes
572 572 bundle2-input: part header size: 16
573 573 bundle2-input: part type: "test:song"
574 574 bundle2-input: part id: "2"
575 575 bundle2-input: part parameters: 0
576 576 :test:song:
577 577 mandatory: 0
578 578 advisory: 0
579 579 bundle2-input: payload chunk size: 178
580 580 bundle2-input: payload chunk size: 0
581 581 bundle2-input-part: total payload size 178
582 582 payload: 178 bytes
583 583 bundle2-input: part header size: 22
584 584 bundle2-input: part type: "test:debugreply"
585 585 bundle2-input: part id: "3"
586 586 bundle2-input: part parameters: 0
587 587 :test:debugreply:
588 588 mandatory: 0
589 589 advisory: 0
590 590 bundle2-input: payload chunk size: 0
591 591 payload: 0 bytes
592 592 bundle2-input: part header size: 43
593 593 bundle2-input: part type: "test:math"
594 594 bundle2-input: part id: "4"
595 595 bundle2-input: part parameters: 3
596 596 :test:math:
597 597 mandatory: 2
598 598 advisory: 1
599 599 bundle2-input: payload chunk size: 2
600 600 bundle2-input: payload chunk size: 0
601 601 bundle2-input-part: total payload size 2
602 602 payload: 2 bytes
603 603 bundle2-input: part header size: 29
604 604 bundle2-input: part type: "test:song"
605 605 bundle2-input: part id: "5"
606 606 bundle2-input: part parameters: 1
607 607 :test:song:
608 608 mandatory: 1
609 609 advisory: 0
610 610 bundle2-input: payload chunk size: 0
611 611 payload: 0 bytes
612 612 bundle2-input: part header size: 16
613 613 bundle2-input: part type: "test:ping"
614 614 bundle2-input: part id: "6"
615 615 bundle2-input: part parameters: 0
616 616 :test:ping:
617 617 mandatory: 0
618 618 advisory: 0
619 619 bundle2-input: payload chunk size: 0
620 620 payload: 0 bytes
621 621 bundle2-input: part header size: 0
622 622 bundle2-input: end of bundle2 stream
623 623 parts count: 7
624 624
625 625 Test actual unbundling of test part
626 626 =======================================
627 627
628 628 Process the bundle
629 629
630 630 $ hg unbundle2 --debug --config progress.debug=true --config devel.bundle2.debug=true < ../parts.hg2
631 631 bundle2-input: start processing of HG20 stream
632 632 bundle2-input: reading bundle2 stream parameters
633 633 bundle2-input-bundle: with-transaction
634 634 bundle2-input: start extraction of bundle2 parts
635 635 bundle2-input: part header size: 17
636 636 bundle2-input: part type: "test:empty"
637 637 bundle2-input: part id: "0"
638 638 bundle2-input: part parameters: 0
639 639 bundle2-input: ignoring unsupported advisory part test:empty
640 640 bundle2-input-part: "test:empty" (advisory) unsupported-type
641 641 bundle2-input: payload chunk size: 0
642 642 bundle2-input: part header size: 17
643 643 bundle2-input: part type: "test:empty"
644 644 bundle2-input: part id: "1"
645 645 bundle2-input: part parameters: 0
646 646 bundle2-input: ignoring unsupported advisory part test:empty
647 647 bundle2-input-part: "test:empty" (advisory) unsupported-type
648 648 bundle2-input: payload chunk size: 0
649 649 bundle2-input: part header size: 16
650 650 bundle2-input: part type: "test:song"
651 651 bundle2-input: part id: "2"
652 652 bundle2-input: part parameters: 0
653 653 bundle2-input: found a handler for part 'test:song'
654 654 bundle2-input-part: "test:song" (advisory) supported
655 655 The choir starts singing:
656 656 bundle2-input: payload chunk size: 178
657 657 bundle2-input: payload chunk size: 0
658 658 bundle2-input-part: total payload size 178
659 659 Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
660 660 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
661 661 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
662 662 bundle2-input: part header size: 22
663 663 bundle2-input: part type: "test:debugreply"
664 664 bundle2-input: part id: "3"
665 665 bundle2-input: part parameters: 0
666 666 bundle2-input: found a handler for part 'test:debugreply'
667 667 bundle2-input-part: "test:debugreply" (advisory) supported
668 668 debugreply: no reply
669 669 bundle2-input: payload chunk size: 0
670 670 bundle2-input: part header size: 43
671 671 bundle2-input: part type: "test:math"
672 672 bundle2-input: part id: "4"
673 673 bundle2-input: part parameters: 3
674 674 bundle2-input: ignoring unsupported advisory part test:math
675 675 bundle2-input-part: "test:math" (advisory) (params: 2 mandatory 2 advisory) unsupported-type
676 676 bundle2-input: payload chunk size: 2
677 677 bundle2-input: payload chunk size: 0
678 678 bundle2-input-part: total payload size 2
679 679 bundle2-input: part header size: 29
680 680 bundle2-input: part type: "test:song"
681 681 bundle2-input: part id: "5"
682 682 bundle2-input: part parameters: 1
683 683 bundle2-input: found a handler for part 'test:song'
684 684 bundle2-input: ignoring unsupported advisory part test:song - randomparam
685 685 bundle2-input-part: "test:song" (advisory) (params: 1 mandatory) unsupported-params (['randomparam'])
686 686 bundle2-input: payload chunk size: 0
687 687 bundle2-input: part header size: 16
688 688 bundle2-input: part type: "test:ping"
689 689 bundle2-input: part id: "6"
690 690 bundle2-input: part parameters: 0
691 691 bundle2-input: found a handler for part 'test:ping'
692 692 bundle2-input-part: "test:ping" (advisory) supported
693 693 received ping request (id 6)
694 694 bundle2-input: payload chunk size: 0
695 695 bundle2-input: part header size: 0
696 696 bundle2-input: end of bundle2 stream
697 697 bundle2-input-bundle: 6 parts total
698 698 0 unread bytes
699 699 3 total verses sung
700 700
701 701 Unbundle with an unknown mandatory part
702 702 (should abort)
703 703
704 704 $ hg bundle2 --parts --unknown ../unknown.hg2
705 705
706 706 $ hg unbundle2 < ../unknown.hg2
707 707 The choir starts singing:
708 708 Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
709 709 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
710 710 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
711 711 debugreply: no reply
712 712 0 unread bytes
713 713 abort: missing support for test:unknown
714 714 [255]
715 715
716 716 Unbundle with an unknown mandatory part parameters
717 717 (should abort)
718 718
719 719 $ hg bundle2 --unknownparams ../unknown.hg2
720 720
721 721 $ hg unbundle2 < ../unknown.hg2
722 722 0 unread bytes
723 723 abort: missing support for test:song - randomparams
724 724 [255]
725 725
726 726 unbundle with a reply
727 727
728 728 $ hg bundle2 --parts --reply ../parts-reply.hg2
729 729 $ hg unbundle2 ../reply.hg2 < ../parts-reply.hg2
730 730 0 unread bytes
731 731 3 total verses sung
732 732
733 733 The reply is a bundle
734 734
735 735 $ f --hexdump ../reply.hg2
736 736 ../reply.hg2:
737 737 0000: 48 47 32 30 00 00 00 00 00 00 00 1b 06 6f 75 74 |HG20.........out|
738 738 0010: 70 75 74 00 00 00 00 00 01 0b 01 69 6e 2d 72 65 |put........in-re|
739 739 0020: 70 6c 79 2d 74 6f 33 00 00 00 d9 54 68 65 20 63 |ply-to3....The c|
740 740 0030: 68 6f 69 72 20 73 74 61 72 74 73 20 73 69 6e 67 |hoir starts sing|
741 741 0040: 69 6e 67 3a 0a 20 20 20 20 50 61 74 61 6c 69 20 |ing:. Patali |
742 742 0050: 44 69 72 61 70 61 74 61 2c 20 43 72 6f 6d 64 61 |Dirapata, Cromda|
743 743 0060: 20 43 72 6f 6d 64 61 20 52 69 70 61 6c 6f 2c 20 | Cromda Ripalo, |
744 744 0070: 50 61 74 61 20 50 61 74 61 2c 20 4b 6f 20 4b 6f |Pata Pata, Ko Ko|
745 745 0080: 20 4b 6f 0a 20 20 20 20 42 6f 6b 6f 72 6f 20 44 | Ko. Bokoro D|
746 746 0090: 69 70 6f 75 6c 69 74 6f 2c 20 52 6f 6e 64 69 20 |ipoulito, Rondi |
747 747 00a0: 52 6f 6e 64 69 20 50 65 70 69 6e 6f 2c 20 50 61 |Rondi Pepino, Pa|
748 748 00b0: 74 61 20 50 61 74 61 2c 20 4b 6f 20 4b 6f 20 4b |ta Pata, Ko Ko K|
749 749 00c0: 6f 0a 20 20 20 20 45 6d 61 6e 61 20 4b 61 72 61 |o. Emana Kara|
750 750 00d0: 73 73 6f 6c 69 2c 20 4c 6f 75 63 72 61 20 4c 6f |ssoli, Loucra Lo|
751 751 00e0: 75 63 72 61 20 50 6f 6e 70 6f 6e 74 6f 2c 20 50 |ucra Ponponto, P|
752 752 00f0: 61 74 61 20 50 61 74 61 2c 20 4b 6f 20 4b 6f 20 |ata Pata, Ko Ko |
753 753 0100: 4b 6f 2e 0a 00 00 00 00 00 00 00 1b 06 6f 75 74 |Ko...........out|
754 754 0110: 70 75 74 00 00 00 01 00 01 0b 01 69 6e 2d 72 65 |put........in-re|
755 755 0120: 70 6c 79 2d 74 6f 34 00 00 00 c9 64 65 62 75 67 |ply-to4....debug|
756 756 0130: 72 65 70 6c 79 3a 20 63 61 70 61 62 69 6c 69 74 |reply: capabilit|
757 757 0140: 69 65 73 3a 0a 64 65 62 75 67 72 65 70 6c 79 3a |ies:.debugreply:|
758 758 0150: 20 20 20 20 20 27 63 69 74 79 3d 21 27 0a 64 65 | 'city=!'.de|
759 759 0160: 62 75 67 72 65 70 6c 79 3a 20 20 20 20 20 20 20 |bugreply: |
760 760 0170: 20 20 27 63 65 6c 65 73 74 65 2c 76 69 6c 6c 65 | 'celeste,ville|
761 761 0180: 27 0a 64 65 62 75 67 72 65 70 6c 79 3a 20 20 20 |'.debugreply: |
762 762 0190: 20 20 27 65 6c 65 70 68 61 6e 74 73 27 0a 64 65 | 'elephants'.de|
763 763 01a0: 62 75 67 72 65 70 6c 79 3a 20 20 20 20 20 20 20 |bugreply: |
764 764 01b0: 20 20 27 62 61 62 61 72 27 0a 64 65 62 75 67 72 | 'babar'.debugr|
765 765 01c0: 65 70 6c 79 3a 20 20 20 20 20 20 20 20 20 27 63 |eply: 'c|
766 766 01d0: 65 6c 65 73 74 65 27 0a 64 65 62 75 67 72 65 70 |eleste'.debugrep|
767 767 01e0: 6c 79 3a 20 20 20 20 20 27 70 69 6e 67 2d 70 6f |ly: 'ping-po|
768 768 01f0: 6e 67 27 0a 00 00 00 00 00 00 00 1e 09 74 65 73 |ng'..........tes|
769 769 0200: 74 3a 70 6f 6e 67 00 00 00 02 01 00 0b 01 69 6e |t:pong........in|
770 770 0210: 2d 72 65 70 6c 79 2d 74 6f 37 00 00 00 00 00 00 |-reply-to7......|
771 771 0220: 00 1b 06 6f 75 74 70 75 74 00 00 00 03 00 01 0b |...output.......|
772 772 0230: 01 69 6e 2d 72 65 70 6c 79 2d 74 6f 37 00 00 00 |.in-reply-to7...|
773 773 0240: 3d 72 65 63 65 69 76 65 64 20 70 69 6e 67 20 72 |=received ping r|
774 774 0250: 65 71 75 65 73 74 20 28 69 64 20 37 29 0a 72 65 |equest (id 7).re|
775 775 0260: 70 6c 79 69 6e 67 20 74 6f 20 70 69 6e 67 20 72 |plying to ping r|
776 776 0270: 65 71 75 65 73 74 20 28 69 64 20 37 29 0a 00 00 |equest (id 7)...|
777 777 0280: 00 00 00 00 00 00 |......|
778 778
779 779 The reply is valid
780 780
781 781 $ hg statbundle2 < ../reply.hg2
782 782 options count: 0
783 783 :output:
784 784 mandatory: 0
785 785 advisory: 1
786 786 payload: 217 bytes
787 787 :output:
788 788 mandatory: 0
789 789 advisory: 1
790 790 payload: 201 bytes
791 791 :test:pong:
792 792 mandatory: 1
793 793 advisory: 0
794 794 payload: 0 bytes
795 795 :output:
796 796 mandatory: 0
797 797 advisory: 1
798 798 payload: 61 bytes
799 799 parts count: 4
800 800
801 801 Unbundle the reply to get the output:
802 802
803 803 $ hg unbundle2 < ../reply.hg2
804 804 remote: The choir starts singing:
805 805 remote: Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
806 806 remote: Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
807 807 remote: Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
808 808 remote: debugreply: capabilities:
809 809 remote: debugreply: 'city=!'
810 810 remote: debugreply: 'celeste,ville'
811 811 remote: debugreply: 'elephants'
812 812 remote: debugreply: 'babar'
813 813 remote: debugreply: 'celeste'
814 814 remote: debugreply: 'ping-pong'
815 815 remote: received ping request (id 7)
816 816 remote: replying to ping request (id 7)
817 817 0 unread bytes
818 818
819 819 Test push race detection
820 820
821 821 $ hg bundle2 --pushrace ../part-race.hg2
822 822
823 823 $ hg unbundle2 < ../part-race.hg2
824 824 0 unread bytes
825 825 abort: push race: repository changed while pushing - please try again
826 826 [255]
827 827
828 828 Support for changegroup
829 829 ===================================
830 830
831 831 $ hg unbundle $TESTDIR/bundles/rebase.hg
832 832 adding changesets
833 833 adding manifests
834 834 adding file changes
835 835 added 8 changesets with 7 changes to 7 files (+3 heads)
836 836 (run 'hg heads' to see heads, 'hg merge' to merge)
837 837
838 838 $ hg log -G
839 839 o 8:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com> H
840 840 |
841 841 | o 7:eea13746799a draft Nicolas Dumazet <nicdumz.commits@gmail.com> G
842 842 |/|
843 843 o | 6:24b6387c8c8c draft Nicolas Dumazet <nicdumz.commits@gmail.com> F
844 844 | |
845 845 | o 5:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
846 846 |/
847 847 | o 4:32af7686d403 draft Nicolas Dumazet <nicdumz.commits@gmail.com> D
848 848 | |
849 849 | o 3:5fddd98957c8 draft Nicolas Dumazet <nicdumz.commits@gmail.com> C
850 850 | |
851 851 | o 2:42ccdea3bb16 draft Nicolas Dumazet <nicdumz.commits@gmail.com> B
852 852 |/
853 853 o 1:cd010b8cd998 draft Nicolas Dumazet <nicdumz.commits@gmail.com> A
854 854
855 855 @ 0:3903775176ed draft test a
856 856
857 857
858 858 $ hg bundle2 --debug --config progress.debug=true --config devel.bundle2.debug=true --rev '8+7+5+4' ../rev.hg2
859 859 4 changesets found
860 860 list of changesets:
861 861 32af7686d403cf45b5d95f2d70cebea587ac806a
862 862 9520eea781bcca16c1e15acc0ba14335a0e8e5ba
863 863 eea13746799a9e0bfd88f29d3c2e9dc9389f524f
864 864 02de42196ebee42ef284b6780a87cdc96e8eaab6
865 865 bundle2-output-bundle: "HG20", 1 parts total
866 866 bundle2-output: start emission of HG20 stream
867 867 bundle2-output: bundle parameter:
868 868 bundle2-output: start of parts
869 869 bundle2-output: bundle part: "changegroup"
870 870 bundle2-output-part: "changegroup" (advisory) streamed payload
871 871 bundle2-output: part 0: "changegroup"
872 872 bundle2-output: header chunk size: 18
873 873 bundling: 1/4 changesets (25.00%)
874 874 bundling: 2/4 changesets (50.00%)
875 875 bundling: 3/4 changesets (75.00%)
876 876 bundling: 4/4 changesets (100.00%)
877 877 bundling: 1/4 manifests (25.00%)
878 878 bundling: 2/4 manifests (50.00%)
879 879 bundling: 3/4 manifests (75.00%)
880 880 bundling: 4/4 manifests (100.00%)
881 881 bundling: D 1/3 files (33.33%)
882 882 bundling: E 2/3 files (66.67%)
883 883 bundling: H 3/3 files (100.00%)
884 884 bundle2-output: payload chunk size: 1555
885 885 bundle2-output: closing payload chunk
886 886 bundle2-output: end of bundle
887 887
888 888 $ f --hexdump ../rev.hg2
889 889 ../rev.hg2:
890 890 0000: 48 47 32 30 00 00 00 00 00 00 00 12 0b 63 68 61 |HG20.........cha|
891 891 0010: 6e 67 65 67 72 6f 75 70 00 00 00 00 00 00 00 00 |ngegroup........|
892 892 0020: 06 13 00 00 00 a4 32 af 76 86 d4 03 cf 45 b5 d9 |......2.v....E..|
893 893 0030: 5f 2d 70 ce be a5 87 ac 80 6a 5f dd d9 89 57 c8 |_-p......j_...W.|
894 894 0040: a5 4a 4d 43 6d fe 1d a9 d8 7f 21 a1 b9 7b 00 00 |.JMCm.....!..{..|
895 895 0050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
896 896 0060: 00 00 32 af 76 86 d4 03 cf 45 b5 d9 5f 2d 70 ce |..2.v....E.._-p.|
897 897 0070: be a5 87 ac 80 6a 00 00 00 00 00 00 00 29 00 00 |.....j.......)..|
898 898 0080: 00 29 36 65 31 66 34 63 34 37 65 63 62 35 33 33 |.)6e1f4c47ecb533|
899 899 0090: 66 66 64 30 63 38 65 35 32 63 64 63 38 38 61 66 |ffd0c8e52cdc88af|
900 900 00a0: 62 36 63 64 33 39 65 32 30 63 0a 00 00 00 66 00 |b6cd39e20c....f.|
901 901 00b0: 00 00 68 00 00 00 02 44 0a 00 00 00 69 00 00 00 |..h....D....i...|
902 902 00c0: 6a 00 00 00 01 44 00 00 00 a4 95 20 ee a7 81 bc |j....D..... ....|
903 903 00d0: ca 16 c1 e1 5a cc 0b a1 43 35 a0 e8 e5 ba cd 01 |....Z...C5......|
904 904 00e0: 0b 8c d9 98 f3 98 1a 5a 81 15 f9 4f 8d a4 ab 50 |.......Z...O...P|
905 905 00f0: 60 89 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |`...............|
906 906 0100: 00 00 00 00 00 00 95 20 ee a7 81 bc ca 16 c1 e1 |....... ........|
907 907 0110: 5a cc 0b a1 43 35 a0 e8 e5 ba 00 00 00 00 00 00 |Z...C5..........|
908 908 0120: 00 29 00 00 00 29 34 64 65 63 65 39 63 38 32 36 |.)...)4dece9c826|
909 909 0130: 66 36 39 34 39 30 35 30 37 62 39 38 63 36 33 38 |f69490507b98c638|
910 910 0140: 33 61 33 30 30 39 62 32 39 35 38 33 37 64 0a 00 |3a3009b295837d..|
911 911 0150: 00 00 66 00 00 00 68 00 00 00 02 45 0a 00 00 00 |..f...h....E....|
912 912 0160: 69 00 00 00 6a 00 00 00 01 45 00 00 00 a2 ee a1 |i...j....E......|
913 913 0170: 37 46 79 9a 9e 0b fd 88 f2 9d 3c 2e 9d c9 38 9f |7Fy.......<...8.|
914 914 0180: 52 4f 24 b6 38 7c 8c 8c ae 37 17 88 80 f3 fa 95 |RO$.8|...7......|
915 915 0190: de d3 cb 1c f7 85 95 20 ee a7 81 bc ca 16 c1 e1 |....... ........|
916 916 01a0: 5a cc 0b a1 43 35 a0 e8 e5 ba ee a1 37 46 79 9a |Z...C5......7Fy.|
917 917 01b0: 9e 0b fd 88 f2 9d 3c 2e 9d c9 38 9f 52 4f 00 00 |......<...8.RO..|
918 918 01c0: 00 00 00 00 00 29 00 00 00 29 33 36 35 62 39 33 |.....)...)365b93|
919 919 01d0: 64 35 37 66 64 66 34 38 31 34 65 32 62 35 39 31 |d57fdf4814e2b591|
920 920 01e0: 31 64 36 62 61 63 66 66 32 62 31 32 30 31 34 34 |1d6bacff2b120144|
921 921 01f0: 34 31 0a 00 00 00 66 00 00 00 68 00 00 00 00 00 |41....f...h.....|
922 922 0200: 00 00 69 00 00 00 6a 00 00 00 01 47 00 00 00 a4 |..i...j....G....|
923 923 0210: 02 de 42 19 6e be e4 2e f2 84 b6 78 0a 87 cd c9 |..B.n......x....|
924 924 0220: 6e 8e aa b6 24 b6 38 7c 8c 8c ae 37 17 88 80 f3 |n...$.8|...7....|
925 925 0230: fa 95 de d3 cb 1c f7 85 00 00 00 00 00 00 00 00 |................|
926 926 0240: 00 00 00 00 00 00 00 00 00 00 00 00 02 de 42 19 |..............B.|
927 927 0250: 6e be e4 2e f2 84 b6 78 0a 87 cd c9 6e 8e aa b6 |n......x....n...|
928 928 0260: 00 00 00 00 00 00 00 29 00 00 00 29 38 62 65 65 |.......)...)8bee|
929 929 0270: 34 38 65 64 63 37 33 31 38 35 34 31 66 63 30 30 |48edc7318541fc00|
930 930 0280: 31 33 65 65 34 31 62 30 38 39 32 37 36 61 38 63 |13ee41b089276a8c|
931 931 0290: 32 34 62 66 0a 00 00 00 66 00 00 00 66 00 00 00 |24bf....f...f...|
932 932 02a0: 02 48 0a 00 00 00 67 00 00 00 68 00 00 00 01 48 |.H....g...h....H|
933 933 02b0: 00 00 00 00 00 00 00 8b 6e 1f 4c 47 ec b5 33 ff |........n.LG..3.|
934 934 02c0: d0 c8 e5 2c dc 88 af b6 cd 39 e2 0c 66 a5 a0 18 |...,.....9..f...|
935 935 02d0: 17 fd f5 23 9c 27 38 02 b5 b7 61 8d 05 1c 89 e4 |...#.'8...a.....|
936 936 02e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
937 937 02f0: 00 00 00 00 32 af 76 86 d4 03 cf 45 b5 d9 5f 2d |....2.v....E.._-|
938 938 0300: 70 ce be a5 87 ac 80 6a 00 00 00 81 00 00 00 81 |p......j........|
939 939 0310: 00 00 00 2b 44 00 63 33 66 31 63 61 32 39 32 34 |...+D.c3f1ca2924|
940 940 0320: 63 31 36 61 31 39 62 30 36 35 36 61 38 34 39 30 |c16a19b0656a8490|
941 941 0330: 30 65 35 30 34 65 35 62 30 61 65 63 32 64 0a 00 |0e504e5b0aec2d..|
942 942 0340: 00 00 8b 4d ec e9 c8 26 f6 94 90 50 7b 98 c6 38 |...M...&...P{..8|
943 943 0350: 3a 30 09 b2 95 83 7d 00 7d 8c 9d 88 84 13 25 f5 |:0....}.}.....%.|
944 944 0360: c6 b0 63 71 b3 5b 4e 8a 2b 1a 83 00 00 00 00 00 |..cq.[N.+.......|
945 945 0370: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 95 |................|
946 946 0380: 20 ee a7 81 bc ca 16 c1 e1 5a cc 0b a1 43 35 a0 | ........Z...C5.|
947 947 0390: e8 e5 ba 00 00 00 2b 00 00 00 ac 00 00 00 2b 45 |......+.......+E|
948 948 03a0: 00 39 63 36 66 64 30 33 35 30 61 36 63 30 64 30 |.9c6fd0350a6c0d0|
949 949 03b0: 63 34 39 64 34 61 39 63 35 30 31 37 63 66 30 37 |c49d4a9c5017cf07|
950 950 03c0: 30 34 33 66 35 34 65 35 38 0a 00 00 00 8b 36 5b |043f54e58.....6[|
951 951 03d0: 93 d5 7f df 48 14 e2 b5 91 1d 6b ac ff 2b 12 01 |....H.....k..+..|
952 952 03e0: 44 41 28 a5 84 c6 5e f1 21 f8 9e b6 6a b7 d0 bc |DA(...^.!...j...|
953 953 03f0: 15 3d 80 99 e7 ce 4d ec e9 c8 26 f6 94 90 50 7b |.=....M...&...P{|
954 954 0400: 98 c6 38 3a 30 09 b2 95 83 7d ee a1 37 46 79 9a |..8:0....}..7Fy.|
955 955 0410: 9e 0b fd 88 f2 9d 3c 2e 9d c9 38 9f 52 4f 00 00 |......<...8.RO..|
956 956 0420: 00 56 00 00 00 56 00 00 00 2b 46 00 32 32 62 66 |.V...V...+F.22bf|
957 957 0430: 63 66 64 36 32 61 32 31 61 33 32 38 37 65 64 62 |cfd62a21a3287edb|
958 958 0440: 64 34 64 36 35 36 32 31 38 64 30 66 35 32 35 65 |d4d656218d0f525e|
959 959 0450: 64 37 36 61 0a 00 00 00 97 8b ee 48 ed c7 31 85 |d76a.......H..1.|
960 960 0460: 41 fc 00 13 ee 41 b0 89 27 6a 8c 24 bf 28 a5 84 |A....A..'j.$.(..|
961 961 0470: c6 5e f1 21 f8 9e b6 6a b7 d0 bc 15 3d 80 99 e7 |.^.!...j....=...|
962 962 0480: ce 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
963 963 0490: 00 00 00 00 00 02 de 42 19 6e be e4 2e f2 84 b6 |.......B.n......|
964 964 04a0: 78 0a 87 cd c9 6e 8e aa b6 00 00 00 2b 00 00 00 |x....n......+...|
965 965 04b0: 56 00 00 00 00 00 00 00 81 00 00 00 81 00 00 00 |V...............|
966 966 04c0: 2b 48 00 38 35 30 30 31 38 39 65 37 34 61 39 65 |+H.8500189e74a9e|
967 967 04d0: 30 34 37 35 65 38 32 32 30 39 33 62 63 37 64 62 |0475e822093bc7db|
968 968 04e0: 30 64 36 33 31 61 65 62 30 62 34 0a 00 00 00 00 |0d631aeb0b4.....|
969 969 04f0: 00 00 00 05 44 00 00 00 62 c3 f1 ca 29 24 c1 6a |....D...b...)$.j|
970 970 0500: 19 b0 65 6a 84 90 0e 50 4e 5b 0a ec 2d 00 00 00 |..ej...PN[..-...|
971 971 0510: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
972 972 0520: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
973 973 0530: 00 00 00 00 00 32 af 76 86 d4 03 cf 45 b5 d9 5f |.....2.v....E.._|
974 974 0540: 2d 70 ce be a5 87 ac 80 6a 00 00 00 00 00 00 00 |-p......j.......|
975 975 0550: 00 00 00 00 02 44 0a 00 00 00 00 00 00 00 05 45 |.....D.........E|
976 976 0560: 00 00 00 62 9c 6f d0 35 0a 6c 0d 0c 49 d4 a9 c5 |...b.o.5.l..I...|
977 977 0570: 01 7c f0 70 43 f5 4e 58 00 00 00 00 00 00 00 00 |.|.pC.NX........|
978 978 0580: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
979 979 0590: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
980 980 05a0: 95 20 ee a7 81 bc ca 16 c1 e1 5a cc 0b a1 43 35 |. ........Z...C5|
981 981 05b0: a0 e8 e5 ba 00 00 00 00 00 00 00 00 00 00 00 02 |................|
982 982 05c0: 45 0a 00 00 00 00 00 00 00 05 48 00 00 00 62 85 |E.........H...b.|
983 983 05d0: 00 18 9e 74 a9 e0 47 5e 82 20 93 bc 7d b0 d6 31 |...t..G^. ..}..1|
984 984 05e0: ae b0 b4 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
985 985 05f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
986 986 0600: 00 00 00 00 00 00 00 00 00 00 00 02 de 42 19 6e |.............B.n|
987 987 0610: be e4 2e f2 84 b6 78 0a 87 cd c9 6e 8e aa b6 00 |......x....n....|
988 988 0620: 00 00 00 00 00 00 00 00 00 00 02 48 0a 00 00 00 |...........H....|
989 989 0630: 00 00 00 00 00 00 00 00 00 00 00 00 00 |.............|
990 990
991 991 $ hg debugbundle ../rev.hg2
992 992 Stream params: {}
993 changegroup -- '{}'
993 changegroup -- 'sortdict()'
994 994 32af7686d403cf45b5d95f2d70cebea587ac806a
995 995 9520eea781bcca16c1e15acc0ba14335a0e8e5ba
996 996 eea13746799a9e0bfd88f29d3c2e9dc9389f524f
997 997 02de42196ebee42ef284b6780a87cdc96e8eaab6
998 998 $ hg unbundle ../rev.hg2
999 999 adding changesets
1000 1000 adding manifests
1001 1001 adding file changes
1002 1002 added 0 changesets with 0 changes to 3 files
1003 1003 (run 'hg update' to get a working copy)
1004 1004
1005 1005 with reply
1006 1006
1007 1007 $ hg bundle2 --rev '8+7+5+4' --reply ../rev-rr.hg2
1008 1008 $ hg unbundle2 ../rev-reply.hg2 < ../rev-rr.hg2
1009 1009 0 unread bytes
1010 1010 addchangegroup return: 1
1011 1011
1012 1012 $ f --hexdump ../rev-reply.hg2
1013 1013 ../rev-reply.hg2:
1014 1014 0000: 48 47 32 30 00 00 00 00 00 00 00 2f 11 72 65 70 |HG20......./.rep|
1015 1015 0010: 6c 79 3a 63 68 61 6e 67 65 67 72 6f 75 70 00 00 |ly:changegroup..|
1016 1016 0020: 00 00 00 02 0b 01 06 01 69 6e 2d 72 65 70 6c 79 |........in-reply|
1017 1017 0030: 2d 74 6f 31 72 65 74 75 72 6e 31 00 00 00 00 00 |-to1return1.....|
1018 1018 0040: 00 00 1b 06 6f 75 74 70 75 74 00 00 00 01 00 01 |....output......|
1019 1019 0050: 0b 01 69 6e 2d 72 65 70 6c 79 2d 74 6f 31 00 00 |..in-reply-to1..|
1020 1020 0060: 00 64 61 64 64 69 6e 67 20 63 68 61 6e 67 65 73 |.dadding changes|
1021 1021 0070: 65 74 73 0a 61 64 64 69 6e 67 20 6d 61 6e 69 66 |ets.adding manif|
1022 1022 0080: 65 73 74 73 0a 61 64 64 69 6e 67 20 66 69 6c 65 |ests.adding file|
1023 1023 0090: 20 63 68 61 6e 67 65 73 0a 61 64 64 65 64 20 30 | changes.added 0|
1024 1024 00a0: 20 63 68 61 6e 67 65 73 65 74 73 20 77 69 74 68 | changesets with|
1025 1025 00b0: 20 30 20 63 68 61 6e 67 65 73 20 74 6f 20 33 20 | 0 changes to 3 |
1026 1026 00c0: 66 69 6c 65 73 0a 00 00 00 00 00 00 00 00 |files.........|
1027 1027
1028 1028 Check handling of exception during generation.
1029 1029 ----------------------------------------------
1030 1030
1031 1031 $ hg bundle2 --genraise > ../genfailed.hg2
1032 1032 abort: Someone set up us the bomb!
1033 1033 [255]
1034 1034
1035 1035 Should still be a valid bundle
1036 1036
1037 1037 $ f --hexdump ../genfailed.hg2
1038 1038 ../genfailed.hg2:
1039 1039 0000: 48 47 32 30 00 00 00 00 00 00 00 0d 06 6f 75 74 |HG20.........out|
1040 1040 0010: 70 75 74 00 00 00 00 00 00 ff ff ff ff 00 00 00 |put.............|
1041 1041 0020: 48 0b 65 72 72 6f 72 3a 61 62 6f 72 74 00 00 00 |H.error:abort...|
1042 1042 0030: 00 01 00 07 2d 6d 65 73 73 61 67 65 75 6e 65 78 |....-messageunex|
1043 1043 0040: 70 65 63 74 65 64 20 65 72 72 6f 72 3a 20 53 6f |pected error: So|
1044 1044 0050: 6d 65 6f 6e 65 20 73 65 74 20 75 70 20 75 73 20 |meone set up us |
1045 1045 0060: 74 68 65 20 62 6f 6d 62 21 00 00 00 00 00 00 00 |the bomb!.......|
1046 1046 0070: 00 |.|
1047 1047
1048 1048 And its handling on the other size raise a clean exception
1049 1049
1050 1050 $ cat ../genfailed.hg2 | hg unbundle2
1051 1051 0 unread bytes
1052 1052 abort: unexpected error: Someone set up us the bomb!
1053 1053 [255]
1054 1054
1055 1055 Test compression
1056 1056 ================
1057 1057
1058 1058 Simple case where it just work: GZ
1059 1059 ----------------------------------
1060 1060
1061 1061 $ hg bundle2 --compress GZ --rev '8+7+5+4' ../rev.hg2.bz
1062 1062 $ f --hexdump ../rev.hg2.bz
1063 1063 ../rev.hg2.bz:
1064 1064 0000: 48 47 32 30 00 00 00 0e 43 6f 6d 70 72 65 73 73 |HG20....Compress|
1065 1065 0010: 69 6f 6e 3d 47 5a 78 9c 95 94 7d 68 95 55 1c c7 |ion=GZx...}h.U..|
1066 1066 0020: 9f 3b 31 e8 ce fa c3 65 be a0 a4 b4 52 b9 29 e7 |.;1....e....R.).|
1067 1067 0030: f5 79 ce 89 fa 63 ed 5e 77 8b 9c c3 3f 2a 1c 68 |.y...c.^w...?*.h|
1068 1068 0040: cf 79 9b dd 6a ae b0 28 74 b8 e5 96 5b bb 86 61 |.y..j..(t...[..a|
1069 1069 0050: a3 15 6e 3a 71 c8 6a e8 a5 da 95 64 28 22 ce 69 |..n:q.j....d(".i|
1070 1070 0060: cd 06 59 34 28 2b 51 2a 58 c3 17 56 2a 9a 9d 67 |..Y4(+Q*X..V*..g|
1071 1071 0070: dc c6 35 9e c4 1d f8 9e 87 f3 9c f3 3b bf 0f bf |..5.........;...|
1072 1072 0080: 97 e3 38 ce f4 42 b9 d6 af ae d2 55 af ae 7b ad |..8..B.....U..{.|
1073 1073 0090: c6 c9 8d bb 8a ec b4 07 ed 7f fd ed d3 53 be 4e |.............S.N|
1074 1074 00a0: f4 0e af 59 52 73 ea 50 d7 96 9e ba d4 9a 1f 87 |...YRs.P........|
1075 1075 00b0: 9b 9f 1d e8 7a 6a 79 e9 cb 7f cf eb fe 7e d3 82 |....zjy......~..|
1076 1076 00c0: ce 2f 36 38 21 23 cc 36 b7 b5 38 90 ab a1 21 92 |./68!#.6..8...!.|
1077 1077 00d0: 78 5a 0a 8a b1 31 0a 48 a6 29 92 4a 32 e6 1b e1 |xZ...1.H.).J2...|
1078 1078 00e0: 4a 85 b9 46 40 46 ed 61 63 b5 d6 aa 20 1e ac 5e |J..F@F.ac... ..^|
1079 1079 00f0: b0 0a ae 8a c4 03 c6 d6 f9 a3 7b eb fb 4e de 7f |..........{..N..|
1080 1080 0100: e4 97 55 5f 15 76 96 d2 5d bf 9d 3f 38 18 29 4c |..U_.v..]..?8.)L|
1081 1081 0110: 0f b7 5d 6e 9b b3 aa 7e c6 d5 15 5b f7 7c 52 f1 |..]n...~...[.|R.|
1082 1082 0120: 7c 73 18 63 98 6d 3e 23 51 5a 6a 2e 19 72 8d cb ||s.c.m>#QZj..r..|
1083 1083 0130: 09 07 14 78 82 33 e9 62 86 7d 0c 00 17 88 53 86 |...x.3.b.}....S.|
1084 1084 0140: 3d 75 0b 63 e2 16 c6 84 9d 76 8f 76 7a cb de fc |=u.c.....v.vz...|
1085 1085 0150: a8 a3 f0 46 d3 a5 f6 c7 96 b6 9f 60 3b 57 ae 28 |...F.......`;W.(|
1086 1086 0160: ce b2 8d e9 f4 3e 6f 66 53 dd e5 6b ad 67 be f9 |.....>ofS..k.g..|
1087 1087 0170: 72 ee 5f 8d 61 3c 61 b6 f9 8c d8 a5 82 63 45 3d |r._.a<a......cE=|
1088 1088 0180: a3 0c 61 90 68 24 28 87 50 b9 c2 97 c6 20 01 11 |..a.h$(.P.... ..|
1089 1089 0190: 80 84 10 98 cf e8 e4 13 96 05 51 2c 38 f3 c4 ec |..........Q,8...|
1090 1090 01a0: ea 43 e7 96 5e 6a c8 be 11 dd 32 78 a2 fa dd 8f |.C..^j....2x....|
1091 1091 01b0: b3 61 84 61 51 0c b3 cd 27 64 42 6b c2 b4 92 1e |.a.aQ...'dBk....|
1092 1092 01c0: 86 8c 12 68 24 00 10 db 7f 50 00 c6 91 e7 fa 4c |...h$....P.....L|
1093 1093 01d0: 22 22 cc bf 84 81 0a 92 c1 aa 2a c7 1b 49 e6 ee |""........*..I..|
1094 1094 01e0: 6b a9 7e e0 e9 b2 91 5e 7c 73 68 e0 fc 23 3f 34 |k.~....^|sh..#?4|
1095 1095 01f0: ed cf 0e f2 b3 d3 4c d7 ae 59 33 6f 8c 3d b8 63 |......L..Y3o.=.c|
1096 1096 0200: 21 2b e8 3d e0 6f 9d 3a b7 f9 dc 24 2a b2 3e a7 |!+.=.o.:...$*.>.|
1097 1097 0210: 58 dc 91 d8 40 e9 23 8e 88 84 ae 0f b9 00 2e b5 |X...@.#.........|
1098 1098 0220: 74 36 f3 40 53 40 34 15 c0 d7 12 8d e7 bb 65 f9 |t6.@S@4.......e.|
1099 1099 0230: c8 ef 03 0f ff f9 fe b6 8a 0d 6d fd ec 51 70 f7 |..........m..Qp.|
1100 1100 0240: a7 ad 9b 6b 9d da 74 7b 53 43 d1 43 63 fd 19 f9 |...k..t{SC.Cc...|
1101 1101 0250: ca 67 95 e5 ef c4 e6 6c 9e 44 e1 c5 ac 7a 82 6f |.g.....l.D...z.o|
1102 1102 0260: c2 e1 d2 b5 2d 81 29 f0 5d 09 6c 6f 10 ae 88 cf |....-.).].lo....|
1103 1103 0270: 25 05 d0 93 06 78 80 60 43 2d 10 1b 47 71 2b b7 |%....x.`C-..Gq+.|
1104 1104 0280: 7f bb e9 a7 e4 7d 67 7b df 9b f7 62 cf cd d8 f4 |.....}g{...b....|
1105 1105 0290: 48 bc 64 51 57 43 ff ea 8b 0b ae 74 64 53 07 86 |H.dQWC.....tdS..|
1106 1106 02a0: fa 66 3c 5e f7 e1 af a7 c2 90 ff a7 be 9e c9 29 |.f<^...........)|
1107 1107 02b0: b6 cc 41 48 18 69 94 8b 7c 04 7d 8c 98 a7 95 50 |..AH.i..|.}....P|
1108 1108 02c0: 44 d9 d0 20 c8 14 30 14 51 ad 6c 16 03 94 0f 5a |D.. ..0.Q.l....Z|
1109 1109 02d0: 46 93 7f 1c 87 8d 25 d7 9d a2 d1 92 4c f3 c2 54 |F.....%.....L..T|
1110 1110 02e0: ba f8 70 18 ca 24 0a 29 96 43 71 f2 93 95 74 18 |..p..$.).Cq...t.|
1111 1111 02f0: b5 65 c4 b8 f6 6c 5c 34 20 1e d5 0c 21 c0 b1 90 |.e...l\4 ...!...|
1112 1112 0300: 9e 12 40 b9 18 fa 5a 00 41 a2 39 d3 a9 c1 73 21 |..@...Z.A.9...s!|
1113 1113 0310: 8e 5e 3c b9 b8 f8 48 6a 76 46 a7 1a b6 dd 5b 51 |.^<...HjvF....[Q|
1114 1114 0320: 5e 19 1d 59 12 c6 32 89 02 9a c0 8f 4f b8 0a ba |^..Y..2.....O...|
1115 1115 0330: 5e ec 58 37 44 a3 2f dd 33 ed c9 d3 dd c7 22 1b |^.X7D./.3.....".|
1116 1116 0340: 2f d4 94 8e 95 3f 77 a7 ae 6e f3 32 8d bb 4a 4c |/....?w..n.2..JL|
1117 1117 0350: b8 0a 5a 43 34 3a b3 3a d6 77 ff 5c b6 fa ad f9 |..ZC4:.:.w.\....|
1118 1118 0360: db fb 6a 33 df c1 7d 99 cf ef d4 d5 6d da 77 7c |..j3..}.....m.w||
1119 1119 0370: 3b 19 fd af c5 3f f1 60 c3 17 |;....?.`..|
1120 1120 $ hg debugbundle ../rev.hg2.bz
1121 Stream params: {'Compression': 'GZ'}
1122 changegroup -- '{}'
1121 Stream params: sortdict([('Compression', 'GZ')])
1122 changegroup -- 'sortdict()'
1123 1123 32af7686d403cf45b5d95f2d70cebea587ac806a
1124 1124 9520eea781bcca16c1e15acc0ba14335a0e8e5ba
1125 1125 eea13746799a9e0bfd88f29d3c2e9dc9389f524f
1126 1126 02de42196ebee42ef284b6780a87cdc96e8eaab6
1127 1127 $ hg unbundle ../rev.hg2.bz
1128 1128 adding changesets
1129 1129 adding manifests
1130 1130 adding file changes
1131 1131 added 0 changesets with 0 changes to 3 files
1132 1132 (run 'hg update' to get a working copy)
1133 1133 Simple case where it just work: BZ
1134 1134 ----------------------------------
1135 1135
1136 1136 $ hg bundle2 --compress BZ --rev '8+7+5+4' ../rev.hg2.bz
1137 1137 $ f --hexdump ../rev.hg2.bz
1138 1138 ../rev.hg2.bz:
1139 1139 0000: 48 47 32 30 00 00 00 0e 43 6f 6d 70 72 65 73 73 |HG20....Compress|
1140 1140 0010: 69 6f 6e 3d 42 5a 42 5a 68 39 31 41 59 26 53 59 |ion=BZBZh91AY&SY|
1141 1141 0020: a3 4b 18 3d 00 00 1a 7f ff ff bf 5f f6 ef ef 7f |.K.=......._....|
1142 1142 0030: f6 3f f7 d1 d9 ff ff f7 6e ff ff 6e f7 f6 bd df |.?......n..n....|
1143 1143 0040: b5 ab ff cf 67 f6 e7 7b f7 c0 02 d7 33 82 8b 51 |....g..{....3..Q|
1144 1144 0050: 04 a5 53 d5 3d 27 a0 99 18 4d 0d 34 00 d1 a1 e8 |..S.='...M.4....|
1145 1145 0060: 80 c8 7a 87 a9 a3 43 6a 3d 46 86 26 80 34 3d 40 |..z...Cj=F.&.4=@|
1146 1146 0070: c8 c9 b5 34 f4 8f 48 0f 51 ea 34 34 fd 4d aa 19 |...4..H.Q.44.M..|
1147 1147 0080: 03 40 0c 08 da 86 43 d4 f5 0f 42 1e a0 f3 54 33 |.@....C...B...T3|
1148 1148 0090: 54 d3 13 4d 03 40 32 00 00 32 03 26 80 0d 00 0d |T..M.@2..2.&....|
1149 1149 00a0: 00 68 c8 c8 03 20 32 30 98 8c 80 00 00 03 4d 00 |.h... 20......M.|
1150 1150 00b0: c8 00 00 0d 00 00 22 99 a1 34 c2 64 a6 d5 34 1a |......"..4.d..4.|
1151 1151 00c0: 00 00 06 86 83 4d 07 a8 d1 a0 68 01 a0 00 00 00 |.....M....h.....|
1152 1152 00d0: 00 0d 06 80 00 00 00 0d 00 03 40 00 00 04 a4 a1 |..........@.....|
1153 1153 00e0: 4d a9 89 89 b4 9a 32 0c 43 46 86 87 a9 8d 41 9a |M.....2.CF....A.|
1154 1154 00f0: 98 46 9a 0d 31 32 1a 34 0d 0c 8d a2 0c 98 4d 06 |.F..12.4......M.|
1155 1155 0100: 8c 40 c2 60 8d 0d 0c 20 c9 89 fa a0 d0 d3 21 a1 |.@.`... ......!.|
1156 1156 0110: ea 34 d3 68 9e a6 d1 74 05 33 cb 66 96 93 28 64 |.4.h...t.3.f..(d|
1157 1157 0120: 40 91 22 ac 55 9b ea 40 7b 38 94 e2 f8 06 00 cb |@.".U..@{8......|
1158 1158 0130: 28 02 00 4d ab 40 24 10 43 18 cf 64 b4 06 83 0c |(..M.@$.C..d....|
1159 1159 0140: 34 6c b4 a3 d4 0a 0a e4 a8 5c 4e 23 c0 c9 7a 31 |4l.......\N#..z1|
1160 1160 0150: 97 87 77 7a 64 88 80 8e 60 97 20 93 0f 8e eb c4 |..wzd...`. .....|
1161 1161 0160: 62 a4 44 a3 52 20 b2 99 a9 2e e1 d7 29 4a 54 ac |b.D.R ......)JT.|
1162 1162 0170: 44 7a bb cc 04 3d e0 aa bd 6a 33 5e 9b a2 57 36 |Dz...=...j3^..W6|
1163 1163 0180: fa cb 45 bb 6d 3e c1 d9 d9 f5 83 69 8a d0 e0 e2 |..E.m>.....i....|
1164 1164 0190: e7 ae 90 55 24 da 3f ab 78 c0 4c b4 56 a3 9e a4 |...U$.?.x.L.V...|
1165 1165 01a0: af 9c 65 74 86 ec 6d dc 62 dc 33 ca c8 50 dd 9d |..et..m.b.3..P..|
1166 1166 01b0: 98 8e 9e 59 20 f3 f0 42 91 4a 09 f5 75 8d 3d a5 |...Y ..B.J..u.=.|
1167 1167 01c0: a5 15 cb 8d 10 63 b0 c2 2e b2 81 f7 c1 76 0e 53 |.....c.......v.S|
1168 1168 01d0: 6c 0e 46 73 b5 ae 67 f9 4c 0b 45 6b a8 32 2a 2f |l.Fs..g.L.Ek.2*/|
1169 1169 01e0: a2 54 a4 44 05 20 a1 38 d1 a4 c6 09 a8 2b 08 99 |.T.D. .8.....+..|
1170 1170 01f0: a4 14 ae 8d a3 e3 aa 34 27 d8 44 ca c3 5d 21 8b |.......4'.D..]!.|
1171 1171 0200: 1a 1e 97 29 71 2b 09 4a 4a 55 55 94 58 65 b2 bc |...)q+.JJUU.Xe..|
1172 1172 0210: f3 a5 90 26 36 76 67 7a 51 98 d6 8a 4a 99 50 b5 |...&6vgzQ...J.P.|
1173 1173 0220: 99 8f 94 21 17 a9 8b f3 ad 4c 33 d4 2e 40 c8 0c |...!.....L3..@..|
1174 1174 0230: 3b 90 53 39 db 48 02 34 83 48 d6 b3 99 13 d2 58 |;.S9.H.4.H.....X|
1175 1175 0240: 65 8e 71 ac a9 06 95 f2 c4 8e b4 08 6b d3 0c ae |e.q.........k...|
1176 1176 0250: d9 90 56 71 43 a7 a2 62 16 3e 50 63 d3 57 3c 2d |..VqC..b.>Pc.W<-|
1177 1177 0260: 9f 0f 34 05 08 d8 a6 4b 59 31 54 66 3a 45 0c 8a |..4....KY1Tf:E..|
1178 1178 0270: c7 90 3a f0 6a 83 1b f5 ca fb 80 2b 50 06 fb 51 |..:.j......+P..Q|
1179 1179 0280: 7e a6 a4 d4 81 44 82 21 54 00 5b 1a 30 83 62 a3 |~....D.!T.[.0.b.|
1180 1180 0290: 18 b6 24 19 1e 45 df 4d 5c db a6 af 5b ac 90 fa |..$..E.M\...[...|
1181 1181 02a0: 3e ed f9 ec 4c ba 36 ee d8 60 20 a7 c7 3b cb d1 |>...L.6..` ..;..|
1182 1182 02b0: 90 43 7d 27 16 50 5d ad f4 14 07 0b 90 5c cc 6b |.C}'.P]......\.k|
1183 1183 02c0: 8d 3f a6 88 f4 34 37 a8 cf 14 63 36 19 f7 3e 28 |.?...47...c6..>(|
1184 1184 02d0: de 99 e8 16 a4 9d 0d 40 a1 a7 24 52 14 a6 72 62 |.......@..$R..rb|
1185 1185 02e0: 59 5a ca 2d e5 51 90 78 88 d9 c6 c7 21 d0 f7 46 |YZ.-.Q.x....!..F|
1186 1186 02f0: b2 04 46 44 4e 20 9c 12 b1 03 4e 25 e0 a9 0c 58 |..FDN ....N%...X|
1187 1187 0300: 5b 1d 3c 93 20 01 51 de a9 1c 69 23 32 46 14 b4 |[.<. .Q...i#2F..|
1188 1188 0310: 90 db 17 98 98 50 03 90 29 aa 40 b0 13 d8 43 d2 |.....P..).@...C.|
1189 1189 0320: 5f c5 9d eb f3 f2 ad 41 e8 7a a9 ed a1 58 84 a6 |_......A.z...X..|
1190 1190 0330: 42 bf d6 fc 24 82 c1 20 32 26 4a 15 a6 1d 29 7f |B...$.. 2&J...).|
1191 1191 0340: 7e f4 3d 07 bc 62 9a 5b ec 44 3d 72 1d 41 8b 5c |~.=..b.[.D=r.A.\|
1192 1192 0350: 80 de 0e 62 9a 2e f8 83 00 d5 07 a0 9c c6 74 98 |...b..........t.|
1193 1193 0360: 11 b2 5e a9 38 02 03 ee fd 86 5c f4 86 b3 ae da |..^.8.....\.....|
1194 1194 0370: 05 94 01 c5 c6 ea 18 e6 ba 2a ba b3 04 5c 96 89 |.........*...\..|
1195 1195 0380: 72 63 5b 10 11 f6 67 34 98 cb e4 c0 4e fa e6 99 |rc[...g4....N...|
1196 1196 0390: 19 6e 50 e8 26 8d 0c 17 e0 be ef e1 8e 02 6f 32 |.nP.&.........o2|
1197 1197 03a0: 82 dc 26 f8 a1 08 f3 8a 0d f3 c4 75 00 48 73 b8 |..&........u.Hs.|
1198 1198 03b0: be 3b 0d 7f d0 fd c7 78 96 ec e0 03 80 68 4d 8d |.;.....x.....hM.|
1199 1199 03c0: 43 8c d7 68 58 f9 50 f0 18 cb 21 58 1b 60 cd 1f |C..hX.P...!X.`..|
1200 1200 03d0: 84 36 2e 16 1f 0a f7 4e 8f eb df 01 2d c2 79 0b |.6.....N....-.y.|
1201 1201 03e0: f7 24 ea 0d e8 59 86 51 6e 1c 30 a3 ad 2f ee 8c |.$...Y.Qn.0../..|
1202 1202 03f0: 90 c8 84 d5 e8 34 c1 95 b2 c9 f6 4d 87 1c 7d 19 |.....4.....M..}.|
1203 1203 0400: d6 41 58 56 7a e0 6c ba 10 c7 e8 33 39 36 96 e7 |.AXVz.l....396..|
1204 1204 0410: d2 f9 59 9a 08 95 48 38 e7 0b b7 0a 24 67 c4 39 |..Y...H8....$g.9|
1205 1205 0420: 8b 43 88 57 9c 01 f5 61 b5 e1 27 41 7e af 83 fe |.C.W...a..'A~...|
1206 1206 0430: 2e e4 8a 70 a1 21 46 96 30 7a |...p.!F.0z|
1207 1207 $ hg debugbundle ../rev.hg2.bz
1208 Stream params: {'Compression': 'BZ'}
1209 changegroup -- '{}'
1208 Stream params: sortdict([('Compression', 'BZ')])
1209 changegroup -- 'sortdict()'
1210 1210 32af7686d403cf45b5d95f2d70cebea587ac806a
1211 1211 9520eea781bcca16c1e15acc0ba14335a0e8e5ba
1212 1212 eea13746799a9e0bfd88f29d3c2e9dc9389f524f
1213 1213 02de42196ebee42ef284b6780a87cdc96e8eaab6
1214 1214 $ hg unbundle ../rev.hg2.bz
1215 1215 adding changesets
1216 1216 adding manifests
1217 1217 adding file changes
1218 1218 added 0 changesets with 0 changes to 3 files
1219 1219 (run 'hg update' to get a working copy)
1220 1220
1221 1221 unknown compression while unbundling
1222 1222 -----------------------------
1223 1223
1224 1224 $ hg bundle2 --param Compression=FooBarUnknown --rev '8+7+5+4' ../rev.hg2.bz
1225 1225 $ cat ../rev.hg2.bz | hg statbundle2
1226 1226 abort: unknown parameters: Stream Parameter - Compression='FooBarUnknown'
1227 1227 [255]
1228 1228 $ hg unbundle ../rev.hg2.bz
1229 1229 abort: ../rev.hg2.bz: unknown bundle feature, Stream Parameter - Compression='FooBarUnknown'
1230 1230 (see https://mercurial-scm.org/wiki/BundleFeature for more information)
1231 1231 [255]
1232 1232
1233 1233 $ cd ..
@@ -1,76 +1,76 b''
1 1
2 2 Create a test repository:
3 3
4 4 $ hg init repo
5 5 $ cd repo
6 6 $ touch a ; hg add a ; hg ci -ma
7 7 $ touch b ; hg add b ; hg ci -mb
8 8 $ touch c ; hg add c ; hg ci -mc
9 9 $ hg bundle --base 0 --rev tip bundle.hg -v --type v1
10 10 2 changesets found
11 11 uncompressed size of bundle content:
12 12 332 (changelog)
13 13 282 (manifests)
14 14 93 b
15 15 93 c
16 16 $ hg bundle --base 0 --rev tip bundle2.hg -v --type none-v2
17 17 2 changesets found
18 18 uncompressed size of bundle content:
19 19 372 (changelog)
20 20 322 (manifests)
21 21 113 b
22 22 113 c
23 23
24 24 Terse output:
25 25
26 26 $ hg debugbundle bundle.hg
27 27 0e067c57feba1a5694ca4844f05588bb1bf82342
28 28 991a3460af53952d10ec8a295d3d2cc2e5fa9690
29 29
30 30 Terse output:
31 31
32 32 $ hg debugbundle bundle2.hg
33 33 Stream params: {}
34 changegroup -- "{'version': '02'}"
34 changegroup -- "sortdict([('version', '02')])"
35 35 0e067c57feba1a5694ca4844f05588bb1bf82342
36 36 991a3460af53952d10ec8a295d3d2cc2e5fa9690
37 37
38 38 Verbose output:
39 39
40 40 $ hg debugbundle --all bundle.hg
41 41 format: id, p1, p2, cset, delta base, len(delta)
42 42
43 43 changelog
44 44 0e067c57feba1a5694ca4844f05588bb1bf82342 3903775176ed42b1458a6281db4a0ccf4d9f287a 0000000000000000000000000000000000000000 0e067c57feba1a5694ca4844f05588bb1bf82342 3903775176ed42b1458a6281db4a0ccf4d9f287a 80
45 45 991a3460af53952d10ec8a295d3d2cc2e5fa9690 0e067c57feba1a5694ca4844f05588bb1bf82342 0000000000000000000000000000000000000000 991a3460af53952d10ec8a295d3d2cc2e5fa9690 0e067c57feba1a5694ca4844f05588bb1bf82342 80
46 46
47 47 manifest
48 48 686dbf0aeca417636fa26a9121c681eabbb15a20 8515d4bfda768e04af4c13a69a72e28c7effbea7 0000000000000000000000000000000000000000 0e067c57feba1a5694ca4844f05588bb1bf82342 8515d4bfda768e04af4c13a69a72e28c7effbea7 55
49 49 ae25a31b30b3490a981e7b96a3238cc69583fda1 686dbf0aeca417636fa26a9121c681eabbb15a20 0000000000000000000000000000000000000000 991a3460af53952d10ec8a295d3d2cc2e5fa9690 686dbf0aeca417636fa26a9121c681eabbb15a20 55
50 50
51 51 b
52 52 b80de5d138758541c5f05265ad144ab9fa86d1db 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 0e067c57feba1a5694ca4844f05588bb1bf82342 0000000000000000000000000000000000000000 0
53 53
54 54 c
55 55 b80de5d138758541c5f05265ad144ab9fa86d1db 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 991a3460af53952d10ec8a295d3d2cc2e5fa9690 0000000000000000000000000000000000000000 0
56 56
57 57 $ hg debugbundle --all bundle2.hg
58 58 Stream params: {}
59 changegroup -- "{'version': '02'}"
59 changegroup -- "sortdict([('version', '02')])"
60 60 format: id, p1, p2, cset, delta base, len(delta)
61 61
62 62 changelog
63 63 0e067c57feba1a5694ca4844f05588bb1bf82342 3903775176ed42b1458a6281db4a0ccf4d9f287a 0000000000000000000000000000000000000000 0e067c57feba1a5694ca4844f05588bb1bf82342 3903775176ed42b1458a6281db4a0ccf4d9f287a 80
64 64 991a3460af53952d10ec8a295d3d2cc2e5fa9690 0e067c57feba1a5694ca4844f05588bb1bf82342 0000000000000000000000000000000000000000 991a3460af53952d10ec8a295d3d2cc2e5fa9690 0e067c57feba1a5694ca4844f05588bb1bf82342 80
65 65
66 66 manifest
67 67 686dbf0aeca417636fa26a9121c681eabbb15a20 8515d4bfda768e04af4c13a69a72e28c7effbea7 0000000000000000000000000000000000000000 0e067c57feba1a5694ca4844f05588bb1bf82342 8515d4bfda768e04af4c13a69a72e28c7effbea7 55
68 68 ae25a31b30b3490a981e7b96a3238cc69583fda1 686dbf0aeca417636fa26a9121c681eabbb15a20 0000000000000000000000000000000000000000 991a3460af53952d10ec8a295d3d2cc2e5fa9690 686dbf0aeca417636fa26a9121c681eabbb15a20 55
69 69
70 70 b
71 71 b80de5d138758541c5f05265ad144ab9fa86d1db 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 0e067c57feba1a5694ca4844f05588bb1bf82342 0000000000000000000000000000000000000000 0
72 72
73 73 c
74 74 b80de5d138758541c5f05265ad144ab9fa86d1db 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 991a3460af53952d10ec8a295d3d2cc2e5fa9690 0000000000000000000000000000000000000000 0
75 75
76 76 $ cd ..
@@ -1,161 +1,161 b''
1 1 Check whether size of generaldelta revlog is not bigger than its
2 2 regular equivalent. Test would fail if generaldelta was naive
3 3 implementation of parentdelta: third manifest revision would be fully
4 4 inserted due to big distance from its paren revision (zero).
5 5
6 6 $ hg init repo --config format.generaldelta=no --config format.usegeneraldelta=no
7 7 $ cd repo
8 8 $ echo foo > foo
9 9 $ echo bar > bar
10 10 $ echo baz > baz
11 11 $ hg commit -q -Am boo
12 12 $ hg clone --pull . ../gdrepo -q --config format.generaldelta=yes
13 13 $ for r in 1 2 3; do
14 14 > echo $r > foo
15 15 > hg commit -q -m $r
16 16 > hg up -q -r 0
17 17 > hg pull . -q -r $r -R ../gdrepo
18 18 > done
19 19
20 20 $ cd ..
21 21 >>> from __future__ import print_function
22 22 >>> import os
23 23 >>> regsize = os.stat("repo/.hg/store/00manifest.i").st_size
24 24 >>> gdsize = os.stat("gdrepo/.hg/store/00manifest.i").st_size
25 25 >>> if regsize < gdsize:
26 26 ... print('generaldata increased size of manifest')
27 27
28 28 Verify rev reordering doesnt create invalid bundles (issue4462)
29 29 This requires a commit tree that when pulled will reorder manifest revs such
30 30 that the second manifest to create a file rev will be ordered before the first
31 31 manifest to create that file rev. We also need to do a partial pull to ensure
32 32 reordering happens. At the end we verify the linkrev points at the earliest
33 33 commit.
34 34
35 35 $ hg init server --config format.generaldelta=True
36 36 $ cd server
37 37 $ touch a
38 38 $ hg commit -Aqm a
39 39 $ echo x > x
40 40 $ echo y > y
41 41 $ hg commit -Aqm xy
42 42 $ hg up -q '.^'
43 43 $ echo x > x
44 44 $ echo z > z
45 45 $ hg commit -Aqm xz
46 46 $ hg up -q 1
47 47 $ echo b > b
48 48 $ hg commit -Aqm b
49 49 $ hg merge -q 2
50 50 $ hg commit -Aqm merge
51 51 $ echo c > c
52 52 $ hg commit -Aqm c
53 53 $ hg log -G -T '{rev} {shortest(node)} {desc}'
54 54 @ 5 ebb8 c
55 55 |
56 56 o 4 baf7 merge
57 57 |\
58 58 | o 3 a129 b
59 59 | |
60 60 o | 2 958c xz
61 61 | |
62 62 | o 1 f00c xy
63 63 |/
64 64 o 0 3903 a
65 65
66 66 $ cd ..
67 67 $ hg init client --config format.generaldelta=false --config format.usegeneraldelta=false
68 68 $ cd client
69 69 $ hg pull -q ../server -r 4
70 70 $ hg debugindex x
71 71 rev offset length base linkrev nodeid p1 p2
72 72 0 0 3 0 1 1406e7411862 000000000000 000000000000
73 73
74 74 $ cd ..
75 75
76 76 Test "usegeneraldelta" config
77 77 (repo are general delta, but incoming bundle are not re-deltified)
78 78
79 79 delta coming from the server base delta server are not recompressed.
80 80 (also include the aggressive version for comparison)
81 81
82 82 $ hg clone repo --pull --config format.usegeneraldelta=1 usegd
83 83 requesting all changes
84 84 adding changesets
85 85 adding manifests
86 86 adding file changes
87 87 added 4 changesets with 6 changes to 3 files (+2 heads)
88 88 updating to branch default
89 89 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
90 90 $ hg clone repo --pull --config format.generaldelta=1 full
91 91 requesting all changes
92 92 adding changesets
93 93 adding manifests
94 94 adding file changes
95 95 added 4 changesets with 6 changes to 3 files (+2 heads)
96 96 updating to branch default
97 97 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
98 98 $ hg -R repo debugindex -m
99 99 rev offset length base linkrev nodeid p1 p2
100 100 0 0 104 0 0 cef96823c800 000000000000 000000000000
101 101 1 104 57 0 1 58ab9a8d541d cef96823c800 000000000000
102 102 2 161 57 0 2 134fdc6fd680 cef96823c800 000000000000
103 103 3 218 104 3 3 723508934dad cef96823c800 000000000000
104 104 $ hg -R usegd debugindex -m
105 105 rev offset length delta linkrev nodeid p1 p2
106 106 0 0 104 -1 0 cef96823c800 000000000000 000000000000
107 107 1 104 57 0 1 58ab9a8d541d cef96823c800 000000000000
108 108 2 161 57 1 2 134fdc6fd680 cef96823c800 000000000000
109 109 3 218 57 0 3 723508934dad cef96823c800 000000000000
110 110 $ hg -R full debugindex -m
111 111 rev offset length delta linkrev nodeid p1 p2
112 112 0 0 104 -1 0 cef96823c800 000000000000 000000000000
113 113 1 104 57 0 1 58ab9a8d541d cef96823c800 000000000000
114 114 2 161 57 0 2 134fdc6fd680 cef96823c800 000000000000
115 115 3 218 57 0 3 723508934dad cef96823c800 000000000000
116 116
117 117 Test format.aggressivemergedeltas
118 118
119 119 $ hg init --config format.generaldelta=1 aggressive
120 120 $ cd aggressive
121 121 $ cat << EOF >> .hg/hgrc
122 122 > [format]
123 123 > generaldelta = 1
124 124 > EOF
125 125 $ touch a b c d e
126 126 $ hg commit -Aqm side1
127 127 $ hg up -q null
128 128 $ touch x y
129 129 $ hg commit -Aqm side2
130 130
131 131 - Verify non-aggressive merge uses p1 (commit 1) as delta parent
132 132 $ hg merge -q 0
133 133 $ hg commit -q -m merge
134 134 $ hg debugindex -m
135 135 rev offset length delta linkrev nodeid p1 p2
136 136 0 0 59 -1 0 8dde941edb6e 000000000000 000000000000
137 137 1 59 61 0 1 315c023f341d 000000000000 000000000000
138 138 2 120 65 1 2 2ab389a983eb 315c023f341d 8dde941edb6e
139 139
140 140 $ hg strip -q -r . --config extensions.strip=
141 141
142 142 - Verify aggressive merge uses p2 (commit 0) as delta parent
143 143 $ hg up -q -C 1
144 144 $ hg merge -q 0
145 145 $ hg commit -q -m merge --config format.aggressivemergedeltas=True
146 146 $ hg debugindex -m
147 147 rev offset length delta linkrev nodeid p1 p2
148 148 0 0 59 -1 0 8dde941edb6e 000000000000 000000000000
149 149 1 59 61 0 1 315c023f341d 000000000000 000000000000
150 150 2 120 62 0 2 2ab389a983eb 315c023f341d 8dde941edb6e
151 151
152 152 Test that strip bundle use bundle2
153 153 $ hg --config extensions.strip= strip .
154 154 0 files updated, 0 files merged, 5 files removed, 0 files unresolved
155 155 saved backup bundle to $TESTTMP/aggressive/.hg/strip-backup/1c5d4dc9a8b8-6c68e60c-backup.hg (glob)
156 156 $ hg debugbundle .hg/strip-backup/*
157 Stream params: {'Compression': 'BZ'}
158 changegroup -- "{'version': '02'}"
157 Stream params: sortdict([('Compression', 'BZ')])
158 changegroup -- "sortdict([('version', '02')])"
159 159 1c5d4dc9a8b8d6e1750966d343e94db665e7a1e9
160 160
161 161 $ cd ..
@@ -1,272 +1,272 b''
1 1 #require serve
2 2
3 3 = Test the getbundle() protocol function =
4 4
5 5 Create a test repository:
6 6
7 7 $ hg init repo
8 8 $ cd repo
9 9 $ hg debugbuilddag -n -m '+2 :fork +5 :p1 *fork +6 :p2 /p1 :m1 +3' > /dev/null
10 10 $ hg log -G --template '{node}\n'
11 11 o 10c14a2cc935e1d8c31f9e98587dcf27fb08a6da
12 12 |
13 13 o 4801a72e5d88cb515b0c7e40fae34180f3f837f2
14 14 |
15 15 o 0b2f73f04880d9cb6a5cd8a757f0db0ad01e32c3
16 16 |
17 17 o 8365676dbab05860ce0d9110f2af51368b961bbd
18 18 |\
19 19 | o 5686dbbd9fc46cb806599c878d02fe1cb56b83d3
20 20 | |
21 21 | o 13c0170174366b441dc68e8e33757232fa744458
22 22 | |
23 23 | o 63476832d8ec6558cf9bbe3cbe0c757e5cf18043
24 24 | |
25 25 | o 700b7e19db54103633c4bf4a6a6b6d55f4d50c03
26 26 | |
27 27 | o 928b5f94cdb278bb536eba552de348a4e92ef24d
28 28 | |
29 29 | o f34414c64173e0ecb61b25dc55e116dbbcc89bee
30 30 | |
31 31 | o 8931463777131cd73923e560b760061f2aa8a4bc
32 32 | |
33 33 o | 6621d79f61b23ec74cf4b69464343d9e0980ec8b
34 34 | |
35 35 o | bac16991d12ff45f9dc43c52da1946dfadb83e80
36 36 | |
37 37 o | ff42371d57168345fdf1a3aac66a51f6a45d41d2
38 38 | |
39 39 o | d5f6e1ea452285324836a49d7d3c2a63cfed1d31
40 40 | |
41 41 o | 713346a995c363120712aed1aee7e04afd867638
42 42 |/
43 43 o 29a4d1f17bd3f0779ca0525bebb1cfb51067c738
44 44 |
45 45 o 7704483d56b2a7b5db54dcee7c62378ac629b348
46 46
47 47 $ cd ..
48 48
49 49
50 50 = Test locally =
51 51
52 52 Get everything:
53 53
54 54 $ hg debuggetbundle repo bundle
55 55 $ hg debugbundle bundle
56 56 7704483d56b2a7b5db54dcee7c62378ac629b348
57 57 29a4d1f17bd3f0779ca0525bebb1cfb51067c738
58 58 713346a995c363120712aed1aee7e04afd867638
59 59 d5f6e1ea452285324836a49d7d3c2a63cfed1d31
60 60 ff42371d57168345fdf1a3aac66a51f6a45d41d2
61 61 bac16991d12ff45f9dc43c52da1946dfadb83e80
62 62 6621d79f61b23ec74cf4b69464343d9e0980ec8b
63 63 8931463777131cd73923e560b760061f2aa8a4bc
64 64 f34414c64173e0ecb61b25dc55e116dbbcc89bee
65 65 928b5f94cdb278bb536eba552de348a4e92ef24d
66 66 700b7e19db54103633c4bf4a6a6b6d55f4d50c03
67 67 63476832d8ec6558cf9bbe3cbe0c757e5cf18043
68 68 13c0170174366b441dc68e8e33757232fa744458
69 69 5686dbbd9fc46cb806599c878d02fe1cb56b83d3
70 70 8365676dbab05860ce0d9110f2af51368b961bbd
71 71 0b2f73f04880d9cb6a5cd8a757f0db0ad01e32c3
72 72 4801a72e5d88cb515b0c7e40fae34180f3f837f2
73 73 10c14a2cc935e1d8c31f9e98587dcf27fb08a6da
74 74
75 75 Get part of linear run:
76 76
77 77 $ hg debuggetbundle repo bundle -H 4801a72e5d88cb515b0c7e40fae34180f3f837f2 -C 8365676dbab05860ce0d9110f2af51368b961bbd
78 78 $ hg debugbundle bundle
79 79 0b2f73f04880d9cb6a5cd8a757f0db0ad01e32c3
80 80 4801a72e5d88cb515b0c7e40fae34180f3f837f2
81 81
82 82 Get missing branch and merge:
83 83
84 84 $ hg debuggetbundle repo bundle -H 4801a72e5d88cb515b0c7e40fae34180f3f837f2 -C 13c0170174366b441dc68e8e33757232fa744458
85 85 $ hg debugbundle bundle
86 86 713346a995c363120712aed1aee7e04afd867638
87 87 d5f6e1ea452285324836a49d7d3c2a63cfed1d31
88 88 ff42371d57168345fdf1a3aac66a51f6a45d41d2
89 89 bac16991d12ff45f9dc43c52da1946dfadb83e80
90 90 6621d79f61b23ec74cf4b69464343d9e0980ec8b
91 91 5686dbbd9fc46cb806599c878d02fe1cb56b83d3
92 92 8365676dbab05860ce0d9110f2af51368b961bbd
93 93 0b2f73f04880d9cb6a5cd8a757f0db0ad01e32c3
94 94 4801a72e5d88cb515b0c7e40fae34180f3f837f2
95 95
96 96 Get from only one head:
97 97
98 98 $ hg debuggetbundle repo bundle -H 928b5f94cdb278bb536eba552de348a4e92ef24d -C 29a4d1f17bd3f0779ca0525bebb1cfb51067c738
99 99 $ hg debugbundle bundle
100 100 8931463777131cd73923e560b760061f2aa8a4bc
101 101 f34414c64173e0ecb61b25dc55e116dbbcc89bee
102 102 928b5f94cdb278bb536eba552de348a4e92ef24d
103 103
104 104 Get parts of two branches:
105 105
106 106 $ hg debuggetbundle repo bundle -H 13c0170174366b441dc68e8e33757232fa744458 -C 700b7e19db54103633c4bf4a6a6b6d55f4d50c03 -H bac16991d12ff45f9dc43c52da1946dfadb83e80 -C d5f6e1ea452285324836a49d7d3c2a63cfed1d31
107 107 $ hg debugbundle bundle
108 108 ff42371d57168345fdf1a3aac66a51f6a45d41d2
109 109 bac16991d12ff45f9dc43c52da1946dfadb83e80
110 110 63476832d8ec6558cf9bbe3cbe0c757e5cf18043
111 111 13c0170174366b441dc68e8e33757232fa744458
112 112
113 113 Check that we get all needed file changes:
114 114
115 115 $ hg debugbundle bundle --all
116 116 format: id, p1, p2, cset, delta base, len(delta)
117 117
118 118 changelog
119 119 ff42371d57168345fdf1a3aac66a51f6a45d41d2 d5f6e1ea452285324836a49d7d3c2a63cfed1d31 0000000000000000000000000000000000000000 ff42371d57168345fdf1a3aac66a51f6a45d41d2 d5f6e1ea452285324836a49d7d3c2a63cfed1d31 99
120 120 bac16991d12ff45f9dc43c52da1946dfadb83e80 ff42371d57168345fdf1a3aac66a51f6a45d41d2 0000000000000000000000000000000000000000 bac16991d12ff45f9dc43c52da1946dfadb83e80 ff42371d57168345fdf1a3aac66a51f6a45d41d2 99
121 121 63476832d8ec6558cf9bbe3cbe0c757e5cf18043 700b7e19db54103633c4bf4a6a6b6d55f4d50c03 0000000000000000000000000000000000000000 63476832d8ec6558cf9bbe3cbe0c757e5cf18043 bac16991d12ff45f9dc43c52da1946dfadb83e80 102
122 122 13c0170174366b441dc68e8e33757232fa744458 63476832d8ec6558cf9bbe3cbe0c757e5cf18043 0000000000000000000000000000000000000000 13c0170174366b441dc68e8e33757232fa744458 63476832d8ec6558cf9bbe3cbe0c757e5cf18043 102
123 123
124 124 manifest
125 125 dac7984588fc4eea7acbf39693a9c1b06f5b175d 591f732a3faf1fb903815273f3c199a514a61ccb 0000000000000000000000000000000000000000 ff42371d57168345fdf1a3aac66a51f6a45d41d2 591f732a3faf1fb903815273f3c199a514a61ccb 113
126 126 0772616e6b48a76afb6c1458e193cbb3dae2e4ff dac7984588fc4eea7acbf39693a9c1b06f5b175d 0000000000000000000000000000000000000000 bac16991d12ff45f9dc43c52da1946dfadb83e80 dac7984588fc4eea7acbf39693a9c1b06f5b175d 113
127 127 eb498cd9af6c44108e43041e951ce829e29f6c80 bff2f4817ced57b386caf7c4e3e36a4bc9af7e93 0000000000000000000000000000000000000000 63476832d8ec6558cf9bbe3cbe0c757e5cf18043 0772616e6b48a76afb6c1458e193cbb3dae2e4ff 295
128 128 b15709c071ddd2d93188508ba156196ab4f19620 eb498cd9af6c44108e43041e951ce829e29f6c80 0000000000000000000000000000000000000000 13c0170174366b441dc68e8e33757232fa744458 eb498cd9af6c44108e43041e951ce829e29f6c80 114
129 129
130 130 mf
131 131 4f73f97080266ab8e0c0561ca8d0da3eaf65b695 301ca08d026bb72cb4258a9d211bdf7ca0bcd810 0000000000000000000000000000000000000000 ff42371d57168345fdf1a3aac66a51f6a45d41d2 301ca08d026bb72cb4258a9d211bdf7ca0bcd810 17
132 132 c7b583de053293870e145f45bd2d61643563fd06 4f73f97080266ab8e0c0561ca8d0da3eaf65b695 0000000000000000000000000000000000000000 bac16991d12ff45f9dc43c52da1946dfadb83e80 4f73f97080266ab8e0c0561ca8d0da3eaf65b695 18
133 133 266ee3c0302a5a18f1cf96817ac79a51836179e9 edc0f6b8db80d68ae6aff2b19f7e5347ab68fa63 0000000000000000000000000000000000000000 63476832d8ec6558cf9bbe3cbe0c757e5cf18043 c7b583de053293870e145f45bd2d61643563fd06 149
134 134 698c6a36220548cd3903ca7dada27c59aa500c52 266ee3c0302a5a18f1cf96817ac79a51836179e9 0000000000000000000000000000000000000000 13c0170174366b441dc68e8e33757232fa744458 266ee3c0302a5a18f1cf96817ac79a51836179e9 19
135 135
136 136 nf11
137 137 33fbc651630ffa7ccbebfe4eb91320a873e7291c 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 63476832d8ec6558cf9bbe3cbe0c757e5cf18043 0000000000000000000000000000000000000000 16
138 138
139 139 nf12
140 140 ddce0544363f037e9fb889faca058f52dc01c0a5 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 13c0170174366b441dc68e8e33757232fa744458 0000000000000000000000000000000000000000 16
141 141
142 142 nf4
143 143 3c1407305701051cbed9f9cb9a68bdfb5997c235 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 ff42371d57168345fdf1a3aac66a51f6a45d41d2 0000000000000000000000000000000000000000 15
144 144
145 145 nf5
146 146 0dbd89c185f53a1727c54cd1ce256482fa23968e 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 bac16991d12ff45f9dc43c52da1946dfadb83e80 0000000000000000000000000000000000000000 15
147 147
148 148 Get branch and merge:
149 149
150 150 $ hg debuggetbundle repo bundle -C 7704483d56b2a7b5db54dcee7c62378ac629b348 -H 0b2f73f04880d9cb6a5cd8a757f0db0ad01e32c3
151 151 $ hg debugbundle bundle
152 152 29a4d1f17bd3f0779ca0525bebb1cfb51067c738
153 153 713346a995c363120712aed1aee7e04afd867638
154 154 d5f6e1ea452285324836a49d7d3c2a63cfed1d31
155 155 ff42371d57168345fdf1a3aac66a51f6a45d41d2
156 156 bac16991d12ff45f9dc43c52da1946dfadb83e80
157 157 6621d79f61b23ec74cf4b69464343d9e0980ec8b
158 158 8931463777131cd73923e560b760061f2aa8a4bc
159 159 f34414c64173e0ecb61b25dc55e116dbbcc89bee
160 160 928b5f94cdb278bb536eba552de348a4e92ef24d
161 161 700b7e19db54103633c4bf4a6a6b6d55f4d50c03
162 162 63476832d8ec6558cf9bbe3cbe0c757e5cf18043
163 163 13c0170174366b441dc68e8e33757232fa744458
164 164 5686dbbd9fc46cb806599c878d02fe1cb56b83d3
165 165 8365676dbab05860ce0d9110f2af51368b961bbd
166 166 0b2f73f04880d9cb6a5cd8a757f0db0ad01e32c3
167 167
168 168 = Test bundle2 =
169 169
170 170 $ hg debuggetbundle repo bundle -t bundle2
171 171 $ hg debugbundle bundle
172 172 Stream params: {}
173 changegroup -- "{'version': '01'}"
173 changegroup -- "sortdict([('version', '01')])"
174 174 7704483d56b2a7b5db54dcee7c62378ac629b348
175 175 29a4d1f17bd3f0779ca0525bebb1cfb51067c738
176 176 713346a995c363120712aed1aee7e04afd867638
177 177 d5f6e1ea452285324836a49d7d3c2a63cfed1d31
178 178 ff42371d57168345fdf1a3aac66a51f6a45d41d2
179 179 bac16991d12ff45f9dc43c52da1946dfadb83e80
180 180 6621d79f61b23ec74cf4b69464343d9e0980ec8b
181 181 8931463777131cd73923e560b760061f2aa8a4bc
182 182 f34414c64173e0ecb61b25dc55e116dbbcc89bee
183 183 928b5f94cdb278bb536eba552de348a4e92ef24d
184 184 700b7e19db54103633c4bf4a6a6b6d55f4d50c03
185 185 63476832d8ec6558cf9bbe3cbe0c757e5cf18043
186 186 13c0170174366b441dc68e8e33757232fa744458
187 187 5686dbbd9fc46cb806599c878d02fe1cb56b83d3
188 188 8365676dbab05860ce0d9110f2af51368b961bbd
189 189 0b2f73f04880d9cb6a5cd8a757f0db0ad01e32c3
190 190 4801a72e5d88cb515b0c7e40fae34180f3f837f2
191 191 10c14a2cc935e1d8c31f9e98587dcf27fb08a6da
192 192 = Test via HTTP =
193 193
194 194 Get everything:
195 195
196 196 $ hg serve -R repo -p $HGPORT -d --pid-file=hg.pid -E error.log -A access.log
197 197 $ cat hg.pid >> $DAEMON_PIDS
198 198 $ hg debuggetbundle http://localhost:$HGPORT/ bundle
199 199 $ hg debugbundle bundle
200 200 7704483d56b2a7b5db54dcee7c62378ac629b348
201 201 29a4d1f17bd3f0779ca0525bebb1cfb51067c738
202 202 713346a995c363120712aed1aee7e04afd867638
203 203 d5f6e1ea452285324836a49d7d3c2a63cfed1d31
204 204 ff42371d57168345fdf1a3aac66a51f6a45d41d2
205 205 bac16991d12ff45f9dc43c52da1946dfadb83e80
206 206 6621d79f61b23ec74cf4b69464343d9e0980ec8b
207 207 8931463777131cd73923e560b760061f2aa8a4bc
208 208 f34414c64173e0ecb61b25dc55e116dbbcc89bee
209 209 928b5f94cdb278bb536eba552de348a4e92ef24d
210 210 700b7e19db54103633c4bf4a6a6b6d55f4d50c03
211 211 63476832d8ec6558cf9bbe3cbe0c757e5cf18043
212 212 13c0170174366b441dc68e8e33757232fa744458
213 213 5686dbbd9fc46cb806599c878d02fe1cb56b83d3
214 214 8365676dbab05860ce0d9110f2af51368b961bbd
215 215 0b2f73f04880d9cb6a5cd8a757f0db0ad01e32c3
216 216 4801a72e5d88cb515b0c7e40fae34180f3f837f2
217 217 10c14a2cc935e1d8c31f9e98587dcf27fb08a6da
218 218
219 219 Get parts of two branches:
220 220
221 221 $ hg debuggetbundle http://localhost:$HGPORT/ bundle -H 13c0170174366b441dc68e8e33757232fa744458 -C 700b7e19db54103633c4bf4a6a6b6d55f4d50c03 -H bac16991d12ff45f9dc43c52da1946dfadb83e80 -C d5f6e1ea452285324836a49d7d3c2a63cfed1d31
222 222 $ hg debugbundle bundle
223 223 ff42371d57168345fdf1a3aac66a51f6a45d41d2
224 224 bac16991d12ff45f9dc43c52da1946dfadb83e80
225 225 63476832d8ec6558cf9bbe3cbe0c757e5cf18043
226 226 13c0170174366b441dc68e8e33757232fa744458
227 227
228 228 Check that we get all needed file changes:
229 229
230 230 $ hg debugbundle bundle --all
231 231 format: id, p1, p2, cset, delta base, len(delta)
232 232
233 233 changelog
234 234 ff42371d57168345fdf1a3aac66a51f6a45d41d2 d5f6e1ea452285324836a49d7d3c2a63cfed1d31 0000000000000000000000000000000000000000 ff42371d57168345fdf1a3aac66a51f6a45d41d2 d5f6e1ea452285324836a49d7d3c2a63cfed1d31 99
235 235 bac16991d12ff45f9dc43c52da1946dfadb83e80 ff42371d57168345fdf1a3aac66a51f6a45d41d2 0000000000000000000000000000000000000000 bac16991d12ff45f9dc43c52da1946dfadb83e80 ff42371d57168345fdf1a3aac66a51f6a45d41d2 99
236 236 63476832d8ec6558cf9bbe3cbe0c757e5cf18043 700b7e19db54103633c4bf4a6a6b6d55f4d50c03 0000000000000000000000000000000000000000 63476832d8ec6558cf9bbe3cbe0c757e5cf18043 bac16991d12ff45f9dc43c52da1946dfadb83e80 102
237 237 13c0170174366b441dc68e8e33757232fa744458 63476832d8ec6558cf9bbe3cbe0c757e5cf18043 0000000000000000000000000000000000000000 13c0170174366b441dc68e8e33757232fa744458 63476832d8ec6558cf9bbe3cbe0c757e5cf18043 102
238 238
239 239 manifest
240 240 dac7984588fc4eea7acbf39693a9c1b06f5b175d 591f732a3faf1fb903815273f3c199a514a61ccb 0000000000000000000000000000000000000000 ff42371d57168345fdf1a3aac66a51f6a45d41d2 591f732a3faf1fb903815273f3c199a514a61ccb 113
241 241 0772616e6b48a76afb6c1458e193cbb3dae2e4ff dac7984588fc4eea7acbf39693a9c1b06f5b175d 0000000000000000000000000000000000000000 bac16991d12ff45f9dc43c52da1946dfadb83e80 dac7984588fc4eea7acbf39693a9c1b06f5b175d 113
242 242 eb498cd9af6c44108e43041e951ce829e29f6c80 bff2f4817ced57b386caf7c4e3e36a4bc9af7e93 0000000000000000000000000000000000000000 63476832d8ec6558cf9bbe3cbe0c757e5cf18043 0772616e6b48a76afb6c1458e193cbb3dae2e4ff 295
243 243 b15709c071ddd2d93188508ba156196ab4f19620 eb498cd9af6c44108e43041e951ce829e29f6c80 0000000000000000000000000000000000000000 13c0170174366b441dc68e8e33757232fa744458 eb498cd9af6c44108e43041e951ce829e29f6c80 114
244 244
245 245 mf
246 246 4f73f97080266ab8e0c0561ca8d0da3eaf65b695 301ca08d026bb72cb4258a9d211bdf7ca0bcd810 0000000000000000000000000000000000000000 ff42371d57168345fdf1a3aac66a51f6a45d41d2 301ca08d026bb72cb4258a9d211bdf7ca0bcd810 17
247 247 c7b583de053293870e145f45bd2d61643563fd06 4f73f97080266ab8e0c0561ca8d0da3eaf65b695 0000000000000000000000000000000000000000 bac16991d12ff45f9dc43c52da1946dfadb83e80 4f73f97080266ab8e0c0561ca8d0da3eaf65b695 18
248 248 266ee3c0302a5a18f1cf96817ac79a51836179e9 edc0f6b8db80d68ae6aff2b19f7e5347ab68fa63 0000000000000000000000000000000000000000 63476832d8ec6558cf9bbe3cbe0c757e5cf18043 c7b583de053293870e145f45bd2d61643563fd06 149
249 249 698c6a36220548cd3903ca7dada27c59aa500c52 266ee3c0302a5a18f1cf96817ac79a51836179e9 0000000000000000000000000000000000000000 13c0170174366b441dc68e8e33757232fa744458 266ee3c0302a5a18f1cf96817ac79a51836179e9 19
250 250
251 251 nf11
252 252 33fbc651630ffa7ccbebfe4eb91320a873e7291c 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 63476832d8ec6558cf9bbe3cbe0c757e5cf18043 0000000000000000000000000000000000000000 16
253 253
254 254 nf12
255 255 ddce0544363f037e9fb889faca058f52dc01c0a5 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 13c0170174366b441dc68e8e33757232fa744458 0000000000000000000000000000000000000000 16
256 256
257 257 nf4
258 258 3c1407305701051cbed9f9cb9a68bdfb5997c235 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 ff42371d57168345fdf1a3aac66a51f6a45d41d2 0000000000000000000000000000000000000000 15
259 259
260 260 nf5
261 261 0dbd89c185f53a1727c54cd1ce256482fa23968e 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 bac16991d12ff45f9dc43c52da1946dfadb83e80 0000000000000000000000000000000000000000 15
262 262
263 263 Verify we hit the HTTP server:
264 264
265 265 $ cat access.log
266 266 * - - [*] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
267 267 * - - [*] "GET /?cmd=getbundle HTTP/1.1" 200 - (glob)
268 268 * - - [*] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
269 269 * - - [*] "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:common=700b7e19db54103633c4bf4a6a6b6d55f4d50c03+d5f6e1ea452285324836a49d7d3c2a63cfed1d31&heads=13c0170174366b441dc68e8e33757232fa744458+bac16991d12ff45f9dc43c52da1946dfadb83e80 (glob)
270 270
271 271 $ cat error.log
272 272
@@ -1,1624 +1,1624 b''
1 1 $ cat <<EOF >> $HGRCPATH
2 2 > [extensions]
3 3 > mq =
4 4 > shelve =
5 5 > [defaults]
6 6 > diff = --nodates --git
7 7 > qnew = --date '0 0'
8 8 > [shelve]
9 9 > maxbackups = 2
10 10 > EOF
11 11
12 12 $ hg init repo
13 13 $ cd repo
14 14 $ mkdir a b
15 15 $ echo a > a/a
16 16 $ echo b > b/b
17 17 $ echo c > c
18 18 $ echo d > d
19 19 $ echo x > x
20 20 $ hg addremove -q
21 21
22 22 shelve has a help message
23 23 $ hg shelve -h
24 24 hg shelve [OPTION]... [FILE]...
25 25
26 26 save and set aside changes from the working directory
27 27
28 28 Shelving takes files that "hg status" reports as not clean, saves the
29 29 modifications to a bundle (a shelved change), and reverts the files so
30 30 that their state in the working directory becomes clean.
31 31
32 32 To restore these changes to the working directory, using "hg unshelve";
33 33 this will work even if you switch to a different commit.
34 34
35 35 When no files are specified, "hg shelve" saves all not-clean files. If
36 36 specific files or directories are named, only changes to those files are
37 37 shelved.
38 38
39 39 In bare shelve(when no files are specified, without interactive, include
40 40 and exclude option), shelving remembers information if the working
41 41 directory was on newly created branch, in other words working directory
42 42 was on different branch than its first parent. In this situation
43 43 unshelving restores branch information to the working directory.
44 44
45 45 Each shelved change has a name that makes it easier to find later. The
46 46 name of a shelved change defaults to being based on the active bookmark,
47 47 or if there is no active bookmark, the current named branch. To specify a
48 48 different name, use "--name".
49 49
50 50 To see a list of existing shelved changes, use the "--list" option. For
51 51 each shelved change, this will print its name, age, and description; use "
52 52 --patch" or "--stat" for more details.
53 53
54 54 To delete specific shelved changes, use "--delete". To delete all shelved
55 55 changes, use "--cleanup".
56 56
57 57 (use "hg help -e shelve" to show help for the shelve extension)
58 58
59 59 options ([+] can be repeated):
60 60
61 61 -A --addremove mark new/missing files as added/removed before
62 62 shelving
63 63 -u --unknown store unknown files in the shelve
64 64 --cleanup delete all shelved changes
65 65 --date DATE shelve with the specified commit date
66 66 -d --delete delete the named shelved change(s)
67 67 -e --edit invoke editor on commit messages
68 68 -l --list list current shelves
69 69 -m --message TEXT use text as shelve message
70 70 -n --name NAME use the given name for the shelved commit
71 71 -p --patch show patch
72 72 -i --interactive interactive mode, only works while creating a shelve
73 73 --stat output diffstat-style summary of changes
74 74 -I --include PATTERN [+] include names matching the given patterns
75 75 -X --exclude PATTERN [+] exclude names matching the given patterns
76 76 --mq operate on patch repository
77 77
78 78 (some details hidden, use --verbose to show complete help)
79 79
80 80 shelving in an empty repo should be possible
81 81 (this tests also that editor is not invoked, if '--edit' is not
82 82 specified)
83 83
84 84 $ HGEDITOR=cat hg shelve
85 85 shelved as default
86 86 0 files updated, 0 files merged, 5 files removed, 0 files unresolved
87 87
88 88 $ hg unshelve
89 89 unshelving change 'default'
90 90
91 91 $ hg commit -q -m 'initial commit'
92 92
93 93 $ hg shelve
94 94 nothing changed
95 95 [1]
96 96
97 97 make sure shelve files were backed up
98 98
99 99 $ ls .hg/shelve-backup
100 100 default.hg
101 101 default.patch
102 102
103 103 create an mq patch - shelving should work fine with a patch applied
104 104
105 105 $ echo n > n
106 106 $ hg add n
107 107 $ hg commit n -m second
108 108 $ hg qnew second.patch
109 109
110 110 shelve a change that we will delete later
111 111
112 112 $ echo a >> a/a
113 113 $ hg shelve
114 114 shelved as default
115 115 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
116 116
117 117 set up some more complex changes to shelve
118 118
119 119 $ echo a >> a/a
120 120 $ hg mv b b.rename
121 121 moving b/b to b.rename/b (glob)
122 122 $ hg cp c c.copy
123 123 $ hg status -C
124 124 M a/a
125 125 A b.rename/b
126 126 b/b
127 127 A c.copy
128 128 c
129 129 R b/b
130 130
131 131 prevent some foot-shooting
132 132
133 133 $ hg shelve -n foo/bar
134 134 abort: shelved change names may not contain slashes
135 135 [255]
136 136 $ hg shelve -n .baz
137 137 abort: shelved change names may not start with '.'
138 138 [255]
139 139
140 140 the common case - no options or filenames
141 141
142 142 $ hg shelve
143 143 shelved as default-01
144 144 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
145 145 $ hg status -C
146 146
147 147 ensure that our shelved changes exist
148 148
149 149 $ hg shelve -l
150 150 default-01 (*)* changes to: [mq]: second.patch (glob)
151 151 default (*)* changes to: [mq]: second.patch (glob)
152 152
153 153 $ hg shelve -l -p default
154 154 default (*)* changes to: [mq]: second.patch (glob)
155 155
156 156 diff --git a/a/a b/a/a
157 157 --- a/a/a
158 158 +++ b/a/a
159 159 @@ -1,1 +1,2 @@
160 160 a
161 161 +a
162 162
163 163 $ hg shelve --list --addremove
164 164 abort: options '--list' and '--addremove' may not be used together
165 165 [255]
166 166
167 167 delete our older shelved change
168 168
169 169 $ hg shelve -d default
170 170 $ hg qfinish -a -q
171 171
172 172 ensure shelve backups aren't overwritten
173 173
174 174 $ ls .hg/shelve-backup/
175 175 default-1.hg
176 176 default-1.patch
177 177 default.hg
178 178 default.patch
179 179
180 180 local edits should not prevent a shelved change from applying
181 181
182 182 $ printf "z\na\n" > a/a
183 183 $ hg unshelve --keep
184 184 unshelving change 'default-01'
185 185 temporarily committing pending changes (restore with 'hg unshelve --abort')
186 186 rebasing shelved changes
187 187 rebasing 4:32c69314e062 "changes to: [mq]: second.patch" (tip)
188 188 merging a/a
189 189
190 190 $ hg revert --all -q
191 191 $ rm a/a.orig b.rename/b c.copy
192 192
193 193 apply it and make sure our state is as expected
194 194
195 195 (this also tests that same timestamp prevents backups from being
196 196 removed, even though there are more than 'maxbackups' backups)
197 197
198 198 $ f -t .hg/shelve-backup/default.hg
199 199 .hg/shelve-backup/default.hg: file
200 200 $ touch -t 200001010000 .hg/shelve-backup/default.hg
201 201 $ f -t .hg/shelve-backup/default-1.hg
202 202 .hg/shelve-backup/default-1.hg: file
203 203 $ touch -t 200001010000 .hg/shelve-backup/default-1.hg
204 204
205 205 $ hg unshelve
206 206 unshelving change 'default-01'
207 207 $ hg status -C
208 208 M a/a
209 209 A b.rename/b
210 210 b/b
211 211 A c.copy
212 212 c
213 213 R b/b
214 214 $ hg shelve -l
215 215
216 216 (both of default.hg and default-1.hg should be still kept, because it
217 217 is difficult to decide actual order of them from same timestamp)
218 218
219 219 $ ls .hg/shelve-backup/
220 220 default-01.hg
221 221 default-01.patch
222 222 default-1.hg
223 223 default-1.patch
224 224 default.hg
225 225 default.patch
226 226
227 227 $ hg unshelve
228 228 abort: no shelved changes to apply!
229 229 [255]
230 230 $ hg unshelve foo
231 231 abort: shelved change 'foo' not found
232 232 [255]
233 233
234 234 named shelves, specific filenames, and "commit messages" should all work
235 235 (this tests also that editor is invoked, if '--edit' is specified)
236 236
237 237 $ hg status -C
238 238 M a/a
239 239 A b.rename/b
240 240 b/b
241 241 A c.copy
242 242 c
243 243 R b/b
244 244 $ HGEDITOR=cat hg shelve -q -n wibble -m wat -e a
245 245 wat
246 246
247 247
248 248 HG: Enter commit message. Lines beginning with 'HG:' are removed.
249 249 HG: Leave message empty to abort commit.
250 250 HG: --
251 251 HG: user: shelve@localhost
252 252 HG: branch 'default'
253 253 HG: changed a/a
254 254
255 255 expect "a" to no longer be present, but status otherwise unchanged
256 256
257 257 $ hg status -C
258 258 A b.rename/b
259 259 b/b
260 260 A c.copy
261 261 c
262 262 R b/b
263 263 $ hg shelve -l --stat
264 264 wibble (*) wat (glob)
265 265 a/a | 1 +
266 266 1 files changed, 1 insertions(+), 0 deletions(-)
267 267
268 268 and now "a/a" should reappear
269 269
270 270 $ cd a
271 271 $ hg unshelve -q wibble
272 272 $ cd ..
273 273 $ hg status -C
274 274 M a/a
275 275 A b.rename/b
276 276 b/b
277 277 A c.copy
278 278 c
279 279 R b/b
280 280
281 281 ensure old shelve backups are being deleted automatically
282 282
283 283 $ ls .hg/shelve-backup/
284 284 default-01.hg
285 285 default-01.patch
286 286 wibble.hg
287 287 wibble.patch
288 288
289 289 cause unshelving to result in a merge with 'a' conflicting
290 290
291 291 $ hg shelve -q
292 292 $ echo c>>a/a
293 293 $ hg commit -m second
294 294 $ hg tip --template '{files}\n'
295 295 a/a
296 296
297 297 add an unrelated change that should be preserved
298 298
299 299 $ mkdir foo
300 300 $ echo foo > foo/foo
301 301 $ hg add foo/foo
302 302
303 303 force a conflicted merge to occur
304 304
305 305 $ hg unshelve
306 306 unshelving change 'default'
307 307 temporarily committing pending changes (restore with 'hg unshelve --abort')
308 308 rebasing shelved changes
309 309 rebasing 5:32c69314e062 "changes to: [mq]: second.patch" (tip)
310 310 merging a/a
311 311 warning: conflicts while merging a/a! (edit, then use 'hg resolve --mark')
312 312 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
313 313 [1]
314 314
315 315 ensure that we have a merge with unresolved conflicts
316 316
317 317 $ hg heads -q --template '{rev}\n'
318 318 5
319 319 4
320 320 $ hg parents -q --template '{rev}\n'
321 321 4
322 322 5
323 323 $ hg status
324 324 M a/a
325 325 M b.rename/b
326 326 M c.copy
327 327 R b/b
328 328 ? a/a.orig
329 329 $ hg diff
330 330 diff --git a/a/a b/a/a
331 331 --- a/a/a
332 332 +++ b/a/a
333 333 @@ -1,2 +1,6 @@
334 334 a
335 335 +<<<<<<< dest: * - shelve: pending changes temporary commit (glob)
336 336 c
337 337 +=======
338 338 +a
339 339 +>>>>>>> source: 32c69314e062 - shelve: changes to: [mq]: second.patch
340 340 diff --git a/b/b b/b.rename/b
341 341 rename from b/b
342 342 rename to b.rename/b
343 343 diff --git a/c b/c.copy
344 344 copy from c
345 345 copy to c.copy
346 346 $ hg resolve -l
347 347 U a/a
348 348
349 349 $ hg shelve
350 350 abort: unshelve already in progress
351 351 (use 'hg unshelve --continue' or 'hg unshelve --abort')
352 352 [255]
353 353
354 354 abort the unshelve and be happy
355 355
356 356 $ hg status
357 357 M a/a
358 358 M b.rename/b
359 359 M c.copy
360 360 R b/b
361 361 ? a/a.orig
362 362 $ hg unshelve -a
363 363 rebase aborted
364 364 unshelve of 'default' aborted
365 365 $ hg heads -q
366 366 3:2e69b451d1ea
367 367 $ hg parents
368 368 changeset: 3:2e69b451d1ea
369 369 tag: tip
370 370 user: test
371 371 date: Thu Jan 01 00:00:00 1970 +0000
372 372 summary: second
373 373
374 374 $ hg resolve -l
375 375 $ hg status
376 376 A foo/foo
377 377 ? a/a.orig
378 378
379 379 try to continue with no unshelve underway
380 380
381 381 $ hg unshelve -c
382 382 abort: no unshelve in progress
383 383 [255]
384 384 $ hg status
385 385 A foo/foo
386 386 ? a/a.orig
387 387
388 388 redo the unshelve to get a conflict
389 389
390 390 $ hg unshelve -q
391 391 warning: conflicts while merging a/a! (edit, then use 'hg resolve --mark')
392 392 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
393 393 [1]
394 394
395 395 attempt to continue
396 396
397 397 $ hg unshelve -c
398 398 abort: unresolved conflicts, can't continue
399 399 (see 'hg resolve', then 'hg unshelve --continue')
400 400 [255]
401 401
402 402 $ hg revert -r . a/a
403 403 $ hg resolve -m a/a
404 404 (no more unresolved files)
405 405 continue: hg unshelve --continue
406 406
407 407 $ hg commit -m 'commit while unshelve in progress'
408 408 abort: unshelve already in progress
409 409 (use 'hg unshelve --continue' or 'hg unshelve --abort')
410 410 [255]
411 411
412 412 $ hg graft --continue
413 413 abort: no graft in progress
414 414 (continue: hg unshelve --continue)
415 415 [255]
416 416 $ hg unshelve -c
417 417 rebasing 5:32c69314e062 "changes to: [mq]: second.patch" (tip)
418 418 unshelve of 'default' complete
419 419
420 420 ensure the repo is as we hope
421 421
422 422 $ hg parents
423 423 changeset: 3:2e69b451d1ea
424 424 tag: tip
425 425 user: test
426 426 date: Thu Jan 01 00:00:00 1970 +0000
427 427 summary: second
428 428
429 429 $ hg heads -q
430 430 3:2e69b451d1ea
431 431
432 432 $ hg status -C
433 433 A b.rename/b
434 434 b/b
435 435 A c.copy
436 436 c
437 437 A foo/foo
438 438 R b/b
439 439 ? a/a.orig
440 440
441 441 there should be no shelves left
442 442
443 443 $ hg shelve -l
444 444
445 445 #if execbit
446 446
447 447 ensure that metadata-only changes are shelved
448 448
449 449 $ chmod +x a/a
450 450 $ hg shelve -q -n execbit a/a
451 451 $ hg status a/a
452 452 $ hg unshelve -q execbit
453 453 $ hg status a/a
454 454 M a/a
455 455 $ hg revert a/a
456 456
457 457 #endif
458 458
459 459 #if symlink
460 460
461 461 $ rm a/a
462 462 $ ln -s foo a/a
463 463 $ hg shelve -q -n symlink a/a
464 464 $ hg status a/a
465 465 $ hg unshelve -q symlink
466 466 $ hg status a/a
467 467 M a/a
468 468 $ hg revert a/a
469 469
470 470 #endif
471 471
472 472 set up another conflict between a commit and a shelved change
473 473
474 474 $ hg revert -q -C -a
475 475 $ rm a/a.orig b.rename/b c.copy
476 476 $ echo a >> a/a
477 477 $ hg shelve -q
478 478 $ echo x >> a/a
479 479 $ hg ci -m 'create conflict'
480 480 $ hg add foo/foo
481 481
482 482 if we resolve a conflict while unshelving, the unshelve should succeed
483 483
484 484 $ hg unshelve --tool :merge-other --keep
485 485 unshelving change 'default'
486 486 temporarily committing pending changes (restore with 'hg unshelve --abort')
487 487 rebasing shelved changes
488 488 rebasing 6:2f694dd83a13 "changes to: second" (tip)
489 489 merging a/a
490 490 $ hg parents -q
491 491 4:33f7f61e6c5e
492 492 $ hg shelve -l
493 493 default (*)* changes to: second (glob)
494 494 $ hg status
495 495 M a/a
496 496 A foo/foo
497 497 $ cat a/a
498 498 a
499 499 c
500 500 a
501 501 $ cat > a/a << EOF
502 502 > a
503 503 > c
504 504 > x
505 505 > EOF
506 506
507 507 $ HGMERGE=true hg unshelve
508 508 unshelving change 'default'
509 509 temporarily committing pending changes (restore with 'hg unshelve --abort')
510 510 rebasing shelved changes
511 511 rebasing 6:2f694dd83a13 "changes to: second" (tip)
512 512 merging a/a
513 513 note: rebase of 6:2f694dd83a13 created no changes to commit
514 514 $ hg parents -q
515 515 4:33f7f61e6c5e
516 516 $ hg shelve -l
517 517 $ hg status
518 518 A foo/foo
519 519 $ cat a/a
520 520 a
521 521 c
522 522 x
523 523
524 524 test keep and cleanup
525 525
526 526 $ hg shelve
527 527 shelved as default
528 528 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
529 529 $ hg shelve --list
530 530 default (*)* changes to: create conflict (glob)
531 531 $ hg unshelve -k
532 532 unshelving change 'default'
533 533 $ hg shelve --list
534 534 default (*)* changes to: create conflict (glob)
535 535 $ hg shelve --cleanup
536 536 $ hg shelve --list
537 537
538 538 $ hg shelve --cleanup --delete
539 539 abort: options '--cleanup' and '--delete' may not be used together
540 540 [255]
541 541 $ hg shelve --cleanup --patch
542 542 abort: options '--cleanup' and '--patch' may not be used together
543 543 [255]
544 544 $ hg shelve --cleanup --message MESSAGE
545 545 abort: options '--cleanup' and '--message' may not be used together
546 546 [255]
547 547
548 548 test bookmarks
549 549
550 550 $ hg bookmark test
551 551 $ hg bookmark
552 552 * test 4:33f7f61e6c5e
553 553 $ hg shelve
554 554 shelved as test
555 555 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
556 556 $ hg bookmark
557 557 * test 4:33f7f61e6c5e
558 558 $ hg unshelve
559 559 unshelving change 'test'
560 560 $ hg bookmark
561 561 * test 4:33f7f61e6c5e
562 562
563 563 shelve should still work even if mq is disabled
564 564
565 565 $ hg --config extensions.mq=! shelve
566 566 shelved as test
567 567 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
568 568 $ hg --config extensions.mq=! shelve --list
569 569 test (*)* changes to: create conflict (glob)
570 570 $ hg bookmark
571 571 * test 4:33f7f61e6c5e
572 572 $ hg --config extensions.mq=! unshelve
573 573 unshelving change 'test'
574 574 $ hg bookmark
575 575 * test 4:33f7f61e6c5e
576 576
577 577 shelve should leave dirstate clean (issue4055)
578 578
579 579 $ cd ..
580 580 $ hg init shelverebase
581 581 $ cd shelverebase
582 582 $ printf 'x\ny\n' > x
583 583 $ echo z > z
584 584 $ hg commit -Aqm xy
585 585 $ echo z >> x
586 586 $ hg commit -Aqm z
587 587 $ hg up 0
588 588 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
589 589 $ printf 'a\nx\ny\nz\n' > x
590 590 $ hg commit -Aqm xyz
591 591 $ echo c >> z
592 592 $ hg shelve
593 593 shelved as default
594 594 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
595 595 $ hg rebase -d 1 --config extensions.rebase=
596 596 rebasing 2:323bfa07f744 "xyz" (tip)
597 597 merging x
598 598 saved backup bundle to $TESTTMP/shelverebase/.hg/strip-backup/323bfa07f744-78114325-backup.hg (glob)
599 599 $ hg unshelve
600 600 unshelving change 'default'
601 601 rebasing shelved changes
602 602 rebasing 4:82a0d7d6ba61 "changes to: xyz" (tip)
603 603 $ hg status
604 604 M z
605 605
606 606 $ cd ..
607 607
608 608 shelve should only unshelve pending changes (issue4068)
609 609
610 610 $ hg init onlypendingchanges
611 611 $ cd onlypendingchanges
612 612 $ touch a
613 613 $ hg ci -Aqm a
614 614 $ touch b
615 615 $ hg ci -Aqm b
616 616 $ hg up -q 0
617 617 $ touch c
618 618 $ hg ci -Aqm c
619 619
620 620 $ touch d
621 621 $ hg add d
622 622 $ hg shelve
623 623 shelved as default
624 624 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
625 625 $ hg up -q 1
626 626 $ hg unshelve
627 627 unshelving change 'default'
628 628 rebasing shelved changes
629 629 rebasing 3:958bcbd1776e "changes to: c" (tip)
630 630 $ hg status
631 631 A d
632 632
633 633 unshelve should work on an ancestor of the original commit
634 634
635 635 $ hg shelve
636 636 shelved as default
637 637 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
638 638 $ hg up 0
639 639 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
640 640 $ hg unshelve
641 641 unshelving change 'default'
642 642 rebasing shelved changes
643 643 rebasing 3:013284d9655e "changes to: b" (tip)
644 644 $ hg status
645 645 A d
646 646
647 647 test bug 4073 we need to enable obsolete markers for it
648 648
649 649 $ cat >> $HGRCPATH << EOF
650 650 > [experimental]
651 651 > evolution=createmarkers
652 652 > EOF
653 653 $ hg shelve
654 654 shelved as default
655 655 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
656 656 $ hg debugobsolete `hg --debug id -i -r 1`
657 657 $ hg unshelve
658 658 unshelving change 'default'
659 659
660 660 unshelve should leave unknown files alone (issue4113)
661 661
662 662 $ echo e > e
663 663 $ hg shelve
664 664 shelved as default
665 665 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
666 666 $ hg status
667 667 ? e
668 668 $ hg unshelve
669 669 unshelving change 'default'
670 670 $ hg status
671 671 A d
672 672 ? e
673 673 $ cat e
674 674 e
675 675
676 676 unshelve should keep a copy of unknown files
677 677
678 678 $ hg add e
679 679 $ hg shelve
680 680 shelved as default
681 681 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
682 682 $ echo z > e
683 683 $ hg unshelve
684 684 unshelving change 'default'
685 685 $ cat e
686 686 e
687 687 $ cat e.orig
688 688 z
689 689
690 690
691 691 unshelve and conflicts with tracked and untracked files
692 692
693 693 preparing:
694 694
695 695 $ rm *.orig
696 696 $ hg ci -qm 'commit stuff'
697 697 $ hg phase -p null:
698 698
699 699 no other changes - no merge:
700 700
701 701 $ echo f > f
702 702 $ hg add f
703 703 $ hg shelve
704 704 shelved as default
705 705 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
706 706 $ echo g > f
707 707 $ hg unshelve
708 708 unshelving change 'default'
709 709 $ hg st
710 710 A f
711 711 ? f.orig
712 712 $ cat f
713 713 f
714 714 $ cat f.orig
715 715 g
716 716
717 717 other uncommitted changes - merge:
718 718
719 719 $ hg st
720 720 A f
721 721 ? f.orig
722 722 $ hg shelve
723 723 shelved as default
724 724 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
725 725 $ hg log -G --template '{rev} {desc|firstline} {author}' -R bundle://.hg/shelved/default.hg -r 'bundle()'
726 726 o 4 changes to: commit stuff shelve@localhost
727 727 |
728 728 ~
729 729 $ hg log -G --template '{rev} {desc|firstline} {author}'
730 730 @ 3 commit stuff test
731 731 |
732 732 | o 2 c test
733 733 |/
734 734 o 0 a test
735 735
736 736 $ mv f.orig f
737 737 $ echo 1 > a
738 738 $ hg unshelve --date '1073741824 0'
739 739 unshelving change 'default'
740 740 temporarily committing pending changes (restore with 'hg unshelve --abort')
741 741 rebasing shelved changes
742 742 rebasing 5:81152db69da7 "changes to: commit stuff" (tip)
743 743 merging f
744 744 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
745 745 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
746 746 [1]
747 747 $ hg log -G --template '{rev} {desc|firstline} {author} {date|isodate}'
748 748 @ 5 changes to: commit stuff shelve@localhost 1970-01-01 00:00 +0000
749 749 |
750 750 | @ 4 pending changes temporary commit shelve@localhost 2004-01-10 13:37 +0000
751 751 |/
752 752 o 3 commit stuff test 1970-01-01 00:00 +0000
753 753 |
754 754 | o 2 c test 1970-01-01 00:00 +0000
755 755 |/
756 756 o 0 a test 1970-01-01 00:00 +0000
757 757
758 758 $ hg st
759 759 M f
760 760 ? f.orig
761 761 $ cat f
762 762 <<<<<<< dest: 5f6b880e719b - shelve: pending changes temporary commit
763 763 g
764 764 =======
765 765 f
766 766 >>>>>>> source: 81152db69da7 - shelve: changes to: commit stuff
767 767 $ cat f.orig
768 768 g
769 769 $ hg unshelve --abort -t false
770 770 tool option will be ignored
771 771 rebase aborted
772 772 unshelve of 'default' aborted
773 773 $ hg st
774 774 M a
775 775 ? f.orig
776 776 $ cat f.orig
777 777 g
778 778 $ hg unshelve
779 779 unshelving change 'default'
780 780 temporarily committing pending changes (restore with 'hg unshelve --abort')
781 781 rebasing shelved changes
782 782 rebasing 5:81152db69da7 "changes to: commit stuff" (tip)
783 783 $ hg st
784 784 M a
785 785 A f
786 786 ? f.orig
787 787
788 788 other committed changes - merge:
789 789
790 790 $ hg shelve f
791 791 shelved as default
792 792 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
793 793 $ hg ci a -m 'intermediate other change'
794 794 $ mv f.orig f
795 795 $ hg unshelve
796 796 unshelving change 'default'
797 797 rebasing shelved changes
798 798 rebasing 5:81152db69da7 "changes to: commit stuff" (tip)
799 799 merging f
800 800 warning: conflicts while merging f! (edit, then use 'hg resolve --mark')
801 801 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
802 802 [1]
803 803 $ hg st
804 804 M f
805 805 ? f.orig
806 806 $ cat f
807 807 <<<<<<< dest: * - test: intermediate other change (glob)
808 808 g
809 809 =======
810 810 f
811 811 >>>>>>> source: 81152db69da7 - shelve: changes to: commit stuff
812 812 $ cat f.orig
813 813 g
814 814 $ hg unshelve --abort
815 815 rebase aborted
816 816 unshelve of 'default' aborted
817 817 $ hg st
818 818 ? f.orig
819 819 $ cat f.orig
820 820 g
821 821 $ hg shelve --delete default
822 822
823 823 Recreate some conflict again
824 824
825 825 $ cd ../repo
826 826 $ hg up -C -r 3
827 827 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
828 828 (leaving bookmark test)
829 829 $ echo y >> a/a
830 830 $ hg shelve
831 831 shelved as default
832 832 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
833 833 $ hg up test
834 834 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
835 835 (activating bookmark test)
836 836 $ hg bookmark
837 837 * test 4:33f7f61e6c5e
838 838 $ hg unshelve
839 839 unshelving change 'default'
840 840 rebasing shelved changes
841 841 rebasing 5:e42a7da90865 "changes to: second" (tip)
842 842 merging a/a
843 843 warning: conflicts while merging a/a! (edit, then use 'hg resolve --mark')
844 844 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
845 845 [1]
846 846 $ hg bookmark
847 847 test 4:33f7f61e6c5e
848 848
849 849 Test that resolving all conflicts in one direction (so that the rebase
850 850 is a no-op), works (issue4398)
851 851
852 852 $ hg revert -a -r .
853 853 reverting a/a (glob)
854 854 $ hg resolve -m a/a
855 855 (no more unresolved files)
856 856 continue: hg unshelve --continue
857 857 $ hg unshelve -c
858 858 rebasing 5:e42a7da90865 "changes to: second" (tip)
859 859 note: rebase of 5:e42a7da90865 created no changes to commit
860 860 unshelve of 'default' complete
861 861 $ hg bookmark
862 862 * test 4:33f7f61e6c5e
863 863 $ hg diff
864 864 $ hg status
865 865 ? a/a.orig
866 866 ? foo/foo
867 867 $ hg summary
868 868 parent: 4:33f7f61e6c5e tip
869 869 create conflict
870 870 branch: default
871 871 bookmarks: *test
872 872 commit: 2 unknown (clean)
873 873 update: (current)
874 874 phases: 5 draft
875 875
876 876 $ hg shelve --delete --stat
877 877 abort: options '--delete' and '--stat' may not be used together
878 878 [255]
879 879 $ hg shelve --delete --name NAME
880 880 abort: options '--delete' and '--name' may not be used together
881 881 [255]
882 882
883 883 Test interactive shelve
884 884 $ cat <<EOF >> $HGRCPATH
885 885 > [ui]
886 886 > interactive = true
887 887 > EOF
888 888 $ echo 'a' >> a/b
889 889 $ cat a/a >> a/b
890 890 $ echo 'x' >> a/b
891 891 $ mv a/b a/a
892 892 $ echo 'a' >> foo/foo
893 893 $ hg st
894 894 M a/a
895 895 ? a/a.orig
896 896 ? foo/foo
897 897 $ cat a/a
898 898 a
899 899 a
900 900 c
901 901 x
902 902 x
903 903 $ cat foo/foo
904 904 foo
905 905 a
906 906 $ hg shelve --interactive --config ui.interactive=false
907 907 abort: running non-interactively
908 908 [255]
909 909 $ hg shelve --interactive << EOF
910 910 > y
911 911 > y
912 912 > n
913 913 > EOF
914 914 diff --git a/a/a b/a/a
915 915 2 hunks, 2 lines changed
916 916 examine changes to 'a/a'? [Ynesfdaq?] y
917 917
918 918 @@ -1,3 +1,4 @@
919 919 +a
920 920 a
921 921 c
922 922 x
923 923 record change 1/2 to 'a/a'? [Ynesfdaq?] y
924 924
925 925 @@ -1,3 +2,4 @@
926 926 a
927 927 c
928 928 x
929 929 +x
930 930 record change 2/2 to 'a/a'? [Ynesfdaq?] n
931 931
932 932 shelved as test
933 933 merging a/a
934 934 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
935 935 $ cat a/a
936 936 a
937 937 c
938 938 x
939 939 x
940 940 $ cat foo/foo
941 941 foo
942 942 a
943 943 $ hg st
944 944 M a/a
945 945 ? foo/foo
946 946 $ hg bookmark
947 947 * test 4:33f7f61e6c5e
948 948 $ hg unshelve
949 949 unshelving change 'test'
950 950 temporarily committing pending changes (restore with 'hg unshelve --abort')
951 951 rebasing shelved changes
952 952 rebasing 6:96a1354f65f6 "changes to: create conflict" (tip)
953 953 merging a/a
954 954 $ hg bookmark
955 955 * test 4:33f7f61e6c5e
956 956 $ cat a/a
957 957 a
958 958 a
959 959 c
960 960 x
961 961 x
962 962
963 963 shelve --patch and shelve --stat should work with a single valid shelfname
964 964
965 965 $ hg up --clean .
966 966 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
967 967 (leaving bookmark test)
968 968 $ hg shelve --list
969 969 $ echo 'patch a' > shelf-patch-a
970 970 $ hg add shelf-patch-a
971 971 $ hg shelve
972 972 shelved as default
973 973 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
974 974 $ echo 'patch b' > shelf-patch-b
975 975 $ hg add shelf-patch-b
976 976 $ hg shelve
977 977 shelved as default-01
978 978 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
979 979 $ hg shelve --patch default default-01
980 980 abort: --patch expects a single shelf
981 981 [255]
982 982 $ hg shelve --stat default default-01
983 983 abort: --stat expects a single shelf
984 984 [255]
985 985 $ hg shelve --patch default
986 986 default (*)* changes to: create conflict (glob)
987 987
988 988 diff --git a/shelf-patch-a b/shelf-patch-a
989 989 new file mode 100644
990 990 --- /dev/null
991 991 +++ b/shelf-patch-a
992 992 @@ -0,0 +1,1 @@
993 993 +patch a
994 994 $ hg shelve --stat default
995 995 default (*)* changes to: create conflict (glob)
996 996 shelf-patch-a | 1 +
997 997 1 files changed, 1 insertions(+), 0 deletions(-)
998 998 $ hg shelve --patch nonexistentshelf
999 999 abort: cannot find shelf nonexistentshelf
1000 1000 [255]
1001 1001 $ hg shelve --stat nonexistentshelf
1002 1002 abort: cannot find shelf nonexistentshelf
1003 1003 [255]
1004 1004
1005 1005 $ cd ..
1006 1006
1007 1007 Shelve from general delta repo uses bundle2 on disk
1008 1008 --------------------------------------------------
1009 1009
1010 1010 no general delta
1011 1011
1012 1012 $ hg clone --pull repo bundle1 --config format.usegeneraldelta=0
1013 1013 requesting all changes
1014 1014 adding changesets
1015 1015 adding manifests
1016 1016 adding file changes
1017 1017 added 5 changesets with 8 changes to 6 files
1018 1018 updating to branch default
1019 1019 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
1020 1020 $ cd bundle1
1021 1021 $ echo babar > jungle
1022 1022 $ hg add jungle
1023 1023 $ hg shelve
1024 1024 shelved as default
1025 1025 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1026 1026 $ hg debugbundle .hg/shelved/*.hg
1027 1027 45993d65fe9dc3c6d8764b9c3b07fa831ee7d92d
1028 1028 $ cd ..
1029 1029
1030 1030 with general delta
1031 1031
1032 1032 $ hg clone --pull repo bundle2 --config format.usegeneraldelta=1
1033 1033 requesting all changes
1034 1034 adding changesets
1035 1035 adding manifests
1036 1036 adding file changes
1037 1037 added 5 changesets with 8 changes to 6 files
1038 1038 updating to branch default
1039 1039 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
1040 1040 $ cd bundle2
1041 1041 $ echo babar > jungle
1042 1042 $ hg add jungle
1043 1043 $ hg shelve
1044 1044 shelved as default
1045 1045 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1046 1046 $ hg debugbundle .hg/shelved/*.hg
1047 Stream params: {'Compression': 'BZ'}
1048 changegroup -- "{'version': '02'}"
1047 Stream params: sortdict([('Compression', 'BZ')])
1048 changegroup -- "sortdict([('version', '02')])"
1049 1049 45993d65fe9dc3c6d8764b9c3b07fa831ee7d92d
1050 1050 $ cd ..
1051 1051
1052 1052 Test visibility of in-memory changes inside transaction to external hook
1053 1053 ------------------------------------------------------------------------
1054 1054
1055 1055 $ cd repo
1056 1056
1057 1057 $ echo xxxx >> x
1058 1058 $ hg commit -m "#5: changes to invoke rebase"
1059 1059
1060 1060 $ cat > $TESTTMP/checkvisibility.sh <<EOF
1061 1061 > echo "==== \$1:"
1062 1062 > hg parents --template "VISIBLE {rev}:{node|short}\n"
1063 1063 > # test that pending changes are hidden
1064 1064 > unset HG_PENDING
1065 1065 > hg parents --template "ACTUAL {rev}:{node|short}\n"
1066 1066 > echo "===="
1067 1067 > EOF
1068 1068
1069 1069 $ cat >> .hg/hgrc <<EOF
1070 1070 > [defaults]
1071 1071 > # to fix hash id of temporary revisions
1072 1072 > unshelve = --date '0 0'
1073 1073 > EOF
1074 1074
1075 1075 "hg unshelve" at REV5 implies steps below:
1076 1076
1077 1077 (1) commit changes in the working directory (REV6)
1078 1078 (2) unbundle shelved revision (REV7)
1079 1079 (3) rebase: merge REV7 into REV6 (REV6 => REV6, REV7)
1080 1080 (4) rebase: commit merged revision (REV8)
1081 1081 (5) rebase: update to REV6 (REV8 => REV6)
1082 1082 (6) update to REV5 (REV6 => REV5)
1083 1083 (7) abort transaction
1084 1084
1085 1085 == test visibility to external preupdate hook
1086 1086
1087 1087 $ cat >> .hg/hgrc <<EOF
1088 1088 > [hooks]
1089 1089 > preupdate.visibility = sh $TESTTMP/checkvisibility.sh preupdate
1090 1090 > EOF
1091 1091
1092 1092 $ echo nnnn >> n
1093 1093
1094 1094 $ sh $TESTTMP/checkvisibility.sh before-unshelving
1095 1095 ==== before-unshelving:
1096 1096 VISIBLE 5:703117a2acfb
1097 1097 ACTUAL 5:703117a2acfb
1098 1098 ====
1099 1099
1100 1100 $ hg unshelve --keep default
1101 1101 temporarily committing pending changes (restore with 'hg unshelve --abort')
1102 1102 rebasing shelved changes
1103 1103 rebasing 7:206bf5d4f922 "changes to: create conflict" (tip)
1104 1104 ==== preupdate:
1105 1105 VISIBLE 6:66b86db80ee4
1106 1106 ACTUAL 5:703117a2acfb
1107 1107 ====
1108 1108 ==== preupdate:
1109 1109 VISIBLE 8:a0e04704317e
1110 1110 ACTUAL 5:703117a2acfb
1111 1111 ====
1112 1112 ==== preupdate:
1113 1113 VISIBLE 6:66b86db80ee4
1114 1114 ACTUAL 5:703117a2acfb
1115 1115 ====
1116 1116
1117 1117 $ cat >> .hg/hgrc <<EOF
1118 1118 > [hooks]
1119 1119 > preupdate.visibility =
1120 1120 > EOF
1121 1121
1122 1122 $ sh $TESTTMP/checkvisibility.sh after-unshelving
1123 1123 ==== after-unshelving:
1124 1124 VISIBLE 5:703117a2acfb
1125 1125 ACTUAL 5:703117a2acfb
1126 1126 ====
1127 1127
1128 1128 == test visibility to external update hook
1129 1129
1130 1130 $ hg update -q -C 5
1131 1131
1132 1132 $ cat >> .hg/hgrc <<EOF
1133 1133 > [hooks]
1134 1134 > update.visibility = sh $TESTTMP/checkvisibility.sh update
1135 1135 > EOF
1136 1136
1137 1137 $ echo nnnn >> n
1138 1138
1139 1139 $ sh $TESTTMP/checkvisibility.sh before-unshelving
1140 1140 ==== before-unshelving:
1141 1141 VISIBLE 5:703117a2acfb
1142 1142 ACTUAL 5:703117a2acfb
1143 1143 ====
1144 1144
1145 1145 $ hg unshelve --keep default
1146 1146 temporarily committing pending changes (restore with 'hg unshelve --abort')
1147 1147 rebasing shelved changes
1148 1148 rebasing 7:206bf5d4f922 "changes to: create conflict" (tip)
1149 1149 ==== update:
1150 1150 VISIBLE 6:66b86db80ee4
1151 1151 VISIBLE 7:206bf5d4f922
1152 1152 ACTUAL 5:703117a2acfb
1153 1153 ====
1154 1154 ==== update:
1155 1155 VISIBLE 6:66b86db80ee4
1156 1156 ACTUAL 5:703117a2acfb
1157 1157 ====
1158 1158 ==== update:
1159 1159 VISIBLE 5:703117a2acfb
1160 1160 ACTUAL 5:703117a2acfb
1161 1161 ====
1162 1162
1163 1163 $ cat >> .hg/hgrc <<EOF
1164 1164 > [hooks]
1165 1165 > update.visibility =
1166 1166 > EOF
1167 1167
1168 1168 $ sh $TESTTMP/checkvisibility.sh after-unshelving
1169 1169 ==== after-unshelving:
1170 1170 VISIBLE 5:703117a2acfb
1171 1171 ACTUAL 5:703117a2acfb
1172 1172 ====
1173 1173
1174 1174 $ cd ..
1175 1175
1176 1176 test .orig files go where the user wants them to
1177 1177 ---------------------------------------------------------------
1178 1178 $ hg init salvage
1179 1179 $ cd salvage
1180 1180 $ echo 'content' > root
1181 1181 $ hg commit -A -m 'root' -q
1182 1182 $ echo '' > root
1183 1183 $ hg shelve -q
1184 1184 $ echo 'contADDent' > root
1185 1185 $ hg unshelve -q --config 'ui.origbackuppath=.hg/origbackups'
1186 1186 warning: conflicts while merging root! (edit, then use 'hg resolve --mark')
1187 1187 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
1188 1188 [1]
1189 1189 $ ls .hg/origbackups
1190 1190 root.orig
1191 1191 $ rm -rf .hg/origbackups
1192 1192
1193 1193 test Abort unshelve always gets user out of the unshelved state
1194 1194 ---------------------------------------------------------------
1195 1195 Wreak havoc on the unshelve process
1196 1196 $ rm .hg/unshelverebasestate
1197 1197 $ hg unshelve --abort
1198 1198 unshelve of 'default' aborted
1199 1199 abort: (No such file or directory|The system cannot find the file specified) (re)
1200 1200 [255]
1201 1201 Can the user leave the current state?
1202 1202 $ hg up -C .
1203 1203 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1204 1204
1205 1205 Try again but with a corrupted shelve state file
1206 1206 $ hg strip -r 2 -r 1 -q
1207 1207 $ hg up -r 0 -q
1208 1208 $ echo '' > root
1209 1209 $ hg shelve -q
1210 1210 $ echo 'contADDent' > root
1211 1211 $ hg unshelve -q
1212 1212 warning: conflicts while merging root! (edit, then use 'hg resolve --mark')
1213 1213 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
1214 1214 [1]
1215 1215 $ sed 's/ae8c668541e8/123456789012/' .hg/shelvedstate > ../corrupt-shelvedstate
1216 1216 $ mv ../corrupt-shelvedstate .hg/histedit-state
1217 1217 $ hg unshelve --abort 2>&1 | grep 'rebase aborted'
1218 1218 rebase aborted
1219 1219 $ hg up -C .
1220 1220 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1221 1221
1222 1222 $ cd ..
1223 1223
1224 1224 Keep active bookmark while (un)shelving even on shared repo (issue4940)
1225 1225 -----------------------------------------------------------------------
1226 1226
1227 1227 $ cat <<EOF >> $HGRCPATH
1228 1228 > [extensions]
1229 1229 > share =
1230 1230 > EOF
1231 1231
1232 1232 $ hg bookmarks -R repo
1233 1233 test 4:33f7f61e6c5e
1234 1234 $ hg share -B repo share
1235 1235 updating working directory
1236 1236 6 files updated, 0 files merged, 0 files removed, 0 files unresolved
1237 1237 $ cd share
1238 1238
1239 1239 $ hg bookmarks
1240 1240 test 4:33f7f61e6c5e
1241 1241 $ hg bookmarks foo
1242 1242 $ hg bookmarks
1243 1243 * foo 5:703117a2acfb
1244 1244 test 4:33f7f61e6c5e
1245 1245 $ echo x >> x
1246 1246 $ hg shelve
1247 1247 shelved as foo
1248 1248 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1249 1249 $ hg bookmarks
1250 1250 * foo 5:703117a2acfb
1251 1251 test 4:33f7f61e6c5e
1252 1252
1253 1253 $ hg unshelve
1254 1254 unshelving change 'foo'
1255 1255 $ hg bookmarks
1256 1256 * foo 5:703117a2acfb
1257 1257 test 4:33f7f61e6c5e
1258 1258
1259 1259 $ cd ..
1260 1260
1261 1261 Shelve and unshelve unknown files. For the purposes of unshelve, a shelved
1262 1262 unknown file is the same as a shelved added file, except that it will be in
1263 1263 unknown state after unshelve if and only if it was either absent or unknown
1264 1264 before the unshelve operation.
1265 1265
1266 1266 $ hg init unknowns
1267 1267 $ cd unknowns
1268 1268
1269 1269 The simplest case is if I simply have an unknown file that I shelve and unshelve
1270 1270
1271 1271 $ echo unknown > unknown
1272 1272 $ hg status
1273 1273 ? unknown
1274 1274 $ hg shelve --unknown
1275 1275 shelved as default
1276 1276 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1277 1277 $ hg status
1278 1278 $ hg unshelve
1279 1279 unshelving change 'default'
1280 1280 $ hg status
1281 1281 ? unknown
1282 1282 $ rm unknown
1283 1283
1284 1284 If I shelve, add the file, and unshelve, does it stay added?
1285 1285
1286 1286 $ echo unknown > unknown
1287 1287 $ hg shelve -u
1288 1288 shelved as default
1289 1289 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1290 1290 $ hg status
1291 1291 $ touch unknown
1292 1292 $ hg add unknown
1293 1293 $ hg status
1294 1294 A unknown
1295 1295 $ hg unshelve
1296 1296 unshelving change 'default'
1297 1297 temporarily committing pending changes (restore with 'hg unshelve --abort')
1298 1298 rebasing shelved changes
1299 1299 rebasing 1:098df96e7410 "(changes in empty repository)" (tip)
1300 1300 merging unknown
1301 1301 $ hg status
1302 1302 A unknown
1303 1303 $ hg forget unknown
1304 1304 $ rm unknown
1305 1305
1306 1306 And if I shelve, commit, then unshelve, does it become modified?
1307 1307
1308 1308 $ echo unknown > unknown
1309 1309 $ hg shelve -u
1310 1310 shelved as default
1311 1311 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1312 1312 $ hg status
1313 1313 $ touch unknown
1314 1314 $ hg add unknown
1315 1315 $ hg commit -qm "Add unknown"
1316 1316 $ hg status
1317 1317 $ hg unshelve
1318 1318 unshelving change 'default'
1319 1319 rebasing shelved changes
1320 1320 rebasing 1:098df96e7410 "(changes in empty repository)" (tip)
1321 1321 merging unknown
1322 1322 $ hg status
1323 1323 M unknown
1324 1324 $ hg remove --force unknown
1325 1325 $ hg commit -qm "Remove unknown"
1326 1326
1327 1327 $ cd ..
1328 1328
1329 1329 We expects that non-bare shelve keeps newly created branch in
1330 1330 working directory.
1331 1331
1332 1332 $ hg init shelve-preserve-new-branch
1333 1333 $ cd shelve-preserve-new-branch
1334 1334 $ echo "a" >> a
1335 1335 $ hg add a
1336 1336 $ echo "b" >> b
1337 1337 $ hg add b
1338 1338 $ hg commit -m "ab"
1339 1339 $ echo "aa" >> a
1340 1340 $ echo "bb" >> b
1341 1341 $ hg branch new-branch
1342 1342 marked working directory as branch new-branch
1343 1343 (branches are permanent and global, did you want a bookmark?)
1344 1344 $ hg status
1345 1345 M a
1346 1346 M b
1347 1347 $ hg branch
1348 1348 new-branch
1349 1349 $ hg shelve a
1350 1350 shelved as default
1351 1351 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1352 1352 $ hg branch
1353 1353 new-branch
1354 1354 $ hg status
1355 1355 M b
1356 1356 $ touch "c" >> c
1357 1357 $ hg add c
1358 1358 $ hg status
1359 1359 M b
1360 1360 A c
1361 1361 $ hg shelve --exclude c
1362 1362 shelved as default-01
1363 1363 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1364 1364 $ hg branch
1365 1365 new-branch
1366 1366 $ hg status
1367 1367 A c
1368 1368 $ hg shelve --include c
1369 1369 shelved as default-02
1370 1370 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1371 1371 $ hg branch
1372 1372 new-branch
1373 1373 $ hg status
1374 1374 $ echo "d" >> d
1375 1375 $ hg add d
1376 1376 $ hg status
1377 1377 A d
1378 1378
1379 1379 We expect that bare-shelve will not keep branch in current working directory.
1380 1380
1381 1381 $ hg shelve
1382 1382 shelved as default-03
1383 1383 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1384 1384 $ hg branch
1385 1385 default
1386 1386
1387 1387 When i shelve commit on newly created branch i expect
1388 1388 that after unshelve newly created branch will be preserved.
1389 1389
1390 1390 $ hg init shelve_on_new_branch_simple
1391 1391 $ cd shelve_on_new_branch_simple
1392 1392 $ echo "aaa" >> a
1393 1393 $ hg commit -A -m "a"
1394 1394 adding a
1395 1395 $ hg branch
1396 1396 default
1397 1397 $ hg branch test
1398 1398 marked working directory as branch test
1399 1399 (branches are permanent and global, did you want a bookmark?)
1400 1400 $ echo "bbb" >> a
1401 1401 $ hg status
1402 1402 M a
1403 1403 $ hg shelve
1404 1404 shelved as default
1405 1405 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1406 1406 $ hg branch
1407 1407 default
1408 1408 $ echo "bbb" >> b
1409 1409 $ hg status
1410 1410 ? b
1411 1411 $ hg unshelve
1412 1412 unshelving change 'default'
1413 1413 marked working directory as branch test
1414 1414 $ hg status
1415 1415 M a
1416 1416 ? b
1417 1417 $ hg branch
1418 1418 test
1419 1419
1420 1420 When i shelve commit on newly created branch, make
1421 1421 some changes, unshelve it and running into merge
1422 1422 conflicts i expect that after fixing them and
1423 1423 running unshelve --continue newly created branch
1424 1424 will be preserved.
1425 1425
1426 1426 $ hg init shelve_on_new_branch_conflict
1427 1427 $ cd shelve_on_new_branch_conflict
1428 1428 $ echo "aaa" >> a
1429 1429 $ hg commit -A -m "a"
1430 1430 adding a
1431 1431 $ hg branch
1432 1432 default
1433 1433 $ hg branch test
1434 1434 marked working directory as branch test
1435 1435 (branches are permanent and global, did you want a bookmark?)
1436 1436 $ echo "bbb" >> a
1437 1437 $ hg status
1438 1438 M a
1439 1439 $ hg shelve
1440 1440 shelved as default
1441 1441 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1442 1442 $ hg branch
1443 1443 default
1444 1444 $ echo "ccc" >> a
1445 1445 $ hg status
1446 1446 M a
1447 1447 $ hg unshelve
1448 1448 unshelving change 'default'
1449 1449 temporarily committing pending changes (restore with 'hg unshelve --abort')
1450 1450 rebasing shelved changes
1451 1451 rebasing 2:425c97ef07f3 "changes to: a" (tip)
1452 1452 merging a
1453 1453 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
1454 1454 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
1455 1455 [1]
1456 1456 $ echo "aaabbbccc" > a
1457 1457 $ rm a.orig
1458 1458 $ hg resolve --mark a
1459 1459 (no more unresolved files)
1460 1460 continue: hg unshelve --continue
1461 1461 $ hg unshelve --continue
1462 1462 rebasing 2:425c97ef07f3 "changes to: a" (tip)
1463 1463 marked working directory as branch test
1464 1464 unshelve of 'default' complete
1465 1465 $ cat a
1466 1466 aaabbbccc
1467 1467 $ hg status
1468 1468 M a
1469 1469 $ hg branch
1470 1470 test
1471 1471 $ hg commit -m "test-commit"
1472 1472
1473 1473 When i shelve on test branch, update to default branch
1474 1474 and unshelve i expect that it will not preserve previous
1475 1475 test branch.
1476 1476
1477 1477 $ echo "xxx" > b
1478 1478 $ hg add b
1479 1479 $ hg shelve
1480 1480 shelved as test
1481 1481 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1482 1482 $ hg update -r default
1483 1483 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1484 1484 $ hg unshelve
1485 1485 unshelving change 'test'
1486 1486 rebasing shelved changes
1487 1487 rebasing 2:357525f34729 "changes to: test-commit" (tip)
1488 1488 $ hg status
1489 1489 A b
1490 1490 $ hg branch
1491 1491 default
1492 1492
1493 1493 When i unshelve resulting in merge conflicts and makes saved
1494 1494 file shelvedstate looks like in previous versions in
1495 1495 mercurial(without restore branch information in 7th line) i
1496 1496 expect that after resolving conflicts and succesfully
1497 1497 running 'shelve --continue' the branch information won't be
1498 1498 restored and branch will be unchanged.
1499 1499
1500 1500 shelve on new branch, conflict with previous shelvedstate
1501 1501
1502 1502 $ hg init conflict
1503 1503 $ cd conflict
1504 1504 $ echo "aaa" >> a
1505 1505 $ hg commit -A -m "a"
1506 1506 adding a
1507 1507 $ hg branch
1508 1508 default
1509 1509 $ hg branch test
1510 1510 marked working directory as branch test
1511 1511 (branches are permanent and global, did you want a bookmark?)
1512 1512 $ echo "bbb" >> a
1513 1513 $ hg status
1514 1514 M a
1515 1515 $ hg shelve
1516 1516 shelved as default
1517 1517 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1518 1518 $ hg branch
1519 1519 default
1520 1520 $ echo "ccc" >> a
1521 1521 $ hg status
1522 1522 M a
1523 1523 $ hg unshelve
1524 1524 unshelving change 'default'
1525 1525 temporarily committing pending changes (restore with 'hg unshelve --abort')
1526 1526 rebasing shelved changes
1527 1527 rebasing 2:425c97ef07f3 "changes to: a" (tip)
1528 1528 merging a
1529 1529 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
1530 1530 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
1531 1531 [1]
1532 1532
1533 1533 Removing restore branch information from shelvedstate file(making it looks like
1534 1534 in previous versions) and running unshelve --continue
1535 1535
1536 1536 $ head -n 6 < .hg/shelvedstate > .hg/shelvedstate_oldformat
1537 1537 $ rm .hg/shelvedstate
1538 1538 $ mv .hg/shelvedstate_oldformat .hg/shelvedstate
1539 1539
1540 1540 $ echo "aaabbbccc" > a
1541 1541 $ rm a.orig
1542 1542 $ hg resolve --mark a
1543 1543 (no more unresolved files)
1544 1544 continue: hg unshelve --continue
1545 1545 $ hg unshelve --continue
1546 1546 rebasing 2:425c97ef07f3 "changes to: a" (tip)
1547 1547 unshelve of 'default' complete
1548 1548 $ cat a
1549 1549 aaabbbccc
1550 1550 $ hg status
1551 1551 M a
1552 1552 $ hg branch
1553 1553 default
1554 1554
1555 1555 On non bare shelve the branch information shouldn't be restored
1556 1556
1557 1557 $ hg init bare_shelve_on_new_branch
1558 1558 $ cd bare_shelve_on_new_branch
1559 1559 $ echo "aaa" >> a
1560 1560 $ hg commit -A -m "a"
1561 1561 adding a
1562 1562 $ hg branch
1563 1563 default
1564 1564 $ hg branch test
1565 1565 marked working directory as branch test
1566 1566 (branches are permanent and global, did you want a bookmark?)
1567 1567 $ echo "bbb" >> a
1568 1568 $ hg status
1569 1569 M a
1570 1570 $ hg shelve a
1571 1571 shelved as default
1572 1572 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1573 1573 $ hg branch
1574 1574 test
1575 1575 $ hg branch default
1576 1576 marked working directory as branch default
1577 1577 (branches are permanent and global, did you want a bookmark?)
1578 1578 $ echo "bbb" >> b
1579 1579 $ hg status
1580 1580 ? b
1581 1581 $ hg unshelve
1582 1582 unshelving change 'default'
1583 1583 $ hg status
1584 1584 M a
1585 1585 ? b
1586 1586 $ hg branch
1587 1587 default
1588 1588 $ cd ..
1589 1589
1590 1590 Prepare unshleve with a corrupted shelvedstate
1591 1591 $ hg init r1 && cd r1
1592 1592 $ echo text1 > file && hg add file
1593 1593 $ hg shelve
1594 1594 shelved as default
1595 1595 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1596 1596 $ echo text2 > file && hg ci -Am text1
1597 1597 adding file
1598 1598 $ hg unshelve
1599 1599 unshelving change 'default'
1600 1600 rebasing shelved changes
1601 1601 rebasing 1:396ea74229f9 "(changes in empty repository)" (tip)
1602 1602 merging file
1603 1603 warning: conflicts while merging file! (edit, then use 'hg resolve --mark')
1604 1604 unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
1605 1605 [1]
1606 1606 $ echo somethingsomething > .hg/shelvedstate
1607 1607
1608 1608 Unshelve --continue fails with appropriate message if shelvedstate is corrupted
1609 1609 $ hg unshelve --continue
1610 1610 abort: corrupted shelved state file
1611 1611 (please run hg unshelve --abort to abort unshelve operation)
1612 1612 [255]
1613 1613
1614 1614 Unshelve --abort works with a corrupted shelvedstate
1615 1615 $ hg unshelve --abort
1616 1616 could not read shelved state file, your working copy may be in an unexpected state
1617 1617 please update to some commit
1618 1618
1619 1619 Unshelve --abort fails with appropriate message if there's no unshelve in
1620 1620 progress
1621 1621 $ hg unshelve --abort
1622 1622 abort: no unshelve in progress
1623 1623 [255]
1624 1624 $ cd ..
@@ -1,898 +1,898 b''
1 1 $ echo "[format]" >> $HGRCPATH
2 2 $ echo "usegeneraldelta=yes" >> $HGRCPATH
3 3 $ echo "[extensions]" >> $HGRCPATH
4 4 $ echo "strip=" >> $HGRCPATH
5 5
6 6 $ restore() {
7 7 > hg unbundle -q .hg/strip-backup/*
8 8 > rm .hg/strip-backup/*
9 9 > }
10 10 $ teststrip() {
11 11 > hg up -C $1
12 12 > echo % before update $1, strip $2
13 13 > hg parents
14 14 > hg --traceback strip $2
15 15 > echo % after update $1, strip $2
16 16 > hg parents
17 17 > restore
18 18 > }
19 19
20 20 $ hg init test
21 21 $ cd test
22 22
23 23 $ echo foo > bar
24 24 $ hg ci -Ama
25 25 adding bar
26 26
27 27 $ echo more >> bar
28 28 $ hg ci -Amb
29 29
30 30 $ echo blah >> bar
31 31 $ hg ci -Amc
32 32
33 33 $ hg up 1
34 34 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
35 35 $ echo blah >> bar
36 36 $ hg ci -Amd
37 37 created new head
38 38
39 39 $ echo final >> bar
40 40 $ hg ci -Ame
41 41
42 42 $ hg log
43 43 changeset: 4:443431ffac4f
44 44 tag: tip
45 45 user: test
46 46 date: Thu Jan 01 00:00:00 1970 +0000
47 47 summary: e
48 48
49 49 changeset: 3:65bd5f99a4a3
50 50 parent: 1:ef3a871183d7
51 51 user: test
52 52 date: Thu Jan 01 00:00:00 1970 +0000
53 53 summary: d
54 54
55 55 changeset: 2:264128213d29
56 56 user: test
57 57 date: Thu Jan 01 00:00:00 1970 +0000
58 58 summary: c
59 59
60 60 changeset: 1:ef3a871183d7
61 61 user: test
62 62 date: Thu Jan 01 00:00:00 1970 +0000
63 63 summary: b
64 64
65 65 changeset: 0:9ab35a2d17cb
66 66 user: test
67 67 date: Thu Jan 01 00:00:00 1970 +0000
68 68 summary: a
69 69
70 70
71 71 $ teststrip 4 4
72 72 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
73 73 % before update 4, strip 4
74 74 changeset: 4:443431ffac4f
75 75 tag: tip
76 76 user: test
77 77 date: Thu Jan 01 00:00:00 1970 +0000
78 78 summary: e
79 79
80 80 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
81 81 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
82 82 % after update 4, strip 4
83 83 changeset: 3:65bd5f99a4a3
84 84 tag: tip
85 85 parent: 1:ef3a871183d7
86 86 user: test
87 87 date: Thu Jan 01 00:00:00 1970 +0000
88 88 summary: d
89 89
90 90 $ teststrip 4 3
91 91 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
92 92 % before update 4, strip 3
93 93 changeset: 4:443431ffac4f
94 94 tag: tip
95 95 user: test
96 96 date: Thu Jan 01 00:00:00 1970 +0000
97 97 summary: e
98 98
99 99 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
100 100 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
101 101 % after update 4, strip 3
102 102 changeset: 1:ef3a871183d7
103 103 user: test
104 104 date: Thu Jan 01 00:00:00 1970 +0000
105 105 summary: b
106 106
107 107 $ teststrip 1 4
108 108 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
109 109 % before update 1, strip 4
110 110 changeset: 1:ef3a871183d7
111 111 user: test
112 112 date: Thu Jan 01 00:00:00 1970 +0000
113 113 summary: b
114 114
115 115 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
116 116 % after update 1, strip 4
117 117 changeset: 1:ef3a871183d7
118 118 user: test
119 119 date: Thu Jan 01 00:00:00 1970 +0000
120 120 summary: b
121 121
122 122 $ teststrip 4 2
123 123 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
124 124 % before update 4, strip 2
125 125 changeset: 4:443431ffac4f
126 126 tag: tip
127 127 user: test
128 128 date: Thu Jan 01 00:00:00 1970 +0000
129 129 summary: e
130 130
131 131 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
132 132 % after update 4, strip 2
133 133 changeset: 3:443431ffac4f
134 134 tag: tip
135 135 user: test
136 136 date: Thu Jan 01 00:00:00 1970 +0000
137 137 summary: e
138 138
139 139 $ teststrip 4 1
140 140 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
141 141 % before update 4, strip 1
142 142 changeset: 4:264128213d29
143 143 tag: tip
144 144 parent: 1:ef3a871183d7
145 145 user: test
146 146 date: Thu Jan 01 00:00:00 1970 +0000
147 147 summary: c
148 148
149 149 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
150 150 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
151 151 % after update 4, strip 1
152 152 changeset: 0:9ab35a2d17cb
153 153 tag: tip
154 154 user: test
155 155 date: Thu Jan 01 00:00:00 1970 +0000
156 156 summary: a
157 157
158 158 $ teststrip null 4
159 159 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
160 160 % before update null, strip 4
161 161 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
162 162 % after update null, strip 4
163 163
164 164 $ hg log
165 165 changeset: 4:264128213d29
166 166 tag: tip
167 167 parent: 1:ef3a871183d7
168 168 user: test
169 169 date: Thu Jan 01 00:00:00 1970 +0000
170 170 summary: c
171 171
172 172 changeset: 3:443431ffac4f
173 173 user: test
174 174 date: Thu Jan 01 00:00:00 1970 +0000
175 175 summary: e
176 176
177 177 changeset: 2:65bd5f99a4a3
178 178 user: test
179 179 date: Thu Jan 01 00:00:00 1970 +0000
180 180 summary: d
181 181
182 182 changeset: 1:ef3a871183d7
183 183 user: test
184 184 date: Thu Jan 01 00:00:00 1970 +0000
185 185 summary: b
186 186
187 187 changeset: 0:9ab35a2d17cb
188 188 user: test
189 189 date: Thu Jan 01 00:00:00 1970 +0000
190 190 summary: a
191 191
192 192 $ hg up -C 4
193 193 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
194 194 $ hg parents
195 195 changeset: 4:264128213d29
196 196 tag: tip
197 197 parent: 1:ef3a871183d7
198 198 user: test
199 199 date: Thu Jan 01 00:00:00 1970 +0000
200 200 summary: c
201 201
202 202
203 203 $ hg --traceback strip 4
204 204 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
205 205 saved backup bundle to $TESTTMP/test/.hg/strip-backup/264128213d29-0b39d6bf-backup.hg (glob)
206 206 $ hg parents
207 207 changeset: 1:ef3a871183d7
208 208 user: test
209 209 date: Thu Jan 01 00:00:00 1970 +0000
210 210 summary: b
211 211
212 212 $ hg debugbundle .hg/strip-backup/*
213 Stream params: {'Compression': 'BZ'}
214 changegroup -- "{'version': '02'}"
213 Stream params: sortdict([('Compression', 'BZ')])
214 changegroup -- "sortdict([('version', '02')])"
215 215 264128213d290d868c54642d13aeaa3675551a78
216 216 $ hg pull .hg/strip-backup/*
217 217 pulling from .hg/strip-backup/264128213d29-0b39d6bf-backup.hg
218 218 searching for changes
219 219 adding changesets
220 220 adding manifests
221 221 adding file changes
222 222 added 1 changesets with 0 changes to 0 files (+1 heads)
223 223 (run 'hg heads' to see heads, 'hg merge' to merge)
224 224 $ rm .hg/strip-backup/*
225 225 $ hg log --graph
226 226 o changeset: 4:264128213d29
227 227 | tag: tip
228 228 | parent: 1:ef3a871183d7
229 229 | user: test
230 230 | date: Thu Jan 01 00:00:00 1970 +0000
231 231 | summary: c
232 232 |
233 233 | o changeset: 3:443431ffac4f
234 234 | | user: test
235 235 | | date: Thu Jan 01 00:00:00 1970 +0000
236 236 | | summary: e
237 237 | |
238 238 | o changeset: 2:65bd5f99a4a3
239 239 |/ user: test
240 240 | date: Thu Jan 01 00:00:00 1970 +0000
241 241 | summary: d
242 242 |
243 243 @ changeset: 1:ef3a871183d7
244 244 | user: test
245 245 | date: Thu Jan 01 00:00:00 1970 +0000
246 246 | summary: b
247 247 |
248 248 o changeset: 0:9ab35a2d17cb
249 249 user: test
250 250 date: Thu Jan 01 00:00:00 1970 +0000
251 251 summary: a
252 252
253 253 $ hg up -C 2
254 254 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
255 255 $ hg merge 4
256 256 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
257 257 (branch merge, don't forget to commit)
258 258
259 259 before strip of merge parent
260 260
261 261 $ hg parents
262 262 changeset: 2:65bd5f99a4a3
263 263 user: test
264 264 date: Thu Jan 01 00:00:00 1970 +0000
265 265 summary: d
266 266
267 267 changeset: 4:264128213d29
268 268 tag: tip
269 269 parent: 1:ef3a871183d7
270 270 user: test
271 271 date: Thu Jan 01 00:00:00 1970 +0000
272 272 summary: c
273 273
274 274 $ hg strip 4
275 275 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
276 276 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
277 277
278 278 after strip of merge parent
279 279
280 280 $ hg parents
281 281 changeset: 1:ef3a871183d7
282 282 user: test
283 283 date: Thu Jan 01 00:00:00 1970 +0000
284 284 summary: b
285 285
286 286 $ restore
287 287
288 288 $ hg up
289 289 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
290 290 1 other heads for branch "default"
291 291 $ hg log -G
292 292 @ changeset: 4:264128213d29
293 293 | tag: tip
294 294 | parent: 1:ef3a871183d7
295 295 | user: test
296 296 | date: Thu Jan 01 00:00:00 1970 +0000
297 297 | summary: c
298 298 |
299 299 | o changeset: 3:443431ffac4f
300 300 | | user: test
301 301 | | date: Thu Jan 01 00:00:00 1970 +0000
302 302 | | summary: e
303 303 | |
304 304 | o changeset: 2:65bd5f99a4a3
305 305 |/ user: test
306 306 | date: Thu Jan 01 00:00:00 1970 +0000
307 307 | summary: d
308 308 |
309 309 o changeset: 1:ef3a871183d7
310 310 | user: test
311 311 | date: Thu Jan 01 00:00:00 1970 +0000
312 312 | summary: b
313 313 |
314 314 o changeset: 0:9ab35a2d17cb
315 315 user: test
316 316 date: Thu Jan 01 00:00:00 1970 +0000
317 317 summary: a
318 318
319 319
320 320 2 is parent of 3, only one strip should happen
321 321
322 322 $ hg strip "roots(2)" 3
323 323 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
324 324 $ hg log -G
325 325 @ changeset: 2:264128213d29
326 326 | tag: tip
327 327 | user: test
328 328 | date: Thu Jan 01 00:00:00 1970 +0000
329 329 | summary: c
330 330 |
331 331 o changeset: 1:ef3a871183d7
332 332 | user: test
333 333 | date: Thu Jan 01 00:00:00 1970 +0000
334 334 | summary: b
335 335 |
336 336 o changeset: 0:9ab35a2d17cb
337 337 user: test
338 338 date: Thu Jan 01 00:00:00 1970 +0000
339 339 summary: a
340 340
341 341 $ restore
342 342 $ hg log -G
343 343 o changeset: 4:443431ffac4f
344 344 | tag: tip
345 345 | user: test
346 346 | date: Thu Jan 01 00:00:00 1970 +0000
347 347 | summary: e
348 348 |
349 349 o changeset: 3:65bd5f99a4a3
350 350 | parent: 1:ef3a871183d7
351 351 | user: test
352 352 | date: Thu Jan 01 00:00:00 1970 +0000
353 353 | summary: d
354 354 |
355 355 | @ changeset: 2:264128213d29
356 356 |/ user: test
357 357 | date: Thu Jan 01 00:00:00 1970 +0000
358 358 | summary: c
359 359 |
360 360 o changeset: 1:ef3a871183d7
361 361 | user: test
362 362 | date: Thu Jan 01 00:00:00 1970 +0000
363 363 | summary: b
364 364 |
365 365 o changeset: 0:9ab35a2d17cb
366 366 user: test
367 367 date: Thu Jan 01 00:00:00 1970 +0000
368 368 summary: a
369 369
370 370
371 371 2 different branches: 2 strips
372 372
373 373 $ hg strip 2 4
374 374 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
375 375 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
376 376 $ hg log -G
377 377 o changeset: 2:65bd5f99a4a3
378 378 | tag: tip
379 379 | user: test
380 380 | date: Thu Jan 01 00:00:00 1970 +0000
381 381 | summary: d
382 382 |
383 383 @ changeset: 1:ef3a871183d7
384 384 | user: test
385 385 | date: Thu Jan 01 00:00:00 1970 +0000
386 386 | summary: b
387 387 |
388 388 o changeset: 0:9ab35a2d17cb
389 389 user: test
390 390 date: Thu Jan 01 00:00:00 1970 +0000
391 391 summary: a
392 392
393 393 $ restore
394 394
395 395 2 different branches and a common ancestor: 1 strip
396 396
397 397 $ hg strip 1 "2|4"
398 398 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
399 399 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
400 400 $ restore
401 401
402 402 verify fncache is kept up-to-date
403 403
404 404 $ touch a
405 405 $ hg ci -qAm a
406 406 $ cat .hg/store/fncache | sort
407 407 data/a.i
408 408 data/bar.i
409 409 $ hg strip tip
410 410 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
411 411 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
412 412 $ cat .hg/store/fncache
413 413 data/bar.i
414 414
415 415 stripping an empty revset
416 416
417 417 $ hg strip "1 and not 1"
418 418 abort: empty revision set
419 419 [255]
420 420
421 421 remove branchy history for qimport tests
422 422
423 423 $ hg strip 3
424 424 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
425 425
426 426
427 427 strip of applied mq should cleanup status file
428 428
429 429 $ echo "mq=" >> $HGRCPATH
430 430 $ hg up -C 3
431 431 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
432 432 $ echo fooagain >> bar
433 433 $ hg ci -mf
434 434 $ hg qimport -r tip:2
435 435
436 436 applied patches before strip
437 437
438 438 $ hg qapplied
439 439 d
440 440 e
441 441 f
442 442
443 443 stripping revision in queue
444 444
445 445 $ hg strip 3
446 446 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
447 447 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
448 448
449 449 applied patches after stripping rev in queue
450 450
451 451 $ hg qapplied
452 452 d
453 453
454 454 stripping ancestor of queue
455 455
456 456 $ hg strip 1
457 457 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
458 458 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
459 459
460 460 applied patches after stripping ancestor of queue
461 461
462 462 $ hg qapplied
463 463
464 464 Verify strip protects against stripping wc parent when there are uncommitted mods
465 465
466 466 $ echo b > b
467 467 $ echo bb > bar
468 468 $ hg add b
469 469 $ hg ci -m 'b'
470 470 $ hg log --graph
471 471 @ changeset: 1:76dcf9fab855
472 472 | tag: tip
473 473 | user: test
474 474 | date: Thu Jan 01 00:00:00 1970 +0000
475 475 | summary: b
476 476 |
477 477 o changeset: 0:9ab35a2d17cb
478 478 user: test
479 479 date: Thu Jan 01 00:00:00 1970 +0000
480 480 summary: a
481 481
482 482 $ hg up 0
483 483 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
484 484 $ echo c > bar
485 485 $ hg up -t false
486 486 merging bar
487 487 merging bar failed!
488 488 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
489 489 use 'hg resolve' to retry unresolved file merges
490 490 [1]
491 491 $ hg sum
492 492 parent: 1:76dcf9fab855 tip
493 493 b
494 494 branch: default
495 495 commit: 1 modified, 1 unknown, 1 unresolved
496 496 update: (current)
497 497 phases: 2 draft
498 498 mq: 3 unapplied
499 499
500 500 $ echo c > b
501 501 $ hg strip tip
502 502 abort: local changes found
503 503 [255]
504 504 $ hg strip tip --keep
505 505 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
506 506 $ hg log --graph
507 507 @ changeset: 0:9ab35a2d17cb
508 508 tag: tip
509 509 user: test
510 510 date: Thu Jan 01 00:00:00 1970 +0000
511 511 summary: a
512 512
513 513 $ hg status
514 514 M bar
515 515 ? b
516 516 ? bar.orig
517 517
518 518 $ rm bar.orig
519 519 $ hg sum
520 520 parent: 0:9ab35a2d17cb tip
521 521 a
522 522 branch: default
523 523 commit: 1 modified, 1 unknown
524 524 update: (current)
525 525 phases: 1 draft
526 526 mq: 3 unapplied
527 527
528 528 Strip adds, removes, modifies with --keep
529 529
530 530 $ touch b
531 531 $ hg add b
532 532 $ hg commit -mb
533 533 $ touch c
534 534
535 535 ... with a clean working dir
536 536
537 537 $ hg add c
538 538 $ hg rm bar
539 539 $ hg commit -mc
540 540 $ hg status
541 541 $ hg strip --keep tip
542 542 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
543 543 $ hg status
544 544 ! bar
545 545 ? c
546 546
547 547 ... with a dirty working dir
548 548
549 549 $ hg add c
550 550 $ hg rm bar
551 551 $ hg commit -mc
552 552 $ hg status
553 553 $ echo b > b
554 554 $ echo d > d
555 555 $ hg strip --keep tip
556 556 saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
557 557 $ hg status
558 558 M b
559 559 ! bar
560 560 ? c
561 561 ? d
562 562
563 563 ... after updating the dirstate
564 564 $ hg add c
565 565 $ hg commit -mc
566 566 $ hg rm c
567 567 $ hg commit -mc
568 568 $ hg strip --keep '.^' -q
569 569 $ cd ..
570 570
571 571 stripping many nodes on a complex graph (issue3299)
572 572
573 573 $ hg init issue3299
574 574 $ cd issue3299
575 575 $ hg debugbuilddag '@a.:a@b.:b.:x<a@a.:a<b@b.:b<a@a.:a'
576 576 $ hg strip 'not ancestors(x)'
577 577 saved backup bundle to $TESTTMP/issue3299/.hg/strip-backup/*-backup.hg (glob)
578 578
579 579 test hg strip -B bookmark
580 580
581 581 $ cd ..
582 582 $ hg init bookmarks
583 583 $ cd bookmarks
584 584 $ hg debugbuilddag '..<2.*1/2:m<2+3:c<m+3:a<2.:b<m+2:d<2.:e<m+1:f'
585 585 $ hg bookmark -r 'a' 'todelete'
586 586 $ hg bookmark -r 'b' 'B'
587 587 $ hg bookmark -r 'b' 'nostrip'
588 588 $ hg bookmark -r 'c' 'delete'
589 589 $ hg bookmark -r 'd' 'multipledelete1'
590 590 $ hg bookmark -r 'e' 'multipledelete2'
591 591 $ hg bookmark -r 'f' 'singlenode1'
592 592 $ hg bookmark -r 'f' 'singlenode2'
593 593 $ hg up -C todelete
594 594 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
595 595 (activating bookmark todelete)
596 596 $ hg strip -B nostrip
597 597 bookmark 'nostrip' deleted
598 598 abort: empty revision set
599 599 [255]
600 600 $ hg strip -B todelete
601 601 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
602 602 saved backup bundle to $TESTTMP/bookmarks/.hg/strip-backup/*-backup.hg (glob)
603 603 bookmark 'todelete' deleted
604 604 $ hg id -ir dcbb326fdec2
605 605 abort: unknown revision 'dcbb326fdec2'!
606 606 [255]
607 607 $ hg id -ir d62d843c9a01
608 608 d62d843c9a01
609 609 $ hg bookmarks
610 610 B 9:ff43616e5d0f
611 611 delete 6:2702dd0c91e7
612 612 multipledelete1 11:e46a4836065c
613 613 multipledelete2 12:b4594d867745
614 614 singlenode1 13:43227190fef8
615 615 singlenode2 13:43227190fef8
616 616 $ hg strip -B multipledelete1 -B multipledelete2
617 617 saved backup bundle to $TESTTMP/bookmarks/.hg/strip-backup/e46a4836065c-89ec65c2-backup.hg (glob)
618 618 bookmark 'multipledelete1' deleted
619 619 bookmark 'multipledelete2' deleted
620 620 $ hg id -ir e46a4836065c
621 621 abort: unknown revision 'e46a4836065c'!
622 622 [255]
623 623 $ hg id -ir b4594d867745
624 624 abort: unknown revision 'b4594d867745'!
625 625 [255]
626 626 $ hg strip -B singlenode1 -B singlenode2
627 627 saved backup bundle to $TESTTMP/bookmarks/.hg/strip-backup/43227190fef8-8da858f2-backup.hg (glob)
628 628 bookmark 'singlenode1' deleted
629 629 bookmark 'singlenode2' deleted
630 630 $ hg id -ir 43227190fef8
631 631 abort: unknown revision '43227190fef8'!
632 632 [255]
633 633 $ hg strip -B unknownbookmark
634 634 abort: bookmark 'unknownbookmark' not found
635 635 [255]
636 636 $ hg strip -B unknownbookmark1 -B unknownbookmark2
637 637 abort: bookmark 'unknownbookmark1,unknownbookmark2' not found
638 638 [255]
639 639 $ hg strip -B delete -B unknownbookmark
640 640 abort: bookmark 'unknownbookmark' not found
641 641 [255]
642 642 $ hg strip -B delete
643 643 saved backup bundle to $TESTTMP/bookmarks/.hg/strip-backup/*-backup.hg (glob)
644 644 bookmark 'delete' deleted
645 645 $ hg id -ir 6:2702dd0c91e7
646 646 abort: unknown revision '2702dd0c91e7'!
647 647 [255]
648 648 $ hg update B
649 649 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
650 650 (activating bookmark B)
651 651 $ echo a > a
652 652 $ hg add a
653 653 $ hg strip -B B
654 654 abort: local changes found
655 655 [255]
656 656 $ hg bookmarks
657 657 * B 6:ff43616e5d0f
658 658
659 659 Make sure no one adds back a -b option:
660 660
661 661 $ hg strip -b tip
662 662 hg strip: option -b not recognized
663 663 hg strip [-k] [-f] [-B bookmark] [-r] REV...
664 664
665 665 strip changesets and all their descendants from the repository
666 666
667 667 (use "hg help -e strip" to show help for the strip extension)
668 668
669 669 options ([+] can be repeated):
670 670
671 671 -r --rev REV [+] strip specified revision (optional, can specify
672 672 revisions without this option)
673 673 -f --force force removal of changesets, discard uncommitted
674 674 changes (no backup)
675 675 --no-backup no backups
676 676 -k --keep do not modify working directory during strip
677 677 -B --bookmark VALUE [+] remove revs only reachable from given bookmark
678 678 --mq operate on patch repository
679 679
680 680 (use "hg strip -h" to show more help)
681 681 [255]
682 682
683 683 $ cd ..
684 684
685 685 Verify bundles don't get overwritten:
686 686
687 687 $ hg init doublebundle
688 688 $ cd doublebundle
689 689 $ touch a
690 690 $ hg commit -Aqm a
691 691 $ touch b
692 692 $ hg commit -Aqm b
693 693 $ hg strip -r 0
694 694 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
695 695 saved backup bundle to $TESTTMP/doublebundle/.hg/strip-backup/3903775176ed-e68910bd-backup.hg (glob)
696 696 $ ls .hg/strip-backup
697 697 3903775176ed-e68910bd-backup.hg
698 698 $ hg pull -q -r 3903775176ed .hg/strip-backup/3903775176ed-e68910bd-backup.hg
699 699 $ hg strip -r 0
700 700 saved backup bundle to $TESTTMP/doublebundle/.hg/strip-backup/3903775176ed-54390173-backup.hg (glob)
701 701 $ ls .hg/strip-backup
702 702 3903775176ed-54390173-backup.hg
703 703 3903775176ed-e68910bd-backup.hg
704 704 $ cd ..
705 705
706 706 Test that we only bundle the stripped changesets (issue4736)
707 707 ------------------------------------------------------------
708 708
709 709 initialization (previous repo is empty anyway)
710 710
711 711 $ hg init issue4736
712 712 $ cd issue4736
713 713 $ echo a > a
714 714 $ hg add a
715 715 $ hg commit -m commitA
716 716 $ echo b > b
717 717 $ hg add b
718 718 $ hg commit -m commitB
719 719 $ echo c > c
720 720 $ hg add c
721 721 $ hg commit -m commitC
722 722 $ hg up 'desc(commitB)'
723 723 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
724 724 $ echo d > d
725 725 $ hg add d
726 726 $ hg commit -m commitD
727 727 created new head
728 728 $ hg up 'desc(commitC)'
729 729 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
730 730 $ hg merge 'desc(commitD)'
731 731 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
732 732 (branch merge, don't forget to commit)
733 733 $ hg ci -m 'mergeCD'
734 734 $ hg log -G
735 735 @ changeset: 4:d8db9d137221
736 736 |\ tag: tip
737 737 | | parent: 2:5c51d8d6557d
738 738 | | parent: 3:6625a5168474
739 739 | | user: test
740 740 | | date: Thu Jan 01 00:00:00 1970 +0000
741 741 | | summary: mergeCD
742 742 | |
743 743 | o changeset: 3:6625a5168474
744 744 | | parent: 1:eca11cf91c71
745 745 | | user: test
746 746 | | date: Thu Jan 01 00:00:00 1970 +0000
747 747 | | summary: commitD
748 748 | |
749 749 o | changeset: 2:5c51d8d6557d
750 750 |/ user: test
751 751 | date: Thu Jan 01 00:00:00 1970 +0000
752 752 | summary: commitC
753 753 |
754 754 o changeset: 1:eca11cf91c71
755 755 | user: test
756 756 | date: Thu Jan 01 00:00:00 1970 +0000
757 757 | summary: commitB
758 758 |
759 759 o changeset: 0:105141ef12d0
760 760 user: test
761 761 date: Thu Jan 01 00:00:00 1970 +0000
762 762 summary: commitA
763 763
764 764
765 765 Check bundle behavior:
766 766
767 767 $ hg bundle -r 'desc(mergeCD)' --base 'desc(commitC)' ../issue4736.hg
768 768 2 changesets found
769 769 $ hg log -r 'bundle()' -R ../issue4736.hg
770 770 changeset: 3:6625a5168474
771 771 parent: 1:eca11cf91c71
772 772 user: test
773 773 date: Thu Jan 01 00:00:00 1970 +0000
774 774 summary: commitD
775 775
776 776 changeset: 4:d8db9d137221
777 777 tag: tip
778 778 parent: 2:5c51d8d6557d
779 779 parent: 3:6625a5168474
780 780 user: test
781 781 date: Thu Jan 01 00:00:00 1970 +0000
782 782 summary: mergeCD
783 783
784 784
785 785 check strip behavior
786 786
787 787 $ hg --config extensions.strip= strip 'desc(commitD)' --debug
788 788 resolving manifests
789 789 branchmerge: False, force: True, partial: False
790 790 ancestor: d8db9d137221+, local: d8db9d137221+, remote: eca11cf91c71
791 791 c: other deleted -> r
792 792 removing c
793 793 d: other deleted -> r
794 794 removing d
795 795 starting 4 threads for background file closing (?)
796 796 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
797 797 2 changesets found
798 798 list of changesets:
799 799 6625a516847449b6f0fa3737b9ba56e9f0f3032c
800 800 d8db9d1372214336d2b5570f20ee468d2c72fa8b
801 801 bundle2-output-bundle: "HG20", (1 params) 1 parts total
802 802 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
803 803 saved backup bundle to $TESTTMP/issue4736/.hg/strip-backup/6625a5168474-345bb43d-backup.hg (glob)
804 804 invalid branchheads cache (served): tip differs
805 805 truncating cache/rbc-revs-v1 to 24
806 806 $ hg log -G
807 807 o changeset: 2:5c51d8d6557d
808 808 | tag: tip
809 809 | user: test
810 810 | date: Thu Jan 01 00:00:00 1970 +0000
811 811 | summary: commitC
812 812 |
813 813 @ changeset: 1:eca11cf91c71
814 814 | user: test
815 815 | date: Thu Jan 01 00:00:00 1970 +0000
816 816 | summary: commitB
817 817 |
818 818 o changeset: 0:105141ef12d0
819 819 user: test
820 820 date: Thu Jan 01 00:00:00 1970 +0000
821 821 summary: commitA
822 822
823 823
824 824 strip backup content
825 825
826 826 $ hg log -r 'bundle()' -R .hg/strip-backup/6625a5168474-*-backup.hg
827 827 changeset: 3:6625a5168474
828 828 parent: 1:eca11cf91c71
829 829 user: test
830 830 date: Thu Jan 01 00:00:00 1970 +0000
831 831 summary: commitD
832 832
833 833 changeset: 4:d8db9d137221
834 834 tag: tip
835 835 parent: 2:5c51d8d6557d
836 836 parent: 3:6625a5168474
837 837 user: test
838 838 date: Thu Jan 01 00:00:00 1970 +0000
839 839 summary: mergeCD
840 840
841 841 Check that the phase cache is properly invalidated after a strip with bookmark.
842 842
843 843 $ cat > ../stripstalephasecache.py << EOF
844 844 > from mercurial import extensions, localrepo
845 845 > def transactioncallback(orig, repo, desc, *args, **kwargs):
846 846 > def test(transaction):
847 847 > # observe cache inconsistency
848 848 > try:
849 849 > [repo.changelog.node(r) for r in repo.revs("not public()")]
850 850 > except IndexError:
851 851 > repo.ui.status("Index error!\n")
852 852 > transaction = orig(repo, desc, *args, **kwargs)
853 853 > # warm up the phase cache
854 854 > list(repo.revs("not public()"))
855 855 > if desc != 'strip':
856 856 > transaction.addpostclose("phase invalidation test", test)
857 857 > return transaction
858 858 > def extsetup(ui):
859 859 > extensions.wrapfunction(localrepo.localrepository, "transaction",
860 860 > transactioncallback)
861 861 > EOF
862 862 $ hg up -C 2
863 863 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
864 864 $ echo k > k
865 865 $ hg add k
866 866 $ hg commit -m commitK
867 867 $ echo l > l
868 868 $ hg add l
869 869 $ hg commit -m commitL
870 870 $ hg book -r tip blah
871 871 $ hg strip ".^" --config extensions.crash=$TESTTMP/stripstalephasecache.py
872 872 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
873 873 saved backup bundle to $TESTTMP/issue4736/.hg/strip-backup/8f0b4384875c-4fa10deb-backup.hg (glob)
874 874 $ hg up -C 1
875 875 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
876 876
877 877 Error during post-close callback of the strip transaction
878 878 (They should be gracefully handled and reported)
879 879
880 880 $ cat > ../crashstrip.py << EOF
881 881 > from mercurial import error
882 882 > def reposetup(ui, repo):
883 883 > class crashstriprepo(repo.__class__):
884 884 > def transaction(self, desc, *args, **kwargs):
885 885 > tr = super(crashstriprepo, self).transaction(self, desc, *args, **kwargs)
886 886 > if desc == 'strip':
887 887 > def crash(tra): raise error.Abort('boom')
888 888 > tr.addpostclose('crash', crash)
889 889 > return tr
890 890 > repo.__class__ = crashstriprepo
891 891 > EOF
892 892 $ hg strip tip --config extensions.crash=$TESTTMP/crashstrip.py
893 893 saved backup bundle to $TESTTMP/issue4736/.hg/strip-backup/5c51d8d6557d-70daef06-backup.hg (glob)
894 894 strip failed, full bundle stored in '$TESTTMP/issue4736/.hg/strip-backup/5c51d8d6557d-70daef06-backup.hg' (glob)
895 895 abort: boom
896 896 [255]
897 897
898 898
General Comments 0
You need to be logged in to leave comments. Login now