##// END OF EJS Templates
bundle2: convey PushkeyFailed error over the wire...
Pierre-Yves David -
r25493:d8e7b078 default
parent child Browse files
Show More
@@ -1,1394 +1,1406 b''
1 1 # bundle2.py - generic container format to transmit arbitrary data.
2 2 #
3 3 # Copyright 2013 Facebook, Inc.
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7 """Handling of the new bundle2 format
8 8
9 9 The goal of bundle2 is to act as an atomically packet to transmit a set of
10 10 payloads in an application agnostic way. It consist in a sequence of "parts"
11 11 that will be handed to and processed by the application layer.
12 12
13 13
14 14 General format architecture
15 15 ===========================
16 16
17 17 The format is architectured as follow
18 18
19 19 - magic string
20 20 - stream level parameters
21 21 - payload parts (any number)
22 22 - end of stream marker.
23 23
24 24 the Binary format
25 25 ============================
26 26
27 27 All numbers are unsigned and big-endian.
28 28
29 29 stream level parameters
30 30 ------------------------
31 31
32 32 Binary format is as follow
33 33
34 34 :params size: int32
35 35
36 36 The total number of Bytes used by the parameters
37 37
38 38 :params value: arbitrary number of Bytes
39 39
40 40 A blob of `params size` containing the serialized version of all stream level
41 41 parameters.
42 42
43 43 The blob contains a space separated list of parameters. Parameters with value
44 44 are stored in the form `<name>=<value>`. Both name and value are urlquoted.
45 45
46 46 Empty name are obviously forbidden.
47 47
48 48 Name MUST start with a letter. If this first letter is lower case, the
49 49 parameter is advisory and can be safely ignored. However when the first
50 50 letter is capital, the parameter is mandatory and the bundling process MUST
51 51 stop if he is not able to proceed it.
52 52
53 53 Stream parameters use a simple textual format for two main reasons:
54 54
55 55 - Stream level parameters should remain simple and we want to discourage any
56 56 crazy usage.
57 57 - Textual data allow easy human inspection of a bundle2 header in case of
58 58 troubles.
59 59
60 60 Any Applicative level options MUST go into a bundle2 part instead.
61 61
62 62 Payload part
63 63 ------------------------
64 64
65 65 Binary format is as follow
66 66
67 67 :header size: int32
68 68
69 69 The total number of Bytes used by the part headers. When the header is empty
70 70 (size = 0) this is interpreted as the end of stream marker.
71 71
72 72 :header:
73 73
74 74 The header defines how to interpret the part. It contains two piece of
75 75 data: the part type, and the part parameters.
76 76
77 77 The part type is used to route an application level handler, that can
78 78 interpret payload.
79 79
80 80 Part parameters are passed to the application level handler. They are
81 81 meant to convey information that will help the application level object to
82 82 interpret the part payload.
83 83
84 84 The binary format of the header is has follow
85 85
86 86 :typesize: (one byte)
87 87
88 88 :parttype: alphanumerical part name (restricted to [a-zA-Z0-9_:-]*)
89 89
90 90 :partid: A 32bits integer (unique in the bundle) that can be used to refer
91 91 to this part.
92 92
93 93 :parameters:
94 94
95 95 Part's parameter may have arbitrary content, the binary structure is::
96 96
97 97 <mandatory-count><advisory-count><param-sizes><param-data>
98 98
99 99 :mandatory-count: 1 byte, number of mandatory parameters
100 100
101 101 :advisory-count: 1 byte, number of advisory parameters
102 102
103 103 :param-sizes:
104 104
105 105 N couple of bytes, where N is the total number of parameters. Each
106 106 couple contains (<size-of-key>, <size-of-value) for one parameter.
107 107
108 108 :param-data:
109 109
110 110 A blob of bytes from which each parameter key and value can be
111 111 retrieved using the list of size couples stored in the previous
112 112 field.
113 113
114 114 Mandatory parameters comes first, then the advisory ones.
115 115
116 116 Each parameter's key MUST be unique within the part.
117 117
118 118 :payload:
119 119
120 120 payload is a series of `<chunksize><chunkdata>`.
121 121
122 122 `chunksize` is an int32, `chunkdata` are plain bytes (as much as
123 123 `chunksize` says)` The payload part is concluded by a zero size chunk.
124 124
125 125 The current implementation always produces either zero or one chunk.
126 126 This is an implementation limitation that will ultimately be lifted.
127 127
128 128 `chunksize` can be negative to trigger special case processing. No such
129 129 processing is in place yet.
130 130
131 131 Bundle processing
132 132 ============================
133 133
134 134 Each part is processed in order using a "part handler". Handler are registered
135 135 for a certain part type.
136 136
137 137 The matching of a part to its handler is case insensitive. The case of the
138 138 part type is used to know if a part is mandatory or advisory. If the Part type
139 139 contains any uppercase char it is considered mandatory. When no handler is
140 140 known for a Mandatory part, the process is aborted and an exception is raised.
141 141 If the part is advisory and no handler is known, the part is ignored. When the
142 142 process is aborted, the full bundle is still read from the stream to keep the
143 143 channel usable. But none of the part read from an abort are processed. In the
144 144 future, dropping the stream may become an option for channel we do not care to
145 145 preserve.
146 146 """
147 147
148 148 import errno
149 149 import sys
150 150 import util
151 151 import struct
152 152 import urllib
153 153 import string
154 154 import obsolete
155 155 import pushkey
156 156 import url
157 157 import re
158 158
159 159 import changegroup, error, tags
160 160 from i18n import _
161 161
162 162 _pack = struct.pack
163 163 _unpack = struct.unpack
164 164
165 165 _fstreamparamsize = '>i'
166 166 _fpartheadersize = '>i'
167 167 _fparttypesize = '>B'
168 168 _fpartid = '>I'
169 169 _fpayloadsize = '>i'
170 170 _fpartparamcount = '>BB'
171 171
172 172 preferedchunksize = 4096
173 173
174 174 _parttypeforbidden = re.compile('[^a-zA-Z0-9_:-]')
175 175
176 176 def outdebug(ui, message):
177 177 """debug regarding output stream (bundling)"""
178 178 if ui.configbool('devel', 'bundle2.debug', False):
179 179 ui.debug('bundle2-output: %s\n' % message)
180 180
181 181 def indebug(ui, message):
182 182 """debug on input stream (unbundling)"""
183 183 if ui.configbool('devel', 'bundle2.debug', False):
184 184 ui.debug('bundle2-input: %s\n' % message)
185 185
186 186 def validateparttype(parttype):
187 187 """raise ValueError if a parttype contains invalid character"""
188 188 if _parttypeforbidden.search(parttype):
189 189 raise ValueError(parttype)
190 190
191 191 def _makefpartparamsizes(nbparams):
192 192 """return a struct format to read part parameter sizes
193 193
194 194 The number parameters is variable so we need to build that format
195 195 dynamically.
196 196 """
197 197 return '>'+('BB'*nbparams)
198 198
199 199 parthandlermapping = {}
200 200
201 201 def parthandler(parttype, params=()):
202 202 """decorator that register a function as a bundle2 part handler
203 203
204 204 eg::
205 205
206 206 @parthandler('myparttype', ('mandatory', 'param', 'handled'))
207 207 def myparttypehandler(...):
208 208 '''process a part of type "my part".'''
209 209 ...
210 210 """
211 211 validateparttype(parttype)
212 212 def _decorator(func):
213 213 lparttype = parttype.lower() # enforce lower case matching.
214 214 assert lparttype not in parthandlermapping
215 215 parthandlermapping[lparttype] = func
216 216 func.params = frozenset(params)
217 217 return func
218 218 return _decorator
219 219
220 220 class unbundlerecords(object):
221 221 """keep record of what happens during and unbundle
222 222
223 223 New records are added using `records.add('cat', obj)`. Where 'cat' is a
224 224 category of record and obj is an arbitrary object.
225 225
226 226 `records['cat']` will return all entries of this category 'cat'.
227 227
228 228 Iterating on the object itself will yield `('category', obj)` tuples
229 229 for all entries.
230 230
231 231 All iterations happens in chronological order.
232 232 """
233 233
234 234 def __init__(self):
235 235 self._categories = {}
236 236 self._sequences = []
237 237 self._replies = {}
238 238
239 239 def add(self, category, entry, inreplyto=None):
240 240 """add a new record of a given category.
241 241
242 242 The entry can then be retrieved in the list returned by
243 243 self['category']."""
244 244 self._categories.setdefault(category, []).append(entry)
245 245 self._sequences.append((category, entry))
246 246 if inreplyto is not None:
247 247 self.getreplies(inreplyto).add(category, entry)
248 248
249 249 def getreplies(self, partid):
250 250 """get the records that are replies to a specific part"""
251 251 return self._replies.setdefault(partid, unbundlerecords())
252 252
253 253 def __getitem__(self, cat):
254 254 return tuple(self._categories.get(cat, ()))
255 255
256 256 def __iter__(self):
257 257 return iter(self._sequences)
258 258
259 259 def __len__(self):
260 260 return len(self._sequences)
261 261
262 262 def __nonzero__(self):
263 263 return bool(self._sequences)
264 264
265 265 class bundleoperation(object):
266 266 """an object that represents a single bundling process
267 267
268 268 Its purpose is to carry unbundle-related objects and states.
269 269
270 270 A new object should be created at the beginning of each bundle processing.
271 271 The object is to be returned by the processing function.
272 272
273 273 The object has very little content now it will ultimately contain:
274 274 * an access to the repo the bundle is applied to,
275 275 * a ui object,
276 276 * a way to retrieve a transaction to add changes to the repo,
277 277 * a way to record the result of processing each part,
278 278 * a way to construct a bundle response when applicable.
279 279 """
280 280
281 281 def __init__(self, repo, transactiongetter, captureoutput=True):
282 282 self.repo = repo
283 283 self.ui = repo.ui
284 284 self.records = unbundlerecords()
285 285 self.gettransaction = transactiongetter
286 286 self.reply = None
287 287 self.captureoutput = captureoutput
288 288
289 289 class TransactionUnavailable(RuntimeError):
290 290 pass
291 291
292 292 def _notransaction():
293 293 """default method to get a transaction while processing a bundle
294 294
295 295 Raise an exception to highlight the fact that no transaction was expected
296 296 to be created"""
297 297 raise TransactionUnavailable()
298 298
299 299 def processbundle(repo, unbundler, transactiongetter=None, op=None):
300 300 """This function process a bundle, apply effect to/from a repo
301 301
302 302 It iterates over each part then searches for and uses the proper handling
303 303 code to process the part. Parts are processed in order.
304 304
305 305 This is very early version of this function that will be strongly reworked
306 306 before final usage.
307 307
308 308 Unknown Mandatory part will abort the process.
309 309
310 310 It is temporarily possible to provide a prebuilt bundleoperation to the
311 311 function. This is used to ensure output is properly propagated in case of
312 312 an error during the unbundling. This output capturing part will likely be
313 313 reworked and this ability will probably go away in the process.
314 314 """
315 315 if op is None:
316 316 if transactiongetter is None:
317 317 transactiongetter = _notransaction
318 318 op = bundleoperation(repo, transactiongetter)
319 319 # todo:
320 320 # - replace this is a init function soon.
321 321 # - exception catching
322 322 unbundler.params
323 323 if repo.ui.debugflag:
324 324 msg = ['bundle2-input-bundle:']
325 325 if unbundler.params:
326 326 msg.append(' %i params')
327 327 if op.gettransaction is None:
328 328 msg.append(' no-transaction')
329 329 else:
330 330 msg.append(' with-transaction')
331 331 msg.append('\n')
332 332 repo.ui.debug(''.join(msg))
333 333 iterparts = enumerate(unbundler.iterparts())
334 334 part = None
335 335 nbpart = 0
336 336 try:
337 337 for nbpart, part in iterparts:
338 338 _processpart(op, part)
339 339 except BaseException, exc:
340 340 for nbpart, part in iterparts:
341 341 # consume the bundle content
342 342 part.seek(0, 2)
343 343 # Small hack to let caller code distinguish exceptions from bundle2
344 344 # processing from processing the old format. This is mostly
345 345 # needed to handle different return codes to unbundle according to the
346 346 # type of bundle. We should probably clean up or drop this return code
347 347 # craziness in a future version.
348 348 exc.duringunbundle2 = True
349 349 salvaged = []
350 350 replycaps = None
351 351 if op.reply is not None:
352 352 salvaged = op.reply.salvageoutput()
353 353 replycaps = op.reply.capabilities
354 354 exc._replycaps = replycaps
355 355 exc._bundle2salvagedoutput = salvaged
356 356 raise
357 357 finally:
358 358 repo.ui.debug('bundle2-input-bundle: %i parts total\n' % nbpart)
359 359
360 360 return op
361 361
362 362 def _processpart(op, part):
363 363 """process a single part from a bundle
364 364
365 365 The part is guaranteed to have been fully consumed when the function exits
366 366 (even if an exception is raised)."""
367 367 status = 'unknown' # used by debug output
368 368 try:
369 369 try:
370 370 handler = parthandlermapping.get(part.type)
371 371 if handler is None:
372 372 status = 'unsupported-type'
373 373 raise error.UnsupportedPartError(parttype=part.type)
374 374 indebug(op.ui, 'found a handler for part %r' % part.type)
375 375 unknownparams = part.mandatorykeys - handler.params
376 376 if unknownparams:
377 377 unknownparams = list(unknownparams)
378 378 unknownparams.sort()
379 379 status = 'unsupported-params (%s)' % unknownparams
380 380 raise error.UnsupportedPartError(parttype=part.type,
381 381 params=unknownparams)
382 382 status = 'supported'
383 383 except error.UnsupportedPartError, exc:
384 384 if part.mandatory: # mandatory parts
385 385 raise
386 386 indebug(op.ui, 'ignoring unsupported advisory part %s' % exc)
387 387 return # skip to part processing
388 388 finally:
389 389 if op.ui.debugflag:
390 390 msg = ['bundle2-input-part: "%s"' % part.type]
391 391 if not part.mandatory:
392 392 msg.append(' (advisory)')
393 393 nbmp = len(part.mandatorykeys)
394 394 nbap = len(part.params) - nbmp
395 395 if nbmp or nbap:
396 396 msg.append(' (params:')
397 397 if nbmp:
398 398 msg.append(' %i mandatory' % nbmp)
399 399 if nbap:
400 400 msg.append(' %i advisory' % nbmp)
401 401 msg.append(')')
402 402 msg.append(' %s\n' % status)
403 403 op.ui.debug(''.join(msg))
404 404
405 405 # handler is called outside the above try block so that we don't
406 406 # risk catching KeyErrors from anything other than the
407 407 # parthandlermapping lookup (any KeyError raised by handler()
408 408 # itself represents a defect of a different variety).
409 409 output = None
410 410 if op.captureoutput and op.reply is not None:
411 411 op.ui.pushbuffer(error=True, subproc=True)
412 412 output = ''
413 413 try:
414 414 handler(op, part)
415 415 finally:
416 416 if output is not None:
417 417 output = op.ui.popbuffer()
418 418 if output:
419 419 outpart = op.reply.newpart('output', data=output,
420 420 mandatory=False)
421 421 outpart.addparam('in-reply-to', str(part.id), mandatory=False)
422 422 finally:
423 423 # consume the part content to not corrupt the stream.
424 424 part.seek(0, 2)
425 425
426 426
427 427 def decodecaps(blob):
428 428 """decode a bundle2 caps bytes blob into a dictionary
429 429
430 430 The blob is a list of capabilities (one per line)
431 431 Capabilities may have values using a line of the form::
432 432
433 433 capability=value1,value2,value3
434 434
435 435 The values are always a list."""
436 436 caps = {}
437 437 for line in blob.splitlines():
438 438 if not line:
439 439 continue
440 440 if '=' not in line:
441 441 key, vals = line, ()
442 442 else:
443 443 key, vals = line.split('=', 1)
444 444 vals = vals.split(',')
445 445 key = urllib.unquote(key)
446 446 vals = [urllib.unquote(v) for v in vals]
447 447 caps[key] = vals
448 448 return caps
449 449
450 450 def encodecaps(caps):
451 451 """encode a bundle2 caps dictionary into a bytes blob"""
452 452 chunks = []
453 453 for ca in sorted(caps):
454 454 vals = caps[ca]
455 455 ca = urllib.quote(ca)
456 456 vals = [urllib.quote(v) for v in vals]
457 457 if vals:
458 458 ca = "%s=%s" % (ca, ','.join(vals))
459 459 chunks.append(ca)
460 460 return '\n'.join(chunks)
461 461
462 462 class bundle20(object):
463 463 """represent an outgoing bundle2 container
464 464
465 465 Use the `addparam` method to add stream level parameter. and `newpart` to
466 466 populate it. Then call `getchunks` to retrieve all the binary chunks of
467 467 data that compose the bundle2 container."""
468 468
469 469 _magicstring = 'HG20'
470 470
471 471 def __init__(self, ui, capabilities=()):
472 472 self.ui = ui
473 473 self._params = []
474 474 self._parts = []
475 475 self.capabilities = dict(capabilities)
476 476
477 477 @property
478 478 def nbparts(self):
479 479 """total number of parts added to the bundler"""
480 480 return len(self._parts)
481 481
482 482 # methods used to defines the bundle2 content
483 483 def addparam(self, name, value=None):
484 484 """add a stream level parameter"""
485 485 if not name:
486 486 raise ValueError('empty parameter name')
487 487 if name[0] not in string.letters:
488 488 raise ValueError('non letter first character: %r' % name)
489 489 self._params.append((name, value))
490 490
491 491 def addpart(self, part):
492 492 """add a new part to the bundle2 container
493 493
494 494 Parts contains the actual applicative payload."""
495 495 assert part.id is None
496 496 part.id = len(self._parts) # very cheap counter
497 497 self._parts.append(part)
498 498
499 499 def newpart(self, typeid, *args, **kwargs):
500 500 """create a new part and add it to the containers
501 501
502 502 As the part is directly added to the containers. For now, this means
503 503 that any failure to properly initialize the part after calling
504 504 ``newpart`` should result in a failure of the whole bundling process.
505 505
506 506 You can still fall back to manually create and add if you need better
507 507 control."""
508 508 part = bundlepart(typeid, *args, **kwargs)
509 509 self.addpart(part)
510 510 return part
511 511
512 512 # methods used to generate the bundle2 stream
513 513 def getchunks(self):
514 514 if self.ui.debugflag:
515 515 msg = ['bundle2-output-bundle: "%s",' % self._magicstring]
516 516 if self._params:
517 517 msg.append(' (%i params)' % len(self._params))
518 518 msg.append(' %i parts total\n' % len(self._parts))
519 519 self.ui.debug(''.join(msg))
520 520 outdebug(self.ui, 'start emission of %s stream' % self._magicstring)
521 521 yield self._magicstring
522 522 param = self._paramchunk()
523 523 outdebug(self.ui, 'bundle parameter: %s' % param)
524 524 yield _pack(_fstreamparamsize, len(param))
525 525 if param:
526 526 yield param
527 527
528 528 outdebug(self.ui, 'start of parts')
529 529 for part in self._parts:
530 530 outdebug(self.ui, 'bundle part: "%s"' % part.type)
531 531 for chunk in part.getchunks(ui=self.ui):
532 532 yield chunk
533 533 outdebug(self.ui, 'end of bundle')
534 534 yield _pack(_fpartheadersize, 0)
535 535
536 536 def _paramchunk(self):
537 537 """return a encoded version of all stream parameters"""
538 538 blocks = []
539 539 for par, value in self._params:
540 540 par = urllib.quote(par)
541 541 if value is not None:
542 542 value = urllib.quote(value)
543 543 par = '%s=%s' % (par, value)
544 544 blocks.append(par)
545 545 return ' '.join(blocks)
546 546
547 547 def salvageoutput(self):
548 548 """return a list with a copy of all output parts in the bundle
549 549
550 550 This is meant to be used during error handling to make sure we preserve
551 551 server output"""
552 552 salvaged = []
553 553 for part in self._parts:
554 554 if part.type.startswith('output'):
555 555 salvaged.append(part.copy())
556 556 return salvaged
557 557
558 558
559 559 class unpackermixin(object):
560 560 """A mixin to extract bytes and struct data from a stream"""
561 561
562 562 def __init__(self, fp):
563 563 self._fp = fp
564 564 self._seekable = (util.safehasattr(fp, 'seek') and
565 565 util.safehasattr(fp, 'tell'))
566 566
567 567 def _unpack(self, format):
568 568 """unpack this struct format from the stream"""
569 569 data = self._readexact(struct.calcsize(format))
570 570 return _unpack(format, data)
571 571
572 572 def _readexact(self, size):
573 573 """read exactly <size> bytes from the stream"""
574 574 return changegroup.readexactly(self._fp, size)
575 575
576 576 def seek(self, offset, whence=0):
577 577 """move the underlying file pointer"""
578 578 if self._seekable:
579 579 return self._fp.seek(offset, whence)
580 580 else:
581 581 raise NotImplementedError(_('File pointer is not seekable'))
582 582
583 583 def tell(self):
584 584 """return the file offset, or None if file is not seekable"""
585 585 if self._seekable:
586 586 try:
587 587 return self._fp.tell()
588 588 except IOError, e:
589 589 if e.errno == errno.ESPIPE:
590 590 self._seekable = False
591 591 else:
592 592 raise
593 593 return None
594 594
595 595 def close(self):
596 596 """close underlying file"""
597 597 if util.safehasattr(self._fp, 'close'):
598 598 return self._fp.close()
599 599
600 600 def getunbundler(ui, fp, header=None):
601 601 """return a valid unbundler object for a given header"""
602 602 if header is None:
603 603 header = changegroup.readexactly(fp, 4)
604 604 magic, version = header[0:2], header[2:4]
605 605 if magic != 'HG':
606 606 raise util.Abort(_('not a Mercurial bundle'))
607 607 unbundlerclass = formatmap.get(version)
608 608 if unbundlerclass is None:
609 609 raise util.Abort(_('unknown bundle version %s') % version)
610 610 unbundler = unbundlerclass(ui, fp)
611 611 indebug(ui, 'start processing of %s stream' % header)
612 612 return unbundler
613 613
614 614 class unbundle20(unpackermixin):
615 615 """interpret a bundle2 stream
616 616
617 617 This class is fed with a binary stream and yields parts through its
618 618 `iterparts` methods."""
619 619
620 620 def __init__(self, ui, fp):
621 621 """If header is specified, we do not read it out of the stream."""
622 622 self.ui = ui
623 623 super(unbundle20, self).__init__(fp)
624 624
625 625 @util.propertycache
626 626 def params(self):
627 627 """dictionary of stream level parameters"""
628 628 indebug(self.ui, 'reading bundle2 stream parameters')
629 629 params = {}
630 630 paramssize = self._unpack(_fstreamparamsize)[0]
631 631 if paramssize < 0:
632 632 raise error.BundleValueError('negative bundle param size: %i'
633 633 % paramssize)
634 634 if paramssize:
635 635 for p in self._readexact(paramssize).split(' '):
636 636 p = p.split('=', 1)
637 637 p = [urllib.unquote(i) for i in p]
638 638 if len(p) < 2:
639 639 p.append(None)
640 640 self._processparam(*p)
641 641 params[p[0]] = p[1]
642 642 return params
643 643
644 644 def _processparam(self, name, value):
645 645 """process a parameter, applying its effect if needed
646 646
647 647 Parameter starting with a lower case letter are advisory and will be
648 648 ignored when unknown. Those starting with an upper case letter are
649 649 mandatory and will this function will raise a KeyError when unknown.
650 650
651 651 Note: no option are currently supported. Any input will be either
652 652 ignored or failing.
653 653 """
654 654 if not name:
655 655 raise ValueError('empty parameter name')
656 656 if name[0] not in string.letters:
657 657 raise ValueError('non letter first character: %r' % name)
658 658 # Some logic will be later added here to try to process the option for
659 659 # a dict of known parameter.
660 660 if name[0].islower():
661 661 indebug(self.ui, "ignoring unknown parameter %r" % name)
662 662 else:
663 663 raise error.UnsupportedPartError(params=(name,))
664 664
665 665
666 666 def iterparts(self):
667 667 """yield all parts contained in the stream"""
668 668 # make sure param have been loaded
669 669 self.params
670 670 indebug(self.ui, 'start extraction of bundle2 parts')
671 671 headerblock = self._readpartheader()
672 672 while headerblock is not None:
673 673 part = unbundlepart(self.ui, headerblock, self._fp)
674 674 yield part
675 675 part.seek(0, 2)
676 676 headerblock = self._readpartheader()
677 677 indebug(self.ui, 'end of bundle2 stream')
678 678
679 679 def _readpartheader(self):
680 680 """reads a part header size and return the bytes blob
681 681
682 682 returns None if empty"""
683 683 headersize = self._unpack(_fpartheadersize)[0]
684 684 if headersize < 0:
685 685 raise error.BundleValueError('negative part header size: %i'
686 686 % headersize)
687 687 indebug(self.ui, 'part header size: %i' % headersize)
688 688 if headersize:
689 689 return self._readexact(headersize)
690 690 return None
691 691
692 692 def compressed(self):
693 693 return False
694 694
695 695 formatmap = {'20': unbundle20}
696 696
697 697 class bundlepart(object):
698 698 """A bundle2 part contains application level payload
699 699
700 700 The part `type` is used to route the part to the application level
701 701 handler.
702 702
703 703 The part payload is contained in ``part.data``. It could be raw bytes or a
704 704 generator of byte chunks.
705 705
706 706 You can add parameters to the part using the ``addparam`` method.
707 707 Parameters can be either mandatory (default) or advisory. Remote side
708 708 should be able to safely ignore the advisory ones.
709 709
710 710 Both data and parameters cannot be modified after the generation has begun.
711 711 """
712 712
713 713 def __init__(self, parttype, mandatoryparams=(), advisoryparams=(),
714 714 data='', mandatory=True):
715 715 validateparttype(parttype)
716 716 self.id = None
717 717 self.type = parttype
718 718 self._data = data
719 719 self._mandatoryparams = list(mandatoryparams)
720 720 self._advisoryparams = list(advisoryparams)
721 721 # checking for duplicated entries
722 722 self._seenparams = set()
723 723 for pname, __ in self._mandatoryparams + self._advisoryparams:
724 724 if pname in self._seenparams:
725 725 raise RuntimeError('duplicated params: %s' % pname)
726 726 self._seenparams.add(pname)
727 727 # status of the part's generation:
728 728 # - None: not started,
729 729 # - False: currently generated,
730 730 # - True: generation done.
731 731 self._generated = None
732 732 self.mandatory = mandatory
733 733
734 734 def copy(self):
735 735 """return a copy of the part
736 736
737 737 The new part have the very same content but no partid assigned yet.
738 738 Parts with generated data cannot be copied."""
739 739 assert not util.safehasattr(self.data, 'next')
740 740 return self.__class__(self.type, self._mandatoryparams,
741 741 self._advisoryparams, self._data, self.mandatory)
742 742
743 743 # methods used to defines the part content
744 744 def __setdata(self, data):
745 745 if self._generated is not None:
746 746 raise error.ReadOnlyPartError('part is being generated')
747 747 self._data = data
748 748 def __getdata(self):
749 749 return self._data
750 750 data = property(__getdata, __setdata)
751 751
752 752 @property
753 753 def mandatoryparams(self):
754 754 # make it an immutable tuple to force people through ``addparam``
755 755 return tuple(self._mandatoryparams)
756 756
757 757 @property
758 758 def advisoryparams(self):
759 759 # make it an immutable tuple to force people through ``addparam``
760 760 return tuple(self._advisoryparams)
761 761
762 762 def addparam(self, name, value='', mandatory=True):
763 763 if self._generated is not None:
764 764 raise error.ReadOnlyPartError('part is being generated')
765 765 if name in self._seenparams:
766 766 raise ValueError('duplicated params: %s' % name)
767 767 self._seenparams.add(name)
768 768 params = self._advisoryparams
769 769 if mandatory:
770 770 params = self._mandatoryparams
771 771 params.append((name, value))
772 772
773 773 # methods used to generates the bundle2 stream
774 774 def getchunks(self, ui):
775 775 if self._generated is not None:
776 776 raise RuntimeError('part can only be consumed once')
777 777 self._generated = False
778 778
779 779 if ui.debugflag:
780 780 msg = ['bundle2-output-part: "%s"' % self.type]
781 781 if not self.mandatory:
782 782 msg.append(' (advisory)')
783 783 nbmp = len(self.mandatoryparams)
784 784 nbap = len(self.advisoryparams)
785 785 if nbmp or nbap:
786 786 msg.append(' (params:')
787 787 if nbmp:
788 788 msg.append(' %i mandatory' % nbmp)
789 789 if nbap:
790 790 msg.append(' %i advisory' % nbmp)
791 791 msg.append(')')
792 792 if not self.data:
793 793 msg.append(' empty payload')
794 794 elif util.safehasattr(self.data, 'next'):
795 795 msg.append(' streamed payload')
796 796 else:
797 797 msg.append(' %i bytes payload' % len(self.data))
798 798 msg.append('\n')
799 799 ui.debug(''.join(msg))
800 800
801 801 #### header
802 802 if self.mandatory:
803 803 parttype = self.type.upper()
804 804 else:
805 805 parttype = self.type.lower()
806 806 outdebug(ui, 'part %s: "%s"' % (self.id, parttype))
807 807 ## parttype
808 808 header = [_pack(_fparttypesize, len(parttype)),
809 809 parttype, _pack(_fpartid, self.id),
810 810 ]
811 811 ## parameters
812 812 # count
813 813 manpar = self.mandatoryparams
814 814 advpar = self.advisoryparams
815 815 header.append(_pack(_fpartparamcount, len(manpar), len(advpar)))
816 816 # size
817 817 parsizes = []
818 818 for key, value in manpar:
819 819 parsizes.append(len(key))
820 820 parsizes.append(len(value))
821 821 for key, value in advpar:
822 822 parsizes.append(len(key))
823 823 parsizes.append(len(value))
824 824 paramsizes = _pack(_makefpartparamsizes(len(parsizes) / 2), *parsizes)
825 825 header.append(paramsizes)
826 826 # key, value
827 827 for key, value in manpar:
828 828 header.append(key)
829 829 header.append(value)
830 830 for key, value in advpar:
831 831 header.append(key)
832 832 header.append(value)
833 833 ## finalize header
834 834 headerchunk = ''.join(header)
835 835 outdebug(ui, 'header chunk size: %i' % len(headerchunk))
836 836 yield _pack(_fpartheadersize, len(headerchunk))
837 837 yield headerchunk
838 838 ## payload
839 839 try:
840 840 for chunk in self._payloadchunks():
841 841 outdebug(ui, 'payload chunk size: %i' % len(chunk))
842 842 yield _pack(_fpayloadsize, len(chunk))
843 843 yield chunk
844 844 except BaseException, exc:
845 845 # backup exception data for later
846 846 ui.debug('bundle2-input-stream-interrupt: encoding exception %s'
847 847 % exc)
848 848 exc_info = sys.exc_info()
849 849 msg = 'unexpected error: %s' % exc
850 850 interpart = bundlepart('error:abort', [('message', msg)],
851 851 mandatory=False)
852 852 interpart.id = 0
853 853 yield _pack(_fpayloadsize, -1)
854 854 for chunk in interpart.getchunks(ui=ui):
855 855 yield chunk
856 856 outdebug(ui, 'closing payload chunk')
857 857 # abort current part payload
858 858 yield _pack(_fpayloadsize, 0)
859 859 raise exc_info[0], exc_info[1], exc_info[2]
860 860 # end of payload
861 861 outdebug(ui, 'closing payload chunk')
862 862 yield _pack(_fpayloadsize, 0)
863 863 self._generated = True
864 864
865 865 def _payloadchunks(self):
866 866 """yield chunks of a the part payload
867 867
868 868 Exists to handle the different methods to provide data to a part."""
869 869 # we only support fixed size data now.
870 870 # This will be improved in the future.
871 871 if util.safehasattr(self.data, 'next'):
872 872 buff = util.chunkbuffer(self.data)
873 873 chunk = buff.read(preferedchunksize)
874 874 while chunk:
875 875 yield chunk
876 876 chunk = buff.read(preferedchunksize)
877 877 elif len(self.data):
878 878 yield self.data
879 879
880 880
881 881 flaginterrupt = -1
882 882
883 883 class interrupthandler(unpackermixin):
884 884 """read one part and process it with restricted capability
885 885
886 886 This allows to transmit exception raised on the producer size during part
887 887 iteration while the consumer is reading a part.
888 888
889 889 Part processed in this manner only have access to a ui object,"""
890 890
891 891 def __init__(self, ui, fp):
892 892 super(interrupthandler, self).__init__(fp)
893 893 self.ui = ui
894 894
895 895 def _readpartheader(self):
896 896 """reads a part header size and return the bytes blob
897 897
898 898 returns None if empty"""
899 899 headersize = self._unpack(_fpartheadersize)[0]
900 900 if headersize < 0:
901 901 raise error.BundleValueError('negative part header size: %i'
902 902 % headersize)
903 903 indebug(self.ui, 'part header size: %i\n' % headersize)
904 904 if headersize:
905 905 return self._readexact(headersize)
906 906 return None
907 907
908 908 def __call__(self):
909 909
910 910 self.ui.debug('bundle2-input-stream-interrupt:'
911 911 ' opening out of band context\n')
912 912 indebug(self.ui, 'bundle2 stream interruption, looking for a part.')
913 913 headerblock = self._readpartheader()
914 914 if headerblock is None:
915 915 indebug(self.ui, 'no part found during interruption.')
916 916 return
917 917 part = unbundlepart(self.ui, headerblock, self._fp)
918 918 op = interruptoperation(self.ui)
919 919 _processpart(op, part)
920 920 self.ui.debug('bundle2-input-stream-interrupt:'
921 921 ' closing out of band context\n')
922 922
923 923 class interruptoperation(object):
924 924 """A limited operation to be use by part handler during interruption
925 925
926 926 It only have access to an ui object.
927 927 """
928 928
929 929 def __init__(self, ui):
930 930 self.ui = ui
931 931 self.reply = None
932 932 self.captureoutput = False
933 933
934 934 @property
935 935 def repo(self):
936 936 raise RuntimeError('no repo access from stream interruption')
937 937
938 938 def gettransaction(self):
939 939 raise TransactionUnavailable('no repo access from stream interruption')
940 940
941 941 class unbundlepart(unpackermixin):
942 942 """a bundle part read from a bundle"""
943 943
944 944 def __init__(self, ui, header, fp):
945 945 super(unbundlepart, self).__init__(fp)
946 946 self.ui = ui
947 947 # unbundle state attr
948 948 self._headerdata = header
949 949 self._headeroffset = 0
950 950 self._initialized = False
951 951 self.consumed = False
952 952 # part data
953 953 self.id = None
954 954 self.type = None
955 955 self.mandatoryparams = None
956 956 self.advisoryparams = None
957 957 self.params = None
958 958 self.mandatorykeys = ()
959 959 self._payloadstream = None
960 960 self._readheader()
961 961 self._mandatory = None
962 962 self._chunkindex = [] #(payload, file) position tuples for chunk starts
963 963 self._pos = 0
964 964
965 965 def _fromheader(self, size):
966 966 """return the next <size> byte from the header"""
967 967 offset = self._headeroffset
968 968 data = self._headerdata[offset:(offset + size)]
969 969 self._headeroffset = offset + size
970 970 return data
971 971
972 972 def _unpackheader(self, format):
973 973 """read given format from header
974 974
975 975 This automatically compute the size of the format to read."""
976 976 data = self._fromheader(struct.calcsize(format))
977 977 return _unpack(format, data)
978 978
979 979 def _initparams(self, mandatoryparams, advisoryparams):
980 980 """internal function to setup all logic related parameters"""
981 981 # make it read only to prevent people touching it by mistake.
982 982 self.mandatoryparams = tuple(mandatoryparams)
983 983 self.advisoryparams = tuple(advisoryparams)
984 984 # user friendly UI
985 985 self.params = dict(self.mandatoryparams)
986 986 self.params.update(dict(self.advisoryparams))
987 987 self.mandatorykeys = frozenset(p[0] for p in mandatoryparams)
988 988
989 989 def _payloadchunks(self, chunknum=0):
990 990 '''seek to specified chunk and start yielding data'''
991 991 if len(self._chunkindex) == 0:
992 992 assert chunknum == 0, 'Must start with chunk 0'
993 993 self._chunkindex.append((0, super(unbundlepart, self).tell()))
994 994 else:
995 995 assert chunknum < len(self._chunkindex), \
996 996 'Unknown chunk %d' % chunknum
997 997 super(unbundlepart, self).seek(self._chunkindex[chunknum][1])
998 998
999 999 pos = self._chunkindex[chunknum][0]
1000 1000 payloadsize = self._unpack(_fpayloadsize)[0]
1001 1001 indebug(self.ui, 'payload chunk size: %i' % payloadsize)
1002 1002 while payloadsize:
1003 1003 if payloadsize == flaginterrupt:
1004 1004 # interruption detection, the handler will now read a
1005 1005 # single part and process it.
1006 1006 interrupthandler(self.ui, self._fp)()
1007 1007 elif payloadsize < 0:
1008 1008 msg = 'negative payload chunk size: %i' % payloadsize
1009 1009 raise error.BundleValueError(msg)
1010 1010 else:
1011 1011 result = self._readexact(payloadsize)
1012 1012 chunknum += 1
1013 1013 pos += payloadsize
1014 1014 if chunknum == len(self._chunkindex):
1015 1015 self._chunkindex.append((pos,
1016 1016 super(unbundlepart, self).tell()))
1017 1017 yield result
1018 1018 payloadsize = self._unpack(_fpayloadsize)[0]
1019 1019 indebug(self.ui, 'payload chunk size: %i' % payloadsize)
1020 1020
1021 1021 def _findchunk(self, pos):
1022 1022 '''for a given payload position, return a chunk number and offset'''
1023 1023 for chunk, (ppos, fpos) in enumerate(self._chunkindex):
1024 1024 if ppos == pos:
1025 1025 return chunk, 0
1026 1026 elif ppos > pos:
1027 1027 return chunk - 1, pos - self._chunkindex[chunk - 1][0]
1028 1028 raise ValueError('Unknown chunk')
1029 1029
1030 1030 def _readheader(self):
1031 1031 """read the header and setup the object"""
1032 1032 typesize = self._unpackheader(_fparttypesize)[0]
1033 1033 self.type = self._fromheader(typesize)
1034 1034 indebug(self.ui, 'part type: "%s"' % self.type)
1035 1035 self.id = self._unpackheader(_fpartid)[0]
1036 1036 indebug(self.ui, 'part id: "%s"' % self.id)
1037 1037 # extract mandatory bit from type
1038 1038 self.mandatory = (self.type != self.type.lower())
1039 1039 self.type = self.type.lower()
1040 1040 ## reading parameters
1041 1041 # param count
1042 1042 mancount, advcount = self._unpackheader(_fpartparamcount)
1043 1043 indebug(self.ui, 'part parameters: %i' % (mancount + advcount))
1044 1044 # param size
1045 1045 fparamsizes = _makefpartparamsizes(mancount + advcount)
1046 1046 paramsizes = self._unpackheader(fparamsizes)
1047 1047 # make it a list of couple again
1048 1048 paramsizes = zip(paramsizes[::2], paramsizes[1::2])
1049 1049 # split mandatory from advisory
1050 1050 mansizes = paramsizes[:mancount]
1051 1051 advsizes = paramsizes[mancount:]
1052 1052 # retrieve param value
1053 1053 manparams = []
1054 1054 for key, value in mansizes:
1055 1055 manparams.append((self._fromheader(key), self._fromheader(value)))
1056 1056 advparams = []
1057 1057 for key, value in advsizes:
1058 1058 advparams.append((self._fromheader(key), self._fromheader(value)))
1059 1059 self._initparams(manparams, advparams)
1060 1060 ## part payload
1061 1061 self._payloadstream = util.chunkbuffer(self._payloadchunks())
1062 1062 # we read the data, tell it
1063 1063 self._initialized = True
1064 1064
1065 1065 def read(self, size=None):
1066 1066 """read payload data"""
1067 1067 if not self._initialized:
1068 1068 self._readheader()
1069 1069 if size is None:
1070 1070 data = self._payloadstream.read()
1071 1071 else:
1072 1072 data = self._payloadstream.read(size)
1073 1073 self._pos += len(data)
1074 1074 if size is None or len(data) < size:
1075 1075 if not self.consumed and self._pos:
1076 1076 self.ui.debug('bundle2-input-part: total payload size %i\n'
1077 1077 % self._pos)
1078 1078 self.consumed = True
1079 1079 return data
1080 1080
1081 1081 def tell(self):
1082 1082 return self._pos
1083 1083
1084 1084 def seek(self, offset, whence=0):
1085 1085 if whence == 0:
1086 1086 newpos = offset
1087 1087 elif whence == 1:
1088 1088 newpos = self._pos + offset
1089 1089 elif whence == 2:
1090 1090 if not self.consumed:
1091 1091 self.read()
1092 1092 newpos = self._chunkindex[-1][0] - offset
1093 1093 else:
1094 1094 raise ValueError('Unknown whence value: %r' % (whence,))
1095 1095
1096 1096 if newpos > self._chunkindex[-1][0] and not self.consumed:
1097 1097 self.read()
1098 1098 if not 0 <= newpos <= self._chunkindex[-1][0]:
1099 1099 raise ValueError('Offset out of range')
1100 1100
1101 1101 if self._pos != newpos:
1102 1102 chunk, internaloffset = self._findchunk(newpos)
1103 1103 self._payloadstream = util.chunkbuffer(self._payloadchunks(chunk))
1104 1104 adjust = self.read(internaloffset)
1105 1105 if len(adjust) != internaloffset:
1106 1106 raise util.Abort(_('Seek failed\n'))
1107 1107 self._pos = newpos
1108 1108
1109 1109 # These are only the static capabilities.
1110 1110 # Check the 'getrepocaps' function for the rest.
1111 1111 capabilities = {'HG20': (),
1112 'error': ('abort', 'unsupportedcontent', 'pushraced'),
1112 'error': ('abort', 'unsupportedcontent', 'pushraced',
1113 'pushkey'),
1113 1114 'listkeys': (),
1114 1115 'pushkey': (),
1115 1116 'digests': tuple(sorted(util.DIGESTS.keys())),
1116 1117 'remote-changegroup': ('http', 'https'),
1117 1118 'hgtagsfnodes': (),
1118 1119 }
1119 1120
1120 1121 def getrepocaps(repo, allowpushback=False):
1121 1122 """return the bundle2 capabilities for a given repo
1122 1123
1123 1124 Exists to allow extensions (like evolution) to mutate the capabilities.
1124 1125 """
1125 1126 caps = capabilities.copy()
1126 1127 caps['changegroup'] = tuple(sorted(changegroup.packermap.keys()))
1127 1128 if obsolete.isenabled(repo, obsolete.exchangeopt):
1128 1129 supportedformat = tuple('V%i' % v for v in obsolete.formats)
1129 1130 caps['obsmarkers'] = supportedformat
1130 1131 if allowpushback:
1131 1132 caps['pushback'] = ()
1132 1133 return caps
1133 1134
1134 1135 def bundle2caps(remote):
1135 1136 """return the bundle capabilities of a peer as dict"""
1136 1137 raw = remote.capable('bundle2')
1137 1138 if not raw and raw != '':
1138 1139 return {}
1139 1140 capsblob = urllib.unquote(remote.capable('bundle2'))
1140 1141 return decodecaps(capsblob)
1141 1142
1142 1143 def obsmarkersversion(caps):
1143 1144 """extract the list of supported obsmarkers versions from a bundle2caps dict
1144 1145 """
1145 1146 obscaps = caps.get('obsmarkers', ())
1146 1147 return [int(c[1:]) for c in obscaps if c.startswith('V')]
1147 1148
1148 1149 @parthandler('changegroup', ('version',))
1149 1150 def handlechangegroup(op, inpart):
1150 1151 """apply a changegroup part on the repo
1151 1152
1152 1153 This is a very early implementation that will massive rework before being
1153 1154 inflicted to any end-user.
1154 1155 """
1155 1156 # Make sure we trigger a transaction creation
1156 1157 #
1157 1158 # The addchangegroup function will get a transaction object by itself, but
1158 1159 # we need to make sure we trigger the creation of a transaction object used
1159 1160 # for the whole processing scope.
1160 1161 op.gettransaction()
1161 1162 unpackerversion = inpart.params.get('version', '01')
1162 1163 # We should raise an appropriate exception here
1163 1164 unpacker = changegroup.packermap[unpackerversion][1]
1164 1165 cg = unpacker(inpart, 'UN')
1165 1166 # the source and url passed here are overwritten by the one contained in
1166 1167 # the transaction.hookargs argument. So 'bundle2' is a placeholder
1167 1168 ret = changegroup.addchangegroup(op.repo, cg, 'bundle2', 'bundle2')
1168 1169 op.records.add('changegroup', {'return': ret})
1169 1170 if op.reply is not None:
1170 1171 # This is definitely not the final form of this
1171 1172 # return. But one need to start somewhere.
1172 1173 part = op.reply.newpart('reply:changegroup', mandatory=False)
1173 1174 part.addparam('in-reply-to', str(inpart.id), mandatory=False)
1174 1175 part.addparam('return', '%i' % ret, mandatory=False)
1175 1176 assert not inpart.read()
1176 1177
1177 1178 _remotechangegroupparams = tuple(['url', 'size', 'digests'] +
1178 1179 ['digest:%s' % k for k in util.DIGESTS.keys()])
1179 1180 @parthandler('remote-changegroup', _remotechangegroupparams)
1180 1181 def handleremotechangegroup(op, inpart):
1181 1182 """apply a bundle10 on the repo, given an url and validation information
1182 1183
1183 1184 All the information about the remote bundle to import are given as
1184 1185 parameters. The parameters include:
1185 1186 - url: the url to the bundle10.
1186 1187 - size: the bundle10 file size. It is used to validate what was
1187 1188 retrieved by the client matches the server knowledge about the bundle.
1188 1189 - digests: a space separated list of the digest types provided as
1189 1190 parameters.
1190 1191 - digest:<digest-type>: the hexadecimal representation of the digest with
1191 1192 that name. Like the size, it is used to validate what was retrieved by
1192 1193 the client matches what the server knows about the bundle.
1193 1194
1194 1195 When multiple digest types are given, all of them are checked.
1195 1196 """
1196 1197 try:
1197 1198 raw_url = inpart.params['url']
1198 1199 except KeyError:
1199 1200 raise util.Abort(_('remote-changegroup: missing "%s" param') % 'url')
1200 1201 parsed_url = util.url(raw_url)
1201 1202 if parsed_url.scheme not in capabilities['remote-changegroup']:
1202 1203 raise util.Abort(_('remote-changegroup does not support %s urls') %
1203 1204 parsed_url.scheme)
1204 1205
1205 1206 try:
1206 1207 size = int(inpart.params['size'])
1207 1208 except ValueError:
1208 1209 raise util.Abort(_('remote-changegroup: invalid value for param "%s"')
1209 1210 % 'size')
1210 1211 except KeyError:
1211 1212 raise util.Abort(_('remote-changegroup: missing "%s" param') % 'size')
1212 1213
1213 1214 digests = {}
1214 1215 for typ in inpart.params.get('digests', '').split():
1215 1216 param = 'digest:%s' % typ
1216 1217 try:
1217 1218 value = inpart.params[param]
1218 1219 except KeyError:
1219 1220 raise util.Abort(_('remote-changegroup: missing "%s" param') %
1220 1221 param)
1221 1222 digests[typ] = value
1222 1223
1223 1224 real_part = util.digestchecker(url.open(op.ui, raw_url), size, digests)
1224 1225
1225 1226 # Make sure we trigger a transaction creation
1226 1227 #
1227 1228 # The addchangegroup function will get a transaction object by itself, but
1228 1229 # we need to make sure we trigger the creation of a transaction object used
1229 1230 # for the whole processing scope.
1230 1231 op.gettransaction()
1231 1232 import exchange
1232 1233 cg = exchange.readbundle(op.repo.ui, real_part, raw_url)
1233 1234 if not isinstance(cg, changegroup.cg1unpacker):
1234 1235 raise util.Abort(_('%s: not a bundle version 1.0') %
1235 1236 util.hidepassword(raw_url))
1236 1237 ret = changegroup.addchangegroup(op.repo, cg, 'bundle2', 'bundle2')
1237 1238 op.records.add('changegroup', {'return': ret})
1238 1239 if op.reply is not None:
1239 1240 # This is definitely not the final form of this
1240 1241 # return. But one need to start somewhere.
1241 1242 part = op.reply.newpart('reply:changegroup')
1242 1243 part.addparam('in-reply-to', str(inpart.id), mandatory=False)
1243 1244 part.addparam('return', '%i' % ret, mandatory=False)
1244 1245 try:
1245 1246 real_part.validate()
1246 1247 except util.Abort, e:
1247 1248 raise util.Abort(_('bundle at %s is corrupted:\n%s') %
1248 1249 (util.hidepassword(raw_url), str(e)))
1249 1250 assert not inpart.read()
1250 1251
1251 1252 @parthandler('reply:changegroup', ('return', 'in-reply-to'))
1252 1253 def handlereplychangegroup(op, inpart):
1253 1254 ret = int(inpart.params['return'])
1254 1255 replyto = int(inpart.params['in-reply-to'])
1255 1256 op.records.add('changegroup', {'return': ret}, replyto)
1256 1257
1257 1258 @parthandler('check:heads')
1258 1259 def handlecheckheads(op, inpart):
1259 1260 """check that head of the repo did not change
1260 1261
1261 1262 This is used to detect a push race when using unbundle.
1262 1263 This replaces the "heads" argument of unbundle."""
1263 1264 h = inpart.read(20)
1264 1265 heads = []
1265 1266 while len(h) == 20:
1266 1267 heads.append(h)
1267 1268 h = inpart.read(20)
1268 1269 assert not h
1269 1270 if heads != op.repo.heads():
1270 1271 raise error.PushRaced('repository changed while pushing - '
1271 1272 'please try again')
1272 1273
1273 1274 @parthandler('output')
1274 1275 def handleoutput(op, inpart):
1275 1276 """forward output captured on the server to the client"""
1276 1277 for line in inpart.read().splitlines():
1277 1278 op.ui.status(('remote: %s\n' % line))
1278 1279
1279 1280 @parthandler('replycaps')
1280 1281 def handlereplycaps(op, inpart):
1281 1282 """Notify that a reply bundle should be created
1282 1283
1283 1284 The payload contains the capabilities information for the reply"""
1284 1285 caps = decodecaps(inpart.read())
1285 1286 if op.reply is None:
1286 1287 op.reply = bundle20(op.ui, caps)
1287 1288
1288 1289 @parthandler('error:abort', ('message', 'hint'))
1289 1290 def handleerrorabort(op, inpart):
1290 1291 """Used to transmit abort error over the wire"""
1291 1292 raise util.Abort(inpart.params['message'], hint=inpart.params.get('hint'))
1292 1293
1294 @parthandler('error:pushkey', ('namespace', 'key', 'new', 'old', 'ret',
1295 'in-reply-to'))
1296 def handleerrorpushkey(op, inpart):
1297 """Used to transmit failure of a mandatory pushkey over the wire"""
1298 kwargs = {}
1299 for name in ('namespace', 'key', 'new', 'old', 'ret'):
1300 value = inpart.params.get(name)
1301 if value is not None:
1302 kwargs[name] = value
1303 raise error.PushkeyFailed(inpart.params['in-reply-to'], **kwargs)
1304
1293 1305 @parthandler('error:unsupportedcontent', ('parttype', 'params'))
1294 1306 def handleerrorunsupportedcontent(op, inpart):
1295 1307 """Used to transmit unknown content error over the wire"""
1296 1308 kwargs = {}
1297 1309 parttype = inpart.params.get('parttype')
1298 1310 if parttype is not None:
1299 1311 kwargs['parttype'] = parttype
1300 1312 params = inpart.params.get('params')
1301 1313 if params is not None:
1302 1314 kwargs['params'] = params.split('\0')
1303 1315
1304 1316 raise error.UnsupportedPartError(**kwargs)
1305 1317
1306 1318 @parthandler('error:pushraced', ('message',))
1307 1319 def handleerrorpushraced(op, inpart):
1308 1320 """Used to transmit push race error over the wire"""
1309 1321 raise error.ResponseError(_('push failed:'), inpart.params['message'])
1310 1322
1311 1323 @parthandler('listkeys', ('namespace',))
1312 1324 def handlelistkeys(op, inpart):
1313 1325 """retrieve pushkey namespace content stored in a bundle2"""
1314 1326 namespace = inpart.params['namespace']
1315 1327 r = pushkey.decodekeys(inpart.read())
1316 1328 op.records.add('listkeys', (namespace, r))
1317 1329
1318 1330 @parthandler('pushkey', ('namespace', 'key', 'old', 'new'))
1319 1331 def handlepushkey(op, inpart):
1320 1332 """process a pushkey request"""
1321 1333 dec = pushkey.decode
1322 1334 namespace = dec(inpart.params['namespace'])
1323 1335 key = dec(inpart.params['key'])
1324 1336 old = dec(inpart.params['old'])
1325 1337 new = dec(inpart.params['new'])
1326 1338 ret = op.repo.pushkey(namespace, key, old, new)
1327 1339 record = {'namespace': namespace,
1328 1340 'key': key,
1329 1341 'old': old,
1330 1342 'new': new}
1331 1343 op.records.add('pushkey', record)
1332 1344 if op.reply is not None:
1333 1345 rpart = op.reply.newpart('reply:pushkey')
1334 1346 rpart.addparam('in-reply-to', str(inpart.id), mandatory=False)
1335 1347 rpart.addparam('return', '%i' % ret, mandatory=False)
1336 1348 if inpart.mandatory and not ret:
1337 1349 kwargs = {}
1338 1350 for key in ('namespace', 'key', 'new', 'old', 'ret'):
1339 1351 if key in inpart.params:
1340 1352 kwargs[key] = inpart.params[key]
1341 1353 raise error.PushkeyFailed(partid=str(inpart.id), **kwargs)
1342 1354
1343 1355 @parthandler('reply:pushkey', ('return', 'in-reply-to'))
1344 1356 def handlepushkeyreply(op, inpart):
1345 1357 """retrieve the result of a pushkey request"""
1346 1358 ret = int(inpart.params['return'])
1347 1359 partid = int(inpart.params['in-reply-to'])
1348 1360 op.records.add('pushkey', {'return': ret}, partid)
1349 1361
1350 1362 @parthandler('obsmarkers')
1351 1363 def handleobsmarker(op, inpart):
1352 1364 """add a stream of obsmarkers to the repo"""
1353 1365 tr = op.gettransaction()
1354 1366 markerdata = inpart.read()
1355 1367 if op.ui.config('experimental', 'obsmarkers-exchange-debug', False):
1356 1368 op.ui.write(('obsmarker-exchange: %i bytes received\n')
1357 1369 % len(markerdata))
1358 1370 new = op.repo.obsstore.mergemarkers(tr, markerdata)
1359 1371 if new:
1360 1372 op.repo.ui.status(_('%i new obsolescence markers\n') % new)
1361 1373 op.records.add('obsmarkers', {'new': new})
1362 1374 if op.reply is not None:
1363 1375 rpart = op.reply.newpart('reply:obsmarkers')
1364 1376 rpart.addparam('in-reply-to', str(inpart.id), mandatory=False)
1365 1377 rpart.addparam('new', '%i' % new, mandatory=False)
1366 1378
1367 1379
1368 1380 @parthandler('reply:obsmarkers', ('new', 'in-reply-to'))
1369 1381 def handlepushkeyreply(op, inpart):
1370 1382 """retrieve the result of a pushkey request"""
1371 1383 ret = int(inpart.params['new'])
1372 1384 partid = int(inpart.params['in-reply-to'])
1373 1385 op.records.add('obsmarkers', {'new': ret}, partid)
1374 1386
1375 1387 @parthandler('hgtagsfnodes')
1376 1388 def handlehgtagsfnodes(op, inpart):
1377 1389 """Applies .hgtags fnodes cache entries to the local repo.
1378 1390
1379 1391 Payload is pairs of 20 byte changeset nodes and filenodes.
1380 1392 """
1381 1393 cache = tags.hgtagsfnodescache(op.repo.unfiltered())
1382 1394
1383 1395 count = 0
1384 1396 while True:
1385 1397 node = inpart.read(20)
1386 1398 fnode = inpart.read(20)
1387 1399 if len(node) < 20 or len(fnode) < 20:
1388 1400 op.ui.debug('received incomplete .hgtags fnodes data, ignoring\n')
1389 1401 break
1390 1402 cache.setfnode(node, fnode)
1391 1403 count += 1
1392 1404
1393 1405 cache.write()
1394 1406 op.ui.debug('applied %i hgtags fnodes cache entries\n' % count)
@@ -1,838 +1,858 b''
1 1 # wireproto.py - generic wire protocol support functions
2 2 #
3 3 # Copyright 2005-2010 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 import urllib, tempfile, os, sys
9 9 from i18n import _
10 10 from node import bin, hex
11 11 import changegroup as changegroupmod, bundle2, pushkey as pushkeymod
12 12 import peer, error, encoding, util, exchange
13 13
14 14
15 15 class abstractserverproto(object):
16 16 """abstract class that summarizes the protocol API
17 17
18 18 Used as reference and documentation.
19 19 """
20 20
21 21 def getargs(self, args):
22 22 """return the value for arguments in <args>
23 23
24 24 returns a list of values (same order as <args>)"""
25 25 raise NotImplementedError()
26 26
27 27 def getfile(self, fp):
28 28 """write the whole content of a file into a file like object
29 29
30 30 The file is in the form::
31 31
32 32 (<chunk-size>\n<chunk>)+0\n
33 33
34 34 chunk size is the ascii version of the int.
35 35 """
36 36 raise NotImplementedError()
37 37
38 38 def redirect(self):
39 39 """may setup interception for stdout and stderr
40 40
41 41 See also the `restore` method."""
42 42 raise NotImplementedError()
43 43
44 44 # If the `redirect` function does install interception, the `restore`
45 45 # function MUST be defined. If interception is not used, this function
46 46 # MUST NOT be defined.
47 47 #
48 48 # left commented here on purpose
49 49 #
50 50 #def restore(self):
51 51 # """reinstall previous stdout and stderr and return intercepted stdout
52 52 # """
53 53 # raise NotImplementedError()
54 54
55 55 def groupchunks(self, cg):
56 56 """return 4096 chunks from a changegroup object
57 57
58 58 Some protocols may have compressed the contents."""
59 59 raise NotImplementedError()
60 60
61 61 # abstract batching support
62 62
63 63 class future(object):
64 64 '''placeholder for a value to be set later'''
65 65 def set(self, value):
66 66 if util.safehasattr(self, 'value'):
67 67 raise error.RepoError("future is already set")
68 68 self.value = value
69 69
70 70 class batcher(object):
71 71 '''base class for batches of commands submittable in a single request
72 72
73 73 All methods invoked on instances of this class are simply queued and
74 74 return a a future for the result. Once you call submit(), all the queued
75 75 calls are performed and the results set in their respective futures.
76 76 '''
77 77 def __init__(self):
78 78 self.calls = []
79 79 def __getattr__(self, name):
80 80 def call(*args, **opts):
81 81 resref = future()
82 82 self.calls.append((name, args, opts, resref,))
83 83 return resref
84 84 return call
85 85 def submit(self):
86 86 pass
87 87
88 88 class localbatch(batcher):
89 89 '''performs the queued calls directly'''
90 90 def __init__(self, local):
91 91 batcher.__init__(self)
92 92 self.local = local
93 93 def submit(self):
94 94 for name, args, opts, resref in self.calls:
95 95 resref.set(getattr(self.local, name)(*args, **opts))
96 96
97 97 class remotebatch(batcher):
98 98 '''batches the queued calls; uses as few roundtrips as possible'''
99 99 def __init__(self, remote):
100 100 '''remote must support _submitbatch(encbatch) and
101 101 _submitone(op, encargs)'''
102 102 batcher.__init__(self)
103 103 self.remote = remote
104 104 def submit(self):
105 105 req, rsp = [], []
106 106 for name, args, opts, resref in self.calls:
107 107 mtd = getattr(self.remote, name)
108 108 batchablefn = getattr(mtd, 'batchable', None)
109 109 if batchablefn is not None:
110 110 batchable = batchablefn(mtd.im_self, *args, **opts)
111 111 encargsorres, encresref = batchable.next()
112 112 if encresref:
113 113 req.append((name, encargsorres,))
114 114 rsp.append((batchable, encresref, resref,))
115 115 else:
116 116 resref.set(encargsorres)
117 117 else:
118 118 if req:
119 119 self._submitreq(req, rsp)
120 120 req, rsp = [], []
121 121 resref.set(mtd(*args, **opts))
122 122 if req:
123 123 self._submitreq(req, rsp)
124 124 def _submitreq(self, req, rsp):
125 125 encresults = self.remote._submitbatch(req)
126 126 for encres, r in zip(encresults, rsp):
127 127 batchable, encresref, resref = r
128 128 encresref.set(encres)
129 129 resref.set(batchable.next())
130 130
131 131 def batchable(f):
132 132 '''annotation for batchable methods
133 133
134 134 Such methods must implement a coroutine as follows:
135 135
136 136 @batchable
137 137 def sample(self, one, two=None):
138 138 # Handle locally computable results first:
139 139 if not one:
140 140 yield "a local result", None
141 141 # Build list of encoded arguments suitable for your wire protocol:
142 142 encargs = [('one', encode(one),), ('two', encode(two),)]
143 143 # Create future for injection of encoded result:
144 144 encresref = future()
145 145 # Return encoded arguments and future:
146 146 yield encargs, encresref
147 147 # Assuming the future to be filled with the result from the batched
148 148 # request now. Decode it:
149 149 yield decode(encresref.value)
150 150
151 151 The decorator returns a function which wraps this coroutine as a plain
152 152 method, but adds the original method as an attribute called "batchable",
153 153 which is used by remotebatch to split the call into separate encoding and
154 154 decoding phases.
155 155 '''
156 156 def plain(*args, **opts):
157 157 batchable = f(*args, **opts)
158 158 encargsorres, encresref = batchable.next()
159 159 if not encresref:
160 160 return encargsorres # a local result in this case
161 161 self = args[0]
162 162 encresref.set(self._submitone(f.func_name, encargsorres))
163 163 return batchable.next()
164 164 setattr(plain, 'batchable', f)
165 165 return plain
166 166
167 167 # list of nodes encoding / decoding
168 168
169 169 def decodelist(l, sep=' '):
170 170 if l:
171 171 return map(bin, l.split(sep))
172 172 return []
173 173
174 174 def encodelist(l, sep=' '):
175 175 try:
176 176 return sep.join(map(hex, l))
177 177 except TypeError:
178 178 print l
179 179 raise
180 180
181 181 # batched call argument encoding
182 182
183 183 def escapearg(plain):
184 184 return (plain
185 185 .replace(':', '::')
186 186 .replace(',', ':,')
187 187 .replace(';', ':;')
188 188 .replace('=', ':='))
189 189
190 190 def unescapearg(escaped):
191 191 return (escaped
192 192 .replace(':=', '=')
193 193 .replace(':;', ';')
194 194 .replace(':,', ',')
195 195 .replace('::', ':'))
196 196
197 197 # mapping of options accepted by getbundle and their types
198 198 #
199 199 # Meant to be extended by extensions. It is extensions responsibility to ensure
200 200 # such options are properly processed in exchange.getbundle.
201 201 #
202 202 # supported types are:
203 203 #
204 204 # :nodes: list of binary nodes
205 205 # :csv: list of comma-separated values
206 206 # :scsv: list of comma-separated values return as set
207 207 # :plain: string with no transformation needed.
208 208 gboptsmap = {'heads': 'nodes',
209 209 'common': 'nodes',
210 210 'obsmarkers': 'boolean',
211 211 'bundlecaps': 'scsv',
212 212 'listkeys': 'csv',
213 213 'cg': 'boolean'}
214 214
215 215 # client side
216 216
217 217 class wirepeer(peer.peerrepository):
218 218
219 219 def batch(self):
220 220 return remotebatch(self)
221 221 def _submitbatch(self, req):
222 222 cmds = []
223 223 for op, argsdict in req:
224 224 args = ','.join('%s=%s' % p for p in argsdict.iteritems())
225 225 cmds.append('%s %s' % (op, args))
226 226 rsp = self._call("batch", cmds=';'.join(cmds))
227 227 return rsp.split(';')
228 228 def _submitone(self, op, args):
229 229 return self._call(op, **args)
230 230
231 231 @batchable
232 232 def lookup(self, key):
233 233 self.requirecap('lookup', _('look up remote revision'))
234 234 f = future()
235 235 yield {'key': encoding.fromlocal(key)}, f
236 236 d = f.value
237 237 success, data = d[:-1].split(" ", 1)
238 238 if int(success):
239 239 yield bin(data)
240 240 self._abort(error.RepoError(data))
241 241
242 242 @batchable
243 243 def heads(self):
244 244 f = future()
245 245 yield {}, f
246 246 d = f.value
247 247 try:
248 248 yield decodelist(d[:-1])
249 249 except ValueError:
250 250 self._abort(error.ResponseError(_("unexpected response:"), d))
251 251
252 252 @batchable
253 253 def known(self, nodes):
254 254 f = future()
255 255 yield {'nodes': encodelist(nodes)}, f
256 256 d = f.value
257 257 try:
258 258 yield [bool(int(b)) for b in d]
259 259 except ValueError:
260 260 self._abort(error.ResponseError(_("unexpected response:"), d))
261 261
262 262 @batchable
263 263 def branchmap(self):
264 264 f = future()
265 265 yield {}, f
266 266 d = f.value
267 267 try:
268 268 branchmap = {}
269 269 for branchpart in d.splitlines():
270 270 branchname, branchheads = branchpart.split(' ', 1)
271 271 branchname = encoding.tolocal(urllib.unquote(branchname))
272 272 branchheads = decodelist(branchheads)
273 273 branchmap[branchname] = branchheads
274 274 yield branchmap
275 275 except TypeError:
276 276 self._abort(error.ResponseError(_("unexpected response:"), d))
277 277
278 278 def branches(self, nodes):
279 279 n = encodelist(nodes)
280 280 d = self._call("branches", nodes=n)
281 281 try:
282 282 br = [tuple(decodelist(b)) for b in d.splitlines()]
283 283 return br
284 284 except ValueError:
285 285 self._abort(error.ResponseError(_("unexpected response:"), d))
286 286
287 287 def between(self, pairs):
288 288 batch = 8 # avoid giant requests
289 289 r = []
290 290 for i in xrange(0, len(pairs), batch):
291 291 n = " ".join([encodelist(p, '-') for p in pairs[i:i + batch]])
292 292 d = self._call("between", pairs=n)
293 293 try:
294 294 r.extend(l and decodelist(l) or [] for l in d.splitlines())
295 295 except ValueError:
296 296 self._abort(error.ResponseError(_("unexpected response:"), d))
297 297 return r
298 298
299 299 @batchable
300 300 def pushkey(self, namespace, key, old, new):
301 301 if not self.capable('pushkey'):
302 302 yield False, None
303 303 f = future()
304 304 self.ui.debug('preparing pushkey for "%s:%s"\n' % (namespace, key))
305 305 yield {'namespace': encoding.fromlocal(namespace),
306 306 'key': encoding.fromlocal(key),
307 307 'old': encoding.fromlocal(old),
308 308 'new': encoding.fromlocal(new)}, f
309 309 d = f.value
310 310 d, output = d.split('\n', 1)
311 311 try:
312 312 d = bool(int(d))
313 313 except ValueError:
314 314 raise error.ResponseError(
315 315 _('push failed (unexpected response):'), d)
316 316 for l in output.splitlines(True):
317 317 self.ui.status(_('remote: '), l)
318 318 yield d
319 319
320 320 @batchable
321 321 def listkeys(self, namespace):
322 322 if not self.capable('pushkey'):
323 323 yield {}, None
324 324 f = future()
325 325 self.ui.debug('preparing listkeys for "%s"\n' % namespace)
326 326 yield {'namespace': encoding.fromlocal(namespace)}, f
327 327 d = f.value
328 328 self.ui.debug('received listkey for "%s": %i bytes\n'
329 329 % (namespace, len(d)))
330 330 yield pushkeymod.decodekeys(d)
331 331
332 332 def stream_out(self):
333 333 return self._callstream('stream_out')
334 334
335 335 def changegroup(self, nodes, kind):
336 336 n = encodelist(nodes)
337 337 f = self._callcompressable("changegroup", roots=n)
338 338 return changegroupmod.cg1unpacker(f, 'UN')
339 339
340 340 def changegroupsubset(self, bases, heads, kind):
341 341 self.requirecap('changegroupsubset', _('look up remote changes'))
342 342 bases = encodelist(bases)
343 343 heads = encodelist(heads)
344 344 f = self._callcompressable("changegroupsubset",
345 345 bases=bases, heads=heads)
346 346 return changegroupmod.cg1unpacker(f, 'UN')
347 347
348 348 def getbundle(self, source, **kwargs):
349 349 self.requirecap('getbundle', _('look up remote changes'))
350 350 opts = {}
351 351 bundlecaps = kwargs.get('bundlecaps')
352 352 if bundlecaps is not None:
353 353 kwargs['bundlecaps'] = sorted(bundlecaps)
354 354 else:
355 355 bundlecaps = () # kwargs could have it to None
356 356 for key, value in kwargs.iteritems():
357 357 if value is None:
358 358 continue
359 359 keytype = gboptsmap.get(key)
360 360 if keytype is None:
361 361 assert False, 'unexpected'
362 362 elif keytype == 'nodes':
363 363 value = encodelist(value)
364 364 elif keytype in ('csv', 'scsv'):
365 365 value = ','.join(value)
366 366 elif keytype == 'boolean':
367 367 value = '%i' % bool(value)
368 368 elif keytype != 'plain':
369 369 raise KeyError('unknown getbundle option type %s'
370 370 % keytype)
371 371 opts[key] = value
372 372 f = self._callcompressable("getbundle", **opts)
373 373 if any((cap.startswith('HG2') for cap in bundlecaps)):
374 374 return bundle2.getunbundler(self.ui, f)
375 375 else:
376 376 return changegroupmod.cg1unpacker(f, 'UN')
377 377
378 378 def unbundle(self, cg, heads, source):
379 379 '''Send cg (a readable file-like object representing the
380 380 changegroup to push, typically a chunkbuffer object) to the
381 381 remote server as a bundle.
382 382
383 383 When pushing a bundle10 stream, return an integer indicating the
384 384 result of the push (see localrepository.addchangegroup()).
385 385
386 386 When pushing a bundle20 stream, return a bundle20 stream.'''
387 387
388 388 if heads != ['force'] and self.capable('unbundlehash'):
389 389 heads = encodelist(['hashed',
390 390 util.sha1(''.join(sorted(heads))).digest()])
391 391 else:
392 392 heads = encodelist(heads)
393 393
394 394 if util.safehasattr(cg, 'deltaheader'):
395 395 # this a bundle10, do the old style call sequence
396 396 ret, output = self._callpush("unbundle", cg, heads=heads)
397 397 if ret == "":
398 398 raise error.ResponseError(
399 399 _('push failed:'), output)
400 400 try:
401 401 ret = int(ret)
402 402 except ValueError:
403 403 raise error.ResponseError(
404 404 _('push failed (unexpected response):'), ret)
405 405
406 406 for l in output.splitlines(True):
407 407 self.ui.status(_('remote: '), l)
408 408 else:
409 409 # bundle2 push. Send a stream, fetch a stream.
410 410 stream = self._calltwowaystream('unbundle', cg, heads=heads)
411 411 ret = bundle2.getunbundler(self.ui, stream)
412 412 return ret
413 413
414 414 def debugwireargs(self, one, two, three=None, four=None, five=None):
415 415 # don't pass optional arguments left at their default value
416 416 opts = {}
417 417 if three is not None:
418 418 opts['three'] = three
419 419 if four is not None:
420 420 opts['four'] = four
421 421 return self._call('debugwireargs', one=one, two=two, **opts)
422 422
423 423 def _call(self, cmd, **args):
424 424 """execute <cmd> on the server
425 425
426 426 The command is expected to return a simple string.
427 427
428 428 returns the server reply as a string."""
429 429 raise NotImplementedError()
430 430
431 431 def _callstream(self, cmd, **args):
432 432 """execute <cmd> on the server
433 433
434 434 The command is expected to return a stream.
435 435
436 436 returns the server reply as a file like object."""
437 437 raise NotImplementedError()
438 438
439 439 def _callcompressable(self, cmd, **args):
440 440 """execute <cmd> on the server
441 441
442 442 The command is expected to return a stream.
443 443
444 444 The stream may have been compressed in some implementations. This
445 445 function takes care of the decompression. This is the only difference
446 446 with _callstream.
447 447
448 448 returns the server reply as a file like object.
449 449 """
450 450 raise NotImplementedError()
451 451
452 452 def _callpush(self, cmd, fp, **args):
453 453 """execute a <cmd> on server
454 454
455 455 The command is expected to be related to a push. Push has a special
456 456 return method.
457 457
458 458 returns the server reply as a (ret, output) tuple. ret is either
459 459 empty (error) or a stringified int.
460 460 """
461 461 raise NotImplementedError()
462 462
463 463 def _calltwowaystream(self, cmd, fp, **args):
464 464 """execute <cmd> on server
465 465
466 466 The command will send a stream to the server and get a stream in reply.
467 467 """
468 468 raise NotImplementedError()
469 469
470 470 def _abort(self, exception):
471 471 """clearly abort the wire protocol connection and raise the exception
472 472 """
473 473 raise NotImplementedError()
474 474
475 475 # server side
476 476
477 477 # wire protocol command can either return a string or one of these classes.
478 478 class streamres(object):
479 479 """wireproto reply: binary stream
480 480
481 481 The call was successful and the result is a stream.
482 482 Iterate on the `self.gen` attribute to retrieve chunks.
483 483 """
484 484 def __init__(self, gen):
485 485 self.gen = gen
486 486
487 487 class pushres(object):
488 488 """wireproto reply: success with simple integer return
489 489
490 490 The call was successful and returned an integer contained in `self.res`.
491 491 """
492 492 def __init__(self, res):
493 493 self.res = res
494 494
495 495 class pusherr(object):
496 496 """wireproto reply: failure
497 497
498 498 The call failed. The `self.res` attribute contains the error message.
499 499 """
500 500 def __init__(self, res):
501 501 self.res = res
502 502
503 503 class ooberror(object):
504 504 """wireproto reply: failure of a batch of operation
505 505
506 506 Something failed during a batch call. The error message is stored in
507 507 `self.message`.
508 508 """
509 509 def __init__(self, message):
510 510 self.message = message
511 511
512 512 def dispatch(repo, proto, command):
513 513 repo = repo.filtered("served")
514 514 func, spec = commands[command]
515 515 args = proto.getargs(spec)
516 516 return func(repo, proto, *args)
517 517
518 518 def options(cmd, keys, others):
519 519 opts = {}
520 520 for k in keys:
521 521 if k in others:
522 522 opts[k] = others[k]
523 523 del others[k]
524 524 if others:
525 525 sys.stderr.write("warning: %s ignored unexpected arguments %s\n"
526 526 % (cmd, ",".join(others)))
527 527 return opts
528 528
529 529 # list of commands
530 530 commands = {}
531 531
532 532 def wireprotocommand(name, args=''):
533 533 """decorator for wire protocol command"""
534 534 def register(func):
535 535 commands[name] = (func, args)
536 536 return func
537 537 return register
538 538
539 539 @wireprotocommand('batch', 'cmds *')
540 540 def batch(repo, proto, cmds, others):
541 541 repo = repo.filtered("served")
542 542 res = []
543 543 for pair in cmds.split(';'):
544 544 op, args = pair.split(' ', 1)
545 545 vals = {}
546 546 for a in args.split(','):
547 547 if a:
548 548 n, v = a.split('=')
549 549 vals[n] = unescapearg(v)
550 550 func, spec = commands[op]
551 551 if spec:
552 552 keys = spec.split()
553 553 data = {}
554 554 for k in keys:
555 555 if k == '*':
556 556 star = {}
557 557 for key in vals.keys():
558 558 if key not in keys:
559 559 star[key] = vals[key]
560 560 data['*'] = star
561 561 else:
562 562 data[k] = vals[k]
563 563 result = func(repo, proto, *[data[k] for k in keys])
564 564 else:
565 565 result = func(repo, proto)
566 566 if isinstance(result, ooberror):
567 567 return result
568 568 res.append(escapearg(result))
569 569 return ';'.join(res)
570 570
571 571 @wireprotocommand('between', 'pairs')
572 572 def between(repo, proto, pairs):
573 573 pairs = [decodelist(p, '-') for p in pairs.split(" ")]
574 574 r = []
575 575 for b in repo.between(pairs):
576 576 r.append(encodelist(b) + "\n")
577 577 return "".join(r)
578 578
579 579 @wireprotocommand('branchmap')
580 580 def branchmap(repo, proto):
581 581 branchmap = repo.branchmap()
582 582 heads = []
583 583 for branch, nodes in branchmap.iteritems():
584 584 branchname = urllib.quote(encoding.fromlocal(branch))
585 585 branchnodes = encodelist(nodes)
586 586 heads.append('%s %s' % (branchname, branchnodes))
587 587 return '\n'.join(heads)
588 588
589 589 @wireprotocommand('branches', 'nodes')
590 590 def branches(repo, proto, nodes):
591 591 nodes = decodelist(nodes)
592 592 r = []
593 593 for b in repo.branches(nodes):
594 594 r.append(encodelist(b) + "\n")
595 595 return "".join(r)
596 596
597 597
598 598 wireprotocaps = ['lookup', 'changegroupsubset', 'branchmap', 'pushkey',
599 599 'known', 'getbundle', 'unbundlehash', 'batch']
600 600
601 601 def _capabilities(repo, proto):
602 602 """return a list of capabilities for a repo
603 603
604 604 This function exists to allow extensions to easily wrap capabilities
605 605 computation
606 606
607 607 - returns a lists: easy to alter
608 608 - change done here will be propagated to both `capabilities` and `hello`
609 609 command without any other action needed.
610 610 """
611 611 # copy to prevent modification of the global list
612 612 caps = list(wireprotocaps)
613 613 if _allowstream(repo.ui):
614 614 if repo.ui.configbool('server', 'preferuncompressed', False):
615 615 caps.append('stream-preferred')
616 616 requiredformats = repo.requirements & repo.supportedformats
617 617 # if our local revlogs are just revlogv1, add 'stream' cap
618 618 if not requiredformats - set(('revlogv1',)):
619 619 caps.append('stream')
620 620 # otherwise, add 'streamreqs' detailing our local revlog format
621 621 else:
622 622 caps.append('streamreqs=%s' % ','.join(requiredformats))
623 623 if repo.ui.configbool('experimental', 'bundle2-advertise', True):
624 624 capsblob = bundle2.encodecaps(bundle2.getrepocaps(repo))
625 625 caps.append('bundle2=' + urllib.quote(capsblob))
626 626 caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority))
627 627 caps.append('httpheader=1024')
628 628 return caps
629 629
630 630 # If you are writing an extension and consider wrapping this function. Wrap
631 631 # `_capabilities` instead.
632 632 @wireprotocommand('capabilities')
633 633 def capabilities(repo, proto):
634 634 return ' '.join(_capabilities(repo, proto))
635 635
636 636 @wireprotocommand('changegroup', 'roots')
637 637 def changegroup(repo, proto, roots):
638 638 nodes = decodelist(roots)
639 639 cg = changegroupmod.changegroup(repo, nodes, 'serve')
640 640 return streamres(proto.groupchunks(cg))
641 641
642 642 @wireprotocommand('changegroupsubset', 'bases heads')
643 643 def changegroupsubset(repo, proto, bases, heads):
644 644 bases = decodelist(bases)
645 645 heads = decodelist(heads)
646 646 cg = changegroupmod.changegroupsubset(repo, bases, heads, 'serve')
647 647 return streamres(proto.groupchunks(cg))
648 648
649 649 @wireprotocommand('debugwireargs', 'one two *')
650 650 def debugwireargs(repo, proto, one, two, others):
651 651 # only accept optional args from the known set
652 652 opts = options('debugwireargs', ['three', 'four'], others)
653 653 return repo.debugwireargs(one, two, **opts)
654 654
655 655 # List of options accepted by getbundle.
656 656 #
657 657 # Meant to be extended by extensions. It is the extension's responsibility to
658 658 # ensure such options are properly processed in exchange.getbundle.
659 659 gboptslist = ['heads', 'common', 'bundlecaps']
660 660
661 661 @wireprotocommand('getbundle', '*')
662 662 def getbundle(repo, proto, others):
663 663 opts = options('getbundle', gboptsmap.keys(), others)
664 664 for k, v in opts.iteritems():
665 665 keytype = gboptsmap[k]
666 666 if keytype == 'nodes':
667 667 opts[k] = decodelist(v)
668 668 elif keytype == 'csv':
669 669 opts[k] = list(v.split(','))
670 670 elif keytype == 'scsv':
671 671 opts[k] = set(v.split(','))
672 672 elif keytype == 'boolean':
673 673 opts[k] = bool(v)
674 674 elif keytype != 'plain':
675 675 raise KeyError('unknown getbundle option type %s'
676 676 % keytype)
677 677 cg = exchange.getbundle(repo, 'serve', **opts)
678 678 return streamres(proto.groupchunks(cg))
679 679
680 680 @wireprotocommand('heads')
681 681 def heads(repo, proto):
682 682 h = repo.heads()
683 683 return encodelist(h) + "\n"
684 684
685 685 @wireprotocommand('hello')
686 686 def hello(repo, proto):
687 687 '''the hello command returns a set of lines describing various
688 688 interesting things about the server, in an RFC822-like format.
689 689 Currently the only one defined is "capabilities", which
690 690 consists of a line in the form:
691 691
692 692 capabilities: space separated list of tokens
693 693 '''
694 694 return "capabilities: %s\n" % (capabilities(repo, proto))
695 695
696 696 @wireprotocommand('listkeys', 'namespace')
697 697 def listkeys(repo, proto, namespace):
698 698 d = repo.listkeys(encoding.tolocal(namespace)).items()
699 699 return pushkeymod.encodekeys(d)
700 700
701 701 @wireprotocommand('lookup', 'key')
702 702 def lookup(repo, proto, key):
703 703 try:
704 704 k = encoding.tolocal(key)
705 705 c = repo[k]
706 706 r = c.hex()
707 707 success = 1
708 708 except Exception, inst:
709 709 r = str(inst)
710 710 success = 0
711 711 return "%s %s\n" % (success, r)
712 712
713 713 @wireprotocommand('known', 'nodes *')
714 714 def known(repo, proto, nodes, others):
715 715 return ''.join(b and "1" or "0" for b in repo.known(decodelist(nodes)))
716 716
717 717 @wireprotocommand('pushkey', 'namespace key old new')
718 718 def pushkey(repo, proto, namespace, key, old, new):
719 719 # compatibility with pre-1.8 clients which were accidentally
720 720 # sending raw binary nodes rather than utf-8-encoded hex
721 721 if len(new) == 20 and new.encode('string-escape') != new:
722 722 # looks like it could be a binary node
723 723 try:
724 724 new.decode('utf-8')
725 725 new = encoding.tolocal(new) # but cleanly decodes as UTF-8
726 726 except UnicodeDecodeError:
727 727 pass # binary, leave unmodified
728 728 else:
729 729 new = encoding.tolocal(new) # normal path
730 730
731 731 if util.safehasattr(proto, 'restore'):
732 732
733 733 proto.redirect()
734 734
735 735 try:
736 736 r = repo.pushkey(encoding.tolocal(namespace), encoding.tolocal(key),
737 737 encoding.tolocal(old), new) or False
738 738 except util.Abort:
739 739 r = False
740 740
741 741 output = proto.restore()
742 742
743 743 return '%s\n%s' % (int(r), output)
744 744
745 745 r = repo.pushkey(encoding.tolocal(namespace), encoding.tolocal(key),
746 746 encoding.tolocal(old), new)
747 747 return '%s\n' % int(r)
748 748
749 749 def _allowstream(ui):
750 750 return ui.configbool('server', 'uncompressed', True, untrusted=True)
751 751
752 752 @wireprotocommand('stream_out')
753 753 def stream(repo, proto):
754 754 '''If the server supports streaming clone, it advertises the "stream"
755 755 capability with a value representing the version and flags of the repo
756 756 it is serving. Client checks to see if it understands the format.
757 757 '''
758 758 if not _allowstream(repo.ui):
759 759 return '1\n'
760 760
761 761 def getstream(it):
762 762 yield '0\n'
763 763 for chunk in it:
764 764 yield chunk
765 765
766 766 try:
767 767 # LockError may be raised before the first result is yielded. Don't
768 768 # emit output until we're sure we got the lock successfully.
769 769 it = exchange.generatestreamclone(repo)
770 770 return streamres(getstream(it))
771 771 except error.LockError:
772 772 return '2\n'
773 773
774 774 @wireprotocommand('unbundle', 'heads')
775 775 def unbundle(repo, proto, heads):
776 776 their_heads = decodelist(heads)
777 777
778 778 try:
779 779 proto.redirect()
780 780
781 781 exchange.check_heads(repo, their_heads, 'preparing changes')
782 782
783 783 # write bundle data to temporary file because it can be big
784 784 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
785 785 fp = os.fdopen(fd, 'wb+')
786 786 r = 0
787 787 try:
788 788 proto.getfile(fp)
789 789 fp.seek(0)
790 790 gen = exchange.readbundle(repo.ui, fp, None)
791 791 r = exchange.unbundle(repo, gen, their_heads, 'serve',
792 792 proto._client())
793 793 if util.safehasattr(r, 'addpart'):
794 794 # The return looks streamable, we are in the bundle2 case and
795 795 # should return a stream.
796 796 return streamres(r.getchunks())
797 797 return pushres(r)
798 798
799 799 finally:
800 800 fp.close()
801 801 os.unlink(tempname)
802 802
803 803 except (error.BundleValueError, util.Abort, error.PushRaced), exc:
804 804 # handle non-bundle2 case first
805 805 if not getattr(exc, 'duringunbundle2', False):
806 806 try:
807 807 raise
808 808 except util.Abort:
809 809 # The old code we moved used sys.stderr directly.
810 810 # We did not change it to minimise code change.
811 811 # This need to be moved to something proper.
812 812 # Feel free to do it.
813 813 sys.stderr.write("abort: %s\n" % exc)
814 814 return pushres(0)
815 815 except error.PushRaced:
816 816 return pusherr(str(exc))
817 817
818 818 bundler = bundle2.bundle20(repo.ui)
819 819 for out in getattr(exc, '_bundle2salvagedoutput', ()):
820 820 bundler.addpart(out)
821 821 try:
822 raise
822 try:
823 raise
824 except error.PushkeyFailed, exc:
825 # check client caps
826 remotecaps = getattr(exc, '_replycaps', None)
827 if (remotecaps is not None
828 and 'pushkey' not in remotecaps.get('error', ())):
829 # no support remote side, fallback to Abort handler.
830 raise
831 part = bundler.newpart('error:pushkey')
832 part.addparam('in-reply-to', exc.partid)
833 if exc.namespace is not None:
834 part.addparam('namespace', exc.namespace, mandatory=False)
835 if exc.key is not None:
836 part.addparam('key', exc.key, mandatory=False)
837 if exc.new is not None:
838 part.addparam('new', exc.new, mandatory=False)
839 if exc.old is not None:
840 part.addparam('old', exc.old, mandatory=False)
841 if exc.ret is not None:
842 part.addparam('ret', exc.ret, mandatory=False)
823 843 except error.BundleValueError, exc:
824 844 errpart = bundler.newpart('error:unsupportedcontent')
825 845 if exc.parttype is not None:
826 846 errpart.addparam('parttype', exc.parttype)
827 847 if exc.params:
828 848 errpart.addparam('params', '\0'.join(exc.params))
829 849 except util.Abort, exc:
830 850 manargs = [('message', str(exc))]
831 851 advargs = []
832 852 if exc.hint is not None:
833 853 advargs.append(('hint', exc.hint))
834 854 bundler.addpart(bundle2.bundlepart('error:abort',
835 855 manargs, advargs))
836 856 except error.PushRaced, exc:
837 857 bundler.newpart('error:pushraced', [('message', str(exc))])
838 858 return streamres(bundler.getchunks())
@@ -1,2166 +1,2166 b''
1 1 > do_push()
2 2 > {
3 3 > user=$1
4 4 > shift
5 5 > echo "Pushing as user $user"
6 6 > echo 'hgrc = """'
7 7 > sed -n '/\[[ha]/,$p' b/.hg/hgrc | grep -v fakegroups.py
8 8 > echo '"""'
9 9 > if test -f acl.config; then
10 10 > echo 'acl.config = """'
11 11 > cat acl.config
12 12 > echo '"""'
13 13 > fi
14 14 > # On AIX /etc/profile sets LOGNAME read-only. So
15 15 > # LOGNAME=$user hg --cws a --debug push ../b
16 16 > # fails with "This variable is read only."
17 17 > # Use env to work around this.
18 18 > env LOGNAME=$user hg --cwd a --debug push ../b
19 19 > hg --cwd b rollback
20 20 > hg --cwd b --quiet tip
21 21 > echo
22 22 > }
23 23
24 24 > init_config()
25 25 > {
26 26 > cat > fakegroups.py <<EOF
27 27 > from hgext import acl
28 28 > def fakegetusers(ui, group):
29 29 > try:
30 30 > return acl._getusersorig(ui, group)
31 31 > except:
32 32 > return ["fred", "betty"]
33 33 > acl._getusersorig = acl._getusers
34 34 > acl._getusers = fakegetusers
35 35 > EOF
36 36 > rm -f acl.config
37 37 > cat > $config <<EOF
38 38 > [hooks]
39 39 > pretxnchangegroup.acl = python:hgext.acl.hook
40 40 > [acl]
41 41 > sources = push
42 42 > [extensions]
43 43 > f=`pwd`/fakegroups.py
44 44 > EOF
45 45 > }
46 46
47 47 $ cat << EOF >> $HGRCPATH
48 48 > [experimental]
49 49 > # drop me once bundle2 is the default,
50 50 > # added to get test change early.
51 51 > bundle2-exp = True
52 52 > EOF
53 53
54 54 $ hg init a
55 55 $ cd a
56 56 $ mkdir foo foo/Bar quux
57 57 $ echo 'in foo' > foo/file.txt
58 58 $ echo 'in foo/Bar' > foo/Bar/file.txt
59 59 $ echo 'in quux' > quux/file.py
60 60 $ hg add -q
61 61 $ hg ci -m 'add files' -d '1000000 0'
62 62 $ echo >> foo/file.txt
63 63 $ hg ci -m 'change foo/file' -d '1000001 0'
64 64 $ echo >> foo/Bar/file.txt
65 65 $ hg ci -m 'change foo/Bar/file' -d '1000002 0'
66 66 $ echo >> quux/file.py
67 67 $ hg ci -m 'change quux/file' -d '1000003 0'
68 68 $ hg tip --quiet
69 69 3:911600dab2ae
70 70
71 71 $ cd ..
72 72 $ hg clone -r 0 a b
73 73 adding changesets
74 74 adding manifests
75 75 adding file changes
76 76 added 1 changesets with 3 changes to 3 files
77 77 updating to branch default
78 78 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
79 79
80 80 $ config=b/.hg/hgrc
81 81
82 82 Extension disabled for lack of a hook
83 83
84 84 $ do_push fred
85 85 Pushing as user fred
86 86 hgrc = """
87 87 """
88 88 pushing to ../b
89 89 query 1; heads
90 90 searching for changes
91 91 all remote heads known locally
92 92 listing keys for "phases"
93 93 checking for updated bookmarks
94 94 listing keys for "bookmarks"
95 95 listing keys for "bookmarks"
96 96 3 changesets found
97 97 list of changesets:
98 98 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
99 99 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
100 100 911600dab2ae7a9baff75958b84fe606851ce955
101 101 bundle2-output-bundle: "HG20", 4 parts total
102 bundle2-output-part: "replycaps" 147 bytes payload
102 bundle2-output-part: "replycaps" 155 bytes payload
103 103 bundle2-output-part: "check:heads" streamed payload
104 104 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
105 105 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
106 106 bundle2-input-bundle: with-transaction
107 107 bundle2-input-part: "replycaps" supported
108 bundle2-input-part: total payload size 147
108 bundle2-input-part: total payload size 155
109 109 bundle2-input-part: "check:heads" supported
110 110 bundle2-input-part: total payload size 20
111 111 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
112 112 adding changesets
113 113 add changeset ef1ea85a6374
114 114 add changeset f9cafe1212c8
115 115 add changeset 911600dab2ae
116 116 adding manifests
117 117 adding file changes
118 118 adding foo/Bar/file.txt revisions
119 119 adding foo/file.txt revisions
120 120 adding quux/file.py revisions
121 121 added 3 changesets with 3 changes to 3 files
122 122 bundle2-input-part: total payload size 1606
123 123 bundle2-input-part: "pushkey" (advisory) (params: 4 mandatory) supported
124 124 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
125 125 bundle2-input-bundle: 3 parts total
126 126 updating the branch cache
127 127 bundle2-output-bundle: "HG20", 2 parts total
128 128 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
129 129 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
130 130 bundle2-input-bundle: with-transaction
131 131 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
132 132 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
133 133 bundle2-input-bundle: 1 parts total
134 134 listing keys for "phases"
135 135 try to push obsolete markers to remote
136 136 repository tip rolled back to revision 0 (undo push)
137 137 0:6675d58eff77
138 138
139 139
140 140 $ echo '[hooks]' >> $config
141 141 $ echo 'pretxnchangegroup.acl = python:hgext.acl.hook' >> $config
142 142
143 143 Extension disabled for lack of acl.sources
144 144
145 145 $ do_push fred
146 146 Pushing as user fred
147 147 hgrc = """
148 148 [hooks]
149 149 pretxnchangegroup.acl = python:hgext.acl.hook
150 150 """
151 151 pushing to ../b
152 152 query 1; heads
153 153 searching for changes
154 154 all remote heads known locally
155 155 listing keys for "phases"
156 156 checking for updated bookmarks
157 157 listing keys for "bookmarks"
158 158 invalid branchheads cache (served): tip differs
159 159 listing keys for "bookmarks"
160 160 3 changesets found
161 161 list of changesets:
162 162 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
163 163 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
164 164 911600dab2ae7a9baff75958b84fe606851ce955
165 165 bundle2-output-bundle: "HG20", 4 parts total
166 bundle2-output-part: "replycaps" 147 bytes payload
166 bundle2-output-part: "replycaps" 155 bytes payload
167 167 bundle2-output-part: "check:heads" streamed payload
168 168 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
169 169 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
170 170 bundle2-input-bundle: with-transaction
171 171 bundle2-input-part: "replycaps" supported
172 bundle2-input-part: total payload size 147
172 bundle2-input-part: total payload size 155
173 173 bundle2-input-part: "check:heads" supported
174 174 bundle2-input-part: total payload size 20
175 175 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
176 176 adding changesets
177 177 add changeset ef1ea85a6374
178 178 add changeset f9cafe1212c8
179 179 add changeset 911600dab2ae
180 180 adding manifests
181 181 adding file changes
182 182 adding foo/Bar/file.txt revisions
183 183 adding foo/file.txt revisions
184 184 adding quux/file.py revisions
185 185 added 3 changesets with 3 changes to 3 files
186 186 calling hook pretxnchangegroup.acl: hgext.acl.hook
187 187 acl: changes have source "push" - skipping
188 188 bundle2-input-part: total payload size 1606
189 189 bundle2-input-part: "pushkey" (advisory) (params: 4 mandatory) supported
190 190 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
191 191 bundle2-input-bundle: 3 parts total
192 192 updating the branch cache
193 193 bundle2-output-bundle: "HG20", 2 parts total
194 194 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
195 195 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
196 196 bundle2-input-bundle: with-transaction
197 197 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
198 198 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
199 199 bundle2-input-bundle: 1 parts total
200 200 listing keys for "phases"
201 201 try to push obsolete markers to remote
202 202 repository tip rolled back to revision 0 (undo push)
203 203 0:6675d58eff77
204 204
205 205
206 206 No [acl.allow]/[acl.deny]
207 207
208 208 $ echo '[acl]' >> $config
209 209 $ echo 'sources = push' >> $config
210 210 $ do_push fred
211 211 Pushing as user fred
212 212 hgrc = """
213 213 [hooks]
214 214 pretxnchangegroup.acl = python:hgext.acl.hook
215 215 [acl]
216 216 sources = push
217 217 """
218 218 pushing to ../b
219 219 query 1; heads
220 220 searching for changes
221 221 all remote heads known locally
222 222 listing keys for "phases"
223 223 checking for updated bookmarks
224 224 listing keys for "bookmarks"
225 225 invalid branchheads cache (served): tip differs
226 226 listing keys for "bookmarks"
227 227 3 changesets found
228 228 list of changesets:
229 229 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
230 230 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
231 231 911600dab2ae7a9baff75958b84fe606851ce955
232 232 bundle2-output-bundle: "HG20", 4 parts total
233 bundle2-output-part: "replycaps" 147 bytes payload
233 bundle2-output-part: "replycaps" 155 bytes payload
234 234 bundle2-output-part: "check:heads" streamed payload
235 235 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
236 236 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
237 237 bundle2-input-bundle: with-transaction
238 238 bundle2-input-part: "replycaps" supported
239 bundle2-input-part: total payload size 147
239 bundle2-input-part: total payload size 155
240 240 bundle2-input-part: "check:heads" supported
241 241 bundle2-input-part: total payload size 20
242 242 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
243 243 adding changesets
244 244 add changeset ef1ea85a6374
245 245 add changeset f9cafe1212c8
246 246 add changeset 911600dab2ae
247 247 adding manifests
248 248 adding file changes
249 249 adding foo/Bar/file.txt revisions
250 250 adding foo/file.txt revisions
251 251 adding quux/file.py revisions
252 252 added 3 changesets with 3 changes to 3 files
253 253 calling hook pretxnchangegroup.acl: hgext.acl.hook
254 254 acl: checking access for user "fred"
255 255 acl: acl.allow.branches not enabled
256 256 acl: acl.deny.branches not enabled
257 257 acl: acl.allow not enabled
258 258 acl: acl.deny not enabled
259 259 acl: branch access granted: "ef1ea85a6374" on branch "default"
260 260 acl: path access granted: "ef1ea85a6374"
261 261 acl: branch access granted: "f9cafe1212c8" on branch "default"
262 262 acl: path access granted: "f9cafe1212c8"
263 263 acl: branch access granted: "911600dab2ae" on branch "default"
264 264 acl: path access granted: "911600dab2ae"
265 265 bundle2-input-part: total payload size 1606
266 266 bundle2-input-part: "pushkey" (advisory) (params: 4 mandatory) supported
267 267 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
268 268 bundle2-input-bundle: 3 parts total
269 269 updating the branch cache
270 270 bundle2-output-bundle: "HG20", 2 parts total
271 271 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
272 272 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
273 273 bundle2-input-bundle: with-transaction
274 274 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
275 275 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
276 276 bundle2-input-bundle: 1 parts total
277 277 listing keys for "phases"
278 278 try to push obsolete markers to remote
279 279 repository tip rolled back to revision 0 (undo push)
280 280 0:6675d58eff77
281 281
282 282
283 283 Empty [acl.allow]
284 284
285 285 $ echo '[acl.allow]' >> $config
286 286 $ do_push fred
287 287 Pushing as user fred
288 288 hgrc = """
289 289 [hooks]
290 290 pretxnchangegroup.acl = python:hgext.acl.hook
291 291 [acl]
292 292 sources = push
293 293 [acl.allow]
294 294 """
295 295 pushing to ../b
296 296 query 1; heads
297 297 searching for changes
298 298 all remote heads known locally
299 299 listing keys for "phases"
300 300 checking for updated bookmarks
301 301 listing keys for "bookmarks"
302 302 invalid branchheads cache (served): tip differs
303 303 listing keys for "bookmarks"
304 304 3 changesets found
305 305 list of changesets:
306 306 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
307 307 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
308 308 911600dab2ae7a9baff75958b84fe606851ce955
309 309 bundle2-output-bundle: "HG20", 4 parts total
310 bundle2-output-part: "replycaps" 147 bytes payload
310 bundle2-output-part: "replycaps" 155 bytes payload
311 311 bundle2-output-part: "check:heads" streamed payload
312 312 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
313 313 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
314 314 bundle2-input-bundle: with-transaction
315 315 bundle2-input-part: "replycaps" supported
316 bundle2-input-part: total payload size 147
316 bundle2-input-part: total payload size 155
317 317 bundle2-input-part: "check:heads" supported
318 318 bundle2-input-part: total payload size 20
319 319 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
320 320 adding changesets
321 321 add changeset ef1ea85a6374
322 322 add changeset f9cafe1212c8
323 323 add changeset 911600dab2ae
324 324 adding manifests
325 325 adding file changes
326 326 adding foo/Bar/file.txt revisions
327 327 adding foo/file.txt revisions
328 328 adding quux/file.py revisions
329 329 added 3 changesets with 3 changes to 3 files
330 330 calling hook pretxnchangegroup.acl: hgext.acl.hook
331 331 acl: checking access for user "fred"
332 332 acl: acl.allow.branches not enabled
333 333 acl: acl.deny.branches not enabled
334 334 acl: acl.allow enabled, 0 entries for user fred
335 335 acl: acl.deny not enabled
336 336 acl: branch access granted: "ef1ea85a6374" on branch "default"
337 337 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
338 338 bundle2-input-part: total payload size 1606
339 339 bundle2-input-bundle: 3 parts total
340 340 transaction abort!
341 341 rollback completed
342 342 abort: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
343 343 no rollback information available
344 344 0:6675d58eff77
345 345
346 346
347 347 fred is allowed inside foo/
348 348
349 349 $ echo 'foo/** = fred' >> $config
350 350 $ do_push fred
351 351 Pushing as user fred
352 352 hgrc = """
353 353 [hooks]
354 354 pretxnchangegroup.acl = python:hgext.acl.hook
355 355 [acl]
356 356 sources = push
357 357 [acl.allow]
358 358 foo/** = fred
359 359 """
360 360 pushing to ../b
361 361 query 1; heads
362 362 searching for changes
363 363 all remote heads known locally
364 364 listing keys for "phases"
365 365 checking for updated bookmarks
366 366 listing keys for "bookmarks"
367 367 invalid branchheads cache (served): tip differs
368 368 listing keys for "bookmarks"
369 369 3 changesets found
370 370 list of changesets:
371 371 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
372 372 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
373 373 911600dab2ae7a9baff75958b84fe606851ce955
374 374 bundle2-output-bundle: "HG20", 4 parts total
375 bundle2-output-part: "replycaps" 147 bytes payload
375 bundle2-output-part: "replycaps" 155 bytes payload
376 376 bundle2-output-part: "check:heads" streamed payload
377 377 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
378 378 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
379 379 bundle2-input-bundle: with-transaction
380 380 bundle2-input-part: "replycaps" supported
381 bundle2-input-part: total payload size 147
381 bundle2-input-part: total payload size 155
382 382 bundle2-input-part: "check:heads" supported
383 383 bundle2-input-part: total payload size 20
384 384 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
385 385 adding changesets
386 386 add changeset ef1ea85a6374
387 387 add changeset f9cafe1212c8
388 388 add changeset 911600dab2ae
389 389 adding manifests
390 390 adding file changes
391 391 adding foo/Bar/file.txt revisions
392 392 adding foo/file.txt revisions
393 393 adding quux/file.py revisions
394 394 added 3 changesets with 3 changes to 3 files
395 395 calling hook pretxnchangegroup.acl: hgext.acl.hook
396 396 acl: checking access for user "fred"
397 397 acl: acl.allow.branches not enabled
398 398 acl: acl.deny.branches not enabled
399 399 acl: acl.allow enabled, 1 entries for user fred
400 400 acl: acl.deny not enabled
401 401 acl: branch access granted: "ef1ea85a6374" on branch "default"
402 402 acl: path access granted: "ef1ea85a6374"
403 403 acl: branch access granted: "f9cafe1212c8" on branch "default"
404 404 acl: path access granted: "f9cafe1212c8"
405 405 acl: branch access granted: "911600dab2ae" on branch "default"
406 406 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
407 407 bundle2-input-part: total payload size 1606
408 408 bundle2-input-bundle: 3 parts total
409 409 transaction abort!
410 410 rollback completed
411 411 abort: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
412 412 no rollback information available
413 413 0:6675d58eff77
414 414
415 415
416 416 Empty [acl.deny]
417 417
418 418 $ echo '[acl.deny]' >> $config
419 419 $ do_push barney
420 420 Pushing as user barney
421 421 hgrc = """
422 422 [hooks]
423 423 pretxnchangegroup.acl = python:hgext.acl.hook
424 424 [acl]
425 425 sources = push
426 426 [acl.allow]
427 427 foo/** = fred
428 428 [acl.deny]
429 429 """
430 430 pushing to ../b
431 431 query 1; heads
432 432 searching for changes
433 433 all remote heads known locally
434 434 listing keys for "phases"
435 435 checking for updated bookmarks
436 436 listing keys for "bookmarks"
437 437 invalid branchheads cache (served): tip differs
438 438 listing keys for "bookmarks"
439 439 3 changesets found
440 440 list of changesets:
441 441 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
442 442 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
443 443 911600dab2ae7a9baff75958b84fe606851ce955
444 444 bundle2-output-bundle: "HG20", 4 parts total
445 bundle2-output-part: "replycaps" 147 bytes payload
445 bundle2-output-part: "replycaps" 155 bytes payload
446 446 bundle2-output-part: "check:heads" streamed payload
447 447 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
448 448 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
449 449 bundle2-input-bundle: with-transaction
450 450 bundle2-input-part: "replycaps" supported
451 bundle2-input-part: total payload size 147
451 bundle2-input-part: total payload size 155
452 452 bundle2-input-part: "check:heads" supported
453 453 bundle2-input-part: total payload size 20
454 454 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
455 455 adding changesets
456 456 add changeset ef1ea85a6374
457 457 add changeset f9cafe1212c8
458 458 add changeset 911600dab2ae
459 459 adding manifests
460 460 adding file changes
461 461 adding foo/Bar/file.txt revisions
462 462 adding foo/file.txt revisions
463 463 adding quux/file.py revisions
464 464 added 3 changesets with 3 changes to 3 files
465 465 calling hook pretxnchangegroup.acl: hgext.acl.hook
466 466 acl: checking access for user "barney"
467 467 acl: acl.allow.branches not enabled
468 468 acl: acl.deny.branches not enabled
469 469 acl: acl.allow enabled, 0 entries for user barney
470 470 acl: acl.deny enabled, 0 entries for user barney
471 471 acl: branch access granted: "ef1ea85a6374" on branch "default"
472 472 error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
473 473 bundle2-input-part: total payload size 1606
474 474 bundle2-input-bundle: 3 parts total
475 475 transaction abort!
476 476 rollback completed
477 477 abort: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
478 478 no rollback information available
479 479 0:6675d58eff77
480 480
481 481
482 482 fred is allowed inside foo/, but not foo/bar/ (case matters)
483 483
484 484 $ echo 'foo/bar/** = fred' >> $config
485 485 $ do_push fred
486 486 Pushing as user fred
487 487 hgrc = """
488 488 [hooks]
489 489 pretxnchangegroup.acl = python:hgext.acl.hook
490 490 [acl]
491 491 sources = push
492 492 [acl.allow]
493 493 foo/** = fred
494 494 [acl.deny]
495 495 foo/bar/** = fred
496 496 """
497 497 pushing to ../b
498 498 query 1; heads
499 499 searching for changes
500 500 all remote heads known locally
501 501 listing keys for "phases"
502 502 checking for updated bookmarks
503 503 listing keys for "bookmarks"
504 504 invalid branchheads cache (served): tip differs
505 505 listing keys for "bookmarks"
506 506 3 changesets found
507 507 list of changesets:
508 508 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
509 509 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
510 510 911600dab2ae7a9baff75958b84fe606851ce955
511 511 bundle2-output-bundle: "HG20", 4 parts total
512 bundle2-output-part: "replycaps" 147 bytes payload
512 bundle2-output-part: "replycaps" 155 bytes payload
513 513 bundle2-output-part: "check:heads" streamed payload
514 514 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
515 515 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
516 516 bundle2-input-bundle: with-transaction
517 517 bundle2-input-part: "replycaps" supported
518 bundle2-input-part: total payload size 147
518 bundle2-input-part: total payload size 155
519 519 bundle2-input-part: "check:heads" supported
520 520 bundle2-input-part: total payload size 20
521 521 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
522 522 adding changesets
523 523 add changeset ef1ea85a6374
524 524 add changeset f9cafe1212c8
525 525 add changeset 911600dab2ae
526 526 adding manifests
527 527 adding file changes
528 528 adding foo/Bar/file.txt revisions
529 529 adding foo/file.txt revisions
530 530 adding quux/file.py revisions
531 531 added 3 changesets with 3 changes to 3 files
532 532 calling hook pretxnchangegroup.acl: hgext.acl.hook
533 533 acl: checking access for user "fred"
534 534 acl: acl.allow.branches not enabled
535 535 acl: acl.deny.branches not enabled
536 536 acl: acl.allow enabled, 1 entries for user fred
537 537 acl: acl.deny enabled, 1 entries for user fred
538 538 acl: branch access granted: "ef1ea85a6374" on branch "default"
539 539 acl: path access granted: "ef1ea85a6374"
540 540 acl: branch access granted: "f9cafe1212c8" on branch "default"
541 541 acl: path access granted: "f9cafe1212c8"
542 542 acl: branch access granted: "911600dab2ae" on branch "default"
543 543 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
544 544 bundle2-input-part: total payload size 1606
545 545 bundle2-input-bundle: 3 parts total
546 546 transaction abort!
547 547 rollback completed
548 548 abort: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
549 549 no rollback information available
550 550 0:6675d58eff77
551 551
552 552
553 553 fred is allowed inside foo/, but not foo/Bar/
554 554
555 555 $ echo 'foo/Bar/** = fred' >> $config
556 556 $ do_push fred
557 557 Pushing as user fred
558 558 hgrc = """
559 559 [hooks]
560 560 pretxnchangegroup.acl = python:hgext.acl.hook
561 561 [acl]
562 562 sources = push
563 563 [acl.allow]
564 564 foo/** = fred
565 565 [acl.deny]
566 566 foo/bar/** = fred
567 567 foo/Bar/** = fred
568 568 """
569 569 pushing to ../b
570 570 query 1; heads
571 571 searching for changes
572 572 all remote heads known locally
573 573 listing keys for "phases"
574 574 checking for updated bookmarks
575 575 listing keys for "bookmarks"
576 576 invalid branchheads cache (served): tip differs
577 577 listing keys for "bookmarks"
578 578 3 changesets found
579 579 list of changesets:
580 580 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
581 581 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
582 582 911600dab2ae7a9baff75958b84fe606851ce955
583 583 bundle2-output-bundle: "HG20", 4 parts total
584 bundle2-output-part: "replycaps" 147 bytes payload
584 bundle2-output-part: "replycaps" 155 bytes payload
585 585 bundle2-output-part: "check:heads" streamed payload
586 586 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
587 587 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
588 588 bundle2-input-bundle: with-transaction
589 589 bundle2-input-part: "replycaps" supported
590 bundle2-input-part: total payload size 147
590 bundle2-input-part: total payload size 155
591 591 bundle2-input-part: "check:heads" supported
592 592 bundle2-input-part: total payload size 20
593 593 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
594 594 adding changesets
595 595 add changeset ef1ea85a6374
596 596 add changeset f9cafe1212c8
597 597 add changeset 911600dab2ae
598 598 adding manifests
599 599 adding file changes
600 600 adding foo/Bar/file.txt revisions
601 601 adding foo/file.txt revisions
602 602 adding quux/file.py revisions
603 603 added 3 changesets with 3 changes to 3 files
604 604 calling hook pretxnchangegroup.acl: hgext.acl.hook
605 605 acl: checking access for user "fred"
606 606 acl: acl.allow.branches not enabled
607 607 acl: acl.deny.branches not enabled
608 608 acl: acl.allow enabled, 1 entries for user fred
609 609 acl: acl.deny enabled, 2 entries for user fred
610 610 acl: branch access granted: "ef1ea85a6374" on branch "default"
611 611 acl: path access granted: "ef1ea85a6374"
612 612 acl: branch access granted: "f9cafe1212c8" on branch "default"
613 613 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
614 614 bundle2-input-part: total payload size 1606
615 615 bundle2-input-bundle: 3 parts total
616 616 transaction abort!
617 617 rollback completed
618 618 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
619 619 no rollback information available
620 620 0:6675d58eff77
621 621
622 622
623 623 $ echo 'barney is not mentioned => not allowed anywhere'
624 624 barney is not mentioned => not allowed anywhere
625 625 $ do_push barney
626 626 Pushing as user barney
627 627 hgrc = """
628 628 [hooks]
629 629 pretxnchangegroup.acl = python:hgext.acl.hook
630 630 [acl]
631 631 sources = push
632 632 [acl.allow]
633 633 foo/** = fred
634 634 [acl.deny]
635 635 foo/bar/** = fred
636 636 foo/Bar/** = fred
637 637 """
638 638 pushing to ../b
639 639 query 1; heads
640 640 searching for changes
641 641 all remote heads known locally
642 642 listing keys for "phases"
643 643 checking for updated bookmarks
644 644 listing keys for "bookmarks"
645 645 invalid branchheads cache (served): tip differs
646 646 listing keys for "bookmarks"
647 647 3 changesets found
648 648 list of changesets:
649 649 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
650 650 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
651 651 911600dab2ae7a9baff75958b84fe606851ce955
652 652 bundle2-output-bundle: "HG20", 4 parts total
653 bundle2-output-part: "replycaps" 147 bytes payload
653 bundle2-output-part: "replycaps" 155 bytes payload
654 654 bundle2-output-part: "check:heads" streamed payload
655 655 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
656 656 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
657 657 bundle2-input-bundle: with-transaction
658 658 bundle2-input-part: "replycaps" supported
659 bundle2-input-part: total payload size 147
659 bundle2-input-part: total payload size 155
660 660 bundle2-input-part: "check:heads" supported
661 661 bundle2-input-part: total payload size 20
662 662 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
663 663 adding changesets
664 664 add changeset ef1ea85a6374
665 665 add changeset f9cafe1212c8
666 666 add changeset 911600dab2ae
667 667 adding manifests
668 668 adding file changes
669 669 adding foo/Bar/file.txt revisions
670 670 adding foo/file.txt revisions
671 671 adding quux/file.py revisions
672 672 added 3 changesets with 3 changes to 3 files
673 673 calling hook pretxnchangegroup.acl: hgext.acl.hook
674 674 acl: checking access for user "barney"
675 675 acl: acl.allow.branches not enabled
676 676 acl: acl.deny.branches not enabled
677 677 acl: acl.allow enabled, 0 entries for user barney
678 678 acl: acl.deny enabled, 0 entries for user barney
679 679 acl: branch access granted: "ef1ea85a6374" on branch "default"
680 680 error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
681 681 bundle2-input-part: total payload size 1606
682 682 bundle2-input-bundle: 3 parts total
683 683 transaction abort!
684 684 rollback completed
685 685 abort: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
686 686 no rollback information available
687 687 0:6675d58eff77
688 688
689 689
690 690 barney is allowed everywhere
691 691
692 692 $ echo '[acl.allow]' >> $config
693 693 $ echo '** = barney' >> $config
694 694 $ do_push barney
695 695 Pushing as user barney
696 696 hgrc = """
697 697 [hooks]
698 698 pretxnchangegroup.acl = python:hgext.acl.hook
699 699 [acl]
700 700 sources = push
701 701 [acl.allow]
702 702 foo/** = fred
703 703 [acl.deny]
704 704 foo/bar/** = fred
705 705 foo/Bar/** = fred
706 706 [acl.allow]
707 707 ** = barney
708 708 """
709 709 pushing to ../b
710 710 query 1; heads
711 711 searching for changes
712 712 all remote heads known locally
713 713 listing keys for "phases"
714 714 checking for updated bookmarks
715 715 listing keys for "bookmarks"
716 716 invalid branchheads cache (served): tip differs
717 717 listing keys for "bookmarks"
718 718 3 changesets found
719 719 list of changesets:
720 720 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
721 721 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
722 722 911600dab2ae7a9baff75958b84fe606851ce955
723 723 bundle2-output-bundle: "HG20", 4 parts total
724 bundle2-output-part: "replycaps" 147 bytes payload
724 bundle2-output-part: "replycaps" 155 bytes payload
725 725 bundle2-output-part: "check:heads" streamed payload
726 726 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
727 727 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
728 728 bundle2-input-bundle: with-transaction
729 729 bundle2-input-part: "replycaps" supported
730 bundle2-input-part: total payload size 147
730 bundle2-input-part: total payload size 155
731 731 bundle2-input-part: "check:heads" supported
732 732 bundle2-input-part: total payload size 20
733 733 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
734 734 adding changesets
735 735 add changeset ef1ea85a6374
736 736 add changeset f9cafe1212c8
737 737 add changeset 911600dab2ae
738 738 adding manifests
739 739 adding file changes
740 740 adding foo/Bar/file.txt revisions
741 741 adding foo/file.txt revisions
742 742 adding quux/file.py revisions
743 743 added 3 changesets with 3 changes to 3 files
744 744 calling hook pretxnchangegroup.acl: hgext.acl.hook
745 745 acl: checking access for user "barney"
746 746 acl: acl.allow.branches not enabled
747 747 acl: acl.deny.branches not enabled
748 748 acl: acl.allow enabled, 1 entries for user barney
749 749 acl: acl.deny enabled, 0 entries for user barney
750 750 acl: branch access granted: "ef1ea85a6374" on branch "default"
751 751 acl: path access granted: "ef1ea85a6374"
752 752 acl: branch access granted: "f9cafe1212c8" on branch "default"
753 753 acl: path access granted: "f9cafe1212c8"
754 754 acl: branch access granted: "911600dab2ae" on branch "default"
755 755 acl: path access granted: "911600dab2ae"
756 756 bundle2-input-part: total payload size 1606
757 757 bundle2-input-part: "pushkey" (advisory) (params: 4 mandatory) supported
758 758 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
759 759 bundle2-input-bundle: 3 parts total
760 760 updating the branch cache
761 761 bundle2-output-bundle: "HG20", 2 parts total
762 762 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
763 763 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
764 764 bundle2-input-bundle: with-transaction
765 765 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
766 766 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
767 767 bundle2-input-bundle: 1 parts total
768 768 listing keys for "phases"
769 769 try to push obsolete markers to remote
770 770 repository tip rolled back to revision 0 (undo push)
771 771 0:6675d58eff77
772 772
773 773
774 774 wilma can change files with a .txt extension
775 775
776 776 $ echo '**/*.txt = wilma' >> $config
777 777 $ do_push wilma
778 778 Pushing as user wilma
779 779 hgrc = """
780 780 [hooks]
781 781 pretxnchangegroup.acl = python:hgext.acl.hook
782 782 [acl]
783 783 sources = push
784 784 [acl.allow]
785 785 foo/** = fred
786 786 [acl.deny]
787 787 foo/bar/** = fred
788 788 foo/Bar/** = fred
789 789 [acl.allow]
790 790 ** = barney
791 791 **/*.txt = wilma
792 792 """
793 793 pushing to ../b
794 794 query 1; heads
795 795 searching for changes
796 796 all remote heads known locally
797 797 listing keys for "phases"
798 798 checking for updated bookmarks
799 799 listing keys for "bookmarks"
800 800 invalid branchheads cache (served): tip differs
801 801 listing keys for "bookmarks"
802 802 3 changesets found
803 803 list of changesets:
804 804 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
805 805 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
806 806 911600dab2ae7a9baff75958b84fe606851ce955
807 807 bundle2-output-bundle: "HG20", 4 parts total
808 bundle2-output-part: "replycaps" 147 bytes payload
808 bundle2-output-part: "replycaps" 155 bytes payload
809 809 bundle2-output-part: "check:heads" streamed payload
810 810 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
811 811 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
812 812 bundle2-input-bundle: with-transaction
813 813 bundle2-input-part: "replycaps" supported
814 bundle2-input-part: total payload size 147
814 bundle2-input-part: total payload size 155
815 815 bundle2-input-part: "check:heads" supported
816 816 bundle2-input-part: total payload size 20
817 817 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
818 818 adding changesets
819 819 add changeset ef1ea85a6374
820 820 add changeset f9cafe1212c8
821 821 add changeset 911600dab2ae
822 822 adding manifests
823 823 adding file changes
824 824 adding foo/Bar/file.txt revisions
825 825 adding foo/file.txt revisions
826 826 adding quux/file.py revisions
827 827 added 3 changesets with 3 changes to 3 files
828 828 calling hook pretxnchangegroup.acl: hgext.acl.hook
829 829 acl: checking access for user "wilma"
830 830 acl: acl.allow.branches not enabled
831 831 acl: acl.deny.branches not enabled
832 832 acl: acl.allow enabled, 1 entries for user wilma
833 833 acl: acl.deny enabled, 0 entries for user wilma
834 834 acl: branch access granted: "ef1ea85a6374" on branch "default"
835 835 acl: path access granted: "ef1ea85a6374"
836 836 acl: branch access granted: "f9cafe1212c8" on branch "default"
837 837 acl: path access granted: "f9cafe1212c8"
838 838 acl: branch access granted: "911600dab2ae" on branch "default"
839 839 error: pretxnchangegroup.acl hook failed: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae")
840 840 bundle2-input-part: total payload size 1606
841 841 bundle2-input-bundle: 3 parts total
842 842 transaction abort!
843 843 rollback completed
844 844 abort: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae")
845 845 no rollback information available
846 846 0:6675d58eff77
847 847
848 848
849 849 file specified by acl.config does not exist
850 850
851 851 $ echo '[acl]' >> $config
852 852 $ echo 'config = ../acl.config' >> $config
853 853 $ do_push barney
854 854 Pushing as user barney
855 855 hgrc = """
856 856 [hooks]
857 857 pretxnchangegroup.acl = python:hgext.acl.hook
858 858 [acl]
859 859 sources = push
860 860 [acl.allow]
861 861 foo/** = fred
862 862 [acl.deny]
863 863 foo/bar/** = fred
864 864 foo/Bar/** = fred
865 865 [acl.allow]
866 866 ** = barney
867 867 **/*.txt = wilma
868 868 [acl]
869 869 config = ../acl.config
870 870 """
871 871 pushing to ../b
872 872 query 1; heads
873 873 searching for changes
874 874 all remote heads known locally
875 875 listing keys for "phases"
876 876 checking for updated bookmarks
877 877 listing keys for "bookmarks"
878 878 invalid branchheads cache (served): tip differs
879 879 listing keys for "bookmarks"
880 880 3 changesets found
881 881 list of changesets:
882 882 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
883 883 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
884 884 911600dab2ae7a9baff75958b84fe606851ce955
885 885 bundle2-output-bundle: "HG20", 4 parts total
886 bundle2-output-part: "replycaps" 147 bytes payload
886 bundle2-output-part: "replycaps" 155 bytes payload
887 887 bundle2-output-part: "check:heads" streamed payload
888 888 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
889 889 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
890 890 bundle2-input-bundle: with-transaction
891 891 bundle2-input-part: "replycaps" supported
892 bundle2-input-part: total payload size 147
892 bundle2-input-part: total payload size 155
893 893 bundle2-input-part: "check:heads" supported
894 894 bundle2-input-part: total payload size 20
895 895 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
896 896 adding changesets
897 897 add changeset ef1ea85a6374
898 898 add changeset f9cafe1212c8
899 899 add changeset 911600dab2ae
900 900 adding manifests
901 901 adding file changes
902 902 adding foo/Bar/file.txt revisions
903 903 adding foo/file.txt revisions
904 904 adding quux/file.py revisions
905 905 added 3 changesets with 3 changes to 3 files
906 906 calling hook pretxnchangegroup.acl: hgext.acl.hook
907 907 acl: checking access for user "barney"
908 908 error: pretxnchangegroup.acl hook raised an exception: [Errno 2] No such file or directory: '../acl.config'
909 909 bundle2-input-part: total payload size 1606
910 910 bundle2-input-bundle: 3 parts total
911 911 transaction abort!
912 912 rollback completed
913 913 abort: No such file or directory: ../acl.config
914 914 no rollback information available
915 915 0:6675d58eff77
916 916
917 917
918 918 betty is allowed inside foo/ by a acl.config file
919 919
920 920 $ echo '[acl.allow]' >> acl.config
921 921 $ echo 'foo/** = betty' >> acl.config
922 922 $ do_push betty
923 923 Pushing as user betty
924 924 hgrc = """
925 925 [hooks]
926 926 pretxnchangegroup.acl = python:hgext.acl.hook
927 927 [acl]
928 928 sources = push
929 929 [acl.allow]
930 930 foo/** = fred
931 931 [acl.deny]
932 932 foo/bar/** = fred
933 933 foo/Bar/** = fred
934 934 [acl.allow]
935 935 ** = barney
936 936 **/*.txt = wilma
937 937 [acl]
938 938 config = ../acl.config
939 939 """
940 940 acl.config = """
941 941 [acl.allow]
942 942 foo/** = betty
943 943 """
944 944 pushing to ../b
945 945 query 1; heads
946 946 searching for changes
947 947 all remote heads known locally
948 948 listing keys for "phases"
949 949 checking for updated bookmarks
950 950 listing keys for "bookmarks"
951 951 invalid branchheads cache (served): tip differs
952 952 listing keys for "bookmarks"
953 953 3 changesets found
954 954 list of changesets:
955 955 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
956 956 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
957 957 911600dab2ae7a9baff75958b84fe606851ce955
958 958 bundle2-output-bundle: "HG20", 4 parts total
959 bundle2-output-part: "replycaps" 147 bytes payload
959 bundle2-output-part: "replycaps" 155 bytes payload
960 960 bundle2-output-part: "check:heads" streamed payload
961 961 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
962 962 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
963 963 bundle2-input-bundle: with-transaction
964 964 bundle2-input-part: "replycaps" supported
965 bundle2-input-part: total payload size 147
965 bundle2-input-part: total payload size 155
966 966 bundle2-input-part: "check:heads" supported
967 967 bundle2-input-part: total payload size 20
968 968 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
969 969 adding changesets
970 970 add changeset ef1ea85a6374
971 971 add changeset f9cafe1212c8
972 972 add changeset 911600dab2ae
973 973 adding manifests
974 974 adding file changes
975 975 adding foo/Bar/file.txt revisions
976 976 adding foo/file.txt revisions
977 977 adding quux/file.py revisions
978 978 added 3 changesets with 3 changes to 3 files
979 979 calling hook pretxnchangegroup.acl: hgext.acl.hook
980 980 acl: checking access for user "betty"
981 981 acl: acl.allow.branches not enabled
982 982 acl: acl.deny.branches not enabled
983 983 acl: acl.allow enabled, 1 entries for user betty
984 984 acl: acl.deny enabled, 0 entries for user betty
985 985 acl: branch access granted: "ef1ea85a6374" on branch "default"
986 986 acl: path access granted: "ef1ea85a6374"
987 987 acl: branch access granted: "f9cafe1212c8" on branch "default"
988 988 acl: path access granted: "f9cafe1212c8"
989 989 acl: branch access granted: "911600dab2ae" on branch "default"
990 990 error: pretxnchangegroup.acl hook failed: acl: user "betty" not allowed on "quux/file.py" (changeset "911600dab2ae")
991 991 bundle2-input-part: total payload size 1606
992 992 bundle2-input-bundle: 3 parts total
993 993 transaction abort!
994 994 rollback completed
995 995 abort: acl: user "betty" not allowed on "quux/file.py" (changeset "911600dab2ae")
996 996 no rollback information available
997 997 0:6675d58eff77
998 998
999 999
1000 1000 acl.config can set only [acl.allow]/[acl.deny]
1001 1001
1002 1002 $ echo '[hooks]' >> acl.config
1003 1003 $ echo 'changegroup.acl = false' >> acl.config
1004 1004 $ do_push barney
1005 1005 Pushing as user barney
1006 1006 hgrc = """
1007 1007 [hooks]
1008 1008 pretxnchangegroup.acl = python:hgext.acl.hook
1009 1009 [acl]
1010 1010 sources = push
1011 1011 [acl.allow]
1012 1012 foo/** = fred
1013 1013 [acl.deny]
1014 1014 foo/bar/** = fred
1015 1015 foo/Bar/** = fred
1016 1016 [acl.allow]
1017 1017 ** = barney
1018 1018 **/*.txt = wilma
1019 1019 [acl]
1020 1020 config = ../acl.config
1021 1021 """
1022 1022 acl.config = """
1023 1023 [acl.allow]
1024 1024 foo/** = betty
1025 1025 [hooks]
1026 1026 changegroup.acl = false
1027 1027 """
1028 1028 pushing to ../b
1029 1029 query 1; heads
1030 1030 searching for changes
1031 1031 all remote heads known locally
1032 1032 listing keys for "phases"
1033 1033 checking for updated bookmarks
1034 1034 listing keys for "bookmarks"
1035 1035 invalid branchheads cache (served): tip differs
1036 1036 listing keys for "bookmarks"
1037 1037 3 changesets found
1038 1038 list of changesets:
1039 1039 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1040 1040 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1041 1041 911600dab2ae7a9baff75958b84fe606851ce955
1042 1042 bundle2-output-bundle: "HG20", 4 parts total
1043 bundle2-output-part: "replycaps" 147 bytes payload
1043 bundle2-output-part: "replycaps" 155 bytes payload
1044 1044 bundle2-output-part: "check:heads" streamed payload
1045 1045 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1046 1046 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
1047 1047 bundle2-input-bundle: with-transaction
1048 1048 bundle2-input-part: "replycaps" supported
1049 bundle2-input-part: total payload size 147
1049 bundle2-input-part: total payload size 155
1050 1050 bundle2-input-part: "check:heads" supported
1051 1051 bundle2-input-part: total payload size 20
1052 1052 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1053 1053 adding changesets
1054 1054 add changeset ef1ea85a6374
1055 1055 add changeset f9cafe1212c8
1056 1056 add changeset 911600dab2ae
1057 1057 adding manifests
1058 1058 adding file changes
1059 1059 adding foo/Bar/file.txt revisions
1060 1060 adding foo/file.txt revisions
1061 1061 adding quux/file.py revisions
1062 1062 added 3 changesets with 3 changes to 3 files
1063 1063 calling hook pretxnchangegroup.acl: hgext.acl.hook
1064 1064 acl: checking access for user "barney"
1065 1065 acl: acl.allow.branches not enabled
1066 1066 acl: acl.deny.branches not enabled
1067 1067 acl: acl.allow enabled, 1 entries for user barney
1068 1068 acl: acl.deny enabled, 0 entries for user barney
1069 1069 acl: branch access granted: "ef1ea85a6374" on branch "default"
1070 1070 acl: path access granted: "ef1ea85a6374"
1071 1071 acl: branch access granted: "f9cafe1212c8" on branch "default"
1072 1072 acl: path access granted: "f9cafe1212c8"
1073 1073 acl: branch access granted: "911600dab2ae" on branch "default"
1074 1074 acl: path access granted: "911600dab2ae"
1075 1075 bundle2-input-part: total payload size 1606
1076 1076 bundle2-input-part: "pushkey" (advisory) (params: 4 mandatory) supported
1077 1077 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
1078 1078 bundle2-input-bundle: 3 parts total
1079 1079 updating the branch cache
1080 1080 bundle2-output-bundle: "HG20", 2 parts total
1081 1081 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1082 1082 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1083 1083 bundle2-input-bundle: with-transaction
1084 1084 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1085 1085 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1086 1086 bundle2-input-bundle: 1 parts total
1087 1087 listing keys for "phases"
1088 1088 try to push obsolete markers to remote
1089 1089 repository tip rolled back to revision 0 (undo push)
1090 1090 0:6675d58eff77
1091 1091
1092 1092
1093 1093 asterisk
1094 1094
1095 1095 $ init_config
1096 1096
1097 1097 asterisk test
1098 1098
1099 1099 $ echo '[acl.allow]' >> $config
1100 1100 $ echo "** = fred" >> $config
1101 1101
1102 1102 fred is always allowed
1103 1103
1104 1104 $ do_push fred
1105 1105 Pushing as user fred
1106 1106 hgrc = """
1107 1107 [hooks]
1108 1108 pretxnchangegroup.acl = python:hgext.acl.hook
1109 1109 [acl]
1110 1110 sources = push
1111 1111 [extensions]
1112 1112 [acl.allow]
1113 1113 ** = fred
1114 1114 """
1115 1115 pushing to ../b
1116 1116 query 1; heads
1117 1117 searching for changes
1118 1118 all remote heads known locally
1119 1119 listing keys for "phases"
1120 1120 checking for updated bookmarks
1121 1121 listing keys for "bookmarks"
1122 1122 invalid branchheads cache (served): tip differs
1123 1123 listing keys for "bookmarks"
1124 1124 3 changesets found
1125 1125 list of changesets:
1126 1126 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1127 1127 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1128 1128 911600dab2ae7a9baff75958b84fe606851ce955
1129 1129 bundle2-output-bundle: "HG20", 4 parts total
1130 bundle2-output-part: "replycaps" 147 bytes payload
1130 bundle2-output-part: "replycaps" 155 bytes payload
1131 1131 bundle2-output-part: "check:heads" streamed payload
1132 1132 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1133 1133 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
1134 1134 bundle2-input-bundle: with-transaction
1135 1135 bundle2-input-part: "replycaps" supported
1136 bundle2-input-part: total payload size 147
1136 bundle2-input-part: total payload size 155
1137 1137 bundle2-input-part: "check:heads" supported
1138 1138 bundle2-input-part: total payload size 20
1139 1139 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1140 1140 adding changesets
1141 1141 add changeset ef1ea85a6374
1142 1142 add changeset f9cafe1212c8
1143 1143 add changeset 911600dab2ae
1144 1144 adding manifests
1145 1145 adding file changes
1146 1146 adding foo/Bar/file.txt revisions
1147 1147 adding foo/file.txt revisions
1148 1148 adding quux/file.py revisions
1149 1149 added 3 changesets with 3 changes to 3 files
1150 1150 calling hook pretxnchangegroup.acl: hgext.acl.hook
1151 1151 acl: checking access for user "fred"
1152 1152 acl: acl.allow.branches not enabled
1153 1153 acl: acl.deny.branches not enabled
1154 1154 acl: acl.allow enabled, 1 entries for user fred
1155 1155 acl: acl.deny not enabled
1156 1156 acl: branch access granted: "ef1ea85a6374" on branch "default"
1157 1157 acl: path access granted: "ef1ea85a6374"
1158 1158 acl: branch access granted: "f9cafe1212c8" on branch "default"
1159 1159 acl: path access granted: "f9cafe1212c8"
1160 1160 acl: branch access granted: "911600dab2ae" on branch "default"
1161 1161 acl: path access granted: "911600dab2ae"
1162 1162 bundle2-input-part: total payload size 1606
1163 1163 bundle2-input-part: "pushkey" (advisory) (params: 4 mandatory) supported
1164 1164 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
1165 1165 bundle2-input-bundle: 3 parts total
1166 1166 updating the branch cache
1167 1167 bundle2-output-bundle: "HG20", 2 parts total
1168 1168 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1169 1169 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1170 1170 bundle2-input-bundle: with-transaction
1171 1171 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1172 1172 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1173 1173 bundle2-input-bundle: 1 parts total
1174 1174 listing keys for "phases"
1175 1175 try to push obsolete markers to remote
1176 1176 repository tip rolled back to revision 0 (undo push)
1177 1177 0:6675d58eff77
1178 1178
1179 1179
1180 1180 $ echo '[acl.deny]' >> $config
1181 1181 $ echo "foo/Bar/** = *" >> $config
1182 1182
1183 1183 no one is allowed inside foo/Bar/
1184 1184
1185 1185 $ do_push fred
1186 1186 Pushing as user fred
1187 1187 hgrc = """
1188 1188 [hooks]
1189 1189 pretxnchangegroup.acl = python:hgext.acl.hook
1190 1190 [acl]
1191 1191 sources = push
1192 1192 [extensions]
1193 1193 [acl.allow]
1194 1194 ** = fred
1195 1195 [acl.deny]
1196 1196 foo/Bar/** = *
1197 1197 """
1198 1198 pushing to ../b
1199 1199 query 1; heads
1200 1200 searching for changes
1201 1201 all remote heads known locally
1202 1202 listing keys for "phases"
1203 1203 checking for updated bookmarks
1204 1204 listing keys for "bookmarks"
1205 1205 invalid branchheads cache (served): tip differs
1206 1206 listing keys for "bookmarks"
1207 1207 3 changesets found
1208 1208 list of changesets:
1209 1209 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1210 1210 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1211 1211 911600dab2ae7a9baff75958b84fe606851ce955
1212 1212 bundle2-output-bundle: "HG20", 4 parts total
1213 bundle2-output-part: "replycaps" 147 bytes payload
1213 bundle2-output-part: "replycaps" 155 bytes payload
1214 1214 bundle2-output-part: "check:heads" streamed payload
1215 1215 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1216 1216 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
1217 1217 bundle2-input-bundle: with-transaction
1218 1218 bundle2-input-part: "replycaps" supported
1219 bundle2-input-part: total payload size 147
1219 bundle2-input-part: total payload size 155
1220 1220 bundle2-input-part: "check:heads" supported
1221 1221 bundle2-input-part: total payload size 20
1222 1222 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1223 1223 adding changesets
1224 1224 add changeset ef1ea85a6374
1225 1225 add changeset f9cafe1212c8
1226 1226 add changeset 911600dab2ae
1227 1227 adding manifests
1228 1228 adding file changes
1229 1229 adding foo/Bar/file.txt revisions
1230 1230 adding foo/file.txt revisions
1231 1231 adding quux/file.py revisions
1232 1232 added 3 changesets with 3 changes to 3 files
1233 1233 calling hook pretxnchangegroup.acl: hgext.acl.hook
1234 1234 acl: checking access for user "fred"
1235 1235 acl: acl.allow.branches not enabled
1236 1236 acl: acl.deny.branches not enabled
1237 1237 acl: acl.allow enabled, 1 entries for user fred
1238 1238 acl: acl.deny enabled, 1 entries for user fred
1239 1239 acl: branch access granted: "ef1ea85a6374" on branch "default"
1240 1240 acl: path access granted: "ef1ea85a6374"
1241 1241 acl: branch access granted: "f9cafe1212c8" on branch "default"
1242 1242 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1243 1243 bundle2-input-part: total payload size 1606
1244 1244 bundle2-input-bundle: 3 parts total
1245 1245 transaction abort!
1246 1246 rollback completed
1247 1247 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1248 1248 no rollback information available
1249 1249 0:6675d58eff77
1250 1250
1251 1251
1252 1252 Groups
1253 1253
1254 1254 $ init_config
1255 1255
1256 1256 OS-level groups
1257 1257
1258 1258 $ echo '[acl.allow]' >> $config
1259 1259 $ echo "** = @group1" >> $config
1260 1260
1261 1261 @group1 is always allowed
1262 1262
1263 1263 $ do_push fred
1264 1264 Pushing as user fred
1265 1265 hgrc = """
1266 1266 [hooks]
1267 1267 pretxnchangegroup.acl = python:hgext.acl.hook
1268 1268 [acl]
1269 1269 sources = push
1270 1270 [extensions]
1271 1271 [acl.allow]
1272 1272 ** = @group1
1273 1273 """
1274 1274 pushing to ../b
1275 1275 query 1; heads
1276 1276 searching for changes
1277 1277 all remote heads known locally
1278 1278 listing keys for "phases"
1279 1279 checking for updated bookmarks
1280 1280 listing keys for "bookmarks"
1281 1281 invalid branchheads cache (served): tip differs
1282 1282 listing keys for "bookmarks"
1283 1283 3 changesets found
1284 1284 list of changesets:
1285 1285 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1286 1286 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1287 1287 911600dab2ae7a9baff75958b84fe606851ce955
1288 1288 bundle2-output-bundle: "HG20", 4 parts total
1289 bundle2-output-part: "replycaps" 147 bytes payload
1289 bundle2-output-part: "replycaps" 155 bytes payload
1290 1290 bundle2-output-part: "check:heads" streamed payload
1291 1291 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1292 1292 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
1293 1293 bundle2-input-bundle: with-transaction
1294 1294 bundle2-input-part: "replycaps" supported
1295 bundle2-input-part: total payload size 147
1295 bundle2-input-part: total payload size 155
1296 1296 bundle2-input-part: "check:heads" supported
1297 1297 bundle2-input-part: total payload size 20
1298 1298 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1299 1299 adding changesets
1300 1300 add changeset ef1ea85a6374
1301 1301 add changeset f9cafe1212c8
1302 1302 add changeset 911600dab2ae
1303 1303 adding manifests
1304 1304 adding file changes
1305 1305 adding foo/Bar/file.txt revisions
1306 1306 adding foo/file.txt revisions
1307 1307 adding quux/file.py revisions
1308 1308 added 3 changesets with 3 changes to 3 files
1309 1309 calling hook pretxnchangegroup.acl: hgext.acl.hook
1310 1310 acl: checking access for user "fred"
1311 1311 acl: acl.allow.branches not enabled
1312 1312 acl: acl.deny.branches not enabled
1313 1313 acl: "group1" not defined in [acl.groups]
1314 1314 acl: acl.allow enabled, 1 entries for user fred
1315 1315 acl: acl.deny not enabled
1316 1316 acl: branch access granted: "ef1ea85a6374" on branch "default"
1317 1317 acl: path access granted: "ef1ea85a6374"
1318 1318 acl: branch access granted: "f9cafe1212c8" on branch "default"
1319 1319 acl: path access granted: "f9cafe1212c8"
1320 1320 acl: branch access granted: "911600dab2ae" on branch "default"
1321 1321 acl: path access granted: "911600dab2ae"
1322 1322 bundle2-input-part: total payload size 1606
1323 1323 bundle2-input-part: "pushkey" (advisory) (params: 4 mandatory) supported
1324 1324 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
1325 1325 bundle2-input-bundle: 3 parts total
1326 1326 updating the branch cache
1327 1327 bundle2-output-bundle: "HG20", 2 parts total
1328 1328 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1329 1329 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1330 1330 bundle2-input-bundle: with-transaction
1331 1331 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1332 1332 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1333 1333 bundle2-input-bundle: 1 parts total
1334 1334 listing keys for "phases"
1335 1335 try to push obsolete markers to remote
1336 1336 repository tip rolled back to revision 0 (undo push)
1337 1337 0:6675d58eff77
1338 1338
1339 1339
1340 1340 $ echo '[acl.deny]' >> $config
1341 1341 $ echo "foo/Bar/** = @group1" >> $config
1342 1342
1343 1343 @group is allowed inside anything but foo/Bar/
1344 1344
1345 1345 $ do_push fred
1346 1346 Pushing as user fred
1347 1347 hgrc = """
1348 1348 [hooks]
1349 1349 pretxnchangegroup.acl = python:hgext.acl.hook
1350 1350 [acl]
1351 1351 sources = push
1352 1352 [extensions]
1353 1353 [acl.allow]
1354 1354 ** = @group1
1355 1355 [acl.deny]
1356 1356 foo/Bar/** = @group1
1357 1357 """
1358 1358 pushing to ../b
1359 1359 query 1; heads
1360 1360 searching for changes
1361 1361 all remote heads known locally
1362 1362 listing keys for "phases"
1363 1363 checking for updated bookmarks
1364 1364 listing keys for "bookmarks"
1365 1365 invalid branchheads cache (served): tip differs
1366 1366 listing keys for "bookmarks"
1367 1367 3 changesets found
1368 1368 list of changesets:
1369 1369 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1370 1370 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1371 1371 911600dab2ae7a9baff75958b84fe606851ce955
1372 1372 bundle2-output-bundle: "HG20", 4 parts total
1373 bundle2-output-part: "replycaps" 147 bytes payload
1373 bundle2-output-part: "replycaps" 155 bytes payload
1374 1374 bundle2-output-part: "check:heads" streamed payload
1375 1375 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1376 1376 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
1377 1377 bundle2-input-bundle: with-transaction
1378 1378 bundle2-input-part: "replycaps" supported
1379 bundle2-input-part: total payload size 147
1379 bundle2-input-part: total payload size 155
1380 1380 bundle2-input-part: "check:heads" supported
1381 1381 bundle2-input-part: total payload size 20
1382 1382 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1383 1383 adding changesets
1384 1384 add changeset ef1ea85a6374
1385 1385 add changeset f9cafe1212c8
1386 1386 add changeset 911600dab2ae
1387 1387 adding manifests
1388 1388 adding file changes
1389 1389 adding foo/Bar/file.txt revisions
1390 1390 adding foo/file.txt revisions
1391 1391 adding quux/file.py revisions
1392 1392 added 3 changesets with 3 changes to 3 files
1393 1393 calling hook pretxnchangegroup.acl: hgext.acl.hook
1394 1394 acl: checking access for user "fred"
1395 1395 acl: acl.allow.branches not enabled
1396 1396 acl: acl.deny.branches not enabled
1397 1397 acl: "group1" not defined in [acl.groups]
1398 1398 acl: acl.allow enabled, 1 entries for user fred
1399 1399 acl: "group1" not defined in [acl.groups]
1400 1400 acl: acl.deny enabled, 1 entries for user fred
1401 1401 acl: branch access granted: "ef1ea85a6374" on branch "default"
1402 1402 acl: path access granted: "ef1ea85a6374"
1403 1403 acl: branch access granted: "f9cafe1212c8" on branch "default"
1404 1404 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1405 1405 bundle2-input-part: total payload size 1606
1406 1406 bundle2-input-bundle: 3 parts total
1407 1407 transaction abort!
1408 1408 rollback completed
1409 1409 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1410 1410 no rollback information available
1411 1411 0:6675d58eff77
1412 1412
1413 1413
1414 1414 Invalid group
1415 1415
1416 1416 Disable the fakegroups trick to get real failures
1417 1417
1418 1418 $ grep -v fakegroups $config > config.tmp
1419 1419 $ mv config.tmp $config
1420 1420 $ echo '[acl.allow]' >> $config
1421 1421 $ echo "** = @unlikelytoexist" >> $config
1422 1422 $ do_push fred 2>&1 | grep unlikelytoexist
1423 1423 ** = @unlikelytoexist
1424 1424 acl: "unlikelytoexist" not defined in [acl.groups]
1425 1425 error: pretxnchangegroup.acl hook failed: group 'unlikelytoexist' is undefined
1426 1426 abort: group 'unlikelytoexist' is undefined
1427 1427
1428 1428
1429 1429 Branch acl tests setup
1430 1430
1431 1431 $ init_config
1432 1432 $ cd b
1433 1433 $ hg up
1434 1434 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1435 1435 $ hg branch foobar
1436 1436 marked working directory as branch foobar
1437 1437 (branches are permanent and global, did you want a bookmark?)
1438 1438 $ hg commit -m 'create foobar'
1439 1439 $ echo 'foo contents' > abc.txt
1440 1440 $ hg add abc.txt
1441 1441 $ hg commit -m 'foobar contents'
1442 1442 $ cd ..
1443 1443 $ hg --cwd a pull ../b
1444 1444 pulling from ../b
1445 1445 searching for changes
1446 1446 adding changesets
1447 1447 adding manifests
1448 1448 adding file changes
1449 1449 added 2 changesets with 1 changes to 1 files (+1 heads)
1450 1450 (run 'hg heads' to see heads)
1451 1451
1452 1452 Create additional changeset on foobar branch
1453 1453
1454 1454 $ cd a
1455 1455 $ hg up -C foobar
1456 1456 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
1457 1457 $ echo 'foo contents2' > abc.txt
1458 1458 $ hg commit -m 'foobar contents2'
1459 1459 $ cd ..
1460 1460
1461 1461
1462 1462 No branch acls specified
1463 1463
1464 1464 $ do_push astro
1465 1465 Pushing as user astro
1466 1466 hgrc = """
1467 1467 [hooks]
1468 1468 pretxnchangegroup.acl = python:hgext.acl.hook
1469 1469 [acl]
1470 1470 sources = push
1471 1471 [extensions]
1472 1472 """
1473 1473 pushing to ../b
1474 1474 query 1; heads
1475 1475 searching for changes
1476 1476 all remote heads known locally
1477 1477 listing keys for "phases"
1478 1478 checking for updated bookmarks
1479 1479 listing keys for "bookmarks"
1480 1480 listing keys for "bookmarks"
1481 1481 4 changesets found
1482 1482 list of changesets:
1483 1483 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1484 1484 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1485 1485 911600dab2ae7a9baff75958b84fe606851ce955
1486 1486 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1487 1487 bundle2-output-bundle: "HG20", 5 parts total
1488 bundle2-output-part: "replycaps" 147 bytes payload
1488 bundle2-output-part: "replycaps" 155 bytes payload
1489 1489 bundle2-output-part: "check:heads" streamed payload
1490 1490 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1491 1491 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
1492 1492 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
1493 1493 bundle2-input-bundle: with-transaction
1494 1494 bundle2-input-part: "replycaps" supported
1495 bundle2-input-part: total payload size 147
1495 bundle2-input-part: total payload size 155
1496 1496 bundle2-input-part: "check:heads" supported
1497 1497 bundle2-input-part: total payload size 20
1498 1498 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1499 1499 adding changesets
1500 1500 add changeset ef1ea85a6374
1501 1501 add changeset f9cafe1212c8
1502 1502 add changeset 911600dab2ae
1503 1503 add changeset e8fc755d4d82
1504 1504 adding manifests
1505 1505 adding file changes
1506 1506 adding abc.txt revisions
1507 1507 adding foo/Bar/file.txt revisions
1508 1508 adding foo/file.txt revisions
1509 1509 adding quux/file.py revisions
1510 1510 added 4 changesets with 4 changes to 4 files (+1 heads)
1511 1511 calling hook pretxnchangegroup.acl: hgext.acl.hook
1512 1512 acl: checking access for user "astro"
1513 1513 acl: acl.allow.branches not enabled
1514 1514 acl: acl.deny.branches not enabled
1515 1515 acl: acl.allow not enabled
1516 1516 acl: acl.deny not enabled
1517 1517 acl: branch access granted: "ef1ea85a6374" on branch "default"
1518 1518 acl: path access granted: "ef1ea85a6374"
1519 1519 acl: branch access granted: "f9cafe1212c8" on branch "default"
1520 1520 acl: path access granted: "f9cafe1212c8"
1521 1521 acl: branch access granted: "911600dab2ae" on branch "default"
1522 1522 acl: path access granted: "911600dab2ae"
1523 1523 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1524 1524 acl: path access granted: "e8fc755d4d82"
1525 1525 bundle2-input-part: total payload size 2101
1526 1526 bundle2-input-part: "pushkey" (advisory) (params: 4 mandatory) supported
1527 1527 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
1528 1528 bundle2-input-part: "pushkey" (advisory) (params: 4 mandatory) supported
1529 1529 pushing key for "phases:e8fc755d4d8217ee5b0c2bb41558c40d43b92c01"
1530 1530 bundle2-input-bundle: 4 parts total
1531 1531 updating the branch cache
1532 1532 bundle2-output-bundle: "HG20", 3 parts total
1533 1533 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1534 1534 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1535 1535 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1536 1536 bundle2-input-bundle: with-transaction
1537 1537 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1538 1538 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1539 1539 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1540 1540 bundle2-input-bundle: 2 parts total
1541 1541 listing keys for "phases"
1542 1542 try to push obsolete markers to remote
1543 1543 repository tip rolled back to revision 2 (undo push)
1544 1544 2:fb35475503ef
1545 1545
1546 1546
1547 1547 Branch acl deny test
1548 1548
1549 1549 $ echo "[acl.deny.branches]" >> $config
1550 1550 $ echo "foobar = *" >> $config
1551 1551 $ do_push astro
1552 1552 Pushing as user astro
1553 1553 hgrc = """
1554 1554 [hooks]
1555 1555 pretxnchangegroup.acl = python:hgext.acl.hook
1556 1556 [acl]
1557 1557 sources = push
1558 1558 [extensions]
1559 1559 [acl.deny.branches]
1560 1560 foobar = *
1561 1561 """
1562 1562 pushing to ../b
1563 1563 query 1; heads
1564 1564 searching for changes
1565 1565 all remote heads known locally
1566 1566 listing keys for "phases"
1567 1567 checking for updated bookmarks
1568 1568 listing keys for "bookmarks"
1569 1569 listing keys for "bookmarks"
1570 1570 4 changesets found
1571 1571 list of changesets:
1572 1572 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1573 1573 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1574 1574 911600dab2ae7a9baff75958b84fe606851ce955
1575 1575 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1576 1576 bundle2-output-bundle: "HG20", 5 parts total
1577 bundle2-output-part: "replycaps" 147 bytes payload
1577 bundle2-output-part: "replycaps" 155 bytes payload
1578 1578 bundle2-output-part: "check:heads" streamed payload
1579 1579 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1580 1580 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
1581 1581 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
1582 1582 bundle2-input-bundle: with-transaction
1583 1583 bundle2-input-part: "replycaps" supported
1584 bundle2-input-part: total payload size 147
1584 bundle2-input-part: total payload size 155
1585 1585 bundle2-input-part: "check:heads" supported
1586 1586 bundle2-input-part: total payload size 20
1587 1587 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1588 1588 adding changesets
1589 1589 add changeset ef1ea85a6374
1590 1590 add changeset f9cafe1212c8
1591 1591 add changeset 911600dab2ae
1592 1592 add changeset e8fc755d4d82
1593 1593 adding manifests
1594 1594 adding file changes
1595 1595 adding abc.txt revisions
1596 1596 adding foo/Bar/file.txt revisions
1597 1597 adding foo/file.txt revisions
1598 1598 adding quux/file.py revisions
1599 1599 added 4 changesets with 4 changes to 4 files (+1 heads)
1600 1600 calling hook pretxnchangegroup.acl: hgext.acl.hook
1601 1601 acl: checking access for user "astro"
1602 1602 acl: acl.allow.branches not enabled
1603 1603 acl: acl.deny.branches enabled, 1 entries for user astro
1604 1604 acl: acl.allow not enabled
1605 1605 acl: acl.deny not enabled
1606 1606 acl: branch access granted: "ef1ea85a6374" on branch "default"
1607 1607 acl: path access granted: "ef1ea85a6374"
1608 1608 acl: branch access granted: "f9cafe1212c8" on branch "default"
1609 1609 acl: path access granted: "f9cafe1212c8"
1610 1610 acl: branch access granted: "911600dab2ae" on branch "default"
1611 1611 acl: path access granted: "911600dab2ae"
1612 1612 error: pretxnchangegroup.acl hook failed: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82")
1613 1613 bundle2-input-part: total payload size 2101
1614 1614 bundle2-input-bundle: 4 parts total
1615 1615 transaction abort!
1616 1616 rollback completed
1617 1617 abort: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82")
1618 1618 no rollback information available
1619 1619 2:fb35475503ef
1620 1620
1621 1621
1622 1622 Branch acl empty allow test
1623 1623
1624 1624 $ init_config
1625 1625 $ echo "[acl.allow.branches]" >> $config
1626 1626 $ do_push astro
1627 1627 Pushing as user astro
1628 1628 hgrc = """
1629 1629 [hooks]
1630 1630 pretxnchangegroup.acl = python:hgext.acl.hook
1631 1631 [acl]
1632 1632 sources = push
1633 1633 [extensions]
1634 1634 [acl.allow.branches]
1635 1635 """
1636 1636 pushing to ../b
1637 1637 query 1; heads
1638 1638 searching for changes
1639 1639 all remote heads known locally
1640 1640 listing keys for "phases"
1641 1641 checking for updated bookmarks
1642 1642 listing keys for "bookmarks"
1643 1643 listing keys for "bookmarks"
1644 1644 4 changesets found
1645 1645 list of changesets:
1646 1646 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1647 1647 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1648 1648 911600dab2ae7a9baff75958b84fe606851ce955
1649 1649 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1650 1650 bundle2-output-bundle: "HG20", 5 parts total
1651 bundle2-output-part: "replycaps" 147 bytes payload
1651 bundle2-output-part: "replycaps" 155 bytes payload
1652 1652 bundle2-output-part: "check:heads" streamed payload
1653 1653 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1654 1654 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
1655 1655 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
1656 1656 bundle2-input-bundle: with-transaction
1657 1657 bundle2-input-part: "replycaps" supported
1658 bundle2-input-part: total payload size 147
1658 bundle2-input-part: total payload size 155
1659 1659 bundle2-input-part: "check:heads" supported
1660 1660 bundle2-input-part: total payload size 20
1661 1661 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1662 1662 adding changesets
1663 1663 add changeset ef1ea85a6374
1664 1664 add changeset f9cafe1212c8
1665 1665 add changeset 911600dab2ae
1666 1666 add changeset e8fc755d4d82
1667 1667 adding manifests
1668 1668 adding file changes
1669 1669 adding abc.txt revisions
1670 1670 adding foo/Bar/file.txt revisions
1671 1671 adding foo/file.txt revisions
1672 1672 adding quux/file.py revisions
1673 1673 added 4 changesets with 4 changes to 4 files (+1 heads)
1674 1674 calling hook pretxnchangegroup.acl: hgext.acl.hook
1675 1675 acl: checking access for user "astro"
1676 1676 acl: acl.allow.branches enabled, 0 entries for user astro
1677 1677 acl: acl.deny.branches not enabled
1678 1678 acl: acl.allow not enabled
1679 1679 acl: acl.deny not enabled
1680 1680 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1681 1681 bundle2-input-part: total payload size 2101
1682 1682 bundle2-input-bundle: 4 parts total
1683 1683 transaction abort!
1684 1684 rollback completed
1685 1685 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1686 1686 no rollback information available
1687 1687 2:fb35475503ef
1688 1688
1689 1689
1690 1690 Branch acl allow other
1691 1691
1692 1692 $ init_config
1693 1693 $ echo "[acl.allow.branches]" >> $config
1694 1694 $ echo "* = george" >> $config
1695 1695 $ do_push astro
1696 1696 Pushing as user astro
1697 1697 hgrc = """
1698 1698 [hooks]
1699 1699 pretxnchangegroup.acl = python:hgext.acl.hook
1700 1700 [acl]
1701 1701 sources = push
1702 1702 [extensions]
1703 1703 [acl.allow.branches]
1704 1704 * = george
1705 1705 """
1706 1706 pushing to ../b
1707 1707 query 1; heads
1708 1708 searching for changes
1709 1709 all remote heads known locally
1710 1710 listing keys for "phases"
1711 1711 checking for updated bookmarks
1712 1712 listing keys for "bookmarks"
1713 1713 listing keys for "bookmarks"
1714 1714 4 changesets found
1715 1715 list of changesets:
1716 1716 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1717 1717 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1718 1718 911600dab2ae7a9baff75958b84fe606851ce955
1719 1719 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1720 1720 bundle2-output-bundle: "HG20", 5 parts total
1721 bundle2-output-part: "replycaps" 147 bytes payload
1721 bundle2-output-part: "replycaps" 155 bytes payload
1722 1722 bundle2-output-part: "check:heads" streamed payload
1723 1723 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1724 1724 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
1725 1725 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
1726 1726 bundle2-input-bundle: with-transaction
1727 1727 bundle2-input-part: "replycaps" supported
1728 bundle2-input-part: total payload size 147
1728 bundle2-input-part: total payload size 155
1729 1729 bundle2-input-part: "check:heads" supported
1730 1730 bundle2-input-part: total payload size 20
1731 1731 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1732 1732 adding changesets
1733 1733 add changeset ef1ea85a6374
1734 1734 add changeset f9cafe1212c8
1735 1735 add changeset 911600dab2ae
1736 1736 add changeset e8fc755d4d82
1737 1737 adding manifests
1738 1738 adding file changes
1739 1739 adding abc.txt revisions
1740 1740 adding foo/Bar/file.txt revisions
1741 1741 adding foo/file.txt revisions
1742 1742 adding quux/file.py revisions
1743 1743 added 4 changesets with 4 changes to 4 files (+1 heads)
1744 1744 calling hook pretxnchangegroup.acl: hgext.acl.hook
1745 1745 acl: checking access for user "astro"
1746 1746 acl: acl.allow.branches enabled, 0 entries for user astro
1747 1747 acl: acl.deny.branches not enabled
1748 1748 acl: acl.allow not enabled
1749 1749 acl: acl.deny not enabled
1750 1750 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1751 1751 bundle2-input-part: total payload size 2101
1752 1752 bundle2-input-bundle: 4 parts total
1753 1753 transaction abort!
1754 1754 rollback completed
1755 1755 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1756 1756 no rollback information available
1757 1757 2:fb35475503ef
1758 1758
1759 1759 $ do_push george
1760 1760 Pushing as user george
1761 1761 hgrc = """
1762 1762 [hooks]
1763 1763 pretxnchangegroup.acl = python:hgext.acl.hook
1764 1764 [acl]
1765 1765 sources = push
1766 1766 [extensions]
1767 1767 [acl.allow.branches]
1768 1768 * = george
1769 1769 """
1770 1770 pushing to ../b
1771 1771 query 1; heads
1772 1772 searching for changes
1773 1773 all remote heads known locally
1774 1774 listing keys for "phases"
1775 1775 checking for updated bookmarks
1776 1776 listing keys for "bookmarks"
1777 1777 listing keys for "bookmarks"
1778 1778 4 changesets found
1779 1779 list of changesets:
1780 1780 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1781 1781 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1782 1782 911600dab2ae7a9baff75958b84fe606851ce955
1783 1783 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1784 1784 bundle2-output-bundle: "HG20", 5 parts total
1785 bundle2-output-part: "replycaps" 147 bytes payload
1785 bundle2-output-part: "replycaps" 155 bytes payload
1786 1786 bundle2-output-part: "check:heads" streamed payload
1787 1787 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1788 1788 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
1789 1789 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
1790 1790 bundle2-input-bundle: with-transaction
1791 1791 bundle2-input-part: "replycaps" supported
1792 bundle2-input-part: total payload size 147
1792 bundle2-input-part: total payload size 155
1793 1793 bundle2-input-part: "check:heads" supported
1794 1794 bundle2-input-part: total payload size 20
1795 1795 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1796 1796 adding changesets
1797 1797 add changeset ef1ea85a6374
1798 1798 add changeset f9cafe1212c8
1799 1799 add changeset 911600dab2ae
1800 1800 add changeset e8fc755d4d82
1801 1801 adding manifests
1802 1802 adding file changes
1803 1803 adding abc.txt revisions
1804 1804 adding foo/Bar/file.txt revisions
1805 1805 adding foo/file.txt revisions
1806 1806 adding quux/file.py revisions
1807 1807 added 4 changesets with 4 changes to 4 files (+1 heads)
1808 1808 calling hook pretxnchangegroup.acl: hgext.acl.hook
1809 1809 acl: checking access for user "george"
1810 1810 acl: acl.allow.branches enabled, 1 entries for user george
1811 1811 acl: acl.deny.branches not enabled
1812 1812 acl: acl.allow not enabled
1813 1813 acl: acl.deny not enabled
1814 1814 acl: branch access granted: "ef1ea85a6374" on branch "default"
1815 1815 acl: path access granted: "ef1ea85a6374"
1816 1816 acl: branch access granted: "f9cafe1212c8" on branch "default"
1817 1817 acl: path access granted: "f9cafe1212c8"
1818 1818 acl: branch access granted: "911600dab2ae" on branch "default"
1819 1819 acl: path access granted: "911600dab2ae"
1820 1820 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1821 1821 acl: path access granted: "e8fc755d4d82"
1822 1822 bundle2-input-part: total payload size 2101
1823 1823 bundle2-input-part: "pushkey" (advisory) (params: 4 mandatory) supported
1824 1824 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
1825 1825 bundle2-input-part: "pushkey" (advisory) (params: 4 mandatory) supported
1826 1826 pushing key for "phases:e8fc755d4d8217ee5b0c2bb41558c40d43b92c01"
1827 1827 bundle2-input-bundle: 4 parts total
1828 1828 updating the branch cache
1829 1829 bundle2-output-bundle: "HG20", 3 parts total
1830 1830 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1831 1831 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1832 1832 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1833 1833 bundle2-input-bundle: with-transaction
1834 1834 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1835 1835 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1836 1836 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1837 1837 bundle2-input-bundle: 2 parts total
1838 1838 listing keys for "phases"
1839 1839 try to push obsolete markers to remote
1840 1840 repository tip rolled back to revision 2 (undo push)
1841 1841 2:fb35475503ef
1842 1842
1843 1843
1844 1844 Branch acl conflicting allow
1845 1845 asterisk ends up applying to all branches and allowing george to
1846 1846 push foobar into the remote
1847 1847
1848 1848 $ init_config
1849 1849 $ echo "[acl.allow.branches]" >> $config
1850 1850 $ echo "foobar = astro" >> $config
1851 1851 $ echo "* = george" >> $config
1852 1852 $ do_push george
1853 1853 Pushing as user george
1854 1854 hgrc = """
1855 1855 [hooks]
1856 1856 pretxnchangegroup.acl = python:hgext.acl.hook
1857 1857 [acl]
1858 1858 sources = push
1859 1859 [extensions]
1860 1860 [acl.allow.branches]
1861 1861 foobar = astro
1862 1862 * = george
1863 1863 """
1864 1864 pushing to ../b
1865 1865 query 1; heads
1866 1866 searching for changes
1867 1867 all remote heads known locally
1868 1868 listing keys for "phases"
1869 1869 checking for updated bookmarks
1870 1870 listing keys for "bookmarks"
1871 1871 listing keys for "bookmarks"
1872 1872 4 changesets found
1873 1873 list of changesets:
1874 1874 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1875 1875 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1876 1876 911600dab2ae7a9baff75958b84fe606851ce955
1877 1877 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1878 1878 bundle2-output-bundle: "HG20", 5 parts total
1879 bundle2-output-part: "replycaps" 147 bytes payload
1879 bundle2-output-part: "replycaps" 155 bytes payload
1880 1880 bundle2-output-part: "check:heads" streamed payload
1881 1881 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1882 1882 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
1883 1883 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
1884 1884 bundle2-input-bundle: with-transaction
1885 1885 bundle2-input-part: "replycaps" supported
1886 bundle2-input-part: total payload size 147
1886 bundle2-input-part: total payload size 155
1887 1887 bundle2-input-part: "check:heads" supported
1888 1888 bundle2-input-part: total payload size 20
1889 1889 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1890 1890 adding changesets
1891 1891 add changeset ef1ea85a6374
1892 1892 add changeset f9cafe1212c8
1893 1893 add changeset 911600dab2ae
1894 1894 add changeset e8fc755d4d82
1895 1895 adding manifests
1896 1896 adding file changes
1897 1897 adding abc.txt revisions
1898 1898 adding foo/Bar/file.txt revisions
1899 1899 adding foo/file.txt revisions
1900 1900 adding quux/file.py revisions
1901 1901 added 4 changesets with 4 changes to 4 files (+1 heads)
1902 1902 calling hook pretxnchangegroup.acl: hgext.acl.hook
1903 1903 acl: checking access for user "george"
1904 1904 acl: acl.allow.branches enabled, 1 entries for user george
1905 1905 acl: acl.deny.branches not enabled
1906 1906 acl: acl.allow not enabled
1907 1907 acl: acl.deny not enabled
1908 1908 acl: branch access granted: "ef1ea85a6374" on branch "default"
1909 1909 acl: path access granted: "ef1ea85a6374"
1910 1910 acl: branch access granted: "f9cafe1212c8" on branch "default"
1911 1911 acl: path access granted: "f9cafe1212c8"
1912 1912 acl: branch access granted: "911600dab2ae" on branch "default"
1913 1913 acl: path access granted: "911600dab2ae"
1914 1914 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1915 1915 acl: path access granted: "e8fc755d4d82"
1916 1916 bundle2-input-part: total payload size 2101
1917 1917 bundle2-input-part: "pushkey" (advisory) (params: 4 mandatory) supported
1918 1918 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
1919 1919 bundle2-input-part: "pushkey" (advisory) (params: 4 mandatory) supported
1920 1920 pushing key for "phases:e8fc755d4d8217ee5b0c2bb41558c40d43b92c01"
1921 1921 bundle2-input-bundle: 4 parts total
1922 1922 updating the branch cache
1923 1923 bundle2-output-bundle: "HG20", 3 parts total
1924 1924 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1925 1925 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1926 1926 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1927 1927 bundle2-input-bundle: with-transaction
1928 1928 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1929 1929 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1930 1930 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1931 1931 bundle2-input-bundle: 2 parts total
1932 1932 listing keys for "phases"
1933 1933 try to push obsolete markers to remote
1934 1934 repository tip rolled back to revision 2 (undo push)
1935 1935 2:fb35475503ef
1936 1936
1937 1937 Branch acl conflicting deny
1938 1938
1939 1939 $ init_config
1940 1940 $ echo "[acl.deny.branches]" >> $config
1941 1941 $ echo "foobar = astro" >> $config
1942 1942 $ echo "default = astro" >> $config
1943 1943 $ echo "* = george" >> $config
1944 1944 $ do_push george
1945 1945 Pushing as user george
1946 1946 hgrc = """
1947 1947 [hooks]
1948 1948 pretxnchangegroup.acl = python:hgext.acl.hook
1949 1949 [acl]
1950 1950 sources = push
1951 1951 [extensions]
1952 1952 [acl.deny.branches]
1953 1953 foobar = astro
1954 1954 default = astro
1955 1955 * = george
1956 1956 """
1957 1957 pushing to ../b
1958 1958 query 1; heads
1959 1959 searching for changes
1960 1960 all remote heads known locally
1961 1961 listing keys for "phases"
1962 1962 checking for updated bookmarks
1963 1963 listing keys for "bookmarks"
1964 1964 listing keys for "bookmarks"
1965 1965 4 changesets found
1966 1966 list of changesets:
1967 1967 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1968 1968 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1969 1969 911600dab2ae7a9baff75958b84fe606851ce955
1970 1970 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1971 1971 bundle2-output-bundle: "HG20", 5 parts total
1972 bundle2-output-part: "replycaps" 147 bytes payload
1972 bundle2-output-part: "replycaps" 155 bytes payload
1973 1973 bundle2-output-part: "check:heads" streamed payload
1974 1974 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1975 1975 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
1976 1976 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
1977 1977 bundle2-input-bundle: with-transaction
1978 1978 bundle2-input-part: "replycaps" supported
1979 bundle2-input-part: total payload size 147
1979 bundle2-input-part: total payload size 155
1980 1980 bundle2-input-part: "check:heads" supported
1981 1981 bundle2-input-part: total payload size 20
1982 1982 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1983 1983 adding changesets
1984 1984 add changeset ef1ea85a6374
1985 1985 add changeset f9cafe1212c8
1986 1986 add changeset 911600dab2ae
1987 1987 add changeset e8fc755d4d82
1988 1988 adding manifests
1989 1989 adding file changes
1990 1990 adding abc.txt revisions
1991 1991 adding foo/Bar/file.txt revisions
1992 1992 adding foo/file.txt revisions
1993 1993 adding quux/file.py revisions
1994 1994 added 4 changesets with 4 changes to 4 files (+1 heads)
1995 1995 calling hook pretxnchangegroup.acl: hgext.acl.hook
1996 1996 acl: checking access for user "george"
1997 1997 acl: acl.allow.branches not enabled
1998 1998 acl: acl.deny.branches enabled, 1 entries for user george
1999 1999 acl: acl.allow not enabled
2000 2000 acl: acl.deny not enabled
2001 2001 error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2002 2002 bundle2-input-part: total payload size 2101
2003 2003 bundle2-input-bundle: 4 parts total
2004 2004 transaction abort!
2005 2005 rollback completed
2006 2006 abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2007 2007 no rollback information available
2008 2008 2:fb35475503ef
2009 2009
2010 2010 User 'astro' must not be denied
2011 2011
2012 2012 $ init_config
2013 2013 $ echo "[acl.deny.branches]" >> $config
2014 2014 $ echo "default = !astro" >> $config
2015 2015 $ do_push astro
2016 2016 Pushing as user astro
2017 2017 hgrc = """
2018 2018 [hooks]
2019 2019 pretxnchangegroup.acl = python:hgext.acl.hook
2020 2020 [acl]
2021 2021 sources = push
2022 2022 [extensions]
2023 2023 [acl.deny.branches]
2024 2024 default = !astro
2025 2025 """
2026 2026 pushing to ../b
2027 2027 query 1; heads
2028 2028 searching for changes
2029 2029 all remote heads known locally
2030 2030 listing keys for "phases"
2031 2031 checking for updated bookmarks
2032 2032 listing keys for "bookmarks"
2033 2033 listing keys for "bookmarks"
2034 2034 4 changesets found
2035 2035 list of changesets:
2036 2036 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
2037 2037 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
2038 2038 911600dab2ae7a9baff75958b84fe606851ce955
2039 2039 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
2040 2040 bundle2-output-bundle: "HG20", 5 parts total
2041 bundle2-output-part: "replycaps" 147 bytes payload
2041 bundle2-output-part: "replycaps" 155 bytes payload
2042 2042 bundle2-output-part: "check:heads" streamed payload
2043 2043 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
2044 2044 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
2045 2045 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
2046 2046 bundle2-input-bundle: with-transaction
2047 2047 bundle2-input-part: "replycaps" supported
2048 bundle2-input-part: total payload size 147
2048 bundle2-input-part: total payload size 155
2049 2049 bundle2-input-part: "check:heads" supported
2050 2050 bundle2-input-part: total payload size 20
2051 2051 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
2052 2052 adding changesets
2053 2053 add changeset ef1ea85a6374
2054 2054 add changeset f9cafe1212c8
2055 2055 add changeset 911600dab2ae
2056 2056 add changeset e8fc755d4d82
2057 2057 adding manifests
2058 2058 adding file changes
2059 2059 adding abc.txt revisions
2060 2060 adding foo/Bar/file.txt revisions
2061 2061 adding foo/file.txt revisions
2062 2062 adding quux/file.py revisions
2063 2063 added 4 changesets with 4 changes to 4 files (+1 heads)
2064 2064 calling hook pretxnchangegroup.acl: hgext.acl.hook
2065 2065 acl: checking access for user "astro"
2066 2066 acl: acl.allow.branches not enabled
2067 2067 acl: acl.deny.branches enabled, 0 entries for user astro
2068 2068 acl: acl.allow not enabled
2069 2069 acl: acl.deny not enabled
2070 2070 acl: branch access granted: "ef1ea85a6374" on branch "default"
2071 2071 acl: path access granted: "ef1ea85a6374"
2072 2072 acl: branch access granted: "f9cafe1212c8" on branch "default"
2073 2073 acl: path access granted: "f9cafe1212c8"
2074 2074 acl: branch access granted: "911600dab2ae" on branch "default"
2075 2075 acl: path access granted: "911600dab2ae"
2076 2076 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
2077 2077 acl: path access granted: "e8fc755d4d82"
2078 2078 bundle2-input-part: total payload size 2101
2079 2079 bundle2-input-part: "pushkey" (advisory) (params: 4 mandatory) supported
2080 2080 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
2081 2081 bundle2-input-part: "pushkey" (advisory) (params: 4 mandatory) supported
2082 2082 pushing key for "phases:e8fc755d4d8217ee5b0c2bb41558c40d43b92c01"
2083 2083 bundle2-input-bundle: 4 parts total
2084 2084 updating the branch cache
2085 2085 bundle2-output-bundle: "HG20", 3 parts total
2086 2086 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
2087 2087 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
2088 2088 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
2089 2089 bundle2-input-bundle: with-transaction
2090 2090 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
2091 2091 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
2092 2092 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
2093 2093 bundle2-input-bundle: 2 parts total
2094 2094 listing keys for "phases"
2095 2095 try to push obsolete markers to remote
2096 2096 repository tip rolled back to revision 2 (undo push)
2097 2097 2:fb35475503ef
2098 2098
2099 2099
2100 2100 Non-astro users must be denied
2101 2101
2102 2102 $ do_push george
2103 2103 Pushing as user george
2104 2104 hgrc = """
2105 2105 [hooks]
2106 2106 pretxnchangegroup.acl = python:hgext.acl.hook
2107 2107 [acl]
2108 2108 sources = push
2109 2109 [extensions]
2110 2110 [acl.deny.branches]
2111 2111 default = !astro
2112 2112 """
2113 2113 pushing to ../b
2114 2114 query 1; heads
2115 2115 searching for changes
2116 2116 all remote heads known locally
2117 2117 listing keys for "phases"
2118 2118 checking for updated bookmarks
2119 2119 listing keys for "bookmarks"
2120 2120 listing keys for "bookmarks"
2121 2121 4 changesets found
2122 2122 list of changesets:
2123 2123 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
2124 2124 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
2125 2125 911600dab2ae7a9baff75958b84fe606851ce955
2126 2126 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
2127 2127 bundle2-output-bundle: "HG20", 5 parts total
2128 bundle2-output-part: "replycaps" 147 bytes payload
2128 bundle2-output-part: "replycaps" 155 bytes payload
2129 2129 bundle2-output-part: "check:heads" streamed payload
2130 2130 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
2131 2131 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
2132 2132 bundle2-output-part: "pushkey" (advisory) (params: 4 mandatory) empty payload
2133 2133 bundle2-input-bundle: with-transaction
2134 2134 bundle2-input-part: "replycaps" supported
2135 bundle2-input-part: total payload size 147
2135 bundle2-input-part: total payload size 155
2136 2136 bundle2-input-part: "check:heads" supported
2137 2137 bundle2-input-part: total payload size 20
2138 2138 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
2139 2139 adding changesets
2140 2140 add changeset ef1ea85a6374
2141 2141 add changeset f9cafe1212c8
2142 2142 add changeset 911600dab2ae
2143 2143 add changeset e8fc755d4d82
2144 2144 adding manifests
2145 2145 adding file changes
2146 2146 adding abc.txt revisions
2147 2147 adding foo/Bar/file.txt revisions
2148 2148 adding foo/file.txt revisions
2149 2149 adding quux/file.py revisions
2150 2150 added 4 changesets with 4 changes to 4 files (+1 heads)
2151 2151 calling hook pretxnchangegroup.acl: hgext.acl.hook
2152 2152 acl: checking access for user "george"
2153 2153 acl: acl.allow.branches not enabled
2154 2154 acl: acl.deny.branches enabled, 1 entries for user george
2155 2155 acl: acl.allow not enabled
2156 2156 acl: acl.deny not enabled
2157 2157 error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2158 2158 bundle2-input-part: total payload size 2101
2159 2159 bundle2-input-bundle: 4 parts total
2160 2160 transaction abort!
2161 2161 rollback completed
2162 2162 abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2163 2163 no rollback information available
2164 2164 2:fb35475503ef
2165 2165
2166 2166
@@ -1,869 +1,869 b''
1 1 Test exchange of common information using bundle2
2 2
3 3
4 4 $ getmainid() {
5 5 > hg -R main log --template '{node}\n' --rev "$1"
6 6 > }
7 7
8 8 enable obsolescence
9 9
10 10 $ cat > $TESTTMP/bundle2-pushkey-hook.sh << EOF
11 11 > echo pushkey: lock state after \"\$HG_NAMESPACE\"
12 12 > hg debuglock
13 13 > EOF
14 14
15 15 $ cat >> $HGRCPATH << EOF
16 16 > [experimental]
17 17 > evolution=createmarkers,exchange
18 18 > bundle2-exp=True
19 19 > bundle2-output-capture=True
20 20 > [ui]
21 21 > ssh=dummyssh
22 22 > logtemplate={rev}:{node|short} {phase} {author} {bookmarks} {desc|firstline}
23 23 > [web]
24 24 > push_ssl = false
25 25 > allow_push = *
26 26 > [phases]
27 27 > publish=False
28 28 > [hooks]
29 29 > pretxnclose.tip = hg log -r tip -T "pre-close-tip:{node|short} {phase} {bookmarks}\n"
30 30 > txnclose.tip = hg log -r tip -T "postclose-tip:{node|short} {phase} {bookmarks}\n"
31 31 > txnclose.env = sh -c "HG_LOCAL= printenv.py txnclose"
32 32 > pushkey= sh "$TESTTMP/bundle2-pushkey-hook.sh"
33 33 > EOF
34 34
35 35 The extension requires a repo (currently unused)
36 36
37 37 $ hg init main
38 38 $ cd main
39 39 $ touch a
40 40 $ hg add a
41 41 $ hg commit -m 'a'
42 42 pre-close-tip:3903775176ed draft
43 43 postclose-tip:3903775176ed draft
44 44 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
45 45
46 46 $ hg unbundle $TESTDIR/bundles/rebase.hg
47 47 adding changesets
48 48 adding manifests
49 49 adding file changes
50 50 added 8 changesets with 7 changes to 7 files (+3 heads)
51 51 pre-close-tip:02de42196ebe draft
52 52 postclose-tip:02de42196ebe draft
53 53 txnclose hook: HG_NODE=cd010b8cd998f3981a5a8115f94f8da4ab506089 HG_PHASES_MOVED=1 HG_SOURCE=unbundle HG_TXNID=TXN:* HG_TXNNAME=unbundle (glob)
54 54 bundle:*/tests/bundles/rebase.hg HG_URL=bundle:*/tests/bundles/rebase.hg (glob)
55 55 (run 'hg heads' to see heads, 'hg merge' to merge)
56 56
57 57 $ cd ..
58 58
59 59 Real world exchange
60 60 =====================
61 61
62 62 Add more obsolescence information
63 63
64 64 $ hg -R main debugobsolete -d '0 0' 1111111111111111111111111111111111111111 `getmainid 9520eea781bc`
65 65 pre-close-tip:02de42196ebe draft
66 66 postclose-tip:02de42196ebe draft
67 67 txnclose hook: HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
68 68 $ hg -R main debugobsolete -d '0 0' 2222222222222222222222222222222222222222 `getmainid 24b6387c8c8c`
69 69 pre-close-tip:02de42196ebe draft
70 70 postclose-tip:02de42196ebe draft
71 71 txnclose hook: HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
72 72
73 73 clone --pull
74 74
75 75 $ hg -R main phase --public cd010b8cd998
76 76 pre-close-tip:02de42196ebe draft
77 77 postclose-tip:02de42196ebe draft
78 78 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=phase (glob)
79 79 $ hg clone main other --pull --rev 9520eea781bc
80 80 adding changesets
81 81 adding manifests
82 82 adding file changes
83 83 added 2 changesets with 2 changes to 2 files
84 84 1 new obsolescence markers
85 85 pre-close-tip:9520eea781bc draft
86 86 postclose-tip:9520eea781bc draft
87 87 txnclose hook: HG_NEW_OBSMARKERS=1 HG_NODE=cd010b8cd998f3981a5a8115f94f8da4ab506089 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
88 88 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
89 89 updating to branch default
90 90 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
91 91 $ hg -R other log -G
92 92 @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
93 93 |
94 94 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
95 95
96 96 $ hg -R other debugobsolete
97 97 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
98 98
99 99 pull
100 100
101 101 $ hg -R main phase --public 9520eea781bc
102 102 pre-close-tip:02de42196ebe draft
103 103 postclose-tip:02de42196ebe draft
104 104 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=phase (glob)
105 105 $ hg -R other pull -r 24b6387c8c8c
106 106 pulling from $TESTTMP/main (glob)
107 107 searching for changes
108 108 adding changesets
109 109 adding manifests
110 110 adding file changes
111 111 added 1 changesets with 1 changes to 1 files (+1 heads)
112 112 1 new obsolescence markers
113 113 pre-close-tip:24b6387c8c8c draft
114 114 postclose-tip:24b6387c8c8c draft
115 115 txnclose hook: HG_NEW_OBSMARKERS=1 HG_NODE=24b6387c8c8cae37178880f3fa95ded3cb1cf785 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
116 116 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
117 117 (run 'hg heads' to see heads, 'hg merge' to merge)
118 118 $ hg -R other log -G
119 119 o 2:24b6387c8c8c draft Nicolas Dumazet <nicdumz.commits@gmail.com> F
120 120 |
121 121 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
122 122 |/
123 123 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
124 124
125 125 $ hg -R other debugobsolete
126 126 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
127 127 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
128 128
129 129 pull empty (with phase movement)
130 130
131 131 $ hg -R main phase --public 24b6387c8c8c
132 132 pre-close-tip:02de42196ebe draft
133 133 postclose-tip:02de42196ebe draft
134 134 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=phase (glob)
135 135 $ hg -R other pull -r 24b6387c8c8c
136 136 pulling from $TESTTMP/main (glob)
137 137 no changes found
138 138 pre-close-tip:24b6387c8c8c public
139 139 postclose-tip:24b6387c8c8c public
140 140 txnclose hook: HG_NEW_OBSMARKERS=0 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
141 141 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
142 142 $ hg -R other log -G
143 143 o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
144 144 |
145 145 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
146 146 |/
147 147 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
148 148
149 149 $ hg -R other debugobsolete
150 150 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
151 151 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
152 152
153 153 pull empty
154 154
155 155 $ hg -R other pull -r 24b6387c8c8c
156 156 pulling from $TESTTMP/main (glob)
157 157 no changes found
158 158 pre-close-tip:24b6387c8c8c public
159 159 postclose-tip:24b6387c8c8c public
160 160 txnclose hook: HG_NEW_OBSMARKERS=0 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
161 161 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
162 162 $ hg -R other log -G
163 163 o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
164 164 |
165 165 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
166 166 |/
167 167 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
168 168
169 169 $ hg -R other debugobsolete
170 170 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
171 171 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
172 172
173 173 add extra data to test their exchange during push
174 174
175 175 $ hg -R main bookmark --rev eea13746799a book_eea1
176 176 $ hg -R main debugobsolete -d '0 0' 3333333333333333333333333333333333333333 `getmainid eea13746799a`
177 177 pre-close-tip:02de42196ebe draft
178 178 postclose-tip:02de42196ebe draft
179 179 txnclose hook: HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
180 180 $ hg -R main bookmark --rev 02de42196ebe book_02de
181 181 $ hg -R main debugobsolete -d '0 0' 4444444444444444444444444444444444444444 `getmainid 02de42196ebe`
182 182 pre-close-tip:02de42196ebe draft book_02de
183 183 postclose-tip:02de42196ebe draft book_02de
184 184 txnclose hook: HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
185 185 $ hg -R main bookmark --rev 42ccdea3bb16 book_42cc
186 186 $ hg -R main debugobsolete -d '0 0' 5555555555555555555555555555555555555555 `getmainid 42ccdea3bb16`
187 187 pre-close-tip:02de42196ebe draft book_02de
188 188 postclose-tip:02de42196ebe draft book_02de
189 189 txnclose hook: HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
190 190 $ hg -R main bookmark --rev 5fddd98957c8 book_5fdd
191 191 $ hg -R main debugobsolete -d '0 0' 6666666666666666666666666666666666666666 `getmainid 5fddd98957c8`
192 192 pre-close-tip:02de42196ebe draft book_02de
193 193 postclose-tip:02de42196ebe draft book_02de
194 194 txnclose hook: HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
195 195 $ hg -R main bookmark --rev 32af7686d403 book_32af
196 196 $ hg -R main debugobsolete -d '0 0' 7777777777777777777777777777777777777777 `getmainid 32af7686d403`
197 197 pre-close-tip:02de42196ebe draft book_02de
198 198 postclose-tip:02de42196ebe draft book_02de
199 199 txnclose hook: HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
200 200
201 201 $ hg -R other bookmark --rev cd010b8cd998 book_eea1
202 202 $ hg -R other bookmark --rev cd010b8cd998 book_02de
203 203 $ hg -R other bookmark --rev cd010b8cd998 book_42cc
204 204 $ hg -R other bookmark --rev cd010b8cd998 book_5fdd
205 205 $ hg -R other bookmark --rev cd010b8cd998 book_32af
206 206
207 207 $ hg -R main phase --public eea13746799a
208 208 pre-close-tip:02de42196ebe draft book_02de
209 209 postclose-tip:02de42196ebe draft book_02de
210 210 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=phase (glob)
211 211
212 212 push
213 213 $ hg -R main push other --rev eea13746799a --bookmark book_eea1
214 214 pushing to other
215 215 searching for changes
216 216 remote: adding changesets
217 217 remote: adding manifests
218 218 remote: adding file changes
219 219 remote: added 1 changesets with 0 changes to 0 files (-1 heads)
220 220 remote: 1 new obsolescence markers
221 221 remote: pre-close-tip:eea13746799a public book_eea1
222 222 remote: pushkey: lock state after "phases"
223 223 remote: lock: free
224 224 remote: wlock: free
225 225 remote: pushkey: lock state after "bookmarks"
226 226 remote: lock: free
227 227 remote: wlock: free
228 228 remote: postclose-tip:eea13746799a public book_eea1
229 229 remote: txnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_NEW_OBSMARKERS=1 HG_NODE=eea13746799a9e0bfd88f29d3c2e9dc9389f524f HG_PHASES_MOVED=1 HG_SOURCE=push HG_TXNID=TXN:* HG_TXNNAME=push HG_URL=push (glob)
230 230 updating bookmark book_eea1
231 231 pre-close-tip:02de42196ebe draft book_02de
232 232 postclose-tip:02de42196ebe draft book_02de
233 233 txnclose hook: HG_SOURCE=push-response HG_TXNID=TXN:* HG_TXNNAME=push-response (glob)
234 234 file:/*/$TESTTMP/other HG_URL=file:$TESTTMP/other (glob)
235 235 $ hg -R other log -G
236 236 o 3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> book_eea1 G
237 237 |\
238 238 | o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
239 239 | |
240 240 @ | 1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
241 241 |/
242 242 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_02de book_32af book_42cc book_5fdd A
243 243
244 244 $ hg -R other debugobsolete
245 245 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
246 246 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
247 247 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
248 248
249 249 pull over ssh
250 250
251 251 $ hg -R other pull ssh://user@dummy/main -r 02de42196ebe --bookmark book_02de
252 252 pulling from ssh://user@dummy/main
253 253 searching for changes
254 254 adding changesets
255 255 adding manifests
256 256 adding file changes
257 257 added 1 changesets with 1 changes to 1 files (+1 heads)
258 258 1 new obsolescence markers
259 259 updating bookmark book_02de
260 260 pre-close-tip:02de42196ebe draft book_02de
261 261 postclose-tip:02de42196ebe draft book_02de
262 262 txnclose hook: HG_BOOKMARK_MOVED=1 HG_NEW_OBSMARKERS=1 HG_NODE=02de42196ebee42ef284b6780a87cdc96e8eaab6 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
263 263 ssh://user@dummy/main HG_URL=ssh://user@dummy/main
264 264 (run 'hg heads' to see heads, 'hg merge' to merge)
265 265 $ hg -R other debugobsolete
266 266 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
267 267 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
268 268 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
269 269 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
270 270
271 271 pull over http
272 272
273 273 $ hg -R main serve -p $HGPORT -d --pid-file=main.pid -E main-error.log
274 274 $ cat main.pid >> $DAEMON_PIDS
275 275
276 276 $ hg -R other pull http://localhost:$HGPORT/ -r 42ccdea3bb16 --bookmark book_42cc
277 277 pulling from http://localhost:$HGPORT/
278 278 searching for changes
279 279 adding changesets
280 280 adding manifests
281 281 adding file changes
282 282 added 1 changesets with 1 changes to 1 files (+1 heads)
283 283 1 new obsolescence markers
284 284 updating bookmark book_42cc
285 285 pre-close-tip:42ccdea3bb16 draft book_42cc
286 286 postclose-tip:42ccdea3bb16 draft book_42cc
287 287 txnclose hook: HG_BOOKMARK_MOVED=1 HG_NEW_OBSMARKERS=1 HG_NODE=42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
288 288 http://localhost:$HGPORT/ HG_URL=http://localhost:$HGPORT/
289 289 (run 'hg heads .' to see heads, 'hg merge' to merge)
290 290 $ cat main-error.log
291 291 $ hg -R other debugobsolete
292 292 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
293 293 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
294 294 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
295 295 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
296 296 5555555555555555555555555555555555555555 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
297 297
298 298 push over ssh
299 299
300 300 $ hg -R main push ssh://user@dummy/other -r 5fddd98957c8 --bookmark book_5fdd
301 301 pushing to ssh://user@dummy/other
302 302 searching for changes
303 303 remote: adding changesets
304 304 remote: adding manifests
305 305 remote: adding file changes
306 306 remote: added 1 changesets with 1 changes to 1 files
307 307 remote: 1 new obsolescence markers
308 308 remote: pre-close-tip:5fddd98957c8 draft book_5fdd
309 309 remote: pushkey: lock state after "bookmarks"
310 310 remote: lock: free
311 311 remote: wlock: free
312 312 remote: postclose-tip:5fddd98957c8 draft book_5fdd
313 313 remote: txnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_NEW_OBSMARKERS=1 HG_NODE=5fddd98957c8a54a4d436dfe1da9d87f21a1b97b HG_SOURCE=serve HG_TXNID=TXN:* HG_TXNNAME=serve HG_URL=remote:ssh:127.0.0.1 (glob)
314 314 updating bookmark book_5fdd
315 315 pre-close-tip:02de42196ebe draft book_02de
316 316 postclose-tip:02de42196ebe draft book_02de
317 317 txnclose hook: HG_SOURCE=push-response HG_TXNID=TXN:* HG_TXNNAME=push-response (glob)
318 318 ssh://user@dummy/other HG_URL=ssh://user@dummy/other
319 319 $ hg -R other log -G
320 320 o 6:5fddd98957c8 draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_5fdd C
321 321 |
322 322 o 5:42ccdea3bb16 draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_42cc B
323 323 |
324 324 | o 4:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_02de H
325 325 | |
326 326 | | o 3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> book_eea1 G
327 327 | |/|
328 328 | o | 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
329 329 |/ /
330 330 | @ 1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
331 331 |/
332 332 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_32af A
333 333
334 334 $ hg -R other debugobsolete
335 335 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
336 336 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
337 337 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
338 338 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
339 339 5555555555555555555555555555555555555555 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
340 340 6666666666666666666666666666666666666666 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
341 341
342 342 push over http
343 343
344 344 $ hg -R other serve -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
345 345 $ cat other.pid >> $DAEMON_PIDS
346 346
347 347 $ hg -R main phase --public 32af7686d403
348 348 pre-close-tip:02de42196ebe draft book_02de
349 349 postclose-tip:02de42196ebe draft book_02de
350 350 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=phase (glob)
351 351 $ hg -R main push http://localhost:$HGPORT2/ -r 32af7686d403 --bookmark book_32af
352 352 pushing to http://localhost:$HGPORT2/
353 353 searching for changes
354 354 remote: adding changesets
355 355 remote: adding manifests
356 356 remote: adding file changes
357 357 remote: added 1 changesets with 1 changes to 1 files
358 358 remote: 1 new obsolescence markers
359 359 remote: pre-close-tip:32af7686d403 public book_32af
360 360 remote: pushkey: lock state after "phases"
361 361 remote: lock: free
362 362 remote: wlock: free
363 363 remote: pushkey: lock state after "bookmarks"
364 364 remote: lock: free
365 365 remote: wlock: free
366 366 remote: postclose-tip:32af7686d403 public book_32af
367 367 remote: txnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_NEW_OBSMARKERS=1 HG_NODE=32af7686d403cf45b5d95f2d70cebea587ac806a HG_PHASES_MOVED=1 HG_SOURCE=serve HG_TXNID=TXN:* HG_TXNNAME=serve HG_URL=remote:http:127.0.0.1: (glob)
368 368 updating bookmark book_32af
369 369 pre-close-tip:02de42196ebe draft book_02de
370 370 postclose-tip:02de42196ebe draft book_02de
371 371 txnclose hook: HG_SOURCE=push-response HG_TXNID=TXN:* HG_TXNNAME=push-response (glob)
372 372 http://localhost:$HGPORT2/ HG_URL=http://localhost:$HGPORT2/
373 373 $ cat other-error.log
374 374
375 375 Check final content.
376 376
377 377 $ hg -R other log -G
378 378 o 7:32af7686d403 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_32af D
379 379 |
380 380 o 6:5fddd98957c8 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_5fdd C
381 381 |
382 382 o 5:42ccdea3bb16 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_42cc B
383 383 |
384 384 | o 4:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_02de H
385 385 | |
386 386 | | o 3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> book_eea1 G
387 387 | |/|
388 388 | o | 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
389 389 |/ /
390 390 | @ 1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
391 391 |/
392 392 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
393 393
394 394 $ hg -R other debugobsolete
395 395 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
396 396 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
397 397 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
398 398 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
399 399 5555555555555555555555555555555555555555 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
400 400 6666666666666666666666666666666666666666 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
401 401 7777777777777777777777777777777777777777 32af7686d403cf45b5d95f2d70cebea587ac806a 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
402 402
403 403 (check that no 'pending' files remain)
404 404
405 405 $ ls -1 other/.hg/bookmarks*
406 406 other/.hg/bookmarks
407 407 $ ls -1 other/.hg/store/phaseroots*
408 408 other/.hg/store/phaseroots
409 409 $ ls -1 other/.hg/store/00changelog.i*
410 410 other/.hg/store/00changelog.i
411 411
412 412 Error Handling
413 413 ==============
414 414
415 415 Check that errors are properly returned to the client during push.
416 416
417 417 Setting up
418 418
419 419 $ cat > failpush.py << EOF
420 420 > """A small extension that makes push fails when using bundle2
421 421 >
422 422 > used to test error handling in bundle2
423 423 > """
424 424 >
425 425 > from mercurial import util
426 426 > from mercurial import bundle2
427 427 > from mercurial import exchange
428 428 > from mercurial import extensions
429 429 >
430 430 > def _pushbundle2failpart(pushop, bundler):
431 431 > reason = pushop.ui.config('failpush', 'reason', None)
432 432 > part = None
433 433 > if reason == 'abort':
434 434 > bundler.newpart('test:abort')
435 435 > if reason == 'unknown':
436 436 > bundler.newpart('test:unknown')
437 437 > if reason == 'race':
438 438 > # 20 Bytes of crap
439 439 > bundler.newpart('check:heads', data='01234567890123456789')
440 440 >
441 441 > @bundle2.parthandler("test:abort")
442 442 > def handleabort(op, part):
443 443 > raise util.Abort('Abandon ship!', hint="don't panic")
444 444 >
445 445 > def uisetup(ui):
446 446 > exchange.b2partsgenmapping['failpart'] = _pushbundle2failpart
447 447 > exchange.b2partsgenorder.insert(0, 'failpart')
448 448 >
449 449 > EOF
450 450
451 451 $ cd main
452 452 $ hg up tip
453 453 3 files updated, 0 files merged, 1 files removed, 0 files unresolved
454 454 $ echo 'I' > I
455 455 $ hg add I
456 456 $ hg ci -m 'I'
457 457 pre-close-tip:e7ec4e813ba6 draft
458 458 postclose-tip:e7ec4e813ba6 draft
459 459 txnclose hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
460 460 $ hg id
461 461 e7ec4e813ba6 tip
462 462 $ cd ..
463 463
464 464 $ cat << EOF >> $HGRCPATH
465 465 > [extensions]
466 466 > failpush=$TESTTMP/failpush.py
467 467 > EOF
468 468
469 469 $ killdaemons.py
470 470 $ hg -R other serve -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
471 471 $ cat other.pid >> $DAEMON_PIDS
472 472
473 473 Doing the actual push: Abort error
474 474
475 475 $ cat << EOF >> $HGRCPATH
476 476 > [failpush]
477 477 > reason = abort
478 478 > EOF
479 479
480 480 $ hg -R main push other -r e7ec4e813ba6
481 481 pushing to other
482 482 searching for changes
483 483 abort: Abandon ship!
484 484 (don't panic)
485 485 [255]
486 486
487 487 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
488 488 pushing to ssh://user@dummy/other
489 489 searching for changes
490 490 abort: Abandon ship!
491 491 (don't panic)
492 492 [255]
493 493
494 494 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
495 495 pushing to http://localhost:$HGPORT2/
496 496 searching for changes
497 497 abort: Abandon ship!
498 498 (don't panic)
499 499 [255]
500 500
501 501
502 502 Doing the actual push: unknown mandatory parts
503 503
504 504 $ cat << EOF >> $HGRCPATH
505 505 > [failpush]
506 506 > reason = unknown
507 507 > EOF
508 508
509 509 $ hg -R main push other -r e7ec4e813ba6
510 510 pushing to other
511 511 searching for changes
512 512 abort: missing support for test:unknown
513 513 [255]
514 514
515 515 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
516 516 pushing to ssh://user@dummy/other
517 517 searching for changes
518 518 abort: missing support for test:unknown
519 519 [255]
520 520
521 521 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
522 522 pushing to http://localhost:$HGPORT2/
523 523 searching for changes
524 524 abort: missing support for test:unknown
525 525 [255]
526 526
527 527 Doing the actual push: race
528 528
529 529 $ cat << EOF >> $HGRCPATH
530 530 > [failpush]
531 531 > reason = race
532 532 > EOF
533 533
534 534 $ hg -R main push other -r e7ec4e813ba6
535 535 pushing to other
536 536 searching for changes
537 537 abort: push failed:
538 538 'repository changed while pushing - please try again'
539 539 [255]
540 540
541 541 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
542 542 pushing to ssh://user@dummy/other
543 543 searching for changes
544 544 abort: push failed:
545 545 'repository changed while pushing - please try again'
546 546 [255]
547 547
548 548 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
549 549 pushing to http://localhost:$HGPORT2/
550 550 searching for changes
551 551 abort: push failed:
552 552 'repository changed while pushing - please try again'
553 553 [255]
554 554
555 555 Doing the actual push: hook abort
556 556
557 557 $ cat << EOF >> $HGRCPATH
558 558 > [failpush]
559 559 > reason =
560 560 > [hooks]
561 561 > pretxnclose.failpush = sh -c "echo 'You shall not pass!'; false"
562 562 > txnabort.failpush = sh -c "echo 'Cleaning up the mess...'"
563 563 > EOF
564 564
565 565 $ killdaemons.py
566 566 $ hg -R other serve -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
567 567 $ cat other.pid >> $DAEMON_PIDS
568 568
569 569 $ hg -R main push other -r e7ec4e813ba6
570 570 pushing to other
571 571 searching for changes
572 572 remote: adding changesets
573 573 remote: adding manifests
574 574 remote: adding file changes
575 575 remote: added 1 changesets with 1 changes to 1 files
576 576 remote: pre-close-tip:e7ec4e813ba6 draft
577 577 remote: You shall not pass!
578 578 remote: transaction abort!
579 579 remote: Cleaning up the mess...
580 580 remote: rollback completed
581 581 abort: pretxnclose.failpush hook exited with status 1
582 582 [255]
583 583
584 584 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
585 585 pushing to ssh://user@dummy/other
586 586 searching for changes
587 587 remote: adding changesets
588 588 remote: adding manifests
589 589 remote: adding file changes
590 590 remote: added 1 changesets with 1 changes to 1 files
591 591 remote: pre-close-tip:e7ec4e813ba6 draft
592 592 remote: You shall not pass!
593 593 remote: transaction abort!
594 594 remote: Cleaning up the mess...
595 595 remote: rollback completed
596 596 abort: pretxnclose.failpush hook exited with status 1
597 597 [255]
598 598
599 599 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
600 600 pushing to http://localhost:$HGPORT2/
601 601 searching for changes
602 602 remote: adding changesets
603 603 remote: adding manifests
604 604 remote: adding file changes
605 605 remote: added 1 changesets with 1 changes to 1 files
606 606 remote: pre-close-tip:e7ec4e813ba6 draft
607 607 remote: You shall not pass!
608 608 remote: transaction abort!
609 609 remote: Cleaning up the mess...
610 610 remote: rollback completed
611 611 abort: pretxnclose.failpush hook exited with status 1
612 612 [255]
613 613
614 614 (check that no 'pending' files remain)
615 615
616 616 $ ls -1 other/.hg/bookmarks*
617 617 other/.hg/bookmarks
618 618 $ ls -1 other/.hg/store/phaseroots*
619 619 other/.hg/store/phaseroots
620 620 $ ls -1 other/.hg/store/00changelog.i*
621 621 other/.hg/store/00changelog.i
622 622
623 623 Check error from hook during the unbundling process itself
624 624
625 625 $ cat << EOF >> $HGRCPATH
626 626 > pretxnchangegroup = sh -c "echo 'Fail early!'; false"
627 627 > EOF
628 628 $ killdaemons.py # reload http config
629 629 $ hg -R other serve -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
630 630 $ cat other.pid >> $DAEMON_PIDS
631 631
632 632 $ hg -R main push other -r e7ec4e813ba6
633 633 pushing to other
634 634 searching for changes
635 635 remote: adding changesets
636 636 remote: adding manifests
637 637 remote: adding file changes
638 638 remote: added 1 changesets with 1 changes to 1 files
639 639 remote: Fail early!
640 640 remote: transaction abort!
641 641 remote: Cleaning up the mess...
642 642 remote: rollback completed
643 643 abort: pretxnchangegroup hook exited with status 1
644 644 [255]
645 645 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
646 646 pushing to ssh://user@dummy/other
647 647 searching for changes
648 648 remote: adding changesets
649 649 remote: adding manifests
650 650 remote: adding file changes
651 651 remote: added 1 changesets with 1 changes to 1 files
652 652 remote: Fail early!
653 653 remote: transaction abort!
654 654 remote: Cleaning up the mess...
655 655 remote: rollback completed
656 656 abort: pretxnchangegroup hook exited with status 1
657 657 [255]
658 658 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
659 659 pushing to http://localhost:$HGPORT2/
660 660 searching for changes
661 661 remote: adding changesets
662 662 remote: adding manifests
663 663 remote: adding file changes
664 664 remote: added 1 changesets with 1 changes to 1 files
665 665 remote: Fail early!
666 666 remote: transaction abort!
667 667 remote: Cleaning up the mess...
668 668 remote: rollback completed
669 669 abort: pretxnchangegroup hook exited with status 1
670 670 [255]
671 671
672 672 Check output capture control.
673 673
674 674 (should be still forced for http, disabled for local and ssh)
675 675
676 676 $ cat >> $HGRCPATH << EOF
677 677 > [experimental]
678 678 > bundle2-output-capture=False
679 679 > EOF
680 680
681 681 $ hg -R main push other -r e7ec4e813ba6
682 682 pushing to other
683 683 searching for changes
684 684 adding changesets
685 685 adding manifests
686 686 adding file changes
687 687 added 1 changesets with 1 changes to 1 files
688 688 Fail early!
689 689 transaction abort!
690 690 Cleaning up the mess...
691 691 rollback completed
692 692 abort: pretxnchangegroup hook exited with status 1
693 693 [255]
694 694 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
695 695 pushing to ssh://user@dummy/other
696 696 searching for changes
697 697 remote: adding changesets
698 698 remote: adding manifests
699 699 remote: adding file changes
700 700 remote: added 1 changesets with 1 changes to 1 files
701 701 remote: Fail early!
702 702 remote: transaction abort!
703 703 remote: Cleaning up the mess...
704 704 remote: rollback completed
705 705 abort: pretxnchangegroup hook exited with status 1
706 706 [255]
707 707 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
708 708 pushing to http://localhost:$HGPORT2/
709 709 searching for changes
710 710 remote: adding changesets
711 711 remote: adding manifests
712 712 remote: adding file changes
713 713 remote: added 1 changesets with 1 changes to 1 files
714 714 remote: Fail early!
715 715 remote: transaction abort!
716 716 remote: Cleaning up the mess...
717 717 remote: rollback completed
718 718 abort: pretxnchangegroup hook exited with status 1
719 719 [255]
720 720
721 721 Check abort from mandatory pushkey
722 722
723 723 $ cat > mandatorypart.py << EOF
724 724 > from mercurial import exchange
725 725 > from mercurial import pushkey
726 726 > from mercurial import node
727 727 > from mercurial import error
728 728 > @exchange.b2partsgenerator('failingpuskey')
729 729 > def addfailingpushey(pushop, bundler):
730 730 > enc = pushkey.encode
731 731 > part = bundler.newpart('pushkey')
732 732 > part.addparam('namespace', enc('phases'))
733 733 > part.addparam('key', enc(pushop.repo['cd010b8cd998'].hex()))
734 734 > part.addparam('old', enc(str(0))) # successful update
735 735 > part.addparam('new', enc(str(0)))
736 736 > def fail(pushop, exc):
737 737 > raise error.Abort('Correct phase push failed (because hooks)')
738 738 > pushop.pkfailcb[part.id] = fail
739 739 > EOF
740 740 $ cat >> $HGRCPATH << EOF
741 741 > [hooks]
742 742 > pretxnchangegroup=
743 743 > pretxnclose.failpush=
744 744 > prepushkey.failpush = sh -c "echo 'do not push the key !'; false"
745 745 > [extensions]
746 746 > mandatorypart=$TESTTMP/mandatorypart.py
747 747 > EOF
748 748 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS # reload http config
749 749 $ hg -R other serve -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
750 750 $ cat other.pid >> $DAEMON_PIDS
751 751
752 752 (Failure from a hook)
753 753
754 754 $ hg -R main push other -r e7ec4e813ba6
755 755 pushing to other
756 756 searching for changes
757 757 adding changesets
758 758 adding manifests
759 759 adding file changes
760 760 added 1 changesets with 1 changes to 1 files
761 761 do not push the key !
762 762 pushkey-abort: prepushkey.failpush hook exited with status 1
763 763 transaction abort!
764 764 Cleaning up the mess...
765 765 rollback completed
766 766 abort: Correct phase push failed (because hooks)
767 767 [255]
768 768 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
769 769 pushing to ssh://user@dummy/other
770 770 searching for changes
771 771 remote: adding changesets
772 772 remote: adding manifests
773 773 remote: adding file changes
774 774 remote: added 1 changesets with 1 changes to 1 files
775 775 remote: do not push the key !
776 776 remote: pushkey-abort: prepushkey.failpush hook exited with status 1
777 777 remote: transaction abort!
778 778 remote: Cleaning up the mess...
779 779 remote: rollback completed
780 abort: failed to update value for "phases/cd010b8cd998f3981a5a8115f94f8da4ab506089"
780 abort: Correct phase push failed (because hooks)
781 781 [255]
782 782 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
783 783 pushing to http://localhost:$HGPORT2/
784 784 searching for changes
785 785 remote: adding changesets
786 786 remote: adding manifests
787 787 remote: adding file changes
788 788 remote: added 1 changesets with 1 changes to 1 files
789 789 remote: do not push the key !
790 790 remote: pushkey-abort: prepushkey.failpush hook exited with status 1
791 791 remote: transaction abort!
792 792 remote: Cleaning up the mess...
793 793 remote: rollback completed
794 abort: failed to update value for "phases/cd010b8cd998f3981a5a8115f94f8da4ab506089"
794 abort: Correct phase push failed (because hooks)
795 795 [255]
796 796
797 797 (Failure from a the pushkey)
798 798
799 799 $ cat > mandatorypart.py << EOF
800 800 > from mercurial import exchange
801 801 > from mercurial import pushkey
802 802 > from mercurial import node
803 803 > from mercurial import error
804 804 > @exchange.b2partsgenerator('failingpuskey')
805 805 > def addfailingpushey(pushop, bundler):
806 806 > enc = pushkey.encode
807 807 > part = bundler.newpart('pushkey')
808 808 > part.addparam('namespace', enc('phases'))
809 809 > part.addparam('key', enc(pushop.repo['cd010b8cd998'].hex()))
810 810 > part.addparam('old', enc(str(4))) # will fail
811 811 > part.addparam('new', enc(str(3)))
812 812 > def fail(pushop, exc):
813 813 > raise error.Abort('Clown phase push failed')
814 814 > pushop.pkfailcb[part.id] = fail
815 815 > EOF
816 816 $ cat >> $HGRCPATH << EOF
817 817 > [hooks]
818 818 > prepushkey.failpush =
819 819 > EOF
820 820 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS # reload http config
821 821 $ hg -R other serve -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
822 822 $ cat other.pid >> $DAEMON_PIDS
823 823
824 824 $ hg -R main push other -r e7ec4e813ba6
825 825 pushing to other
826 826 searching for changes
827 827 adding changesets
828 828 adding manifests
829 829 adding file changes
830 830 added 1 changesets with 1 changes to 1 files
831 831 transaction abort!
832 832 Cleaning up the mess...
833 833 rollback completed
834 834 pushkey: lock state after "phases"
835 835 lock: free
836 836 wlock: free
837 837 abort: Clown phase push failed
838 838 [255]
839 839 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
840 840 pushing to ssh://user@dummy/other
841 841 searching for changes
842 842 remote: adding changesets
843 843 remote: adding manifests
844 844 remote: adding file changes
845 845 remote: added 1 changesets with 1 changes to 1 files
846 846 remote: transaction abort!
847 847 remote: Cleaning up the mess...
848 848 remote: rollback completed
849 849 remote: pushkey: lock state after "phases"
850 850 remote: lock: free
851 851 remote: wlock: free
852 abort: failed to update value for "phases/cd010b8cd998f3981a5a8115f94f8da4ab506089"
852 abort: Clown phase push failed
853 853 [255]
854 854 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
855 855 pushing to http://localhost:$HGPORT2/
856 856 searching for changes
857 857 remote: adding changesets
858 858 remote: adding manifests
859 859 remote: adding file changes
860 860 remote: added 1 changesets with 1 changes to 1 files
861 861 remote: transaction abort!
862 862 remote: Cleaning up the mess...
863 863 remote: rollback completed
864 864 remote: pushkey: lock state after "phases"
865 865 remote: lock: free
866 866 remote: wlock: free
867 abort: failed to update value for "phases/cd010b8cd998f3981a5a8115f94f8da4ab506089"
867 abort: Clown phase push failed
868 868 [255]
869 869
@@ -1,130 +1,130 b''
1 1 #require serve
2 2 $ cat << EOF >> $HGRCPATH
3 3 > [experimental]
4 4 > # drop me once bundle2 is the default,
5 5 > # added to get test change early.
6 6 > bundle2-exp = True
7 7 > EOF
8 8
9 9 $ hg init a
10 10 $ cd a
11 11 $ echo a > a
12 12 $ hg ci -Ama -d '1123456789 0'
13 13 adding a
14 14 $ hg --config server.uncompressed=True serve -p $HGPORT -d --pid-file=hg.pid
15 15 $ cat hg.pid >> $DAEMON_PIDS
16 16 $ cd ..
17 17 $ tinyproxy.py $HGPORT1 localhost >proxy.log 2>&1 </dev/null &
18 18 $ while [ ! -f proxy.pid ]; do sleep 0; done
19 19 $ cat proxy.pid >> $DAEMON_PIDS
20 20
21 21 url for proxy, stream
22 22
23 23 $ http_proxy=http://localhost:$HGPORT1/ hg --config http_proxy.always=True clone --uncompressed http://localhost:$HGPORT/ b
24 24 streaming all changes
25 25 3 files to transfer, 303 bytes of data
26 26 transferred * bytes in * seconds (*/sec) (glob)
27 27 searching for changes
28 28 no changes found
29 29 updating to branch default
30 30 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
31 31 $ cd b
32 32 $ hg verify
33 33 checking changesets
34 34 checking manifests
35 35 crosschecking files in changesets and manifests
36 36 checking files
37 37 1 files, 1 changesets, 1 total revisions
38 38 $ cd ..
39 39
40 40 url for proxy, pull
41 41
42 42 $ http_proxy=http://localhost:$HGPORT1/ hg --config http_proxy.always=True clone http://localhost:$HGPORT/ b-pull
43 43 requesting all changes
44 44 adding changesets
45 45 adding manifests
46 46 adding file changes
47 47 added 1 changesets with 1 changes to 1 files
48 48 updating to branch default
49 49 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
50 50 $ cd b-pull
51 51 $ hg verify
52 52 checking changesets
53 53 checking manifests
54 54 crosschecking files in changesets and manifests
55 55 checking files
56 56 1 files, 1 changesets, 1 total revisions
57 57 $ cd ..
58 58
59 59 host:port for proxy
60 60
61 61 $ http_proxy=localhost:$HGPORT1 hg clone --config http_proxy.always=True http://localhost:$HGPORT/ c
62 62 requesting all changes
63 63 adding changesets
64 64 adding manifests
65 65 adding file changes
66 66 added 1 changesets with 1 changes to 1 files
67 67 updating to branch default
68 68 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
69 69
70 70 proxy url with user name and password
71 71
72 72 $ http_proxy=http://user:passwd@localhost:$HGPORT1 hg clone --config http_proxy.always=True http://localhost:$HGPORT/ d
73 73 requesting all changes
74 74 adding changesets
75 75 adding manifests
76 76 adding file changes
77 77 added 1 changesets with 1 changes to 1 files
78 78 updating to branch default
79 79 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
80 80
81 81 url with user name and password
82 82
83 83 $ http_proxy=http://user:passwd@localhost:$HGPORT1 hg clone --config http_proxy.always=True http://user:passwd@localhost:$HGPORT/ e
84 84 requesting all changes
85 85 adding changesets
86 86 adding manifests
87 87 adding file changes
88 88 added 1 changesets with 1 changes to 1 files
89 89 updating to branch default
90 90 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
91 91
92 92 bad host:port for proxy
93 93
94 94 $ http_proxy=localhost:$HGPORT2 hg clone --config http_proxy.always=True http://localhost:$HGPORT/ f
95 95 abort: error: Connection refused
96 96 [255]
97 97
98 98 do not use the proxy if it is in the no list
99 99
100 100 $ http_proxy=localhost:$HGPORT1 hg clone --config http_proxy.no=localhost http://localhost:$HGPORT/ g
101 101 requesting all changes
102 102 adding changesets
103 103 adding manifests
104 104 adding file changes
105 105 added 1 changesets with 1 changes to 1 files
106 106 updating to branch default
107 107 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
108 108 $ cat proxy.log
109 109 * - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob)
110 110 * - - [*] "GET http://localhost:$HGPORT/?cmd=branchmap HTTP/1.1" - - (glob)
111 111 * - - [*] "GET http://localhost:$HGPORT/?cmd=stream_out HTTP/1.1" - - (glob)
112 112 * - - [*] "GET http://localhost:$HGPORT/?cmd=batch HTTP/1.1" - - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D83180e7845de420a1bb46896fd5fe05294f8d629 (glob)
113 * - - [*] "GET http://localhost:$HGPORT/?cmd=getbundle HTTP/1.1" - - x-hgarg-1:bundlecaps=HG20%2Cbundle2%3DHG20%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%250Ahgtagsfnodes%250Alistkeys%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps&cg=0&common=83180e7845de420a1bb46896fd5fe05294f8d629&heads=83180e7845de420a1bb46896fd5fe05294f8d629&listkeys=phase%2Cbookmarks (glob)
113 * - - [*] "GET http://localhost:$HGPORT/?cmd=getbundle HTTP/1.1" - - x-hgarg-1:bundlecaps=HG20%2Cbundle2%3DHG20%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps&cg=0&common=83180e7845de420a1bb46896fd5fe05294f8d629&heads=83180e7845de420a1bb46896fd5fe05294f8d629&listkeys=phase%2Cbookmarks (glob)
114 114 * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=phases (glob)
115 115 * - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob)
116 116 * - - [*] "GET http://localhost:$HGPORT/?cmd=batch HTTP/1.1" - - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D (glob)
117 * - - [*] "GET http://localhost:$HGPORT/?cmd=getbundle HTTP/1.1" - - x-hgarg-1:bundlecaps=HG20%2Cbundle2%3DHG20%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%250Ahgtagsfnodes%250Alistkeys%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps&cg=1&common=0000000000000000000000000000000000000000&heads=83180e7845de420a1bb46896fd5fe05294f8d629&listkeys=phase%2Cbookmarks (glob)
117 * - - [*] "GET http://localhost:$HGPORT/?cmd=getbundle HTTP/1.1" - - x-hgarg-1:bundlecaps=HG20%2Cbundle2%3DHG20%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps&cg=1&common=0000000000000000000000000000000000000000&heads=83180e7845de420a1bb46896fd5fe05294f8d629&listkeys=phase%2Cbookmarks (glob)
118 118 * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=phases (glob)
119 119 * - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob)
120 120 * - - [*] "GET http://localhost:$HGPORT/?cmd=batch HTTP/1.1" - - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D (glob)
121 * - - [*] "GET http://localhost:$HGPORT/?cmd=getbundle HTTP/1.1" - - x-hgarg-1:bundlecaps=HG20%2Cbundle2%3DHG20%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%250Ahgtagsfnodes%250Alistkeys%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps&cg=1&common=0000000000000000000000000000000000000000&heads=83180e7845de420a1bb46896fd5fe05294f8d629&listkeys=phase%2Cbookmarks (glob)
121 * - - [*] "GET http://localhost:$HGPORT/?cmd=getbundle HTTP/1.1" - - x-hgarg-1:bundlecaps=HG20%2Cbundle2%3DHG20%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps&cg=1&common=0000000000000000000000000000000000000000&heads=83180e7845de420a1bb46896fd5fe05294f8d629&listkeys=phase%2Cbookmarks (glob)
122 122 * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=phases (glob)
123 123 * - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob)
124 124 * - - [*] "GET http://localhost:$HGPORT/?cmd=batch HTTP/1.1" - - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D (glob)
125 * - - [*] "GET http://localhost:$HGPORT/?cmd=getbundle HTTP/1.1" - - x-hgarg-1:bundlecaps=HG20%2Cbundle2%3DHG20%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%250Ahgtagsfnodes%250Alistkeys%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps&cg=1&common=0000000000000000000000000000000000000000&heads=83180e7845de420a1bb46896fd5fe05294f8d629&listkeys=phase%2Cbookmarks (glob)
125 * - - [*] "GET http://localhost:$HGPORT/?cmd=getbundle HTTP/1.1" - - x-hgarg-1:bundlecaps=HG20%2Cbundle2%3DHG20%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps&cg=1&common=0000000000000000000000000000000000000000&heads=83180e7845de420a1bb46896fd5fe05294f8d629&listkeys=phase%2Cbookmarks (glob)
126 126 * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=phases (glob)
127 127 * - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob)
128 128 * - - [*] "GET http://localhost:$HGPORT/?cmd=batch HTTP/1.1" - - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D (glob)
129 * - - [*] "GET http://localhost:$HGPORT/?cmd=getbundle HTTP/1.1" - - x-hgarg-1:bundlecaps=HG20%2Cbundle2%3DHG20%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%250Ahgtagsfnodes%250Alistkeys%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps&cg=1&common=0000000000000000000000000000000000000000&heads=83180e7845de420a1bb46896fd5fe05294f8d629&listkeys=phase%2Cbookmarks (glob)
129 * - - [*] "GET http://localhost:$HGPORT/?cmd=getbundle HTTP/1.1" - - x-hgarg-1:bundlecaps=HG20%2Cbundle2%3DHG20%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps&cg=1&common=0000000000000000000000000000000000000000&heads=83180e7845de420a1bb46896fd5fe05294f8d629&listkeys=phase%2Cbookmarks (glob)
130 130 * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys HTTP/1.1" - - x-hgarg-1:namespace=phases (glob)
@@ -1,323 +1,323 b''
1 1 #require serve
2 2
3 3 $ hg init test
4 4 $ cd test
5 5 $ echo foo>foo
6 6 $ mkdir foo.d foo.d/bAr.hg.d foo.d/baR.d.hg
7 7 $ echo foo>foo.d/foo
8 8 $ echo bar>foo.d/bAr.hg.d/BaR
9 9 $ echo bar>foo.d/baR.d.hg/bAR
10 10 $ hg commit -A -m 1
11 11 adding foo
12 12 adding foo.d/bAr.hg.d/BaR
13 13 adding foo.d/baR.d.hg/bAR
14 14 adding foo.d/foo
15 15 $ hg serve -p $HGPORT -d --pid-file=../hg1.pid -E ../error.log
16 16 $ hg --config server.uncompressed=False serve -p $HGPORT1 -d --pid-file=../hg2.pid
17 17
18 18 Test server address cannot be reused
19 19
20 20 #if windows
21 21 $ hg serve -p $HGPORT1 2>&1
22 22 abort: cannot start server at ':$HGPORT1': * (glob)
23 23 [255]
24 24 #else
25 25 $ hg serve -p $HGPORT1 2>&1
26 26 abort: cannot start server at ':$HGPORT1': Address already in use
27 27 [255]
28 28 #endif
29 29 $ cd ..
30 30 $ cat hg1.pid hg2.pid >> $DAEMON_PIDS
31 31
32 32 clone via stream
33 33
34 34 $ hg clone --uncompressed http://localhost:$HGPORT/ copy 2>&1
35 35 streaming all changes
36 36 6 files to transfer, 606 bytes of data
37 37 transferred * bytes in * seconds (*/sec) (glob)
38 38 searching for changes
39 39 no changes found
40 40 updating to branch default
41 41 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
42 42 $ hg verify -R copy
43 43 checking changesets
44 44 checking manifests
45 45 crosschecking files in changesets and manifests
46 46 checking files
47 47 4 files, 1 changesets, 4 total revisions
48 48
49 49 try to clone via stream, should use pull instead
50 50
51 51 $ hg clone --uncompressed http://localhost:$HGPORT1/ copy2
52 52 requesting all changes
53 53 adding changesets
54 54 adding manifests
55 55 adding file changes
56 56 added 1 changesets with 4 changes to 4 files
57 57 updating to branch default
58 58 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
59 59
60 60 clone via pull
61 61
62 62 $ hg clone http://localhost:$HGPORT1/ copy-pull
63 63 requesting all changes
64 64 adding changesets
65 65 adding manifests
66 66 adding file changes
67 67 added 1 changesets with 4 changes to 4 files
68 68 updating to branch default
69 69 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
70 70 $ hg verify -R copy-pull
71 71 checking changesets
72 72 checking manifests
73 73 crosschecking files in changesets and manifests
74 74 checking files
75 75 4 files, 1 changesets, 4 total revisions
76 76 $ cd test
77 77 $ echo bar > bar
78 78 $ hg commit -A -d '1 0' -m 2
79 79 adding bar
80 80 $ cd ..
81 81
82 82 clone over http with --update
83 83
84 84 $ hg clone http://localhost:$HGPORT1/ updated --update 0
85 85 requesting all changes
86 86 adding changesets
87 87 adding manifests
88 88 adding file changes
89 89 added 2 changesets with 5 changes to 5 files
90 90 updating to branch default
91 91 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
92 92 $ hg log -r . -R updated
93 93 changeset: 0:8b6053c928fe
94 94 user: test
95 95 date: Thu Jan 01 00:00:00 1970 +0000
96 96 summary: 1
97 97
98 98 $ rm -rf updated
99 99
100 100 incoming via HTTP
101 101
102 102 $ hg clone http://localhost:$HGPORT1/ --rev 0 partial
103 103 adding changesets
104 104 adding manifests
105 105 adding file changes
106 106 added 1 changesets with 4 changes to 4 files
107 107 updating to branch default
108 108 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
109 109 $ cd partial
110 110 $ touch LOCAL
111 111 $ hg ci -qAm LOCAL
112 112 $ hg incoming http://localhost:$HGPORT1/ --template '{desc}\n'
113 113 comparing with http://localhost:$HGPORT1/
114 114 searching for changes
115 115 2
116 116 $ cd ..
117 117
118 118 pull
119 119
120 120 $ cd copy-pull
121 121 $ echo '[hooks]' >> .hg/hgrc
122 122 $ echo "changegroup = printenv.py changegroup" >> .hg/hgrc
123 123 $ hg pull
124 124 pulling from http://localhost:$HGPORT1/
125 125 searching for changes
126 126 adding changesets
127 127 adding manifests
128 128 adding file changes
129 129 added 1 changesets with 1 changes to 1 files
130 130 changegroup hook: HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_SOURCE=pull HG_TXNID=TXN:* HG_URL=http://localhost:$HGPORT1/ (glob)
131 131 (run 'hg update' to get a working copy)
132 132 $ cd ..
133 133
134 134 clone from invalid URL
135 135
136 136 $ hg clone http://localhost:$HGPORT/bad
137 137 abort: HTTP Error 404: Not Found
138 138 [255]
139 139
140 140 test http authentication
141 141 + use the same server to test server side streaming preference
142 142
143 143 $ cd test
144 144 $ cat << EOT > userpass.py
145 145 > import base64
146 146 > from mercurial.hgweb import common
147 147 > def perform_authentication(hgweb, req, op):
148 148 > auth = req.env.get('HTTP_AUTHORIZATION')
149 149 > if not auth:
150 150 > raise common.ErrorResponse(common.HTTP_UNAUTHORIZED, 'who',
151 151 > [('WWW-Authenticate', 'Basic Realm="mercurial"')])
152 152 > if base64.b64decode(auth.split()[1]).split(':', 1) != ['user', 'pass']:
153 153 > raise common.ErrorResponse(common.HTTP_FORBIDDEN, 'no')
154 154 > def extsetup():
155 155 > common.permhooks.insert(0, perform_authentication)
156 156 > EOT
157 157 $ hg --config extensions.x=userpass.py serve -p $HGPORT2 -d --pid-file=pid \
158 158 > --config server.preferuncompressed=True \
159 159 > --config web.push_ssl=False --config web.allow_push=* -A ../access.log
160 160 $ cat pid >> $DAEMON_PIDS
161 161
162 162 $ cat << EOF > get_pass.py
163 163 > import getpass
164 164 > def newgetpass(arg):
165 165 > return "pass"
166 166 > getpass.getpass = newgetpass
167 167 > EOF
168 168
169 169 $ hg id http://localhost:$HGPORT2/
170 170 abort: http authorization required for http://localhost:$HGPORT2/
171 171 [255]
172 172 $ hg id http://localhost:$HGPORT2/
173 173 abort: http authorization required for http://localhost:$HGPORT2/
174 174 [255]
175 175 $ hg id --config ui.interactive=true --config extensions.getpass=get_pass.py http://user@localhost:$HGPORT2/
176 176 http authorization required for http://localhost:$HGPORT2/
177 177 realm: mercurial
178 178 user: user
179 179 password: 5fed3813f7f5
180 180 $ hg id http://user:pass@localhost:$HGPORT2/
181 181 5fed3813f7f5
182 182 $ echo '[auth]' >> .hg/hgrc
183 183 $ echo 'l.schemes=http' >> .hg/hgrc
184 184 $ echo 'l.prefix=lo' >> .hg/hgrc
185 185 $ echo 'l.username=user' >> .hg/hgrc
186 186 $ echo 'l.password=pass' >> .hg/hgrc
187 187 $ hg id http://localhost:$HGPORT2/
188 188 5fed3813f7f5
189 189 $ hg id http://localhost:$HGPORT2/
190 190 5fed3813f7f5
191 191 $ hg id http://user@localhost:$HGPORT2/
192 192 5fed3813f7f5
193 193 $ hg clone http://user:pass@localhost:$HGPORT2/ dest 2>&1
194 194 streaming all changes
195 195 7 files to transfer, 916 bytes of data
196 196 transferred * bytes in * seconds (*/sec) (glob)
197 197 searching for changes
198 198 no changes found
199 199 updating to branch default
200 200 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
201 201 --pull should override server's preferuncompressed
202 202 $ hg clone --pull http://user:pass@localhost:$HGPORT2/ dest-pull 2>&1
203 203 requesting all changes
204 204 adding changesets
205 205 adding manifests
206 206 adding file changes
207 207 added 2 changesets with 5 changes to 5 files
208 208 updating to branch default
209 209 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
210 210
211 211 $ hg id http://user2@localhost:$HGPORT2/
212 212 abort: http authorization required for http://localhost:$HGPORT2/
213 213 [255]
214 214 $ hg id http://user:pass2@localhost:$HGPORT2/
215 215 abort: HTTP Error 403: no
216 216 [255]
217 217
218 218 $ hg -R dest tag -r tip top
219 219 $ hg -R dest push http://user:pass@localhost:$HGPORT2/
220 220 pushing to http://user:***@localhost:$HGPORT2/
221 221 searching for changes
222 222 remote: adding changesets
223 223 remote: adding manifests
224 224 remote: adding file changes
225 225 remote: added 1 changesets with 1 changes to 1 files
226 226 $ hg rollback -q
227 227
228 228 $ cut -c38- ../access.log
229 229 "GET /?cmd=capabilities HTTP/1.1" 200 -
230 230 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip
231 231 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces
232 232 "GET /?cmd=capabilities HTTP/1.1" 200 -
233 233 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip
234 234 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces
235 235 "GET /?cmd=capabilities HTTP/1.1" 200 -
236 236 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip
237 237 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces
238 238 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces
239 239 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks
240 240 "GET /?cmd=capabilities HTTP/1.1" 200 -
241 241 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip
242 242 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces
243 243 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces
244 244 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks
245 245 "GET /?cmd=capabilities HTTP/1.1" 200 -
246 246 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip
247 247 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces
248 248 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces
249 249 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks
250 250 "GET /?cmd=capabilities HTTP/1.1" 200 -
251 251 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip
252 252 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces
253 253 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces
254 254 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks
255 255 "GET /?cmd=capabilities HTTP/1.1" 200 -
256 256 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip
257 257 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces
258 258 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces
259 259 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks
260 260 "GET /?cmd=capabilities HTTP/1.1" 200 -
261 261 "GET /?cmd=branchmap HTTP/1.1" 200 -
262 262 "GET /?cmd=stream_out HTTP/1.1" 401 -
263 263 "GET /?cmd=stream_out HTTP/1.1" 200 -
264 264 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D5fed3813f7f5e1824344fdc9cf8f63bb662c292d
265 "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bundlecaps=HG20%2Cbundle2%3DHG20%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%250Ahgtagsfnodes%250Alistkeys%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps&cg=0&common=5fed3813f7f5e1824344fdc9cf8f63bb662c292d&heads=5fed3813f7f5e1824344fdc9cf8f63bb662c292d&listkeys=phase%2Cbookmarks
265 "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bundlecaps=HG20%2Cbundle2%3DHG20%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps&cg=0&common=5fed3813f7f5e1824344fdc9cf8f63bb662c292d&heads=5fed3813f7f5e1824344fdc9cf8f63bb662c292d&listkeys=phase%2Cbookmarks
266 266 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases
267 267 "GET /?cmd=capabilities HTTP/1.1" 200 -
268 268 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D
269 "GET /?cmd=getbundle HTTP/1.1" 401 - x-hgarg-1:bundlecaps=HG20%2Cbundle2%3DHG20%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%250Ahgtagsfnodes%250Alistkeys%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps&cg=1&common=0000000000000000000000000000000000000000&heads=5fed3813f7f5e1824344fdc9cf8f63bb662c292d&listkeys=phase%2Cbookmarks
270 "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bundlecaps=HG20%2Cbundle2%3DHG20%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%250Ahgtagsfnodes%250Alistkeys%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps&cg=1&common=0000000000000000000000000000000000000000&heads=5fed3813f7f5e1824344fdc9cf8f63bb662c292d&listkeys=phase%2Cbookmarks
269 "GET /?cmd=getbundle HTTP/1.1" 401 - x-hgarg-1:bundlecaps=HG20%2Cbundle2%3DHG20%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps&cg=1&common=0000000000000000000000000000000000000000&heads=5fed3813f7f5e1824344fdc9cf8f63bb662c292d&listkeys=phase%2Cbookmarks
270 "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bundlecaps=HG20%2Cbundle2%3DHG20%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps&cg=1&common=0000000000000000000000000000000000000000&heads=5fed3813f7f5e1824344fdc9cf8f63bb662c292d&listkeys=phase%2Cbookmarks
271 271 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases
272 272 "GET /?cmd=capabilities HTTP/1.1" 200 -
273 273 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip
274 274 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces
275 275 "GET /?cmd=capabilities HTTP/1.1" 200 -
276 276 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip
277 277 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces
278 278 "GET /?cmd=listkeys HTTP/1.1" 403 - x-hgarg-1:namespace=namespaces
279 279 "GET /?cmd=capabilities HTTP/1.1" 200 -
280 280 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D7f4e523d01f2cc3765ac8934da3d14db775ff872
281 281 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=phases
282 282 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases
283 283 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks
284 284 "GET /?cmd=branchmap HTTP/1.1" 200 -
285 285 "GET /?cmd=branchmap HTTP/1.1" 200 -
286 286 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks
287 287 "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=666f726365
288 288 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases
289 289
290 290 $ cd ..
291 291
292 292 clone of serve with repo in root and unserved subrepo (issue2970)
293 293
294 294 $ hg --cwd test init sub
295 295 $ echo empty > test/sub/empty
296 296 $ hg --cwd test/sub add empty
297 297 $ hg --cwd test/sub commit -qm 'add empty'
298 298 $ hg --cwd test/sub tag -r 0 something
299 299 $ echo sub = sub > test/.hgsub
300 300 $ hg --cwd test add .hgsub
301 301 $ hg --cwd test commit -qm 'add subrepo'
302 302 $ hg clone http://localhost:$HGPORT noslash-clone
303 303 requesting all changes
304 304 adding changesets
305 305 adding manifests
306 306 adding file changes
307 307 added 3 changesets with 7 changes to 7 files
308 308 updating to branch default
309 309 abort: HTTP Error 404: Not Found
310 310 [255]
311 311 $ hg clone http://localhost:$HGPORT/ slash-clone
312 312 requesting all changes
313 313 adding changesets
314 314 adding manifests
315 315 adding file changes
316 316 added 3 changesets with 7 changes to 7 files
317 317 updating to branch default
318 318 abort: HTTP Error 404: Not Found
319 319 [255]
320 320
321 321 check error log
322 322
323 323 $ cat error.log
@@ -1,512 +1,512 b''
1 1 This test is a duplicate of 'test-http.t' feel free to factor out
2 2 parts that are not bundle1/bundle2 specific.
3 3
4 4 $ cat << EOF >> $HGRCPATH
5 5 > [experimental]
6 6 > # This test is dedicated to interaction through old bundle
7 7 > bundle2-exp = False
8 8 > EOF
9 9
10 10
11 11 This test tries to exercise the ssh functionality with a dummy script
12 12
13 13 creating 'remote' repo
14 14
15 15 $ hg init remote
16 16 $ cd remote
17 17 $ echo this > foo
18 18 $ echo this > fooO
19 19 $ hg ci -A -m "init" foo fooO
20 20
21 21 insert a closed branch (issue4428)
22 22
23 23 $ hg up null
24 24 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
25 25 $ hg branch closed
26 26 marked working directory as branch closed
27 27 (branches are permanent and global, did you want a bookmark?)
28 28 $ hg ci -mc0
29 29 $ hg ci --close-branch -mc1
30 30 $ hg up -q default
31 31
32 32 configure for serving
33 33
34 34 $ cat <<EOF > .hg/hgrc
35 35 > [server]
36 36 > uncompressed = True
37 37 >
38 38 > [hooks]
39 39 > changegroup = printenv.py changegroup-in-remote 0 ../dummylog
40 40 > EOF
41 41 $ cd ..
42 42
43 43 repo not found error
44 44
45 45 $ hg clone -e dummyssh ssh://user@dummy/nonexistent local
46 46 remote: abort: there is no Mercurial repository here (.hg not found)!
47 47 abort: no suitable response from remote hg!
48 48 [255]
49 49
50 50 non-existent absolute path
51 51
52 52 $ hg clone -e dummyssh ssh://user@dummy//`pwd`/nonexistent local
53 53 remote: abort: there is no Mercurial repository here (.hg not found)!
54 54 abort: no suitable response from remote hg!
55 55 [255]
56 56
57 57 clone remote via stream
58 58
59 59 $ hg clone -e dummyssh --uncompressed ssh://user@dummy/remote local-stream
60 60 streaming all changes
61 61 4 files to transfer, 615 bytes of data
62 62 transferred 615 bytes in * seconds (*) (glob)
63 63 searching for changes
64 64 no changes found
65 65 updating to branch default
66 66 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
67 67 $ cd local-stream
68 68 $ hg verify
69 69 checking changesets
70 70 checking manifests
71 71 crosschecking files in changesets and manifests
72 72 checking files
73 73 2 files, 3 changesets, 2 total revisions
74 74 $ hg branches
75 75 default 0:1160648e36ce
76 76 $ cd ..
77 77
78 78 clone bookmarks via stream
79 79
80 80 $ hg -R local-stream book mybook
81 81 $ hg clone -e dummyssh --uncompressed ssh://user@dummy/local-stream stream2
82 82 streaming all changes
83 83 4 files to transfer, 615 bytes of data
84 84 transferred 615 bytes in * seconds (*) (glob)
85 85 searching for changes
86 86 no changes found
87 87 updating to branch default
88 88 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
89 89 $ cd stream2
90 90 $ hg book
91 91 mybook 0:1160648e36ce
92 92 $ cd ..
93 93 $ rm -rf local-stream stream2
94 94
95 95 clone remote via pull
96 96
97 97 $ hg clone -e dummyssh ssh://user@dummy/remote local
98 98 requesting all changes
99 99 adding changesets
100 100 adding manifests
101 101 adding file changes
102 102 added 3 changesets with 2 changes to 2 files
103 103 updating to branch default
104 104 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
105 105
106 106 verify
107 107
108 108 $ cd local
109 109 $ hg verify
110 110 checking changesets
111 111 checking manifests
112 112 crosschecking files in changesets and manifests
113 113 checking files
114 114 2 files, 3 changesets, 2 total revisions
115 115 $ echo '[hooks]' >> .hg/hgrc
116 116 $ echo "changegroup = printenv.py changegroup-in-local 0 ../dummylog" >> .hg/hgrc
117 117
118 118 empty default pull
119 119
120 120 $ hg paths
121 121 default = ssh://user@dummy/remote
122 122 $ hg pull -e dummyssh
123 123 pulling from ssh://user@dummy/remote
124 124 searching for changes
125 125 no changes found
126 126
127 127 pull from wrong ssh URL
128 128
129 129 $ hg pull -e dummyssh ssh://user@dummy/doesnotexist
130 130 pulling from ssh://user@dummy/doesnotexist
131 131 remote: abort: there is no Mercurial repository here (.hg not found)!
132 132 abort: no suitable response from remote hg!
133 133 [255]
134 134
135 135 local change
136 136
137 137 $ echo bleah > foo
138 138 $ hg ci -m "add"
139 139
140 140 updating rc
141 141
142 142 $ echo "default-push = ssh://user@dummy/remote" >> .hg/hgrc
143 143 $ echo "[ui]" >> .hg/hgrc
144 144 $ echo "ssh = dummyssh" >> .hg/hgrc
145 145
146 146 find outgoing
147 147
148 148 $ hg out ssh://user@dummy/remote
149 149 comparing with ssh://user@dummy/remote
150 150 searching for changes
151 151 changeset: 3:a28a9d1a809c
152 152 tag: tip
153 153 parent: 0:1160648e36ce
154 154 user: test
155 155 date: Thu Jan 01 00:00:00 1970 +0000
156 156 summary: add
157 157
158 158
159 159 find incoming on the remote side
160 160
161 161 $ hg incoming -R ../remote -e dummyssh ssh://user@dummy/local
162 162 comparing with ssh://user@dummy/local
163 163 searching for changes
164 164 changeset: 3:a28a9d1a809c
165 165 tag: tip
166 166 parent: 0:1160648e36ce
167 167 user: test
168 168 date: Thu Jan 01 00:00:00 1970 +0000
169 169 summary: add
170 170
171 171
172 172 find incoming on the remote side (using absolute path)
173 173
174 174 $ hg incoming -R ../remote -e dummyssh "ssh://user@dummy/`pwd`"
175 175 comparing with ssh://user@dummy/$TESTTMP/local
176 176 searching for changes
177 177 changeset: 3:a28a9d1a809c
178 178 tag: tip
179 179 parent: 0:1160648e36ce
180 180 user: test
181 181 date: Thu Jan 01 00:00:00 1970 +0000
182 182 summary: add
183 183
184 184
185 185 push
186 186
187 187 $ hg push
188 188 pushing to ssh://user@dummy/remote
189 189 searching for changes
190 190 remote: adding changesets
191 191 remote: adding manifests
192 192 remote: adding file changes
193 193 remote: added 1 changesets with 1 changes to 1 files
194 194 $ cd ../remote
195 195
196 196 check remote tip
197 197
198 198 $ hg tip
199 199 changeset: 3:a28a9d1a809c
200 200 tag: tip
201 201 parent: 0:1160648e36ce
202 202 user: test
203 203 date: Thu Jan 01 00:00:00 1970 +0000
204 204 summary: add
205 205
206 206 $ hg verify
207 207 checking changesets
208 208 checking manifests
209 209 crosschecking files in changesets and manifests
210 210 checking files
211 211 2 files, 4 changesets, 3 total revisions
212 212 $ hg cat -r tip foo
213 213 bleah
214 214 $ echo z > z
215 215 $ hg ci -A -m z z
216 216 created new head
217 217
218 218 test pushkeys and bookmarks
219 219
220 220 $ cd ../local
221 221 $ hg debugpushkey --config ui.ssh=dummyssh ssh://user@dummy/remote namespaces
222 222 bookmarks
223 223 namespaces
224 224 phases
225 225 $ hg book foo -r 0
226 226 $ hg out -B
227 227 comparing with ssh://user@dummy/remote
228 228 searching for changed bookmarks
229 229 foo 1160648e36ce
230 230 $ hg push -B foo
231 231 pushing to ssh://user@dummy/remote
232 232 searching for changes
233 233 no changes found
234 234 exporting bookmark foo
235 235 [1]
236 236 $ hg debugpushkey --config ui.ssh=dummyssh ssh://user@dummy/remote bookmarks
237 237 foo 1160648e36cec0054048a7edc4110c6f84fde594
238 238 $ hg book -f foo
239 239 $ hg push --traceback
240 240 pushing to ssh://user@dummy/remote
241 241 searching for changes
242 242 no changes found
243 243 updating bookmark foo
244 244 [1]
245 245 $ hg book -d foo
246 246 $ hg in -B
247 247 comparing with ssh://user@dummy/remote
248 248 searching for changed bookmarks
249 249 foo a28a9d1a809c
250 250 $ hg book -f -r 0 foo
251 251 $ hg pull -B foo
252 252 pulling from ssh://user@dummy/remote
253 253 no changes found
254 254 updating bookmark foo
255 255 $ hg book -d foo
256 256 $ hg push -B foo
257 257 pushing to ssh://user@dummy/remote
258 258 searching for changes
259 259 no changes found
260 260 deleting remote bookmark foo
261 261 [1]
262 262
263 263 a bad, evil hook that prints to stdout
264 264
265 265 $ cat <<EOF > $TESTTMP/badhook
266 266 > import sys
267 267 > sys.stdout.write("KABOOM\n")
268 268 > EOF
269 269
270 270 $ echo '[hooks]' >> ../remote/.hg/hgrc
271 271 $ echo "changegroup.stdout = python $TESTTMP/badhook" >> ../remote/.hg/hgrc
272 272 $ echo r > r
273 273 $ hg ci -A -m z r
274 274
275 275 push should succeed even though it has an unexpected response
276 276
277 277 $ hg push
278 278 pushing to ssh://user@dummy/remote
279 279 searching for changes
280 280 remote has heads on branch 'default' that are not known locally: 6c0482d977a3
281 281 remote: adding changesets
282 282 remote: adding manifests
283 283 remote: adding file changes
284 284 remote: added 1 changesets with 1 changes to 1 files
285 285 remote: KABOOM
286 286 $ hg -R ../remote heads
287 287 changeset: 5:1383141674ec
288 288 tag: tip
289 289 parent: 3:a28a9d1a809c
290 290 user: test
291 291 date: Thu Jan 01 00:00:00 1970 +0000
292 292 summary: z
293 293
294 294 changeset: 4:6c0482d977a3
295 295 parent: 0:1160648e36ce
296 296 user: test
297 297 date: Thu Jan 01 00:00:00 1970 +0000
298 298 summary: z
299 299
300 300
301 301 clone bookmarks
302 302
303 303 $ hg -R ../remote bookmark test
304 304 $ hg -R ../remote bookmarks
305 305 * test 4:6c0482d977a3
306 306 $ hg clone -e dummyssh ssh://user@dummy/remote local-bookmarks
307 307 requesting all changes
308 308 adding changesets
309 309 adding manifests
310 310 adding file changes
311 311 added 6 changesets with 5 changes to 4 files (+1 heads)
312 312 updating to branch default
313 313 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
314 314 $ hg -R local-bookmarks bookmarks
315 315 test 4:6c0482d977a3
316 316
317 317 passwords in ssh urls are not supported
318 318 (we use a glob here because different Python versions give different
319 319 results here)
320 320
321 321 $ hg push ssh://user:erroneouspwd@dummy/remote
322 322 pushing to ssh://user:*@dummy/remote (glob)
323 323 abort: password in URL not supported!
324 324 [255]
325 325
326 326 $ cd ..
327 327
328 328 hide outer repo
329 329 $ hg init
330 330
331 331 Test remote paths with spaces (issue2983):
332 332
333 333 $ hg init --ssh dummyssh "ssh://user@dummy/a repo"
334 334 $ touch "$TESTTMP/a repo/test"
335 335 $ hg -R 'a repo' commit -A -m "test"
336 336 adding test
337 337 $ hg -R 'a repo' tag tag
338 338 $ hg id --ssh dummyssh "ssh://user@dummy/a repo"
339 339 73649e48688a
340 340
341 341 $ hg id --ssh dummyssh "ssh://user@dummy/a repo#noNoNO"
342 342 abort: unknown revision 'noNoNO'!
343 343 [255]
344 344
345 345 Test (non-)escaping of remote paths with spaces when cloning (issue3145):
346 346
347 347 $ hg clone --ssh dummyssh "ssh://user@dummy/a repo"
348 348 destination directory: a repo
349 349 abort: destination 'a repo' is not empty
350 350 [255]
351 351
352 352 Test hg-ssh using a helper script that will restore PYTHONPATH (which might
353 353 have been cleared by a hg.exe wrapper) and invoke hg-ssh with the right
354 354 parameters:
355 355
356 356 $ cat > ssh.sh << EOF
357 357 > userhost="\$1"
358 358 > SSH_ORIGINAL_COMMAND="\$2"
359 359 > export SSH_ORIGINAL_COMMAND
360 360 > PYTHONPATH="$PYTHONPATH"
361 361 > export PYTHONPATH
362 362 > python "$TESTDIR/../contrib/hg-ssh" "$TESTTMP/a repo"
363 363 > EOF
364 364
365 365 $ hg id --ssh "sh ssh.sh" "ssh://user@dummy/a repo"
366 366 73649e48688a
367 367
368 368 $ hg id --ssh "sh ssh.sh" "ssh://user@dummy/a'repo"
369 369 remote: Illegal repository "$TESTTMP/a'repo" (glob)
370 370 abort: no suitable response from remote hg!
371 371 [255]
372 372
373 373 $ hg id --ssh "sh ssh.sh" --remotecmd hacking "ssh://user@dummy/a'repo"
374 374 remote: Illegal command "hacking -R 'a'\''repo' serve --stdio"
375 375 abort: no suitable response from remote hg!
376 376 [255]
377 377
378 378 $ SSH_ORIGINAL_COMMAND="'hg' -R 'a'repo' serve --stdio" python "$TESTDIR/../contrib/hg-ssh"
379 379 Illegal command "'hg' -R 'a'repo' serve --stdio": No closing quotation
380 380 [255]
381 381
382 382 Test hg-ssh in read-only mode:
383 383
384 384 $ cat > ssh.sh << EOF
385 385 > userhost="\$1"
386 386 > SSH_ORIGINAL_COMMAND="\$2"
387 387 > export SSH_ORIGINAL_COMMAND
388 388 > PYTHONPATH="$PYTHONPATH"
389 389 > export PYTHONPATH
390 390 > python "$TESTDIR/../contrib/hg-ssh" --read-only "$TESTTMP/remote"
391 391 > EOF
392 392
393 393 $ hg clone --ssh "sh ssh.sh" "ssh://user@dummy/$TESTTMP/remote" read-only-local
394 394 requesting all changes
395 395 adding changesets
396 396 adding manifests
397 397 adding file changes
398 398 added 6 changesets with 5 changes to 4 files (+1 heads)
399 399 updating to branch default
400 400 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
401 401
402 402 $ cd read-only-local
403 403 $ echo "baz" > bar
404 404 $ hg ci -A -m "unpushable commit" bar
405 405 $ hg push --ssh "sh ../ssh.sh"
406 406 pushing to ssh://user@dummy/*/remote (glob)
407 407 searching for changes
408 408 remote: Permission denied
409 409 remote: abort: pretxnopen.hg-ssh hook failed
410 410 remote: Permission denied
411 411 remote: pushkey-abort: prepushkey.hg-ssh hook failed
412 412 updating 6c0482d977a3 to public failed!
413 413 [1]
414 414
415 415 $ cd ..
416 416
417 417 stderr from remote commands should be printed before stdout from local code (issue4336)
418 418
419 419 $ hg clone remote stderr-ordering
420 420 updating to branch default
421 421 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
422 422 $ cd stderr-ordering
423 423 $ cat >> localwrite.py << EOF
424 424 > from mercurial import exchange, extensions
425 425 >
426 426 > def wrappedpush(orig, repo, *args, **kwargs):
427 427 > res = orig(repo, *args, **kwargs)
428 428 > repo.ui.write('local stdout\n')
429 429 > return res
430 430 >
431 431 > def extsetup(ui):
432 432 > extensions.wrapfunction(exchange, 'push', wrappedpush)
433 433 > EOF
434 434
435 435 $ cat >> .hg/hgrc << EOF
436 436 > [paths]
437 437 > default-push = ssh://user@dummy/remote
438 438 > [ui]
439 439 > ssh = dummyssh
440 440 > [extensions]
441 441 > localwrite = localwrite.py
442 442 > EOF
443 443
444 444 $ echo localwrite > foo
445 445 $ hg commit -m 'testing localwrite'
446 446 $ hg push
447 447 pushing to ssh://user@dummy/remote
448 448 searching for changes
449 449 remote: adding changesets
450 450 remote: adding manifests
451 451 remote: adding file changes
452 452 remote: added 1 changesets with 1 changes to 1 files
453 453 remote: KABOOM
454 454 local stdout
455 455
456 456 debug output
457 457
458 458 $ hg pull --debug ssh://user@dummy/remote
459 459 pulling from ssh://user@dummy/remote
460 460 running dummyssh user@dummy ('|")hg -R remote serve --stdio('|") (re)
461 461 sending hello command
462 462 sending between command
463 remote: 335
464 remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%0Ahgtagsfnodes%0Alistkeys%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024
463 remote: 345
464 remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0Ahgtagsfnodes%0Alistkeys%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024
465 465 remote: 1
466 466 preparing listkeys for "bookmarks"
467 467 sending listkeys command
468 468 received listkey for "bookmarks": 45 bytes
469 469 query 1; heads
470 470 sending batch command
471 471 searching for changes
472 472 all remote heads known locally
473 473 no changes found
474 474 preparing listkeys for "phases"
475 475 sending listkeys command
476 476 received listkey for "phases": 15 bytes
477 477 checking for updated bookmarks
478 478
479 479 $ cd ..
480 480
481 481 $ cat dummylog
482 482 Got arguments 1:user@dummy 2:hg -R nonexistent serve --stdio
483 483 Got arguments 1:user@dummy 2:hg -R /$TESTTMP/nonexistent serve --stdio
484 484 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
485 485 Got arguments 1:user@dummy 2:hg -R local-stream serve --stdio
486 486 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
487 487 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
488 488 Got arguments 1:user@dummy 2:hg -R doesnotexist serve --stdio
489 489 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
490 490 Got arguments 1:user@dummy 2:hg -R local serve --stdio
491 491 Got arguments 1:user@dummy 2:hg -R $TESTTMP/local serve --stdio
492 492 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
493 493 changegroup-in-remote hook: HG_NODE=a28a9d1a809cab7d4e2fde4bee738a9ede948b60 HG_SOURCE=serve HG_TXNID=TXN:* HG_URL=remote:ssh:127.0.0.1 (glob)
494 494 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
495 495 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
496 496 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
497 497 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
498 498 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
499 499 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
500 500 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
501 501 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
502 502 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
503 503 changegroup-in-remote hook: HG_NODE=1383141674ec756a6056f6a9097618482fe0f4a6 HG_SOURCE=serve HG_TXNID=TXN:* HG_URL=remote:ssh:127.0.0.1 (glob)
504 504 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
505 505 Got arguments 1:user@dummy 2:hg init 'a repo'
506 506 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
507 507 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
508 508 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
509 509 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
510 510 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
511 511 changegroup-in-remote hook: HG_NODE=65c38f4125f9602c8db4af56530cc221d93b8ef8 HG_SOURCE=serve HG_TXNID=TXN:* HG_URL=remote:ssh:127.0.0.1 (glob)
512 512 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
@@ -1,503 +1,503 b''
1 1
2 2 This test tries to exercise the ssh functionality with a dummy script
3 3
4 4 creating 'remote' repo
5 5
6 6 $ hg init remote
7 7 $ cd remote
8 8 $ echo this > foo
9 9 $ echo this > fooO
10 10 $ hg ci -A -m "init" foo fooO
11 11
12 12 insert a closed branch (issue4428)
13 13
14 14 $ hg up null
15 15 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
16 16 $ hg branch closed
17 17 marked working directory as branch closed
18 18 (branches are permanent and global, did you want a bookmark?)
19 19 $ hg ci -mc0
20 20 $ hg ci --close-branch -mc1
21 21 $ hg up -q default
22 22
23 23 configure for serving
24 24
25 25 $ cat <<EOF > .hg/hgrc
26 26 > [server]
27 27 > uncompressed = True
28 28 >
29 29 > [hooks]
30 30 > changegroup = printenv.py changegroup-in-remote 0 ../dummylog
31 31 > EOF
32 32 $ cd ..
33 33
34 34 repo not found error
35 35
36 36 $ hg clone -e dummyssh ssh://user@dummy/nonexistent local
37 37 remote: abort: there is no Mercurial repository here (.hg not found)!
38 38 abort: no suitable response from remote hg!
39 39 [255]
40 40
41 41 non-existent absolute path
42 42
43 43 $ hg clone -e dummyssh ssh://user@dummy//`pwd`/nonexistent local
44 44 remote: abort: there is no Mercurial repository here (.hg not found)!
45 45 abort: no suitable response from remote hg!
46 46 [255]
47 47
48 48 clone remote via stream
49 49
50 50 $ hg clone -e dummyssh --uncompressed ssh://user@dummy/remote local-stream
51 51 streaming all changes
52 52 4 files to transfer, 615 bytes of data
53 53 transferred 615 bytes in * seconds (*) (glob)
54 54 searching for changes
55 55 no changes found
56 56 updating to branch default
57 57 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
58 58 $ cd local-stream
59 59 $ hg verify
60 60 checking changesets
61 61 checking manifests
62 62 crosschecking files in changesets and manifests
63 63 checking files
64 64 2 files, 3 changesets, 2 total revisions
65 65 $ hg branches
66 66 default 0:1160648e36ce
67 67 $ cd ..
68 68
69 69 clone bookmarks via stream
70 70
71 71 $ hg -R local-stream book mybook
72 72 $ hg clone -e dummyssh --uncompressed ssh://user@dummy/local-stream stream2
73 73 streaming all changes
74 74 4 files to transfer, 615 bytes of data
75 75 transferred 615 bytes in * seconds (*) (glob)
76 76 searching for changes
77 77 no changes found
78 78 updating to branch default
79 79 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
80 80 $ cd stream2
81 81 $ hg book
82 82 mybook 0:1160648e36ce
83 83 $ cd ..
84 84 $ rm -rf local-stream stream2
85 85
86 86 clone remote via pull
87 87
88 88 $ hg clone -e dummyssh ssh://user@dummy/remote local
89 89 requesting all changes
90 90 adding changesets
91 91 adding manifests
92 92 adding file changes
93 93 added 3 changesets with 2 changes to 2 files
94 94 updating to branch default
95 95 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
96 96
97 97 verify
98 98
99 99 $ cd local
100 100 $ hg verify
101 101 checking changesets
102 102 checking manifests
103 103 crosschecking files in changesets and manifests
104 104 checking files
105 105 2 files, 3 changesets, 2 total revisions
106 106 $ echo '[hooks]' >> .hg/hgrc
107 107 $ echo "changegroup = printenv.py changegroup-in-local 0 ../dummylog" >> .hg/hgrc
108 108
109 109 empty default pull
110 110
111 111 $ hg paths
112 112 default = ssh://user@dummy/remote
113 113 $ hg pull -e dummyssh
114 114 pulling from ssh://user@dummy/remote
115 115 searching for changes
116 116 no changes found
117 117
118 118 pull from wrong ssh URL
119 119
120 120 $ hg pull -e dummyssh ssh://user@dummy/doesnotexist
121 121 pulling from ssh://user@dummy/doesnotexist
122 122 remote: abort: there is no Mercurial repository here (.hg not found)!
123 123 abort: no suitable response from remote hg!
124 124 [255]
125 125
126 126 local change
127 127
128 128 $ echo bleah > foo
129 129 $ hg ci -m "add"
130 130
131 131 updating rc
132 132
133 133 $ echo "default-push = ssh://user@dummy/remote" >> .hg/hgrc
134 134 $ echo "[ui]" >> .hg/hgrc
135 135 $ echo "ssh = dummyssh" >> .hg/hgrc
136 136
137 137 find outgoing
138 138
139 139 $ hg out ssh://user@dummy/remote
140 140 comparing with ssh://user@dummy/remote
141 141 searching for changes
142 142 changeset: 3:a28a9d1a809c
143 143 tag: tip
144 144 parent: 0:1160648e36ce
145 145 user: test
146 146 date: Thu Jan 01 00:00:00 1970 +0000
147 147 summary: add
148 148
149 149
150 150 find incoming on the remote side
151 151
152 152 $ hg incoming -R ../remote -e dummyssh ssh://user@dummy/local
153 153 comparing with ssh://user@dummy/local
154 154 searching for changes
155 155 changeset: 3:a28a9d1a809c
156 156 tag: tip
157 157 parent: 0:1160648e36ce
158 158 user: test
159 159 date: Thu Jan 01 00:00:00 1970 +0000
160 160 summary: add
161 161
162 162
163 163 find incoming on the remote side (using absolute path)
164 164
165 165 $ hg incoming -R ../remote -e dummyssh "ssh://user@dummy/`pwd`"
166 166 comparing with ssh://user@dummy/$TESTTMP/local
167 167 searching for changes
168 168 changeset: 3:a28a9d1a809c
169 169 tag: tip
170 170 parent: 0:1160648e36ce
171 171 user: test
172 172 date: Thu Jan 01 00:00:00 1970 +0000
173 173 summary: add
174 174
175 175
176 176 push
177 177
178 178 $ hg push
179 179 pushing to ssh://user@dummy/remote
180 180 searching for changes
181 181 remote: adding changesets
182 182 remote: adding manifests
183 183 remote: adding file changes
184 184 remote: added 1 changesets with 1 changes to 1 files
185 185 $ cd ../remote
186 186
187 187 check remote tip
188 188
189 189 $ hg tip
190 190 changeset: 3:a28a9d1a809c
191 191 tag: tip
192 192 parent: 0:1160648e36ce
193 193 user: test
194 194 date: Thu Jan 01 00:00:00 1970 +0000
195 195 summary: add
196 196
197 197 $ hg verify
198 198 checking changesets
199 199 checking manifests
200 200 crosschecking files in changesets and manifests
201 201 checking files
202 202 2 files, 4 changesets, 3 total revisions
203 203 $ hg cat -r tip foo
204 204 bleah
205 205 $ echo z > z
206 206 $ hg ci -A -m z z
207 207 created new head
208 208
209 209 test pushkeys and bookmarks
210 210
211 211 $ cd ../local
212 212 $ hg debugpushkey --config ui.ssh=dummyssh ssh://user@dummy/remote namespaces
213 213 bookmarks
214 214 namespaces
215 215 phases
216 216 $ hg book foo -r 0
217 217 $ hg out -B
218 218 comparing with ssh://user@dummy/remote
219 219 searching for changed bookmarks
220 220 foo 1160648e36ce
221 221 $ hg push -B foo
222 222 pushing to ssh://user@dummy/remote
223 223 searching for changes
224 224 no changes found
225 225 exporting bookmark foo
226 226 [1]
227 227 $ hg debugpushkey --config ui.ssh=dummyssh ssh://user@dummy/remote bookmarks
228 228 foo 1160648e36cec0054048a7edc4110c6f84fde594
229 229 $ hg book -f foo
230 230 $ hg push --traceback
231 231 pushing to ssh://user@dummy/remote
232 232 searching for changes
233 233 no changes found
234 234 updating bookmark foo
235 235 [1]
236 236 $ hg book -d foo
237 237 $ hg in -B
238 238 comparing with ssh://user@dummy/remote
239 239 searching for changed bookmarks
240 240 foo a28a9d1a809c
241 241 $ hg book -f -r 0 foo
242 242 $ hg pull -B foo
243 243 pulling from ssh://user@dummy/remote
244 244 no changes found
245 245 updating bookmark foo
246 246 $ hg book -d foo
247 247 $ hg push -B foo
248 248 pushing to ssh://user@dummy/remote
249 249 searching for changes
250 250 no changes found
251 251 deleting remote bookmark foo
252 252 [1]
253 253
254 254 a bad, evil hook that prints to stdout
255 255
256 256 $ cat <<EOF > $TESTTMP/badhook
257 257 > import sys
258 258 > sys.stdout.write("KABOOM\n")
259 259 > EOF
260 260
261 261 $ echo '[hooks]' >> ../remote/.hg/hgrc
262 262 $ echo "changegroup.stdout = python $TESTTMP/badhook" >> ../remote/.hg/hgrc
263 263 $ echo r > r
264 264 $ hg ci -A -m z r
265 265
266 266 push should succeed even though it has an unexpected response
267 267
268 268 $ hg push
269 269 pushing to ssh://user@dummy/remote
270 270 searching for changes
271 271 remote has heads on branch 'default' that are not known locally: 6c0482d977a3
272 272 remote: adding changesets
273 273 remote: adding manifests
274 274 remote: adding file changes
275 275 remote: added 1 changesets with 1 changes to 1 files
276 276 remote: KABOOM
277 277 $ hg -R ../remote heads
278 278 changeset: 5:1383141674ec
279 279 tag: tip
280 280 parent: 3:a28a9d1a809c
281 281 user: test
282 282 date: Thu Jan 01 00:00:00 1970 +0000
283 283 summary: z
284 284
285 285 changeset: 4:6c0482d977a3
286 286 parent: 0:1160648e36ce
287 287 user: test
288 288 date: Thu Jan 01 00:00:00 1970 +0000
289 289 summary: z
290 290
291 291
292 292 clone bookmarks
293 293
294 294 $ hg -R ../remote bookmark test
295 295 $ hg -R ../remote bookmarks
296 296 * test 4:6c0482d977a3
297 297 $ hg clone -e dummyssh ssh://user@dummy/remote local-bookmarks
298 298 requesting all changes
299 299 adding changesets
300 300 adding manifests
301 301 adding file changes
302 302 added 6 changesets with 5 changes to 4 files (+1 heads)
303 303 updating to branch default
304 304 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
305 305 $ hg -R local-bookmarks bookmarks
306 306 test 4:6c0482d977a3
307 307
308 308 passwords in ssh urls are not supported
309 309 (we use a glob here because different Python versions give different
310 310 results here)
311 311
312 312 $ hg push ssh://user:erroneouspwd@dummy/remote
313 313 pushing to ssh://user:*@dummy/remote (glob)
314 314 abort: password in URL not supported!
315 315 [255]
316 316
317 317 $ cd ..
318 318
319 319 hide outer repo
320 320 $ hg init
321 321
322 322 Test remote paths with spaces (issue2983):
323 323
324 324 $ hg init --ssh dummyssh "ssh://user@dummy/a repo"
325 325 $ touch "$TESTTMP/a repo/test"
326 326 $ hg -R 'a repo' commit -A -m "test"
327 327 adding test
328 328 $ hg -R 'a repo' tag tag
329 329 $ hg id --ssh dummyssh "ssh://user@dummy/a repo"
330 330 73649e48688a
331 331
332 332 $ hg id --ssh dummyssh "ssh://user@dummy/a repo#noNoNO"
333 333 abort: unknown revision 'noNoNO'!
334 334 [255]
335 335
336 336 Test (non-)escaping of remote paths with spaces when cloning (issue3145):
337 337
338 338 $ hg clone --ssh dummyssh "ssh://user@dummy/a repo"
339 339 destination directory: a repo
340 340 abort: destination 'a repo' is not empty
341 341 [255]
342 342
343 343 Test hg-ssh using a helper script that will restore PYTHONPATH (which might
344 344 have been cleared by a hg.exe wrapper) and invoke hg-ssh with the right
345 345 parameters:
346 346
347 347 $ cat > ssh.sh << EOF
348 348 > userhost="\$1"
349 349 > SSH_ORIGINAL_COMMAND="\$2"
350 350 > export SSH_ORIGINAL_COMMAND
351 351 > PYTHONPATH="$PYTHONPATH"
352 352 > export PYTHONPATH
353 353 > python "$TESTDIR/../contrib/hg-ssh" "$TESTTMP/a repo"
354 354 > EOF
355 355
356 356 $ hg id --ssh "sh ssh.sh" "ssh://user@dummy/a repo"
357 357 73649e48688a
358 358
359 359 $ hg id --ssh "sh ssh.sh" "ssh://user@dummy/a'repo"
360 360 remote: Illegal repository "$TESTTMP/a'repo" (glob)
361 361 abort: no suitable response from remote hg!
362 362 [255]
363 363
364 364 $ hg id --ssh "sh ssh.sh" --remotecmd hacking "ssh://user@dummy/a'repo"
365 365 remote: Illegal command "hacking -R 'a'\''repo' serve --stdio"
366 366 abort: no suitable response from remote hg!
367 367 [255]
368 368
369 369 $ SSH_ORIGINAL_COMMAND="'hg' -R 'a'repo' serve --stdio" python "$TESTDIR/../contrib/hg-ssh"
370 370 Illegal command "'hg' -R 'a'repo' serve --stdio": No closing quotation
371 371 [255]
372 372
373 373 Test hg-ssh in read-only mode:
374 374
375 375 $ cat > ssh.sh << EOF
376 376 > userhost="\$1"
377 377 > SSH_ORIGINAL_COMMAND="\$2"
378 378 > export SSH_ORIGINAL_COMMAND
379 379 > PYTHONPATH="$PYTHONPATH"
380 380 > export PYTHONPATH
381 381 > python "$TESTDIR/../contrib/hg-ssh" --read-only "$TESTTMP/remote"
382 382 > EOF
383 383
384 384 $ hg clone --ssh "sh ssh.sh" "ssh://user@dummy/$TESTTMP/remote" read-only-local
385 385 requesting all changes
386 386 adding changesets
387 387 adding manifests
388 388 adding file changes
389 389 added 6 changesets with 5 changes to 4 files (+1 heads)
390 390 updating to branch default
391 391 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
392 392
393 393 $ cd read-only-local
394 394 $ echo "baz" > bar
395 395 $ hg ci -A -m "unpushable commit" bar
396 396 $ hg push --ssh "sh ../ssh.sh"
397 397 pushing to ssh://user@dummy/*/remote (glob)
398 398 searching for changes
399 399 remote: Permission denied
400 400 abort: pretxnopen.hg-ssh hook failed
401 401 [255]
402 402
403 403 $ cd ..
404 404
405 405 stderr from remote commands should be printed before stdout from local code (issue4336)
406 406
407 407 $ hg clone remote stderr-ordering
408 408 updating to branch default
409 409 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
410 410 $ cd stderr-ordering
411 411 $ cat >> localwrite.py << EOF
412 412 > from mercurial import exchange, extensions
413 413 >
414 414 > def wrappedpush(orig, repo, *args, **kwargs):
415 415 > res = orig(repo, *args, **kwargs)
416 416 > repo.ui.write('local stdout\n')
417 417 > return res
418 418 >
419 419 > def extsetup(ui):
420 420 > extensions.wrapfunction(exchange, 'push', wrappedpush)
421 421 > EOF
422 422
423 423 $ cat >> .hg/hgrc << EOF
424 424 > [paths]
425 425 > default-push = ssh://user@dummy/remote
426 426 > [ui]
427 427 > ssh = dummyssh
428 428 > [extensions]
429 429 > localwrite = localwrite.py
430 430 > EOF
431 431
432 432 $ echo localwrite > foo
433 433 $ hg commit -m 'testing localwrite'
434 434 $ hg push
435 435 pushing to ssh://user@dummy/remote
436 436 searching for changes
437 437 remote: adding changesets
438 438 remote: adding manifests
439 439 remote: adding file changes
440 440 remote: added 1 changesets with 1 changes to 1 files
441 441 remote: KABOOM
442 442 local stdout
443 443
444 444 debug output
445 445
446 446 $ hg pull --debug ssh://user@dummy/remote
447 447 pulling from ssh://user@dummy/remote
448 448 running dummyssh user@dummy ('|")hg -R remote serve --stdio('|") (re)
449 449 sending hello command
450 450 sending between command
451 remote: 335
452 remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%0Ahgtagsfnodes%0Alistkeys%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024
451 remote: 345
452 remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2=HG20%0Achangegroup%3D01%2C02%0Adigests%3Dmd5%2Csha1%2Csha512%0Aerror%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0Ahgtagsfnodes%0Alistkeys%0Apushkey%0Aremote-changegroup%3Dhttp%2Chttps unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024
453 453 remote: 1
454 454 query 1; heads
455 455 sending batch command
456 456 searching for changes
457 457 all remote heads known locally
458 458 no changes found
459 459 sending getbundle command
460 460 bundle2-input-bundle: with-transaction
461 461 bundle2-input-part: "listkeys" (params: 1 mandatory) supported
462 462 bundle2-input-part: "listkeys" (params: 1 mandatory) supported
463 463 bundle2-input-part: total payload size 45
464 464 bundle2-input-bundle: 1 parts total
465 465 checking for updated bookmarks
466 466 preparing listkeys for "phases"
467 467 sending listkeys command
468 468 received listkey for "phases": 15 bytes
469 469
470 470 $ cd ..
471 471
472 472 $ cat dummylog
473 473 Got arguments 1:user@dummy 2:hg -R nonexistent serve --stdio
474 474 Got arguments 1:user@dummy 2:hg -R /$TESTTMP/nonexistent serve --stdio
475 475 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
476 476 Got arguments 1:user@dummy 2:hg -R local-stream serve --stdio
477 477 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
478 478 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
479 479 Got arguments 1:user@dummy 2:hg -R doesnotexist serve --stdio
480 480 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
481 481 Got arguments 1:user@dummy 2:hg -R local serve --stdio
482 482 Got arguments 1:user@dummy 2:hg -R $TESTTMP/local serve --stdio
483 483 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
484 484 changegroup-in-remote hook: HG_BUNDLE2=1 HG_NODE=a28a9d1a809cab7d4e2fde4bee738a9ede948b60 HG_SOURCE=serve HG_TXNID=TXN:* HG_URL=remote:ssh:127.0.0.1 (glob)
485 485 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
486 486 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
487 487 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
488 488 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
489 489 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
490 490 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
491 491 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
492 492 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
493 493 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
494 494 changegroup-in-remote hook: HG_BUNDLE2=1 HG_NODE=1383141674ec756a6056f6a9097618482fe0f4a6 HG_SOURCE=serve HG_TXNID=TXN:* HG_URL=remote:ssh:127.0.0.1 (glob)
495 495 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
496 496 Got arguments 1:user@dummy 2:hg init 'a repo'
497 497 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
498 498 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
499 499 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
500 500 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
501 501 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
502 502 changegroup-in-remote hook: HG_BUNDLE2=1 HG_NODE=65c38f4125f9602c8db4af56530cc221d93b8ef8 HG_SOURCE=serve HG_TXNID=TXN:* HG_URL=remote:ssh:127.0.0.1 (glob)
503 503 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
General Comments 0
You need to be logged in to leave comments. Login now