##// END OF EJS Templates
push: include a 'check:bookmarks' part when possible...
Boris Feld -
r35260:ad5f2b92 default
parent child Browse files
Show More
@@ -1,2059 +1,2060 b''
1 # bundle2.py - generic container format to transmit arbitrary data.
1 # bundle2.py - generic container format to transmit arbitrary data.
2 #
2 #
3 # Copyright 2013 Facebook, Inc.
3 # Copyright 2013 Facebook, Inc.
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7 """Handling of the new bundle2 format
7 """Handling of the new bundle2 format
8
8
9 The goal of bundle2 is to act as an atomically packet to transmit a set of
9 The goal of bundle2 is to act as an atomically packet to transmit a set of
10 payloads in an application agnostic way. It consist in a sequence of "parts"
10 payloads in an application agnostic way. It consist in a sequence of "parts"
11 that will be handed to and processed by the application layer.
11 that will be handed to and processed by the application layer.
12
12
13
13
14 General format architecture
14 General format architecture
15 ===========================
15 ===========================
16
16
17 The format is architectured as follow
17 The format is architectured as follow
18
18
19 - magic string
19 - magic string
20 - stream level parameters
20 - stream level parameters
21 - payload parts (any number)
21 - payload parts (any number)
22 - end of stream marker.
22 - end of stream marker.
23
23
24 the Binary format
24 the Binary format
25 ============================
25 ============================
26
26
27 All numbers are unsigned and big-endian.
27 All numbers are unsigned and big-endian.
28
28
29 stream level parameters
29 stream level parameters
30 ------------------------
30 ------------------------
31
31
32 Binary format is as follow
32 Binary format is as follow
33
33
34 :params size: int32
34 :params size: int32
35
35
36 The total number of Bytes used by the parameters
36 The total number of Bytes used by the parameters
37
37
38 :params value: arbitrary number of Bytes
38 :params value: arbitrary number of Bytes
39
39
40 A blob of `params size` containing the serialized version of all stream level
40 A blob of `params size` containing the serialized version of all stream level
41 parameters.
41 parameters.
42
42
43 The blob contains a space separated list of parameters. Parameters with value
43 The blob contains a space separated list of parameters. Parameters with value
44 are stored in the form `<name>=<value>`. Both name and value are urlquoted.
44 are stored in the form `<name>=<value>`. Both name and value are urlquoted.
45
45
46 Empty name are obviously forbidden.
46 Empty name are obviously forbidden.
47
47
48 Name MUST start with a letter. If this first letter is lower case, the
48 Name MUST start with a letter. If this first letter is lower case, the
49 parameter is advisory and can be safely ignored. However when the first
49 parameter is advisory and can be safely ignored. However when the first
50 letter is capital, the parameter is mandatory and the bundling process MUST
50 letter is capital, the parameter is mandatory and the bundling process MUST
51 stop if he is not able to proceed it.
51 stop if he is not able to proceed it.
52
52
53 Stream parameters use a simple textual format for two main reasons:
53 Stream parameters use a simple textual format for two main reasons:
54
54
55 - Stream level parameters should remain simple and we want to discourage any
55 - Stream level parameters should remain simple and we want to discourage any
56 crazy usage.
56 crazy usage.
57 - Textual data allow easy human inspection of a bundle2 header in case of
57 - Textual data allow easy human inspection of a bundle2 header in case of
58 troubles.
58 troubles.
59
59
60 Any Applicative level options MUST go into a bundle2 part instead.
60 Any Applicative level options MUST go into a bundle2 part instead.
61
61
62 Payload part
62 Payload part
63 ------------------------
63 ------------------------
64
64
65 Binary format is as follow
65 Binary format is as follow
66
66
67 :header size: int32
67 :header size: int32
68
68
69 The total number of Bytes used by the part header. When the header is empty
69 The total number of Bytes used by the part header. When the header is empty
70 (size = 0) this is interpreted as the end of stream marker.
70 (size = 0) this is interpreted as the end of stream marker.
71
71
72 :header:
72 :header:
73
73
74 The header defines how to interpret the part. It contains two piece of
74 The header defines how to interpret the part. It contains two piece of
75 data: the part type, and the part parameters.
75 data: the part type, and the part parameters.
76
76
77 The part type is used to route an application level handler, that can
77 The part type is used to route an application level handler, that can
78 interpret payload.
78 interpret payload.
79
79
80 Part parameters are passed to the application level handler. They are
80 Part parameters are passed to the application level handler. They are
81 meant to convey information that will help the application level object to
81 meant to convey information that will help the application level object to
82 interpret the part payload.
82 interpret the part payload.
83
83
84 The binary format of the header is has follow
84 The binary format of the header is has follow
85
85
86 :typesize: (one byte)
86 :typesize: (one byte)
87
87
88 :parttype: alphanumerical part name (restricted to [a-zA-Z0-9_:-]*)
88 :parttype: alphanumerical part name (restricted to [a-zA-Z0-9_:-]*)
89
89
90 :partid: A 32bits integer (unique in the bundle) that can be used to refer
90 :partid: A 32bits integer (unique in the bundle) that can be used to refer
91 to this part.
91 to this part.
92
92
93 :parameters:
93 :parameters:
94
94
95 Part's parameter may have arbitrary content, the binary structure is::
95 Part's parameter may have arbitrary content, the binary structure is::
96
96
97 <mandatory-count><advisory-count><param-sizes><param-data>
97 <mandatory-count><advisory-count><param-sizes><param-data>
98
98
99 :mandatory-count: 1 byte, number of mandatory parameters
99 :mandatory-count: 1 byte, number of mandatory parameters
100
100
101 :advisory-count: 1 byte, number of advisory parameters
101 :advisory-count: 1 byte, number of advisory parameters
102
102
103 :param-sizes:
103 :param-sizes:
104
104
105 N couple of bytes, where N is the total number of parameters. Each
105 N couple of bytes, where N is the total number of parameters. Each
106 couple contains (<size-of-key>, <size-of-value) for one parameter.
106 couple contains (<size-of-key>, <size-of-value) for one parameter.
107
107
108 :param-data:
108 :param-data:
109
109
110 A blob of bytes from which each parameter key and value can be
110 A blob of bytes from which each parameter key and value can be
111 retrieved using the list of size couples stored in the previous
111 retrieved using the list of size couples stored in the previous
112 field.
112 field.
113
113
114 Mandatory parameters comes first, then the advisory ones.
114 Mandatory parameters comes first, then the advisory ones.
115
115
116 Each parameter's key MUST be unique within the part.
116 Each parameter's key MUST be unique within the part.
117
117
118 :payload:
118 :payload:
119
119
120 payload is a series of `<chunksize><chunkdata>`.
120 payload is a series of `<chunksize><chunkdata>`.
121
121
122 `chunksize` is an int32, `chunkdata` are plain bytes (as much as
122 `chunksize` is an int32, `chunkdata` are plain bytes (as much as
123 `chunksize` says)` The payload part is concluded by a zero size chunk.
123 `chunksize` says)` The payload part is concluded by a zero size chunk.
124
124
125 The current implementation always produces either zero or one chunk.
125 The current implementation always produces either zero or one chunk.
126 This is an implementation limitation that will ultimately be lifted.
126 This is an implementation limitation that will ultimately be lifted.
127
127
128 `chunksize` can be negative to trigger special case processing. No such
128 `chunksize` can be negative to trigger special case processing. No such
129 processing is in place yet.
129 processing is in place yet.
130
130
131 Bundle processing
131 Bundle processing
132 ============================
132 ============================
133
133
134 Each part is processed in order using a "part handler". Handler are registered
134 Each part is processed in order using a "part handler". Handler are registered
135 for a certain part type.
135 for a certain part type.
136
136
137 The matching of a part to its handler is case insensitive. The case of the
137 The matching of a part to its handler is case insensitive. The case of the
138 part type is used to know if a part is mandatory or advisory. If the Part type
138 part type is used to know if a part is mandatory or advisory. If the Part type
139 contains any uppercase char it is considered mandatory. When no handler is
139 contains any uppercase char it is considered mandatory. When no handler is
140 known for a Mandatory part, the process is aborted and an exception is raised.
140 known for a Mandatory part, the process is aborted and an exception is raised.
141 If the part is advisory and no handler is known, the part is ignored. When the
141 If the part is advisory and no handler is known, the part is ignored. When the
142 process is aborted, the full bundle is still read from the stream to keep the
142 process is aborted, the full bundle is still read from the stream to keep the
143 channel usable. But none of the part read from an abort are processed. In the
143 channel usable. But none of the part read from an abort are processed. In the
144 future, dropping the stream may become an option for channel we do not care to
144 future, dropping the stream may become an option for channel we do not care to
145 preserve.
145 preserve.
146 """
146 """
147
147
148 from __future__ import absolute_import, division
148 from __future__ import absolute_import, division
149
149
150 import errno
150 import errno
151 import os
151 import os
152 import re
152 import re
153 import string
153 import string
154 import struct
154 import struct
155 import sys
155 import sys
156
156
157 from .i18n import _
157 from .i18n import _
158 from . import (
158 from . import (
159 bookmarks,
159 bookmarks,
160 changegroup,
160 changegroup,
161 error,
161 error,
162 node as nodemod,
162 node as nodemod,
163 obsolete,
163 obsolete,
164 phases,
164 phases,
165 pushkey,
165 pushkey,
166 pycompat,
166 pycompat,
167 tags,
167 tags,
168 url,
168 url,
169 util,
169 util,
170 )
170 )
171
171
172 urlerr = util.urlerr
172 urlerr = util.urlerr
173 urlreq = util.urlreq
173 urlreq = util.urlreq
174
174
175 _pack = struct.pack
175 _pack = struct.pack
176 _unpack = struct.unpack
176 _unpack = struct.unpack
177
177
178 _fstreamparamsize = '>i'
178 _fstreamparamsize = '>i'
179 _fpartheadersize = '>i'
179 _fpartheadersize = '>i'
180 _fparttypesize = '>B'
180 _fparttypesize = '>B'
181 _fpartid = '>I'
181 _fpartid = '>I'
182 _fpayloadsize = '>i'
182 _fpayloadsize = '>i'
183 _fpartparamcount = '>BB'
183 _fpartparamcount = '>BB'
184
184
185 preferedchunksize = 4096
185 preferedchunksize = 4096
186
186
187 _parttypeforbidden = re.compile('[^a-zA-Z0-9_:-]')
187 _parttypeforbidden = re.compile('[^a-zA-Z0-9_:-]')
188
188
189 def outdebug(ui, message):
189 def outdebug(ui, message):
190 """debug regarding output stream (bundling)"""
190 """debug regarding output stream (bundling)"""
191 if ui.configbool('devel', 'bundle2.debug'):
191 if ui.configbool('devel', 'bundle2.debug'):
192 ui.debug('bundle2-output: %s\n' % message)
192 ui.debug('bundle2-output: %s\n' % message)
193
193
194 def indebug(ui, message):
194 def indebug(ui, message):
195 """debug on input stream (unbundling)"""
195 """debug on input stream (unbundling)"""
196 if ui.configbool('devel', 'bundle2.debug'):
196 if ui.configbool('devel', 'bundle2.debug'):
197 ui.debug('bundle2-input: %s\n' % message)
197 ui.debug('bundle2-input: %s\n' % message)
198
198
199 def validateparttype(parttype):
199 def validateparttype(parttype):
200 """raise ValueError if a parttype contains invalid character"""
200 """raise ValueError if a parttype contains invalid character"""
201 if _parttypeforbidden.search(parttype):
201 if _parttypeforbidden.search(parttype):
202 raise ValueError(parttype)
202 raise ValueError(parttype)
203
203
204 def _makefpartparamsizes(nbparams):
204 def _makefpartparamsizes(nbparams):
205 """return a struct format to read part parameter sizes
205 """return a struct format to read part parameter sizes
206
206
207 The number parameters is variable so we need to build that format
207 The number parameters is variable so we need to build that format
208 dynamically.
208 dynamically.
209 """
209 """
210 return '>'+('BB'*nbparams)
210 return '>'+('BB'*nbparams)
211
211
212 parthandlermapping = {}
212 parthandlermapping = {}
213
213
214 def parthandler(parttype, params=()):
214 def parthandler(parttype, params=()):
215 """decorator that register a function as a bundle2 part handler
215 """decorator that register a function as a bundle2 part handler
216
216
217 eg::
217 eg::
218
218
219 @parthandler('myparttype', ('mandatory', 'param', 'handled'))
219 @parthandler('myparttype', ('mandatory', 'param', 'handled'))
220 def myparttypehandler(...):
220 def myparttypehandler(...):
221 '''process a part of type "my part".'''
221 '''process a part of type "my part".'''
222 ...
222 ...
223 """
223 """
224 validateparttype(parttype)
224 validateparttype(parttype)
225 def _decorator(func):
225 def _decorator(func):
226 lparttype = parttype.lower() # enforce lower case matching.
226 lparttype = parttype.lower() # enforce lower case matching.
227 assert lparttype not in parthandlermapping
227 assert lparttype not in parthandlermapping
228 parthandlermapping[lparttype] = func
228 parthandlermapping[lparttype] = func
229 func.params = frozenset(params)
229 func.params = frozenset(params)
230 return func
230 return func
231 return _decorator
231 return _decorator
232
232
233 class unbundlerecords(object):
233 class unbundlerecords(object):
234 """keep record of what happens during and unbundle
234 """keep record of what happens during and unbundle
235
235
236 New records are added using `records.add('cat', obj)`. Where 'cat' is a
236 New records are added using `records.add('cat', obj)`. Where 'cat' is a
237 category of record and obj is an arbitrary object.
237 category of record and obj is an arbitrary object.
238
238
239 `records['cat']` will return all entries of this category 'cat'.
239 `records['cat']` will return all entries of this category 'cat'.
240
240
241 Iterating on the object itself will yield `('category', obj)` tuples
241 Iterating on the object itself will yield `('category', obj)` tuples
242 for all entries.
242 for all entries.
243
243
244 All iterations happens in chronological order.
244 All iterations happens in chronological order.
245 """
245 """
246
246
247 def __init__(self):
247 def __init__(self):
248 self._categories = {}
248 self._categories = {}
249 self._sequences = []
249 self._sequences = []
250 self._replies = {}
250 self._replies = {}
251
251
252 def add(self, category, entry, inreplyto=None):
252 def add(self, category, entry, inreplyto=None):
253 """add a new record of a given category.
253 """add a new record of a given category.
254
254
255 The entry can then be retrieved in the list returned by
255 The entry can then be retrieved in the list returned by
256 self['category']."""
256 self['category']."""
257 self._categories.setdefault(category, []).append(entry)
257 self._categories.setdefault(category, []).append(entry)
258 self._sequences.append((category, entry))
258 self._sequences.append((category, entry))
259 if inreplyto is not None:
259 if inreplyto is not None:
260 self.getreplies(inreplyto).add(category, entry)
260 self.getreplies(inreplyto).add(category, entry)
261
261
262 def getreplies(self, partid):
262 def getreplies(self, partid):
263 """get the records that are replies to a specific part"""
263 """get the records that are replies to a specific part"""
264 return self._replies.setdefault(partid, unbundlerecords())
264 return self._replies.setdefault(partid, unbundlerecords())
265
265
266 def __getitem__(self, cat):
266 def __getitem__(self, cat):
267 return tuple(self._categories.get(cat, ()))
267 return tuple(self._categories.get(cat, ()))
268
268
269 def __iter__(self):
269 def __iter__(self):
270 return iter(self._sequences)
270 return iter(self._sequences)
271
271
272 def __len__(self):
272 def __len__(self):
273 return len(self._sequences)
273 return len(self._sequences)
274
274
275 def __nonzero__(self):
275 def __nonzero__(self):
276 return bool(self._sequences)
276 return bool(self._sequences)
277
277
278 __bool__ = __nonzero__
278 __bool__ = __nonzero__
279
279
280 class bundleoperation(object):
280 class bundleoperation(object):
281 """an object that represents a single bundling process
281 """an object that represents a single bundling process
282
282
283 Its purpose is to carry unbundle-related objects and states.
283 Its purpose is to carry unbundle-related objects and states.
284
284
285 A new object should be created at the beginning of each bundle processing.
285 A new object should be created at the beginning of each bundle processing.
286 The object is to be returned by the processing function.
286 The object is to be returned by the processing function.
287
287
288 The object has very little content now it will ultimately contain:
288 The object has very little content now it will ultimately contain:
289 * an access to the repo the bundle is applied to,
289 * an access to the repo the bundle is applied to,
290 * a ui object,
290 * a ui object,
291 * a way to retrieve a transaction to add changes to the repo,
291 * a way to retrieve a transaction to add changes to the repo,
292 * a way to record the result of processing each part,
292 * a way to record the result of processing each part,
293 * a way to construct a bundle response when applicable.
293 * a way to construct a bundle response when applicable.
294 """
294 """
295
295
296 def __init__(self, repo, transactiongetter, captureoutput=True):
296 def __init__(self, repo, transactiongetter, captureoutput=True):
297 self.repo = repo
297 self.repo = repo
298 self.ui = repo.ui
298 self.ui = repo.ui
299 self.records = unbundlerecords()
299 self.records = unbundlerecords()
300 self.reply = None
300 self.reply = None
301 self.captureoutput = captureoutput
301 self.captureoutput = captureoutput
302 self.hookargs = {}
302 self.hookargs = {}
303 self._gettransaction = transactiongetter
303 self._gettransaction = transactiongetter
304
304
305 def gettransaction(self):
305 def gettransaction(self):
306 transaction = self._gettransaction()
306 transaction = self._gettransaction()
307
307
308 if self.hookargs:
308 if self.hookargs:
309 # the ones added to the transaction supercede those added
309 # the ones added to the transaction supercede those added
310 # to the operation.
310 # to the operation.
311 self.hookargs.update(transaction.hookargs)
311 self.hookargs.update(transaction.hookargs)
312 transaction.hookargs = self.hookargs
312 transaction.hookargs = self.hookargs
313
313
314 # mark the hookargs as flushed. further attempts to add to
314 # mark the hookargs as flushed. further attempts to add to
315 # hookargs will result in an abort.
315 # hookargs will result in an abort.
316 self.hookargs = None
316 self.hookargs = None
317
317
318 return transaction
318 return transaction
319
319
320 def addhookargs(self, hookargs):
320 def addhookargs(self, hookargs):
321 if self.hookargs is None:
321 if self.hookargs is None:
322 raise error.ProgrammingError('attempted to add hookargs to '
322 raise error.ProgrammingError('attempted to add hookargs to '
323 'operation after transaction started')
323 'operation after transaction started')
324 self.hookargs.update(hookargs)
324 self.hookargs.update(hookargs)
325
325
326 class TransactionUnavailable(RuntimeError):
326 class TransactionUnavailable(RuntimeError):
327 pass
327 pass
328
328
329 def _notransaction():
329 def _notransaction():
330 """default method to get a transaction while processing a bundle
330 """default method to get a transaction while processing a bundle
331
331
332 Raise an exception to highlight the fact that no transaction was expected
332 Raise an exception to highlight the fact that no transaction was expected
333 to be created"""
333 to be created"""
334 raise TransactionUnavailable()
334 raise TransactionUnavailable()
335
335
336 def applybundle(repo, unbundler, tr, source=None, url=None, **kwargs):
336 def applybundle(repo, unbundler, tr, source=None, url=None, **kwargs):
337 # transform me into unbundler.apply() as soon as the freeze is lifted
337 # transform me into unbundler.apply() as soon as the freeze is lifted
338 if isinstance(unbundler, unbundle20):
338 if isinstance(unbundler, unbundle20):
339 tr.hookargs['bundle2'] = '1'
339 tr.hookargs['bundle2'] = '1'
340 if source is not None and 'source' not in tr.hookargs:
340 if source is not None and 'source' not in tr.hookargs:
341 tr.hookargs['source'] = source
341 tr.hookargs['source'] = source
342 if url is not None and 'url' not in tr.hookargs:
342 if url is not None and 'url' not in tr.hookargs:
343 tr.hookargs['url'] = url
343 tr.hookargs['url'] = url
344 return processbundle(repo, unbundler, lambda: tr)
344 return processbundle(repo, unbundler, lambda: tr)
345 else:
345 else:
346 # the transactiongetter won't be used, but we might as well set it
346 # the transactiongetter won't be used, but we might as well set it
347 op = bundleoperation(repo, lambda: tr)
347 op = bundleoperation(repo, lambda: tr)
348 _processchangegroup(op, unbundler, tr, source, url, **kwargs)
348 _processchangegroup(op, unbundler, tr, source, url, **kwargs)
349 return op
349 return op
350
350
351 class partiterator(object):
351 class partiterator(object):
352 def __init__(self, repo, op, unbundler):
352 def __init__(self, repo, op, unbundler):
353 self.repo = repo
353 self.repo = repo
354 self.op = op
354 self.op = op
355 self.unbundler = unbundler
355 self.unbundler = unbundler
356 self.iterator = None
356 self.iterator = None
357 self.count = 0
357 self.count = 0
358 self.current = None
358 self.current = None
359
359
360 def __enter__(self):
360 def __enter__(self):
361 def func():
361 def func():
362 itr = enumerate(self.unbundler.iterparts())
362 itr = enumerate(self.unbundler.iterparts())
363 for count, p in itr:
363 for count, p in itr:
364 self.count = count
364 self.count = count
365 self.current = p
365 self.current = p
366 yield p
366 yield p
367 p.consume()
367 p.consume()
368 self.current = None
368 self.current = None
369 self.iterator = func()
369 self.iterator = func()
370 return self.iterator
370 return self.iterator
371
371
372 def __exit__(self, type, exc, tb):
372 def __exit__(self, type, exc, tb):
373 if not self.iterator:
373 if not self.iterator:
374 return
374 return
375
375
376 # Only gracefully abort in a normal exception situation. User aborts
376 # Only gracefully abort in a normal exception situation. User aborts
377 # like Ctrl+C throw a KeyboardInterrupt which is not a base Exception,
377 # like Ctrl+C throw a KeyboardInterrupt which is not a base Exception,
378 # and should not gracefully cleanup.
378 # and should not gracefully cleanup.
379 if isinstance(exc, Exception):
379 if isinstance(exc, Exception):
380 # Any exceptions seeking to the end of the bundle at this point are
380 # Any exceptions seeking to the end of the bundle at this point are
381 # almost certainly related to the underlying stream being bad.
381 # almost certainly related to the underlying stream being bad.
382 # And, chances are that the exception we're handling is related to
382 # And, chances are that the exception we're handling is related to
383 # getting in that bad state. So, we swallow the seeking error and
383 # getting in that bad state. So, we swallow the seeking error and
384 # re-raise the original error.
384 # re-raise the original error.
385 seekerror = False
385 seekerror = False
386 try:
386 try:
387 if self.current:
387 if self.current:
388 # consume the part content to not corrupt the stream.
388 # consume the part content to not corrupt the stream.
389 self.current.consume()
389 self.current.consume()
390
390
391 for part in self.iterator:
391 for part in self.iterator:
392 # consume the bundle content
392 # consume the bundle content
393 part.consume()
393 part.consume()
394 except Exception:
394 except Exception:
395 seekerror = True
395 seekerror = True
396
396
397 # Small hack to let caller code distinguish exceptions from bundle2
397 # Small hack to let caller code distinguish exceptions from bundle2
398 # processing from processing the old format. This is mostly needed
398 # processing from processing the old format. This is mostly needed
399 # to handle different return codes to unbundle according to the type
399 # to handle different return codes to unbundle according to the type
400 # of bundle. We should probably clean up or drop this return code
400 # of bundle. We should probably clean up or drop this return code
401 # craziness in a future version.
401 # craziness in a future version.
402 exc.duringunbundle2 = True
402 exc.duringunbundle2 = True
403 salvaged = []
403 salvaged = []
404 replycaps = None
404 replycaps = None
405 if self.op.reply is not None:
405 if self.op.reply is not None:
406 salvaged = self.op.reply.salvageoutput()
406 salvaged = self.op.reply.salvageoutput()
407 replycaps = self.op.reply.capabilities
407 replycaps = self.op.reply.capabilities
408 exc._replycaps = replycaps
408 exc._replycaps = replycaps
409 exc._bundle2salvagedoutput = salvaged
409 exc._bundle2salvagedoutput = salvaged
410
410
411 # Re-raising from a variable loses the original stack. So only use
411 # Re-raising from a variable loses the original stack. So only use
412 # that form if we need to.
412 # that form if we need to.
413 if seekerror:
413 if seekerror:
414 raise exc
414 raise exc
415
415
416 self.repo.ui.debug('bundle2-input-bundle: %i parts total\n' %
416 self.repo.ui.debug('bundle2-input-bundle: %i parts total\n' %
417 self.count)
417 self.count)
418
418
419 def processbundle(repo, unbundler, transactiongetter=None, op=None):
419 def processbundle(repo, unbundler, transactiongetter=None, op=None):
420 """This function process a bundle, apply effect to/from a repo
420 """This function process a bundle, apply effect to/from a repo
421
421
422 It iterates over each part then searches for and uses the proper handling
422 It iterates over each part then searches for and uses the proper handling
423 code to process the part. Parts are processed in order.
423 code to process the part. Parts are processed in order.
424
424
425 Unknown Mandatory part will abort the process.
425 Unknown Mandatory part will abort the process.
426
426
427 It is temporarily possible to provide a prebuilt bundleoperation to the
427 It is temporarily possible to provide a prebuilt bundleoperation to the
428 function. This is used to ensure output is properly propagated in case of
428 function. This is used to ensure output is properly propagated in case of
429 an error during the unbundling. This output capturing part will likely be
429 an error during the unbundling. This output capturing part will likely be
430 reworked and this ability will probably go away in the process.
430 reworked and this ability will probably go away in the process.
431 """
431 """
432 if op is None:
432 if op is None:
433 if transactiongetter is None:
433 if transactiongetter is None:
434 transactiongetter = _notransaction
434 transactiongetter = _notransaction
435 op = bundleoperation(repo, transactiongetter)
435 op = bundleoperation(repo, transactiongetter)
436 # todo:
436 # todo:
437 # - replace this is a init function soon.
437 # - replace this is a init function soon.
438 # - exception catching
438 # - exception catching
439 unbundler.params
439 unbundler.params
440 if repo.ui.debugflag:
440 if repo.ui.debugflag:
441 msg = ['bundle2-input-bundle:']
441 msg = ['bundle2-input-bundle:']
442 if unbundler.params:
442 if unbundler.params:
443 msg.append(' %i params' % len(unbundler.params))
443 msg.append(' %i params' % len(unbundler.params))
444 if op._gettransaction is None or op._gettransaction is _notransaction:
444 if op._gettransaction is None or op._gettransaction is _notransaction:
445 msg.append(' no-transaction')
445 msg.append(' no-transaction')
446 else:
446 else:
447 msg.append(' with-transaction')
447 msg.append(' with-transaction')
448 msg.append('\n')
448 msg.append('\n')
449 repo.ui.debug(''.join(msg))
449 repo.ui.debug(''.join(msg))
450
450
451 processparts(repo, op, unbundler)
451 processparts(repo, op, unbundler)
452
452
453 return op
453 return op
454
454
455 def processparts(repo, op, unbundler):
455 def processparts(repo, op, unbundler):
456 with partiterator(repo, op, unbundler) as parts:
456 with partiterator(repo, op, unbundler) as parts:
457 for part in parts:
457 for part in parts:
458 _processpart(op, part)
458 _processpart(op, part)
459
459
460 def _processchangegroup(op, cg, tr, source, url, **kwargs):
460 def _processchangegroup(op, cg, tr, source, url, **kwargs):
461 ret = cg.apply(op.repo, tr, source, url, **kwargs)
461 ret = cg.apply(op.repo, tr, source, url, **kwargs)
462 op.records.add('changegroup', {
462 op.records.add('changegroup', {
463 'return': ret,
463 'return': ret,
464 })
464 })
465 return ret
465 return ret
466
466
467 def _gethandler(op, part):
467 def _gethandler(op, part):
468 status = 'unknown' # used by debug output
468 status = 'unknown' # used by debug output
469 try:
469 try:
470 handler = parthandlermapping.get(part.type)
470 handler = parthandlermapping.get(part.type)
471 if handler is None:
471 if handler is None:
472 status = 'unsupported-type'
472 status = 'unsupported-type'
473 raise error.BundleUnknownFeatureError(parttype=part.type)
473 raise error.BundleUnknownFeatureError(parttype=part.type)
474 indebug(op.ui, 'found a handler for part %s' % part.type)
474 indebug(op.ui, 'found a handler for part %s' % part.type)
475 unknownparams = part.mandatorykeys - handler.params
475 unknownparams = part.mandatorykeys - handler.params
476 if unknownparams:
476 if unknownparams:
477 unknownparams = list(unknownparams)
477 unknownparams = list(unknownparams)
478 unknownparams.sort()
478 unknownparams.sort()
479 status = 'unsupported-params (%s)' % ', '.join(unknownparams)
479 status = 'unsupported-params (%s)' % ', '.join(unknownparams)
480 raise error.BundleUnknownFeatureError(parttype=part.type,
480 raise error.BundleUnknownFeatureError(parttype=part.type,
481 params=unknownparams)
481 params=unknownparams)
482 status = 'supported'
482 status = 'supported'
483 except error.BundleUnknownFeatureError as exc:
483 except error.BundleUnknownFeatureError as exc:
484 if part.mandatory: # mandatory parts
484 if part.mandatory: # mandatory parts
485 raise
485 raise
486 indebug(op.ui, 'ignoring unsupported advisory part %s' % exc)
486 indebug(op.ui, 'ignoring unsupported advisory part %s' % exc)
487 return # skip to part processing
487 return # skip to part processing
488 finally:
488 finally:
489 if op.ui.debugflag:
489 if op.ui.debugflag:
490 msg = ['bundle2-input-part: "%s"' % part.type]
490 msg = ['bundle2-input-part: "%s"' % part.type]
491 if not part.mandatory:
491 if not part.mandatory:
492 msg.append(' (advisory)')
492 msg.append(' (advisory)')
493 nbmp = len(part.mandatorykeys)
493 nbmp = len(part.mandatorykeys)
494 nbap = len(part.params) - nbmp
494 nbap = len(part.params) - nbmp
495 if nbmp or nbap:
495 if nbmp or nbap:
496 msg.append(' (params:')
496 msg.append(' (params:')
497 if nbmp:
497 if nbmp:
498 msg.append(' %i mandatory' % nbmp)
498 msg.append(' %i mandatory' % nbmp)
499 if nbap:
499 if nbap:
500 msg.append(' %i advisory' % nbmp)
500 msg.append(' %i advisory' % nbmp)
501 msg.append(')')
501 msg.append(')')
502 msg.append(' %s\n' % status)
502 msg.append(' %s\n' % status)
503 op.ui.debug(''.join(msg))
503 op.ui.debug(''.join(msg))
504
504
505 return handler
505 return handler
506
506
507 def _processpart(op, part):
507 def _processpart(op, part):
508 """process a single part from a bundle
508 """process a single part from a bundle
509
509
510 The part is guaranteed to have been fully consumed when the function exits
510 The part is guaranteed to have been fully consumed when the function exits
511 (even if an exception is raised)."""
511 (even if an exception is raised)."""
512 handler = _gethandler(op, part)
512 handler = _gethandler(op, part)
513 if handler is None:
513 if handler is None:
514 return
514 return
515
515
516 # handler is called outside the above try block so that we don't
516 # handler is called outside the above try block so that we don't
517 # risk catching KeyErrors from anything other than the
517 # risk catching KeyErrors from anything other than the
518 # parthandlermapping lookup (any KeyError raised by handler()
518 # parthandlermapping lookup (any KeyError raised by handler()
519 # itself represents a defect of a different variety).
519 # itself represents a defect of a different variety).
520 output = None
520 output = None
521 if op.captureoutput and op.reply is not None:
521 if op.captureoutput and op.reply is not None:
522 op.ui.pushbuffer(error=True, subproc=True)
522 op.ui.pushbuffer(error=True, subproc=True)
523 output = ''
523 output = ''
524 try:
524 try:
525 handler(op, part)
525 handler(op, part)
526 finally:
526 finally:
527 if output is not None:
527 if output is not None:
528 output = op.ui.popbuffer()
528 output = op.ui.popbuffer()
529 if output:
529 if output:
530 outpart = op.reply.newpart('output', data=output,
530 outpart = op.reply.newpart('output', data=output,
531 mandatory=False)
531 mandatory=False)
532 outpart.addparam(
532 outpart.addparam(
533 'in-reply-to', pycompat.bytestr(part.id), mandatory=False)
533 'in-reply-to', pycompat.bytestr(part.id), mandatory=False)
534
534
535 def decodecaps(blob):
535 def decodecaps(blob):
536 """decode a bundle2 caps bytes blob into a dictionary
536 """decode a bundle2 caps bytes blob into a dictionary
537
537
538 The blob is a list of capabilities (one per line)
538 The blob is a list of capabilities (one per line)
539 Capabilities may have values using a line of the form::
539 Capabilities may have values using a line of the form::
540
540
541 capability=value1,value2,value3
541 capability=value1,value2,value3
542
542
543 The values are always a list."""
543 The values are always a list."""
544 caps = {}
544 caps = {}
545 for line in blob.splitlines():
545 for line in blob.splitlines():
546 if not line:
546 if not line:
547 continue
547 continue
548 if '=' not in line:
548 if '=' not in line:
549 key, vals = line, ()
549 key, vals = line, ()
550 else:
550 else:
551 key, vals = line.split('=', 1)
551 key, vals = line.split('=', 1)
552 vals = vals.split(',')
552 vals = vals.split(',')
553 key = urlreq.unquote(key)
553 key = urlreq.unquote(key)
554 vals = [urlreq.unquote(v) for v in vals]
554 vals = [urlreq.unquote(v) for v in vals]
555 caps[key] = vals
555 caps[key] = vals
556 return caps
556 return caps
557
557
558 def encodecaps(caps):
558 def encodecaps(caps):
559 """encode a bundle2 caps dictionary into a bytes blob"""
559 """encode a bundle2 caps dictionary into a bytes blob"""
560 chunks = []
560 chunks = []
561 for ca in sorted(caps):
561 for ca in sorted(caps):
562 vals = caps[ca]
562 vals = caps[ca]
563 ca = urlreq.quote(ca)
563 ca = urlreq.quote(ca)
564 vals = [urlreq.quote(v) for v in vals]
564 vals = [urlreq.quote(v) for v in vals]
565 if vals:
565 if vals:
566 ca = "%s=%s" % (ca, ','.join(vals))
566 ca = "%s=%s" % (ca, ','.join(vals))
567 chunks.append(ca)
567 chunks.append(ca)
568 return '\n'.join(chunks)
568 return '\n'.join(chunks)
569
569
570 bundletypes = {
570 bundletypes = {
571 "": ("", 'UN'), # only when using unbundle on ssh and old http servers
571 "": ("", 'UN'), # only when using unbundle on ssh and old http servers
572 # since the unification ssh accepts a header but there
572 # since the unification ssh accepts a header but there
573 # is no capability signaling it.
573 # is no capability signaling it.
574 "HG20": (), # special-cased below
574 "HG20": (), # special-cased below
575 "HG10UN": ("HG10UN", 'UN'),
575 "HG10UN": ("HG10UN", 'UN'),
576 "HG10BZ": ("HG10", 'BZ'),
576 "HG10BZ": ("HG10", 'BZ'),
577 "HG10GZ": ("HG10GZ", 'GZ'),
577 "HG10GZ": ("HG10GZ", 'GZ'),
578 }
578 }
579
579
580 # hgweb uses this list to communicate its preferred type
580 # hgweb uses this list to communicate its preferred type
581 bundlepriority = ['HG10GZ', 'HG10BZ', 'HG10UN']
581 bundlepriority = ['HG10GZ', 'HG10BZ', 'HG10UN']
582
582
583 class bundle20(object):
583 class bundle20(object):
584 """represent an outgoing bundle2 container
584 """represent an outgoing bundle2 container
585
585
586 Use the `addparam` method to add stream level parameter. and `newpart` to
586 Use the `addparam` method to add stream level parameter. and `newpart` to
587 populate it. Then call `getchunks` to retrieve all the binary chunks of
587 populate it. Then call `getchunks` to retrieve all the binary chunks of
588 data that compose the bundle2 container."""
588 data that compose the bundle2 container."""
589
589
590 _magicstring = 'HG20'
590 _magicstring = 'HG20'
591
591
592 def __init__(self, ui, capabilities=()):
592 def __init__(self, ui, capabilities=()):
593 self.ui = ui
593 self.ui = ui
594 self._params = []
594 self._params = []
595 self._parts = []
595 self._parts = []
596 self.capabilities = dict(capabilities)
596 self.capabilities = dict(capabilities)
597 self._compengine = util.compengines.forbundletype('UN')
597 self._compengine = util.compengines.forbundletype('UN')
598 self._compopts = None
598 self._compopts = None
599
599
600 def setcompression(self, alg, compopts=None):
600 def setcompression(self, alg, compopts=None):
601 """setup core part compression to <alg>"""
601 """setup core part compression to <alg>"""
602 if alg in (None, 'UN'):
602 if alg in (None, 'UN'):
603 return
603 return
604 assert not any(n.lower() == 'compression' for n, v in self._params)
604 assert not any(n.lower() == 'compression' for n, v in self._params)
605 self.addparam('Compression', alg)
605 self.addparam('Compression', alg)
606 self._compengine = util.compengines.forbundletype(alg)
606 self._compengine = util.compengines.forbundletype(alg)
607 self._compopts = compopts
607 self._compopts = compopts
608
608
609 @property
609 @property
610 def nbparts(self):
610 def nbparts(self):
611 """total number of parts added to the bundler"""
611 """total number of parts added to the bundler"""
612 return len(self._parts)
612 return len(self._parts)
613
613
614 # methods used to defines the bundle2 content
614 # methods used to defines the bundle2 content
615 def addparam(self, name, value=None):
615 def addparam(self, name, value=None):
616 """add a stream level parameter"""
616 """add a stream level parameter"""
617 if not name:
617 if not name:
618 raise ValueError(r'empty parameter name')
618 raise ValueError(r'empty parameter name')
619 if name[0:1] not in pycompat.bytestr(string.ascii_letters):
619 if name[0:1] not in pycompat.bytestr(string.ascii_letters):
620 raise ValueError(r'non letter first character: %s' % name)
620 raise ValueError(r'non letter first character: %s' % name)
621 self._params.append((name, value))
621 self._params.append((name, value))
622
622
623 def addpart(self, part):
623 def addpart(self, part):
624 """add a new part to the bundle2 container
624 """add a new part to the bundle2 container
625
625
626 Parts contains the actual applicative payload."""
626 Parts contains the actual applicative payload."""
627 assert part.id is None
627 assert part.id is None
628 part.id = len(self._parts) # very cheap counter
628 part.id = len(self._parts) # very cheap counter
629 self._parts.append(part)
629 self._parts.append(part)
630
630
631 def newpart(self, typeid, *args, **kwargs):
631 def newpart(self, typeid, *args, **kwargs):
632 """create a new part and add it to the containers
632 """create a new part and add it to the containers
633
633
634 As the part is directly added to the containers. For now, this means
634 As the part is directly added to the containers. For now, this means
635 that any failure to properly initialize the part after calling
635 that any failure to properly initialize the part after calling
636 ``newpart`` should result in a failure of the whole bundling process.
636 ``newpart`` should result in a failure of the whole bundling process.
637
637
638 You can still fall back to manually create and add if you need better
638 You can still fall back to manually create and add if you need better
639 control."""
639 control."""
640 part = bundlepart(typeid, *args, **kwargs)
640 part = bundlepart(typeid, *args, **kwargs)
641 self.addpart(part)
641 self.addpart(part)
642 return part
642 return part
643
643
644 # methods used to generate the bundle2 stream
644 # methods used to generate the bundle2 stream
645 def getchunks(self):
645 def getchunks(self):
646 if self.ui.debugflag:
646 if self.ui.debugflag:
647 msg = ['bundle2-output-bundle: "%s",' % self._magicstring]
647 msg = ['bundle2-output-bundle: "%s",' % self._magicstring]
648 if self._params:
648 if self._params:
649 msg.append(' (%i params)' % len(self._params))
649 msg.append(' (%i params)' % len(self._params))
650 msg.append(' %i parts total\n' % len(self._parts))
650 msg.append(' %i parts total\n' % len(self._parts))
651 self.ui.debug(''.join(msg))
651 self.ui.debug(''.join(msg))
652 outdebug(self.ui, 'start emission of %s stream' % self._magicstring)
652 outdebug(self.ui, 'start emission of %s stream' % self._magicstring)
653 yield self._magicstring
653 yield self._magicstring
654 param = self._paramchunk()
654 param = self._paramchunk()
655 outdebug(self.ui, 'bundle parameter: %s' % param)
655 outdebug(self.ui, 'bundle parameter: %s' % param)
656 yield _pack(_fstreamparamsize, len(param))
656 yield _pack(_fstreamparamsize, len(param))
657 if param:
657 if param:
658 yield param
658 yield param
659 for chunk in self._compengine.compressstream(self._getcorechunk(),
659 for chunk in self._compengine.compressstream(self._getcorechunk(),
660 self._compopts):
660 self._compopts):
661 yield chunk
661 yield chunk
662
662
663 def _paramchunk(self):
663 def _paramchunk(self):
664 """return a encoded version of all stream parameters"""
664 """return a encoded version of all stream parameters"""
665 blocks = []
665 blocks = []
666 for par, value in self._params:
666 for par, value in self._params:
667 par = urlreq.quote(par)
667 par = urlreq.quote(par)
668 if value is not None:
668 if value is not None:
669 value = urlreq.quote(value)
669 value = urlreq.quote(value)
670 par = '%s=%s' % (par, value)
670 par = '%s=%s' % (par, value)
671 blocks.append(par)
671 blocks.append(par)
672 return ' '.join(blocks)
672 return ' '.join(blocks)
673
673
674 def _getcorechunk(self):
674 def _getcorechunk(self):
675 """yield chunk for the core part of the bundle
675 """yield chunk for the core part of the bundle
676
676
677 (all but headers and parameters)"""
677 (all but headers and parameters)"""
678 outdebug(self.ui, 'start of parts')
678 outdebug(self.ui, 'start of parts')
679 for part in self._parts:
679 for part in self._parts:
680 outdebug(self.ui, 'bundle part: "%s"' % part.type)
680 outdebug(self.ui, 'bundle part: "%s"' % part.type)
681 for chunk in part.getchunks(ui=self.ui):
681 for chunk in part.getchunks(ui=self.ui):
682 yield chunk
682 yield chunk
683 outdebug(self.ui, 'end of bundle')
683 outdebug(self.ui, 'end of bundle')
684 yield _pack(_fpartheadersize, 0)
684 yield _pack(_fpartheadersize, 0)
685
685
686
686
687 def salvageoutput(self):
687 def salvageoutput(self):
688 """return a list with a copy of all output parts in the bundle
688 """return a list with a copy of all output parts in the bundle
689
689
690 This is meant to be used during error handling to make sure we preserve
690 This is meant to be used during error handling to make sure we preserve
691 server output"""
691 server output"""
692 salvaged = []
692 salvaged = []
693 for part in self._parts:
693 for part in self._parts:
694 if part.type.startswith('output'):
694 if part.type.startswith('output'):
695 salvaged.append(part.copy())
695 salvaged.append(part.copy())
696 return salvaged
696 return salvaged
697
697
698
698
699 class unpackermixin(object):
699 class unpackermixin(object):
700 """A mixin to extract bytes and struct data from a stream"""
700 """A mixin to extract bytes and struct data from a stream"""
701
701
702 def __init__(self, fp):
702 def __init__(self, fp):
703 self._fp = fp
703 self._fp = fp
704
704
705 def _unpack(self, format):
705 def _unpack(self, format):
706 """unpack this struct format from the stream
706 """unpack this struct format from the stream
707
707
708 This method is meant for internal usage by the bundle2 protocol only.
708 This method is meant for internal usage by the bundle2 protocol only.
709 They directly manipulate the low level stream including bundle2 level
709 They directly manipulate the low level stream including bundle2 level
710 instruction.
710 instruction.
711
711
712 Do not use it to implement higher-level logic or methods."""
712 Do not use it to implement higher-level logic or methods."""
713 data = self._readexact(struct.calcsize(format))
713 data = self._readexact(struct.calcsize(format))
714 return _unpack(format, data)
714 return _unpack(format, data)
715
715
716 def _readexact(self, size):
716 def _readexact(self, size):
717 """read exactly <size> bytes from the stream
717 """read exactly <size> bytes from the stream
718
718
719 This method is meant for internal usage by the bundle2 protocol only.
719 This method is meant for internal usage by the bundle2 protocol only.
720 They directly manipulate the low level stream including bundle2 level
720 They directly manipulate the low level stream including bundle2 level
721 instruction.
721 instruction.
722
722
723 Do not use it to implement higher-level logic or methods."""
723 Do not use it to implement higher-level logic or methods."""
724 return changegroup.readexactly(self._fp, size)
724 return changegroup.readexactly(self._fp, size)
725
725
726 def getunbundler(ui, fp, magicstring=None):
726 def getunbundler(ui, fp, magicstring=None):
727 """return a valid unbundler object for a given magicstring"""
727 """return a valid unbundler object for a given magicstring"""
728 if magicstring is None:
728 if magicstring is None:
729 magicstring = changegroup.readexactly(fp, 4)
729 magicstring = changegroup.readexactly(fp, 4)
730 magic, version = magicstring[0:2], magicstring[2:4]
730 magic, version = magicstring[0:2], magicstring[2:4]
731 if magic != 'HG':
731 if magic != 'HG':
732 ui.debug(
732 ui.debug(
733 "error: invalid magic: %r (version %r), should be 'HG'\n"
733 "error: invalid magic: %r (version %r), should be 'HG'\n"
734 % (magic, version))
734 % (magic, version))
735 raise error.Abort(_('not a Mercurial bundle'))
735 raise error.Abort(_('not a Mercurial bundle'))
736 unbundlerclass = formatmap.get(version)
736 unbundlerclass = formatmap.get(version)
737 if unbundlerclass is None:
737 if unbundlerclass is None:
738 raise error.Abort(_('unknown bundle version %s') % version)
738 raise error.Abort(_('unknown bundle version %s') % version)
739 unbundler = unbundlerclass(ui, fp)
739 unbundler = unbundlerclass(ui, fp)
740 indebug(ui, 'start processing of %s stream' % magicstring)
740 indebug(ui, 'start processing of %s stream' % magicstring)
741 return unbundler
741 return unbundler
742
742
743 class unbundle20(unpackermixin):
743 class unbundle20(unpackermixin):
744 """interpret a bundle2 stream
744 """interpret a bundle2 stream
745
745
746 This class is fed with a binary stream and yields parts through its
746 This class is fed with a binary stream and yields parts through its
747 `iterparts` methods."""
747 `iterparts` methods."""
748
748
749 _magicstring = 'HG20'
749 _magicstring = 'HG20'
750
750
751 def __init__(self, ui, fp):
751 def __init__(self, ui, fp):
752 """If header is specified, we do not read it out of the stream."""
752 """If header is specified, we do not read it out of the stream."""
753 self.ui = ui
753 self.ui = ui
754 self._compengine = util.compengines.forbundletype('UN')
754 self._compengine = util.compengines.forbundletype('UN')
755 self._compressed = None
755 self._compressed = None
756 super(unbundle20, self).__init__(fp)
756 super(unbundle20, self).__init__(fp)
757
757
758 @util.propertycache
758 @util.propertycache
759 def params(self):
759 def params(self):
760 """dictionary of stream level parameters"""
760 """dictionary of stream level parameters"""
761 indebug(self.ui, 'reading bundle2 stream parameters')
761 indebug(self.ui, 'reading bundle2 stream parameters')
762 params = {}
762 params = {}
763 paramssize = self._unpack(_fstreamparamsize)[0]
763 paramssize = self._unpack(_fstreamparamsize)[0]
764 if paramssize < 0:
764 if paramssize < 0:
765 raise error.BundleValueError('negative bundle param size: %i'
765 raise error.BundleValueError('negative bundle param size: %i'
766 % paramssize)
766 % paramssize)
767 if paramssize:
767 if paramssize:
768 params = self._readexact(paramssize)
768 params = self._readexact(paramssize)
769 params = self._processallparams(params)
769 params = self._processallparams(params)
770 return params
770 return params
771
771
772 def _processallparams(self, paramsblock):
772 def _processallparams(self, paramsblock):
773 """"""
773 """"""
774 params = util.sortdict()
774 params = util.sortdict()
775 for p in paramsblock.split(' '):
775 for p in paramsblock.split(' '):
776 p = p.split('=', 1)
776 p = p.split('=', 1)
777 p = [urlreq.unquote(i) for i in p]
777 p = [urlreq.unquote(i) for i in p]
778 if len(p) < 2:
778 if len(p) < 2:
779 p.append(None)
779 p.append(None)
780 self._processparam(*p)
780 self._processparam(*p)
781 params[p[0]] = p[1]
781 params[p[0]] = p[1]
782 return params
782 return params
783
783
784
784
785 def _processparam(self, name, value):
785 def _processparam(self, name, value):
786 """process a parameter, applying its effect if needed
786 """process a parameter, applying its effect if needed
787
787
788 Parameter starting with a lower case letter are advisory and will be
788 Parameter starting with a lower case letter are advisory and will be
789 ignored when unknown. Those starting with an upper case letter are
789 ignored when unknown. Those starting with an upper case letter are
790 mandatory and will this function will raise a KeyError when unknown.
790 mandatory and will this function will raise a KeyError when unknown.
791
791
792 Note: no option are currently supported. Any input will be either
792 Note: no option are currently supported. Any input will be either
793 ignored or failing.
793 ignored or failing.
794 """
794 """
795 if not name:
795 if not name:
796 raise ValueError(r'empty parameter name')
796 raise ValueError(r'empty parameter name')
797 if name[0:1] not in pycompat.bytestr(string.ascii_letters):
797 if name[0:1] not in pycompat.bytestr(string.ascii_letters):
798 raise ValueError(r'non letter first character: %s' % name)
798 raise ValueError(r'non letter first character: %s' % name)
799 try:
799 try:
800 handler = b2streamparamsmap[name.lower()]
800 handler = b2streamparamsmap[name.lower()]
801 except KeyError:
801 except KeyError:
802 if name[0:1].islower():
802 if name[0:1].islower():
803 indebug(self.ui, "ignoring unknown parameter %s" % name)
803 indebug(self.ui, "ignoring unknown parameter %s" % name)
804 else:
804 else:
805 raise error.BundleUnknownFeatureError(params=(name,))
805 raise error.BundleUnknownFeatureError(params=(name,))
806 else:
806 else:
807 handler(self, name, value)
807 handler(self, name, value)
808
808
809 def _forwardchunks(self):
809 def _forwardchunks(self):
810 """utility to transfer a bundle2 as binary
810 """utility to transfer a bundle2 as binary
811
811
812 This is made necessary by the fact the 'getbundle' command over 'ssh'
812 This is made necessary by the fact the 'getbundle' command over 'ssh'
813 have no way to know then the reply end, relying on the bundle to be
813 have no way to know then the reply end, relying on the bundle to be
814 interpreted to know its end. This is terrible and we are sorry, but we
814 interpreted to know its end. This is terrible and we are sorry, but we
815 needed to move forward to get general delta enabled.
815 needed to move forward to get general delta enabled.
816 """
816 """
817 yield self._magicstring
817 yield self._magicstring
818 assert 'params' not in vars(self)
818 assert 'params' not in vars(self)
819 paramssize = self._unpack(_fstreamparamsize)[0]
819 paramssize = self._unpack(_fstreamparamsize)[0]
820 if paramssize < 0:
820 if paramssize < 0:
821 raise error.BundleValueError('negative bundle param size: %i'
821 raise error.BundleValueError('negative bundle param size: %i'
822 % paramssize)
822 % paramssize)
823 yield _pack(_fstreamparamsize, paramssize)
823 yield _pack(_fstreamparamsize, paramssize)
824 if paramssize:
824 if paramssize:
825 params = self._readexact(paramssize)
825 params = self._readexact(paramssize)
826 self._processallparams(params)
826 self._processallparams(params)
827 yield params
827 yield params
828 assert self._compengine.bundletype == 'UN'
828 assert self._compengine.bundletype == 'UN'
829 # From there, payload might need to be decompressed
829 # From there, payload might need to be decompressed
830 self._fp = self._compengine.decompressorreader(self._fp)
830 self._fp = self._compengine.decompressorreader(self._fp)
831 emptycount = 0
831 emptycount = 0
832 while emptycount < 2:
832 while emptycount < 2:
833 # so we can brainlessly loop
833 # so we can brainlessly loop
834 assert _fpartheadersize == _fpayloadsize
834 assert _fpartheadersize == _fpayloadsize
835 size = self._unpack(_fpartheadersize)[0]
835 size = self._unpack(_fpartheadersize)[0]
836 yield _pack(_fpartheadersize, size)
836 yield _pack(_fpartheadersize, size)
837 if size:
837 if size:
838 emptycount = 0
838 emptycount = 0
839 else:
839 else:
840 emptycount += 1
840 emptycount += 1
841 continue
841 continue
842 if size == flaginterrupt:
842 if size == flaginterrupt:
843 continue
843 continue
844 elif size < 0:
844 elif size < 0:
845 raise error.BundleValueError('negative chunk size: %i')
845 raise error.BundleValueError('negative chunk size: %i')
846 yield self._readexact(size)
846 yield self._readexact(size)
847
847
848
848
849 def iterparts(self, seekable=False):
849 def iterparts(self, seekable=False):
850 """yield all parts contained in the stream"""
850 """yield all parts contained in the stream"""
851 cls = seekableunbundlepart if seekable else unbundlepart
851 cls = seekableunbundlepart if seekable else unbundlepart
852 # make sure param have been loaded
852 # make sure param have been loaded
853 self.params
853 self.params
854 # From there, payload need to be decompressed
854 # From there, payload need to be decompressed
855 self._fp = self._compengine.decompressorreader(self._fp)
855 self._fp = self._compengine.decompressorreader(self._fp)
856 indebug(self.ui, 'start extraction of bundle2 parts')
856 indebug(self.ui, 'start extraction of bundle2 parts')
857 headerblock = self._readpartheader()
857 headerblock = self._readpartheader()
858 while headerblock is not None:
858 while headerblock is not None:
859 part = cls(self.ui, headerblock, self._fp)
859 part = cls(self.ui, headerblock, self._fp)
860 yield part
860 yield part
861 # Ensure part is fully consumed so we can start reading the next
861 # Ensure part is fully consumed so we can start reading the next
862 # part.
862 # part.
863 part.consume()
863 part.consume()
864
864
865 headerblock = self._readpartheader()
865 headerblock = self._readpartheader()
866 indebug(self.ui, 'end of bundle2 stream')
866 indebug(self.ui, 'end of bundle2 stream')
867
867
868 def _readpartheader(self):
868 def _readpartheader(self):
869 """reads a part header size and return the bytes blob
869 """reads a part header size and return the bytes blob
870
870
871 returns None if empty"""
871 returns None if empty"""
872 headersize = self._unpack(_fpartheadersize)[0]
872 headersize = self._unpack(_fpartheadersize)[0]
873 if headersize < 0:
873 if headersize < 0:
874 raise error.BundleValueError('negative part header size: %i'
874 raise error.BundleValueError('negative part header size: %i'
875 % headersize)
875 % headersize)
876 indebug(self.ui, 'part header size: %i' % headersize)
876 indebug(self.ui, 'part header size: %i' % headersize)
877 if headersize:
877 if headersize:
878 return self._readexact(headersize)
878 return self._readexact(headersize)
879 return None
879 return None
880
880
881 def compressed(self):
881 def compressed(self):
882 self.params # load params
882 self.params # load params
883 return self._compressed
883 return self._compressed
884
884
885 def close(self):
885 def close(self):
886 """close underlying file"""
886 """close underlying file"""
887 if util.safehasattr(self._fp, 'close'):
887 if util.safehasattr(self._fp, 'close'):
888 return self._fp.close()
888 return self._fp.close()
889
889
890 formatmap = {'20': unbundle20}
890 formatmap = {'20': unbundle20}
891
891
892 b2streamparamsmap = {}
892 b2streamparamsmap = {}
893
893
894 def b2streamparamhandler(name):
894 def b2streamparamhandler(name):
895 """register a handler for a stream level parameter"""
895 """register a handler for a stream level parameter"""
896 def decorator(func):
896 def decorator(func):
897 assert name not in formatmap
897 assert name not in formatmap
898 b2streamparamsmap[name] = func
898 b2streamparamsmap[name] = func
899 return func
899 return func
900 return decorator
900 return decorator
901
901
902 @b2streamparamhandler('compression')
902 @b2streamparamhandler('compression')
903 def processcompression(unbundler, param, value):
903 def processcompression(unbundler, param, value):
904 """read compression parameter and install payload decompression"""
904 """read compression parameter and install payload decompression"""
905 if value not in util.compengines.supportedbundletypes:
905 if value not in util.compengines.supportedbundletypes:
906 raise error.BundleUnknownFeatureError(params=(param,),
906 raise error.BundleUnknownFeatureError(params=(param,),
907 values=(value,))
907 values=(value,))
908 unbundler._compengine = util.compengines.forbundletype(value)
908 unbundler._compengine = util.compengines.forbundletype(value)
909 if value is not None:
909 if value is not None:
910 unbundler._compressed = True
910 unbundler._compressed = True
911
911
912 class bundlepart(object):
912 class bundlepart(object):
913 """A bundle2 part contains application level payload
913 """A bundle2 part contains application level payload
914
914
915 The part `type` is used to route the part to the application level
915 The part `type` is used to route the part to the application level
916 handler.
916 handler.
917
917
918 The part payload is contained in ``part.data``. It could be raw bytes or a
918 The part payload is contained in ``part.data``. It could be raw bytes or a
919 generator of byte chunks.
919 generator of byte chunks.
920
920
921 You can add parameters to the part using the ``addparam`` method.
921 You can add parameters to the part using the ``addparam`` method.
922 Parameters can be either mandatory (default) or advisory. Remote side
922 Parameters can be either mandatory (default) or advisory. Remote side
923 should be able to safely ignore the advisory ones.
923 should be able to safely ignore the advisory ones.
924
924
925 Both data and parameters cannot be modified after the generation has begun.
925 Both data and parameters cannot be modified after the generation has begun.
926 """
926 """
927
927
928 def __init__(self, parttype, mandatoryparams=(), advisoryparams=(),
928 def __init__(self, parttype, mandatoryparams=(), advisoryparams=(),
929 data='', mandatory=True):
929 data='', mandatory=True):
930 validateparttype(parttype)
930 validateparttype(parttype)
931 self.id = None
931 self.id = None
932 self.type = parttype
932 self.type = parttype
933 self._data = data
933 self._data = data
934 self._mandatoryparams = list(mandatoryparams)
934 self._mandatoryparams = list(mandatoryparams)
935 self._advisoryparams = list(advisoryparams)
935 self._advisoryparams = list(advisoryparams)
936 # checking for duplicated entries
936 # checking for duplicated entries
937 self._seenparams = set()
937 self._seenparams = set()
938 for pname, __ in self._mandatoryparams + self._advisoryparams:
938 for pname, __ in self._mandatoryparams + self._advisoryparams:
939 if pname in self._seenparams:
939 if pname in self._seenparams:
940 raise error.ProgrammingError('duplicated params: %s' % pname)
940 raise error.ProgrammingError('duplicated params: %s' % pname)
941 self._seenparams.add(pname)
941 self._seenparams.add(pname)
942 # status of the part's generation:
942 # status of the part's generation:
943 # - None: not started,
943 # - None: not started,
944 # - False: currently generated,
944 # - False: currently generated,
945 # - True: generation done.
945 # - True: generation done.
946 self._generated = None
946 self._generated = None
947 self.mandatory = mandatory
947 self.mandatory = mandatory
948
948
949 def __repr__(self):
949 def __repr__(self):
950 cls = "%s.%s" % (self.__class__.__module__, self.__class__.__name__)
950 cls = "%s.%s" % (self.__class__.__module__, self.__class__.__name__)
951 return ('<%s object at %x; id: %s; type: %s; mandatory: %s>'
951 return ('<%s object at %x; id: %s; type: %s; mandatory: %s>'
952 % (cls, id(self), self.id, self.type, self.mandatory))
952 % (cls, id(self), self.id, self.type, self.mandatory))
953
953
954 def copy(self):
954 def copy(self):
955 """return a copy of the part
955 """return a copy of the part
956
956
957 The new part have the very same content but no partid assigned yet.
957 The new part have the very same content but no partid assigned yet.
958 Parts with generated data cannot be copied."""
958 Parts with generated data cannot be copied."""
959 assert not util.safehasattr(self.data, 'next')
959 assert not util.safehasattr(self.data, 'next')
960 return self.__class__(self.type, self._mandatoryparams,
960 return self.__class__(self.type, self._mandatoryparams,
961 self._advisoryparams, self._data, self.mandatory)
961 self._advisoryparams, self._data, self.mandatory)
962
962
963 # methods used to defines the part content
963 # methods used to defines the part content
964 @property
964 @property
965 def data(self):
965 def data(self):
966 return self._data
966 return self._data
967
967
968 @data.setter
968 @data.setter
969 def data(self, data):
969 def data(self, data):
970 if self._generated is not None:
970 if self._generated is not None:
971 raise error.ReadOnlyPartError('part is being generated')
971 raise error.ReadOnlyPartError('part is being generated')
972 self._data = data
972 self._data = data
973
973
974 @property
974 @property
975 def mandatoryparams(self):
975 def mandatoryparams(self):
976 # make it an immutable tuple to force people through ``addparam``
976 # make it an immutable tuple to force people through ``addparam``
977 return tuple(self._mandatoryparams)
977 return tuple(self._mandatoryparams)
978
978
979 @property
979 @property
980 def advisoryparams(self):
980 def advisoryparams(self):
981 # make it an immutable tuple to force people through ``addparam``
981 # make it an immutable tuple to force people through ``addparam``
982 return tuple(self._advisoryparams)
982 return tuple(self._advisoryparams)
983
983
984 def addparam(self, name, value='', mandatory=True):
984 def addparam(self, name, value='', mandatory=True):
985 """add a parameter to the part
985 """add a parameter to the part
986
986
987 If 'mandatory' is set to True, the remote handler must claim support
987 If 'mandatory' is set to True, the remote handler must claim support
988 for this parameter or the unbundling will be aborted.
988 for this parameter or the unbundling will be aborted.
989
989
990 The 'name' and 'value' cannot exceed 255 bytes each.
990 The 'name' and 'value' cannot exceed 255 bytes each.
991 """
991 """
992 if self._generated is not None:
992 if self._generated is not None:
993 raise error.ReadOnlyPartError('part is being generated')
993 raise error.ReadOnlyPartError('part is being generated')
994 if name in self._seenparams:
994 if name in self._seenparams:
995 raise ValueError('duplicated params: %s' % name)
995 raise ValueError('duplicated params: %s' % name)
996 self._seenparams.add(name)
996 self._seenparams.add(name)
997 params = self._advisoryparams
997 params = self._advisoryparams
998 if mandatory:
998 if mandatory:
999 params = self._mandatoryparams
999 params = self._mandatoryparams
1000 params.append((name, value))
1000 params.append((name, value))
1001
1001
1002 # methods used to generates the bundle2 stream
1002 # methods used to generates the bundle2 stream
1003 def getchunks(self, ui):
1003 def getchunks(self, ui):
1004 if self._generated is not None:
1004 if self._generated is not None:
1005 raise error.ProgrammingError('part can only be consumed once')
1005 raise error.ProgrammingError('part can only be consumed once')
1006 self._generated = False
1006 self._generated = False
1007
1007
1008 if ui.debugflag:
1008 if ui.debugflag:
1009 msg = ['bundle2-output-part: "%s"' % self.type]
1009 msg = ['bundle2-output-part: "%s"' % self.type]
1010 if not self.mandatory:
1010 if not self.mandatory:
1011 msg.append(' (advisory)')
1011 msg.append(' (advisory)')
1012 nbmp = len(self.mandatoryparams)
1012 nbmp = len(self.mandatoryparams)
1013 nbap = len(self.advisoryparams)
1013 nbap = len(self.advisoryparams)
1014 if nbmp or nbap:
1014 if nbmp or nbap:
1015 msg.append(' (params:')
1015 msg.append(' (params:')
1016 if nbmp:
1016 if nbmp:
1017 msg.append(' %i mandatory' % nbmp)
1017 msg.append(' %i mandatory' % nbmp)
1018 if nbap:
1018 if nbap:
1019 msg.append(' %i advisory' % nbmp)
1019 msg.append(' %i advisory' % nbmp)
1020 msg.append(')')
1020 msg.append(')')
1021 if not self.data:
1021 if not self.data:
1022 msg.append(' empty payload')
1022 msg.append(' empty payload')
1023 elif (util.safehasattr(self.data, 'next')
1023 elif (util.safehasattr(self.data, 'next')
1024 or util.safehasattr(self.data, '__next__')):
1024 or util.safehasattr(self.data, '__next__')):
1025 msg.append(' streamed payload')
1025 msg.append(' streamed payload')
1026 else:
1026 else:
1027 msg.append(' %i bytes payload' % len(self.data))
1027 msg.append(' %i bytes payload' % len(self.data))
1028 msg.append('\n')
1028 msg.append('\n')
1029 ui.debug(''.join(msg))
1029 ui.debug(''.join(msg))
1030
1030
1031 #### header
1031 #### header
1032 if self.mandatory:
1032 if self.mandatory:
1033 parttype = self.type.upper()
1033 parttype = self.type.upper()
1034 else:
1034 else:
1035 parttype = self.type.lower()
1035 parttype = self.type.lower()
1036 outdebug(ui, 'part %s: "%s"' % (pycompat.bytestr(self.id), parttype))
1036 outdebug(ui, 'part %s: "%s"' % (pycompat.bytestr(self.id), parttype))
1037 ## parttype
1037 ## parttype
1038 header = [_pack(_fparttypesize, len(parttype)),
1038 header = [_pack(_fparttypesize, len(parttype)),
1039 parttype, _pack(_fpartid, self.id),
1039 parttype, _pack(_fpartid, self.id),
1040 ]
1040 ]
1041 ## parameters
1041 ## parameters
1042 # count
1042 # count
1043 manpar = self.mandatoryparams
1043 manpar = self.mandatoryparams
1044 advpar = self.advisoryparams
1044 advpar = self.advisoryparams
1045 header.append(_pack(_fpartparamcount, len(manpar), len(advpar)))
1045 header.append(_pack(_fpartparamcount, len(manpar), len(advpar)))
1046 # size
1046 # size
1047 parsizes = []
1047 parsizes = []
1048 for key, value in manpar:
1048 for key, value in manpar:
1049 parsizes.append(len(key))
1049 parsizes.append(len(key))
1050 parsizes.append(len(value))
1050 parsizes.append(len(value))
1051 for key, value in advpar:
1051 for key, value in advpar:
1052 parsizes.append(len(key))
1052 parsizes.append(len(key))
1053 parsizes.append(len(value))
1053 parsizes.append(len(value))
1054 paramsizes = _pack(_makefpartparamsizes(len(parsizes) // 2), *parsizes)
1054 paramsizes = _pack(_makefpartparamsizes(len(parsizes) // 2), *parsizes)
1055 header.append(paramsizes)
1055 header.append(paramsizes)
1056 # key, value
1056 # key, value
1057 for key, value in manpar:
1057 for key, value in manpar:
1058 header.append(key)
1058 header.append(key)
1059 header.append(value)
1059 header.append(value)
1060 for key, value in advpar:
1060 for key, value in advpar:
1061 header.append(key)
1061 header.append(key)
1062 header.append(value)
1062 header.append(value)
1063 ## finalize header
1063 ## finalize header
1064 try:
1064 try:
1065 headerchunk = ''.join(header)
1065 headerchunk = ''.join(header)
1066 except TypeError:
1066 except TypeError:
1067 raise TypeError(r'Found a non-bytes trying to '
1067 raise TypeError(r'Found a non-bytes trying to '
1068 r'build bundle part header: %r' % header)
1068 r'build bundle part header: %r' % header)
1069 outdebug(ui, 'header chunk size: %i' % len(headerchunk))
1069 outdebug(ui, 'header chunk size: %i' % len(headerchunk))
1070 yield _pack(_fpartheadersize, len(headerchunk))
1070 yield _pack(_fpartheadersize, len(headerchunk))
1071 yield headerchunk
1071 yield headerchunk
1072 ## payload
1072 ## payload
1073 try:
1073 try:
1074 for chunk in self._payloadchunks():
1074 for chunk in self._payloadchunks():
1075 outdebug(ui, 'payload chunk size: %i' % len(chunk))
1075 outdebug(ui, 'payload chunk size: %i' % len(chunk))
1076 yield _pack(_fpayloadsize, len(chunk))
1076 yield _pack(_fpayloadsize, len(chunk))
1077 yield chunk
1077 yield chunk
1078 except GeneratorExit:
1078 except GeneratorExit:
1079 # GeneratorExit means that nobody is listening for our
1079 # GeneratorExit means that nobody is listening for our
1080 # results anyway, so just bail quickly rather than trying
1080 # results anyway, so just bail quickly rather than trying
1081 # to produce an error part.
1081 # to produce an error part.
1082 ui.debug('bundle2-generatorexit\n')
1082 ui.debug('bundle2-generatorexit\n')
1083 raise
1083 raise
1084 except BaseException as exc:
1084 except BaseException as exc:
1085 bexc = util.forcebytestr(exc)
1085 bexc = util.forcebytestr(exc)
1086 # backup exception data for later
1086 # backup exception data for later
1087 ui.debug('bundle2-input-stream-interrupt: encoding exception %s'
1087 ui.debug('bundle2-input-stream-interrupt: encoding exception %s'
1088 % bexc)
1088 % bexc)
1089 tb = sys.exc_info()[2]
1089 tb = sys.exc_info()[2]
1090 msg = 'unexpected error: %s' % bexc
1090 msg = 'unexpected error: %s' % bexc
1091 interpart = bundlepart('error:abort', [('message', msg)],
1091 interpart = bundlepart('error:abort', [('message', msg)],
1092 mandatory=False)
1092 mandatory=False)
1093 interpart.id = 0
1093 interpart.id = 0
1094 yield _pack(_fpayloadsize, -1)
1094 yield _pack(_fpayloadsize, -1)
1095 for chunk in interpart.getchunks(ui=ui):
1095 for chunk in interpart.getchunks(ui=ui):
1096 yield chunk
1096 yield chunk
1097 outdebug(ui, 'closing payload chunk')
1097 outdebug(ui, 'closing payload chunk')
1098 # abort current part payload
1098 # abort current part payload
1099 yield _pack(_fpayloadsize, 0)
1099 yield _pack(_fpayloadsize, 0)
1100 pycompat.raisewithtb(exc, tb)
1100 pycompat.raisewithtb(exc, tb)
1101 # end of payload
1101 # end of payload
1102 outdebug(ui, 'closing payload chunk')
1102 outdebug(ui, 'closing payload chunk')
1103 yield _pack(_fpayloadsize, 0)
1103 yield _pack(_fpayloadsize, 0)
1104 self._generated = True
1104 self._generated = True
1105
1105
1106 def _payloadchunks(self):
1106 def _payloadchunks(self):
1107 """yield chunks of a the part payload
1107 """yield chunks of a the part payload
1108
1108
1109 Exists to handle the different methods to provide data to a part."""
1109 Exists to handle the different methods to provide data to a part."""
1110 # we only support fixed size data now.
1110 # we only support fixed size data now.
1111 # This will be improved in the future.
1111 # This will be improved in the future.
1112 if (util.safehasattr(self.data, 'next')
1112 if (util.safehasattr(self.data, 'next')
1113 or util.safehasattr(self.data, '__next__')):
1113 or util.safehasattr(self.data, '__next__')):
1114 buff = util.chunkbuffer(self.data)
1114 buff = util.chunkbuffer(self.data)
1115 chunk = buff.read(preferedchunksize)
1115 chunk = buff.read(preferedchunksize)
1116 while chunk:
1116 while chunk:
1117 yield chunk
1117 yield chunk
1118 chunk = buff.read(preferedchunksize)
1118 chunk = buff.read(preferedchunksize)
1119 elif len(self.data):
1119 elif len(self.data):
1120 yield self.data
1120 yield self.data
1121
1121
1122
1122
1123 flaginterrupt = -1
1123 flaginterrupt = -1
1124
1124
1125 class interrupthandler(unpackermixin):
1125 class interrupthandler(unpackermixin):
1126 """read one part and process it with restricted capability
1126 """read one part and process it with restricted capability
1127
1127
1128 This allows to transmit exception raised on the producer size during part
1128 This allows to transmit exception raised on the producer size during part
1129 iteration while the consumer is reading a part.
1129 iteration while the consumer is reading a part.
1130
1130
1131 Part processed in this manner only have access to a ui object,"""
1131 Part processed in this manner only have access to a ui object,"""
1132
1132
1133 def __init__(self, ui, fp):
1133 def __init__(self, ui, fp):
1134 super(interrupthandler, self).__init__(fp)
1134 super(interrupthandler, self).__init__(fp)
1135 self.ui = ui
1135 self.ui = ui
1136
1136
1137 def _readpartheader(self):
1137 def _readpartheader(self):
1138 """reads a part header size and return the bytes blob
1138 """reads a part header size and return the bytes blob
1139
1139
1140 returns None if empty"""
1140 returns None if empty"""
1141 headersize = self._unpack(_fpartheadersize)[0]
1141 headersize = self._unpack(_fpartheadersize)[0]
1142 if headersize < 0:
1142 if headersize < 0:
1143 raise error.BundleValueError('negative part header size: %i'
1143 raise error.BundleValueError('negative part header size: %i'
1144 % headersize)
1144 % headersize)
1145 indebug(self.ui, 'part header size: %i\n' % headersize)
1145 indebug(self.ui, 'part header size: %i\n' % headersize)
1146 if headersize:
1146 if headersize:
1147 return self._readexact(headersize)
1147 return self._readexact(headersize)
1148 return None
1148 return None
1149
1149
1150 def __call__(self):
1150 def __call__(self):
1151
1151
1152 self.ui.debug('bundle2-input-stream-interrupt:'
1152 self.ui.debug('bundle2-input-stream-interrupt:'
1153 ' opening out of band context\n')
1153 ' opening out of band context\n')
1154 indebug(self.ui, 'bundle2 stream interruption, looking for a part.')
1154 indebug(self.ui, 'bundle2 stream interruption, looking for a part.')
1155 headerblock = self._readpartheader()
1155 headerblock = self._readpartheader()
1156 if headerblock is None:
1156 if headerblock is None:
1157 indebug(self.ui, 'no part found during interruption.')
1157 indebug(self.ui, 'no part found during interruption.')
1158 return
1158 return
1159 part = unbundlepart(self.ui, headerblock, self._fp)
1159 part = unbundlepart(self.ui, headerblock, self._fp)
1160 op = interruptoperation(self.ui)
1160 op = interruptoperation(self.ui)
1161 hardabort = False
1161 hardabort = False
1162 try:
1162 try:
1163 _processpart(op, part)
1163 _processpart(op, part)
1164 except (SystemExit, KeyboardInterrupt):
1164 except (SystemExit, KeyboardInterrupt):
1165 hardabort = True
1165 hardabort = True
1166 raise
1166 raise
1167 finally:
1167 finally:
1168 if not hardabort:
1168 if not hardabort:
1169 part.consume()
1169 part.consume()
1170 self.ui.debug('bundle2-input-stream-interrupt:'
1170 self.ui.debug('bundle2-input-stream-interrupt:'
1171 ' closing out of band context\n')
1171 ' closing out of band context\n')
1172
1172
1173 class interruptoperation(object):
1173 class interruptoperation(object):
1174 """A limited operation to be use by part handler during interruption
1174 """A limited operation to be use by part handler during interruption
1175
1175
1176 It only have access to an ui object.
1176 It only have access to an ui object.
1177 """
1177 """
1178
1178
1179 def __init__(self, ui):
1179 def __init__(self, ui):
1180 self.ui = ui
1180 self.ui = ui
1181 self.reply = None
1181 self.reply = None
1182 self.captureoutput = False
1182 self.captureoutput = False
1183
1183
1184 @property
1184 @property
1185 def repo(self):
1185 def repo(self):
1186 raise error.ProgrammingError('no repo access from stream interruption')
1186 raise error.ProgrammingError('no repo access from stream interruption')
1187
1187
1188 def gettransaction(self):
1188 def gettransaction(self):
1189 raise TransactionUnavailable('no repo access from stream interruption')
1189 raise TransactionUnavailable('no repo access from stream interruption')
1190
1190
1191 def decodepayloadchunks(ui, fh):
1191 def decodepayloadchunks(ui, fh):
1192 """Reads bundle2 part payload data into chunks.
1192 """Reads bundle2 part payload data into chunks.
1193
1193
1194 Part payload data consists of framed chunks. This function takes
1194 Part payload data consists of framed chunks. This function takes
1195 a file handle and emits those chunks.
1195 a file handle and emits those chunks.
1196 """
1196 """
1197 dolog = ui.configbool('devel', 'bundle2.debug')
1197 dolog = ui.configbool('devel', 'bundle2.debug')
1198 debug = ui.debug
1198 debug = ui.debug
1199
1199
1200 headerstruct = struct.Struct(_fpayloadsize)
1200 headerstruct = struct.Struct(_fpayloadsize)
1201 headersize = headerstruct.size
1201 headersize = headerstruct.size
1202 unpack = headerstruct.unpack
1202 unpack = headerstruct.unpack
1203
1203
1204 readexactly = changegroup.readexactly
1204 readexactly = changegroup.readexactly
1205 read = fh.read
1205 read = fh.read
1206
1206
1207 chunksize = unpack(readexactly(fh, headersize))[0]
1207 chunksize = unpack(readexactly(fh, headersize))[0]
1208 indebug(ui, 'payload chunk size: %i' % chunksize)
1208 indebug(ui, 'payload chunk size: %i' % chunksize)
1209
1209
1210 # changegroup.readexactly() is inlined below for performance.
1210 # changegroup.readexactly() is inlined below for performance.
1211 while chunksize:
1211 while chunksize:
1212 if chunksize >= 0:
1212 if chunksize >= 0:
1213 s = read(chunksize)
1213 s = read(chunksize)
1214 if len(s) < chunksize:
1214 if len(s) < chunksize:
1215 raise error.Abort(_('stream ended unexpectedly '
1215 raise error.Abort(_('stream ended unexpectedly '
1216 ' (got %d bytes, expected %d)') %
1216 ' (got %d bytes, expected %d)') %
1217 (len(s), chunksize))
1217 (len(s), chunksize))
1218
1218
1219 yield s
1219 yield s
1220 elif chunksize == flaginterrupt:
1220 elif chunksize == flaginterrupt:
1221 # Interrupt "signal" detected. The regular stream is interrupted
1221 # Interrupt "signal" detected. The regular stream is interrupted
1222 # and a bundle2 part follows. Consume it.
1222 # and a bundle2 part follows. Consume it.
1223 interrupthandler(ui, fh)()
1223 interrupthandler(ui, fh)()
1224 else:
1224 else:
1225 raise error.BundleValueError(
1225 raise error.BundleValueError(
1226 'negative payload chunk size: %s' % chunksize)
1226 'negative payload chunk size: %s' % chunksize)
1227
1227
1228 s = read(headersize)
1228 s = read(headersize)
1229 if len(s) < headersize:
1229 if len(s) < headersize:
1230 raise error.Abort(_('stream ended unexpectedly '
1230 raise error.Abort(_('stream ended unexpectedly '
1231 ' (got %d bytes, expected %d)') %
1231 ' (got %d bytes, expected %d)') %
1232 (len(s), chunksize))
1232 (len(s), chunksize))
1233
1233
1234 chunksize = unpack(s)[0]
1234 chunksize = unpack(s)[0]
1235
1235
1236 # indebug() inlined for performance.
1236 # indebug() inlined for performance.
1237 if dolog:
1237 if dolog:
1238 debug('bundle2-input: payload chunk size: %i\n' % chunksize)
1238 debug('bundle2-input: payload chunk size: %i\n' % chunksize)
1239
1239
1240 class unbundlepart(unpackermixin):
1240 class unbundlepart(unpackermixin):
1241 """a bundle part read from a bundle"""
1241 """a bundle part read from a bundle"""
1242
1242
1243 def __init__(self, ui, header, fp):
1243 def __init__(self, ui, header, fp):
1244 super(unbundlepart, self).__init__(fp)
1244 super(unbundlepart, self).__init__(fp)
1245 self._seekable = (util.safehasattr(fp, 'seek') and
1245 self._seekable = (util.safehasattr(fp, 'seek') and
1246 util.safehasattr(fp, 'tell'))
1246 util.safehasattr(fp, 'tell'))
1247 self.ui = ui
1247 self.ui = ui
1248 # unbundle state attr
1248 # unbundle state attr
1249 self._headerdata = header
1249 self._headerdata = header
1250 self._headeroffset = 0
1250 self._headeroffset = 0
1251 self._initialized = False
1251 self._initialized = False
1252 self.consumed = False
1252 self.consumed = False
1253 # part data
1253 # part data
1254 self.id = None
1254 self.id = None
1255 self.type = None
1255 self.type = None
1256 self.mandatoryparams = None
1256 self.mandatoryparams = None
1257 self.advisoryparams = None
1257 self.advisoryparams = None
1258 self.params = None
1258 self.params = None
1259 self.mandatorykeys = ()
1259 self.mandatorykeys = ()
1260 self._readheader()
1260 self._readheader()
1261 self._mandatory = None
1261 self._mandatory = None
1262 self._pos = 0
1262 self._pos = 0
1263
1263
1264 def _fromheader(self, size):
1264 def _fromheader(self, size):
1265 """return the next <size> byte from the header"""
1265 """return the next <size> byte from the header"""
1266 offset = self._headeroffset
1266 offset = self._headeroffset
1267 data = self._headerdata[offset:(offset + size)]
1267 data = self._headerdata[offset:(offset + size)]
1268 self._headeroffset = offset + size
1268 self._headeroffset = offset + size
1269 return data
1269 return data
1270
1270
1271 def _unpackheader(self, format):
1271 def _unpackheader(self, format):
1272 """read given format from header
1272 """read given format from header
1273
1273
1274 This automatically compute the size of the format to read."""
1274 This automatically compute the size of the format to read."""
1275 data = self._fromheader(struct.calcsize(format))
1275 data = self._fromheader(struct.calcsize(format))
1276 return _unpack(format, data)
1276 return _unpack(format, data)
1277
1277
1278 def _initparams(self, mandatoryparams, advisoryparams):
1278 def _initparams(self, mandatoryparams, advisoryparams):
1279 """internal function to setup all logic related parameters"""
1279 """internal function to setup all logic related parameters"""
1280 # make it read only to prevent people touching it by mistake.
1280 # make it read only to prevent people touching it by mistake.
1281 self.mandatoryparams = tuple(mandatoryparams)
1281 self.mandatoryparams = tuple(mandatoryparams)
1282 self.advisoryparams = tuple(advisoryparams)
1282 self.advisoryparams = tuple(advisoryparams)
1283 # user friendly UI
1283 # user friendly UI
1284 self.params = util.sortdict(self.mandatoryparams)
1284 self.params = util.sortdict(self.mandatoryparams)
1285 self.params.update(self.advisoryparams)
1285 self.params.update(self.advisoryparams)
1286 self.mandatorykeys = frozenset(p[0] for p in mandatoryparams)
1286 self.mandatorykeys = frozenset(p[0] for p in mandatoryparams)
1287
1287
1288 def _readheader(self):
1288 def _readheader(self):
1289 """read the header and setup the object"""
1289 """read the header and setup the object"""
1290 typesize = self._unpackheader(_fparttypesize)[0]
1290 typesize = self._unpackheader(_fparttypesize)[0]
1291 self.type = self._fromheader(typesize)
1291 self.type = self._fromheader(typesize)
1292 indebug(self.ui, 'part type: "%s"' % self.type)
1292 indebug(self.ui, 'part type: "%s"' % self.type)
1293 self.id = self._unpackheader(_fpartid)[0]
1293 self.id = self._unpackheader(_fpartid)[0]
1294 indebug(self.ui, 'part id: "%s"' % pycompat.bytestr(self.id))
1294 indebug(self.ui, 'part id: "%s"' % pycompat.bytestr(self.id))
1295 # extract mandatory bit from type
1295 # extract mandatory bit from type
1296 self.mandatory = (self.type != self.type.lower())
1296 self.mandatory = (self.type != self.type.lower())
1297 self.type = self.type.lower()
1297 self.type = self.type.lower()
1298 ## reading parameters
1298 ## reading parameters
1299 # param count
1299 # param count
1300 mancount, advcount = self._unpackheader(_fpartparamcount)
1300 mancount, advcount = self._unpackheader(_fpartparamcount)
1301 indebug(self.ui, 'part parameters: %i' % (mancount + advcount))
1301 indebug(self.ui, 'part parameters: %i' % (mancount + advcount))
1302 # param size
1302 # param size
1303 fparamsizes = _makefpartparamsizes(mancount + advcount)
1303 fparamsizes = _makefpartparamsizes(mancount + advcount)
1304 paramsizes = self._unpackheader(fparamsizes)
1304 paramsizes = self._unpackheader(fparamsizes)
1305 # make it a list of couple again
1305 # make it a list of couple again
1306 paramsizes = list(zip(paramsizes[::2], paramsizes[1::2]))
1306 paramsizes = list(zip(paramsizes[::2], paramsizes[1::2]))
1307 # split mandatory from advisory
1307 # split mandatory from advisory
1308 mansizes = paramsizes[:mancount]
1308 mansizes = paramsizes[:mancount]
1309 advsizes = paramsizes[mancount:]
1309 advsizes = paramsizes[mancount:]
1310 # retrieve param value
1310 # retrieve param value
1311 manparams = []
1311 manparams = []
1312 for key, value in mansizes:
1312 for key, value in mansizes:
1313 manparams.append((self._fromheader(key), self._fromheader(value)))
1313 manparams.append((self._fromheader(key), self._fromheader(value)))
1314 advparams = []
1314 advparams = []
1315 for key, value in advsizes:
1315 for key, value in advsizes:
1316 advparams.append((self._fromheader(key), self._fromheader(value)))
1316 advparams.append((self._fromheader(key), self._fromheader(value)))
1317 self._initparams(manparams, advparams)
1317 self._initparams(manparams, advparams)
1318 ## part payload
1318 ## part payload
1319 self._payloadstream = util.chunkbuffer(self._payloadchunks())
1319 self._payloadstream = util.chunkbuffer(self._payloadchunks())
1320 # we read the data, tell it
1320 # we read the data, tell it
1321 self._initialized = True
1321 self._initialized = True
1322
1322
1323 def _payloadchunks(self):
1323 def _payloadchunks(self):
1324 """Generator of decoded chunks in the payload."""
1324 """Generator of decoded chunks in the payload."""
1325 return decodepayloadchunks(self.ui, self._fp)
1325 return decodepayloadchunks(self.ui, self._fp)
1326
1326
1327 def consume(self):
1327 def consume(self):
1328 """Read the part payload until completion.
1328 """Read the part payload until completion.
1329
1329
1330 By consuming the part data, the underlying stream read offset will
1330 By consuming the part data, the underlying stream read offset will
1331 be advanced to the next part (or end of stream).
1331 be advanced to the next part (or end of stream).
1332 """
1332 """
1333 if self.consumed:
1333 if self.consumed:
1334 return
1334 return
1335
1335
1336 chunk = self.read(32768)
1336 chunk = self.read(32768)
1337 while chunk:
1337 while chunk:
1338 self._pos += len(chunk)
1338 self._pos += len(chunk)
1339 chunk = self.read(32768)
1339 chunk = self.read(32768)
1340
1340
1341 def read(self, size=None):
1341 def read(self, size=None):
1342 """read payload data"""
1342 """read payload data"""
1343 if not self._initialized:
1343 if not self._initialized:
1344 self._readheader()
1344 self._readheader()
1345 if size is None:
1345 if size is None:
1346 data = self._payloadstream.read()
1346 data = self._payloadstream.read()
1347 else:
1347 else:
1348 data = self._payloadstream.read(size)
1348 data = self._payloadstream.read(size)
1349 self._pos += len(data)
1349 self._pos += len(data)
1350 if size is None or len(data) < size:
1350 if size is None or len(data) < size:
1351 if not self.consumed and self._pos:
1351 if not self.consumed and self._pos:
1352 self.ui.debug('bundle2-input-part: total payload size %i\n'
1352 self.ui.debug('bundle2-input-part: total payload size %i\n'
1353 % self._pos)
1353 % self._pos)
1354 self.consumed = True
1354 self.consumed = True
1355 return data
1355 return data
1356
1356
1357 class seekableunbundlepart(unbundlepart):
1357 class seekableunbundlepart(unbundlepart):
1358 """A bundle2 part in a bundle that is seekable.
1358 """A bundle2 part in a bundle that is seekable.
1359
1359
1360 Regular ``unbundlepart`` instances can only be read once. This class
1360 Regular ``unbundlepart`` instances can only be read once. This class
1361 extends ``unbundlepart`` to enable bi-directional seeking within the
1361 extends ``unbundlepart`` to enable bi-directional seeking within the
1362 part.
1362 part.
1363
1363
1364 Bundle2 part data consists of framed chunks. Offsets when seeking
1364 Bundle2 part data consists of framed chunks. Offsets when seeking
1365 refer to the decoded data, not the offsets in the underlying bundle2
1365 refer to the decoded data, not the offsets in the underlying bundle2
1366 stream.
1366 stream.
1367
1367
1368 To facilitate quickly seeking within the decoded data, instances of this
1368 To facilitate quickly seeking within the decoded data, instances of this
1369 class maintain a mapping between offsets in the underlying stream and
1369 class maintain a mapping between offsets in the underlying stream and
1370 the decoded payload. This mapping will consume memory in proportion
1370 the decoded payload. This mapping will consume memory in proportion
1371 to the number of chunks within the payload (which almost certainly
1371 to the number of chunks within the payload (which almost certainly
1372 increases in proportion with the size of the part).
1372 increases in proportion with the size of the part).
1373 """
1373 """
1374 def __init__(self, ui, header, fp):
1374 def __init__(self, ui, header, fp):
1375 # (payload, file) offsets for chunk starts.
1375 # (payload, file) offsets for chunk starts.
1376 self._chunkindex = []
1376 self._chunkindex = []
1377
1377
1378 super(seekableunbundlepart, self).__init__(ui, header, fp)
1378 super(seekableunbundlepart, self).__init__(ui, header, fp)
1379
1379
1380 def _payloadchunks(self, chunknum=0):
1380 def _payloadchunks(self, chunknum=0):
1381 '''seek to specified chunk and start yielding data'''
1381 '''seek to specified chunk and start yielding data'''
1382 if len(self._chunkindex) == 0:
1382 if len(self._chunkindex) == 0:
1383 assert chunknum == 0, 'Must start with chunk 0'
1383 assert chunknum == 0, 'Must start with chunk 0'
1384 self._chunkindex.append((0, self._tellfp()))
1384 self._chunkindex.append((0, self._tellfp()))
1385 else:
1385 else:
1386 assert chunknum < len(self._chunkindex), \
1386 assert chunknum < len(self._chunkindex), \
1387 'Unknown chunk %d' % chunknum
1387 'Unknown chunk %d' % chunknum
1388 self._seekfp(self._chunkindex[chunknum][1])
1388 self._seekfp(self._chunkindex[chunknum][1])
1389
1389
1390 pos = self._chunkindex[chunknum][0]
1390 pos = self._chunkindex[chunknum][0]
1391
1391
1392 for chunk in decodepayloadchunks(self.ui, self._fp):
1392 for chunk in decodepayloadchunks(self.ui, self._fp):
1393 chunknum += 1
1393 chunknum += 1
1394 pos += len(chunk)
1394 pos += len(chunk)
1395 if chunknum == len(self._chunkindex):
1395 if chunknum == len(self._chunkindex):
1396 self._chunkindex.append((pos, self._tellfp()))
1396 self._chunkindex.append((pos, self._tellfp()))
1397
1397
1398 yield chunk
1398 yield chunk
1399
1399
1400 def _findchunk(self, pos):
1400 def _findchunk(self, pos):
1401 '''for a given payload position, return a chunk number and offset'''
1401 '''for a given payload position, return a chunk number and offset'''
1402 for chunk, (ppos, fpos) in enumerate(self._chunkindex):
1402 for chunk, (ppos, fpos) in enumerate(self._chunkindex):
1403 if ppos == pos:
1403 if ppos == pos:
1404 return chunk, 0
1404 return chunk, 0
1405 elif ppos > pos:
1405 elif ppos > pos:
1406 return chunk - 1, pos - self._chunkindex[chunk - 1][0]
1406 return chunk - 1, pos - self._chunkindex[chunk - 1][0]
1407 raise ValueError('Unknown chunk')
1407 raise ValueError('Unknown chunk')
1408
1408
1409 def tell(self):
1409 def tell(self):
1410 return self._pos
1410 return self._pos
1411
1411
1412 def seek(self, offset, whence=os.SEEK_SET):
1412 def seek(self, offset, whence=os.SEEK_SET):
1413 if whence == os.SEEK_SET:
1413 if whence == os.SEEK_SET:
1414 newpos = offset
1414 newpos = offset
1415 elif whence == os.SEEK_CUR:
1415 elif whence == os.SEEK_CUR:
1416 newpos = self._pos + offset
1416 newpos = self._pos + offset
1417 elif whence == os.SEEK_END:
1417 elif whence == os.SEEK_END:
1418 if not self.consumed:
1418 if not self.consumed:
1419 # Can't use self.consume() here because it advances self._pos.
1419 # Can't use self.consume() here because it advances self._pos.
1420 chunk = self.read(32768)
1420 chunk = self.read(32768)
1421 while chunk:
1421 while chunk:
1422 chunk = self.read(32768)
1422 chunk = self.read(32768)
1423 newpos = self._chunkindex[-1][0] - offset
1423 newpos = self._chunkindex[-1][0] - offset
1424 else:
1424 else:
1425 raise ValueError('Unknown whence value: %r' % (whence,))
1425 raise ValueError('Unknown whence value: %r' % (whence,))
1426
1426
1427 if newpos > self._chunkindex[-1][0] and not self.consumed:
1427 if newpos > self._chunkindex[-1][0] and not self.consumed:
1428 # Can't use self.consume() here because it advances self._pos.
1428 # Can't use self.consume() here because it advances self._pos.
1429 chunk = self.read(32768)
1429 chunk = self.read(32768)
1430 while chunk:
1430 while chunk:
1431 chunk = self.read(32668)
1431 chunk = self.read(32668)
1432
1432
1433 if not 0 <= newpos <= self._chunkindex[-1][0]:
1433 if not 0 <= newpos <= self._chunkindex[-1][0]:
1434 raise ValueError('Offset out of range')
1434 raise ValueError('Offset out of range')
1435
1435
1436 if self._pos != newpos:
1436 if self._pos != newpos:
1437 chunk, internaloffset = self._findchunk(newpos)
1437 chunk, internaloffset = self._findchunk(newpos)
1438 self._payloadstream = util.chunkbuffer(self._payloadchunks(chunk))
1438 self._payloadstream = util.chunkbuffer(self._payloadchunks(chunk))
1439 adjust = self.read(internaloffset)
1439 adjust = self.read(internaloffset)
1440 if len(adjust) != internaloffset:
1440 if len(adjust) != internaloffset:
1441 raise error.Abort(_('Seek failed\n'))
1441 raise error.Abort(_('Seek failed\n'))
1442 self._pos = newpos
1442 self._pos = newpos
1443
1443
1444 def _seekfp(self, offset, whence=0):
1444 def _seekfp(self, offset, whence=0):
1445 """move the underlying file pointer
1445 """move the underlying file pointer
1446
1446
1447 This method is meant for internal usage by the bundle2 protocol only.
1447 This method is meant for internal usage by the bundle2 protocol only.
1448 They directly manipulate the low level stream including bundle2 level
1448 They directly manipulate the low level stream including bundle2 level
1449 instruction.
1449 instruction.
1450
1450
1451 Do not use it to implement higher-level logic or methods."""
1451 Do not use it to implement higher-level logic or methods."""
1452 if self._seekable:
1452 if self._seekable:
1453 return self._fp.seek(offset, whence)
1453 return self._fp.seek(offset, whence)
1454 else:
1454 else:
1455 raise NotImplementedError(_('File pointer is not seekable'))
1455 raise NotImplementedError(_('File pointer is not seekable'))
1456
1456
1457 def _tellfp(self):
1457 def _tellfp(self):
1458 """return the file offset, or None if file is not seekable
1458 """return the file offset, or None if file is not seekable
1459
1459
1460 This method is meant for internal usage by the bundle2 protocol only.
1460 This method is meant for internal usage by the bundle2 protocol only.
1461 They directly manipulate the low level stream including bundle2 level
1461 They directly manipulate the low level stream including bundle2 level
1462 instruction.
1462 instruction.
1463
1463
1464 Do not use it to implement higher-level logic or methods."""
1464 Do not use it to implement higher-level logic or methods."""
1465 if self._seekable:
1465 if self._seekable:
1466 try:
1466 try:
1467 return self._fp.tell()
1467 return self._fp.tell()
1468 except IOError as e:
1468 except IOError as e:
1469 if e.errno == errno.ESPIPE:
1469 if e.errno == errno.ESPIPE:
1470 self._seekable = False
1470 self._seekable = False
1471 else:
1471 else:
1472 raise
1472 raise
1473 return None
1473 return None
1474
1474
1475 # These are only the static capabilities.
1475 # These are only the static capabilities.
1476 # Check the 'getrepocaps' function for the rest.
1476 # Check the 'getrepocaps' function for the rest.
1477 capabilities = {'HG20': (),
1477 capabilities = {'HG20': (),
1478 'bookmarks': (),
1478 'error': ('abort', 'unsupportedcontent', 'pushraced',
1479 'error': ('abort', 'unsupportedcontent', 'pushraced',
1479 'pushkey'),
1480 'pushkey'),
1480 'listkeys': (),
1481 'listkeys': (),
1481 'pushkey': (),
1482 'pushkey': (),
1482 'digests': tuple(sorted(util.DIGESTS.keys())),
1483 'digests': tuple(sorted(util.DIGESTS.keys())),
1483 'remote-changegroup': ('http', 'https'),
1484 'remote-changegroup': ('http', 'https'),
1484 'hgtagsfnodes': (),
1485 'hgtagsfnodes': (),
1485 'phases': ('heads',),
1486 'phases': ('heads',),
1486 }
1487 }
1487
1488
1488 def getrepocaps(repo, allowpushback=False):
1489 def getrepocaps(repo, allowpushback=False):
1489 """return the bundle2 capabilities for a given repo
1490 """return the bundle2 capabilities for a given repo
1490
1491
1491 Exists to allow extensions (like evolution) to mutate the capabilities.
1492 Exists to allow extensions (like evolution) to mutate the capabilities.
1492 """
1493 """
1493 caps = capabilities.copy()
1494 caps = capabilities.copy()
1494 caps['changegroup'] = tuple(sorted(
1495 caps['changegroup'] = tuple(sorted(
1495 changegroup.supportedincomingversions(repo)))
1496 changegroup.supportedincomingversions(repo)))
1496 if obsolete.isenabled(repo, obsolete.exchangeopt):
1497 if obsolete.isenabled(repo, obsolete.exchangeopt):
1497 supportedformat = tuple('V%i' % v for v in obsolete.formats)
1498 supportedformat = tuple('V%i' % v for v in obsolete.formats)
1498 caps['obsmarkers'] = supportedformat
1499 caps['obsmarkers'] = supportedformat
1499 if allowpushback:
1500 if allowpushback:
1500 caps['pushback'] = ()
1501 caps['pushback'] = ()
1501 cpmode = repo.ui.config('server', 'concurrent-push-mode')
1502 cpmode = repo.ui.config('server', 'concurrent-push-mode')
1502 if cpmode == 'check-related':
1503 if cpmode == 'check-related':
1503 caps['checkheads'] = ('related',)
1504 caps['checkheads'] = ('related',)
1504 if 'phases' in repo.ui.configlist('devel', 'legacy.exchange'):
1505 if 'phases' in repo.ui.configlist('devel', 'legacy.exchange'):
1505 caps.pop('phases')
1506 caps.pop('phases')
1506 return caps
1507 return caps
1507
1508
1508 def bundle2caps(remote):
1509 def bundle2caps(remote):
1509 """return the bundle capabilities of a peer as dict"""
1510 """return the bundle capabilities of a peer as dict"""
1510 raw = remote.capable('bundle2')
1511 raw = remote.capable('bundle2')
1511 if not raw and raw != '':
1512 if not raw and raw != '':
1512 return {}
1513 return {}
1513 capsblob = urlreq.unquote(remote.capable('bundle2'))
1514 capsblob = urlreq.unquote(remote.capable('bundle2'))
1514 return decodecaps(capsblob)
1515 return decodecaps(capsblob)
1515
1516
1516 def obsmarkersversion(caps):
1517 def obsmarkersversion(caps):
1517 """extract the list of supported obsmarkers versions from a bundle2caps dict
1518 """extract the list of supported obsmarkers versions from a bundle2caps dict
1518 """
1519 """
1519 obscaps = caps.get('obsmarkers', ())
1520 obscaps = caps.get('obsmarkers', ())
1520 return [int(c[1:]) for c in obscaps if c.startswith('V')]
1521 return [int(c[1:]) for c in obscaps if c.startswith('V')]
1521
1522
1522 def writenewbundle(ui, repo, source, filename, bundletype, outgoing, opts,
1523 def writenewbundle(ui, repo, source, filename, bundletype, outgoing, opts,
1523 vfs=None, compression=None, compopts=None):
1524 vfs=None, compression=None, compopts=None):
1524 if bundletype.startswith('HG10'):
1525 if bundletype.startswith('HG10'):
1525 cg = changegroup.makechangegroup(repo, outgoing, '01', source)
1526 cg = changegroup.makechangegroup(repo, outgoing, '01', source)
1526 return writebundle(ui, cg, filename, bundletype, vfs=vfs,
1527 return writebundle(ui, cg, filename, bundletype, vfs=vfs,
1527 compression=compression, compopts=compopts)
1528 compression=compression, compopts=compopts)
1528 elif not bundletype.startswith('HG20'):
1529 elif not bundletype.startswith('HG20'):
1529 raise error.ProgrammingError('unknown bundle type: %s' % bundletype)
1530 raise error.ProgrammingError('unknown bundle type: %s' % bundletype)
1530
1531
1531 caps = {}
1532 caps = {}
1532 if 'obsolescence' in opts:
1533 if 'obsolescence' in opts:
1533 caps['obsmarkers'] = ('V1',)
1534 caps['obsmarkers'] = ('V1',)
1534 bundle = bundle20(ui, caps)
1535 bundle = bundle20(ui, caps)
1535 bundle.setcompression(compression, compopts)
1536 bundle.setcompression(compression, compopts)
1536 _addpartsfromopts(ui, repo, bundle, source, outgoing, opts)
1537 _addpartsfromopts(ui, repo, bundle, source, outgoing, opts)
1537 chunkiter = bundle.getchunks()
1538 chunkiter = bundle.getchunks()
1538
1539
1539 return changegroup.writechunks(ui, chunkiter, filename, vfs=vfs)
1540 return changegroup.writechunks(ui, chunkiter, filename, vfs=vfs)
1540
1541
1541 def _addpartsfromopts(ui, repo, bundler, source, outgoing, opts):
1542 def _addpartsfromopts(ui, repo, bundler, source, outgoing, opts):
1542 # We should eventually reconcile this logic with the one behind
1543 # We should eventually reconcile this logic with the one behind
1543 # 'exchange.getbundle2partsgenerator'.
1544 # 'exchange.getbundle2partsgenerator'.
1544 #
1545 #
1545 # The type of input from 'getbundle' and 'writenewbundle' are a bit
1546 # The type of input from 'getbundle' and 'writenewbundle' are a bit
1546 # different right now. So we keep them separated for now for the sake of
1547 # different right now. So we keep them separated for now for the sake of
1547 # simplicity.
1548 # simplicity.
1548
1549
1549 # we always want a changegroup in such bundle
1550 # we always want a changegroup in such bundle
1550 cgversion = opts.get('cg.version')
1551 cgversion = opts.get('cg.version')
1551 if cgversion is None:
1552 if cgversion is None:
1552 cgversion = changegroup.safeversion(repo)
1553 cgversion = changegroup.safeversion(repo)
1553 cg = changegroup.makechangegroup(repo, outgoing, cgversion, source)
1554 cg = changegroup.makechangegroup(repo, outgoing, cgversion, source)
1554 part = bundler.newpart('changegroup', data=cg.getchunks())
1555 part = bundler.newpart('changegroup', data=cg.getchunks())
1555 part.addparam('version', cg.version)
1556 part.addparam('version', cg.version)
1556 if 'clcount' in cg.extras:
1557 if 'clcount' in cg.extras:
1557 part.addparam('nbchanges', '%d' % cg.extras['clcount'],
1558 part.addparam('nbchanges', '%d' % cg.extras['clcount'],
1558 mandatory=False)
1559 mandatory=False)
1559 if opts.get('phases') and repo.revs('%ln and secret()',
1560 if opts.get('phases') and repo.revs('%ln and secret()',
1560 outgoing.missingheads):
1561 outgoing.missingheads):
1561 part.addparam('targetphase', '%d' % phases.secret, mandatory=False)
1562 part.addparam('targetphase', '%d' % phases.secret, mandatory=False)
1562
1563
1563 addparttagsfnodescache(repo, bundler, outgoing)
1564 addparttagsfnodescache(repo, bundler, outgoing)
1564
1565
1565 if opts.get('obsolescence', False):
1566 if opts.get('obsolescence', False):
1566 obsmarkers = repo.obsstore.relevantmarkers(outgoing.missing)
1567 obsmarkers = repo.obsstore.relevantmarkers(outgoing.missing)
1567 buildobsmarkerspart(bundler, obsmarkers)
1568 buildobsmarkerspart(bundler, obsmarkers)
1568
1569
1569 if opts.get('phases', False):
1570 if opts.get('phases', False):
1570 headsbyphase = phases.subsetphaseheads(repo, outgoing.missing)
1571 headsbyphase = phases.subsetphaseheads(repo, outgoing.missing)
1571 phasedata = phases.binaryencode(headsbyphase)
1572 phasedata = phases.binaryencode(headsbyphase)
1572 bundler.newpart('phase-heads', data=phasedata)
1573 bundler.newpart('phase-heads', data=phasedata)
1573
1574
1574 def addparttagsfnodescache(repo, bundler, outgoing):
1575 def addparttagsfnodescache(repo, bundler, outgoing):
1575 # we include the tags fnode cache for the bundle changeset
1576 # we include the tags fnode cache for the bundle changeset
1576 # (as an optional parts)
1577 # (as an optional parts)
1577 cache = tags.hgtagsfnodescache(repo.unfiltered())
1578 cache = tags.hgtagsfnodescache(repo.unfiltered())
1578 chunks = []
1579 chunks = []
1579
1580
1580 # .hgtags fnodes are only relevant for head changesets. While we could
1581 # .hgtags fnodes are only relevant for head changesets. While we could
1581 # transfer values for all known nodes, there will likely be little to
1582 # transfer values for all known nodes, there will likely be little to
1582 # no benefit.
1583 # no benefit.
1583 #
1584 #
1584 # We don't bother using a generator to produce output data because
1585 # We don't bother using a generator to produce output data because
1585 # a) we only have 40 bytes per head and even esoteric numbers of heads
1586 # a) we only have 40 bytes per head and even esoteric numbers of heads
1586 # consume little memory (1M heads is 40MB) b) we don't want to send the
1587 # consume little memory (1M heads is 40MB) b) we don't want to send the
1587 # part if we don't have entries and knowing if we have entries requires
1588 # part if we don't have entries and knowing if we have entries requires
1588 # cache lookups.
1589 # cache lookups.
1589 for node in outgoing.missingheads:
1590 for node in outgoing.missingheads:
1590 # Don't compute missing, as this may slow down serving.
1591 # Don't compute missing, as this may slow down serving.
1591 fnode = cache.getfnode(node, computemissing=False)
1592 fnode = cache.getfnode(node, computemissing=False)
1592 if fnode is not None:
1593 if fnode is not None:
1593 chunks.extend([node, fnode])
1594 chunks.extend([node, fnode])
1594
1595
1595 if chunks:
1596 if chunks:
1596 bundler.newpart('hgtagsfnodes', data=''.join(chunks))
1597 bundler.newpart('hgtagsfnodes', data=''.join(chunks))
1597
1598
1598 def buildobsmarkerspart(bundler, markers):
1599 def buildobsmarkerspart(bundler, markers):
1599 """add an obsmarker part to the bundler with <markers>
1600 """add an obsmarker part to the bundler with <markers>
1600
1601
1601 No part is created if markers is empty.
1602 No part is created if markers is empty.
1602 Raises ValueError if the bundler doesn't support any known obsmarker format.
1603 Raises ValueError if the bundler doesn't support any known obsmarker format.
1603 """
1604 """
1604 if not markers:
1605 if not markers:
1605 return None
1606 return None
1606
1607
1607 remoteversions = obsmarkersversion(bundler.capabilities)
1608 remoteversions = obsmarkersversion(bundler.capabilities)
1608 version = obsolete.commonversion(remoteversions)
1609 version = obsolete.commonversion(remoteversions)
1609 if version is None:
1610 if version is None:
1610 raise ValueError('bundler does not support common obsmarker format')
1611 raise ValueError('bundler does not support common obsmarker format')
1611 stream = obsolete.encodemarkers(markers, True, version=version)
1612 stream = obsolete.encodemarkers(markers, True, version=version)
1612 return bundler.newpart('obsmarkers', data=stream)
1613 return bundler.newpart('obsmarkers', data=stream)
1613
1614
1614 def writebundle(ui, cg, filename, bundletype, vfs=None, compression=None,
1615 def writebundle(ui, cg, filename, bundletype, vfs=None, compression=None,
1615 compopts=None):
1616 compopts=None):
1616 """Write a bundle file and return its filename.
1617 """Write a bundle file and return its filename.
1617
1618
1618 Existing files will not be overwritten.
1619 Existing files will not be overwritten.
1619 If no filename is specified, a temporary file is created.
1620 If no filename is specified, a temporary file is created.
1620 bz2 compression can be turned off.
1621 bz2 compression can be turned off.
1621 The bundle file will be deleted in case of errors.
1622 The bundle file will be deleted in case of errors.
1622 """
1623 """
1623
1624
1624 if bundletype == "HG20":
1625 if bundletype == "HG20":
1625 bundle = bundle20(ui)
1626 bundle = bundle20(ui)
1626 bundle.setcompression(compression, compopts)
1627 bundle.setcompression(compression, compopts)
1627 part = bundle.newpart('changegroup', data=cg.getchunks())
1628 part = bundle.newpart('changegroup', data=cg.getchunks())
1628 part.addparam('version', cg.version)
1629 part.addparam('version', cg.version)
1629 if 'clcount' in cg.extras:
1630 if 'clcount' in cg.extras:
1630 part.addparam('nbchanges', '%d' % cg.extras['clcount'],
1631 part.addparam('nbchanges', '%d' % cg.extras['clcount'],
1631 mandatory=False)
1632 mandatory=False)
1632 chunkiter = bundle.getchunks()
1633 chunkiter = bundle.getchunks()
1633 else:
1634 else:
1634 # compression argument is only for the bundle2 case
1635 # compression argument is only for the bundle2 case
1635 assert compression is None
1636 assert compression is None
1636 if cg.version != '01':
1637 if cg.version != '01':
1637 raise error.Abort(_('old bundle types only supports v1 '
1638 raise error.Abort(_('old bundle types only supports v1 '
1638 'changegroups'))
1639 'changegroups'))
1639 header, comp = bundletypes[bundletype]
1640 header, comp = bundletypes[bundletype]
1640 if comp not in util.compengines.supportedbundletypes:
1641 if comp not in util.compengines.supportedbundletypes:
1641 raise error.Abort(_('unknown stream compression type: %s')
1642 raise error.Abort(_('unknown stream compression type: %s')
1642 % comp)
1643 % comp)
1643 compengine = util.compengines.forbundletype(comp)
1644 compengine = util.compengines.forbundletype(comp)
1644 def chunkiter():
1645 def chunkiter():
1645 yield header
1646 yield header
1646 for chunk in compengine.compressstream(cg.getchunks(), compopts):
1647 for chunk in compengine.compressstream(cg.getchunks(), compopts):
1647 yield chunk
1648 yield chunk
1648 chunkiter = chunkiter()
1649 chunkiter = chunkiter()
1649
1650
1650 # parse the changegroup data, otherwise we will block
1651 # parse the changegroup data, otherwise we will block
1651 # in case of sshrepo because we don't know the end of the stream
1652 # in case of sshrepo because we don't know the end of the stream
1652 return changegroup.writechunks(ui, chunkiter, filename, vfs=vfs)
1653 return changegroup.writechunks(ui, chunkiter, filename, vfs=vfs)
1653
1654
1654 def combinechangegroupresults(op):
1655 def combinechangegroupresults(op):
1655 """logic to combine 0 or more addchangegroup results into one"""
1656 """logic to combine 0 or more addchangegroup results into one"""
1656 results = [r.get('return', 0)
1657 results = [r.get('return', 0)
1657 for r in op.records['changegroup']]
1658 for r in op.records['changegroup']]
1658 changedheads = 0
1659 changedheads = 0
1659 result = 1
1660 result = 1
1660 for ret in results:
1661 for ret in results:
1661 # If any changegroup result is 0, return 0
1662 # If any changegroup result is 0, return 0
1662 if ret == 0:
1663 if ret == 0:
1663 result = 0
1664 result = 0
1664 break
1665 break
1665 if ret < -1:
1666 if ret < -1:
1666 changedheads += ret + 1
1667 changedheads += ret + 1
1667 elif ret > 1:
1668 elif ret > 1:
1668 changedheads += ret - 1
1669 changedheads += ret - 1
1669 if changedheads > 0:
1670 if changedheads > 0:
1670 result = 1 + changedheads
1671 result = 1 + changedheads
1671 elif changedheads < 0:
1672 elif changedheads < 0:
1672 result = -1 + changedheads
1673 result = -1 + changedheads
1673 return result
1674 return result
1674
1675
1675 @parthandler('changegroup', ('version', 'nbchanges', 'treemanifest',
1676 @parthandler('changegroup', ('version', 'nbchanges', 'treemanifest',
1676 'targetphase'))
1677 'targetphase'))
1677 def handlechangegroup(op, inpart):
1678 def handlechangegroup(op, inpart):
1678 """apply a changegroup part on the repo
1679 """apply a changegroup part on the repo
1679
1680
1680 This is a very early implementation that will massive rework before being
1681 This is a very early implementation that will massive rework before being
1681 inflicted to any end-user.
1682 inflicted to any end-user.
1682 """
1683 """
1683 tr = op.gettransaction()
1684 tr = op.gettransaction()
1684 unpackerversion = inpart.params.get('version', '01')
1685 unpackerversion = inpart.params.get('version', '01')
1685 # We should raise an appropriate exception here
1686 # We should raise an appropriate exception here
1686 cg = changegroup.getunbundler(unpackerversion, inpart, None)
1687 cg = changegroup.getunbundler(unpackerversion, inpart, None)
1687 # the source and url passed here are overwritten by the one contained in
1688 # the source and url passed here are overwritten by the one contained in
1688 # the transaction.hookargs argument. So 'bundle2' is a placeholder
1689 # the transaction.hookargs argument. So 'bundle2' is a placeholder
1689 nbchangesets = None
1690 nbchangesets = None
1690 if 'nbchanges' in inpart.params:
1691 if 'nbchanges' in inpart.params:
1691 nbchangesets = int(inpart.params.get('nbchanges'))
1692 nbchangesets = int(inpart.params.get('nbchanges'))
1692 if ('treemanifest' in inpart.params and
1693 if ('treemanifest' in inpart.params and
1693 'treemanifest' not in op.repo.requirements):
1694 'treemanifest' not in op.repo.requirements):
1694 if len(op.repo.changelog) != 0:
1695 if len(op.repo.changelog) != 0:
1695 raise error.Abort(_(
1696 raise error.Abort(_(
1696 "bundle contains tree manifests, but local repo is "
1697 "bundle contains tree manifests, but local repo is "
1697 "non-empty and does not use tree manifests"))
1698 "non-empty and does not use tree manifests"))
1698 op.repo.requirements.add('treemanifest')
1699 op.repo.requirements.add('treemanifest')
1699 op.repo._applyopenerreqs()
1700 op.repo._applyopenerreqs()
1700 op.repo._writerequirements()
1701 op.repo._writerequirements()
1701 extrakwargs = {}
1702 extrakwargs = {}
1702 targetphase = inpart.params.get('targetphase')
1703 targetphase = inpart.params.get('targetphase')
1703 if targetphase is not None:
1704 if targetphase is not None:
1704 extrakwargs['targetphase'] = int(targetphase)
1705 extrakwargs['targetphase'] = int(targetphase)
1705 ret = _processchangegroup(op, cg, tr, 'bundle2', 'bundle2',
1706 ret = _processchangegroup(op, cg, tr, 'bundle2', 'bundle2',
1706 expectedtotal=nbchangesets, **extrakwargs)
1707 expectedtotal=nbchangesets, **extrakwargs)
1707 if op.reply is not None:
1708 if op.reply is not None:
1708 # This is definitely not the final form of this
1709 # This is definitely not the final form of this
1709 # return. But one need to start somewhere.
1710 # return. But one need to start somewhere.
1710 part = op.reply.newpart('reply:changegroup', mandatory=False)
1711 part = op.reply.newpart('reply:changegroup', mandatory=False)
1711 part.addparam(
1712 part.addparam(
1712 'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False)
1713 'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False)
1713 part.addparam('return', '%i' % ret, mandatory=False)
1714 part.addparam('return', '%i' % ret, mandatory=False)
1714 assert not inpart.read()
1715 assert not inpart.read()
1715
1716
1716 _remotechangegroupparams = tuple(['url', 'size', 'digests'] +
1717 _remotechangegroupparams = tuple(['url', 'size', 'digests'] +
1717 ['digest:%s' % k for k in util.DIGESTS.keys()])
1718 ['digest:%s' % k for k in util.DIGESTS.keys()])
1718 @parthandler('remote-changegroup', _remotechangegroupparams)
1719 @parthandler('remote-changegroup', _remotechangegroupparams)
1719 def handleremotechangegroup(op, inpart):
1720 def handleremotechangegroup(op, inpart):
1720 """apply a bundle10 on the repo, given an url and validation information
1721 """apply a bundle10 on the repo, given an url and validation information
1721
1722
1722 All the information about the remote bundle to import are given as
1723 All the information about the remote bundle to import are given as
1723 parameters. The parameters include:
1724 parameters. The parameters include:
1724 - url: the url to the bundle10.
1725 - url: the url to the bundle10.
1725 - size: the bundle10 file size. It is used to validate what was
1726 - size: the bundle10 file size. It is used to validate what was
1726 retrieved by the client matches the server knowledge about the bundle.
1727 retrieved by the client matches the server knowledge about the bundle.
1727 - digests: a space separated list of the digest types provided as
1728 - digests: a space separated list of the digest types provided as
1728 parameters.
1729 parameters.
1729 - digest:<digest-type>: the hexadecimal representation of the digest with
1730 - digest:<digest-type>: the hexadecimal representation of the digest with
1730 that name. Like the size, it is used to validate what was retrieved by
1731 that name. Like the size, it is used to validate what was retrieved by
1731 the client matches what the server knows about the bundle.
1732 the client matches what the server knows about the bundle.
1732
1733
1733 When multiple digest types are given, all of them are checked.
1734 When multiple digest types are given, all of them are checked.
1734 """
1735 """
1735 try:
1736 try:
1736 raw_url = inpart.params['url']
1737 raw_url = inpart.params['url']
1737 except KeyError:
1738 except KeyError:
1738 raise error.Abort(_('remote-changegroup: missing "%s" param') % 'url')
1739 raise error.Abort(_('remote-changegroup: missing "%s" param') % 'url')
1739 parsed_url = util.url(raw_url)
1740 parsed_url = util.url(raw_url)
1740 if parsed_url.scheme not in capabilities['remote-changegroup']:
1741 if parsed_url.scheme not in capabilities['remote-changegroup']:
1741 raise error.Abort(_('remote-changegroup does not support %s urls') %
1742 raise error.Abort(_('remote-changegroup does not support %s urls') %
1742 parsed_url.scheme)
1743 parsed_url.scheme)
1743
1744
1744 try:
1745 try:
1745 size = int(inpart.params['size'])
1746 size = int(inpart.params['size'])
1746 except ValueError:
1747 except ValueError:
1747 raise error.Abort(_('remote-changegroup: invalid value for param "%s"')
1748 raise error.Abort(_('remote-changegroup: invalid value for param "%s"')
1748 % 'size')
1749 % 'size')
1749 except KeyError:
1750 except KeyError:
1750 raise error.Abort(_('remote-changegroup: missing "%s" param') % 'size')
1751 raise error.Abort(_('remote-changegroup: missing "%s" param') % 'size')
1751
1752
1752 digests = {}
1753 digests = {}
1753 for typ in inpart.params.get('digests', '').split():
1754 for typ in inpart.params.get('digests', '').split():
1754 param = 'digest:%s' % typ
1755 param = 'digest:%s' % typ
1755 try:
1756 try:
1756 value = inpart.params[param]
1757 value = inpart.params[param]
1757 except KeyError:
1758 except KeyError:
1758 raise error.Abort(_('remote-changegroup: missing "%s" param') %
1759 raise error.Abort(_('remote-changegroup: missing "%s" param') %
1759 param)
1760 param)
1760 digests[typ] = value
1761 digests[typ] = value
1761
1762
1762 real_part = util.digestchecker(url.open(op.ui, raw_url), size, digests)
1763 real_part = util.digestchecker(url.open(op.ui, raw_url), size, digests)
1763
1764
1764 tr = op.gettransaction()
1765 tr = op.gettransaction()
1765 from . import exchange
1766 from . import exchange
1766 cg = exchange.readbundle(op.repo.ui, real_part, raw_url)
1767 cg = exchange.readbundle(op.repo.ui, real_part, raw_url)
1767 if not isinstance(cg, changegroup.cg1unpacker):
1768 if not isinstance(cg, changegroup.cg1unpacker):
1768 raise error.Abort(_('%s: not a bundle version 1.0') %
1769 raise error.Abort(_('%s: not a bundle version 1.0') %
1769 util.hidepassword(raw_url))
1770 util.hidepassword(raw_url))
1770 ret = _processchangegroup(op, cg, tr, 'bundle2', 'bundle2')
1771 ret = _processchangegroup(op, cg, tr, 'bundle2', 'bundle2')
1771 if op.reply is not None:
1772 if op.reply is not None:
1772 # This is definitely not the final form of this
1773 # This is definitely not the final form of this
1773 # return. But one need to start somewhere.
1774 # return. But one need to start somewhere.
1774 part = op.reply.newpart('reply:changegroup')
1775 part = op.reply.newpart('reply:changegroup')
1775 part.addparam(
1776 part.addparam(
1776 'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False)
1777 'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False)
1777 part.addparam('return', '%i' % ret, mandatory=False)
1778 part.addparam('return', '%i' % ret, mandatory=False)
1778 try:
1779 try:
1779 real_part.validate()
1780 real_part.validate()
1780 except error.Abort as e:
1781 except error.Abort as e:
1781 raise error.Abort(_('bundle at %s is corrupted:\n%s') %
1782 raise error.Abort(_('bundle at %s is corrupted:\n%s') %
1782 (util.hidepassword(raw_url), str(e)))
1783 (util.hidepassword(raw_url), str(e)))
1783 assert not inpart.read()
1784 assert not inpart.read()
1784
1785
1785 @parthandler('reply:changegroup', ('return', 'in-reply-to'))
1786 @parthandler('reply:changegroup', ('return', 'in-reply-to'))
1786 def handlereplychangegroup(op, inpart):
1787 def handlereplychangegroup(op, inpart):
1787 ret = int(inpart.params['return'])
1788 ret = int(inpart.params['return'])
1788 replyto = int(inpart.params['in-reply-to'])
1789 replyto = int(inpart.params['in-reply-to'])
1789 op.records.add('changegroup', {'return': ret}, replyto)
1790 op.records.add('changegroup', {'return': ret}, replyto)
1790
1791
1791 @parthandler('check:bookmarks')
1792 @parthandler('check:bookmarks')
1792 def handlecheckbookmarks(op, inpart):
1793 def handlecheckbookmarks(op, inpart):
1793 """check location of bookmarks
1794 """check location of bookmarks
1794
1795
1795 This part is to be used to detect push race regarding bookmark, it
1796 This part is to be used to detect push race regarding bookmark, it
1796 contains binary encoded (bookmark, node) tuple. If the local state does
1797 contains binary encoded (bookmark, node) tuple. If the local state does
1797 not marks the one in the part, a PushRaced exception is raised
1798 not marks the one in the part, a PushRaced exception is raised
1798 """
1799 """
1799 bookdata = bookmarks.binarydecode(inpart)
1800 bookdata = bookmarks.binarydecode(inpart)
1800
1801
1801 msgstandard = ('repository changed while pushing - please try again '
1802 msgstandard = ('repository changed while pushing - please try again '
1802 '(bookmark "%s" move from %s to %s)')
1803 '(bookmark "%s" move from %s to %s)')
1803 msgmissing = ('repository changed while pushing - please try again '
1804 msgmissing = ('repository changed while pushing - please try again '
1804 '(bookmark "%s" is missing, expected %s)')
1805 '(bookmark "%s" is missing, expected %s)')
1805 msgexist = ('repository changed while pushing - please try again '
1806 msgexist = ('repository changed while pushing - please try again '
1806 '(bookmark "%s" set on %s, expected missing)')
1807 '(bookmark "%s" set on %s, expected missing)')
1807 for book, node in bookdata:
1808 for book, node in bookdata:
1808 currentnode = op.repo._bookmarks.get(book)
1809 currentnode = op.repo._bookmarks.get(book)
1809 if currentnode != node:
1810 if currentnode != node:
1810 if node is None:
1811 if node is None:
1811 finalmsg = msgexist % (book, nodemod.short(currentnode))
1812 finalmsg = msgexist % (book, nodemod.short(currentnode))
1812 elif currentnode is None:
1813 elif currentnode is None:
1813 finalmsg = msgmissing % (book, nodemod.short(node))
1814 finalmsg = msgmissing % (book, nodemod.short(node))
1814 else:
1815 else:
1815 finalmsg = msgstandard % (book, nodemod.short(node),
1816 finalmsg = msgstandard % (book, nodemod.short(node),
1816 nodemod.short(currentnode))
1817 nodemod.short(currentnode))
1817 raise error.PushRaced(finalmsg)
1818 raise error.PushRaced(finalmsg)
1818
1819
1819 @parthandler('check:heads')
1820 @parthandler('check:heads')
1820 def handlecheckheads(op, inpart):
1821 def handlecheckheads(op, inpart):
1821 """check that head of the repo did not change
1822 """check that head of the repo did not change
1822
1823
1823 This is used to detect a push race when using unbundle.
1824 This is used to detect a push race when using unbundle.
1824 This replaces the "heads" argument of unbundle."""
1825 This replaces the "heads" argument of unbundle."""
1825 h = inpart.read(20)
1826 h = inpart.read(20)
1826 heads = []
1827 heads = []
1827 while len(h) == 20:
1828 while len(h) == 20:
1828 heads.append(h)
1829 heads.append(h)
1829 h = inpart.read(20)
1830 h = inpart.read(20)
1830 assert not h
1831 assert not h
1831 # Trigger a transaction so that we are guaranteed to have the lock now.
1832 # Trigger a transaction so that we are guaranteed to have the lock now.
1832 if op.ui.configbool('experimental', 'bundle2lazylocking'):
1833 if op.ui.configbool('experimental', 'bundle2lazylocking'):
1833 op.gettransaction()
1834 op.gettransaction()
1834 if sorted(heads) != sorted(op.repo.heads()):
1835 if sorted(heads) != sorted(op.repo.heads()):
1835 raise error.PushRaced('repository changed while pushing - '
1836 raise error.PushRaced('repository changed while pushing - '
1836 'please try again')
1837 'please try again')
1837
1838
1838 @parthandler('check:updated-heads')
1839 @parthandler('check:updated-heads')
1839 def handlecheckupdatedheads(op, inpart):
1840 def handlecheckupdatedheads(op, inpart):
1840 """check for race on the heads touched by a push
1841 """check for race on the heads touched by a push
1841
1842
1842 This is similar to 'check:heads' but focus on the heads actually updated
1843 This is similar to 'check:heads' but focus on the heads actually updated
1843 during the push. If other activities happen on unrelated heads, it is
1844 during the push. If other activities happen on unrelated heads, it is
1844 ignored.
1845 ignored.
1845
1846
1846 This allow server with high traffic to avoid push contention as long as
1847 This allow server with high traffic to avoid push contention as long as
1847 unrelated parts of the graph are involved."""
1848 unrelated parts of the graph are involved."""
1848 h = inpart.read(20)
1849 h = inpart.read(20)
1849 heads = []
1850 heads = []
1850 while len(h) == 20:
1851 while len(h) == 20:
1851 heads.append(h)
1852 heads.append(h)
1852 h = inpart.read(20)
1853 h = inpart.read(20)
1853 assert not h
1854 assert not h
1854 # trigger a transaction so that we are guaranteed to have the lock now.
1855 # trigger a transaction so that we are guaranteed to have the lock now.
1855 if op.ui.configbool('experimental', 'bundle2lazylocking'):
1856 if op.ui.configbool('experimental', 'bundle2lazylocking'):
1856 op.gettransaction()
1857 op.gettransaction()
1857
1858
1858 currentheads = set()
1859 currentheads = set()
1859 for ls in op.repo.branchmap().itervalues():
1860 for ls in op.repo.branchmap().itervalues():
1860 currentheads.update(ls)
1861 currentheads.update(ls)
1861
1862
1862 for h in heads:
1863 for h in heads:
1863 if h not in currentheads:
1864 if h not in currentheads:
1864 raise error.PushRaced('repository changed while pushing - '
1865 raise error.PushRaced('repository changed while pushing - '
1865 'please try again')
1866 'please try again')
1866
1867
1867 @parthandler('check:phases')
1868 @parthandler('check:phases')
1868 def handlecheckphases(op, inpart):
1869 def handlecheckphases(op, inpart):
1869 """check that phase boundaries of the repository did not change
1870 """check that phase boundaries of the repository did not change
1870
1871
1871 This is used to detect a push race.
1872 This is used to detect a push race.
1872 """
1873 """
1873 phasetonodes = phases.binarydecode(inpart)
1874 phasetonodes = phases.binarydecode(inpart)
1874 unfi = op.repo.unfiltered()
1875 unfi = op.repo.unfiltered()
1875 cl = unfi.changelog
1876 cl = unfi.changelog
1876 phasecache = unfi._phasecache
1877 phasecache = unfi._phasecache
1877 msg = ('repository changed while pushing - please try again '
1878 msg = ('repository changed while pushing - please try again '
1878 '(%s is %s expected %s)')
1879 '(%s is %s expected %s)')
1879 for expectedphase, nodes in enumerate(phasetonodes):
1880 for expectedphase, nodes in enumerate(phasetonodes):
1880 for n in nodes:
1881 for n in nodes:
1881 actualphase = phasecache.phase(unfi, cl.rev(n))
1882 actualphase = phasecache.phase(unfi, cl.rev(n))
1882 if actualphase != expectedphase:
1883 if actualphase != expectedphase:
1883 finalmsg = msg % (nodemod.short(n),
1884 finalmsg = msg % (nodemod.short(n),
1884 phases.phasenames[actualphase],
1885 phases.phasenames[actualphase],
1885 phases.phasenames[expectedphase])
1886 phases.phasenames[expectedphase])
1886 raise error.PushRaced(finalmsg)
1887 raise error.PushRaced(finalmsg)
1887
1888
1888 @parthandler('output')
1889 @parthandler('output')
1889 def handleoutput(op, inpart):
1890 def handleoutput(op, inpart):
1890 """forward output captured on the server to the client"""
1891 """forward output captured on the server to the client"""
1891 for line in inpart.read().splitlines():
1892 for line in inpart.read().splitlines():
1892 op.ui.status(_('remote: %s\n') % line)
1893 op.ui.status(_('remote: %s\n') % line)
1893
1894
1894 @parthandler('replycaps')
1895 @parthandler('replycaps')
1895 def handlereplycaps(op, inpart):
1896 def handlereplycaps(op, inpart):
1896 """Notify that a reply bundle should be created
1897 """Notify that a reply bundle should be created
1897
1898
1898 The payload contains the capabilities information for the reply"""
1899 The payload contains the capabilities information for the reply"""
1899 caps = decodecaps(inpart.read())
1900 caps = decodecaps(inpart.read())
1900 if op.reply is None:
1901 if op.reply is None:
1901 op.reply = bundle20(op.ui, caps)
1902 op.reply = bundle20(op.ui, caps)
1902
1903
1903 class AbortFromPart(error.Abort):
1904 class AbortFromPart(error.Abort):
1904 """Sub-class of Abort that denotes an error from a bundle2 part."""
1905 """Sub-class of Abort that denotes an error from a bundle2 part."""
1905
1906
1906 @parthandler('error:abort', ('message', 'hint'))
1907 @parthandler('error:abort', ('message', 'hint'))
1907 def handleerrorabort(op, inpart):
1908 def handleerrorabort(op, inpart):
1908 """Used to transmit abort error over the wire"""
1909 """Used to transmit abort error over the wire"""
1909 raise AbortFromPart(inpart.params['message'],
1910 raise AbortFromPart(inpart.params['message'],
1910 hint=inpart.params.get('hint'))
1911 hint=inpart.params.get('hint'))
1911
1912
1912 @parthandler('error:pushkey', ('namespace', 'key', 'new', 'old', 'ret',
1913 @parthandler('error:pushkey', ('namespace', 'key', 'new', 'old', 'ret',
1913 'in-reply-to'))
1914 'in-reply-to'))
1914 def handleerrorpushkey(op, inpart):
1915 def handleerrorpushkey(op, inpart):
1915 """Used to transmit failure of a mandatory pushkey over the wire"""
1916 """Used to transmit failure of a mandatory pushkey over the wire"""
1916 kwargs = {}
1917 kwargs = {}
1917 for name in ('namespace', 'key', 'new', 'old', 'ret'):
1918 for name in ('namespace', 'key', 'new', 'old', 'ret'):
1918 value = inpart.params.get(name)
1919 value = inpart.params.get(name)
1919 if value is not None:
1920 if value is not None:
1920 kwargs[name] = value
1921 kwargs[name] = value
1921 raise error.PushkeyFailed(inpart.params['in-reply-to'], **kwargs)
1922 raise error.PushkeyFailed(inpart.params['in-reply-to'], **kwargs)
1922
1923
1923 @parthandler('error:unsupportedcontent', ('parttype', 'params'))
1924 @parthandler('error:unsupportedcontent', ('parttype', 'params'))
1924 def handleerrorunsupportedcontent(op, inpart):
1925 def handleerrorunsupportedcontent(op, inpart):
1925 """Used to transmit unknown content error over the wire"""
1926 """Used to transmit unknown content error over the wire"""
1926 kwargs = {}
1927 kwargs = {}
1927 parttype = inpart.params.get('parttype')
1928 parttype = inpart.params.get('parttype')
1928 if parttype is not None:
1929 if parttype is not None:
1929 kwargs['parttype'] = parttype
1930 kwargs['parttype'] = parttype
1930 params = inpart.params.get('params')
1931 params = inpart.params.get('params')
1931 if params is not None:
1932 if params is not None:
1932 kwargs['params'] = params.split('\0')
1933 kwargs['params'] = params.split('\0')
1933
1934
1934 raise error.BundleUnknownFeatureError(**kwargs)
1935 raise error.BundleUnknownFeatureError(**kwargs)
1935
1936
1936 @parthandler('error:pushraced', ('message',))
1937 @parthandler('error:pushraced', ('message',))
1937 def handleerrorpushraced(op, inpart):
1938 def handleerrorpushraced(op, inpart):
1938 """Used to transmit push race error over the wire"""
1939 """Used to transmit push race error over the wire"""
1939 raise error.ResponseError(_('push failed:'), inpart.params['message'])
1940 raise error.ResponseError(_('push failed:'), inpart.params['message'])
1940
1941
1941 @parthandler('listkeys', ('namespace',))
1942 @parthandler('listkeys', ('namespace',))
1942 def handlelistkeys(op, inpart):
1943 def handlelistkeys(op, inpart):
1943 """retrieve pushkey namespace content stored in a bundle2"""
1944 """retrieve pushkey namespace content stored in a bundle2"""
1944 namespace = inpart.params['namespace']
1945 namespace = inpart.params['namespace']
1945 r = pushkey.decodekeys(inpart.read())
1946 r = pushkey.decodekeys(inpart.read())
1946 op.records.add('listkeys', (namespace, r))
1947 op.records.add('listkeys', (namespace, r))
1947
1948
1948 @parthandler('pushkey', ('namespace', 'key', 'old', 'new'))
1949 @parthandler('pushkey', ('namespace', 'key', 'old', 'new'))
1949 def handlepushkey(op, inpart):
1950 def handlepushkey(op, inpart):
1950 """process a pushkey request"""
1951 """process a pushkey request"""
1951 dec = pushkey.decode
1952 dec = pushkey.decode
1952 namespace = dec(inpart.params['namespace'])
1953 namespace = dec(inpart.params['namespace'])
1953 key = dec(inpart.params['key'])
1954 key = dec(inpart.params['key'])
1954 old = dec(inpart.params['old'])
1955 old = dec(inpart.params['old'])
1955 new = dec(inpart.params['new'])
1956 new = dec(inpart.params['new'])
1956 # Grab the transaction to ensure that we have the lock before performing the
1957 # Grab the transaction to ensure that we have the lock before performing the
1957 # pushkey.
1958 # pushkey.
1958 if op.ui.configbool('experimental', 'bundle2lazylocking'):
1959 if op.ui.configbool('experimental', 'bundle2lazylocking'):
1959 op.gettransaction()
1960 op.gettransaction()
1960 ret = op.repo.pushkey(namespace, key, old, new)
1961 ret = op.repo.pushkey(namespace, key, old, new)
1961 record = {'namespace': namespace,
1962 record = {'namespace': namespace,
1962 'key': key,
1963 'key': key,
1963 'old': old,
1964 'old': old,
1964 'new': new}
1965 'new': new}
1965 op.records.add('pushkey', record)
1966 op.records.add('pushkey', record)
1966 if op.reply is not None:
1967 if op.reply is not None:
1967 rpart = op.reply.newpart('reply:pushkey')
1968 rpart = op.reply.newpart('reply:pushkey')
1968 rpart.addparam(
1969 rpart.addparam(
1969 'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False)
1970 'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False)
1970 rpart.addparam('return', '%i' % ret, mandatory=False)
1971 rpart.addparam('return', '%i' % ret, mandatory=False)
1971 if inpart.mandatory and not ret:
1972 if inpart.mandatory and not ret:
1972 kwargs = {}
1973 kwargs = {}
1973 for key in ('namespace', 'key', 'new', 'old', 'ret'):
1974 for key in ('namespace', 'key', 'new', 'old', 'ret'):
1974 if key in inpart.params:
1975 if key in inpart.params:
1975 kwargs[key] = inpart.params[key]
1976 kwargs[key] = inpart.params[key]
1976 raise error.PushkeyFailed(partid=str(inpart.id), **kwargs)
1977 raise error.PushkeyFailed(partid=str(inpart.id), **kwargs)
1977
1978
1978 @parthandler('phase-heads')
1979 @parthandler('phase-heads')
1979 def handlephases(op, inpart):
1980 def handlephases(op, inpart):
1980 """apply phases from bundle part to repo"""
1981 """apply phases from bundle part to repo"""
1981 headsbyphase = phases.binarydecode(inpart)
1982 headsbyphase = phases.binarydecode(inpart)
1982 phases.updatephases(op.repo.unfiltered(), op.gettransaction, headsbyphase)
1983 phases.updatephases(op.repo.unfiltered(), op.gettransaction, headsbyphase)
1983
1984
1984 @parthandler('reply:pushkey', ('return', 'in-reply-to'))
1985 @parthandler('reply:pushkey', ('return', 'in-reply-to'))
1985 def handlepushkeyreply(op, inpart):
1986 def handlepushkeyreply(op, inpart):
1986 """retrieve the result of a pushkey request"""
1987 """retrieve the result of a pushkey request"""
1987 ret = int(inpart.params['return'])
1988 ret = int(inpart.params['return'])
1988 partid = int(inpart.params['in-reply-to'])
1989 partid = int(inpart.params['in-reply-to'])
1989 op.records.add('pushkey', {'return': ret}, partid)
1990 op.records.add('pushkey', {'return': ret}, partid)
1990
1991
1991 @parthandler('obsmarkers')
1992 @parthandler('obsmarkers')
1992 def handleobsmarker(op, inpart):
1993 def handleobsmarker(op, inpart):
1993 """add a stream of obsmarkers to the repo"""
1994 """add a stream of obsmarkers to the repo"""
1994 tr = op.gettransaction()
1995 tr = op.gettransaction()
1995 markerdata = inpart.read()
1996 markerdata = inpart.read()
1996 if op.ui.config('experimental', 'obsmarkers-exchange-debug'):
1997 if op.ui.config('experimental', 'obsmarkers-exchange-debug'):
1997 op.ui.write(('obsmarker-exchange: %i bytes received\n')
1998 op.ui.write(('obsmarker-exchange: %i bytes received\n')
1998 % len(markerdata))
1999 % len(markerdata))
1999 # The mergemarkers call will crash if marker creation is not enabled.
2000 # The mergemarkers call will crash if marker creation is not enabled.
2000 # we want to avoid this if the part is advisory.
2001 # we want to avoid this if the part is advisory.
2001 if not inpart.mandatory and op.repo.obsstore.readonly:
2002 if not inpart.mandatory and op.repo.obsstore.readonly:
2002 op.repo.ui.debug('ignoring obsolescence markers, feature not enabled')
2003 op.repo.ui.debug('ignoring obsolescence markers, feature not enabled')
2003 return
2004 return
2004 new = op.repo.obsstore.mergemarkers(tr, markerdata)
2005 new = op.repo.obsstore.mergemarkers(tr, markerdata)
2005 op.repo.invalidatevolatilesets()
2006 op.repo.invalidatevolatilesets()
2006 if new:
2007 if new:
2007 op.repo.ui.status(_('%i new obsolescence markers\n') % new)
2008 op.repo.ui.status(_('%i new obsolescence markers\n') % new)
2008 op.records.add('obsmarkers', {'new': new})
2009 op.records.add('obsmarkers', {'new': new})
2009 if op.reply is not None:
2010 if op.reply is not None:
2010 rpart = op.reply.newpart('reply:obsmarkers')
2011 rpart = op.reply.newpart('reply:obsmarkers')
2011 rpart.addparam(
2012 rpart.addparam(
2012 'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False)
2013 'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False)
2013 rpart.addparam('new', '%i' % new, mandatory=False)
2014 rpart.addparam('new', '%i' % new, mandatory=False)
2014
2015
2015
2016
2016 @parthandler('reply:obsmarkers', ('new', 'in-reply-to'))
2017 @parthandler('reply:obsmarkers', ('new', 'in-reply-to'))
2017 def handleobsmarkerreply(op, inpart):
2018 def handleobsmarkerreply(op, inpart):
2018 """retrieve the result of a pushkey request"""
2019 """retrieve the result of a pushkey request"""
2019 ret = int(inpart.params['new'])
2020 ret = int(inpart.params['new'])
2020 partid = int(inpart.params['in-reply-to'])
2021 partid = int(inpart.params['in-reply-to'])
2021 op.records.add('obsmarkers', {'new': ret}, partid)
2022 op.records.add('obsmarkers', {'new': ret}, partid)
2022
2023
2023 @parthandler('hgtagsfnodes')
2024 @parthandler('hgtagsfnodes')
2024 def handlehgtagsfnodes(op, inpart):
2025 def handlehgtagsfnodes(op, inpart):
2025 """Applies .hgtags fnodes cache entries to the local repo.
2026 """Applies .hgtags fnodes cache entries to the local repo.
2026
2027
2027 Payload is pairs of 20 byte changeset nodes and filenodes.
2028 Payload is pairs of 20 byte changeset nodes and filenodes.
2028 """
2029 """
2029 # Grab the transaction so we ensure that we have the lock at this point.
2030 # Grab the transaction so we ensure that we have the lock at this point.
2030 if op.ui.configbool('experimental', 'bundle2lazylocking'):
2031 if op.ui.configbool('experimental', 'bundle2lazylocking'):
2031 op.gettransaction()
2032 op.gettransaction()
2032 cache = tags.hgtagsfnodescache(op.repo.unfiltered())
2033 cache = tags.hgtagsfnodescache(op.repo.unfiltered())
2033
2034
2034 count = 0
2035 count = 0
2035 while True:
2036 while True:
2036 node = inpart.read(20)
2037 node = inpart.read(20)
2037 fnode = inpart.read(20)
2038 fnode = inpart.read(20)
2038 if len(node) < 20 or len(fnode) < 20:
2039 if len(node) < 20 or len(fnode) < 20:
2039 op.ui.debug('ignoring incomplete received .hgtags fnodes data\n')
2040 op.ui.debug('ignoring incomplete received .hgtags fnodes data\n')
2040 break
2041 break
2041 cache.setfnode(node, fnode)
2042 cache.setfnode(node, fnode)
2042 count += 1
2043 count += 1
2043
2044
2044 cache.write()
2045 cache.write()
2045 op.ui.debug('applied %i hgtags fnodes cache entries\n' % count)
2046 op.ui.debug('applied %i hgtags fnodes cache entries\n' % count)
2046
2047
2047 @parthandler('pushvars')
2048 @parthandler('pushvars')
2048 def bundle2getvars(op, part):
2049 def bundle2getvars(op, part):
2049 '''unbundle a bundle2 containing shellvars on the server'''
2050 '''unbundle a bundle2 containing shellvars on the server'''
2050 # An option to disable unbundling on server-side for security reasons
2051 # An option to disable unbundling on server-side for security reasons
2051 if op.ui.configbool('push', 'pushvars.server'):
2052 if op.ui.configbool('push', 'pushvars.server'):
2052 hookargs = {}
2053 hookargs = {}
2053 for key, value in part.advisoryparams:
2054 for key, value in part.advisoryparams:
2054 key = key.upper()
2055 key = key.upper()
2055 # We want pushed variables to have USERVAR_ prepended so we know
2056 # We want pushed variables to have USERVAR_ prepended so we know
2056 # they came from the --pushvar flag.
2057 # they came from the --pushvar flag.
2057 key = "USERVAR_" + key
2058 key = "USERVAR_" + key
2058 hookargs[key] = value
2059 hookargs[key] = value
2059 op.addhookargs(hookargs)
2060 op.addhookargs(hookargs)
@@ -1,2120 +1,2137 b''
1 # exchange.py - utility to exchange data between repos.
1 # exchange.py - utility to exchange data between repos.
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import collections
10 import collections
11 import errno
11 import errno
12 import hashlib
12 import hashlib
13
13
14 from .i18n import _
14 from .i18n import _
15 from .node import (
15 from .node import (
16 bin,
16 hex,
17 hex,
17 nullid,
18 nullid,
18 )
19 )
19 from . import (
20 from . import (
20 bookmarks as bookmod,
21 bookmarks as bookmod,
21 bundle2,
22 bundle2,
22 changegroup,
23 changegroup,
23 discovery,
24 discovery,
24 error,
25 error,
25 lock as lockmod,
26 lock as lockmod,
26 obsolete,
27 obsolete,
27 phases,
28 phases,
28 pushkey,
29 pushkey,
29 pycompat,
30 pycompat,
30 remotenames,
31 remotenames,
31 scmutil,
32 scmutil,
32 sslutil,
33 sslutil,
33 streamclone,
34 streamclone,
34 url as urlmod,
35 url as urlmod,
35 util,
36 util,
36 )
37 )
37
38
38 urlerr = util.urlerr
39 urlerr = util.urlerr
39 urlreq = util.urlreq
40 urlreq = util.urlreq
40
41
41 # Maps bundle version human names to changegroup versions.
42 # Maps bundle version human names to changegroup versions.
42 _bundlespeccgversions = {'v1': '01',
43 _bundlespeccgversions = {'v1': '01',
43 'v2': '02',
44 'v2': '02',
44 'packed1': 's1',
45 'packed1': 's1',
45 'bundle2': '02', #legacy
46 'bundle2': '02', #legacy
46 }
47 }
47
48
48 # Compression engines allowed in version 1. THIS SHOULD NEVER CHANGE.
49 # Compression engines allowed in version 1. THIS SHOULD NEVER CHANGE.
49 _bundlespecv1compengines = {'gzip', 'bzip2', 'none'}
50 _bundlespecv1compengines = {'gzip', 'bzip2', 'none'}
50
51
51 def parsebundlespec(repo, spec, strict=True, externalnames=False):
52 def parsebundlespec(repo, spec, strict=True, externalnames=False):
52 """Parse a bundle string specification into parts.
53 """Parse a bundle string specification into parts.
53
54
54 Bundle specifications denote a well-defined bundle/exchange format.
55 Bundle specifications denote a well-defined bundle/exchange format.
55 The content of a given specification should not change over time in
56 The content of a given specification should not change over time in
56 order to ensure that bundles produced by a newer version of Mercurial are
57 order to ensure that bundles produced by a newer version of Mercurial are
57 readable from an older version.
58 readable from an older version.
58
59
59 The string currently has the form:
60 The string currently has the form:
60
61
61 <compression>-<type>[;<parameter0>[;<parameter1>]]
62 <compression>-<type>[;<parameter0>[;<parameter1>]]
62
63
63 Where <compression> is one of the supported compression formats
64 Where <compression> is one of the supported compression formats
64 and <type> is (currently) a version string. A ";" can follow the type and
65 and <type> is (currently) a version string. A ";" can follow the type and
65 all text afterwards is interpreted as URI encoded, ";" delimited key=value
66 all text afterwards is interpreted as URI encoded, ";" delimited key=value
66 pairs.
67 pairs.
67
68
68 If ``strict`` is True (the default) <compression> is required. Otherwise,
69 If ``strict`` is True (the default) <compression> is required. Otherwise,
69 it is optional.
70 it is optional.
70
71
71 If ``externalnames`` is False (the default), the human-centric names will
72 If ``externalnames`` is False (the default), the human-centric names will
72 be converted to their internal representation.
73 be converted to their internal representation.
73
74
74 Returns a 3-tuple of (compression, version, parameters). Compression will
75 Returns a 3-tuple of (compression, version, parameters). Compression will
75 be ``None`` if not in strict mode and a compression isn't defined.
76 be ``None`` if not in strict mode and a compression isn't defined.
76
77
77 An ``InvalidBundleSpecification`` is raised when the specification is
78 An ``InvalidBundleSpecification`` is raised when the specification is
78 not syntactically well formed.
79 not syntactically well formed.
79
80
80 An ``UnsupportedBundleSpecification`` is raised when the compression or
81 An ``UnsupportedBundleSpecification`` is raised when the compression or
81 bundle type/version is not recognized.
82 bundle type/version is not recognized.
82
83
83 Note: this function will likely eventually return a more complex data
84 Note: this function will likely eventually return a more complex data
84 structure, including bundle2 part information.
85 structure, including bundle2 part information.
85 """
86 """
86 def parseparams(s):
87 def parseparams(s):
87 if ';' not in s:
88 if ';' not in s:
88 return s, {}
89 return s, {}
89
90
90 params = {}
91 params = {}
91 version, paramstr = s.split(';', 1)
92 version, paramstr = s.split(';', 1)
92
93
93 for p in paramstr.split(';'):
94 for p in paramstr.split(';'):
94 if '=' not in p:
95 if '=' not in p:
95 raise error.InvalidBundleSpecification(
96 raise error.InvalidBundleSpecification(
96 _('invalid bundle specification: '
97 _('invalid bundle specification: '
97 'missing "=" in parameter: %s') % p)
98 'missing "=" in parameter: %s') % p)
98
99
99 key, value = p.split('=', 1)
100 key, value = p.split('=', 1)
100 key = urlreq.unquote(key)
101 key = urlreq.unquote(key)
101 value = urlreq.unquote(value)
102 value = urlreq.unquote(value)
102 params[key] = value
103 params[key] = value
103
104
104 return version, params
105 return version, params
105
106
106
107
107 if strict and '-' not in spec:
108 if strict and '-' not in spec:
108 raise error.InvalidBundleSpecification(
109 raise error.InvalidBundleSpecification(
109 _('invalid bundle specification; '
110 _('invalid bundle specification; '
110 'must be prefixed with compression: %s') % spec)
111 'must be prefixed with compression: %s') % spec)
111
112
112 if '-' in spec:
113 if '-' in spec:
113 compression, version = spec.split('-', 1)
114 compression, version = spec.split('-', 1)
114
115
115 if compression not in util.compengines.supportedbundlenames:
116 if compression not in util.compengines.supportedbundlenames:
116 raise error.UnsupportedBundleSpecification(
117 raise error.UnsupportedBundleSpecification(
117 _('%s compression is not supported') % compression)
118 _('%s compression is not supported') % compression)
118
119
119 version, params = parseparams(version)
120 version, params = parseparams(version)
120
121
121 if version not in _bundlespeccgversions:
122 if version not in _bundlespeccgversions:
122 raise error.UnsupportedBundleSpecification(
123 raise error.UnsupportedBundleSpecification(
123 _('%s is not a recognized bundle version') % version)
124 _('%s is not a recognized bundle version') % version)
124 else:
125 else:
125 # Value could be just the compression or just the version, in which
126 # Value could be just the compression or just the version, in which
126 # case some defaults are assumed (but only when not in strict mode).
127 # case some defaults are assumed (but only when not in strict mode).
127 assert not strict
128 assert not strict
128
129
129 spec, params = parseparams(spec)
130 spec, params = parseparams(spec)
130
131
131 if spec in util.compengines.supportedbundlenames:
132 if spec in util.compengines.supportedbundlenames:
132 compression = spec
133 compression = spec
133 version = 'v1'
134 version = 'v1'
134 # Generaldelta repos require v2.
135 # Generaldelta repos require v2.
135 if 'generaldelta' in repo.requirements:
136 if 'generaldelta' in repo.requirements:
136 version = 'v2'
137 version = 'v2'
137 # Modern compression engines require v2.
138 # Modern compression engines require v2.
138 if compression not in _bundlespecv1compengines:
139 if compression not in _bundlespecv1compengines:
139 version = 'v2'
140 version = 'v2'
140 elif spec in _bundlespeccgversions:
141 elif spec in _bundlespeccgversions:
141 if spec == 'packed1':
142 if spec == 'packed1':
142 compression = 'none'
143 compression = 'none'
143 else:
144 else:
144 compression = 'bzip2'
145 compression = 'bzip2'
145 version = spec
146 version = spec
146 else:
147 else:
147 raise error.UnsupportedBundleSpecification(
148 raise error.UnsupportedBundleSpecification(
148 _('%s is not a recognized bundle specification') % spec)
149 _('%s is not a recognized bundle specification') % spec)
149
150
150 # Bundle version 1 only supports a known set of compression engines.
151 # Bundle version 1 only supports a known set of compression engines.
151 if version == 'v1' and compression not in _bundlespecv1compengines:
152 if version == 'v1' and compression not in _bundlespecv1compengines:
152 raise error.UnsupportedBundleSpecification(
153 raise error.UnsupportedBundleSpecification(
153 _('compression engine %s is not supported on v1 bundles') %
154 _('compression engine %s is not supported on v1 bundles') %
154 compression)
155 compression)
155
156
156 # The specification for packed1 can optionally declare the data formats
157 # The specification for packed1 can optionally declare the data formats
157 # required to apply it. If we see this metadata, compare against what the
158 # required to apply it. If we see this metadata, compare against what the
158 # repo supports and error if the bundle isn't compatible.
159 # repo supports and error if the bundle isn't compatible.
159 if version == 'packed1' and 'requirements' in params:
160 if version == 'packed1' and 'requirements' in params:
160 requirements = set(params['requirements'].split(','))
161 requirements = set(params['requirements'].split(','))
161 missingreqs = requirements - repo.supportedformats
162 missingreqs = requirements - repo.supportedformats
162 if missingreqs:
163 if missingreqs:
163 raise error.UnsupportedBundleSpecification(
164 raise error.UnsupportedBundleSpecification(
164 _('missing support for repository features: %s') %
165 _('missing support for repository features: %s') %
165 ', '.join(sorted(missingreqs)))
166 ', '.join(sorted(missingreqs)))
166
167
167 if not externalnames:
168 if not externalnames:
168 engine = util.compengines.forbundlename(compression)
169 engine = util.compengines.forbundlename(compression)
169 compression = engine.bundletype()[1]
170 compression = engine.bundletype()[1]
170 version = _bundlespeccgversions[version]
171 version = _bundlespeccgversions[version]
171 return compression, version, params
172 return compression, version, params
172
173
173 def readbundle(ui, fh, fname, vfs=None):
174 def readbundle(ui, fh, fname, vfs=None):
174 header = changegroup.readexactly(fh, 4)
175 header = changegroup.readexactly(fh, 4)
175
176
176 alg = None
177 alg = None
177 if not fname:
178 if not fname:
178 fname = "stream"
179 fname = "stream"
179 if not header.startswith('HG') and header.startswith('\0'):
180 if not header.startswith('HG') and header.startswith('\0'):
180 fh = changegroup.headerlessfixup(fh, header)
181 fh = changegroup.headerlessfixup(fh, header)
181 header = "HG10"
182 header = "HG10"
182 alg = 'UN'
183 alg = 'UN'
183 elif vfs:
184 elif vfs:
184 fname = vfs.join(fname)
185 fname = vfs.join(fname)
185
186
186 magic, version = header[0:2], header[2:4]
187 magic, version = header[0:2], header[2:4]
187
188
188 if magic != 'HG':
189 if magic != 'HG':
189 raise error.Abort(_('%s: not a Mercurial bundle') % fname)
190 raise error.Abort(_('%s: not a Mercurial bundle') % fname)
190 if version == '10':
191 if version == '10':
191 if alg is None:
192 if alg is None:
192 alg = changegroup.readexactly(fh, 2)
193 alg = changegroup.readexactly(fh, 2)
193 return changegroup.cg1unpacker(fh, alg)
194 return changegroup.cg1unpacker(fh, alg)
194 elif version.startswith('2'):
195 elif version.startswith('2'):
195 return bundle2.getunbundler(ui, fh, magicstring=magic + version)
196 return bundle2.getunbundler(ui, fh, magicstring=magic + version)
196 elif version == 'S1':
197 elif version == 'S1':
197 return streamclone.streamcloneapplier(fh)
198 return streamclone.streamcloneapplier(fh)
198 else:
199 else:
199 raise error.Abort(_('%s: unknown bundle version %s') % (fname, version))
200 raise error.Abort(_('%s: unknown bundle version %s') % (fname, version))
200
201
201 def getbundlespec(ui, fh):
202 def getbundlespec(ui, fh):
202 """Infer the bundlespec from a bundle file handle.
203 """Infer the bundlespec from a bundle file handle.
203
204
204 The input file handle is seeked and the original seek position is not
205 The input file handle is seeked and the original seek position is not
205 restored.
206 restored.
206 """
207 """
207 def speccompression(alg):
208 def speccompression(alg):
208 try:
209 try:
209 return util.compengines.forbundletype(alg).bundletype()[0]
210 return util.compengines.forbundletype(alg).bundletype()[0]
210 except KeyError:
211 except KeyError:
211 return None
212 return None
212
213
213 b = readbundle(ui, fh, None)
214 b = readbundle(ui, fh, None)
214 if isinstance(b, changegroup.cg1unpacker):
215 if isinstance(b, changegroup.cg1unpacker):
215 alg = b._type
216 alg = b._type
216 if alg == '_truncatedBZ':
217 if alg == '_truncatedBZ':
217 alg = 'BZ'
218 alg = 'BZ'
218 comp = speccompression(alg)
219 comp = speccompression(alg)
219 if not comp:
220 if not comp:
220 raise error.Abort(_('unknown compression algorithm: %s') % alg)
221 raise error.Abort(_('unknown compression algorithm: %s') % alg)
221 return '%s-v1' % comp
222 return '%s-v1' % comp
222 elif isinstance(b, bundle2.unbundle20):
223 elif isinstance(b, bundle2.unbundle20):
223 if 'Compression' in b.params:
224 if 'Compression' in b.params:
224 comp = speccompression(b.params['Compression'])
225 comp = speccompression(b.params['Compression'])
225 if not comp:
226 if not comp:
226 raise error.Abort(_('unknown compression algorithm: %s') % comp)
227 raise error.Abort(_('unknown compression algorithm: %s') % comp)
227 else:
228 else:
228 comp = 'none'
229 comp = 'none'
229
230
230 version = None
231 version = None
231 for part in b.iterparts():
232 for part in b.iterparts():
232 if part.type == 'changegroup':
233 if part.type == 'changegroup':
233 version = part.params['version']
234 version = part.params['version']
234 if version in ('01', '02'):
235 if version in ('01', '02'):
235 version = 'v2'
236 version = 'v2'
236 else:
237 else:
237 raise error.Abort(_('changegroup version %s does not have '
238 raise error.Abort(_('changegroup version %s does not have '
238 'a known bundlespec') % version,
239 'a known bundlespec') % version,
239 hint=_('try upgrading your Mercurial '
240 hint=_('try upgrading your Mercurial '
240 'client'))
241 'client'))
241
242
242 if not version:
243 if not version:
243 raise error.Abort(_('could not identify changegroup version in '
244 raise error.Abort(_('could not identify changegroup version in '
244 'bundle'))
245 'bundle'))
245
246
246 return '%s-%s' % (comp, version)
247 return '%s-%s' % (comp, version)
247 elif isinstance(b, streamclone.streamcloneapplier):
248 elif isinstance(b, streamclone.streamcloneapplier):
248 requirements = streamclone.readbundle1header(fh)[2]
249 requirements = streamclone.readbundle1header(fh)[2]
249 params = 'requirements=%s' % ','.join(sorted(requirements))
250 params = 'requirements=%s' % ','.join(sorted(requirements))
250 return 'none-packed1;%s' % urlreq.quote(params)
251 return 'none-packed1;%s' % urlreq.quote(params)
251 else:
252 else:
252 raise error.Abort(_('unknown bundle type: %s') % b)
253 raise error.Abort(_('unknown bundle type: %s') % b)
253
254
254 def _computeoutgoing(repo, heads, common):
255 def _computeoutgoing(repo, heads, common):
255 """Computes which revs are outgoing given a set of common
256 """Computes which revs are outgoing given a set of common
256 and a set of heads.
257 and a set of heads.
257
258
258 This is a separate function so extensions can have access to
259 This is a separate function so extensions can have access to
259 the logic.
260 the logic.
260
261
261 Returns a discovery.outgoing object.
262 Returns a discovery.outgoing object.
262 """
263 """
263 cl = repo.changelog
264 cl = repo.changelog
264 if common:
265 if common:
265 hasnode = cl.hasnode
266 hasnode = cl.hasnode
266 common = [n for n in common if hasnode(n)]
267 common = [n for n in common if hasnode(n)]
267 else:
268 else:
268 common = [nullid]
269 common = [nullid]
269 if not heads:
270 if not heads:
270 heads = cl.heads()
271 heads = cl.heads()
271 return discovery.outgoing(repo, common, heads)
272 return discovery.outgoing(repo, common, heads)
272
273
273 def _forcebundle1(op):
274 def _forcebundle1(op):
274 """return true if a pull/push must use bundle1
275 """return true if a pull/push must use bundle1
275
276
276 This function is used to allow testing of the older bundle version"""
277 This function is used to allow testing of the older bundle version"""
277 ui = op.repo.ui
278 ui = op.repo.ui
278 forcebundle1 = False
279 forcebundle1 = False
279 # The goal is this config is to allow developer to choose the bundle
280 # The goal is this config is to allow developer to choose the bundle
280 # version used during exchanged. This is especially handy during test.
281 # version used during exchanged. This is especially handy during test.
281 # Value is a list of bundle version to be picked from, highest version
282 # Value is a list of bundle version to be picked from, highest version
282 # should be used.
283 # should be used.
283 #
284 #
284 # developer config: devel.legacy.exchange
285 # developer config: devel.legacy.exchange
285 exchange = ui.configlist('devel', 'legacy.exchange')
286 exchange = ui.configlist('devel', 'legacy.exchange')
286 forcebundle1 = 'bundle2' not in exchange and 'bundle1' in exchange
287 forcebundle1 = 'bundle2' not in exchange and 'bundle1' in exchange
287 return forcebundle1 or not op.remote.capable('bundle2')
288 return forcebundle1 or not op.remote.capable('bundle2')
288
289
289 class pushoperation(object):
290 class pushoperation(object):
290 """A object that represent a single push operation
291 """A object that represent a single push operation
291
292
292 Its purpose is to carry push related state and very common operations.
293 Its purpose is to carry push related state and very common operations.
293
294
294 A new pushoperation should be created at the beginning of each push and
295 A new pushoperation should be created at the beginning of each push and
295 discarded afterward.
296 discarded afterward.
296 """
297 """
297
298
298 def __init__(self, repo, remote, force=False, revs=None, newbranch=False,
299 def __init__(self, repo, remote, force=False, revs=None, newbranch=False,
299 bookmarks=(), pushvars=None):
300 bookmarks=(), pushvars=None):
300 # repo we push from
301 # repo we push from
301 self.repo = repo
302 self.repo = repo
302 self.ui = repo.ui
303 self.ui = repo.ui
303 # repo we push to
304 # repo we push to
304 self.remote = remote
305 self.remote = remote
305 # force option provided
306 # force option provided
306 self.force = force
307 self.force = force
307 # revs to be pushed (None is "all")
308 # revs to be pushed (None is "all")
308 self.revs = revs
309 self.revs = revs
309 # bookmark explicitly pushed
310 # bookmark explicitly pushed
310 self.bookmarks = bookmarks
311 self.bookmarks = bookmarks
311 # allow push of new branch
312 # allow push of new branch
312 self.newbranch = newbranch
313 self.newbranch = newbranch
313 # step already performed
314 # step already performed
314 # (used to check what steps have been already performed through bundle2)
315 # (used to check what steps have been already performed through bundle2)
315 self.stepsdone = set()
316 self.stepsdone = set()
316 # Integer version of the changegroup push result
317 # Integer version of the changegroup push result
317 # - None means nothing to push
318 # - None means nothing to push
318 # - 0 means HTTP error
319 # - 0 means HTTP error
319 # - 1 means we pushed and remote head count is unchanged *or*
320 # - 1 means we pushed and remote head count is unchanged *or*
320 # we have outgoing changesets but refused to push
321 # we have outgoing changesets but refused to push
321 # - other values as described by addchangegroup()
322 # - other values as described by addchangegroup()
322 self.cgresult = None
323 self.cgresult = None
323 # Boolean value for the bookmark push
324 # Boolean value for the bookmark push
324 self.bkresult = None
325 self.bkresult = None
325 # discover.outgoing object (contains common and outgoing data)
326 # discover.outgoing object (contains common and outgoing data)
326 self.outgoing = None
327 self.outgoing = None
327 # all remote topological heads before the push
328 # all remote topological heads before the push
328 self.remoteheads = None
329 self.remoteheads = None
329 # Details of the remote branch pre and post push
330 # Details of the remote branch pre and post push
330 #
331 #
331 # mapping: {'branch': ([remoteheads],
332 # mapping: {'branch': ([remoteheads],
332 # [newheads],
333 # [newheads],
333 # [unsyncedheads],
334 # [unsyncedheads],
334 # [discardedheads])}
335 # [discardedheads])}
335 # - branch: the branch name
336 # - branch: the branch name
336 # - remoteheads: the list of remote heads known locally
337 # - remoteheads: the list of remote heads known locally
337 # None if the branch is new
338 # None if the branch is new
338 # - newheads: the new remote heads (known locally) with outgoing pushed
339 # - newheads: the new remote heads (known locally) with outgoing pushed
339 # - unsyncedheads: the list of remote heads unknown locally.
340 # - unsyncedheads: the list of remote heads unknown locally.
340 # - discardedheads: the list of remote heads made obsolete by the push
341 # - discardedheads: the list of remote heads made obsolete by the push
341 self.pushbranchmap = None
342 self.pushbranchmap = None
342 # testable as a boolean indicating if any nodes are missing locally.
343 # testable as a boolean indicating if any nodes are missing locally.
343 self.incoming = None
344 self.incoming = None
344 # summary of the remote phase situation
345 # summary of the remote phase situation
345 self.remotephases = None
346 self.remotephases = None
346 # phases changes that must be pushed along side the changesets
347 # phases changes that must be pushed along side the changesets
347 self.outdatedphases = None
348 self.outdatedphases = None
348 # phases changes that must be pushed if changeset push fails
349 # phases changes that must be pushed if changeset push fails
349 self.fallbackoutdatedphases = None
350 self.fallbackoutdatedphases = None
350 # outgoing obsmarkers
351 # outgoing obsmarkers
351 self.outobsmarkers = set()
352 self.outobsmarkers = set()
352 # outgoing bookmarks
353 # outgoing bookmarks
353 self.outbookmarks = []
354 self.outbookmarks = []
354 # transaction manager
355 # transaction manager
355 self.trmanager = None
356 self.trmanager = None
356 # map { pushkey partid -> callback handling failure}
357 # map { pushkey partid -> callback handling failure}
357 # used to handle exception from mandatory pushkey part failure
358 # used to handle exception from mandatory pushkey part failure
358 self.pkfailcb = {}
359 self.pkfailcb = {}
359 # an iterable of pushvars or None
360 # an iterable of pushvars or None
360 self.pushvars = pushvars
361 self.pushvars = pushvars
361
362
362 @util.propertycache
363 @util.propertycache
363 def futureheads(self):
364 def futureheads(self):
364 """future remote heads if the changeset push succeeds"""
365 """future remote heads if the changeset push succeeds"""
365 return self.outgoing.missingheads
366 return self.outgoing.missingheads
366
367
367 @util.propertycache
368 @util.propertycache
368 def fallbackheads(self):
369 def fallbackheads(self):
369 """future remote heads if the changeset push fails"""
370 """future remote heads if the changeset push fails"""
370 if self.revs is None:
371 if self.revs is None:
371 # not target to push, all common are relevant
372 # not target to push, all common are relevant
372 return self.outgoing.commonheads
373 return self.outgoing.commonheads
373 unfi = self.repo.unfiltered()
374 unfi = self.repo.unfiltered()
374 # I want cheads = heads(::missingheads and ::commonheads)
375 # I want cheads = heads(::missingheads and ::commonheads)
375 # (missingheads is revs with secret changeset filtered out)
376 # (missingheads is revs with secret changeset filtered out)
376 #
377 #
377 # This can be expressed as:
378 # This can be expressed as:
378 # cheads = ( (missingheads and ::commonheads)
379 # cheads = ( (missingheads and ::commonheads)
379 # + (commonheads and ::missingheads))"
380 # + (commonheads and ::missingheads))"
380 # )
381 # )
381 #
382 #
382 # while trying to push we already computed the following:
383 # while trying to push we already computed the following:
383 # common = (::commonheads)
384 # common = (::commonheads)
384 # missing = ((commonheads::missingheads) - commonheads)
385 # missing = ((commonheads::missingheads) - commonheads)
385 #
386 #
386 # We can pick:
387 # We can pick:
387 # * missingheads part of common (::commonheads)
388 # * missingheads part of common (::commonheads)
388 common = self.outgoing.common
389 common = self.outgoing.common
389 nm = self.repo.changelog.nodemap
390 nm = self.repo.changelog.nodemap
390 cheads = [node for node in self.revs if nm[node] in common]
391 cheads = [node for node in self.revs if nm[node] in common]
391 # and
392 # and
392 # * commonheads parents on missing
393 # * commonheads parents on missing
393 revset = unfi.set('%ln and parents(roots(%ln))',
394 revset = unfi.set('%ln and parents(roots(%ln))',
394 self.outgoing.commonheads,
395 self.outgoing.commonheads,
395 self.outgoing.missing)
396 self.outgoing.missing)
396 cheads.extend(c.node() for c in revset)
397 cheads.extend(c.node() for c in revset)
397 return cheads
398 return cheads
398
399
399 @property
400 @property
400 def commonheads(self):
401 def commonheads(self):
401 """set of all common heads after changeset bundle push"""
402 """set of all common heads after changeset bundle push"""
402 if self.cgresult:
403 if self.cgresult:
403 return self.futureheads
404 return self.futureheads
404 else:
405 else:
405 return self.fallbackheads
406 return self.fallbackheads
406
407
407 # mapping of message used when pushing bookmark
408 # mapping of message used when pushing bookmark
408 bookmsgmap = {'update': (_("updating bookmark %s\n"),
409 bookmsgmap = {'update': (_("updating bookmark %s\n"),
409 _('updating bookmark %s failed!\n')),
410 _('updating bookmark %s failed!\n')),
410 'export': (_("exporting bookmark %s\n"),
411 'export': (_("exporting bookmark %s\n"),
411 _('exporting bookmark %s failed!\n')),
412 _('exporting bookmark %s failed!\n')),
412 'delete': (_("deleting remote bookmark %s\n"),
413 'delete': (_("deleting remote bookmark %s\n"),
413 _('deleting remote bookmark %s failed!\n')),
414 _('deleting remote bookmark %s failed!\n')),
414 }
415 }
415
416
416
417
417 def push(repo, remote, force=False, revs=None, newbranch=False, bookmarks=(),
418 def push(repo, remote, force=False, revs=None, newbranch=False, bookmarks=(),
418 opargs=None):
419 opargs=None):
419 '''Push outgoing changesets (limited by revs) from a local
420 '''Push outgoing changesets (limited by revs) from a local
420 repository to remote. Return an integer:
421 repository to remote. Return an integer:
421 - None means nothing to push
422 - None means nothing to push
422 - 0 means HTTP error
423 - 0 means HTTP error
423 - 1 means we pushed and remote head count is unchanged *or*
424 - 1 means we pushed and remote head count is unchanged *or*
424 we have outgoing changesets but refused to push
425 we have outgoing changesets but refused to push
425 - other values as described by addchangegroup()
426 - other values as described by addchangegroup()
426 '''
427 '''
427 if opargs is None:
428 if opargs is None:
428 opargs = {}
429 opargs = {}
429 pushop = pushoperation(repo, remote, force, revs, newbranch, bookmarks,
430 pushop = pushoperation(repo, remote, force, revs, newbranch, bookmarks,
430 **pycompat.strkwargs(opargs))
431 **pycompat.strkwargs(opargs))
431 if pushop.remote.local():
432 if pushop.remote.local():
432 missing = (set(pushop.repo.requirements)
433 missing = (set(pushop.repo.requirements)
433 - pushop.remote.local().supported)
434 - pushop.remote.local().supported)
434 if missing:
435 if missing:
435 msg = _("required features are not"
436 msg = _("required features are not"
436 " supported in the destination:"
437 " supported in the destination:"
437 " %s") % (', '.join(sorted(missing)))
438 " %s") % (', '.join(sorted(missing)))
438 raise error.Abort(msg)
439 raise error.Abort(msg)
439
440
440 if not pushop.remote.canpush():
441 if not pushop.remote.canpush():
441 raise error.Abort(_("destination does not support push"))
442 raise error.Abort(_("destination does not support push"))
442
443
443 if not pushop.remote.capable('unbundle'):
444 if not pushop.remote.capable('unbundle'):
444 raise error.Abort(_('cannot push: destination does not support the '
445 raise error.Abort(_('cannot push: destination does not support the '
445 'unbundle wire protocol command'))
446 'unbundle wire protocol command'))
446
447
447 # get lock as we might write phase data
448 # get lock as we might write phase data
448 wlock = lock = None
449 wlock = lock = None
449 try:
450 try:
450 # bundle2 push may receive a reply bundle touching bookmarks or other
451 # bundle2 push may receive a reply bundle touching bookmarks or other
451 # things requiring the wlock. Take it now to ensure proper ordering.
452 # things requiring the wlock. Take it now to ensure proper ordering.
452 maypushback = pushop.ui.configbool('experimental', 'bundle2.pushback')
453 maypushback = pushop.ui.configbool('experimental', 'bundle2.pushback')
453 if (not _forcebundle1(pushop)) and maypushback:
454 if (not _forcebundle1(pushop)) and maypushback:
454 wlock = pushop.repo.wlock()
455 wlock = pushop.repo.wlock()
455 lock = pushop.repo.lock()
456 lock = pushop.repo.lock()
456 pushop.trmanager = transactionmanager(pushop.repo,
457 pushop.trmanager = transactionmanager(pushop.repo,
457 'push-response',
458 'push-response',
458 pushop.remote.url())
459 pushop.remote.url())
459 except IOError as err:
460 except IOError as err:
460 if err.errno != errno.EACCES:
461 if err.errno != errno.EACCES:
461 raise
462 raise
462 # source repo cannot be locked.
463 # source repo cannot be locked.
463 # We do not abort the push, but just disable the local phase
464 # We do not abort the push, but just disable the local phase
464 # synchronisation.
465 # synchronisation.
465 msg = 'cannot lock source repository: %s\n' % err
466 msg = 'cannot lock source repository: %s\n' % err
466 pushop.ui.debug(msg)
467 pushop.ui.debug(msg)
467
468
468 with wlock or util.nullcontextmanager(), \
469 with wlock or util.nullcontextmanager(), \
469 lock or util.nullcontextmanager(), \
470 lock or util.nullcontextmanager(), \
470 pushop.trmanager or util.nullcontextmanager():
471 pushop.trmanager or util.nullcontextmanager():
471 pushop.repo.checkpush(pushop)
472 pushop.repo.checkpush(pushop)
472 _pushdiscovery(pushop)
473 _pushdiscovery(pushop)
473 if not _forcebundle1(pushop):
474 if not _forcebundle1(pushop):
474 _pushbundle2(pushop)
475 _pushbundle2(pushop)
475 _pushchangeset(pushop)
476 _pushchangeset(pushop)
476 _pushsyncphase(pushop)
477 _pushsyncphase(pushop)
477 _pushobsolete(pushop)
478 _pushobsolete(pushop)
478 _pushbookmark(pushop)
479 _pushbookmark(pushop)
479
480
480 return pushop
481 return pushop
481
482
482 # list of steps to perform discovery before push
483 # list of steps to perform discovery before push
483 pushdiscoveryorder = []
484 pushdiscoveryorder = []
484
485
485 # Mapping between step name and function
486 # Mapping between step name and function
486 #
487 #
487 # This exists to help extensions wrap steps if necessary
488 # This exists to help extensions wrap steps if necessary
488 pushdiscoverymapping = {}
489 pushdiscoverymapping = {}
489
490
490 def pushdiscovery(stepname):
491 def pushdiscovery(stepname):
491 """decorator for function performing discovery before push
492 """decorator for function performing discovery before push
492
493
493 The function is added to the step -> function mapping and appended to the
494 The function is added to the step -> function mapping and appended to the
494 list of steps. Beware that decorated function will be added in order (this
495 list of steps. Beware that decorated function will be added in order (this
495 may matter).
496 may matter).
496
497
497 You can only use this decorator for a new step, if you want to wrap a step
498 You can only use this decorator for a new step, if you want to wrap a step
498 from an extension, change the pushdiscovery dictionary directly."""
499 from an extension, change the pushdiscovery dictionary directly."""
499 def dec(func):
500 def dec(func):
500 assert stepname not in pushdiscoverymapping
501 assert stepname not in pushdiscoverymapping
501 pushdiscoverymapping[stepname] = func
502 pushdiscoverymapping[stepname] = func
502 pushdiscoveryorder.append(stepname)
503 pushdiscoveryorder.append(stepname)
503 return func
504 return func
504 return dec
505 return dec
505
506
506 def _pushdiscovery(pushop):
507 def _pushdiscovery(pushop):
507 """Run all discovery steps"""
508 """Run all discovery steps"""
508 for stepname in pushdiscoveryorder:
509 for stepname in pushdiscoveryorder:
509 step = pushdiscoverymapping[stepname]
510 step = pushdiscoverymapping[stepname]
510 step(pushop)
511 step(pushop)
511
512
512 @pushdiscovery('changeset')
513 @pushdiscovery('changeset')
513 def _pushdiscoverychangeset(pushop):
514 def _pushdiscoverychangeset(pushop):
514 """discover the changeset that need to be pushed"""
515 """discover the changeset that need to be pushed"""
515 fci = discovery.findcommonincoming
516 fci = discovery.findcommonincoming
516 commoninc = fci(pushop.repo, pushop.remote, force=pushop.force)
517 commoninc = fci(pushop.repo, pushop.remote, force=pushop.force)
517 common, inc, remoteheads = commoninc
518 common, inc, remoteheads = commoninc
518 fco = discovery.findcommonoutgoing
519 fco = discovery.findcommonoutgoing
519 outgoing = fco(pushop.repo, pushop.remote, onlyheads=pushop.revs,
520 outgoing = fco(pushop.repo, pushop.remote, onlyheads=pushop.revs,
520 commoninc=commoninc, force=pushop.force)
521 commoninc=commoninc, force=pushop.force)
521 pushop.outgoing = outgoing
522 pushop.outgoing = outgoing
522 pushop.remoteheads = remoteheads
523 pushop.remoteheads = remoteheads
523 pushop.incoming = inc
524 pushop.incoming = inc
524
525
525 @pushdiscovery('phase')
526 @pushdiscovery('phase')
526 def _pushdiscoveryphase(pushop):
527 def _pushdiscoveryphase(pushop):
527 """discover the phase that needs to be pushed
528 """discover the phase that needs to be pushed
528
529
529 (computed for both success and failure case for changesets push)"""
530 (computed for both success and failure case for changesets push)"""
530 outgoing = pushop.outgoing
531 outgoing = pushop.outgoing
531 unfi = pushop.repo.unfiltered()
532 unfi = pushop.repo.unfiltered()
532 remotephases = pushop.remote.listkeys('phases')
533 remotephases = pushop.remote.listkeys('phases')
533 if (pushop.ui.configbool('ui', '_usedassubrepo')
534 if (pushop.ui.configbool('ui', '_usedassubrepo')
534 and remotephases # server supports phases
535 and remotephases # server supports phases
535 and not pushop.outgoing.missing # no changesets to be pushed
536 and not pushop.outgoing.missing # no changesets to be pushed
536 and remotephases.get('publishing', False)):
537 and remotephases.get('publishing', False)):
537 # When:
538 # When:
538 # - this is a subrepo push
539 # - this is a subrepo push
539 # - and remote support phase
540 # - and remote support phase
540 # - and no changeset are to be pushed
541 # - and no changeset are to be pushed
541 # - and remote is publishing
542 # - and remote is publishing
542 # We may be in issue 3781 case!
543 # We may be in issue 3781 case!
543 # We drop the possible phase synchronisation done by
544 # We drop the possible phase synchronisation done by
544 # courtesy to publish changesets possibly locally draft
545 # courtesy to publish changesets possibly locally draft
545 # on the remote.
546 # on the remote.
546 pushop.outdatedphases = []
547 pushop.outdatedphases = []
547 pushop.fallbackoutdatedphases = []
548 pushop.fallbackoutdatedphases = []
548 return
549 return
549
550
550 pushop.remotephases = phases.remotephasessummary(pushop.repo,
551 pushop.remotephases = phases.remotephasessummary(pushop.repo,
551 pushop.fallbackheads,
552 pushop.fallbackheads,
552 remotephases)
553 remotephases)
553 droots = pushop.remotephases.draftroots
554 droots = pushop.remotephases.draftroots
554
555
555 extracond = ''
556 extracond = ''
556 if not pushop.remotephases.publishing:
557 if not pushop.remotephases.publishing:
557 extracond = ' and public()'
558 extracond = ' and public()'
558 revset = 'heads((%%ln::%%ln) %s)' % extracond
559 revset = 'heads((%%ln::%%ln) %s)' % extracond
559 # Get the list of all revs draft on remote by public here.
560 # Get the list of all revs draft on remote by public here.
560 # XXX Beware that revset break if droots is not strictly
561 # XXX Beware that revset break if droots is not strictly
561 # XXX root we may want to ensure it is but it is costly
562 # XXX root we may want to ensure it is but it is costly
562 fallback = list(unfi.set(revset, droots, pushop.fallbackheads))
563 fallback = list(unfi.set(revset, droots, pushop.fallbackheads))
563 if not outgoing.missing:
564 if not outgoing.missing:
564 future = fallback
565 future = fallback
565 else:
566 else:
566 # adds changeset we are going to push as draft
567 # adds changeset we are going to push as draft
567 #
568 #
568 # should not be necessary for publishing server, but because of an
569 # should not be necessary for publishing server, but because of an
569 # issue fixed in xxxxx we have to do it anyway.
570 # issue fixed in xxxxx we have to do it anyway.
570 fdroots = list(unfi.set('roots(%ln + %ln::)',
571 fdroots = list(unfi.set('roots(%ln + %ln::)',
571 outgoing.missing, droots))
572 outgoing.missing, droots))
572 fdroots = [f.node() for f in fdroots]
573 fdroots = [f.node() for f in fdroots]
573 future = list(unfi.set(revset, fdroots, pushop.futureheads))
574 future = list(unfi.set(revset, fdroots, pushop.futureheads))
574 pushop.outdatedphases = future
575 pushop.outdatedphases = future
575 pushop.fallbackoutdatedphases = fallback
576 pushop.fallbackoutdatedphases = fallback
576
577
577 @pushdiscovery('obsmarker')
578 @pushdiscovery('obsmarker')
578 def _pushdiscoveryobsmarkers(pushop):
579 def _pushdiscoveryobsmarkers(pushop):
579 if (obsolete.isenabled(pushop.repo, obsolete.exchangeopt)
580 if (obsolete.isenabled(pushop.repo, obsolete.exchangeopt)
580 and pushop.repo.obsstore
581 and pushop.repo.obsstore
581 and 'obsolete' in pushop.remote.listkeys('namespaces')):
582 and 'obsolete' in pushop.remote.listkeys('namespaces')):
582 repo = pushop.repo
583 repo = pushop.repo
583 # very naive computation, that can be quite expensive on big repo.
584 # very naive computation, that can be quite expensive on big repo.
584 # However: evolution is currently slow on them anyway.
585 # However: evolution is currently slow on them anyway.
585 nodes = (c.node() for c in repo.set('::%ln', pushop.futureheads))
586 nodes = (c.node() for c in repo.set('::%ln', pushop.futureheads))
586 pushop.outobsmarkers = pushop.repo.obsstore.relevantmarkers(nodes)
587 pushop.outobsmarkers = pushop.repo.obsstore.relevantmarkers(nodes)
587
588
588 @pushdiscovery('bookmarks')
589 @pushdiscovery('bookmarks')
589 def _pushdiscoverybookmarks(pushop):
590 def _pushdiscoverybookmarks(pushop):
590 ui = pushop.ui
591 ui = pushop.ui
591 repo = pushop.repo.unfiltered()
592 repo = pushop.repo.unfiltered()
592 remote = pushop.remote
593 remote = pushop.remote
593 ui.debug("checking for updated bookmarks\n")
594 ui.debug("checking for updated bookmarks\n")
594 ancestors = ()
595 ancestors = ()
595 if pushop.revs:
596 if pushop.revs:
596 revnums = map(repo.changelog.rev, pushop.revs)
597 revnums = map(repo.changelog.rev, pushop.revs)
597 ancestors = repo.changelog.ancestors(revnums, inclusive=True)
598 ancestors = repo.changelog.ancestors(revnums, inclusive=True)
598 remotebookmark = remote.listkeys('bookmarks')
599 remotebookmark = remote.listkeys('bookmarks')
599
600
600 explicit = set([repo._bookmarks.expandname(bookmark)
601 explicit = set([repo._bookmarks.expandname(bookmark)
601 for bookmark in pushop.bookmarks])
602 for bookmark in pushop.bookmarks])
602
603
603 remotebookmark = bookmod.unhexlifybookmarks(remotebookmark)
604 remotebookmark = bookmod.unhexlifybookmarks(remotebookmark)
604 comp = bookmod.comparebookmarks(repo, repo._bookmarks, remotebookmark)
605 comp = bookmod.comparebookmarks(repo, repo._bookmarks, remotebookmark)
605
606
606 def safehex(x):
607 def safehex(x):
607 if x is None:
608 if x is None:
608 return x
609 return x
609 return hex(x)
610 return hex(x)
610
611
611 def hexifycompbookmarks(bookmarks):
612 def hexifycompbookmarks(bookmarks):
612 for b, scid, dcid in bookmarks:
613 for b, scid, dcid in bookmarks:
613 yield b, safehex(scid), safehex(dcid)
614 yield b, safehex(scid), safehex(dcid)
614
615
615 comp = [hexifycompbookmarks(marks) for marks in comp]
616 comp = [hexifycompbookmarks(marks) for marks in comp]
616 addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same = comp
617 addsrc, adddst, advsrc, advdst, diverge, differ, invalid, same = comp
617
618
618 for b, scid, dcid in advsrc:
619 for b, scid, dcid in advsrc:
619 if b in explicit:
620 if b in explicit:
620 explicit.remove(b)
621 explicit.remove(b)
621 if not ancestors or repo[scid].rev() in ancestors:
622 if not ancestors or repo[scid].rev() in ancestors:
622 pushop.outbookmarks.append((b, dcid, scid))
623 pushop.outbookmarks.append((b, dcid, scid))
623 # search added bookmark
624 # search added bookmark
624 for b, scid, dcid in addsrc:
625 for b, scid, dcid in addsrc:
625 if b in explicit:
626 if b in explicit:
626 explicit.remove(b)
627 explicit.remove(b)
627 pushop.outbookmarks.append((b, '', scid))
628 pushop.outbookmarks.append((b, '', scid))
628 # search for overwritten bookmark
629 # search for overwritten bookmark
629 for b, scid, dcid in list(advdst) + list(diverge) + list(differ):
630 for b, scid, dcid in list(advdst) + list(diverge) + list(differ):
630 if b in explicit:
631 if b in explicit:
631 explicit.remove(b)
632 explicit.remove(b)
632 pushop.outbookmarks.append((b, dcid, scid))
633 pushop.outbookmarks.append((b, dcid, scid))
633 # search for bookmark to delete
634 # search for bookmark to delete
634 for b, scid, dcid in adddst:
635 for b, scid, dcid in adddst:
635 if b in explicit:
636 if b in explicit:
636 explicit.remove(b)
637 explicit.remove(b)
637 # treat as "deleted locally"
638 # treat as "deleted locally"
638 pushop.outbookmarks.append((b, dcid, ''))
639 pushop.outbookmarks.append((b, dcid, ''))
639 # identical bookmarks shouldn't get reported
640 # identical bookmarks shouldn't get reported
640 for b, scid, dcid in same:
641 for b, scid, dcid in same:
641 if b in explicit:
642 if b in explicit:
642 explicit.remove(b)
643 explicit.remove(b)
643
644
644 if explicit:
645 if explicit:
645 explicit = sorted(explicit)
646 explicit = sorted(explicit)
646 # we should probably list all of them
647 # we should probably list all of them
647 ui.warn(_('bookmark %s does not exist on the local '
648 ui.warn(_('bookmark %s does not exist on the local '
648 'or remote repository!\n') % explicit[0])
649 'or remote repository!\n') % explicit[0])
649 pushop.bkresult = 2
650 pushop.bkresult = 2
650
651
651 pushop.outbookmarks.sort()
652 pushop.outbookmarks.sort()
652
653
653 def _pushcheckoutgoing(pushop):
654 def _pushcheckoutgoing(pushop):
654 outgoing = pushop.outgoing
655 outgoing = pushop.outgoing
655 unfi = pushop.repo.unfiltered()
656 unfi = pushop.repo.unfiltered()
656 if not outgoing.missing:
657 if not outgoing.missing:
657 # nothing to push
658 # nothing to push
658 scmutil.nochangesfound(unfi.ui, unfi, outgoing.excluded)
659 scmutil.nochangesfound(unfi.ui, unfi, outgoing.excluded)
659 return False
660 return False
660 # something to push
661 # something to push
661 if not pushop.force:
662 if not pushop.force:
662 # if repo.obsstore == False --> no obsolete
663 # if repo.obsstore == False --> no obsolete
663 # then, save the iteration
664 # then, save the iteration
664 if unfi.obsstore:
665 if unfi.obsstore:
665 # this message are here for 80 char limit reason
666 # this message are here for 80 char limit reason
666 mso = _("push includes obsolete changeset: %s!")
667 mso = _("push includes obsolete changeset: %s!")
667 mspd = _("push includes phase-divergent changeset: %s!")
668 mspd = _("push includes phase-divergent changeset: %s!")
668 mscd = _("push includes content-divergent changeset: %s!")
669 mscd = _("push includes content-divergent changeset: %s!")
669 mst = {"orphan": _("push includes orphan changeset: %s!"),
670 mst = {"orphan": _("push includes orphan changeset: %s!"),
670 "phase-divergent": mspd,
671 "phase-divergent": mspd,
671 "content-divergent": mscd}
672 "content-divergent": mscd}
672 # If we are to push if there is at least one
673 # If we are to push if there is at least one
673 # obsolete or unstable changeset in missing, at
674 # obsolete or unstable changeset in missing, at
674 # least one of the missinghead will be obsolete or
675 # least one of the missinghead will be obsolete or
675 # unstable. So checking heads only is ok
676 # unstable. So checking heads only is ok
676 for node in outgoing.missingheads:
677 for node in outgoing.missingheads:
677 ctx = unfi[node]
678 ctx = unfi[node]
678 if ctx.obsolete():
679 if ctx.obsolete():
679 raise error.Abort(mso % ctx)
680 raise error.Abort(mso % ctx)
680 elif ctx.isunstable():
681 elif ctx.isunstable():
681 # TODO print more than one instability in the abort
682 # TODO print more than one instability in the abort
682 # message
683 # message
683 raise error.Abort(mst[ctx.instabilities()[0]] % ctx)
684 raise error.Abort(mst[ctx.instabilities()[0]] % ctx)
684
685
685 discovery.checkheads(pushop)
686 discovery.checkheads(pushop)
686 return True
687 return True
687
688
688 # List of names of steps to perform for an outgoing bundle2, order matters.
689 # List of names of steps to perform for an outgoing bundle2, order matters.
689 b2partsgenorder = []
690 b2partsgenorder = []
690
691
691 # Mapping between step name and function
692 # Mapping between step name and function
692 #
693 #
693 # This exists to help extensions wrap steps if necessary
694 # This exists to help extensions wrap steps if necessary
694 b2partsgenmapping = {}
695 b2partsgenmapping = {}
695
696
696 def b2partsgenerator(stepname, idx=None):
697 def b2partsgenerator(stepname, idx=None):
697 """decorator for function generating bundle2 part
698 """decorator for function generating bundle2 part
698
699
699 The function is added to the step -> function mapping and appended to the
700 The function is added to the step -> function mapping and appended to the
700 list of steps. Beware that decorated functions will be added in order
701 list of steps. Beware that decorated functions will be added in order
701 (this may matter).
702 (this may matter).
702
703
703 You can only use this decorator for new steps, if you want to wrap a step
704 You can only use this decorator for new steps, if you want to wrap a step
704 from an extension, attack the b2partsgenmapping dictionary directly."""
705 from an extension, attack the b2partsgenmapping dictionary directly."""
705 def dec(func):
706 def dec(func):
706 assert stepname not in b2partsgenmapping
707 assert stepname not in b2partsgenmapping
707 b2partsgenmapping[stepname] = func
708 b2partsgenmapping[stepname] = func
708 if idx is None:
709 if idx is None:
709 b2partsgenorder.append(stepname)
710 b2partsgenorder.append(stepname)
710 else:
711 else:
711 b2partsgenorder.insert(idx, stepname)
712 b2partsgenorder.insert(idx, stepname)
712 return func
713 return func
713 return dec
714 return dec
714
715
715 def _pushb2ctxcheckheads(pushop, bundler):
716 def _pushb2ctxcheckheads(pushop, bundler):
716 """Generate race condition checking parts
717 """Generate race condition checking parts
717
718
718 Exists as an independent function to aid extensions
719 Exists as an independent function to aid extensions
719 """
720 """
720 # * 'force' do not check for push race,
721 # * 'force' do not check for push race,
721 # * if we don't push anything, there are nothing to check.
722 # * if we don't push anything, there are nothing to check.
722 if not pushop.force and pushop.outgoing.missingheads:
723 if not pushop.force and pushop.outgoing.missingheads:
723 allowunrelated = 'related' in bundler.capabilities.get('checkheads', ())
724 allowunrelated = 'related' in bundler.capabilities.get('checkheads', ())
724 emptyremote = pushop.pushbranchmap is None
725 emptyremote = pushop.pushbranchmap is None
725 if not allowunrelated or emptyremote:
726 if not allowunrelated or emptyremote:
726 bundler.newpart('check:heads', data=iter(pushop.remoteheads))
727 bundler.newpart('check:heads', data=iter(pushop.remoteheads))
727 else:
728 else:
728 affected = set()
729 affected = set()
729 for branch, heads in pushop.pushbranchmap.iteritems():
730 for branch, heads in pushop.pushbranchmap.iteritems():
730 remoteheads, newheads, unsyncedheads, discardedheads = heads
731 remoteheads, newheads, unsyncedheads, discardedheads = heads
731 if remoteheads is not None:
732 if remoteheads is not None:
732 remote = set(remoteheads)
733 remote = set(remoteheads)
733 affected |= set(discardedheads) & remote
734 affected |= set(discardedheads) & remote
734 affected |= remote - set(newheads)
735 affected |= remote - set(newheads)
735 if affected:
736 if affected:
736 data = iter(sorted(affected))
737 data = iter(sorted(affected))
737 bundler.newpart('check:updated-heads', data=data)
738 bundler.newpart('check:updated-heads', data=data)
738
739
739 def _pushing(pushop):
740 def _pushing(pushop):
740 """return True if we are pushing anything"""
741 """return True if we are pushing anything"""
741 return bool(pushop.outgoing.missing
742 return bool(pushop.outgoing.missing
742 or pushop.outdatedphases
743 or pushop.outdatedphases
743 or pushop.outobsmarkers
744 or pushop.outobsmarkers
744 or pushop.outbookmarks)
745 or pushop.outbookmarks)
745
746
747 @b2partsgenerator('check-bookmarks')
748 def _pushb2checkbookmarks(pushop, bundler):
749 """insert bookmark move checking"""
750 if not _pushing(pushop) or pushop.force:
751 return
752 b2caps = bundle2.bundle2caps(pushop.remote)
753 hasbookmarkcheck = 'bookmarks' in b2caps
754 if not (pushop.outbookmarks and hasbookmarkcheck):
755 return
756 data = []
757 for book, old, new in pushop.outbookmarks:
758 old = bin(old)
759 data.append((book, old))
760 checkdata = bookmod.binaryencode(data)
761 bundler.newpart('check:bookmarks', data=checkdata)
762
746 @b2partsgenerator('check-phases')
763 @b2partsgenerator('check-phases')
747 def _pushb2checkphases(pushop, bundler):
764 def _pushb2checkphases(pushop, bundler):
748 """insert phase move checking"""
765 """insert phase move checking"""
749 if not _pushing(pushop) or pushop.force:
766 if not _pushing(pushop) or pushop.force:
750 return
767 return
751 b2caps = bundle2.bundle2caps(pushop.remote)
768 b2caps = bundle2.bundle2caps(pushop.remote)
752 hasphaseheads = 'heads' in b2caps.get('phases', ())
769 hasphaseheads = 'heads' in b2caps.get('phases', ())
753 if pushop.remotephases is not None and hasphaseheads:
770 if pushop.remotephases is not None and hasphaseheads:
754 # check that the remote phase has not changed
771 # check that the remote phase has not changed
755 checks = [[] for p in phases.allphases]
772 checks = [[] for p in phases.allphases]
756 checks[phases.public].extend(pushop.remotephases.publicheads)
773 checks[phases.public].extend(pushop.remotephases.publicheads)
757 checks[phases.draft].extend(pushop.remotephases.draftroots)
774 checks[phases.draft].extend(pushop.remotephases.draftroots)
758 if any(checks):
775 if any(checks):
759 for nodes in checks:
776 for nodes in checks:
760 nodes.sort()
777 nodes.sort()
761 checkdata = phases.binaryencode(checks)
778 checkdata = phases.binaryencode(checks)
762 bundler.newpart('check:phases', data=checkdata)
779 bundler.newpart('check:phases', data=checkdata)
763
780
764 @b2partsgenerator('changeset')
781 @b2partsgenerator('changeset')
765 def _pushb2ctx(pushop, bundler):
782 def _pushb2ctx(pushop, bundler):
766 """handle changegroup push through bundle2
783 """handle changegroup push through bundle2
767
784
768 addchangegroup result is stored in the ``pushop.cgresult`` attribute.
785 addchangegroup result is stored in the ``pushop.cgresult`` attribute.
769 """
786 """
770 if 'changesets' in pushop.stepsdone:
787 if 'changesets' in pushop.stepsdone:
771 return
788 return
772 pushop.stepsdone.add('changesets')
789 pushop.stepsdone.add('changesets')
773 # Send known heads to the server for race detection.
790 # Send known heads to the server for race detection.
774 if not _pushcheckoutgoing(pushop):
791 if not _pushcheckoutgoing(pushop):
775 return
792 return
776 pushop.repo.prepushoutgoinghooks(pushop)
793 pushop.repo.prepushoutgoinghooks(pushop)
777
794
778 _pushb2ctxcheckheads(pushop, bundler)
795 _pushb2ctxcheckheads(pushop, bundler)
779
796
780 b2caps = bundle2.bundle2caps(pushop.remote)
797 b2caps = bundle2.bundle2caps(pushop.remote)
781 version = '01'
798 version = '01'
782 cgversions = b2caps.get('changegroup')
799 cgversions = b2caps.get('changegroup')
783 if cgversions: # 3.1 and 3.2 ship with an empty value
800 if cgversions: # 3.1 and 3.2 ship with an empty value
784 cgversions = [v for v in cgversions
801 cgversions = [v for v in cgversions
785 if v in changegroup.supportedoutgoingversions(
802 if v in changegroup.supportedoutgoingversions(
786 pushop.repo)]
803 pushop.repo)]
787 if not cgversions:
804 if not cgversions:
788 raise ValueError(_('no common changegroup version'))
805 raise ValueError(_('no common changegroup version'))
789 version = max(cgversions)
806 version = max(cgversions)
790 cgstream = changegroup.makestream(pushop.repo, pushop.outgoing, version,
807 cgstream = changegroup.makestream(pushop.repo, pushop.outgoing, version,
791 'push')
808 'push')
792 cgpart = bundler.newpart('changegroup', data=cgstream)
809 cgpart = bundler.newpart('changegroup', data=cgstream)
793 if cgversions:
810 if cgversions:
794 cgpart.addparam('version', version)
811 cgpart.addparam('version', version)
795 if 'treemanifest' in pushop.repo.requirements:
812 if 'treemanifest' in pushop.repo.requirements:
796 cgpart.addparam('treemanifest', '1')
813 cgpart.addparam('treemanifest', '1')
797 def handlereply(op):
814 def handlereply(op):
798 """extract addchangegroup returns from server reply"""
815 """extract addchangegroup returns from server reply"""
799 cgreplies = op.records.getreplies(cgpart.id)
816 cgreplies = op.records.getreplies(cgpart.id)
800 assert len(cgreplies['changegroup']) == 1
817 assert len(cgreplies['changegroup']) == 1
801 pushop.cgresult = cgreplies['changegroup'][0]['return']
818 pushop.cgresult = cgreplies['changegroup'][0]['return']
802 return handlereply
819 return handlereply
803
820
804 @b2partsgenerator('phase')
821 @b2partsgenerator('phase')
805 def _pushb2phases(pushop, bundler):
822 def _pushb2phases(pushop, bundler):
806 """handle phase push through bundle2"""
823 """handle phase push through bundle2"""
807 if 'phases' in pushop.stepsdone:
824 if 'phases' in pushop.stepsdone:
808 return
825 return
809 b2caps = bundle2.bundle2caps(pushop.remote)
826 b2caps = bundle2.bundle2caps(pushop.remote)
810 ui = pushop.repo.ui
827 ui = pushop.repo.ui
811
828
812 legacyphase = 'phases' in ui.configlist('devel', 'legacy.exchange')
829 legacyphase = 'phases' in ui.configlist('devel', 'legacy.exchange')
813 haspushkey = 'pushkey' in b2caps
830 haspushkey = 'pushkey' in b2caps
814 hasphaseheads = 'heads' in b2caps.get('phases', ())
831 hasphaseheads = 'heads' in b2caps.get('phases', ())
815
832
816 if hasphaseheads and not legacyphase:
833 if hasphaseheads and not legacyphase:
817 return _pushb2phaseheads(pushop, bundler)
834 return _pushb2phaseheads(pushop, bundler)
818 elif haspushkey:
835 elif haspushkey:
819 return _pushb2phasespushkey(pushop, bundler)
836 return _pushb2phasespushkey(pushop, bundler)
820
837
821 def _pushb2phaseheads(pushop, bundler):
838 def _pushb2phaseheads(pushop, bundler):
822 """push phase information through a bundle2 - binary part"""
839 """push phase information through a bundle2 - binary part"""
823 pushop.stepsdone.add('phases')
840 pushop.stepsdone.add('phases')
824 if pushop.outdatedphases:
841 if pushop.outdatedphases:
825 updates = [[] for p in phases.allphases]
842 updates = [[] for p in phases.allphases]
826 updates[0].extend(h.node() for h in pushop.outdatedphases)
843 updates[0].extend(h.node() for h in pushop.outdatedphases)
827 phasedata = phases.binaryencode(updates)
844 phasedata = phases.binaryencode(updates)
828 bundler.newpart('phase-heads', data=phasedata)
845 bundler.newpart('phase-heads', data=phasedata)
829
846
830 def _pushb2phasespushkey(pushop, bundler):
847 def _pushb2phasespushkey(pushop, bundler):
831 """push phase information through a bundle2 - pushkey part"""
848 """push phase information through a bundle2 - pushkey part"""
832 pushop.stepsdone.add('phases')
849 pushop.stepsdone.add('phases')
833 part2node = []
850 part2node = []
834
851
835 def handlefailure(pushop, exc):
852 def handlefailure(pushop, exc):
836 targetid = int(exc.partid)
853 targetid = int(exc.partid)
837 for partid, node in part2node:
854 for partid, node in part2node:
838 if partid == targetid:
855 if partid == targetid:
839 raise error.Abort(_('updating %s to public failed') % node)
856 raise error.Abort(_('updating %s to public failed') % node)
840
857
841 enc = pushkey.encode
858 enc = pushkey.encode
842 for newremotehead in pushop.outdatedphases:
859 for newremotehead in pushop.outdatedphases:
843 part = bundler.newpart('pushkey')
860 part = bundler.newpart('pushkey')
844 part.addparam('namespace', enc('phases'))
861 part.addparam('namespace', enc('phases'))
845 part.addparam('key', enc(newremotehead.hex()))
862 part.addparam('key', enc(newremotehead.hex()))
846 part.addparam('old', enc('%d' % phases.draft))
863 part.addparam('old', enc('%d' % phases.draft))
847 part.addparam('new', enc('%d' % phases.public))
864 part.addparam('new', enc('%d' % phases.public))
848 part2node.append((part.id, newremotehead))
865 part2node.append((part.id, newremotehead))
849 pushop.pkfailcb[part.id] = handlefailure
866 pushop.pkfailcb[part.id] = handlefailure
850
867
851 def handlereply(op):
868 def handlereply(op):
852 for partid, node in part2node:
869 for partid, node in part2node:
853 partrep = op.records.getreplies(partid)
870 partrep = op.records.getreplies(partid)
854 results = partrep['pushkey']
871 results = partrep['pushkey']
855 assert len(results) <= 1
872 assert len(results) <= 1
856 msg = None
873 msg = None
857 if not results:
874 if not results:
858 msg = _('server ignored update of %s to public!\n') % node
875 msg = _('server ignored update of %s to public!\n') % node
859 elif not int(results[0]['return']):
876 elif not int(results[0]['return']):
860 msg = _('updating %s to public failed!\n') % node
877 msg = _('updating %s to public failed!\n') % node
861 if msg is not None:
878 if msg is not None:
862 pushop.ui.warn(msg)
879 pushop.ui.warn(msg)
863 return handlereply
880 return handlereply
864
881
865 @b2partsgenerator('obsmarkers')
882 @b2partsgenerator('obsmarkers')
866 def _pushb2obsmarkers(pushop, bundler):
883 def _pushb2obsmarkers(pushop, bundler):
867 if 'obsmarkers' in pushop.stepsdone:
884 if 'obsmarkers' in pushop.stepsdone:
868 return
885 return
869 remoteversions = bundle2.obsmarkersversion(bundler.capabilities)
886 remoteversions = bundle2.obsmarkersversion(bundler.capabilities)
870 if obsolete.commonversion(remoteversions) is None:
887 if obsolete.commonversion(remoteversions) is None:
871 return
888 return
872 pushop.stepsdone.add('obsmarkers')
889 pushop.stepsdone.add('obsmarkers')
873 if pushop.outobsmarkers:
890 if pushop.outobsmarkers:
874 markers = sorted(pushop.outobsmarkers)
891 markers = sorted(pushop.outobsmarkers)
875 bundle2.buildobsmarkerspart(bundler, markers)
892 bundle2.buildobsmarkerspart(bundler, markers)
876
893
877 @b2partsgenerator('bookmarks')
894 @b2partsgenerator('bookmarks')
878 def _pushb2bookmarks(pushop, bundler):
895 def _pushb2bookmarks(pushop, bundler):
879 """handle bookmark push through bundle2"""
896 """handle bookmark push through bundle2"""
880 if 'bookmarks' in pushop.stepsdone:
897 if 'bookmarks' in pushop.stepsdone:
881 return
898 return
882 b2caps = bundle2.bundle2caps(pushop.remote)
899 b2caps = bundle2.bundle2caps(pushop.remote)
883 if 'pushkey' not in b2caps:
900 if 'pushkey' not in b2caps:
884 return
901 return
885 pushop.stepsdone.add('bookmarks')
902 pushop.stepsdone.add('bookmarks')
886 part2book = []
903 part2book = []
887 enc = pushkey.encode
904 enc = pushkey.encode
888
905
889 def handlefailure(pushop, exc):
906 def handlefailure(pushop, exc):
890 targetid = int(exc.partid)
907 targetid = int(exc.partid)
891 for partid, book, action in part2book:
908 for partid, book, action in part2book:
892 if partid == targetid:
909 if partid == targetid:
893 raise error.Abort(bookmsgmap[action][1].rstrip() % book)
910 raise error.Abort(bookmsgmap[action][1].rstrip() % book)
894 # we should not be called for part we did not generated
911 # we should not be called for part we did not generated
895 assert False
912 assert False
896
913
897 for book, old, new in pushop.outbookmarks:
914 for book, old, new in pushop.outbookmarks:
898 part = bundler.newpart('pushkey')
915 part = bundler.newpart('pushkey')
899 part.addparam('namespace', enc('bookmarks'))
916 part.addparam('namespace', enc('bookmarks'))
900 part.addparam('key', enc(book))
917 part.addparam('key', enc(book))
901 part.addparam('old', enc(old))
918 part.addparam('old', enc(old))
902 part.addparam('new', enc(new))
919 part.addparam('new', enc(new))
903 action = 'update'
920 action = 'update'
904 if not old:
921 if not old:
905 action = 'export'
922 action = 'export'
906 elif not new:
923 elif not new:
907 action = 'delete'
924 action = 'delete'
908 part2book.append((part.id, book, action))
925 part2book.append((part.id, book, action))
909 pushop.pkfailcb[part.id] = handlefailure
926 pushop.pkfailcb[part.id] = handlefailure
910
927
911 def handlereply(op):
928 def handlereply(op):
912 ui = pushop.ui
929 ui = pushop.ui
913 for partid, book, action in part2book:
930 for partid, book, action in part2book:
914 partrep = op.records.getreplies(partid)
931 partrep = op.records.getreplies(partid)
915 results = partrep['pushkey']
932 results = partrep['pushkey']
916 assert len(results) <= 1
933 assert len(results) <= 1
917 if not results:
934 if not results:
918 pushop.ui.warn(_('server ignored bookmark %s update\n') % book)
935 pushop.ui.warn(_('server ignored bookmark %s update\n') % book)
919 else:
936 else:
920 ret = int(results[0]['return'])
937 ret = int(results[0]['return'])
921 if ret:
938 if ret:
922 ui.status(bookmsgmap[action][0] % book)
939 ui.status(bookmsgmap[action][0] % book)
923 else:
940 else:
924 ui.warn(bookmsgmap[action][1] % book)
941 ui.warn(bookmsgmap[action][1] % book)
925 if pushop.bkresult is not None:
942 if pushop.bkresult is not None:
926 pushop.bkresult = 1
943 pushop.bkresult = 1
927 return handlereply
944 return handlereply
928
945
929 @b2partsgenerator('pushvars', idx=0)
946 @b2partsgenerator('pushvars', idx=0)
930 def _getbundlesendvars(pushop, bundler):
947 def _getbundlesendvars(pushop, bundler):
931 '''send shellvars via bundle2'''
948 '''send shellvars via bundle2'''
932 pushvars = pushop.pushvars
949 pushvars = pushop.pushvars
933 if pushvars:
950 if pushvars:
934 shellvars = {}
951 shellvars = {}
935 for raw in pushvars:
952 for raw in pushvars:
936 if '=' not in raw:
953 if '=' not in raw:
937 msg = ("unable to parse variable '%s', should follow "
954 msg = ("unable to parse variable '%s', should follow "
938 "'KEY=VALUE' or 'KEY=' format")
955 "'KEY=VALUE' or 'KEY=' format")
939 raise error.Abort(msg % raw)
956 raise error.Abort(msg % raw)
940 k, v = raw.split('=', 1)
957 k, v = raw.split('=', 1)
941 shellvars[k] = v
958 shellvars[k] = v
942
959
943 part = bundler.newpart('pushvars')
960 part = bundler.newpart('pushvars')
944
961
945 for key, value in shellvars.iteritems():
962 for key, value in shellvars.iteritems():
946 part.addparam(key, value, mandatory=False)
963 part.addparam(key, value, mandatory=False)
947
964
948 def _pushbundle2(pushop):
965 def _pushbundle2(pushop):
949 """push data to the remote using bundle2
966 """push data to the remote using bundle2
950
967
951 The only currently supported type of data is changegroup but this will
968 The only currently supported type of data is changegroup but this will
952 evolve in the future."""
969 evolve in the future."""
953 bundler = bundle2.bundle20(pushop.ui, bundle2.bundle2caps(pushop.remote))
970 bundler = bundle2.bundle20(pushop.ui, bundle2.bundle2caps(pushop.remote))
954 pushback = (pushop.trmanager
971 pushback = (pushop.trmanager
955 and pushop.ui.configbool('experimental', 'bundle2.pushback'))
972 and pushop.ui.configbool('experimental', 'bundle2.pushback'))
956
973
957 # create reply capability
974 # create reply capability
958 capsblob = bundle2.encodecaps(bundle2.getrepocaps(pushop.repo,
975 capsblob = bundle2.encodecaps(bundle2.getrepocaps(pushop.repo,
959 allowpushback=pushback))
976 allowpushback=pushback))
960 bundler.newpart('replycaps', data=capsblob)
977 bundler.newpart('replycaps', data=capsblob)
961 replyhandlers = []
978 replyhandlers = []
962 for partgenname in b2partsgenorder:
979 for partgenname in b2partsgenorder:
963 partgen = b2partsgenmapping[partgenname]
980 partgen = b2partsgenmapping[partgenname]
964 ret = partgen(pushop, bundler)
981 ret = partgen(pushop, bundler)
965 if callable(ret):
982 if callable(ret):
966 replyhandlers.append(ret)
983 replyhandlers.append(ret)
967 # do not push if nothing to push
984 # do not push if nothing to push
968 if bundler.nbparts <= 1:
985 if bundler.nbparts <= 1:
969 return
986 return
970 stream = util.chunkbuffer(bundler.getchunks())
987 stream = util.chunkbuffer(bundler.getchunks())
971 try:
988 try:
972 try:
989 try:
973 reply = pushop.remote.unbundle(
990 reply = pushop.remote.unbundle(
974 stream, ['force'], pushop.remote.url())
991 stream, ['force'], pushop.remote.url())
975 except error.BundleValueError as exc:
992 except error.BundleValueError as exc:
976 raise error.Abort(_('missing support for %s') % exc)
993 raise error.Abort(_('missing support for %s') % exc)
977 try:
994 try:
978 trgetter = None
995 trgetter = None
979 if pushback:
996 if pushback:
980 trgetter = pushop.trmanager.transaction
997 trgetter = pushop.trmanager.transaction
981 op = bundle2.processbundle(pushop.repo, reply, trgetter)
998 op = bundle2.processbundle(pushop.repo, reply, trgetter)
982 except error.BundleValueError as exc:
999 except error.BundleValueError as exc:
983 raise error.Abort(_('missing support for %s') % exc)
1000 raise error.Abort(_('missing support for %s') % exc)
984 except bundle2.AbortFromPart as exc:
1001 except bundle2.AbortFromPart as exc:
985 pushop.ui.status(_('remote: %s\n') % exc)
1002 pushop.ui.status(_('remote: %s\n') % exc)
986 if exc.hint is not None:
1003 if exc.hint is not None:
987 pushop.ui.status(_('remote: %s\n') % ('(%s)' % exc.hint))
1004 pushop.ui.status(_('remote: %s\n') % ('(%s)' % exc.hint))
988 raise error.Abort(_('push failed on remote'))
1005 raise error.Abort(_('push failed on remote'))
989 except error.PushkeyFailed as exc:
1006 except error.PushkeyFailed as exc:
990 partid = int(exc.partid)
1007 partid = int(exc.partid)
991 if partid not in pushop.pkfailcb:
1008 if partid not in pushop.pkfailcb:
992 raise
1009 raise
993 pushop.pkfailcb[partid](pushop, exc)
1010 pushop.pkfailcb[partid](pushop, exc)
994 for rephand in replyhandlers:
1011 for rephand in replyhandlers:
995 rephand(op)
1012 rephand(op)
996
1013
997 def _pushchangeset(pushop):
1014 def _pushchangeset(pushop):
998 """Make the actual push of changeset bundle to remote repo"""
1015 """Make the actual push of changeset bundle to remote repo"""
999 if 'changesets' in pushop.stepsdone:
1016 if 'changesets' in pushop.stepsdone:
1000 return
1017 return
1001 pushop.stepsdone.add('changesets')
1018 pushop.stepsdone.add('changesets')
1002 if not _pushcheckoutgoing(pushop):
1019 if not _pushcheckoutgoing(pushop):
1003 return
1020 return
1004
1021
1005 # Should have verified this in push().
1022 # Should have verified this in push().
1006 assert pushop.remote.capable('unbundle')
1023 assert pushop.remote.capable('unbundle')
1007
1024
1008 pushop.repo.prepushoutgoinghooks(pushop)
1025 pushop.repo.prepushoutgoinghooks(pushop)
1009 outgoing = pushop.outgoing
1026 outgoing = pushop.outgoing
1010 # TODO: get bundlecaps from remote
1027 # TODO: get bundlecaps from remote
1011 bundlecaps = None
1028 bundlecaps = None
1012 # create a changegroup from local
1029 # create a changegroup from local
1013 if pushop.revs is None and not (outgoing.excluded
1030 if pushop.revs is None and not (outgoing.excluded
1014 or pushop.repo.changelog.filteredrevs):
1031 or pushop.repo.changelog.filteredrevs):
1015 # push everything,
1032 # push everything,
1016 # use the fast path, no race possible on push
1033 # use the fast path, no race possible on push
1017 cg = changegroup.makechangegroup(pushop.repo, outgoing, '01', 'push',
1034 cg = changegroup.makechangegroup(pushop.repo, outgoing, '01', 'push',
1018 fastpath=True, bundlecaps=bundlecaps)
1035 fastpath=True, bundlecaps=bundlecaps)
1019 else:
1036 else:
1020 cg = changegroup.makechangegroup(pushop.repo, outgoing, '01',
1037 cg = changegroup.makechangegroup(pushop.repo, outgoing, '01',
1021 'push', bundlecaps=bundlecaps)
1038 'push', bundlecaps=bundlecaps)
1022
1039
1023 # apply changegroup to remote
1040 # apply changegroup to remote
1024 # local repo finds heads on server, finds out what
1041 # local repo finds heads on server, finds out what
1025 # revs it must push. once revs transferred, if server
1042 # revs it must push. once revs transferred, if server
1026 # finds it has different heads (someone else won
1043 # finds it has different heads (someone else won
1027 # commit/push race), server aborts.
1044 # commit/push race), server aborts.
1028 if pushop.force:
1045 if pushop.force:
1029 remoteheads = ['force']
1046 remoteheads = ['force']
1030 else:
1047 else:
1031 remoteheads = pushop.remoteheads
1048 remoteheads = pushop.remoteheads
1032 # ssh: return remote's addchangegroup()
1049 # ssh: return remote's addchangegroup()
1033 # http: return remote's addchangegroup() or 0 for error
1050 # http: return remote's addchangegroup() or 0 for error
1034 pushop.cgresult = pushop.remote.unbundle(cg, remoteheads,
1051 pushop.cgresult = pushop.remote.unbundle(cg, remoteheads,
1035 pushop.repo.url())
1052 pushop.repo.url())
1036
1053
1037 def _pushsyncphase(pushop):
1054 def _pushsyncphase(pushop):
1038 """synchronise phase information locally and remotely"""
1055 """synchronise phase information locally and remotely"""
1039 cheads = pushop.commonheads
1056 cheads = pushop.commonheads
1040 # even when we don't push, exchanging phase data is useful
1057 # even when we don't push, exchanging phase data is useful
1041 remotephases = pushop.remote.listkeys('phases')
1058 remotephases = pushop.remote.listkeys('phases')
1042 if (pushop.ui.configbool('ui', '_usedassubrepo')
1059 if (pushop.ui.configbool('ui', '_usedassubrepo')
1043 and remotephases # server supports phases
1060 and remotephases # server supports phases
1044 and pushop.cgresult is None # nothing was pushed
1061 and pushop.cgresult is None # nothing was pushed
1045 and remotephases.get('publishing', False)):
1062 and remotephases.get('publishing', False)):
1046 # When:
1063 # When:
1047 # - this is a subrepo push
1064 # - this is a subrepo push
1048 # - and remote support phase
1065 # - and remote support phase
1049 # - and no changeset was pushed
1066 # - and no changeset was pushed
1050 # - and remote is publishing
1067 # - and remote is publishing
1051 # We may be in issue 3871 case!
1068 # We may be in issue 3871 case!
1052 # We drop the possible phase synchronisation done by
1069 # We drop the possible phase synchronisation done by
1053 # courtesy to publish changesets possibly locally draft
1070 # courtesy to publish changesets possibly locally draft
1054 # on the remote.
1071 # on the remote.
1055 remotephases = {'publishing': 'True'}
1072 remotephases = {'publishing': 'True'}
1056 if not remotephases: # old server or public only reply from non-publishing
1073 if not remotephases: # old server or public only reply from non-publishing
1057 _localphasemove(pushop, cheads)
1074 _localphasemove(pushop, cheads)
1058 # don't push any phase data as there is nothing to push
1075 # don't push any phase data as there is nothing to push
1059 else:
1076 else:
1060 ana = phases.analyzeremotephases(pushop.repo, cheads,
1077 ana = phases.analyzeremotephases(pushop.repo, cheads,
1061 remotephases)
1078 remotephases)
1062 pheads, droots = ana
1079 pheads, droots = ana
1063 ### Apply remote phase on local
1080 ### Apply remote phase on local
1064 if remotephases.get('publishing', False):
1081 if remotephases.get('publishing', False):
1065 _localphasemove(pushop, cheads)
1082 _localphasemove(pushop, cheads)
1066 else: # publish = False
1083 else: # publish = False
1067 _localphasemove(pushop, pheads)
1084 _localphasemove(pushop, pheads)
1068 _localphasemove(pushop, cheads, phases.draft)
1085 _localphasemove(pushop, cheads, phases.draft)
1069 ### Apply local phase on remote
1086 ### Apply local phase on remote
1070
1087
1071 if pushop.cgresult:
1088 if pushop.cgresult:
1072 if 'phases' in pushop.stepsdone:
1089 if 'phases' in pushop.stepsdone:
1073 # phases already pushed though bundle2
1090 # phases already pushed though bundle2
1074 return
1091 return
1075 outdated = pushop.outdatedphases
1092 outdated = pushop.outdatedphases
1076 else:
1093 else:
1077 outdated = pushop.fallbackoutdatedphases
1094 outdated = pushop.fallbackoutdatedphases
1078
1095
1079 pushop.stepsdone.add('phases')
1096 pushop.stepsdone.add('phases')
1080
1097
1081 # filter heads already turned public by the push
1098 # filter heads already turned public by the push
1082 outdated = [c for c in outdated if c.node() not in pheads]
1099 outdated = [c for c in outdated if c.node() not in pheads]
1083 # fallback to independent pushkey command
1100 # fallback to independent pushkey command
1084 for newremotehead in outdated:
1101 for newremotehead in outdated:
1085 r = pushop.remote.pushkey('phases',
1102 r = pushop.remote.pushkey('phases',
1086 newremotehead.hex(),
1103 newremotehead.hex(),
1087 str(phases.draft),
1104 str(phases.draft),
1088 str(phases.public))
1105 str(phases.public))
1089 if not r:
1106 if not r:
1090 pushop.ui.warn(_('updating %s to public failed!\n')
1107 pushop.ui.warn(_('updating %s to public failed!\n')
1091 % newremotehead)
1108 % newremotehead)
1092
1109
1093 def _localphasemove(pushop, nodes, phase=phases.public):
1110 def _localphasemove(pushop, nodes, phase=phases.public):
1094 """move <nodes> to <phase> in the local source repo"""
1111 """move <nodes> to <phase> in the local source repo"""
1095 if pushop.trmanager:
1112 if pushop.trmanager:
1096 phases.advanceboundary(pushop.repo,
1113 phases.advanceboundary(pushop.repo,
1097 pushop.trmanager.transaction(),
1114 pushop.trmanager.transaction(),
1098 phase,
1115 phase,
1099 nodes)
1116 nodes)
1100 else:
1117 else:
1101 # repo is not locked, do not change any phases!
1118 # repo is not locked, do not change any phases!
1102 # Informs the user that phases should have been moved when
1119 # Informs the user that phases should have been moved when
1103 # applicable.
1120 # applicable.
1104 actualmoves = [n for n in nodes if phase < pushop.repo[n].phase()]
1121 actualmoves = [n for n in nodes if phase < pushop.repo[n].phase()]
1105 phasestr = phases.phasenames[phase]
1122 phasestr = phases.phasenames[phase]
1106 if actualmoves:
1123 if actualmoves:
1107 pushop.ui.status(_('cannot lock source repo, skipping '
1124 pushop.ui.status(_('cannot lock source repo, skipping '
1108 'local %s phase update\n') % phasestr)
1125 'local %s phase update\n') % phasestr)
1109
1126
1110 def _pushobsolete(pushop):
1127 def _pushobsolete(pushop):
1111 """utility function to push obsolete markers to a remote"""
1128 """utility function to push obsolete markers to a remote"""
1112 if 'obsmarkers' in pushop.stepsdone:
1129 if 'obsmarkers' in pushop.stepsdone:
1113 return
1130 return
1114 repo = pushop.repo
1131 repo = pushop.repo
1115 remote = pushop.remote
1132 remote = pushop.remote
1116 pushop.stepsdone.add('obsmarkers')
1133 pushop.stepsdone.add('obsmarkers')
1117 if pushop.outobsmarkers:
1134 if pushop.outobsmarkers:
1118 pushop.ui.debug('try to push obsolete markers to remote\n')
1135 pushop.ui.debug('try to push obsolete markers to remote\n')
1119 rslts = []
1136 rslts = []
1120 remotedata = obsolete._pushkeyescape(sorted(pushop.outobsmarkers))
1137 remotedata = obsolete._pushkeyescape(sorted(pushop.outobsmarkers))
1121 for key in sorted(remotedata, reverse=True):
1138 for key in sorted(remotedata, reverse=True):
1122 # reverse sort to ensure we end with dump0
1139 # reverse sort to ensure we end with dump0
1123 data = remotedata[key]
1140 data = remotedata[key]
1124 rslts.append(remote.pushkey('obsolete', key, '', data))
1141 rslts.append(remote.pushkey('obsolete', key, '', data))
1125 if [r for r in rslts if not r]:
1142 if [r for r in rslts if not r]:
1126 msg = _('failed to push some obsolete markers!\n')
1143 msg = _('failed to push some obsolete markers!\n')
1127 repo.ui.warn(msg)
1144 repo.ui.warn(msg)
1128
1145
1129 def _pushbookmark(pushop):
1146 def _pushbookmark(pushop):
1130 """Update bookmark position on remote"""
1147 """Update bookmark position on remote"""
1131 if pushop.cgresult == 0 or 'bookmarks' in pushop.stepsdone:
1148 if pushop.cgresult == 0 or 'bookmarks' in pushop.stepsdone:
1132 return
1149 return
1133 pushop.stepsdone.add('bookmarks')
1150 pushop.stepsdone.add('bookmarks')
1134 ui = pushop.ui
1151 ui = pushop.ui
1135 remote = pushop.remote
1152 remote = pushop.remote
1136
1153
1137 for b, old, new in pushop.outbookmarks:
1154 for b, old, new in pushop.outbookmarks:
1138 action = 'update'
1155 action = 'update'
1139 if not old:
1156 if not old:
1140 action = 'export'
1157 action = 'export'
1141 elif not new:
1158 elif not new:
1142 action = 'delete'
1159 action = 'delete'
1143 if remote.pushkey('bookmarks', b, old, new):
1160 if remote.pushkey('bookmarks', b, old, new):
1144 ui.status(bookmsgmap[action][0] % b)
1161 ui.status(bookmsgmap[action][0] % b)
1145 else:
1162 else:
1146 ui.warn(bookmsgmap[action][1] % b)
1163 ui.warn(bookmsgmap[action][1] % b)
1147 # discovery can have set the value form invalid entry
1164 # discovery can have set the value form invalid entry
1148 if pushop.bkresult is not None:
1165 if pushop.bkresult is not None:
1149 pushop.bkresult = 1
1166 pushop.bkresult = 1
1150
1167
1151 class pulloperation(object):
1168 class pulloperation(object):
1152 """A object that represent a single pull operation
1169 """A object that represent a single pull operation
1153
1170
1154 It purpose is to carry pull related state and very common operation.
1171 It purpose is to carry pull related state and very common operation.
1155
1172
1156 A new should be created at the beginning of each pull and discarded
1173 A new should be created at the beginning of each pull and discarded
1157 afterward.
1174 afterward.
1158 """
1175 """
1159
1176
1160 def __init__(self, repo, remote, heads=None, force=False, bookmarks=(),
1177 def __init__(self, repo, remote, heads=None, force=False, bookmarks=(),
1161 remotebookmarks=None, streamclonerequested=None):
1178 remotebookmarks=None, streamclonerequested=None):
1162 # repo we pull into
1179 # repo we pull into
1163 self.repo = repo
1180 self.repo = repo
1164 # repo we pull from
1181 # repo we pull from
1165 self.remote = remote
1182 self.remote = remote
1166 # revision we try to pull (None is "all")
1183 # revision we try to pull (None is "all")
1167 self.heads = heads
1184 self.heads = heads
1168 # bookmark pulled explicitly
1185 # bookmark pulled explicitly
1169 self.explicitbookmarks = [repo._bookmarks.expandname(bookmark)
1186 self.explicitbookmarks = [repo._bookmarks.expandname(bookmark)
1170 for bookmark in bookmarks]
1187 for bookmark in bookmarks]
1171 # do we force pull?
1188 # do we force pull?
1172 self.force = force
1189 self.force = force
1173 # whether a streaming clone was requested
1190 # whether a streaming clone was requested
1174 self.streamclonerequested = streamclonerequested
1191 self.streamclonerequested = streamclonerequested
1175 # transaction manager
1192 # transaction manager
1176 self.trmanager = None
1193 self.trmanager = None
1177 # set of common changeset between local and remote before pull
1194 # set of common changeset between local and remote before pull
1178 self.common = None
1195 self.common = None
1179 # set of pulled head
1196 # set of pulled head
1180 self.rheads = None
1197 self.rheads = None
1181 # list of missing changeset to fetch remotely
1198 # list of missing changeset to fetch remotely
1182 self.fetch = None
1199 self.fetch = None
1183 # remote bookmarks data
1200 # remote bookmarks data
1184 self.remotebookmarks = remotebookmarks
1201 self.remotebookmarks = remotebookmarks
1185 # result of changegroup pulling (used as return code by pull)
1202 # result of changegroup pulling (used as return code by pull)
1186 self.cgresult = None
1203 self.cgresult = None
1187 # list of step already done
1204 # list of step already done
1188 self.stepsdone = set()
1205 self.stepsdone = set()
1189 # Whether we attempted a clone from pre-generated bundles.
1206 # Whether we attempted a clone from pre-generated bundles.
1190 self.clonebundleattempted = False
1207 self.clonebundleattempted = False
1191
1208
1192 @util.propertycache
1209 @util.propertycache
1193 def pulledsubset(self):
1210 def pulledsubset(self):
1194 """heads of the set of changeset target by the pull"""
1211 """heads of the set of changeset target by the pull"""
1195 # compute target subset
1212 # compute target subset
1196 if self.heads is None:
1213 if self.heads is None:
1197 # We pulled every thing possible
1214 # We pulled every thing possible
1198 # sync on everything common
1215 # sync on everything common
1199 c = set(self.common)
1216 c = set(self.common)
1200 ret = list(self.common)
1217 ret = list(self.common)
1201 for n in self.rheads:
1218 for n in self.rheads:
1202 if n not in c:
1219 if n not in c:
1203 ret.append(n)
1220 ret.append(n)
1204 return ret
1221 return ret
1205 else:
1222 else:
1206 # We pulled a specific subset
1223 # We pulled a specific subset
1207 # sync on this subset
1224 # sync on this subset
1208 return self.heads
1225 return self.heads
1209
1226
1210 @util.propertycache
1227 @util.propertycache
1211 def canusebundle2(self):
1228 def canusebundle2(self):
1212 return not _forcebundle1(self)
1229 return not _forcebundle1(self)
1213
1230
1214 @util.propertycache
1231 @util.propertycache
1215 def remotebundle2caps(self):
1232 def remotebundle2caps(self):
1216 return bundle2.bundle2caps(self.remote)
1233 return bundle2.bundle2caps(self.remote)
1217
1234
1218 def gettransaction(self):
1235 def gettransaction(self):
1219 # deprecated; talk to trmanager directly
1236 # deprecated; talk to trmanager directly
1220 return self.trmanager.transaction()
1237 return self.trmanager.transaction()
1221
1238
1222 class transactionmanager(util.transactional):
1239 class transactionmanager(util.transactional):
1223 """An object to manage the life cycle of a transaction
1240 """An object to manage the life cycle of a transaction
1224
1241
1225 It creates the transaction on demand and calls the appropriate hooks when
1242 It creates the transaction on demand and calls the appropriate hooks when
1226 closing the transaction."""
1243 closing the transaction."""
1227 def __init__(self, repo, source, url):
1244 def __init__(self, repo, source, url):
1228 self.repo = repo
1245 self.repo = repo
1229 self.source = source
1246 self.source = source
1230 self.url = url
1247 self.url = url
1231 self._tr = None
1248 self._tr = None
1232
1249
1233 def transaction(self):
1250 def transaction(self):
1234 """Return an open transaction object, constructing if necessary"""
1251 """Return an open transaction object, constructing if necessary"""
1235 if not self._tr:
1252 if not self._tr:
1236 trname = '%s\n%s' % (self.source, util.hidepassword(self.url))
1253 trname = '%s\n%s' % (self.source, util.hidepassword(self.url))
1237 self._tr = self.repo.transaction(trname)
1254 self._tr = self.repo.transaction(trname)
1238 self._tr.hookargs['source'] = self.source
1255 self._tr.hookargs['source'] = self.source
1239 self._tr.hookargs['url'] = self.url
1256 self._tr.hookargs['url'] = self.url
1240 return self._tr
1257 return self._tr
1241
1258
1242 def close(self):
1259 def close(self):
1243 """close transaction if created"""
1260 """close transaction if created"""
1244 if self._tr is not None:
1261 if self._tr is not None:
1245 self._tr.close()
1262 self._tr.close()
1246
1263
1247 def release(self):
1264 def release(self):
1248 """release transaction if created"""
1265 """release transaction if created"""
1249 if self._tr is not None:
1266 if self._tr is not None:
1250 self._tr.release()
1267 self._tr.release()
1251
1268
1252 def pull(repo, remote, heads=None, force=False, bookmarks=(), opargs=None,
1269 def pull(repo, remote, heads=None, force=False, bookmarks=(), opargs=None,
1253 streamclonerequested=None):
1270 streamclonerequested=None):
1254 """Fetch repository data from a remote.
1271 """Fetch repository data from a remote.
1255
1272
1256 This is the main function used to retrieve data from a remote repository.
1273 This is the main function used to retrieve data from a remote repository.
1257
1274
1258 ``repo`` is the local repository to clone into.
1275 ``repo`` is the local repository to clone into.
1259 ``remote`` is a peer instance.
1276 ``remote`` is a peer instance.
1260 ``heads`` is an iterable of revisions we want to pull. ``None`` (the
1277 ``heads`` is an iterable of revisions we want to pull. ``None`` (the
1261 default) means to pull everything from the remote.
1278 default) means to pull everything from the remote.
1262 ``bookmarks`` is an iterable of bookmarks requesting to be pulled. By
1279 ``bookmarks`` is an iterable of bookmarks requesting to be pulled. By
1263 default, all remote bookmarks are pulled.
1280 default, all remote bookmarks are pulled.
1264 ``opargs`` are additional keyword arguments to pass to ``pulloperation``
1281 ``opargs`` are additional keyword arguments to pass to ``pulloperation``
1265 initialization.
1282 initialization.
1266 ``streamclonerequested`` is a boolean indicating whether a "streaming
1283 ``streamclonerequested`` is a boolean indicating whether a "streaming
1267 clone" is requested. A "streaming clone" is essentially a raw file copy
1284 clone" is requested. A "streaming clone" is essentially a raw file copy
1268 of revlogs from the server. This only works when the local repository is
1285 of revlogs from the server. This only works when the local repository is
1269 empty. The default value of ``None`` means to respect the server
1286 empty. The default value of ``None`` means to respect the server
1270 configuration for preferring stream clones.
1287 configuration for preferring stream clones.
1271
1288
1272 Returns the ``pulloperation`` created for this pull.
1289 Returns the ``pulloperation`` created for this pull.
1273 """
1290 """
1274 if opargs is None:
1291 if opargs is None:
1275 opargs = {}
1292 opargs = {}
1276 pullop = pulloperation(repo, remote, heads, force, bookmarks=bookmarks,
1293 pullop = pulloperation(repo, remote, heads, force, bookmarks=bookmarks,
1277 streamclonerequested=streamclonerequested, **opargs)
1294 streamclonerequested=streamclonerequested, **opargs)
1278
1295
1279 peerlocal = pullop.remote.local()
1296 peerlocal = pullop.remote.local()
1280 if peerlocal:
1297 if peerlocal:
1281 missing = set(peerlocal.requirements) - pullop.repo.supported
1298 missing = set(peerlocal.requirements) - pullop.repo.supported
1282 if missing:
1299 if missing:
1283 msg = _("required features are not"
1300 msg = _("required features are not"
1284 " supported in the destination:"
1301 " supported in the destination:"
1285 " %s") % (', '.join(sorted(missing)))
1302 " %s") % (', '.join(sorted(missing)))
1286 raise error.Abort(msg)
1303 raise error.Abort(msg)
1287
1304
1288 wlock = lock = None
1305 wlock = lock = None
1289 try:
1306 try:
1290 wlock = pullop.repo.wlock()
1307 wlock = pullop.repo.wlock()
1291 lock = pullop.repo.lock()
1308 lock = pullop.repo.lock()
1292 pullop.trmanager = transactionmanager(repo, 'pull', remote.url())
1309 pullop.trmanager = transactionmanager(repo, 'pull', remote.url())
1293 # This should ideally be in _pullbundle2(). However, it needs to run
1310 # This should ideally be in _pullbundle2(). However, it needs to run
1294 # before discovery to avoid extra work.
1311 # before discovery to avoid extra work.
1295 _maybeapplyclonebundle(pullop)
1312 _maybeapplyclonebundle(pullop)
1296 streamclone.maybeperformlegacystreamclone(pullop)
1313 streamclone.maybeperformlegacystreamclone(pullop)
1297 _pulldiscovery(pullop)
1314 _pulldiscovery(pullop)
1298 if pullop.canusebundle2:
1315 if pullop.canusebundle2:
1299 _pullbundle2(pullop)
1316 _pullbundle2(pullop)
1300 _pullchangeset(pullop)
1317 _pullchangeset(pullop)
1301 _pullphase(pullop)
1318 _pullphase(pullop)
1302 _pullbookmarks(pullop)
1319 _pullbookmarks(pullop)
1303 _pullobsolete(pullop)
1320 _pullobsolete(pullop)
1304 pullop.trmanager.close()
1321 pullop.trmanager.close()
1305 finally:
1322 finally:
1306 lockmod.release(pullop.trmanager, lock, wlock)
1323 lockmod.release(pullop.trmanager, lock, wlock)
1307
1324
1308 # storing remotenames
1325 # storing remotenames
1309 if repo.ui.configbool('experimental', 'remotenames'):
1326 if repo.ui.configbool('experimental', 'remotenames'):
1310 remotenames.pullremotenames(repo, remote)
1327 remotenames.pullremotenames(repo, remote)
1311
1328
1312 return pullop
1329 return pullop
1313
1330
1314 # list of steps to perform discovery before pull
1331 # list of steps to perform discovery before pull
1315 pulldiscoveryorder = []
1332 pulldiscoveryorder = []
1316
1333
1317 # Mapping between step name and function
1334 # Mapping between step name and function
1318 #
1335 #
1319 # This exists to help extensions wrap steps if necessary
1336 # This exists to help extensions wrap steps if necessary
1320 pulldiscoverymapping = {}
1337 pulldiscoverymapping = {}
1321
1338
1322 def pulldiscovery(stepname):
1339 def pulldiscovery(stepname):
1323 """decorator for function performing discovery before pull
1340 """decorator for function performing discovery before pull
1324
1341
1325 The function is added to the step -> function mapping and appended to the
1342 The function is added to the step -> function mapping and appended to the
1326 list of steps. Beware that decorated function will be added in order (this
1343 list of steps. Beware that decorated function will be added in order (this
1327 may matter).
1344 may matter).
1328
1345
1329 You can only use this decorator for a new step, if you want to wrap a step
1346 You can only use this decorator for a new step, if you want to wrap a step
1330 from an extension, change the pulldiscovery dictionary directly."""
1347 from an extension, change the pulldiscovery dictionary directly."""
1331 def dec(func):
1348 def dec(func):
1332 assert stepname not in pulldiscoverymapping
1349 assert stepname not in pulldiscoverymapping
1333 pulldiscoverymapping[stepname] = func
1350 pulldiscoverymapping[stepname] = func
1334 pulldiscoveryorder.append(stepname)
1351 pulldiscoveryorder.append(stepname)
1335 return func
1352 return func
1336 return dec
1353 return dec
1337
1354
1338 def _pulldiscovery(pullop):
1355 def _pulldiscovery(pullop):
1339 """Run all discovery steps"""
1356 """Run all discovery steps"""
1340 for stepname in pulldiscoveryorder:
1357 for stepname in pulldiscoveryorder:
1341 step = pulldiscoverymapping[stepname]
1358 step = pulldiscoverymapping[stepname]
1342 step(pullop)
1359 step(pullop)
1343
1360
1344 @pulldiscovery('b1:bookmarks')
1361 @pulldiscovery('b1:bookmarks')
1345 def _pullbookmarkbundle1(pullop):
1362 def _pullbookmarkbundle1(pullop):
1346 """fetch bookmark data in bundle1 case
1363 """fetch bookmark data in bundle1 case
1347
1364
1348 If not using bundle2, we have to fetch bookmarks before changeset
1365 If not using bundle2, we have to fetch bookmarks before changeset
1349 discovery to reduce the chance and impact of race conditions."""
1366 discovery to reduce the chance and impact of race conditions."""
1350 if pullop.remotebookmarks is not None:
1367 if pullop.remotebookmarks is not None:
1351 return
1368 return
1352 if pullop.canusebundle2 and 'listkeys' in pullop.remotebundle2caps:
1369 if pullop.canusebundle2 and 'listkeys' in pullop.remotebundle2caps:
1353 # all known bundle2 servers now support listkeys, but lets be nice with
1370 # all known bundle2 servers now support listkeys, but lets be nice with
1354 # new implementation.
1371 # new implementation.
1355 return
1372 return
1356 books = pullop.remote.listkeys('bookmarks')
1373 books = pullop.remote.listkeys('bookmarks')
1357 pullop.remotebookmarks = bookmod.unhexlifybookmarks(books)
1374 pullop.remotebookmarks = bookmod.unhexlifybookmarks(books)
1358
1375
1359
1376
1360 @pulldiscovery('changegroup')
1377 @pulldiscovery('changegroup')
1361 def _pulldiscoverychangegroup(pullop):
1378 def _pulldiscoverychangegroup(pullop):
1362 """discovery phase for the pull
1379 """discovery phase for the pull
1363
1380
1364 Current handle changeset discovery only, will change handle all discovery
1381 Current handle changeset discovery only, will change handle all discovery
1365 at some point."""
1382 at some point."""
1366 tmp = discovery.findcommonincoming(pullop.repo,
1383 tmp = discovery.findcommonincoming(pullop.repo,
1367 pullop.remote,
1384 pullop.remote,
1368 heads=pullop.heads,
1385 heads=pullop.heads,
1369 force=pullop.force)
1386 force=pullop.force)
1370 common, fetch, rheads = tmp
1387 common, fetch, rheads = tmp
1371 nm = pullop.repo.unfiltered().changelog.nodemap
1388 nm = pullop.repo.unfiltered().changelog.nodemap
1372 if fetch and rheads:
1389 if fetch and rheads:
1373 # If a remote heads is filtered locally, put in back in common.
1390 # If a remote heads is filtered locally, put in back in common.
1374 #
1391 #
1375 # This is a hackish solution to catch most of "common but locally
1392 # This is a hackish solution to catch most of "common but locally
1376 # hidden situation". We do not performs discovery on unfiltered
1393 # hidden situation". We do not performs discovery on unfiltered
1377 # repository because it end up doing a pathological amount of round
1394 # repository because it end up doing a pathological amount of round
1378 # trip for w huge amount of changeset we do not care about.
1395 # trip for w huge amount of changeset we do not care about.
1379 #
1396 #
1380 # If a set of such "common but filtered" changeset exist on the server
1397 # If a set of such "common but filtered" changeset exist on the server
1381 # but are not including a remote heads, we'll not be able to detect it,
1398 # but are not including a remote heads, we'll not be able to detect it,
1382 scommon = set(common)
1399 scommon = set(common)
1383 for n in rheads:
1400 for n in rheads:
1384 if n in nm:
1401 if n in nm:
1385 if n not in scommon:
1402 if n not in scommon:
1386 common.append(n)
1403 common.append(n)
1387 if set(rheads).issubset(set(common)):
1404 if set(rheads).issubset(set(common)):
1388 fetch = []
1405 fetch = []
1389 pullop.common = common
1406 pullop.common = common
1390 pullop.fetch = fetch
1407 pullop.fetch = fetch
1391 pullop.rheads = rheads
1408 pullop.rheads = rheads
1392
1409
1393 def _pullbundle2(pullop):
1410 def _pullbundle2(pullop):
1394 """pull data using bundle2
1411 """pull data using bundle2
1395
1412
1396 For now, the only supported data are changegroup."""
1413 For now, the only supported data are changegroup."""
1397 kwargs = {'bundlecaps': caps20to10(pullop.repo)}
1414 kwargs = {'bundlecaps': caps20to10(pullop.repo)}
1398
1415
1399 # At the moment we don't do stream clones over bundle2. If that is
1416 # At the moment we don't do stream clones over bundle2. If that is
1400 # implemented then here's where the check for that will go.
1417 # implemented then here's where the check for that will go.
1401 streaming = False
1418 streaming = False
1402
1419
1403 # pulling changegroup
1420 # pulling changegroup
1404 pullop.stepsdone.add('changegroup')
1421 pullop.stepsdone.add('changegroup')
1405
1422
1406 kwargs['common'] = pullop.common
1423 kwargs['common'] = pullop.common
1407 kwargs['heads'] = pullop.heads or pullop.rheads
1424 kwargs['heads'] = pullop.heads or pullop.rheads
1408 kwargs['cg'] = pullop.fetch
1425 kwargs['cg'] = pullop.fetch
1409
1426
1410 ui = pullop.repo.ui
1427 ui = pullop.repo.ui
1411 legacyphase = 'phases' in ui.configlist('devel', 'legacy.exchange')
1428 legacyphase = 'phases' in ui.configlist('devel', 'legacy.exchange')
1412 hasbinaryphase = 'heads' in pullop.remotebundle2caps.get('phases', ())
1429 hasbinaryphase = 'heads' in pullop.remotebundle2caps.get('phases', ())
1413 if (not legacyphase and hasbinaryphase):
1430 if (not legacyphase and hasbinaryphase):
1414 kwargs['phases'] = True
1431 kwargs['phases'] = True
1415 pullop.stepsdone.add('phases')
1432 pullop.stepsdone.add('phases')
1416
1433
1417 if 'listkeys' in pullop.remotebundle2caps:
1434 if 'listkeys' in pullop.remotebundle2caps:
1418 if 'phases' not in pullop.stepsdone:
1435 if 'phases' not in pullop.stepsdone:
1419 kwargs['listkeys'] = ['phases']
1436 kwargs['listkeys'] = ['phases']
1420 if pullop.remotebookmarks is None:
1437 if pullop.remotebookmarks is None:
1421 # make sure to always includes bookmark data when migrating
1438 # make sure to always includes bookmark data when migrating
1422 # `hg incoming --bundle` to using this function.
1439 # `hg incoming --bundle` to using this function.
1423 kwargs.setdefault('listkeys', []).append('bookmarks')
1440 kwargs.setdefault('listkeys', []).append('bookmarks')
1424
1441
1425 # If this is a full pull / clone and the server supports the clone bundles
1442 # If this is a full pull / clone and the server supports the clone bundles
1426 # feature, tell the server whether we attempted a clone bundle. The
1443 # feature, tell the server whether we attempted a clone bundle. The
1427 # presence of this flag indicates the client supports clone bundles. This
1444 # presence of this flag indicates the client supports clone bundles. This
1428 # will enable the server to treat clients that support clone bundles
1445 # will enable the server to treat clients that support clone bundles
1429 # differently from those that don't.
1446 # differently from those that don't.
1430 if (pullop.remote.capable('clonebundles')
1447 if (pullop.remote.capable('clonebundles')
1431 and pullop.heads is None and list(pullop.common) == [nullid]):
1448 and pullop.heads is None and list(pullop.common) == [nullid]):
1432 kwargs['cbattempted'] = pullop.clonebundleattempted
1449 kwargs['cbattempted'] = pullop.clonebundleattempted
1433
1450
1434 if streaming:
1451 if streaming:
1435 pullop.repo.ui.status(_('streaming all changes\n'))
1452 pullop.repo.ui.status(_('streaming all changes\n'))
1436 elif not pullop.fetch:
1453 elif not pullop.fetch:
1437 pullop.repo.ui.status(_("no changes found\n"))
1454 pullop.repo.ui.status(_("no changes found\n"))
1438 pullop.cgresult = 0
1455 pullop.cgresult = 0
1439 else:
1456 else:
1440 if pullop.heads is None and list(pullop.common) == [nullid]:
1457 if pullop.heads is None and list(pullop.common) == [nullid]:
1441 pullop.repo.ui.status(_("requesting all changes\n"))
1458 pullop.repo.ui.status(_("requesting all changes\n"))
1442 if obsolete.isenabled(pullop.repo, obsolete.exchangeopt):
1459 if obsolete.isenabled(pullop.repo, obsolete.exchangeopt):
1443 remoteversions = bundle2.obsmarkersversion(pullop.remotebundle2caps)
1460 remoteversions = bundle2.obsmarkersversion(pullop.remotebundle2caps)
1444 if obsolete.commonversion(remoteversions) is not None:
1461 if obsolete.commonversion(remoteversions) is not None:
1445 kwargs['obsmarkers'] = True
1462 kwargs['obsmarkers'] = True
1446 pullop.stepsdone.add('obsmarkers')
1463 pullop.stepsdone.add('obsmarkers')
1447 _pullbundle2extraprepare(pullop, kwargs)
1464 _pullbundle2extraprepare(pullop, kwargs)
1448 bundle = pullop.remote.getbundle('pull', **pycompat.strkwargs(kwargs))
1465 bundle = pullop.remote.getbundle('pull', **pycompat.strkwargs(kwargs))
1449 try:
1466 try:
1450 op = bundle2.processbundle(pullop.repo, bundle, pullop.gettransaction)
1467 op = bundle2.processbundle(pullop.repo, bundle, pullop.gettransaction)
1451 except bundle2.AbortFromPart as exc:
1468 except bundle2.AbortFromPart as exc:
1452 pullop.repo.ui.status(_('remote: abort: %s\n') % exc)
1469 pullop.repo.ui.status(_('remote: abort: %s\n') % exc)
1453 raise error.Abort(_('pull failed on remote'), hint=exc.hint)
1470 raise error.Abort(_('pull failed on remote'), hint=exc.hint)
1454 except error.BundleValueError as exc:
1471 except error.BundleValueError as exc:
1455 raise error.Abort(_('missing support for %s') % exc)
1472 raise error.Abort(_('missing support for %s') % exc)
1456
1473
1457 if pullop.fetch:
1474 if pullop.fetch:
1458 pullop.cgresult = bundle2.combinechangegroupresults(op)
1475 pullop.cgresult = bundle2.combinechangegroupresults(op)
1459
1476
1460 # processing phases change
1477 # processing phases change
1461 for namespace, value in op.records['listkeys']:
1478 for namespace, value in op.records['listkeys']:
1462 if namespace == 'phases':
1479 if namespace == 'phases':
1463 _pullapplyphases(pullop, value)
1480 _pullapplyphases(pullop, value)
1464
1481
1465 # processing bookmark update
1482 # processing bookmark update
1466 for namespace, value in op.records['listkeys']:
1483 for namespace, value in op.records['listkeys']:
1467 if namespace == 'bookmarks':
1484 if namespace == 'bookmarks':
1468 pullop.remotebookmarks = bookmod.unhexlifybookmarks(value)
1485 pullop.remotebookmarks = bookmod.unhexlifybookmarks(value)
1469
1486
1470 # bookmark data were either already there or pulled in the bundle
1487 # bookmark data were either already there or pulled in the bundle
1471 if pullop.remotebookmarks is not None:
1488 if pullop.remotebookmarks is not None:
1472 _pullbookmarks(pullop)
1489 _pullbookmarks(pullop)
1473
1490
1474 def _pullbundle2extraprepare(pullop, kwargs):
1491 def _pullbundle2extraprepare(pullop, kwargs):
1475 """hook function so that extensions can extend the getbundle call"""
1492 """hook function so that extensions can extend the getbundle call"""
1476
1493
1477 def _pullchangeset(pullop):
1494 def _pullchangeset(pullop):
1478 """pull changeset from unbundle into the local repo"""
1495 """pull changeset from unbundle into the local repo"""
1479 # We delay the open of the transaction as late as possible so we
1496 # We delay the open of the transaction as late as possible so we
1480 # don't open transaction for nothing or you break future useful
1497 # don't open transaction for nothing or you break future useful
1481 # rollback call
1498 # rollback call
1482 if 'changegroup' in pullop.stepsdone:
1499 if 'changegroup' in pullop.stepsdone:
1483 return
1500 return
1484 pullop.stepsdone.add('changegroup')
1501 pullop.stepsdone.add('changegroup')
1485 if not pullop.fetch:
1502 if not pullop.fetch:
1486 pullop.repo.ui.status(_("no changes found\n"))
1503 pullop.repo.ui.status(_("no changes found\n"))
1487 pullop.cgresult = 0
1504 pullop.cgresult = 0
1488 return
1505 return
1489 tr = pullop.gettransaction()
1506 tr = pullop.gettransaction()
1490 if pullop.heads is None and list(pullop.common) == [nullid]:
1507 if pullop.heads is None and list(pullop.common) == [nullid]:
1491 pullop.repo.ui.status(_("requesting all changes\n"))
1508 pullop.repo.ui.status(_("requesting all changes\n"))
1492 elif pullop.heads is None and pullop.remote.capable('changegroupsubset'):
1509 elif pullop.heads is None and pullop.remote.capable('changegroupsubset'):
1493 # issue1320, avoid a race if remote changed after discovery
1510 # issue1320, avoid a race if remote changed after discovery
1494 pullop.heads = pullop.rheads
1511 pullop.heads = pullop.rheads
1495
1512
1496 if pullop.remote.capable('getbundle'):
1513 if pullop.remote.capable('getbundle'):
1497 # TODO: get bundlecaps from remote
1514 # TODO: get bundlecaps from remote
1498 cg = pullop.remote.getbundle('pull', common=pullop.common,
1515 cg = pullop.remote.getbundle('pull', common=pullop.common,
1499 heads=pullop.heads or pullop.rheads)
1516 heads=pullop.heads or pullop.rheads)
1500 elif pullop.heads is None:
1517 elif pullop.heads is None:
1501 cg = pullop.remote.changegroup(pullop.fetch, 'pull')
1518 cg = pullop.remote.changegroup(pullop.fetch, 'pull')
1502 elif not pullop.remote.capable('changegroupsubset'):
1519 elif not pullop.remote.capable('changegroupsubset'):
1503 raise error.Abort(_("partial pull cannot be done because "
1520 raise error.Abort(_("partial pull cannot be done because "
1504 "other repository doesn't support "
1521 "other repository doesn't support "
1505 "changegroupsubset."))
1522 "changegroupsubset."))
1506 else:
1523 else:
1507 cg = pullop.remote.changegroupsubset(pullop.fetch, pullop.heads, 'pull')
1524 cg = pullop.remote.changegroupsubset(pullop.fetch, pullop.heads, 'pull')
1508 bundleop = bundle2.applybundle(pullop.repo, cg, tr, 'pull',
1525 bundleop = bundle2.applybundle(pullop.repo, cg, tr, 'pull',
1509 pullop.remote.url())
1526 pullop.remote.url())
1510 pullop.cgresult = bundle2.combinechangegroupresults(bundleop)
1527 pullop.cgresult = bundle2.combinechangegroupresults(bundleop)
1511
1528
1512 def _pullphase(pullop):
1529 def _pullphase(pullop):
1513 # Get remote phases data from remote
1530 # Get remote phases data from remote
1514 if 'phases' in pullop.stepsdone:
1531 if 'phases' in pullop.stepsdone:
1515 return
1532 return
1516 remotephases = pullop.remote.listkeys('phases')
1533 remotephases = pullop.remote.listkeys('phases')
1517 _pullapplyphases(pullop, remotephases)
1534 _pullapplyphases(pullop, remotephases)
1518
1535
1519 def _pullapplyphases(pullop, remotephases):
1536 def _pullapplyphases(pullop, remotephases):
1520 """apply phase movement from observed remote state"""
1537 """apply phase movement from observed remote state"""
1521 if 'phases' in pullop.stepsdone:
1538 if 'phases' in pullop.stepsdone:
1522 return
1539 return
1523 pullop.stepsdone.add('phases')
1540 pullop.stepsdone.add('phases')
1524 publishing = bool(remotephases.get('publishing', False))
1541 publishing = bool(remotephases.get('publishing', False))
1525 if remotephases and not publishing:
1542 if remotephases and not publishing:
1526 # remote is new and non-publishing
1543 # remote is new and non-publishing
1527 pheads, _dr = phases.analyzeremotephases(pullop.repo,
1544 pheads, _dr = phases.analyzeremotephases(pullop.repo,
1528 pullop.pulledsubset,
1545 pullop.pulledsubset,
1529 remotephases)
1546 remotephases)
1530 dheads = pullop.pulledsubset
1547 dheads = pullop.pulledsubset
1531 else:
1548 else:
1532 # Remote is old or publishing all common changesets
1549 # Remote is old or publishing all common changesets
1533 # should be seen as public
1550 # should be seen as public
1534 pheads = pullop.pulledsubset
1551 pheads = pullop.pulledsubset
1535 dheads = []
1552 dheads = []
1536 unfi = pullop.repo.unfiltered()
1553 unfi = pullop.repo.unfiltered()
1537 phase = unfi._phasecache.phase
1554 phase = unfi._phasecache.phase
1538 rev = unfi.changelog.nodemap.get
1555 rev = unfi.changelog.nodemap.get
1539 public = phases.public
1556 public = phases.public
1540 draft = phases.draft
1557 draft = phases.draft
1541
1558
1542 # exclude changesets already public locally and update the others
1559 # exclude changesets already public locally and update the others
1543 pheads = [pn for pn in pheads if phase(unfi, rev(pn)) > public]
1560 pheads = [pn for pn in pheads if phase(unfi, rev(pn)) > public]
1544 if pheads:
1561 if pheads:
1545 tr = pullop.gettransaction()
1562 tr = pullop.gettransaction()
1546 phases.advanceboundary(pullop.repo, tr, public, pheads)
1563 phases.advanceboundary(pullop.repo, tr, public, pheads)
1547
1564
1548 # exclude changesets already draft locally and update the others
1565 # exclude changesets already draft locally and update the others
1549 dheads = [pn for pn in dheads if phase(unfi, rev(pn)) > draft]
1566 dheads = [pn for pn in dheads if phase(unfi, rev(pn)) > draft]
1550 if dheads:
1567 if dheads:
1551 tr = pullop.gettransaction()
1568 tr = pullop.gettransaction()
1552 phases.advanceboundary(pullop.repo, tr, draft, dheads)
1569 phases.advanceboundary(pullop.repo, tr, draft, dheads)
1553
1570
1554 def _pullbookmarks(pullop):
1571 def _pullbookmarks(pullop):
1555 """process the remote bookmark information to update the local one"""
1572 """process the remote bookmark information to update the local one"""
1556 if 'bookmarks' in pullop.stepsdone:
1573 if 'bookmarks' in pullop.stepsdone:
1557 return
1574 return
1558 pullop.stepsdone.add('bookmarks')
1575 pullop.stepsdone.add('bookmarks')
1559 repo = pullop.repo
1576 repo = pullop.repo
1560 remotebookmarks = pullop.remotebookmarks
1577 remotebookmarks = pullop.remotebookmarks
1561 bookmod.updatefromremote(repo.ui, repo, remotebookmarks,
1578 bookmod.updatefromremote(repo.ui, repo, remotebookmarks,
1562 pullop.remote.url(),
1579 pullop.remote.url(),
1563 pullop.gettransaction,
1580 pullop.gettransaction,
1564 explicit=pullop.explicitbookmarks)
1581 explicit=pullop.explicitbookmarks)
1565
1582
1566 def _pullobsolete(pullop):
1583 def _pullobsolete(pullop):
1567 """utility function to pull obsolete markers from a remote
1584 """utility function to pull obsolete markers from a remote
1568
1585
1569 The `gettransaction` is function that return the pull transaction, creating
1586 The `gettransaction` is function that return the pull transaction, creating
1570 one if necessary. We return the transaction to inform the calling code that
1587 one if necessary. We return the transaction to inform the calling code that
1571 a new transaction have been created (when applicable).
1588 a new transaction have been created (when applicable).
1572
1589
1573 Exists mostly to allow overriding for experimentation purpose"""
1590 Exists mostly to allow overriding for experimentation purpose"""
1574 if 'obsmarkers' in pullop.stepsdone:
1591 if 'obsmarkers' in pullop.stepsdone:
1575 return
1592 return
1576 pullop.stepsdone.add('obsmarkers')
1593 pullop.stepsdone.add('obsmarkers')
1577 tr = None
1594 tr = None
1578 if obsolete.isenabled(pullop.repo, obsolete.exchangeopt):
1595 if obsolete.isenabled(pullop.repo, obsolete.exchangeopt):
1579 pullop.repo.ui.debug('fetching remote obsolete markers\n')
1596 pullop.repo.ui.debug('fetching remote obsolete markers\n')
1580 remoteobs = pullop.remote.listkeys('obsolete')
1597 remoteobs = pullop.remote.listkeys('obsolete')
1581 if 'dump0' in remoteobs:
1598 if 'dump0' in remoteobs:
1582 tr = pullop.gettransaction()
1599 tr = pullop.gettransaction()
1583 markers = []
1600 markers = []
1584 for key in sorted(remoteobs, reverse=True):
1601 for key in sorted(remoteobs, reverse=True):
1585 if key.startswith('dump'):
1602 if key.startswith('dump'):
1586 data = util.b85decode(remoteobs[key])
1603 data = util.b85decode(remoteobs[key])
1587 version, newmarks = obsolete._readmarkers(data)
1604 version, newmarks = obsolete._readmarkers(data)
1588 markers += newmarks
1605 markers += newmarks
1589 if markers:
1606 if markers:
1590 pullop.repo.obsstore.add(tr, markers)
1607 pullop.repo.obsstore.add(tr, markers)
1591 pullop.repo.invalidatevolatilesets()
1608 pullop.repo.invalidatevolatilesets()
1592 return tr
1609 return tr
1593
1610
1594 def caps20to10(repo):
1611 def caps20to10(repo):
1595 """return a set with appropriate options to use bundle20 during getbundle"""
1612 """return a set with appropriate options to use bundle20 during getbundle"""
1596 caps = {'HG20'}
1613 caps = {'HG20'}
1597 capsblob = bundle2.encodecaps(bundle2.getrepocaps(repo))
1614 capsblob = bundle2.encodecaps(bundle2.getrepocaps(repo))
1598 caps.add('bundle2=' + urlreq.quote(capsblob))
1615 caps.add('bundle2=' + urlreq.quote(capsblob))
1599 return caps
1616 return caps
1600
1617
1601 # List of names of steps to perform for a bundle2 for getbundle, order matters.
1618 # List of names of steps to perform for a bundle2 for getbundle, order matters.
1602 getbundle2partsorder = []
1619 getbundle2partsorder = []
1603
1620
1604 # Mapping between step name and function
1621 # Mapping between step name and function
1605 #
1622 #
1606 # This exists to help extensions wrap steps if necessary
1623 # This exists to help extensions wrap steps if necessary
1607 getbundle2partsmapping = {}
1624 getbundle2partsmapping = {}
1608
1625
1609 def getbundle2partsgenerator(stepname, idx=None):
1626 def getbundle2partsgenerator(stepname, idx=None):
1610 """decorator for function generating bundle2 part for getbundle
1627 """decorator for function generating bundle2 part for getbundle
1611
1628
1612 The function is added to the step -> function mapping and appended to the
1629 The function is added to the step -> function mapping and appended to the
1613 list of steps. Beware that decorated functions will be added in order
1630 list of steps. Beware that decorated functions will be added in order
1614 (this may matter).
1631 (this may matter).
1615
1632
1616 You can only use this decorator for new steps, if you want to wrap a step
1633 You can only use this decorator for new steps, if you want to wrap a step
1617 from an extension, attack the getbundle2partsmapping dictionary directly."""
1634 from an extension, attack the getbundle2partsmapping dictionary directly."""
1618 def dec(func):
1635 def dec(func):
1619 assert stepname not in getbundle2partsmapping
1636 assert stepname not in getbundle2partsmapping
1620 getbundle2partsmapping[stepname] = func
1637 getbundle2partsmapping[stepname] = func
1621 if idx is None:
1638 if idx is None:
1622 getbundle2partsorder.append(stepname)
1639 getbundle2partsorder.append(stepname)
1623 else:
1640 else:
1624 getbundle2partsorder.insert(idx, stepname)
1641 getbundle2partsorder.insert(idx, stepname)
1625 return func
1642 return func
1626 return dec
1643 return dec
1627
1644
1628 def bundle2requested(bundlecaps):
1645 def bundle2requested(bundlecaps):
1629 if bundlecaps is not None:
1646 if bundlecaps is not None:
1630 return any(cap.startswith('HG2') for cap in bundlecaps)
1647 return any(cap.startswith('HG2') for cap in bundlecaps)
1631 return False
1648 return False
1632
1649
1633 def getbundlechunks(repo, source, heads=None, common=None, bundlecaps=None,
1650 def getbundlechunks(repo, source, heads=None, common=None, bundlecaps=None,
1634 **kwargs):
1651 **kwargs):
1635 """Return chunks constituting a bundle's raw data.
1652 """Return chunks constituting a bundle's raw data.
1636
1653
1637 Could be a bundle HG10 or a bundle HG20 depending on bundlecaps
1654 Could be a bundle HG10 or a bundle HG20 depending on bundlecaps
1638 passed.
1655 passed.
1639
1656
1640 Returns an iterator over raw chunks (of varying sizes).
1657 Returns an iterator over raw chunks (of varying sizes).
1641 """
1658 """
1642 kwargs = pycompat.byteskwargs(kwargs)
1659 kwargs = pycompat.byteskwargs(kwargs)
1643 usebundle2 = bundle2requested(bundlecaps)
1660 usebundle2 = bundle2requested(bundlecaps)
1644 # bundle10 case
1661 # bundle10 case
1645 if not usebundle2:
1662 if not usebundle2:
1646 if bundlecaps and not kwargs.get('cg', True):
1663 if bundlecaps and not kwargs.get('cg', True):
1647 raise ValueError(_('request for bundle10 must include changegroup'))
1664 raise ValueError(_('request for bundle10 must include changegroup'))
1648
1665
1649 if kwargs:
1666 if kwargs:
1650 raise ValueError(_('unsupported getbundle arguments: %s')
1667 raise ValueError(_('unsupported getbundle arguments: %s')
1651 % ', '.join(sorted(kwargs.keys())))
1668 % ', '.join(sorted(kwargs.keys())))
1652 outgoing = _computeoutgoing(repo, heads, common)
1669 outgoing = _computeoutgoing(repo, heads, common)
1653 return changegroup.makestream(repo, outgoing, '01', source,
1670 return changegroup.makestream(repo, outgoing, '01', source,
1654 bundlecaps=bundlecaps)
1671 bundlecaps=bundlecaps)
1655
1672
1656 # bundle20 case
1673 # bundle20 case
1657 b2caps = {}
1674 b2caps = {}
1658 for bcaps in bundlecaps:
1675 for bcaps in bundlecaps:
1659 if bcaps.startswith('bundle2='):
1676 if bcaps.startswith('bundle2='):
1660 blob = urlreq.unquote(bcaps[len('bundle2='):])
1677 blob = urlreq.unquote(bcaps[len('bundle2='):])
1661 b2caps.update(bundle2.decodecaps(blob))
1678 b2caps.update(bundle2.decodecaps(blob))
1662 bundler = bundle2.bundle20(repo.ui, b2caps)
1679 bundler = bundle2.bundle20(repo.ui, b2caps)
1663
1680
1664 kwargs['heads'] = heads
1681 kwargs['heads'] = heads
1665 kwargs['common'] = common
1682 kwargs['common'] = common
1666
1683
1667 for name in getbundle2partsorder:
1684 for name in getbundle2partsorder:
1668 func = getbundle2partsmapping[name]
1685 func = getbundle2partsmapping[name]
1669 func(bundler, repo, source, bundlecaps=bundlecaps, b2caps=b2caps,
1686 func(bundler, repo, source, bundlecaps=bundlecaps, b2caps=b2caps,
1670 **pycompat.strkwargs(kwargs))
1687 **pycompat.strkwargs(kwargs))
1671
1688
1672 return bundler.getchunks()
1689 return bundler.getchunks()
1673
1690
1674 @getbundle2partsgenerator('changegroup')
1691 @getbundle2partsgenerator('changegroup')
1675 def _getbundlechangegrouppart(bundler, repo, source, bundlecaps=None,
1692 def _getbundlechangegrouppart(bundler, repo, source, bundlecaps=None,
1676 b2caps=None, heads=None, common=None, **kwargs):
1693 b2caps=None, heads=None, common=None, **kwargs):
1677 """add a changegroup part to the requested bundle"""
1694 """add a changegroup part to the requested bundle"""
1678 cgstream = None
1695 cgstream = None
1679 if kwargs.get('cg', True):
1696 if kwargs.get('cg', True):
1680 # build changegroup bundle here.
1697 # build changegroup bundle here.
1681 version = '01'
1698 version = '01'
1682 cgversions = b2caps.get('changegroup')
1699 cgversions = b2caps.get('changegroup')
1683 if cgversions: # 3.1 and 3.2 ship with an empty value
1700 if cgversions: # 3.1 and 3.2 ship with an empty value
1684 cgversions = [v for v in cgversions
1701 cgversions = [v for v in cgversions
1685 if v in changegroup.supportedoutgoingversions(repo)]
1702 if v in changegroup.supportedoutgoingversions(repo)]
1686 if not cgversions:
1703 if not cgversions:
1687 raise ValueError(_('no common changegroup version'))
1704 raise ValueError(_('no common changegroup version'))
1688 version = max(cgversions)
1705 version = max(cgversions)
1689 outgoing = _computeoutgoing(repo, heads, common)
1706 outgoing = _computeoutgoing(repo, heads, common)
1690 if outgoing.missing:
1707 if outgoing.missing:
1691 cgstream = changegroup.makestream(repo, outgoing, version, source,
1708 cgstream = changegroup.makestream(repo, outgoing, version, source,
1692 bundlecaps=bundlecaps)
1709 bundlecaps=bundlecaps)
1693
1710
1694 if cgstream:
1711 if cgstream:
1695 part = bundler.newpart('changegroup', data=cgstream)
1712 part = bundler.newpart('changegroup', data=cgstream)
1696 if cgversions:
1713 if cgversions:
1697 part.addparam('version', version)
1714 part.addparam('version', version)
1698 part.addparam('nbchanges', '%d' % len(outgoing.missing),
1715 part.addparam('nbchanges', '%d' % len(outgoing.missing),
1699 mandatory=False)
1716 mandatory=False)
1700 if 'treemanifest' in repo.requirements:
1717 if 'treemanifest' in repo.requirements:
1701 part.addparam('treemanifest', '1')
1718 part.addparam('treemanifest', '1')
1702
1719
1703 @getbundle2partsgenerator('listkeys')
1720 @getbundle2partsgenerator('listkeys')
1704 def _getbundlelistkeysparts(bundler, repo, source, bundlecaps=None,
1721 def _getbundlelistkeysparts(bundler, repo, source, bundlecaps=None,
1705 b2caps=None, **kwargs):
1722 b2caps=None, **kwargs):
1706 """add parts containing listkeys namespaces to the requested bundle"""
1723 """add parts containing listkeys namespaces to the requested bundle"""
1707 listkeys = kwargs.get('listkeys', ())
1724 listkeys = kwargs.get('listkeys', ())
1708 for namespace in listkeys:
1725 for namespace in listkeys:
1709 part = bundler.newpart('listkeys')
1726 part = bundler.newpart('listkeys')
1710 part.addparam('namespace', namespace)
1727 part.addparam('namespace', namespace)
1711 keys = repo.listkeys(namespace).items()
1728 keys = repo.listkeys(namespace).items()
1712 part.data = pushkey.encodekeys(keys)
1729 part.data = pushkey.encodekeys(keys)
1713
1730
1714 @getbundle2partsgenerator('obsmarkers')
1731 @getbundle2partsgenerator('obsmarkers')
1715 def _getbundleobsmarkerpart(bundler, repo, source, bundlecaps=None,
1732 def _getbundleobsmarkerpart(bundler, repo, source, bundlecaps=None,
1716 b2caps=None, heads=None, **kwargs):
1733 b2caps=None, heads=None, **kwargs):
1717 """add an obsolescence markers part to the requested bundle"""
1734 """add an obsolescence markers part to the requested bundle"""
1718 if kwargs.get('obsmarkers', False):
1735 if kwargs.get('obsmarkers', False):
1719 if heads is None:
1736 if heads is None:
1720 heads = repo.heads()
1737 heads = repo.heads()
1721 subset = [c.node() for c in repo.set('::%ln', heads)]
1738 subset = [c.node() for c in repo.set('::%ln', heads)]
1722 markers = repo.obsstore.relevantmarkers(subset)
1739 markers = repo.obsstore.relevantmarkers(subset)
1723 markers = sorted(markers)
1740 markers = sorted(markers)
1724 bundle2.buildobsmarkerspart(bundler, markers)
1741 bundle2.buildobsmarkerspart(bundler, markers)
1725
1742
1726 @getbundle2partsgenerator('phases')
1743 @getbundle2partsgenerator('phases')
1727 def _getbundlephasespart(bundler, repo, source, bundlecaps=None,
1744 def _getbundlephasespart(bundler, repo, source, bundlecaps=None,
1728 b2caps=None, heads=None, **kwargs):
1745 b2caps=None, heads=None, **kwargs):
1729 """add phase heads part to the requested bundle"""
1746 """add phase heads part to the requested bundle"""
1730 if kwargs.get('phases', False):
1747 if kwargs.get('phases', False):
1731 if not 'heads' in b2caps.get('phases'):
1748 if not 'heads' in b2caps.get('phases'):
1732 raise ValueError(_('no common phases exchange method'))
1749 raise ValueError(_('no common phases exchange method'))
1733 if heads is None:
1750 if heads is None:
1734 heads = repo.heads()
1751 heads = repo.heads()
1735
1752
1736 headsbyphase = collections.defaultdict(set)
1753 headsbyphase = collections.defaultdict(set)
1737 if repo.publishing():
1754 if repo.publishing():
1738 headsbyphase[phases.public] = heads
1755 headsbyphase[phases.public] = heads
1739 else:
1756 else:
1740 # find the appropriate heads to move
1757 # find the appropriate heads to move
1741
1758
1742 phase = repo._phasecache.phase
1759 phase = repo._phasecache.phase
1743 node = repo.changelog.node
1760 node = repo.changelog.node
1744 rev = repo.changelog.rev
1761 rev = repo.changelog.rev
1745 for h in heads:
1762 for h in heads:
1746 headsbyphase[phase(repo, rev(h))].add(h)
1763 headsbyphase[phase(repo, rev(h))].add(h)
1747 seenphases = list(headsbyphase.keys())
1764 seenphases = list(headsbyphase.keys())
1748
1765
1749 # We do not handle anything but public and draft phase for now)
1766 # We do not handle anything but public and draft phase for now)
1750 if seenphases:
1767 if seenphases:
1751 assert max(seenphases) <= phases.draft
1768 assert max(seenphases) <= phases.draft
1752
1769
1753 # if client is pulling non-public changesets, we need to find
1770 # if client is pulling non-public changesets, we need to find
1754 # intermediate public heads.
1771 # intermediate public heads.
1755 draftheads = headsbyphase.get(phases.draft, set())
1772 draftheads = headsbyphase.get(phases.draft, set())
1756 if draftheads:
1773 if draftheads:
1757 publicheads = headsbyphase.get(phases.public, set())
1774 publicheads = headsbyphase.get(phases.public, set())
1758
1775
1759 revset = 'heads(only(%ln, %ln) and public())'
1776 revset = 'heads(only(%ln, %ln) and public())'
1760 extraheads = repo.revs(revset, draftheads, publicheads)
1777 extraheads = repo.revs(revset, draftheads, publicheads)
1761 for r in extraheads:
1778 for r in extraheads:
1762 headsbyphase[phases.public].add(node(r))
1779 headsbyphase[phases.public].add(node(r))
1763
1780
1764 # transform data in a format used by the encoding function
1781 # transform data in a format used by the encoding function
1765 phasemapping = []
1782 phasemapping = []
1766 for phase in phases.allphases:
1783 for phase in phases.allphases:
1767 phasemapping.append(sorted(headsbyphase[phase]))
1784 phasemapping.append(sorted(headsbyphase[phase]))
1768
1785
1769 # generate the actual part
1786 # generate the actual part
1770 phasedata = phases.binaryencode(phasemapping)
1787 phasedata = phases.binaryencode(phasemapping)
1771 bundler.newpart('phase-heads', data=phasedata)
1788 bundler.newpart('phase-heads', data=phasedata)
1772
1789
1773 @getbundle2partsgenerator('hgtagsfnodes')
1790 @getbundle2partsgenerator('hgtagsfnodes')
1774 def _getbundletagsfnodes(bundler, repo, source, bundlecaps=None,
1791 def _getbundletagsfnodes(bundler, repo, source, bundlecaps=None,
1775 b2caps=None, heads=None, common=None,
1792 b2caps=None, heads=None, common=None,
1776 **kwargs):
1793 **kwargs):
1777 """Transfer the .hgtags filenodes mapping.
1794 """Transfer the .hgtags filenodes mapping.
1778
1795
1779 Only values for heads in this bundle will be transferred.
1796 Only values for heads in this bundle will be transferred.
1780
1797
1781 The part data consists of pairs of 20 byte changeset node and .hgtags
1798 The part data consists of pairs of 20 byte changeset node and .hgtags
1782 filenodes raw values.
1799 filenodes raw values.
1783 """
1800 """
1784 # Don't send unless:
1801 # Don't send unless:
1785 # - changeset are being exchanged,
1802 # - changeset are being exchanged,
1786 # - the client supports it.
1803 # - the client supports it.
1787 if not (kwargs.get('cg', True) and 'hgtagsfnodes' in b2caps):
1804 if not (kwargs.get('cg', True) and 'hgtagsfnodes' in b2caps):
1788 return
1805 return
1789
1806
1790 outgoing = _computeoutgoing(repo, heads, common)
1807 outgoing = _computeoutgoing(repo, heads, common)
1791 bundle2.addparttagsfnodescache(repo, bundler, outgoing)
1808 bundle2.addparttagsfnodescache(repo, bundler, outgoing)
1792
1809
1793 def check_heads(repo, their_heads, context):
1810 def check_heads(repo, their_heads, context):
1794 """check if the heads of a repo have been modified
1811 """check if the heads of a repo have been modified
1795
1812
1796 Used by peer for unbundling.
1813 Used by peer for unbundling.
1797 """
1814 """
1798 heads = repo.heads()
1815 heads = repo.heads()
1799 heads_hash = hashlib.sha1(''.join(sorted(heads))).digest()
1816 heads_hash = hashlib.sha1(''.join(sorted(heads))).digest()
1800 if not (their_heads == ['force'] or their_heads == heads or
1817 if not (their_heads == ['force'] or their_heads == heads or
1801 their_heads == ['hashed', heads_hash]):
1818 their_heads == ['hashed', heads_hash]):
1802 # someone else committed/pushed/unbundled while we
1819 # someone else committed/pushed/unbundled while we
1803 # were transferring data
1820 # were transferring data
1804 raise error.PushRaced('repository changed while %s - '
1821 raise error.PushRaced('repository changed while %s - '
1805 'please try again' % context)
1822 'please try again' % context)
1806
1823
1807 def unbundle(repo, cg, heads, source, url):
1824 def unbundle(repo, cg, heads, source, url):
1808 """Apply a bundle to a repo.
1825 """Apply a bundle to a repo.
1809
1826
1810 this function makes sure the repo is locked during the application and have
1827 this function makes sure the repo is locked during the application and have
1811 mechanism to check that no push race occurred between the creation of the
1828 mechanism to check that no push race occurred between the creation of the
1812 bundle and its application.
1829 bundle and its application.
1813
1830
1814 If the push was raced as PushRaced exception is raised."""
1831 If the push was raced as PushRaced exception is raised."""
1815 r = 0
1832 r = 0
1816 # need a transaction when processing a bundle2 stream
1833 # need a transaction when processing a bundle2 stream
1817 # [wlock, lock, tr] - needs to be an array so nested functions can modify it
1834 # [wlock, lock, tr] - needs to be an array so nested functions can modify it
1818 lockandtr = [None, None, None]
1835 lockandtr = [None, None, None]
1819 recordout = None
1836 recordout = None
1820 # quick fix for output mismatch with bundle2 in 3.4
1837 # quick fix for output mismatch with bundle2 in 3.4
1821 captureoutput = repo.ui.configbool('experimental', 'bundle2-output-capture')
1838 captureoutput = repo.ui.configbool('experimental', 'bundle2-output-capture')
1822 if url.startswith('remote:http:') or url.startswith('remote:https:'):
1839 if url.startswith('remote:http:') or url.startswith('remote:https:'):
1823 captureoutput = True
1840 captureoutput = True
1824 try:
1841 try:
1825 # note: outside bundle1, 'heads' is expected to be empty and this
1842 # note: outside bundle1, 'heads' is expected to be empty and this
1826 # 'check_heads' call wil be a no-op
1843 # 'check_heads' call wil be a no-op
1827 check_heads(repo, heads, 'uploading changes')
1844 check_heads(repo, heads, 'uploading changes')
1828 # push can proceed
1845 # push can proceed
1829 if not isinstance(cg, bundle2.unbundle20):
1846 if not isinstance(cg, bundle2.unbundle20):
1830 # legacy case: bundle1 (changegroup 01)
1847 # legacy case: bundle1 (changegroup 01)
1831 txnname = "\n".join([source, util.hidepassword(url)])
1848 txnname = "\n".join([source, util.hidepassword(url)])
1832 with repo.lock(), repo.transaction(txnname) as tr:
1849 with repo.lock(), repo.transaction(txnname) as tr:
1833 op = bundle2.applybundle(repo, cg, tr, source, url)
1850 op = bundle2.applybundle(repo, cg, tr, source, url)
1834 r = bundle2.combinechangegroupresults(op)
1851 r = bundle2.combinechangegroupresults(op)
1835 else:
1852 else:
1836 r = None
1853 r = None
1837 try:
1854 try:
1838 def gettransaction():
1855 def gettransaction():
1839 if not lockandtr[2]:
1856 if not lockandtr[2]:
1840 lockandtr[0] = repo.wlock()
1857 lockandtr[0] = repo.wlock()
1841 lockandtr[1] = repo.lock()
1858 lockandtr[1] = repo.lock()
1842 lockandtr[2] = repo.transaction(source)
1859 lockandtr[2] = repo.transaction(source)
1843 lockandtr[2].hookargs['source'] = source
1860 lockandtr[2].hookargs['source'] = source
1844 lockandtr[2].hookargs['url'] = url
1861 lockandtr[2].hookargs['url'] = url
1845 lockandtr[2].hookargs['bundle2'] = '1'
1862 lockandtr[2].hookargs['bundle2'] = '1'
1846 return lockandtr[2]
1863 return lockandtr[2]
1847
1864
1848 # Do greedy locking by default until we're satisfied with lazy
1865 # Do greedy locking by default until we're satisfied with lazy
1849 # locking.
1866 # locking.
1850 if not repo.ui.configbool('experimental', 'bundle2lazylocking'):
1867 if not repo.ui.configbool('experimental', 'bundle2lazylocking'):
1851 gettransaction()
1868 gettransaction()
1852
1869
1853 op = bundle2.bundleoperation(repo, gettransaction,
1870 op = bundle2.bundleoperation(repo, gettransaction,
1854 captureoutput=captureoutput)
1871 captureoutput=captureoutput)
1855 try:
1872 try:
1856 op = bundle2.processbundle(repo, cg, op=op)
1873 op = bundle2.processbundle(repo, cg, op=op)
1857 finally:
1874 finally:
1858 r = op.reply
1875 r = op.reply
1859 if captureoutput and r is not None:
1876 if captureoutput and r is not None:
1860 repo.ui.pushbuffer(error=True, subproc=True)
1877 repo.ui.pushbuffer(error=True, subproc=True)
1861 def recordout(output):
1878 def recordout(output):
1862 r.newpart('output', data=output, mandatory=False)
1879 r.newpart('output', data=output, mandatory=False)
1863 if lockandtr[2] is not None:
1880 if lockandtr[2] is not None:
1864 lockandtr[2].close()
1881 lockandtr[2].close()
1865 except BaseException as exc:
1882 except BaseException as exc:
1866 exc.duringunbundle2 = True
1883 exc.duringunbundle2 = True
1867 if captureoutput and r is not None:
1884 if captureoutput and r is not None:
1868 parts = exc._bundle2salvagedoutput = r.salvageoutput()
1885 parts = exc._bundle2salvagedoutput = r.salvageoutput()
1869 def recordout(output):
1886 def recordout(output):
1870 part = bundle2.bundlepart('output', data=output,
1887 part = bundle2.bundlepart('output', data=output,
1871 mandatory=False)
1888 mandatory=False)
1872 parts.append(part)
1889 parts.append(part)
1873 raise
1890 raise
1874 finally:
1891 finally:
1875 lockmod.release(lockandtr[2], lockandtr[1], lockandtr[0])
1892 lockmod.release(lockandtr[2], lockandtr[1], lockandtr[0])
1876 if recordout is not None:
1893 if recordout is not None:
1877 recordout(repo.ui.popbuffer())
1894 recordout(repo.ui.popbuffer())
1878 return r
1895 return r
1879
1896
1880 def _maybeapplyclonebundle(pullop):
1897 def _maybeapplyclonebundle(pullop):
1881 """Apply a clone bundle from a remote, if possible."""
1898 """Apply a clone bundle from a remote, if possible."""
1882
1899
1883 repo = pullop.repo
1900 repo = pullop.repo
1884 remote = pullop.remote
1901 remote = pullop.remote
1885
1902
1886 if not repo.ui.configbool('ui', 'clonebundles'):
1903 if not repo.ui.configbool('ui', 'clonebundles'):
1887 return
1904 return
1888
1905
1889 # Only run if local repo is empty.
1906 # Only run if local repo is empty.
1890 if len(repo):
1907 if len(repo):
1891 return
1908 return
1892
1909
1893 if pullop.heads:
1910 if pullop.heads:
1894 return
1911 return
1895
1912
1896 if not remote.capable('clonebundles'):
1913 if not remote.capable('clonebundles'):
1897 return
1914 return
1898
1915
1899 res = remote._call('clonebundles')
1916 res = remote._call('clonebundles')
1900
1917
1901 # If we call the wire protocol command, that's good enough to record the
1918 # If we call the wire protocol command, that's good enough to record the
1902 # attempt.
1919 # attempt.
1903 pullop.clonebundleattempted = True
1920 pullop.clonebundleattempted = True
1904
1921
1905 entries = parseclonebundlesmanifest(repo, res)
1922 entries = parseclonebundlesmanifest(repo, res)
1906 if not entries:
1923 if not entries:
1907 repo.ui.note(_('no clone bundles available on remote; '
1924 repo.ui.note(_('no clone bundles available on remote; '
1908 'falling back to regular clone\n'))
1925 'falling back to regular clone\n'))
1909 return
1926 return
1910
1927
1911 entries = filterclonebundleentries(
1928 entries = filterclonebundleentries(
1912 repo, entries, streamclonerequested=pullop.streamclonerequested)
1929 repo, entries, streamclonerequested=pullop.streamclonerequested)
1913
1930
1914 if not entries:
1931 if not entries:
1915 # There is a thundering herd concern here. However, if a server
1932 # There is a thundering herd concern here. However, if a server
1916 # operator doesn't advertise bundles appropriate for its clients,
1933 # operator doesn't advertise bundles appropriate for its clients,
1917 # they deserve what's coming. Furthermore, from a client's
1934 # they deserve what's coming. Furthermore, from a client's
1918 # perspective, no automatic fallback would mean not being able to
1935 # perspective, no automatic fallback would mean not being able to
1919 # clone!
1936 # clone!
1920 repo.ui.warn(_('no compatible clone bundles available on server; '
1937 repo.ui.warn(_('no compatible clone bundles available on server; '
1921 'falling back to regular clone\n'))
1938 'falling back to regular clone\n'))
1922 repo.ui.warn(_('(you may want to report this to the server '
1939 repo.ui.warn(_('(you may want to report this to the server '
1923 'operator)\n'))
1940 'operator)\n'))
1924 return
1941 return
1925
1942
1926 entries = sortclonebundleentries(repo.ui, entries)
1943 entries = sortclonebundleentries(repo.ui, entries)
1927
1944
1928 url = entries[0]['URL']
1945 url = entries[0]['URL']
1929 repo.ui.status(_('applying clone bundle from %s\n') % url)
1946 repo.ui.status(_('applying clone bundle from %s\n') % url)
1930 if trypullbundlefromurl(repo.ui, repo, url):
1947 if trypullbundlefromurl(repo.ui, repo, url):
1931 repo.ui.status(_('finished applying clone bundle\n'))
1948 repo.ui.status(_('finished applying clone bundle\n'))
1932 # Bundle failed.
1949 # Bundle failed.
1933 #
1950 #
1934 # We abort by default to avoid the thundering herd of
1951 # We abort by default to avoid the thundering herd of
1935 # clients flooding a server that was expecting expensive
1952 # clients flooding a server that was expecting expensive
1936 # clone load to be offloaded.
1953 # clone load to be offloaded.
1937 elif repo.ui.configbool('ui', 'clonebundlefallback'):
1954 elif repo.ui.configbool('ui', 'clonebundlefallback'):
1938 repo.ui.warn(_('falling back to normal clone\n'))
1955 repo.ui.warn(_('falling back to normal clone\n'))
1939 else:
1956 else:
1940 raise error.Abort(_('error applying bundle'),
1957 raise error.Abort(_('error applying bundle'),
1941 hint=_('if this error persists, consider contacting '
1958 hint=_('if this error persists, consider contacting '
1942 'the server operator or disable clone '
1959 'the server operator or disable clone '
1943 'bundles via '
1960 'bundles via '
1944 '"--config ui.clonebundles=false"'))
1961 '"--config ui.clonebundles=false"'))
1945
1962
1946 def parseclonebundlesmanifest(repo, s):
1963 def parseclonebundlesmanifest(repo, s):
1947 """Parses the raw text of a clone bundles manifest.
1964 """Parses the raw text of a clone bundles manifest.
1948
1965
1949 Returns a list of dicts. The dicts have a ``URL`` key corresponding
1966 Returns a list of dicts. The dicts have a ``URL`` key corresponding
1950 to the URL and other keys are the attributes for the entry.
1967 to the URL and other keys are the attributes for the entry.
1951 """
1968 """
1952 m = []
1969 m = []
1953 for line in s.splitlines():
1970 for line in s.splitlines():
1954 fields = line.split()
1971 fields = line.split()
1955 if not fields:
1972 if not fields:
1956 continue
1973 continue
1957 attrs = {'URL': fields[0]}
1974 attrs = {'URL': fields[0]}
1958 for rawattr in fields[1:]:
1975 for rawattr in fields[1:]:
1959 key, value = rawattr.split('=', 1)
1976 key, value = rawattr.split('=', 1)
1960 key = urlreq.unquote(key)
1977 key = urlreq.unquote(key)
1961 value = urlreq.unquote(value)
1978 value = urlreq.unquote(value)
1962 attrs[key] = value
1979 attrs[key] = value
1963
1980
1964 # Parse BUNDLESPEC into components. This makes client-side
1981 # Parse BUNDLESPEC into components. This makes client-side
1965 # preferences easier to specify since you can prefer a single
1982 # preferences easier to specify since you can prefer a single
1966 # component of the BUNDLESPEC.
1983 # component of the BUNDLESPEC.
1967 if key == 'BUNDLESPEC':
1984 if key == 'BUNDLESPEC':
1968 try:
1985 try:
1969 comp, version, params = parsebundlespec(repo, value,
1986 comp, version, params = parsebundlespec(repo, value,
1970 externalnames=True)
1987 externalnames=True)
1971 attrs['COMPRESSION'] = comp
1988 attrs['COMPRESSION'] = comp
1972 attrs['VERSION'] = version
1989 attrs['VERSION'] = version
1973 except error.InvalidBundleSpecification:
1990 except error.InvalidBundleSpecification:
1974 pass
1991 pass
1975 except error.UnsupportedBundleSpecification:
1992 except error.UnsupportedBundleSpecification:
1976 pass
1993 pass
1977
1994
1978 m.append(attrs)
1995 m.append(attrs)
1979
1996
1980 return m
1997 return m
1981
1998
1982 def filterclonebundleentries(repo, entries, streamclonerequested=False):
1999 def filterclonebundleentries(repo, entries, streamclonerequested=False):
1983 """Remove incompatible clone bundle manifest entries.
2000 """Remove incompatible clone bundle manifest entries.
1984
2001
1985 Accepts a list of entries parsed with ``parseclonebundlesmanifest``
2002 Accepts a list of entries parsed with ``parseclonebundlesmanifest``
1986 and returns a new list consisting of only the entries that this client
2003 and returns a new list consisting of only the entries that this client
1987 should be able to apply.
2004 should be able to apply.
1988
2005
1989 There is no guarantee we'll be able to apply all returned entries because
2006 There is no guarantee we'll be able to apply all returned entries because
1990 the metadata we use to filter on may be missing or wrong.
2007 the metadata we use to filter on may be missing or wrong.
1991 """
2008 """
1992 newentries = []
2009 newentries = []
1993 for entry in entries:
2010 for entry in entries:
1994 spec = entry.get('BUNDLESPEC')
2011 spec = entry.get('BUNDLESPEC')
1995 if spec:
2012 if spec:
1996 try:
2013 try:
1997 comp, version, params = parsebundlespec(repo, spec, strict=True)
2014 comp, version, params = parsebundlespec(repo, spec, strict=True)
1998
2015
1999 # If a stream clone was requested, filter out non-streamclone
2016 # If a stream clone was requested, filter out non-streamclone
2000 # entries.
2017 # entries.
2001 if streamclonerequested and (comp != 'UN' or version != 's1'):
2018 if streamclonerequested and (comp != 'UN' or version != 's1'):
2002 repo.ui.debug('filtering %s because not a stream clone\n' %
2019 repo.ui.debug('filtering %s because not a stream clone\n' %
2003 entry['URL'])
2020 entry['URL'])
2004 continue
2021 continue
2005
2022
2006 except error.InvalidBundleSpecification as e:
2023 except error.InvalidBundleSpecification as e:
2007 repo.ui.debug(str(e) + '\n')
2024 repo.ui.debug(str(e) + '\n')
2008 continue
2025 continue
2009 except error.UnsupportedBundleSpecification as e:
2026 except error.UnsupportedBundleSpecification as e:
2010 repo.ui.debug('filtering %s because unsupported bundle '
2027 repo.ui.debug('filtering %s because unsupported bundle '
2011 'spec: %s\n' % (entry['URL'], str(e)))
2028 'spec: %s\n' % (entry['URL'], str(e)))
2012 continue
2029 continue
2013 # If we don't have a spec and requested a stream clone, we don't know
2030 # If we don't have a spec and requested a stream clone, we don't know
2014 # what the entry is so don't attempt to apply it.
2031 # what the entry is so don't attempt to apply it.
2015 elif streamclonerequested:
2032 elif streamclonerequested:
2016 repo.ui.debug('filtering %s because cannot determine if a stream '
2033 repo.ui.debug('filtering %s because cannot determine if a stream '
2017 'clone bundle\n' % entry['URL'])
2034 'clone bundle\n' % entry['URL'])
2018 continue
2035 continue
2019
2036
2020 if 'REQUIRESNI' in entry and not sslutil.hassni:
2037 if 'REQUIRESNI' in entry and not sslutil.hassni:
2021 repo.ui.debug('filtering %s because SNI not supported\n' %
2038 repo.ui.debug('filtering %s because SNI not supported\n' %
2022 entry['URL'])
2039 entry['URL'])
2023 continue
2040 continue
2024
2041
2025 newentries.append(entry)
2042 newentries.append(entry)
2026
2043
2027 return newentries
2044 return newentries
2028
2045
2029 class clonebundleentry(object):
2046 class clonebundleentry(object):
2030 """Represents an item in a clone bundles manifest.
2047 """Represents an item in a clone bundles manifest.
2031
2048
2032 This rich class is needed to support sorting since sorted() in Python 3
2049 This rich class is needed to support sorting since sorted() in Python 3
2033 doesn't support ``cmp`` and our comparison is complex enough that ``key=``
2050 doesn't support ``cmp`` and our comparison is complex enough that ``key=``
2034 won't work.
2051 won't work.
2035 """
2052 """
2036
2053
2037 def __init__(self, value, prefers):
2054 def __init__(self, value, prefers):
2038 self.value = value
2055 self.value = value
2039 self.prefers = prefers
2056 self.prefers = prefers
2040
2057
2041 def _cmp(self, other):
2058 def _cmp(self, other):
2042 for prefkey, prefvalue in self.prefers:
2059 for prefkey, prefvalue in self.prefers:
2043 avalue = self.value.get(prefkey)
2060 avalue = self.value.get(prefkey)
2044 bvalue = other.value.get(prefkey)
2061 bvalue = other.value.get(prefkey)
2045
2062
2046 # Special case for b missing attribute and a matches exactly.
2063 # Special case for b missing attribute and a matches exactly.
2047 if avalue is not None and bvalue is None and avalue == prefvalue:
2064 if avalue is not None and bvalue is None and avalue == prefvalue:
2048 return -1
2065 return -1
2049
2066
2050 # Special case for a missing attribute and b matches exactly.
2067 # Special case for a missing attribute and b matches exactly.
2051 if bvalue is not None and avalue is None and bvalue == prefvalue:
2068 if bvalue is not None and avalue is None and bvalue == prefvalue:
2052 return 1
2069 return 1
2053
2070
2054 # We can't compare unless attribute present on both.
2071 # We can't compare unless attribute present on both.
2055 if avalue is None or bvalue is None:
2072 if avalue is None or bvalue is None:
2056 continue
2073 continue
2057
2074
2058 # Same values should fall back to next attribute.
2075 # Same values should fall back to next attribute.
2059 if avalue == bvalue:
2076 if avalue == bvalue:
2060 continue
2077 continue
2061
2078
2062 # Exact matches come first.
2079 # Exact matches come first.
2063 if avalue == prefvalue:
2080 if avalue == prefvalue:
2064 return -1
2081 return -1
2065 if bvalue == prefvalue:
2082 if bvalue == prefvalue:
2066 return 1
2083 return 1
2067
2084
2068 # Fall back to next attribute.
2085 # Fall back to next attribute.
2069 continue
2086 continue
2070
2087
2071 # If we got here we couldn't sort by attributes and prefers. Fall
2088 # If we got here we couldn't sort by attributes and prefers. Fall
2072 # back to index order.
2089 # back to index order.
2073 return 0
2090 return 0
2074
2091
2075 def __lt__(self, other):
2092 def __lt__(self, other):
2076 return self._cmp(other) < 0
2093 return self._cmp(other) < 0
2077
2094
2078 def __gt__(self, other):
2095 def __gt__(self, other):
2079 return self._cmp(other) > 0
2096 return self._cmp(other) > 0
2080
2097
2081 def __eq__(self, other):
2098 def __eq__(self, other):
2082 return self._cmp(other) == 0
2099 return self._cmp(other) == 0
2083
2100
2084 def __le__(self, other):
2101 def __le__(self, other):
2085 return self._cmp(other) <= 0
2102 return self._cmp(other) <= 0
2086
2103
2087 def __ge__(self, other):
2104 def __ge__(self, other):
2088 return self._cmp(other) >= 0
2105 return self._cmp(other) >= 0
2089
2106
2090 def __ne__(self, other):
2107 def __ne__(self, other):
2091 return self._cmp(other) != 0
2108 return self._cmp(other) != 0
2092
2109
2093 def sortclonebundleentries(ui, entries):
2110 def sortclonebundleentries(ui, entries):
2094 prefers = ui.configlist('ui', 'clonebundleprefers')
2111 prefers = ui.configlist('ui', 'clonebundleprefers')
2095 if not prefers:
2112 if not prefers:
2096 return list(entries)
2113 return list(entries)
2097
2114
2098 prefers = [p.split('=', 1) for p in prefers]
2115 prefers = [p.split('=', 1) for p in prefers]
2099
2116
2100 items = sorted(clonebundleentry(v, prefers) for v in entries)
2117 items = sorted(clonebundleentry(v, prefers) for v in entries)
2101 return [i.value for i in items]
2118 return [i.value for i in items]
2102
2119
2103 def trypullbundlefromurl(ui, repo, url):
2120 def trypullbundlefromurl(ui, repo, url):
2104 """Attempt to apply a bundle from a URL."""
2121 """Attempt to apply a bundle from a URL."""
2105 with repo.lock(), repo.transaction('bundleurl') as tr:
2122 with repo.lock(), repo.transaction('bundleurl') as tr:
2106 try:
2123 try:
2107 fh = urlmod.open(ui, url)
2124 fh = urlmod.open(ui, url)
2108 cg = readbundle(ui, fh, 'stream')
2125 cg = readbundle(ui, fh, 'stream')
2109
2126
2110 if isinstance(cg, streamclone.streamcloneapplier):
2127 if isinstance(cg, streamclone.streamcloneapplier):
2111 cg.apply(repo)
2128 cg.apply(repo)
2112 else:
2129 else:
2113 bundle2.applybundle(repo, cg, tr, 'clonebundles', url)
2130 bundle2.applybundle(repo, cg, tr, 'clonebundles', url)
2114 return True
2131 return True
2115 except urlerr.httperror as e:
2132 except urlerr.httperror as e:
2116 ui.warn(_('HTTP error fetching bundle: %s\n') % str(e))
2133 ui.warn(_('HTTP error fetching bundle: %s\n') % str(e))
2117 except urlerr.urlerror as e:
2134 except urlerr.urlerror as e:
2118 ui.warn(_('error fetching bundle: %s\n') % e.reason)
2135 ui.warn(_('error fetching bundle: %s\n') % e.reason)
2119
2136
2120 return False
2137 return False
@@ -1,75 +1,77 b''
1 # common patterns in test at can safely be replaced
1 # common patterns in test at can safely be replaced
2 from __future__ import absolute_import
2 from __future__ import absolute_import
3
3
4 substitutions = [
4 substitutions = [
5 # list of possible compressions
5 # list of possible compressions
6 (br'(zstd,)?zlib,none,bzip2',
6 (br'(zstd,)?zlib,none,bzip2',
7 br'$USUAL_COMPRESSIONS$'
7 br'$USUAL_COMPRESSIONS$'
8 ),
8 ),
9 # capabilities sent through http
9 # capabilities sent through http
10 (br'bundlecaps=HG20%2Cbundle2%3DHG20%250A'
10 (br'bundlecaps=HG20%2Cbundle2%3DHG20%250A'
11 br'bookmarks%250A'
11 br'changegroup%253D01%252C02%250A'
12 br'changegroup%253D01%252C02%250A'
12 br'digests%253Dmd5%252Csha1%252Csha512%250A'
13 br'digests%253Dmd5%252Csha1%252Csha512%250A'
13 br'error%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250A'
14 br'error%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250A'
14 br'hgtagsfnodes%250A'
15 br'hgtagsfnodes%250A'
15 br'listkeys%250A'
16 br'listkeys%250A'
16 br'phases%253Dheads%250A'
17 br'phases%253Dheads%250A'
17 br'pushkey%250A'
18 br'pushkey%250A'
18 br'remote-changegroup%253Dhttp%252Chttps',
19 br'remote-changegroup%253Dhttp%252Chttps',
19 # (the replacement patterns)
20 # (the replacement patterns)
20 br'$USUAL_BUNDLE_CAPS$'
21 br'$USUAL_BUNDLE_CAPS$'
21 ),
22 ),
22 # bundle2 capabilities sent through ssh
23 # bundle2 capabilities sent through ssh
23 (br'bundle2=HG20%0A'
24 (br'bundle2=HG20%0A'
25 br'bookmarks%0A'
24 br'changegroup%3D01%2C02%0A'
26 br'changegroup%3D01%2C02%0A'
25 br'digests%3Dmd5%2Csha1%2Csha512%0A'
27 br'digests%3Dmd5%2Csha1%2Csha512%0A'
26 br'error%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0A'
28 br'error%3Dabort%2Cunsupportedcontent%2Cpushraced%2Cpushkey%0A'
27 br'hgtagsfnodes%0A'
29 br'hgtagsfnodes%0A'
28 br'listkeys%0A'
30 br'listkeys%0A'
29 br'phases%3Dheads%0A'
31 br'phases%3Dheads%0A'
30 br'pushkey%0A'
32 br'pushkey%0A'
31 br'remote-changegroup%3Dhttp%2Chttps',
33 br'remote-changegroup%3Dhttp%2Chttps',
32 # (replacement patterns)
34 # (replacement patterns)
33 br'$USUAL_BUNDLE2_CAPS$'
35 br'$USUAL_BUNDLE2_CAPS$'
34 ),
36 ),
35 # HTTP log dates
37 # HTTP log dates
36 (br' - - \[\d\d/.../2\d\d\d \d\d:\d\d:\d\d] "GET',
38 (br' - - \[\d\d/.../2\d\d\d \d\d:\d\d:\d\d] "GET',
37 br' - - [$LOGDATE$] "GET'
39 br' - - [$LOGDATE$] "GET'
38 ),
40 ),
39 ]
41 ]
40
42
41 # Various platform error strings, keyed on a common replacement string
43 # Various platform error strings, keyed on a common replacement string
42 _errors = {
44 _errors = {
43 br'$ENOENT$': (
45 br'$ENOENT$': (
44 # strerror()
46 # strerror()
45 br'No such file or directory',
47 br'No such file or directory',
46
48
47 # FormatMessage(ERROR_FILE_NOT_FOUND)
49 # FormatMessage(ERROR_FILE_NOT_FOUND)
48 br'The system cannot find the file specified',
50 br'The system cannot find the file specified',
49 ),
51 ),
50 br'$ENOTDIR$': (
52 br'$ENOTDIR$': (
51 # strerror()
53 # strerror()
52 br'Not a directory',
54 br'Not a directory',
53
55
54 # FormatMessage(ERROR_PATH_NOT_FOUND)
56 # FormatMessage(ERROR_PATH_NOT_FOUND)
55 br'The system cannot find the path specified',
57 br'The system cannot find the path specified',
56 ),
58 ),
57 br'$ECONNRESET$': (
59 br'$ECONNRESET$': (
58 # strerror()
60 # strerror()
59 br'Connection reset by peer',
61 br'Connection reset by peer',
60
62
61 # FormatMessage(WSAECONNRESET)
63 # FormatMessage(WSAECONNRESET)
62 br'An existing connection was forcibly closed by the remote host',
64 br'An existing connection was forcibly closed by the remote host',
63 ),
65 ),
64 br'$EADDRINUSE$': (
66 br'$EADDRINUSE$': (
65 # strerror()
67 # strerror()
66 br'Address already in use',
68 br'Address already in use',
67
69
68 # FormatMessage(WSAEADDRINUSE)
70 # FormatMessage(WSAEADDRINUSE)
69 br'Only one usage of each socket address'
71 br'Only one usage of each socket address'
70 br' \(protocol/network address/port\) is normally permitted',
72 br' \(protocol/network address/port\) is normally permitted',
71 ),
73 ),
72 }
74 }
73
75
74 for replace, msgs in _errors.items():
76 for replace, msgs in _errors.items():
75 substitutions.extend((m, replace) for m in msgs)
77 substitutions.extend((m, replace) for m in msgs)
@@ -1,2183 +1,2183 b''
1 > do_push()
1 > do_push()
2 > {
2 > {
3 > user=$1
3 > user=$1
4 > shift
4 > shift
5 > echo "Pushing as user $user"
5 > echo "Pushing as user $user"
6 > echo 'hgrc = """'
6 > echo 'hgrc = """'
7 > sed -n '/\[[ha]/,$p' b/.hg/hgrc | grep -v fakegroups.py
7 > sed -n '/\[[ha]/,$p' b/.hg/hgrc | grep -v fakegroups.py
8 > echo '"""'
8 > echo '"""'
9 > if test -f acl.config; then
9 > if test -f acl.config; then
10 > echo 'acl.config = """'
10 > echo 'acl.config = """'
11 > cat acl.config
11 > cat acl.config
12 > echo '"""'
12 > echo '"""'
13 > fi
13 > fi
14 > # On AIX /etc/profile sets LOGNAME read-only. So
14 > # On AIX /etc/profile sets LOGNAME read-only. So
15 > # LOGNAME=$user hg --cws a --debug push ../b
15 > # LOGNAME=$user hg --cws a --debug push ../b
16 > # fails with "This variable is read only."
16 > # fails with "This variable is read only."
17 > # Use env to work around this.
17 > # Use env to work around this.
18 > env LOGNAME=$user hg --cwd a --debug push ../b
18 > env LOGNAME=$user hg --cwd a --debug push ../b
19 > hg --cwd b rollback
19 > hg --cwd b rollback
20 > hg --cwd b --quiet tip
20 > hg --cwd b --quiet tip
21 > echo
21 > echo
22 > }
22 > }
23
23
24 > init_config()
24 > init_config()
25 > {
25 > {
26 > cat > fakegroups.py <<EOF
26 > cat > fakegroups.py <<EOF
27 > from hgext import acl
27 > from hgext import acl
28 > def fakegetusers(ui, group):
28 > def fakegetusers(ui, group):
29 > try:
29 > try:
30 > return acl._getusersorig(ui, group)
30 > return acl._getusersorig(ui, group)
31 > except:
31 > except:
32 > return ["fred", "betty"]
32 > return ["fred", "betty"]
33 > acl._getusersorig = acl._getusers
33 > acl._getusersorig = acl._getusers
34 > acl._getusers = fakegetusers
34 > acl._getusers = fakegetusers
35 > EOF
35 > EOF
36 > rm -f acl.config
36 > rm -f acl.config
37 > cat > $config <<EOF
37 > cat > $config <<EOF
38 > [hooks]
38 > [hooks]
39 > pretxnchangegroup.acl = python:hgext.acl.hook
39 > pretxnchangegroup.acl = python:hgext.acl.hook
40 > [acl]
40 > [acl]
41 > sources = push
41 > sources = push
42 > [extensions]
42 > [extensions]
43 > f=`pwd`/fakegroups.py
43 > f=`pwd`/fakegroups.py
44 > EOF
44 > EOF
45 > }
45 > }
46
46
47 $ hg init a
47 $ hg init a
48 $ cd a
48 $ cd a
49 $ mkdir foo foo/Bar quux
49 $ mkdir foo foo/Bar quux
50 $ echo 'in foo' > foo/file.txt
50 $ echo 'in foo' > foo/file.txt
51 $ echo 'in foo/Bar' > foo/Bar/file.txt
51 $ echo 'in foo/Bar' > foo/Bar/file.txt
52 $ echo 'in quux' > quux/file.py
52 $ echo 'in quux' > quux/file.py
53 $ hg add -q
53 $ hg add -q
54 $ hg ci -m 'add files' -d '1000000 0'
54 $ hg ci -m 'add files' -d '1000000 0'
55 $ echo >> foo/file.txt
55 $ echo >> foo/file.txt
56 $ hg ci -m 'change foo/file' -d '1000001 0'
56 $ hg ci -m 'change foo/file' -d '1000001 0'
57 $ echo >> foo/Bar/file.txt
57 $ echo >> foo/Bar/file.txt
58 $ hg ci -m 'change foo/Bar/file' -d '1000002 0'
58 $ hg ci -m 'change foo/Bar/file' -d '1000002 0'
59 $ echo >> quux/file.py
59 $ echo >> quux/file.py
60 $ hg ci -m 'change quux/file' -d '1000003 0'
60 $ hg ci -m 'change quux/file' -d '1000003 0'
61 $ hg tip --quiet
61 $ hg tip --quiet
62 3:911600dab2ae
62 3:911600dab2ae
63
63
64 $ cd ..
64 $ cd ..
65 $ hg clone -r 0 a b
65 $ hg clone -r 0 a b
66 adding changesets
66 adding changesets
67 adding manifests
67 adding manifests
68 adding file changes
68 adding file changes
69 added 1 changesets with 3 changes to 3 files
69 added 1 changesets with 3 changes to 3 files
70 new changesets 6675d58eff77
70 new changesets 6675d58eff77
71 updating to branch default
71 updating to branch default
72 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
72 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
73
73
74 $ config=b/.hg/hgrc
74 $ config=b/.hg/hgrc
75
75
76 Extension disabled for lack of a hook
76 Extension disabled for lack of a hook
77
77
78 $ do_push fred
78 $ do_push fred
79 Pushing as user fred
79 Pushing as user fred
80 hgrc = """
80 hgrc = """
81 """
81 """
82 pushing to ../b
82 pushing to ../b
83 query 1; heads
83 query 1; heads
84 searching for changes
84 searching for changes
85 all remote heads known locally
85 all remote heads known locally
86 listing keys for "phases"
86 listing keys for "phases"
87 checking for updated bookmarks
87 checking for updated bookmarks
88 listing keys for "bookmarks"
88 listing keys for "bookmarks"
89 listing keys for "bookmarks"
89 listing keys for "bookmarks"
90 3 changesets found
90 3 changesets found
91 list of changesets:
91 list of changesets:
92 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
92 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
93 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
93 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
94 911600dab2ae7a9baff75958b84fe606851ce955
94 911600dab2ae7a9baff75958b84fe606851ce955
95 bundle2-output-bundle: "HG20", 5 parts total
95 bundle2-output-bundle: "HG20", 5 parts total
96 bundle2-output-part: "replycaps" 168 bytes payload
96 bundle2-output-part: "replycaps" 178 bytes payload
97 bundle2-output-part: "check:phases" 24 bytes payload
97 bundle2-output-part: "check:phases" 24 bytes payload
98 bundle2-output-part: "check:heads" streamed payload
98 bundle2-output-part: "check:heads" streamed payload
99 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
99 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
100 bundle2-output-part: "phase-heads" 24 bytes payload
100 bundle2-output-part: "phase-heads" 24 bytes payload
101 bundle2-input-bundle: with-transaction
101 bundle2-input-bundle: with-transaction
102 bundle2-input-part: "replycaps" supported
102 bundle2-input-part: "replycaps" supported
103 bundle2-input-part: total payload size 168
103 bundle2-input-part: total payload size 178
104 bundle2-input-part: "check:phases" supported
104 bundle2-input-part: "check:phases" supported
105 bundle2-input-part: total payload size 24
105 bundle2-input-part: total payload size 24
106 bundle2-input-part: "check:heads" supported
106 bundle2-input-part: "check:heads" supported
107 bundle2-input-part: total payload size 20
107 bundle2-input-part: total payload size 20
108 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
108 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
109 adding changesets
109 adding changesets
110 add changeset ef1ea85a6374
110 add changeset ef1ea85a6374
111 add changeset f9cafe1212c8
111 add changeset f9cafe1212c8
112 add changeset 911600dab2ae
112 add changeset 911600dab2ae
113 adding manifests
113 adding manifests
114 adding file changes
114 adding file changes
115 adding foo/Bar/file.txt revisions
115 adding foo/Bar/file.txt revisions
116 adding foo/file.txt revisions
116 adding foo/file.txt revisions
117 adding quux/file.py revisions
117 adding quux/file.py revisions
118 added 3 changesets with 3 changes to 3 files
118 added 3 changesets with 3 changes to 3 files
119 bundle2-input-part: total payload size 1553
119 bundle2-input-part: total payload size 1553
120 bundle2-input-part: "phase-heads" supported
120 bundle2-input-part: "phase-heads" supported
121 bundle2-input-part: total payload size 24
121 bundle2-input-part: total payload size 24
122 bundle2-input-bundle: 4 parts total
122 bundle2-input-bundle: 4 parts total
123 updating the branch cache
123 updating the branch cache
124 bundle2-output-bundle: "HG20", 1 parts total
124 bundle2-output-bundle: "HG20", 1 parts total
125 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
125 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
126 bundle2-input-bundle: no-transaction
126 bundle2-input-bundle: no-transaction
127 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
127 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
128 bundle2-input-bundle: 0 parts total
128 bundle2-input-bundle: 0 parts total
129 listing keys for "phases"
129 listing keys for "phases"
130 repository tip rolled back to revision 0 (undo push)
130 repository tip rolled back to revision 0 (undo push)
131 0:6675d58eff77
131 0:6675d58eff77
132
132
133
133
134 $ echo '[hooks]' >> $config
134 $ echo '[hooks]' >> $config
135 $ echo 'pretxnchangegroup.acl = python:hgext.acl.hook' >> $config
135 $ echo 'pretxnchangegroup.acl = python:hgext.acl.hook' >> $config
136
136
137 Extension disabled for lack of acl.sources
137 Extension disabled for lack of acl.sources
138
138
139 $ do_push fred
139 $ do_push fred
140 Pushing as user fred
140 Pushing as user fred
141 hgrc = """
141 hgrc = """
142 [hooks]
142 [hooks]
143 pretxnchangegroup.acl = python:hgext.acl.hook
143 pretxnchangegroup.acl = python:hgext.acl.hook
144 """
144 """
145 pushing to ../b
145 pushing to ../b
146 query 1; heads
146 query 1; heads
147 searching for changes
147 searching for changes
148 all remote heads known locally
148 all remote heads known locally
149 listing keys for "phases"
149 listing keys for "phases"
150 checking for updated bookmarks
150 checking for updated bookmarks
151 listing keys for "bookmarks"
151 listing keys for "bookmarks"
152 listing keys for "bookmarks"
152 listing keys for "bookmarks"
153 3 changesets found
153 3 changesets found
154 list of changesets:
154 list of changesets:
155 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
155 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
156 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
156 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
157 911600dab2ae7a9baff75958b84fe606851ce955
157 911600dab2ae7a9baff75958b84fe606851ce955
158 bundle2-output-bundle: "HG20", 5 parts total
158 bundle2-output-bundle: "HG20", 5 parts total
159 bundle2-output-part: "replycaps" 168 bytes payload
159 bundle2-output-part: "replycaps" 178 bytes payload
160 bundle2-output-part: "check:phases" 24 bytes payload
160 bundle2-output-part: "check:phases" 24 bytes payload
161 bundle2-output-part: "check:heads" streamed payload
161 bundle2-output-part: "check:heads" streamed payload
162 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
162 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
163 bundle2-output-part: "phase-heads" 24 bytes payload
163 bundle2-output-part: "phase-heads" 24 bytes payload
164 bundle2-input-bundle: with-transaction
164 bundle2-input-bundle: with-transaction
165 bundle2-input-part: "replycaps" supported
165 bundle2-input-part: "replycaps" supported
166 bundle2-input-part: total payload size 168
166 bundle2-input-part: total payload size 178
167 bundle2-input-part: "check:phases" supported
167 bundle2-input-part: "check:phases" supported
168 bundle2-input-part: total payload size 24
168 bundle2-input-part: total payload size 24
169 bundle2-input-part: "check:heads" supported
169 bundle2-input-part: "check:heads" supported
170 bundle2-input-part: total payload size 20
170 bundle2-input-part: total payload size 20
171 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
171 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
172 adding changesets
172 adding changesets
173 add changeset ef1ea85a6374
173 add changeset ef1ea85a6374
174 add changeset f9cafe1212c8
174 add changeset f9cafe1212c8
175 add changeset 911600dab2ae
175 add changeset 911600dab2ae
176 adding manifests
176 adding manifests
177 adding file changes
177 adding file changes
178 adding foo/Bar/file.txt revisions
178 adding foo/Bar/file.txt revisions
179 adding foo/file.txt revisions
179 adding foo/file.txt revisions
180 adding quux/file.py revisions
180 adding quux/file.py revisions
181 added 3 changesets with 3 changes to 3 files
181 added 3 changesets with 3 changes to 3 files
182 calling hook pretxnchangegroup.acl: hgext.acl.hook
182 calling hook pretxnchangegroup.acl: hgext.acl.hook
183 acl: changes have source "push" - skipping
183 acl: changes have source "push" - skipping
184 bundle2-input-part: total payload size 1553
184 bundle2-input-part: total payload size 1553
185 bundle2-input-part: "phase-heads" supported
185 bundle2-input-part: "phase-heads" supported
186 bundle2-input-part: total payload size 24
186 bundle2-input-part: total payload size 24
187 bundle2-input-bundle: 4 parts total
187 bundle2-input-bundle: 4 parts total
188 updating the branch cache
188 updating the branch cache
189 bundle2-output-bundle: "HG20", 1 parts total
189 bundle2-output-bundle: "HG20", 1 parts total
190 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
190 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
191 bundle2-input-bundle: no-transaction
191 bundle2-input-bundle: no-transaction
192 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
192 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
193 bundle2-input-bundle: 0 parts total
193 bundle2-input-bundle: 0 parts total
194 listing keys for "phases"
194 listing keys for "phases"
195 repository tip rolled back to revision 0 (undo push)
195 repository tip rolled back to revision 0 (undo push)
196 0:6675d58eff77
196 0:6675d58eff77
197
197
198
198
199 No [acl.allow]/[acl.deny]
199 No [acl.allow]/[acl.deny]
200
200
201 $ echo '[acl]' >> $config
201 $ echo '[acl]' >> $config
202 $ echo 'sources = push' >> $config
202 $ echo 'sources = push' >> $config
203 $ do_push fred
203 $ do_push fred
204 Pushing as user fred
204 Pushing as user fred
205 hgrc = """
205 hgrc = """
206 [hooks]
206 [hooks]
207 pretxnchangegroup.acl = python:hgext.acl.hook
207 pretxnchangegroup.acl = python:hgext.acl.hook
208 [acl]
208 [acl]
209 sources = push
209 sources = push
210 """
210 """
211 pushing to ../b
211 pushing to ../b
212 query 1; heads
212 query 1; heads
213 searching for changes
213 searching for changes
214 all remote heads known locally
214 all remote heads known locally
215 listing keys for "phases"
215 listing keys for "phases"
216 checking for updated bookmarks
216 checking for updated bookmarks
217 listing keys for "bookmarks"
217 listing keys for "bookmarks"
218 listing keys for "bookmarks"
218 listing keys for "bookmarks"
219 3 changesets found
219 3 changesets found
220 list of changesets:
220 list of changesets:
221 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
221 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
222 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
222 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
223 911600dab2ae7a9baff75958b84fe606851ce955
223 911600dab2ae7a9baff75958b84fe606851ce955
224 bundle2-output-bundle: "HG20", 5 parts total
224 bundle2-output-bundle: "HG20", 5 parts total
225 bundle2-output-part: "replycaps" 168 bytes payload
225 bundle2-output-part: "replycaps" 178 bytes payload
226 bundle2-output-part: "check:phases" 24 bytes payload
226 bundle2-output-part: "check:phases" 24 bytes payload
227 bundle2-output-part: "check:heads" streamed payload
227 bundle2-output-part: "check:heads" streamed payload
228 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
228 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
229 bundle2-output-part: "phase-heads" 24 bytes payload
229 bundle2-output-part: "phase-heads" 24 bytes payload
230 bundle2-input-bundle: with-transaction
230 bundle2-input-bundle: with-transaction
231 bundle2-input-part: "replycaps" supported
231 bundle2-input-part: "replycaps" supported
232 bundle2-input-part: total payload size 168
232 bundle2-input-part: total payload size 178
233 bundle2-input-part: "check:phases" supported
233 bundle2-input-part: "check:phases" supported
234 bundle2-input-part: total payload size 24
234 bundle2-input-part: total payload size 24
235 bundle2-input-part: "check:heads" supported
235 bundle2-input-part: "check:heads" supported
236 bundle2-input-part: total payload size 20
236 bundle2-input-part: total payload size 20
237 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
237 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
238 adding changesets
238 adding changesets
239 add changeset ef1ea85a6374
239 add changeset ef1ea85a6374
240 add changeset f9cafe1212c8
240 add changeset f9cafe1212c8
241 add changeset 911600dab2ae
241 add changeset 911600dab2ae
242 adding manifests
242 adding manifests
243 adding file changes
243 adding file changes
244 adding foo/Bar/file.txt revisions
244 adding foo/Bar/file.txt revisions
245 adding foo/file.txt revisions
245 adding foo/file.txt revisions
246 adding quux/file.py revisions
246 adding quux/file.py revisions
247 added 3 changesets with 3 changes to 3 files
247 added 3 changesets with 3 changes to 3 files
248 calling hook pretxnchangegroup.acl: hgext.acl.hook
248 calling hook pretxnchangegroup.acl: hgext.acl.hook
249 acl: checking access for user "fred"
249 acl: checking access for user "fred"
250 acl: acl.allow.branches not enabled
250 acl: acl.allow.branches not enabled
251 acl: acl.deny.branches not enabled
251 acl: acl.deny.branches not enabled
252 acl: acl.allow not enabled
252 acl: acl.allow not enabled
253 acl: acl.deny not enabled
253 acl: acl.deny not enabled
254 acl: branch access granted: "ef1ea85a6374" on branch "default"
254 acl: branch access granted: "ef1ea85a6374" on branch "default"
255 acl: path access granted: "ef1ea85a6374"
255 acl: path access granted: "ef1ea85a6374"
256 acl: branch access granted: "f9cafe1212c8" on branch "default"
256 acl: branch access granted: "f9cafe1212c8" on branch "default"
257 acl: path access granted: "f9cafe1212c8"
257 acl: path access granted: "f9cafe1212c8"
258 acl: branch access granted: "911600dab2ae" on branch "default"
258 acl: branch access granted: "911600dab2ae" on branch "default"
259 acl: path access granted: "911600dab2ae"
259 acl: path access granted: "911600dab2ae"
260 bundle2-input-part: total payload size 1553
260 bundle2-input-part: total payload size 1553
261 bundle2-input-part: "phase-heads" supported
261 bundle2-input-part: "phase-heads" supported
262 bundle2-input-part: total payload size 24
262 bundle2-input-part: total payload size 24
263 bundle2-input-bundle: 4 parts total
263 bundle2-input-bundle: 4 parts total
264 updating the branch cache
264 updating the branch cache
265 bundle2-output-bundle: "HG20", 1 parts total
265 bundle2-output-bundle: "HG20", 1 parts total
266 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
266 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
267 bundle2-input-bundle: no-transaction
267 bundle2-input-bundle: no-transaction
268 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
268 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
269 bundle2-input-bundle: 0 parts total
269 bundle2-input-bundle: 0 parts total
270 listing keys for "phases"
270 listing keys for "phases"
271 repository tip rolled back to revision 0 (undo push)
271 repository tip rolled back to revision 0 (undo push)
272 0:6675d58eff77
272 0:6675d58eff77
273
273
274
274
275 Empty [acl.allow]
275 Empty [acl.allow]
276
276
277 $ echo '[acl.allow]' >> $config
277 $ echo '[acl.allow]' >> $config
278 $ do_push fred
278 $ do_push fred
279 Pushing as user fred
279 Pushing as user fred
280 hgrc = """
280 hgrc = """
281 [hooks]
281 [hooks]
282 pretxnchangegroup.acl = python:hgext.acl.hook
282 pretxnchangegroup.acl = python:hgext.acl.hook
283 [acl]
283 [acl]
284 sources = push
284 sources = push
285 [acl.allow]
285 [acl.allow]
286 """
286 """
287 pushing to ../b
287 pushing to ../b
288 query 1; heads
288 query 1; heads
289 searching for changes
289 searching for changes
290 all remote heads known locally
290 all remote heads known locally
291 listing keys for "phases"
291 listing keys for "phases"
292 checking for updated bookmarks
292 checking for updated bookmarks
293 listing keys for "bookmarks"
293 listing keys for "bookmarks"
294 listing keys for "bookmarks"
294 listing keys for "bookmarks"
295 3 changesets found
295 3 changesets found
296 list of changesets:
296 list of changesets:
297 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
297 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
298 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
298 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
299 911600dab2ae7a9baff75958b84fe606851ce955
299 911600dab2ae7a9baff75958b84fe606851ce955
300 bundle2-output-bundle: "HG20", 5 parts total
300 bundle2-output-bundle: "HG20", 5 parts total
301 bundle2-output-part: "replycaps" 168 bytes payload
301 bundle2-output-part: "replycaps" 178 bytes payload
302 bundle2-output-part: "check:phases" 24 bytes payload
302 bundle2-output-part: "check:phases" 24 bytes payload
303 bundle2-output-part: "check:heads" streamed payload
303 bundle2-output-part: "check:heads" streamed payload
304 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
304 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
305 bundle2-output-part: "phase-heads" 24 bytes payload
305 bundle2-output-part: "phase-heads" 24 bytes payload
306 bundle2-input-bundle: with-transaction
306 bundle2-input-bundle: with-transaction
307 bundle2-input-part: "replycaps" supported
307 bundle2-input-part: "replycaps" supported
308 bundle2-input-part: total payload size 168
308 bundle2-input-part: total payload size 178
309 bundle2-input-part: "check:phases" supported
309 bundle2-input-part: "check:phases" supported
310 bundle2-input-part: total payload size 24
310 bundle2-input-part: total payload size 24
311 bundle2-input-part: "check:heads" supported
311 bundle2-input-part: "check:heads" supported
312 bundle2-input-part: total payload size 20
312 bundle2-input-part: total payload size 20
313 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
313 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
314 adding changesets
314 adding changesets
315 add changeset ef1ea85a6374
315 add changeset ef1ea85a6374
316 add changeset f9cafe1212c8
316 add changeset f9cafe1212c8
317 add changeset 911600dab2ae
317 add changeset 911600dab2ae
318 adding manifests
318 adding manifests
319 adding file changes
319 adding file changes
320 adding foo/Bar/file.txt revisions
320 adding foo/Bar/file.txt revisions
321 adding foo/file.txt revisions
321 adding foo/file.txt revisions
322 adding quux/file.py revisions
322 adding quux/file.py revisions
323 added 3 changesets with 3 changes to 3 files
323 added 3 changesets with 3 changes to 3 files
324 calling hook pretxnchangegroup.acl: hgext.acl.hook
324 calling hook pretxnchangegroup.acl: hgext.acl.hook
325 acl: checking access for user "fred"
325 acl: checking access for user "fred"
326 acl: acl.allow.branches not enabled
326 acl: acl.allow.branches not enabled
327 acl: acl.deny.branches not enabled
327 acl: acl.deny.branches not enabled
328 acl: acl.allow enabled, 0 entries for user fred
328 acl: acl.allow enabled, 0 entries for user fred
329 acl: acl.deny not enabled
329 acl: acl.deny not enabled
330 acl: branch access granted: "ef1ea85a6374" on branch "default"
330 acl: branch access granted: "ef1ea85a6374" on branch "default"
331 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
331 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
332 bundle2-input-part: total payload size 1553
332 bundle2-input-part: total payload size 1553
333 bundle2-input-part: total payload size 24
333 bundle2-input-part: total payload size 24
334 bundle2-input-bundle: 4 parts total
334 bundle2-input-bundle: 4 parts total
335 transaction abort!
335 transaction abort!
336 rollback completed
336 rollback completed
337 abort: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
337 abort: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
338 no rollback information available
338 no rollback information available
339 0:6675d58eff77
339 0:6675d58eff77
340
340
341
341
342 fred is allowed inside foo/
342 fred is allowed inside foo/
343
343
344 $ echo 'foo/** = fred' >> $config
344 $ echo 'foo/** = fred' >> $config
345 $ do_push fred
345 $ do_push fred
346 Pushing as user fred
346 Pushing as user fred
347 hgrc = """
347 hgrc = """
348 [hooks]
348 [hooks]
349 pretxnchangegroup.acl = python:hgext.acl.hook
349 pretxnchangegroup.acl = python:hgext.acl.hook
350 [acl]
350 [acl]
351 sources = push
351 sources = push
352 [acl.allow]
352 [acl.allow]
353 foo/** = fred
353 foo/** = fred
354 """
354 """
355 pushing to ../b
355 pushing to ../b
356 query 1; heads
356 query 1; heads
357 searching for changes
357 searching for changes
358 all remote heads known locally
358 all remote heads known locally
359 listing keys for "phases"
359 listing keys for "phases"
360 checking for updated bookmarks
360 checking for updated bookmarks
361 listing keys for "bookmarks"
361 listing keys for "bookmarks"
362 listing keys for "bookmarks"
362 listing keys for "bookmarks"
363 3 changesets found
363 3 changesets found
364 list of changesets:
364 list of changesets:
365 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
365 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
366 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
366 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
367 911600dab2ae7a9baff75958b84fe606851ce955
367 911600dab2ae7a9baff75958b84fe606851ce955
368 bundle2-output-bundle: "HG20", 5 parts total
368 bundle2-output-bundle: "HG20", 5 parts total
369 bundle2-output-part: "replycaps" 168 bytes payload
369 bundle2-output-part: "replycaps" 178 bytes payload
370 bundle2-output-part: "check:phases" 24 bytes payload
370 bundle2-output-part: "check:phases" 24 bytes payload
371 bundle2-output-part: "check:heads" streamed payload
371 bundle2-output-part: "check:heads" streamed payload
372 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
372 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
373 bundle2-output-part: "phase-heads" 24 bytes payload
373 bundle2-output-part: "phase-heads" 24 bytes payload
374 bundle2-input-bundle: with-transaction
374 bundle2-input-bundle: with-transaction
375 bundle2-input-part: "replycaps" supported
375 bundle2-input-part: "replycaps" supported
376 bundle2-input-part: total payload size 168
376 bundle2-input-part: total payload size 178
377 bundle2-input-part: "check:phases" supported
377 bundle2-input-part: "check:phases" supported
378 bundle2-input-part: total payload size 24
378 bundle2-input-part: total payload size 24
379 bundle2-input-part: "check:heads" supported
379 bundle2-input-part: "check:heads" supported
380 bundle2-input-part: total payload size 20
380 bundle2-input-part: total payload size 20
381 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
381 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
382 adding changesets
382 adding changesets
383 add changeset ef1ea85a6374
383 add changeset ef1ea85a6374
384 add changeset f9cafe1212c8
384 add changeset f9cafe1212c8
385 add changeset 911600dab2ae
385 add changeset 911600dab2ae
386 adding manifests
386 adding manifests
387 adding file changes
387 adding file changes
388 adding foo/Bar/file.txt revisions
388 adding foo/Bar/file.txt revisions
389 adding foo/file.txt revisions
389 adding foo/file.txt revisions
390 adding quux/file.py revisions
390 adding quux/file.py revisions
391 added 3 changesets with 3 changes to 3 files
391 added 3 changesets with 3 changes to 3 files
392 calling hook pretxnchangegroup.acl: hgext.acl.hook
392 calling hook pretxnchangegroup.acl: hgext.acl.hook
393 acl: checking access for user "fred"
393 acl: checking access for user "fred"
394 acl: acl.allow.branches not enabled
394 acl: acl.allow.branches not enabled
395 acl: acl.deny.branches not enabled
395 acl: acl.deny.branches not enabled
396 acl: acl.allow enabled, 1 entries for user fred
396 acl: acl.allow enabled, 1 entries for user fred
397 acl: acl.deny not enabled
397 acl: acl.deny not enabled
398 acl: branch access granted: "ef1ea85a6374" on branch "default"
398 acl: branch access granted: "ef1ea85a6374" on branch "default"
399 acl: path access granted: "ef1ea85a6374"
399 acl: path access granted: "ef1ea85a6374"
400 acl: branch access granted: "f9cafe1212c8" on branch "default"
400 acl: branch access granted: "f9cafe1212c8" on branch "default"
401 acl: path access granted: "f9cafe1212c8"
401 acl: path access granted: "f9cafe1212c8"
402 acl: branch access granted: "911600dab2ae" on branch "default"
402 acl: branch access granted: "911600dab2ae" on branch "default"
403 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
403 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
404 bundle2-input-part: total payload size 1553
404 bundle2-input-part: total payload size 1553
405 bundle2-input-part: total payload size 24
405 bundle2-input-part: total payload size 24
406 bundle2-input-bundle: 4 parts total
406 bundle2-input-bundle: 4 parts total
407 transaction abort!
407 transaction abort!
408 rollback completed
408 rollback completed
409 abort: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
409 abort: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
410 no rollback information available
410 no rollback information available
411 0:6675d58eff77
411 0:6675d58eff77
412
412
413
413
414 Empty [acl.deny]
414 Empty [acl.deny]
415
415
416 $ echo '[acl.deny]' >> $config
416 $ echo '[acl.deny]' >> $config
417 $ do_push barney
417 $ do_push barney
418 Pushing as user barney
418 Pushing as user barney
419 hgrc = """
419 hgrc = """
420 [hooks]
420 [hooks]
421 pretxnchangegroup.acl = python:hgext.acl.hook
421 pretxnchangegroup.acl = python:hgext.acl.hook
422 [acl]
422 [acl]
423 sources = push
423 sources = push
424 [acl.allow]
424 [acl.allow]
425 foo/** = fred
425 foo/** = fred
426 [acl.deny]
426 [acl.deny]
427 """
427 """
428 pushing to ../b
428 pushing to ../b
429 query 1; heads
429 query 1; heads
430 searching for changes
430 searching for changes
431 all remote heads known locally
431 all remote heads known locally
432 listing keys for "phases"
432 listing keys for "phases"
433 checking for updated bookmarks
433 checking for updated bookmarks
434 listing keys for "bookmarks"
434 listing keys for "bookmarks"
435 listing keys for "bookmarks"
435 listing keys for "bookmarks"
436 3 changesets found
436 3 changesets found
437 list of changesets:
437 list of changesets:
438 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
438 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
439 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
439 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
440 911600dab2ae7a9baff75958b84fe606851ce955
440 911600dab2ae7a9baff75958b84fe606851ce955
441 bundle2-output-bundle: "HG20", 5 parts total
441 bundle2-output-bundle: "HG20", 5 parts total
442 bundle2-output-part: "replycaps" 168 bytes payload
442 bundle2-output-part: "replycaps" 178 bytes payload
443 bundle2-output-part: "check:phases" 24 bytes payload
443 bundle2-output-part: "check:phases" 24 bytes payload
444 bundle2-output-part: "check:heads" streamed payload
444 bundle2-output-part: "check:heads" streamed payload
445 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
445 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
446 bundle2-output-part: "phase-heads" 24 bytes payload
446 bundle2-output-part: "phase-heads" 24 bytes payload
447 bundle2-input-bundle: with-transaction
447 bundle2-input-bundle: with-transaction
448 bundle2-input-part: "replycaps" supported
448 bundle2-input-part: "replycaps" supported
449 bundle2-input-part: total payload size 168
449 bundle2-input-part: total payload size 178
450 bundle2-input-part: "check:phases" supported
450 bundle2-input-part: "check:phases" supported
451 bundle2-input-part: total payload size 24
451 bundle2-input-part: total payload size 24
452 bundle2-input-part: "check:heads" supported
452 bundle2-input-part: "check:heads" supported
453 bundle2-input-part: total payload size 20
453 bundle2-input-part: total payload size 20
454 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
454 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
455 adding changesets
455 adding changesets
456 add changeset ef1ea85a6374
456 add changeset ef1ea85a6374
457 add changeset f9cafe1212c8
457 add changeset f9cafe1212c8
458 add changeset 911600dab2ae
458 add changeset 911600dab2ae
459 adding manifests
459 adding manifests
460 adding file changes
460 adding file changes
461 adding foo/Bar/file.txt revisions
461 adding foo/Bar/file.txt revisions
462 adding foo/file.txt revisions
462 adding foo/file.txt revisions
463 adding quux/file.py revisions
463 adding quux/file.py revisions
464 added 3 changesets with 3 changes to 3 files
464 added 3 changesets with 3 changes to 3 files
465 calling hook pretxnchangegroup.acl: hgext.acl.hook
465 calling hook pretxnchangegroup.acl: hgext.acl.hook
466 acl: checking access for user "barney"
466 acl: checking access for user "barney"
467 acl: acl.allow.branches not enabled
467 acl: acl.allow.branches not enabled
468 acl: acl.deny.branches not enabled
468 acl: acl.deny.branches not enabled
469 acl: acl.allow enabled, 0 entries for user barney
469 acl: acl.allow enabled, 0 entries for user barney
470 acl: acl.deny enabled, 0 entries for user barney
470 acl: acl.deny enabled, 0 entries for user barney
471 acl: branch access granted: "ef1ea85a6374" on branch "default"
471 acl: branch access granted: "ef1ea85a6374" on branch "default"
472 error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
472 error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
473 bundle2-input-part: total payload size 1553
473 bundle2-input-part: total payload size 1553
474 bundle2-input-part: total payload size 24
474 bundle2-input-part: total payload size 24
475 bundle2-input-bundle: 4 parts total
475 bundle2-input-bundle: 4 parts total
476 transaction abort!
476 transaction abort!
477 rollback completed
477 rollback completed
478 abort: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
478 abort: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
479 no rollback information available
479 no rollback information available
480 0:6675d58eff77
480 0:6675d58eff77
481
481
482
482
483 fred is allowed inside foo/, but not foo/bar/ (case matters)
483 fred is allowed inside foo/, but not foo/bar/ (case matters)
484
484
485 $ echo 'foo/bar/** = fred' >> $config
485 $ echo 'foo/bar/** = fred' >> $config
486 $ do_push fred
486 $ do_push fred
487 Pushing as user fred
487 Pushing as user fred
488 hgrc = """
488 hgrc = """
489 [hooks]
489 [hooks]
490 pretxnchangegroup.acl = python:hgext.acl.hook
490 pretxnchangegroup.acl = python:hgext.acl.hook
491 [acl]
491 [acl]
492 sources = push
492 sources = push
493 [acl.allow]
493 [acl.allow]
494 foo/** = fred
494 foo/** = fred
495 [acl.deny]
495 [acl.deny]
496 foo/bar/** = fred
496 foo/bar/** = fred
497 """
497 """
498 pushing to ../b
498 pushing to ../b
499 query 1; heads
499 query 1; heads
500 searching for changes
500 searching for changes
501 all remote heads known locally
501 all remote heads known locally
502 listing keys for "phases"
502 listing keys for "phases"
503 checking for updated bookmarks
503 checking for updated bookmarks
504 listing keys for "bookmarks"
504 listing keys for "bookmarks"
505 listing keys for "bookmarks"
505 listing keys for "bookmarks"
506 3 changesets found
506 3 changesets found
507 list of changesets:
507 list of changesets:
508 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
508 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
509 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
509 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
510 911600dab2ae7a9baff75958b84fe606851ce955
510 911600dab2ae7a9baff75958b84fe606851ce955
511 bundle2-output-bundle: "HG20", 5 parts total
511 bundle2-output-bundle: "HG20", 5 parts total
512 bundle2-output-part: "replycaps" 168 bytes payload
512 bundle2-output-part: "replycaps" 178 bytes payload
513 bundle2-output-part: "check:phases" 24 bytes payload
513 bundle2-output-part: "check:phases" 24 bytes payload
514 bundle2-output-part: "check:heads" streamed payload
514 bundle2-output-part: "check:heads" streamed payload
515 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
515 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
516 bundle2-output-part: "phase-heads" 24 bytes payload
516 bundle2-output-part: "phase-heads" 24 bytes payload
517 bundle2-input-bundle: with-transaction
517 bundle2-input-bundle: with-transaction
518 bundle2-input-part: "replycaps" supported
518 bundle2-input-part: "replycaps" supported
519 bundle2-input-part: total payload size 168
519 bundle2-input-part: total payload size 178
520 bundle2-input-part: "check:phases" supported
520 bundle2-input-part: "check:phases" supported
521 bundle2-input-part: total payload size 24
521 bundle2-input-part: total payload size 24
522 bundle2-input-part: "check:heads" supported
522 bundle2-input-part: "check:heads" supported
523 bundle2-input-part: total payload size 20
523 bundle2-input-part: total payload size 20
524 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
524 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
525 adding changesets
525 adding changesets
526 add changeset ef1ea85a6374
526 add changeset ef1ea85a6374
527 add changeset f9cafe1212c8
527 add changeset f9cafe1212c8
528 add changeset 911600dab2ae
528 add changeset 911600dab2ae
529 adding manifests
529 adding manifests
530 adding file changes
530 adding file changes
531 adding foo/Bar/file.txt revisions
531 adding foo/Bar/file.txt revisions
532 adding foo/file.txt revisions
532 adding foo/file.txt revisions
533 adding quux/file.py revisions
533 adding quux/file.py revisions
534 added 3 changesets with 3 changes to 3 files
534 added 3 changesets with 3 changes to 3 files
535 calling hook pretxnchangegroup.acl: hgext.acl.hook
535 calling hook pretxnchangegroup.acl: hgext.acl.hook
536 acl: checking access for user "fred"
536 acl: checking access for user "fred"
537 acl: acl.allow.branches not enabled
537 acl: acl.allow.branches not enabled
538 acl: acl.deny.branches not enabled
538 acl: acl.deny.branches not enabled
539 acl: acl.allow enabled, 1 entries for user fred
539 acl: acl.allow enabled, 1 entries for user fred
540 acl: acl.deny enabled, 1 entries for user fred
540 acl: acl.deny enabled, 1 entries for user fred
541 acl: branch access granted: "ef1ea85a6374" on branch "default"
541 acl: branch access granted: "ef1ea85a6374" on branch "default"
542 acl: path access granted: "ef1ea85a6374"
542 acl: path access granted: "ef1ea85a6374"
543 acl: branch access granted: "f9cafe1212c8" on branch "default"
543 acl: branch access granted: "f9cafe1212c8" on branch "default"
544 acl: path access granted: "f9cafe1212c8"
544 acl: path access granted: "f9cafe1212c8"
545 acl: branch access granted: "911600dab2ae" on branch "default"
545 acl: branch access granted: "911600dab2ae" on branch "default"
546 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
546 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
547 bundle2-input-part: total payload size 1553
547 bundle2-input-part: total payload size 1553
548 bundle2-input-part: total payload size 24
548 bundle2-input-part: total payload size 24
549 bundle2-input-bundle: 4 parts total
549 bundle2-input-bundle: 4 parts total
550 transaction abort!
550 transaction abort!
551 rollback completed
551 rollback completed
552 abort: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
552 abort: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
553 no rollback information available
553 no rollback information available
554 0:6675d58eff77
554 0:6675d58eff77
555
555
556
556
557 fred is allowed inside foo/, but not foo/Bar/
557 fred is allowed inside foo/, but not foo/Bar/
558
558
559 $ echo 'foo/Bar/** = fred' >> $config
559 $ echo 'foo/Bar/** = fred' >> $config
560 $ do_push fred
560 $ do_push fred
561 Pushing as user fred
561 Pushing as user fred
562 hgrc = """
562 hgrc = """
563 [hooks]
563 [hooks]
564 pretxnchangegroup.acl = python:hgext.acl.hook
564 pretxnchangegroup.acl = python:hgext.acl.hook
565 [acl]
565 [acl]
566 sources = push
566 sources = push
567 [acl.allow]
567 [acl.allow]
568 foo/** = fred
568 foo/** = fred
569 [acl.deny]
569 [acl.deny]
570 foo/bar/** = fred
570 foo/bar/** = fred
571 foo/Bar/** = fred
571 foo/Bar/** = fred
572 """
572 """
573 pushing to ../b
573 pushing to ../b
574 query 1; heads
574 query 1; heads
575 searching for changes
575 searching for changes
576 all remote heads known locally
576 all remote heads known locally
577 listing keys for "phases"
577 listing keys for "phases"
578 checking for updated bookmarks
578 checking for updated bookmarks
579 listing keys for "bookmarks"
579 listing keys for "bookmarks"
580 listing keys for "bookmarks"
580 listing keys for "bookmarks"
581 3 changesets found
581 3 changesets found
582 list of changesets:
582 list of changesets:
583 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
583 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
584 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
584 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
585 911600dab2ae7a9baff75958b84fe606851ce955
585 911600dab2ae7a9baff75958b84fe606851ce955
586 bundle2-output-bundle: "HG20", 5 parts total
586 bundle2-output-bundle: "HG20", 5 parts total
587 bundle2-output-part: "replycaps" 168 bytes payload
587 bundle2-output-part: "replycaps" 178 bytes payload
588 bundle2-output-part: "check:phases" 24 bytes payload
588 bundle2-output-part: "check:phases" 24 bytes payload
589 bundle2-output-part: "check:heads" streamed payload
589 bundle2-output-part: "check:heads" streamed payload
590 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
590 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
591 bundle2-output-part: "phase-heads" 24 bytes payload
591 bundle2-output-part: "phase-heads" 24 bytes payload
592 bundle2-input-bundle: with-transaction
592 bundle2-input-bundle: with-transaction
593 bundle2-input-part: "replycaps" supported
593 bundle2-input-part: "replycaps" supported
594 bundle2-input-part: total payload size 168
594 bundle2-input-part: total payload size 178
595 bundle2-input-part: "check:phases" supported
595 bundle2-input-part: "check:phases" supported
596 bundle2-input-part: total payload size 24
596 bundle2-input-part: total payload size 24
597 bundle2-input-part: "check:heads" supported
597 bundle2-input-part: "check:heads" supported
598 bundle2-input-part: total payload size 20
598 bundle2-input-part: total payload size 20
599 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
599 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
600 adding changesets
600 adding changesets
601 add changeset ef1ea85a6374
601 add changeset ef1ea85a6374
602 add changeset f9cafe1212c8
602 add changeset f9cafe1212c8
603 add changeset 911600dab2ae
603 add changeset 911600dab2ae
604 adding manifests
604 adding manifests
605 adding file changes
605 adding file changes
606 adding foo/Bar/file.txt revisions
606 adding foo/Bar/file.txt revisions
607 adding foo/file.txt revisions
607 adding foo/file.txt revisions
608 adding quux/file.py revisions
608 adding quux/file.py revisions
609 added 3 changesets with 3 changes to 3 files
609 added 3 changesets with 3 changes to 3 files
610 calling hook pretxnchangegroup.acl: hgext.acl.hook
610 calling hook pretxnchangegroup.acl: hgext.acl.hook
611 acl: checking access for user "fred"
611 acl: checking access for user "fred"
612 acl: acl.allow.branches not enabled
612 acl: acl.allow.branches not enabled
613 acl: acl.deny.branches not enabled
613 acl: acl.deny.branches not enabled
614 acl: acl.allow enabled, 1 entries for user fred
614 acl: acl.allow enabled, 1 entries for user fred
615 acl: acl.deny enabled, 2 entries for user fred
615 acl: acl.deny enabled, 2 entries for user fred
616 acl: branch access granted: "ef1ea85a6374" on branch "default"
616 acl: branch access granted: "ef1ea85a6374" on branch "default"
617 acl: path access granted: "ef1ea85a6374"
617 acl: path access granted: "ef1ea85a6374"
618 acl: branch access granted: "f9cafe1212c8" on branch "default"
618 acl: branch access granted: "f9cafe1212c8" on branch "default"
619 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
619 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
620 bundle2-input-part: total payload size 1553
620 bundle2-input-part: total payload size 1553
621 bundle2-input-part: total payload size 24
621 bundle2-input-part: total payload size 24
622 bundle2-input-bundle: 4 parts total
622 bundle2-input-bundle: 4 parts total
623 transaction abort!
623 transaction abort!
624 rollback completed
624 rollback completed
625 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
625 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
626 no rollback information available
626 no rollback information available
627 0:6675d58eff77
627 0:6675d58eff77
628
628
629
629
630 $ echo 'barney is not mentioned => not allowed anywhere'
630 $ echo 'barney is not mentioned => not allowed anywhere'
631 barney is not mentioned => not allowed anywhere
631 barney is not mentioned => not allowed anywhere
632 $ do_push barney
632 $ do_push barney
633 Pushing as user barney
633 Pushing as user barney
634 hgrc = """
634 hgrc = """
635 [hooks]
635 [hooks]
636 pretxnchangegroup.acl = python:hgext.acl.hook
636 pretxnchangegroup.acl = python:hgext.acl.hook
637 [acl]
637 [acl]
638 sources = push
638 sources = push
639 [acl.allow]
639 [acl.allow]
640 foo/** = fred
640 foo/** = fred
641 [acl.deny]
641 [acl.deny]
642 foo/bar/** = fred
642 foo/bar/** = fred
643 foo/Bar/** = fred
643 foo/Bar/** = fred
644 """
644 """
645 pushing to ../b
645 pushing to ../b
646 query 1; heads
646 query 1; heads
647 searching for changes
647 searching for changes
648 all remote heads known locally
648 all remote heads known locally
649 listing keys for "phases"
649 listing keys for "phases"
650 checking for updated bookmarks
650 checking for updated bookmarks
651 listing keys for "bookmarks"
651 listing keys for "bookmarks"
652 listing keys for "bookmarks"
652 listing keys for "bookmarks"
653 3 changesets found
653 3 changesets found
654 list of changesets:
654 list of changesets:
655 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
655 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
656 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
656 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
657 911600dab2ae7a9baff75958b84fe606851ce955
657 911600dab2ae7a9baff75958b84fe606851ce955
658 bundle2-output-bundle: "HG20", 5 parts total
658 bundle2-output-bundle: "HG20", 5 parts total
659 bundle2-output-part: "replycaps" 168 bytes payload
659 bundle2-output-part: "replycaps" 178 bytes payload
660 bundle2-output-part: "check:phases" 24 bytes payload
660 bundle2-output-part: "check:phases" 24 bytes payload
661 bundle2-output-part: "check:heads" streamed payload
661 bundle2-output-part: "check:heads" streamed payload
662 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
662 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
663 bundle2-output-part: "phase-heads" 24 bytes payload
663 bundle2-output-part: "phase-heads" 24 bytes payload
664 bundle2-input-bundle: with-transaction
664 bundle2-input-bundle: with-transaction
665 bundle2-input-part: "replycaps" supported
665 bundle2-input-part: "replycaps" supported
666 bundle2-input-part: total payload size 168
666 bundle2-input-part: total payload size 178
667 bundle2-input-part: "check:phases" supported
667 bundle2-input-part: "check:phases" supported
668 bundle2-input-part: total payload size 24
668 bundle2-input-part: total payload size 24
669 bundle2-input-part: "check:heads" supported
669 bundle2-input-part: "check:heads" supported
670 bundle2-input-part: total payload size 20
670 bundle2-input-part: total payload size 20
671 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
671 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
672 adding changesets
672 adding changesets
673 add changeset ef1ea85a6374
673 add changeset ef1ea85a6374
674 add changeset f9cafe1212c8
674 add changeset f9cafe1212c8
675 add changeset 911600dab2ae
675 add changeset 911600dab2ae
676 adding manifests
676 adding manifests
677 adding file changes
677 adding file changes
678 adding foo/Bar/file.txt revisions
678 adding foo/Bar/file.txt revisions
679 adding foo/file.txt revisions
679 adding foo/file.txt revisions
680 adding quux/file.py revisions
680 adding quux/file.py revisions
681 added 3 changesets with 3 changes to 3 files
681 added 3 changesets with 3 changes to 3 files
682 calling hook pretxnchangegroup.acl: hgext.acl.hook
682 calling hook pretxnchangegroup.acl: hgext.acl.hook
683 acl: checking access for user "barney"
683 acl: checking access for user "barney"
684 acl: acl.allow.branches not enabled
684 acl: acl.allow.branches not enabled
685 acl: acl.deny.branches not enabled
685 acl: acl.deny.branches not enabled
686 acl: acl.allow enabled, 0 entries for user barney
686 acl: acl.allow enabled, 0 entries for user barney
687 acl: acl.deny enabled, 0 entries for user barney
687 acl: acl.deny enabled, 0 entries for user barney
688 acl: branch access granted: "ef1ea85a6374" on branch "default"
688 acl: branch access granted: "ef1ea85a6374" on branch "default"
689 error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
689 error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
690 bundle2-input-part: total payload size 1553
690 bundle2-input-part: total payload size 1553
691 bundle2-input-part: total payload size 24
691 bundle2-input-part: total payload size 24
692 bundle2-input-bundle: 4 parts total
692 bundle2-input-bundle: 4 parts total
693 transaction abort!
693 transaction abort!
694 rollback completed
694 rollback completed
695 abort: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
695 abort: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
696 no rollback information available
696 no rollback information available
697 0:6675d58eff77
697 0:6675d58eff77
698
698
699
699
700 barney is allowed everywhere
700 barney is allowed everywhere
701
701
702 $ echo '[acl.allow]' >> $config
702 $ echo '[acl.allow]' >> $config
703 $ echo '** = barney' >> $config
703 $ echo '** = barney' >> $config
704 $ do_push barney
704 $ do_push barney
705 Pushing as user barney
705 Pushing as user barney
706 hgrc = """
706 hgrc = """
707 [hooks]
707 [hooks]
708 pretxnchangegroup.acl = python:hgext.acl.hook
708 pretxnchangegroup.acl = python:hgext.acl.hook
709 [acl]
709 [acl]
710 sources = push
710 sources = push
711 [acl.allow]
711 [acl.allow]
712 foo/** = fred
712 foo/** = fred
713 [acl.deny]
713 [acl.deny]
714 foo/bar/** = fred
714 foo/bar/** = fred
715 foo/Bar/** = fred
715 foo/Bar/** = fred
716 [acl.allow]
716 [acl.allow]
717 ** = barney
717 ** = barney
718 """
718 """
719 pushing to ../b
719 pushing to ../b
720 query 1; heads
720 query 1; heads
721 searching for changes
721 searching for changes
722 all remote heads known locally
722 all remote heads known locally
723 listing keys for "phases"
723 listing keys for "phases"
724 checking for updated bookmarks
724 checking for updated bookmarks
725 listing keys for "bookmarks"
725 listing keys for "bookmarks"
726 listing keys for "bookmarks"
726 listing keys for "bookmarks"
727 3 changesets found
727 3 changesets found
728 list of changesets:
728 list of changesets:
729 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
729 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
730 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
730 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
731 911600dab2ae7a9baff75958b84fe606851ce955
731 911600dab2ae7a9baff75958b84fe606851ce955
732 bundle2-output-bundle: "HG20", 5 parts total
732 bundle2-output-bundle: "HG20", 5 parts total
733 bundle2-output-part: "replycaps" 168 bytes payload
733 bundle2-output-part: "replycaps" 178 bytes payload
734 bundle2-output-part: "check:phases" 24 bytes payload
734 bundle2-output-part: "check:phases" 24 bytes payload
735 bundle2-output-part: "check:heads" streamed payload
735 bundle2-output-part: "check:heads" streamed payload
736 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
736 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
737 bundle2-output-part: "phase-heads" 24 bytes payload
737 bundle2-output-part: "phase-heads" 24 bytes payload
738 bundle2-input-bundle: with-transaction
738 bundle2-input-bundle: with-transaction
739 bundle2-input-part: "replycaps" supported
739 bundle2-input-part: "replycaps" supported
740 bundle2-input-part: total payload size 168
740 bundle2-input-part: total payload size 178
741 bundle2-input-part: "check:phases" supported
741 bundle2-input-part: "check:phases" supported
742 bundle2-input-part: total payload size 24
742 bundle2-input-part: total payload size 24
743 bundle2-input-part: "check:heads" supported
743 bundle2-input-part: "check:heads" supported
744 bundle2-input-part: total payload size 20
744 bundle2-input-part: total payload size 20
745 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
745 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
746 adding changesets
746 adding changesets
747 add changeset ef1ea85a6374
747 add changeset ef1ea85a6374
748 add changeset f9cafe1212c8
748 add changeset f9cafe1212c8
749 add changeset 911600dab2ae
749 add changeset 911600dab2ae
750 adding manifests
750 adding manifests
751 adding file changes
751 adding file changes
752 adding foo/Bar/file.txt revisions
752 adding foo/Bar/file.txt revisions
753 adding foo/file.txt revisions
753 adding foo/file.txt revisions
754 adding quux/file.py revisions
754 adding quux/file.py revisions
755 added 3 changesets with 3 changes to 3 files
755 added 3 changesets with 3 changes to 3 files
756 calling hook pretxnchangegroup.acl: hgext.acl.hook
756 calling hook pretxnchangegroup.acl: hgext.acl.hook
757 acl: checking access for user "barney"
757 acl: checking access for user "barney"
758 acl: acl.allow.branches not enabled
758 acl: acl.allow.branches not enabled
759 acl: acl.deny.branches not enabled
759 acl: acl.deny.branches not enabled
760 acl: acl.allow enabled, 1 entries for user barney
760 acl: acl.allow enabled, 1 entries for user barney
761 acl: acl.deny enabled, 0 entries for user barney
761 acl: acl.deny enabled, 0 entries for user barney
762 acl: branch access granted: "ef1ea85a6374" on branch "default"
762 acl: branch access granted: "ef1ea85a6374" on branch "default"
763 acl: path access granted: "ef1ea85a6374"
763 acl: path access granted: "ef1ea85a6374"
764 acl: branch access granted: "f9cafe1212c8" on branch "default"
764 acl: branch access granted: "f9cafe1212c8" on branch "default"
765 acl: path access granted: "f9cafe1212c8"
765 acl: path access granted: "f9cafe1212c8"
766 acl: branch access granted: "911600dab2ae" on branch "default"
766 acl: branch access granted: "911600dab2ae" on branch "default"
767 acl: path access granted: "911600dab2ae"
767 acl: path access granted: "911600dab2ae"
768 bundle2-input-part: total payload size 1553
768 bundle2-input-part: total payload size 1553
769 bundle2-input-part: "phase-heads" supported
769 bundle2-input-part: "phase-heads" supported
770 bundle2-input-part: total payload size 24
770 bundle2-input-part: total payload size 24
771 bundle2-input-bundle: 4 parts total
771 bundle2-input-bundle: 4 parts total
772 updating the branch cache
772 updating the branch cache
773 bundle2-output-bundle: "HG20", 1 parts total
773 bundle2-output-bundle: "HG20", 1 parts total
774 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
774 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
775 bundle2-input-bundle: no-transaction
775 bundle2-input-bundle: no-transaction
776 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
776 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
777 bundle2-input-bundle: 0 parts total
777 bundle2-input-bundle: 0 parts total
778 listing keys for "phases"
778 listing keys for "phases"
779 repository tip rolled back to revision 0 (undo push)
779 repository tip rolled back to revision 0 (undo push)
780 0:6675d58eff77
780 0:6675d58eff77
781
781
782
782
783 wilma can change files with a .txt extension
783 wilma can change files with a .txt extension
784
784
785 $ echo '**/*.txt = wilma' >> $config
785 $ echo '**/*.txt = wilma' >> $config
786 $ do_push wilma
786 $ do_push wilma
787 Pushing as user wilma
787 Pushing as user wilma
788 hgrc = """
788 hgrc = """
789 [hooks]
789 [hooks]
790 pretxnchangegroup.acl = python:hgext.acl.hook
790 pretxnchangegroup.acl = python:hgext.acl.hook
791 [acl]
791 [acl]
792 sources = push
792 sources = push
793 [acl.allow]
793 [acl.allow]
794 foo/** = fred
794 foo/** = fred
795 [acl.deny]
795 [acl.deny]
796 foo/bar/** = fred
796 foo/bar/** = fred
797 foo/Bar/** = fred
797 foo/Bar/** = fred
798 [acl.allow]
798 [acl.allow]
799 ** = barney
799 ** = barney
800 **/*.txt = wilma
800 **/*.txt = wilma
801 """
801 """
802 pushing to ../b
802 pushing to ../b
803 query 1; heads
803 query 1; heads
804 searching for changes
804 searching for changes
805 all remote heads known locally
805 all remote heads known locally
806 listing keys for "phases"
806 listing keys for "phases"
807 checking for updated bookmarks
807 checking for updated bookmarks
808 listing keys for "bookmarks"
808 listing keys for "bookmarks"
809 listing keys for "bookmarks"
809 listing keys for "bookmarks"
810 3 changesets found
810 3 changesets found
811 list of changesets:
811 list of changesets:
812 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
812 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
813 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
813 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
814 911600dab2ae7a9baff75958b84fe606851ce955
814 911600dab2ae7a9baff75958b84fe606851ce955
815 bundle2-output-bundle: "HG20", 5 parts total
815 bundle2-output-bundle: "HG20", 5 parts total
816 bundle2-output-part: "replycaps" 168 bytes payload
816 bundle2-output-part: "replycaps" 178 bytes payload
817 bundle2-output-part: "check:phases" 24 bytes payload
817 bundle2-output-part: "check:phases" 24 bytes payload
818 bundle2-output-part: "check:heads" streamed payload
818 bundle2-output-part: "check:heads" streamed payload
819 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
819 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
820 bundle2-output-part: "phase-heads" 24 bytes payload
820 bundle2-output-part: "phase-heads" 24 bytes payload
821 bundle2-input-bundle: with-transaction
821 bundle2-input-bundle: with-transaction
822 bundle2-input-part: "replycaps" supported
822 bundle2-input-part: "replycaps" supported
823 bundle2-input-part: total payload size 168
823 bundle2-input-part: total payload size 178
824 bundle2-input-part: "check:phases" supported
824 bundle2-input-part: "check:phases" supported
825 bundle2-input-part: total payload size 24
825 bundle2-input-part: total payload size 24
826 bundle2-input-part: "check:heads" supported
826 bundle2-input-part: "check:heads" supported
827 bundle2-input-part: total payload size 20
827 bundle2-input-part: total payload size 20
828 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
828 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
829 adding changesets
829 adding changesets
830 add changeset ef1ea85a6374
830 add changeset ef1ea85a6374
831 add changeset f9cafe1212c8
831 add changeset f9cafe1212c8
832 add changeset 911600dab2ae
832 add changeset 911600dab2ae
833 adding manifests
833 adding manifests
834 adding file changes
834 adding file changes
835 adding foo/Bar/file.txt revisions
835 adding foo/Bar/file.txt revisions
836 adding foo/file.txt revisions
836 adding foo/file.txt revisions
837 adding quux/file.py revisions
837 adding quux/file.py revisions
838 added 3 changesets with 3 changes to 3 files
838 added 3 changesets with 3 changes to 3 files
839 calling hook pretxnchangegroup.acl: hgext.acl.hook
839 calling hook pretxnchangegroup.acl: hgext.acl.hook
840 acl: checking access for user "wilma"
840 acl: checking access for user "wilma"
841 acl: acl.allow.branches not enabled
841 acl: acl.allow.branches not enabled
842 acl: acl.deny.branches not enabled
842 acl: acl.deny.branches not enabled
843 acl: acl.allow enabled, 1 entries for user wilma
843 acl: acl.allow enabled, 1 entries for user wilma
844 acl: acl.deny enabled, 0 entries for user wilma
844 acl: acl.deny enabled, 0 entries for user wilma
845 acl: branch access granted: "ef1ea85a6374" on branch "default"
845 acl: branch access granted: "ef1ea85a6374" on branch "default"
846 acl: path access granted: "ef1ea85a6374"
846 acl: path access granted: "ef1ea85a6374"
847 acl: branch access granted: "f9cafe1212c8" on branch "default"
847 acl: branch access granted: "f9cafe1212c8" on branch "default"
848 acl: path access granted: "f9cafe1212c8"
848 acl: path access granted: "f9cafe1212c8"
849 acl: branch access granted: "911600dab2ae" on branch "default"
849 acl: branch access granted: "911600dab2ae" on branch "default"
850 error: pretxnchangegroup.acl hook failed: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae")
850 error: pretxnchangegroup.acl hook failed: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae")
851 bundle2-input-part: total payload size 1553
851 bundle2-input-part: total payload size 1553
852 bundle2-input-part: total payload size 24
852 bundle2-input-part: total payload size 24
853 bundle2-input-bundle: 4 parts total
853 bundle2-input-bundle: 4 parts total
854 transaction abort!
854 transaction abort!
855 rollback completed
855 rollback completed
856 abort: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae")
856 abort: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae")
857 no rollback information available
857 no rollback information available
858 0:6675d58eff77
858 0:6675d58eff77
859
859
860
860
861 file specified by acl.config does not exist
861 file specified by acl.config does not exist
862
862
863 $ echo '[acl]' >> $config
863 $ echo '[acl]' >> $config
864 $ echo 'config = ../acl.config' >> $config
864 $ echo 'config = ../acl.config' >> $config
865 $ do_push barney
865 $ do_push barney
866 Pushing as user barney
866 Pushing as user barney
867 hgrc = """
867 hgrc = """
868 [hooks]
868 [hooks]
869 pretxnchangegroup.acl = python:hgext.acl.hook
869 pretxnchangegroup.acl = python:hgext.acl.hook
870 [acl]
870 [acl]
871 sources = push
871 sources = push
872 [acl.allow]
872 [acl.allow]
873 foo/** = fred
873 foo/** = fred
874 [acl.deny]
874 [acl.deny]
875 foo/bar/** = fred
875 foo/bar/** = fred
876 foo/Bar/** = fred
876 foo/Bar/** = fred
877 [acl.allow]
877 [acl.allow]
878 ** = barney
878 ** = barney
879 **/*.txt = wilma
879 **/*.txt = wilma
880 [acl]
880 [acl]
881 config = ../acl.config
881 config = ../acl.config
882 """
882 """
883 pushing to ../b
883 pushing to ../b
884 query 1; heads
884 query 1; heads
885 searching for changes
885 searching for changes
886 all remote heads known locally
886 all remote heads known locally
887 listing keys for "phases"
887 listing keys for "phases"
888 checking for updated bookmarks
888 checking for updated bookmarks
889 listing keys for "bookmarks"
889 listing keys for "bookmarks"
890 listing keys for "bookmarks"
890 listing keys for "bookmarks"
891 3 changesets found
891 3 changesets found
892 list of changesets:
892 list of changesets:
893 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
893 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
894 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
894 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
895 911600dab2ae7a9baff75958b84fe606851ce955
895 911600dab2ae7a9baff75958b84fe606851ce955
896 bundle2-output-bundle: "HG20", 5 parts total
896 bundle2-output-bundle: "HG20", 5 parts total
897 bundle2-output-part: "replycaps" 168 bytes payload
897 bundle2-output-part: "replycaps" 178 bytes payload
898 bundle2-output-part: "check:phases" 24 bytes payload
898 bundle2-output-part: "check:phases" 24 bytes payload
899 bundle2-output-part: "check:heads" streamed payload
899 bundle2-output-part: "check:heads" streamed payload
900 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
900 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
901 bundle2-output-part: "phase-heads" 24 bytes payload
901 bundle2-output-part: "phase-heads" 24 bytes payload
902 bundle2-input-bundle: with-transaction
902 bundle2-input-bundle: with-transaction
903 bundle2-input-part: "replycaps" supported
903 bundle2-input-part: "replycaps" supported
904 bundle2-input-part: total payload size 168
904 bundle2-input-part: total payload size 178
905 bundle2-input-part: "check:phases" supported
905 bundle2-input-part: "check:phases" supported
906 bundle2-input-part: total payload size 24
906 bundle2-input-part: total payload size 24
907 bundle2-input-part: "check:heads" supported
907 bundle2-input-part: "check:heads" supported
908 bundle2-input-part: total payload size 20
908 bundle2-input-part: total payload size 20
909 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
909 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
910 adding changesets
910 adding changesets
911 add changeset ef1ea85a6374
911 add changeset ef1ea85a6374
912 add changeset f9cafe1212c8
912 add changeset f9cafe1212c8
913 add changeset 911600dab2ae
913 add changeset 911600dab2ae
914 adding manifests
914 adding manifests
915 adding file changes
915 adding file changes
916 adding foo/Bar/file.txt revisions
916 adding foo/Bar/file.txt revisions
917 adding foo/file.txt revisions
917 adding foo/file.txt revisions
918 adding quux/file.py revisions
918 adding quux/file.py revisions
919 added 3 changesets with 3 changes to 3 files
919 added 3 changesets with 3 changes to 3 files
920 calling hook pretxnchangegroup.acl: hgext.acl.hook
920 calling hook pretxnchangegroup.acl: hgext.acl.hook
921 acl: checking access for user "barney"
921 acl: checking access for user "barney"
922 error: pretxnchangegroup.acl hook raised an exception: [Errno *] * (glob)
922 error: pretxnchangegroup.acl hook raised an exception: [Errno *] * (glob)
923 bundle2-input-part: total payload size 1553
923 bundle2-input-part: total payload size 1553
924 bundle2-input-part: total payload size 24
924 bundle2-input-part: total payload size 24
925 bundle2-input-bundle: 4 parts total
925 bundle2-input-bundle: 4 parts total
926 transaction abort!
926 transaction abort!
927 rollback completed
927 rollback completed
928 abort: $ENOENT$: ../acl.config
928 abort: $ENOENT$: ../acl.config
929 no rollback information available
929 no rollback information available
930 0:6675d58eff77
930 0:6675d58eff77
931
931
932
932
933 betty is allowed inside foo/ by a acl.config file
933 betty is allowed inside foo/ by a acl.config file
934
934
935 $ echo '[acl.allow]' >> acl.config
935 $ echo '[acl.allow]' >> acl.config
936 $ echo 'foo/** = betty' >> acl.config
936 $ echo 'foo/** = betty' >> acl.config
937 $ do_push betty
937 $ do_push betty
938 Pushing as user betty
938 Pushing as user betty
939 hgrc = """
939 hgrc = """
940 [hooks]
940 [hooks]
941 pretxnchangegroup.acl = python:hgext.acl.hook
941 pretxnchangegroup.acl = python:hgext.acl.hook
942 [acl]
942 [acl]
943 sources = push
943 sources = push
944 [acl.allow]
944 [acl.allow]
945 foo/** = fred
945 foo/** = fred
946 [acl.deny]
946 [acl.deny]
947 foo/bar/** = fred
947 foo/bar/** = fred
948 foo/Bar/** = fred
948 foo/Bar/** = fred
949 [acl.allow]
949 [acl.allow]
950 ** = barney
950 ** = barney
951 **/*.txt = wilma
951 **/*.txt = wilma
952 [acl]
952 [acl]
953 config = ../acl.config
953 config = ../acl.config
954 """
954 """
955 acl.config = """
955 acl.config = """
956 [acl.allow]
956 [acl.allow]
957 foo/** = betty
957 foo/** = betty
958 """
958 """
959 pushing to ../b
959 pushing to ../b
960 query 1; heads
960 query 1; heads
961 searching for changes
961 searching for changes
962 all remote heads known locally
962 all remote heads known locally
963 listing keys for "phases"
963 listing keys for "phases"
964 checking for updated bookmarks
964 checking for updated bookmarks
965 listing keys for "bookmarks"
965 listing keys for "bookmarks"
966 listing keys for "bookmarks"
966 listing keys for "bookmarks"
967 3 changesets found
967 3 changesets found
968 list of changesets:
968 list of changesets:
969 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
969 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
970 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
970 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
971 911600dab2ae7a9baff75958b84fe606851ce955
971 911600dab2ae7a9baff75958b84fe606851ce955
972 bundle2-output-bundle: "HG20", 5 parts total
972 bundle2-output-bundle: "HG20", 5 parts total
973 bundle2-output-part: "replycaps" 168 bytes payload
973 bundle2-output-part: "replycaps" 178 bytes payload
974 bundle2-output-part: "check:phases" 24 bytes payload
974 bundle2-output-part: "check:phases" 24 bytes payload
975 bundle2-output-part: "check:heads" streamed payload
975 bundle2-output-part: "check:heads" streamed payload
976 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
976 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
977 bundle2-output-part: "phase-heads" 24 bytes payload
977 bundle2-output-part: "phase-heads" 24 bytes payload
978 bundle2-input-bundle: with-transaction
978 bundle2-input-bundle: with-transaction
979 bundle2-input-part: "replycaps" supported
979 bundle2-input-part: "replycaps" supported
980 bundle2-input-part: total payload size 168
980 bundle2-input-part: total payload size 178
981 bundle2-input-part: "check:phases" supported
981 bundle2-input-part: "check:phases" supported
982 bundle2-input-part: total payload size 24
982 bundle2-input-part: total payload size 24
983 bundle2-input-part: "check:heads" supported
983 bundle2-input-part: "check:heads" supported
984 bundle2-input-part: total payload size 20
984 bundle2-input-part: total payload size 20
985 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
985 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
986 adding changesets
986 adding changesets
987 add changeset ef1ea85a6374
987 add changeset ef1ea85a6374
988 add changeset f9cafe1212c8
988 add changeset f9cafe1212c8
989 add changeset 911600dab2ae
989 add changeset 911600dab2ae
990 adding manifests
990 adding manifests
991 adding file changes
991 adding file changes
992 adding foo/Bar/file.txt revisions
992 adding foo/Bar/file.txt revisions
993 adding foo/file.txt revisions
993 adding foo/file.txt revisions
994 adding quux/file.py revisions
994 adding quux/file.py revisions
995 added 3 changesets with 3 changes to 3 files
995 added 3 changesets with 3 changes to 3 files
996 calling hook pretxnchangegroup.acl: hgext.acl.hook
996 calling hook pretxnchangegroup.acl: hgext.acl.hook
997 acl: checking access for user "betty"
997 acl: checking access for user "betty"
998 acl: acl.allow.branches not enabled
998 acl: acl.allow.branches not enabled
999 acl: acl.deny.branches not enabled
999 acl: acl.deny.branches not enabled
1000 acl: acl.allow enabled, 1 entries for user betty
1000 acl: acl.allow enabled, 1 entries for user betty
1001 acl: acl.deny enabled, 0 entries for user betty
1001 acl: acl.deny enabled, 0 entries for user betty
1002 acl: branch access granted: "ef1ea85a6374" on branch "default"
1002 acl: branch access granted: "ef1ea85a6374" on branch "default"
1003 acl: path access granted: "ef1ea85a6374"
1003 acl: path access granted: "ef1ea85a6374"
1004 acl: branch access granted: "f9cafe1212c8" on branch "default"
1004 acl: branch access granted: "f9cafe1212c8" on branch "default"
1005 acl: path access granted: "f9cafe1212c8"
1005 acl: path access granted: "f9cafe1212c8"
1006 acl: branch access granted: "911600dab2ae" on branch "default"
1006 acl: branch access granted: "911600dab2ae" on branch "default"
1007 error: pretxnchangegroup.acl hook failed: acl: user "betty" not allowed on "quux/file.py" (changeset "911600dab2ae")
1007 error: pretxnchangegroup.acl hook failed: acl: user "betty" not allowed on "quux/file.py" (changeset "911600dab2ae")
1008 bundle2-input-part: total payload size 1553
1008 bundle2-input-part: total payload size 1553
1009 bundle2-input-part: total payload size 24
1009 bundle2-input-part: total payload size 24
1010 bundle2-input-bundle: 4 parts total
1010 bundle2-input-bundle: 4 parts total
1011 transaction abort!
1011 transaction abort!
1012 rollback completed
1012 rollback completed
1013 abort: acl: user "betty" not allowed on "quux/file.py" (changeset "911600dab2ae")
1013 abort: acl: user "betty" not allowed on "quux/file.py" (changeset "911600dab2ae")
1014 no rollback information available
1014 no rollback information available
1015 0:6675d58eff77
1015 0:6675d58eff77
1016
1016
1017
1017
1018 acl.config can set only [acl.allow]/[acl.deny]
1018 acl.config can set only [acl.allow]/[acl.deny]
1019
1019
1020 $ echo '[hooks]' >> acl.config
1020 $ echo '[hooks]' >> acl.config
1021 $ echo 'changegroup.acl = false' >> acl.config
1021 $ echo 'changegroup.acl = false' >> acl.config
1022 $ do_push barney
1022 $ do_push barney
1023 Pushing as user barney
1023 Pushing as user barney
1024 hgrc = """
1024 hgrc = """
1025 [hooks]
1025 [hooks]
1026 pretxnchangegroup.acl = python:hgext.acl.hook
1026 pretxnchangegroup.acl = python:hgext.acl.hook
1027 [acl]
1027 [acl]
1028 sources = push
1028 sources = push
1029 [acl.allow]
1029 [acl.allow]
1030 foo/** = fred
1030 foo/** = fred
1031 [acl.deny]
1031 [acl.deny]
1032 foo/bar/** = fred
1032 foo/bar/** = fred
1033 foo/Bar/** = fred
1033 foo/Bar/** = fred
1034 [acl.allow]
1034 [acl.allow]
1035 ** = barney
1035 ** = barney
1036 **/*.txt = wilma
1036 **/*.txt = wilma
1037 [acl]
1037 [acl]
1038 config = ../acl.config
1038 config = ../acl.config
1039 """
1039 """
1040 acl.config = """
1040 acl.config = """
1041 [acl.allow]
1041 [acl.allow]
1042 foo/** = betty
1042 foo/** = betty
1043 [hooks]
1043 [hooks]
1044 changegroup.acl = false
1044 changegroup.acl = false
1045 """
1045 """
1046 pushing to ../b
1046 pushing to ../b
1047 query 1; heads
1047 query 1; heads
1048 searching for changes
1048 searching for changes
1049 all remote heads known locally
1049 all remote heads known locally
1050 listing keys for "phases"
1050 listing keys for "phases"
1051 checking for updated bookmarks
1051 checking for updated bookmarks
1052 listing keys for "bookmarks"
1052 listing keys for "bookmarks"
1053 listing keys for "bookmarks"
1053 listing keys for "bookmarks"
1054 3 changesets found
1054 3 changesets found
1055 list of changesets:
1055 list of changesets:
1056 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1056 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1057 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1057 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1058 911600dab2ae7a9baff75958b84fe606851ce955
1058 911600dab2ae7a9baff75958b84fe606851ce955
1059 bundle2-output-bundle: "HG20", 5 parts total
1059 bundle2-output-bundle: "HG20", 5 parts total
1060 bundle2-output-part: "replycaps" 168 bytes payload
1060 bundle2-output-part: "replycaps" 178 bytes payload
1061 bundle2-output-part: "check:phases" 24 bytes payload
1061 bundle2-output-part: "check:phases" 24 bytes payload
1062 bundle2-output-part: "check:heads" streamed payload
1062 bundle2-output-part: "check:heads" streamed payload
1063 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1063 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1064 bundle2-output-part: "phase-heads" 24 bytes payload
1064 bundle2-output-part: "phase-heads" 24 bytes payload
1065 bundle2-input-bundle: with-transaction
1065 bundle2-input-bundle: with-transaction
1066 bundle2-input-part: "replycaps" supported
1066 bundle2-input-part: "replycaps" supported
1067 bundle2-input-part: total payload size 168
1067 bundle2-input-part: total payload size 178
1068 bundle2-input-part: "check:phases" supported
1068 bundle2-input-part: "check:phases" supported
1069 bundle2-input-part: total payload size 24
1069 bundle2-input-part: total payload size 24
1070 bundle2-input-part: "check:heads" supported
1070 bundle2-input-part: "check:heads" supported
1071 bundle2-input-part: total payload size 20
1071 bundle2-input-part: total payload size 20
1072 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1072 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1073 adding changesets
1073 adding changesets
1074 add changeset ef1ea85a6374
1074 add changeset ef1ea85a6374
1075 add changeset f9cafe1212c8
1075 add changeset f9cafe1212c8
1076 add changeset 911600dab2ae
1076 add changeset 911600dab2ae
1077 adding manifests
1077 adding manifests
1078 adding file changes
1078 adding file changes
1079 adding foo/Bar/file.txt revisions
1079 adding foo/Bar/file.txt revisions
1080 adding foo/file.txt revisions
1080 adding foo/file.txt revisions
1081 adding quux/file.py revisions
1081 adding quux/file.py revisions
1082 added 3 changesets with 3 changes to 3 files
1082 added 3 changesets with 3 changes to 3 files
1083 calling hook pretxnchangegroup.acl: hgext.acl.hook
1083 calling hook pretxnchangegroup.acl: hgext.acl.hook
1084 acl: checking access for user "barney"
1084 acl: checking access for user "barney"
1085 acl: acl.allow.branches not enabled
1085 acl: acl.allow.branches not enabled
1086 acl: acl.deny.branches not enabled
1086 acl: acl.deny.branches not enabled
1087 acl: acl.allow enabled, 1 entries for user barney
1087 acl: acl.allow enabled, 1 entries for user barney
1088 acl: acl.deny enabled, 0 entries for user barney
1088 acl: acl.deny enabled, 0 entries for user barney
1089 acl: branch access granted: "ef1ea85a6374" on branch "default"
1089 acl: branch access granted: "ef1ea85a6374" on branch "default"
1090 acl: path access granted: "ef1ea85a6374"
1090 acl: path access granted: "ef1ea85a6374"
1091 acl: branch access granted: "f9cafe1212c8" on branch "default"
1091 acl: branch access granted: "f9cafe1212c8" on branch "default"
1092 acl: path access granted: "f9cafe1212c8"
1092 acl: path access granted: "f9cafe1212c8"
1093 acl: branch access granted: "911600dab2ae" on branch "default"
1093 acl: branch access granted: "911600dab2ae" on branch "default"
1094 acl: path access granted: "911600dab2ae"
1094 acl: path access granted: "911600dab2ae"
1095 bundle2-input-part: total payload size 1553
1095 bundle2-input-part: total payload size 1553
1096 bundle2-input-part: "phase-heads" supported
1096 bundle2-input-part: "phase-heads" supported
1097 bundle2-input-part: total payload size 24
1097 bundle2-input-part: total payload size 24
1098 bundle2-input-bundle: 4 parts total
1098 bundle2-input-bundle: 4 parts total
1099 updating the branch cache
1099 updating the branch cache
1100 bundle2-output-bundle: "HG20", 1 parts total
1100 bundle2-output-bundle: "HG20", 1 parts total
1101 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1101 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1102 bundle2-input-bundle: no-transaction
1102 bundle2-input-bundle: no-transaction
1103 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1103 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1104 bundle2-input-bundle: 0 parts total
1104 bundle2-input-bundle: 0 parts total
1105 listing keys for "phases"
1105 listing keys for "phases"
1106 repository tip rolled back to revision 0 (undo push)
1106 repository tip rolled back to revision 0 (undo push)
1107 0:6675d58eff77
1107 0:6675d58eff77
1108
1108
1109
1109
1110 asterisk
1110 asterisk
1111
1111
1112 $ init_config
1112 $ init_config
1113
1113
1114 asterisk test
1114 asterisk test
1115
1115
1116 $ echo '[acl.allow]' >> $config
1116 $ echo '[acl.allow]' >> $config
1117 $ echo "** = fred" >> $config
1117 $ echo "** = fred" >> $config
1118
1118
1119 fred is always allowed
1119 fred is always allowed
1120
1120
1121 $ do_push fred
1121 $ do_push fred
1122 Pushing as user fred
1122 Pushing as user fred
1123 hgrc = """
1123 hgrc = """
1124 [hooks]
1124 [hooks]
1125 pretxnchangegroup.acl = python:hgext.acl.hook
1125 pretxnchangegroup.acl = python:hgext.acl.hook
1126 [acl]
1126 [acl]
1127 sources = push
1127 sources = push
1128 [extensions]
1128 [extensions]
1129 [acl.allow]
1129 [acl.allow]
1130 ** = fred
1130 ** = fred
1131 """
1131 """
1132 pushing to ../b
1132 pushing to ../b
1133 query 1; heads
1133 query 1; heads
1134 searching for changes
1134 searching for changes
1135 all remote heads known locally
1135 all remote heads known locally
1136 listing keys for "phases"
1136 listing keys for "phases"
1137 checking for updated bookmarks
1137 checking for updated bookmarks
1138 listing keys for "bookmarks"
1138 listing keys for "bookmarks"
1139 listing keys for "bookmarks"
1139 listing keys for "bookmarks"
1140 3 changesets found
1140 3 changesets found
1141 list of changesets:
1141 list of changesets:
1142 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1142 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1143 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1143 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1144 911600dab2ae7a9baff75958b84fe606851ce955
1144 911600dab2ae7a9baff75958b84fe606851ce955
1145 bundle2-output-bundle: "HG20", 5 parts total
1145 bundle2-output-bundle: "HG20", 5 parts total
1146 bundle2-output-part: "replycaps" 168 bytes payload
1146 bundle2-output-part: "replycaps" 178 bytes payload
1147 bundle2-output-part: "check:phases" 24 bytes payload
1147 bundle2-output-part: "check:phases" 24 bytes payload
1148 bundle2-output-part: "check:heads" streamed payload
1148 bundle2-output-part: "check:heads" streamed payload
1149 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1149 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1150 bundle2-output-part: "phase-heads" 24 bytes payload
1150 bundle2-output-part: "phase-heads" 24 bytes payload
1151 bundle2-input-bundle: with-transaction
1151 bundle2-input-bundle: with-transaction
1152 bundle2-input-part: "replycaps" supported
1152 bundle2-input-part: "replycaps" supported
1153 bundle2-input-part: total payload size 168
1153 bundle2-input-part: total payload size 178
1154 bundle2-input-part: "check:phases" supported
1154 bundle2-input-part: "check:phases" supported
1155 bundle2-input-part: total payload size 24
1155 bundle2-input-part: total payload size 24
1156 bundle2-input-part: "check:heads" supported
1156 bundle2-input-part: "check:heads" supported
1157 bundle2-input-part: total payload size 20
1157 bundle2-input-part: total payload size 20
1158 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1158 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1159 adding changesets
1159 adding changesets
1160 add changeset ef1ea85a6374
1160 add changeset ef1ea85a6374
1161 add changeset f9cafe1212c8
1161 add changeset f9cafe1212c8
1162 add changeset 911600dab2ae
1162 add changeset 911600dab2ae
1163 adding manifests
1163 adding manifests
1164 adding file changes
1164 adding file changes
1165 adding foo/Bar/file.txt revisions
1165 adding foo/Bar/file.txt revisions
1166 adding foo/file.txt revisions
1166 adding foo/file.txt revisions
1167 adding quux/file.py revisions
1167 adding quux/file.py revisions
1168 added 3 changesets with 3 changes to 3 files
1168 added 3 changesets with 3 changes to 3 files
1169 calling hook pretxnchangegroup.acl: hgext.acl.hook
1169 calling hook pretxnchangegroup.acl: hgext.acl.hook
1170 acl: checking access for user "fred"
1170 acl: checking access for user "fred"
1171 acl: acl.allow.branches not enabled
1171 acl: acl.allow.branches not enabled
1172 acl: acl.deny.branches not enabled
1172 acl: acl.deny.branches not enabled
1173 acl: acl.allow enabled, 1 entries for user fred
1173 acl: acl.allow enabled, 1 entries for user fred
1174 acl: acl.deny not enabled
1174 acl: acl.deny not enabled
1175 acl: branch access granted: "ef1ea85a6374" on branch "default"
1175 acl: branch access granted: "ef1ea85a6374" on branch "default"
1176 acl: path access granted: "ef1ea85a6374"
1176 acl: path access granted: "ef1ea85a6374"
1177 acl: branch access granted: "f9cafe1212c8" on branch "default"
1177 acl: branch access granted: "f9cafe1212c8" on branch "default"
1178 acl: path access granted: "f9cafe1212c8"
1178 acl: path access granted: "f9cafe1212c8"
1179 acl: branch access granted: "911600dab2ae" on branch "default"
1179 acl: branch access granted: "911600dab2ae" on branch "default"
1180 acl: path access granted: "911600dab2ae"
1180 acl: path access granted: "911600dab2ae"
1181 bundle2-input-part: total payload size 1553
1181 bundle2-input-part: total payload size 1553
1182 bundle2-input-part: "phase-heads" supported
1182 bundle2-input-part: "phase-heads" supported
1183 bundle2-input-part: total payload size 24
1183 bundle2-input-part: total payload size 24
1184 bundle2-input-bundle: 4 parts total
1184 bundle2-input-bundle: 4 parts total
1185 updating the branch cache
1185 updating the branch cache
1186 bundle2-output-bundle: "HG20", 1 parts total
1186 bundle2-output-bundle: "HG20", 1 parts total
1187 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1187 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1188 bundle2-input-bundle: no-transaction
1188 bundle2-input-bundle: no-transaction
1189 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1189 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1190 bundle2-input-bundle: 0 parts total
1190 bundle2-input-bundle: 0 parts total
1191 listing keys for "phases"
1191 listing keys for "phases"
1192 repository tip rolled back to revision 0 (undo push)
1192 repository tip rolled back to revision 0 (undo push)
1193 0:6675d58eff77
1193 0:6675d58eff77
1194
1194
1195
1195
1196 $ echo '[acl.deny]' >> $config
1196 $ echo '[acl.deny]' >> $config
1197 $ echo "foo/Bar/** = *" >> $config
1197 $ echo "foo/Bar/** = *" >> $config
1198
1198
1199 no one is allowed inside foo/Bar/
1199 no one is allowed inside foo/Bar/
1200
1200
1201 $ do_push fred
1201 $ do_push fred
1202 Pushing as user fred
1202 Pushing as user fred
1203 hgrc = """
1203 hgrc = """
1204 [hooks]
1204 [hooks]
1205 pretxnchangegroup.acl = python:hgext.acl.hook
1205 pretxnchangegroup.acl = python:hgext.acl.hook
1206 [acl]
1206 [acl]
1207 sources = push
1207 sources = push
1208 [extensions]
1208 [extensions]
1209 [acl.allow]
1209 [acl.allow]
1210 ** = fred
1210 ** = fred
1211 [acl.deny]
1211 [acl.deny]
1212 foo/Bar/** = *
1212 foo/Bar/** = *
1213 """
1213 """
1214 pushing to ../b
1214 pushing to ../b
1215 query 1; heads
1215 query 1; heads
1216 searching for changes
1216 searching for changes
1217 all remote heads known locally
1217 all remote heads known locally
1218 listing keys for "phases"
1218 listing keys for "phases"
1219 checking for updated bookmarks
1219 checking for updated bookmarks
1220 listing keys for "bookmarks"
1220 listing keys for "bookmarks"
1221 listing keys for "bookmarks"
1221 listing keys for "bookmarks"
1222 3 changesets found
1222 3 changesets found
1223 list of changesets:
1223 list of changesets:
1224 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1224 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1225 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1225 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1226 911600dab2ae7a9baff75958b84fe606851ce955
1226 911600dab2ae7a9baff75958b84fe606851ce955
1227 bundle2-output-bundle: "HG20", 5 parts total
1227 bundle2-output-bundle: "HG20", 5 parts total
1228 bundle2-output-part: "replycaps" 168 bytes payload
1228 bundle2-output-part: "replycaps" 178 bytes payload
1229 bundle2-output-part: "check:phases" 24 bytes payload
1229 bundle2-output-part: "check:phases" 24 bytes payload
1230 bundle2-output-part: "check:heads" streamed payload
1230 bundle2-output-part: "check:heads" streamed payload
1231 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1231 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1232 bundle2-output-part: "phase-heads" 24 bytes payload
1232 bundle2-output-part: "phase-heads" 24 bytes payload
1233 bundle2-input-bundle: with-transaction
1233 bundle2-input-bundle: with-transaction
1234 bundle2-input-part: "replycaps" supported
1234 bundle2-input-part: "replycaps" supported
1235 bundle2-input-part: total payload size 168
1235 bundle2-input-part: total payload size 178
1236 bundle2-input-part: "check:phases" supported
1236 bundle2-input-part: "check:phases" supported
1237 bundle2-input-part: total payload size 24
1237 bundle2-input-part: total payload size 24
1238 bundle2-input-part: "check:heads" supported
1238 bundle2-input-part: "check:heads" supported
1239 bundle2-input-part: total payload size 20
1239 bundle2-input-part: total payload size 20
1240 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1240 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1241 adding changesets
1241 adding changesets
1242 add changeset ef1ea85a6374
1242 add changeset ef1ea85a6374
1243 add changeset f9cafe1212c8
1243 add changeset f9cafe1212c8
1244 add changeset 911600dab2ae
1244 add changeset 911600dab2ae
1245 adding manifests
1245 adding manifests
1246 adding file changes
1246 adding file changes
1247 adding foo/Bar/file.txt revisions
1247 adding foo/Bar/file.txt revisions
1248 adding foo/file.txt revisions
1248 adding foo/file.txt revisions
1249 adding quux/file.py revisions
1249 adding quux/file.py revisions
1250 added 3 changesets with 3 changes to 3 files
1250 added 3 changesets with 3 changes to 3 files
1251 calling hook pretxnchangegroup.acl: hgext.acl.hook
1251 calling hook pretxnchangegroup.acl: hgext.acl.hook
1252 acl: checking access for user "fred"
1252 acl: checking access for user "fred"
1253 acl: acl.allow.branches not enabled
1253 acl: acl.allow.branches not enabled
1254 acl: acl.deny.branches not enabled
1254 acl: acl.deny.branches not enabled
1255 acl: acl.allow enabled, 1 entries for user fred
1255 acl: acl.allow enabled, 1 entries for user fred
1256 acl: acl.deny enabled, 1 entries for user fred
1256 acl: acl.deny enabled, 1 entries for user fred
1257 acl: branch access granted: "ef1ea85a6374" on branch "default"
1257 acl: branch access granted: "ef1ea85a6374" on branch "default"
1258 acl: path access granted: "ef1ea85a6374"
1258 acl: path access granted: "ef1ea85a6374"
1259 acl: branch access granted: "f9cafe1212c8" on branch "default"
1259 acl: branch access granted: "f9cafe1212c8" on branch "default"
1260 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1260 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1261 bundle2-input-part: total payload size 1553
1261 bundle2-input-part: total payload size 1553
1262 bundle2-input-part: total payload size 24
1262 bundle2-input-part: total payload size 24
1263 bundle2-input-bundle: 4 parts total
1263 bundle2-input-bundle: 4 parts total
1264 transaction abort!
1264 transaction abort!
1265 rollback completed
1265 rollback completed
1266 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1266 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1267 no rollback information available
1267 no rollback information available
1268 0:6675d58eff77
1268 0:6675d58eff77
1269
1269
1270
1270
1271 Groups
1271 Groups
1272
1272
1273 $ init_config
1273 $ init_config
1274
1274
1275 OS-level groups
1275 OS-level groups
1276
1276
1277 $ echo '[acl.allow]' >> $config
1277 $ echo '[acl.allow]' >> $config
1278 $ echo "** = @group1" >> $config
1278 $ echo "** = @group1" >> $config
1279
1279
1280 @group1 is always allowed
1280 @group1 is always allowed
1281
1281
1282 $ do_push fred
1282 $ do_push fred
1283 Pushing as user fred
1283 Pushing as user fred
1284 hgrc = """
1284 hgrc = """
1285 [hooks]
1285 [hooks]
1286 pretxnchangegroup.acl = python:hgext.acl.hook
1286 pretxnchangegroup.acl = python:hgext.acl.hook
1287 [acl]
1287 [acl]
1288 sources = push
1288 sources = push
1289 [extensions]
1289 [extensions]
1290 [acl.allow]
1290 [acl.allow]
1291 ** = @group1
1291 ** = @group1
1292 """
1292 """
1293 pushing to ../b
1293 pushing to ../b
1294 query 1; heads
1294 query 1; heads
1295 searching for changes
1295 searching for changes
1296 all remote heads known locally
1296 all remote heads known locally
1297 listing keys for "phases"
1297 listing keys for "phases"
1298 checking for updated bookmarks
1298 checking for updated bookmarks
1299 listing keys for "bookmarks"
1299 listing keys for "bookmarks"
1300 listing keys for "bookmarks"
1300 listing keys for "bookmarks"
1301 3 changesets found
1301 3 changesets found
1302 list of changesets:
1302 list of changesets:
1303 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1303 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1304 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1304 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1305 911600dab2ae7a9baff75958b84fe606851ce955
1305 911600dab2ae7a9baff75958b84fe606851ce955
1306 bundle2-output-bundle: "HG20", 5 parts total
1306 bundle2-output-bundle: "HG20", 5 parts total
1307 bundle2-output-part: "replycaps" 168 bytes payload
1307 bundle2-output-part: "replycaps" 178 bytes payload
1308 bundle2-output-part: "check:phases" 24 bytes payload
1308 bundle2-output-part: "check:phases" 24 bytes payload
1309 bundle2-output-part: "check:heads" streamed payload
1309 bundle2-output-part: "check:heads" streamed payload
1310 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1310 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1311 bundle2-output-part: "phase-heads" 24 bytes payload
1311 bundle2-output-part: "phase-heads" 24 bytes payload
1312 bundle2-input-bundle: with-transaction
1312 bundle2-input-bundle: with-transaction
1313 bundle2-input-part: "replycaps" supported
1313 bundle2-input-part: "replycaps" supported
1314 bundle2-input-part: total payload size 168
1314 bundle2-input-part: total payload size 178
1315 bundle2-input-part: "check:phases" supported
1315 bundle2-input-part: "check:phases" supported
1316 bundle2-input-part: total payload size 24
1316 bundle2-input-part: total payload size 24
1317 bundle2-input-part: "check:heads" supported
1317 bundle2-input-part: "check:heads" supported
1318 bundle2-input-part: total payload size 20
1318 bundle2-input-part: total payload size 20
1319 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1319 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1320 adding changesets
1320 adding changesets
1321 add changeset ef1ea85a6374
1321 add changeset ef1ea85a6374
1322 add changeset f9cafe1212c8
1322 add changeset f9cafe1212c8
1323 add changeset 911600dab2ae
1323 add changeset 911600dab2ae
1324 adding manifests
1324 adding manifests
1325 adding file changes
1325 adding file changes
1326 adding foo/Bar/file.txt revisions
1326 adding foo/Bar/file.txt revisions
1327 adding foo/file.txt revisions
1327 adding foo/file.txt revisions
1328 adding quux/file.py revisions
1328 adding quux/file.py revisions
1329 added 3 changesets with 3 changes to 3 files
1329 added 3 changesets with 3 changes to 3 files
1330 calling hook pretxnchangegroup.acl: hgext.acl.hook
1330 calling hook pretxnchangegroup.acl: hgext.acl.hook
1331 acl: checking access for user "fred"
1331 acl: checking access for user "fred"
1332 acl: acl.allow.branches not enabled
1332 acl: acl.allow.branches not enabled
1333 acl: acl.deny.branches not enabled
1333 acl: acl.deny.branches not enabled
1334 acl: "group1" not defined in [acl.groups]
1334 acl: "group1" not defined in [acl.groups]
1335 acl: acl.allow enabled, 1 entries for user fred
1335 acl: acl.allow enabled, 1 entries for user fred
1336 acl: acl.deny not enabled
1336 acl: acl.deny not enabled
1337 acl: branch access granted: "ef1ea85a6374" on branch "default"
1337 acl: branch access granted: "ef1ea85a6374" on branch "default"
1338 acl: path access granted: "ef1ea85a6374"
1338 acl: path access granted: "ef1ea85a6374"
1339 acl: branch access granted: "f9cafe1212c8" on branch "default"
1339 acl: branch access granted: "f9cafe1212c8" on branch "default"
1340 acl: path access granted: "f9cafe1212c8"
1340 acl: path access granted: "f9cafe1212c8"
1341 acl: branch access granted: "911600dab2ae" on branch "default"
1341 acl: branch access granted: "911600dab2ae" on branch "default"
1342 acl: path access granted: "911600dab2ae"
1342 acl: path access granted: "911600dab2ae"
1343 bundle2-input-part: total payload size 1553
1343 bundle2-input-part: total payload size 1553
1344 bundle2-input-part: "phase-heads" supported
1344 bundle2-input-part: "phase-heads" supported
1345 bundle2-input-part: total payload size 24
1345 bundle2-input-part: total payload size 24
1346 bundle2-input-bundle: 4 parts total
1346 bundle2-input-bundle: 4 parts total
1347 updating the branch cache
1347 updating the branch cache
1348 bundle2-output-bundle: "HG20", 1 parts total
1348 bundle2-output-bundle: "HG20", 1 parts total
1349 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1349 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1350 bundle2-input-bundle: no-transaction
1350 bundle2-input-bundle: no-transaction
1351 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1351 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1352 bundle2-input-bundle: 0 parts total
1352 bundle2-input-bundle: 0 parts total
1353 listing keys for "phases"
1353 listing keys for "phases"
1354 repository tip rolled back to revision 0 (undo push)
1354 repository tip rolled back to revision 0 (undo push)
1355 0:6675d58eff77
1355 0:6675d58eff77
1356
1356
1357
1357
1358 $ echo '[acl.deny]' >> $config
1358 $ echo '[acl.deny]' >> $config
1359 $ echo "foo/Bar/** = @group1" >> $config
1359 $ echo "foo/Bar/** = @group1" >> $config
1360
1360
1361 @group is allowed inside anything but foo/Bar/
1361 @group is allowed inside anything but foo/Bar/
1362
1362
1363 $ do_push fred
1363 $ do_push fred
1364 Pushing as user fred
1364 Pushing as user fred
1365 hgrc = """
1365 hgrc = """
1366 [hooks]
1366 [hooks]
1367 pretxnchangegroup.acl = python:hgext.acl.hook
1367 pretxnchangegroup.acl = python:hgext.acl.hook
1368 [acl]
1368 [acl]
1369 sources = push
1369 sources = push
1370 [extensions]
1370 [extensions]
1371 [acl.allow]
1371 [acl.allow]
1372 ** = @group1
1372 ** = @group1
1373 [acl.deny]
1373 [acl.deny]
1374 foo/Bar/** = @group1
1374 foo/Bar/** = @group1
1375 """
1375 """
1376 pushing to ../b
1376 pushing to ../b
1377 query 1; heads
1377 query 1; heads
1378 searching for changes
1378 searching for changes
1379 all remote heads known locally
1379 all remote heads known locally
1380 listing keys for "phases"
1380 listing keys for "phases"
1381 checking for updated bookmarks
1381 checking for updated bookmarks
1382 listing keys for "bookmarks"
1382 listing keys for "bookmarks"
1383 listing keys for "bookmarks"
1383 listing keys for "bookmarks"
1384 3 changesets found
1384 3 changesets found
1385 list of changesets:
1385 list of changesets:
1386 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1386 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1387 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1387 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1388 911600dab2ae7a9baff75958b84fe606851ce955
1388 911600dab2ae7a9baff75958b84fe606851ce955
1389 bundle2-output-bundle: "HG20", 5 parts total
1389 bundle2-output-bundle: "HG20", 5 parts total
1390 bundle2-output-part: "replycaps" 168 bytes payload
1390 bundle2-output-part: "replycaps" 178 bytes payload
1391 bundle2-output-part: "check:phases" 24 bytes payload
1391 bundle2-output-part: "check:phases" 24 bytes payload
1392 bundle2-output-part: "check:heads" streamed payload
1392 bundle2-output-part: "check:heads" streamed payload
1393 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1393 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1394 bundle2-output-part: "phase-heads" 24 bytes payload
1394 bundle2-output-part: "phase-heads" 24 bytes payload
1395 bundle2-input-bundle: with-transaction
1395 bundle2-input-bundle: with-transaction
1396 bundle2-input-part: "replycaps" supported
1396 bundle2-input-part: "replycaps" supported
1397 bundle2-input-part: total payload size 168
1397 bundle2-input-part: total payload size 178
1398 bundle2-input-part: "check:phases" supported
1398 bundle2-input-part: "check:phases" supported
1399 bundle2-input-part: total payload size 24
1399 bundle2-input-part: total payload size 24
1400 bundle2-input-part: "check:heads" supported
1400 bundle2-input-part: "check:heads" supported
1401 bundle2-input-part: total payload size 20
1401 bundle2-input-part: total payload size 20
1402 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1402 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1403 adding changesets
1403 adding changesets
1404 add changeset ef1ea85a6374
1404 add changeset ef1ea85a6374
1405 add changeset f9cafe1212c8
1405 add changeset f9cafe1212c8
1406 add changeset 911600dab2ae
1406 add changeset 911600dab2ae
1407 adding manifests
1407 adding manifests
1408 adding file changes
1408 adding file changes
1409 adding foo/Bar/file.txt revisions
1409 adding foo/Bar/file.txt revisions
1410 adding foo/file.txt revisions
1410 adding foo/file.txt revisions
1411 adding quux/file.py revisions
1411 adding quux/file.py revisions
1412 added 3 changesets with 3 changes to 3 files
1412 added 3 changesets with 3 changes to 3 files
1413 calling hook pretxnchangegroup.acl: hgext.acl.hook
1413 calling hook pretxnchangegroup.acl: hgext.acl.hook
1414 acl: checking access for user "fred"
1414 acl: checking access for user "fred"
1415 acl: acl.allow.branches not enabled
1415 acl: acl.allow.branches not enabled
1416 acl: acl.deny.branches not enabled
1416 acl: acl.deny.branches not enabled
1417 acl: "group1" not defined in [acl.groups]
1417 acl: "group1" not defined in [acl.groups]
1418 acl: acl.allow enabled, 1 entries for user fred
1418 acl: acl.allow enabled, 1 entries for user fred
1419 acl: "group1" not defined in [acl.groups]
1419 acl: "group1" not defined in [acl.groups]
1420 acl: acl.deny enabled, 1 entries for user fred
1420 acl: acl.deny enabled, 1 entries for user fred
1421 acl: branch access granted: "ef1ea85a6374" on branch "default"
1421 acl: branch access granted: "ef1ea85a6374" on branch "default"
1422 acl: path access granted: "ef1ea85a6374"
1422 acl: path access granted: "ef1ea85a6374"
1423 acl: branch access granted: "f9cafe1212c8" on branch "default"
1423 acl: branch access granted: "f9cafe1212c8" on branch "default"
1424 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1424 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1425 bundle2-input-part: total payload size 1553
1425 bundle2-input-part: total payload size 1553
1426 bundle2-input-part: total payload size 24
1426 bundle2-input-part: total payload size 24
1427 bundle2-input-bundle: 4 parts total
1427 bundle2-input-bundle: 4 parts total
1428 transaction abort!
1428 transaction abort!
1429 rollback completed
1429 rollback completed
1430 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1430 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1431 no rollback information available
1431 no rollback information available
1432 0:6675d58eff77
1432 0:6675d58eff77
1433
1433
1434
1434
1435 Invalid group
1435 Invalid group
1436
1436
1437 Disable the fakegroups trick to get real failures
1437 Disable the fakegroups trick to get real failures
1438
1438
1439 $ grep -v fakegroups $config > config.tmp
1439 $ grep -v fakegroups $config > config.tmp
1440 $ mv config.tmp $config
1440 $ mv config.tmp $config
1441 $ echo '[acl.allow]' >> $config
1441 $ echo '[acl.allow]' >> $config
1442 $ echo "** = @unlikelytoexist" >> $config
1442 $ echo "** = @unlikelytoexist" >> $config
1443 $ do_push fred 2>&1 | grep unlikelytoexist
1443 $ do_push fred 2>&1 | grep unlikelytoexist
1444 ** = @unlikelytoexist
1444 ** = @unlikelytoexist
1445 acl: "unlikelytoexist" not defined in [acl.groups]
1445 acl: "unlikelytoexist" not defined in [acl.groups]
1446 error: pretxnchangegroup.acl hook failed: group 'unlikelytoexist' is undefined
1446 error: pretxnchangegroup.acl hook failed: group 'unlikelytoexist' is undefined
1447 abort: group 'unlikelytoexist' is undefined
1447 abort: group 'unlikelytoexist' is undefined
1448
1448
1449
1449
1450 Branch acl tests setup
1450 Branch acl tests setup
1451
1451
1452 $ init_config
1452 $ init_config
1453 $ cd b
1453 $ cd b
1454 $ hg up
1454 $ hg up
1455 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1455 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1456 $ hg branch foobar
1456 $ hg branch foobar
1457 marked working directory as branch foobar
1457 marked working directory as branch foobar
1458 (branches are permanent and global, did you want a bookmark?)
1458 (branches are permanent and global, did you want a bookmark?)
1459 $ hg commit -m 'create foobar'
1459 $ hg commit -m 'create foobar'
1460 $ echo 'foo contents' > abc.txt
1460 $ echo 'foo contents' > abc.txt
1461 $ hg add abc.txt
1461 $ hg add abc.txt
1462 $ hg commit -m 'foobar contents'
1462 $ hg commit -m 'foobar contents'
1463 $ cd ..
1463 $ cd ..
1464 $ hg --cwd a pull ../b
1464 $ hg --cwd a pull ../b
1465 pulling from ../b
1465 pulling from ../b
1466 searching for changes
1466 searching for changes
1467 adding changesets
1467 adding changesets
1468 adding manifests
1468 adding manifests
1469 adding file changes
1469 adding file changes
1470 added 2 changesets with 1 changes to 1 files (+1 heads)
1470 added 2 changesets with 1 changes to 1 files (+1 heads)
1471 new changesets 81fbf4469322:fb35475503ef
1471 new changesets 81fbf4469322:fb35475503ef
1472 (run 'hg heads' to see heads)
1472 (run 'hg heads' to see heads)
1473
1473
1474 Create additional changeset on foobar branch
1474 Create additional changeset on foobar branch
1475
1475
1476 $ cd a
1476 $ cd a
1477 $ hg up -C foobar
1477 $ hg up -C foobar
1478 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
1478 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
1479 $ echo 'foo contents2' > abc.txt
1479 $ echo 'foo contents2' > abc.txt
1480 $ hg commit -m 'foobar contents2'
1480 $ hg commit -m 'foobar contents2'
1481 $ cd ..
1481 $ cd ..
1482
1482
1483
1483
1484 No branch acls specified
1484 No branch acls specified
1485
1485
1486 $ do_push astro
1486 $ do_push astro
1487 Pushing as user astro
1487 Pushing as user astro
1488 hgrc = """
1488 hgrc = """
1489 [hooks]
1489 [hooks]
1490 pretxnchangegroup.acl = python:hgext.acl.hook
1490 pretxnchangegroup.acl = python:hgext.acl.hook
1491 [acl]
1491 [acl]
1492 sources = push
1492 sources = push
1493 [extensions]
1493 [extensions]
1494 """
1494 """
1495 pushing to ../b
1495 pushing to ../b
1496 query 1; heads
1496 query 1; heads
1497 searching for changes
1497 searching for changes
1498 all remote heads known locally
1498 all remote heads known locally
1499 listing keys for "phases"
1499 listing keys for "phases"
1500 checking for updated bookmarks
1500 checking for updated bookmarks
1501 listing keys for "bookmarks"
1501 listing keys for "bookmarks"
1502 listing keys for "bookmarks"
1502 listing keys for "bookmarks"
1503 4 changesets found
1503 4 changesets found
1504 list of changesets:
1504 list of changesets:
1505 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1505 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1506 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1506 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1507 911600dab2ae7a9baff75958b84fe606851ce955
1507 911600dab2ae7a9baff75958b84fe606851ce955
1508 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1508 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1509 bundle2-output-bundle: "HG20", 5 parts total
1509 bundle2-output-bundle: "HG20", 5 parts total
1510 bundle2-output-part: "replycaps" 168 bytes payload
1510 bundle2-output-part: "replycaps" 178 bytes payload
1511 bundle2-output-part: "check:phases" 48 bytes payload
1511 bundle2-output-part: "check:phases" 48 bytes payload
1512 bundle2-output-part: "check:heads" streamed payload
1512 bundle2-output-part: "check:heads" streamed payload
1513 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1513 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1514 bundle2-output-part: "phase-heads" 48 bytes payload
1514 bundle2-output-part: "phase-heads" 48 bytes payload
1515 bundle2-input-bundle: with-transaction
1515 bundle2-input-bundle: with-transaction
1516 bundle2-input-part: "replycaps" supported
1516 bundle2-input-part: "replycaps" supported
1517 bundle2-input-part: total payload size 168
1517 bundle2-input-part: total payload size 178
1518 bundle2-input-part: "check:phases" supported
1518 bundle2-input-part: "check:phases" supported
1519 bundle2-input-part: total payload size 48
1519 bundle2-input-part: total payload size 48
1520 bundle2-input-part: "check:heads" supported
1520 bundle2-input-part: "check:heads" supported
1521 bundle2-input-part: total payload size 20
1521 bundle2-input-part: total payload size 20
1522 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1522 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1523 adding changesets
1523 adding changesets
1524 add changeset ef1ea85a6374
1524 add changeset ef1ea85a6374
1525 add changeset f9cafe1212c8
1525 add changeset f9cafe1212c8
1526 add changeset 911600dab2ae
1526 add changeset 911600dab2ae
1527 add changeset e8fc755d4d82
1527 add changeset e8fc755d4d82
1528 adding manifests
1528 adding manifests
1529 adding file changes
1529 adding file changes
1530 adding abc.txt revisions
1530 adding abc.txt revisions
1531 adding foo/Bar/file.txt revisions
1531 adding foo/Bar/file.txt revisions
1532 adding foo/file.txt revisions
1532 adding foo/file.txt revisions
1533 adding quux/file.py revisions
1533 adding quux/file.py revisions
1534 added 4 changesets with 4 changes to 4 files (+1 heads)
1534 added 4 changesets with 4 changes to 4 files (+1 heads)
1535 calling hook pretxnchangegroup.acl: hgext.acl.hook
1535 calling hook pretxnchangegroup.acl: hgext.acl.hook
1536 acl: checking access for user "astro"
1536 acl: checking access for user "astro"
1537 acl: acl.allow.branches not enabled
1537 acl: acl.allow.branches not enabled
1538 acl: acl.deny.branches not enabled
1538 acl: acl.deny.branches not enabled
1539 acl: acl.allow not enabled
1539 acl: acl.allow not enabled
1540 acl: acl.deny not enabled
1540 acl: acl.deny not enabled
1541 acl: branch access granted: "ef1ea85a6374" on branch "default"
1541 acl: branch access granted: "ef1ea85a6374" on branch "default"
1542 acl: path access granted: "ef1ea85a6374"
1542 acl: path access granted: "ef1ea85a6374"
1543 acl: branch access granted: "f9cafe1212c8" on branch "default"
1543 acl: branch access granted: "f9cafe1212c8" on branch "default"
1544 acl: path access granted: "f9cafe1212c8"
1544 acl: path access granted: "f9cafe1212c8"
1545 acl: branch access granted: "911600dab2ae" on branch "default"
1545 acl: branch access granted: "911600dab2ae" on branch "default"
1546 acl: path access granted: "911600dab2ae"
1546 acl: path access granted: "911600dab2ae"
1547 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1547 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1548 acl: path access granted: "e8fc755d4d82"
1548 acl: path access granted: "e8fc755d4d82"
1549 bundle2-input-part: total payload size 2068
1549 bundle2-input-part: total payload size 2068
1550 bundle2-input-part: "phase-heads" supported
1550 bundle2-input-part: "phase-heads" supported
1551 bundle2-input-part: total payload size 48
1551 bundle2-input-part: total payload size 48
1552 bundle2-input-bundle: 4 parts total
1552 bundle2-input-bundle: 4 parts total
1553 updating the branch cache
1553 updating the branch cache
1554 bundle2-output-bundle: "HG20", 1 parts total
1554 bundle2-output-bundle: "HG20", 1 parts total
1555 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1555 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1556 bundle2-input-bundle: no-transaction
1556 bundle2-input-bundle: no-transaction
1557 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1557 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1558 bundle2-input-bundle: 0 parts total
1558 bundle2-input-bundle: 0 parts total
1559 listing keys for "phases"
1559 listing keys for "phases"
1560 repository tip rolled back to revision 2 (undo push)
1560 repository tip rolled back to revision 2 (undo push)
1561 2:fb35475503ef
1561 2:fb35475503ef
1562
1562
1563
1563
1564 Branch acl deny test
1564 Branch acl deny test
1565
1565
1566 $ echo "[acl.deny.branches]" >> $config
1566 $ echo "[acl.deny.branches]" >> $config
1567 $ echo "foobar = *" >> $config
1567 $ echo "foobar = *" >> $config
1568 $ do_push astro
1568 $ do_push astro
1569 Pushing as user astro
1569 Pushing as user astro
1570 hgrc = """
1570 hgrc = """
1571 [hooks]
1571 [hooks]
1572 pretxnchangegroup.acl = python:hgext.acl.hook
1572 pretxnchangegroup.acl = python:hgext.acl.hook
1573 [acl]
1573 [acl]
1574 sources = push
1574 sources = push
1575 [extensions]
1575 [extensions]
1576 [acl.deny.branches]
1576 [acl.deny.branches]
1577 foobar = *
1577 foobar = *
1578 """
1578 """
1579 pushing to ../b
1579 pushing to ../b
1580 query 1; heads
1580 query 1; heads
1581 searching for changes
1581 searching for changes
1582 all remote heads known locally
1582 all remote heads known locally
1583 listing keys for "phases"
1583 listing keys for "phases"
1584 checking for updated bookmarks
1584 checking for updated bookmarks
1585 listing keys for "bookmarks"
1585 listing keys for "bookmarks"
1586 listing keys for "bookmarks"
1586 listing keys for "bookmarks"
1587 4 changesets found
1587 4 changesets found
1588 list of changesets:
1588 list of changesets:
1589 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1589 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1590 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1590 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1591 911600dab2ae7a9baff75958b84fe606851ce955
1591 911600dab2ae7a9baff75958b84fe606851ce955
1592 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1592 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1593 bundle2-output-bundle: "HG20", 5 parts total
1593 bundle2-output-bundle: "HG20", 5 parts total
1594 bundle2-output-part: "replycaps" 168 bytes payload
1594 bundle2-output-part: "replycaps" 178 bytes payload
1595 bundle2-output-part: "check:phases" 48 bytes payload
1595 bundle2-output-part: "check:phases" 48 bytes payload
1596 bundle2-output-part: "check:heads" streamed payload
1596 bundle2-output-part: "check:heads" streamed payload
1597 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1597 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1598 bundle2-output-part: "phase-heads" 48 bytes payload
1598 bundle2-output-part: "phase-heads" 48 bytes payload
1599 bundle2-input-bundle: with-transaction
1599 bundle2-input-bundle: with-transaction
1600 bundle2-input-part: "replycaps" supported
1600 bundle2-input-part: "replycaps" supported
1601 bundle2-input-part: total payload size 168
1601 bundle2-input-part: total payload size 178
1602 bundle2-input-part: "check:phases" supported
1602 bundle2-input-part: "check:phases" supported
1603 bundle2-input-part: total payload size 48
1603 bundle2-input-part: total payload size 48
1604 bundle2-input-part: "check:heads" supported
1604 bundle2-input-part: "check:heads" supported
1605 bundle2-input-part: total payload size 20
1605 bundle2-input-part: total payload size 20
1606 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1606 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1607 adding changesets
1607 adding changesets
1608 add changeset ef1ea85a6374
1608 add changeset ef1ea85a6374
1609 add changeset f9cafe1212c8
1609 add changeset f9cafe1212c8
1610 add changeset 911600dab2ae
1610 add changeset 911600dab2ae
1611 add changeset e8fc755d4d82
1611 add changeset e8fc755d4d82
1612 adding manifests
1612 adding manifests
1613 adding file changes
1613 adding file changes
1614 adding abc.txt revisions
1614 adding abc.txt revisions
1615 adding foo/Bar/file.txt revisions
1615 adding foo/Bar/file.txt revisions
1616 adding foo/file.txt revisions
1616 adding foo/file.txt revisions
1617 adding quux/file.py revisions
1617 adding quux/file.py revisions
1618 added 4 changesets with 4 changes to 4 files (+1 heads)
1618 added 4 changesets with 4 changes to 4 files (+1 heads)
1619 calling hook pretxnchangegroup.acl: hgext.acl.hook
1619 calling hook pretxnchangegroup.acl: hgext.acl.hook
1620 acl: checking access for user "astro"
1620 acl: checking access for user "astro"
1621 acl: acl.allow.branches not enabled
1621 acl: acl.allow.branches not enabled
1622 acl: acl.deny.branches enabled, 1 entries for user astro
1622 acl: acl.deny.branches enabled, 1 entries for user astro
1623 acl: acl.allow not enabled
1623 acl: acl.allow not enabled
1624 acl: acl.deny not enabled
1624 acl: acl.deny not enabled
1625 acl: branch access granted: "ef1ea85a6374" on branch "default"
1625 acl: branch access granted: "ef1ea85a6374" on branch "default"
1626 acl: path access granted: "ef1ea85a6374"
1626 acl: path access granted: "ef1ea85a6374"
1627 acl: branch access granted: "f9cafe1212c8" on branch "default"
1627 acl: branch access granted: "f9cafe1212c8" on branch "default"
1628 acl: path access granted: "f9cafe1212c8"
1628 acl: path access granted: "f9cafe1212c8"
1629 acl: branch access granted: "911600dab2ae" on branch "default"
1629 acl: branch access granted: "911600dab2ae" on branch "default"
1630 acl: path access granted: "911600dab2ae"
1630 acl: path access granted: "911600dab2ae"
1631 error: pretxnchangegroup.acl hook failed: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82")
1631 error: pretxnchangegroup.acl hook failed: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82")
1632 bundle2-input-part: total payload size 2068
1632 bundle2-input-part: total payload size 2068
1633 bundle2-input-part: total payload size 48
1633 bundle2-input-part: total payload size 48
1634 bundle2-input-bundle: 4 parts total
1634 bundle2-input-bundle: 4 parts total
1635 transaction abort!
1635 transaction abort!
1636 rollback completed
1636 rollback completed
1637 abort: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82")
1637 abort: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82")
1638 no rollback information available
1638 no rollback information available
1639 2:fb35475503ef
1639 2:fb35475503ef
1640
1640
1641
1641
1642 Branch acl empty allow test
1642 Branch acl empty allow test
1643
1643
1644 $ init_config
1644 $ init_config
1645 $ echo "[acl.allow.branches]" >> $config
1645 $ echo "[acl.allow.branches]" >> $config
1646 $ do_push astro
1646 $ do_push astro
1647 Pushing as user astro
1647 Pushing as user astro
1648 hgrc = """
1648 hgrc = """
1649 [hooks]
1649 [hooks]
1650 pretxnchangegroup.acl = python:hgext.acl.hook
1650 pretxnchangegroup.acl = python:hgext.acl.hook
1651 [acl]
1651 [acl]
1652 sources = push
1652 sources = push
1653 [extensions]
1653 [extensions]
1654 [acl.allow.branches]
1654 [acl.allow.branches]
1655 """
1655 """
1656 pushing to ../b
1656 pushing to ../b
1657 query 1; heads
1657 query 1; heads
1658 searching for changes
1658 searching for changes
1659 all remote heads known locally
1659 all remote heads known locally
1660 listing keys for "phases"
1660 listing keys for "phases"
1661 checking for updated bookmarks
1661 checking for updated bookmarks
1662 listing keys for "bookmarks"
1662 listing keys for "bookmarks"
1663 listing keys for "bookmarks"
1663 listing keys for "bookmarks"
1664 4 changesets found
1664 4 changesets found
1665 list of changesets:
1665 list of changesets:
1666 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1666 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1667 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1667 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1668 911600dab2ae7a9baff75958b84fe606851ce955
1668 911600dab2ae7a9baff75958b84fe606851ce955
1669 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1669 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1670 bundle2-output-bundle: "HG20", 5 parts total
1670 bundle2-output-bundle: "HG20", 5 parts total
1671 bundle2-output-part: "replycaps" 168 bytes payload
1671 bundle2-output-part: "replycaps" 178 bytes payload
1672 bundle2-output-part: "check:phases" 48 bytes payload
1672 bundle2-output-part: "check:phases" 48 bytes payload
1673 bundle2-output-part: "check:heads" streamed payload
1673 bundle2-output-part: "check:heads" streamed payload
1674 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1674 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1675 bundle2-output-part: "phase-heads" 48 bytes payload
1675 bundle2-output-part: "phase-heads" 48 bytes payload
1676 bundle2-input-bundle: with-transaction
1676 bundle2-input-bundle: with-transaction
1677 bundle2-input-part: "replycaps" supported
1677 bundle2-input-part: "replycaps" supported
1678 bundle2-input-part: total payload size 168
1678 bundle2-input-part: total payload size 178
1679 bundle2-input-part: "check:phases" supported
1679 bundle2-input-part: "check:phases" supported
1680 bundle2-input-part: total payload size 48
1680 bundle2-input-part: total payload size 48
1681 bundle2-input-part: "check:heads" supported
1681 bundle2-input-part: "check:heads" supported
1682 bundle2-input-part: total payload size 20
1682 bundle2-input-part: total payload size 20
1683 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1683 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1684 adding changesets
1684 adding changesets
1685 add changeset ef1ea85a6374
1685 add changeset ef1ea85a6374
1686 add changeset f9cafe1212c8
1686 add changeset f9cafe1212c8
1687 add changeset 911600dab2ae
1687 add changeset 911600dab2ae
1688 add changeset e8fc755d4d82
1688 add changeset e8fc755d4d82
1689 adding manifests
1689 adding manifests
1690 adding file changes
1690 adding file changes
1691 adding abc.txt revisions
1691 adding abc.txt revisions
1692 adding foo/Bar/file.txt revisions
1692 adding foo/Bar/file.txt revisions
1693 adding foo/file.txt revisions
1693 adding foo/file.txt revisions
1694 adding quux/file.py revisions
1694 adding quux/file.py revisions
1695 added 4 changesets with 4 changes to 4 files (+1 heads)
1695 added 4 changesets with 4 changes to 4 files (+1 heads)
1696 calling hook pretxnchangegroup.acl: hgext.acl.hook
1696 calling hook pretxnchangegroup.acl: hgext.acl.hook
1697 acl: checking access for user "astro"
1697 acl: checking access for user "astro"
1698 acl: acl.allow.branches enabled, 0 entries for user astro
1698 acl: acl.allow.branches enabled, 0 entries for user astro
1699 acl: acl.deny.branches not enabled
1699 acl: acl.deny.branches not enabled
1700 acl: acl.allow not enabled
1700 acl: acl.allow not enabled
1701 acl: acl.deny not enabled
1701 acl: acl.deny not enabled
1702 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1702 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1703 bundle2-input-part: total payload size 2068
1703 bundle2-input-part: total payload size 2068
1704 bundle2-input-part: total payload size 48
1704 bundle2-input-part: total payload size 48
1705 bundle2-input-bundle: 4 parts total
1705 bundle2-input-bundle: 4 parts total
1706 transaction abort!
1706 transaction abort!
1707 rollback completed
1707 rollback completed
1708 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1708 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1709 no rollback information available
1709 no rollback information available
1710 2:fb35475503ef
1710 2:fb35475503ef
1711
1711
1712
1712
1713 Branch acl allow other
1713 Branch acl allow other
1714
1714
1715 $ init_config
1715 $ init_config
1716 $ echo "[acl.allow.branches]" >> $config
1716 $ echo "[acl.allow.branches]" >> $config
1717 $ echo "* = george" >> $config
1717 $ echo "* = george" >> $config
1718 $ do_push astro
1718 $ do_push astro
1719 Pushing as user astro
1719 Pushing as user astro
1720 hgrc = """
1720 hgrc = """
1721 [hooks]
1721 [hooks]
1722 pretxnchangegroup.acl = python:hgext.acl.hook
1722 pretxnchangegroup.acl = python:hgext.acl.hook
1723 [acl]
1723 [acl]
1724 sources = push
1724 sources = push
1725 [extensions]
1725 [extensions]
1726 [acl.allow.branches]
1726 [acl.allow.branches]
1727 * = george
1727 * = george
1728 """
1728 """
1729 pushing to ../b
1729 pushing to ../b
1730 query 1; heads
1730 query 1; heads
1731 searching for changes
1731 searching for changes
1732 all remote heads known locally
1732 all remote heads known locally
1733 listing keys for "phases"
1733 listing keys for "phases"
1734 checking for updated bookmarks
1734 checking for updated bookmarks
1735 listing keys for "bookmarks"
1735 listing keys for "bookmarks"
1736 listing keys for "bookmarks"
1736 listing keys for "bookmarks"
1737 4 changesets found
1737 4 changesets found
1738 list of changesets:
1738 list of changesets:
1739 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1739 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1740 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1740 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1741 911600dab2ae7a9baff75958b84fe606851ce955
1741 911600dab2ae7a9baff75958b84fe606851ce955
1742 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1742 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1743 bundle2-output-bundle: "HG20", 5 parts total
1743 bundle2-output-bundle: "HG20", 5 parts total
1744 bundle2-output-part: "replycaps" 168 bytes payload
1744 bundle2-output-part: "replycaps" 178 bytes payload
1745 bundle2-output-part: "check:phases" 48 bytes payload
1745 bundle2-output-part: "check:phases" 48 bytes payload
1746 bundle2-output-part: "check:heads" streamed payload
1746 bundle2-output-part: "check:heads" streamed payload
1747 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1747 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1748 bundle2-output-part: "phase-heads" 48 bytes payload
1748 bundle2-output-part: "phase-heads" 48 bytes payload
1749 bundle2-input-bundle: with-transaction
1749 bundle2-input-bundle: with-transaction
1750 bundle2-input-part: "replycaps" supported
1750 bundle2-input-part: "replycaps" supported
1751 bundle2-input-part: total payload size 168
1751 bundle2-input-part: total payload size 178
1752 bundle2-input-part: "check:phases" supported
1752 bundle2-input-part: "check:phases" supported
1753 bundle2-input-part: total payload size 48
1753 bundle2-input-part: total payload size 48
1754 bundle2-input-part: "check:heads" supported
1754 bundle2-input-part: "check:heads" supported
1755 bundle2-input-part: total payload size 20
1755 bundle2-input-part: total payload size 20
1756 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1756 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1757 adding changesets
1757 adding changesets
1758 add changeset ef1ea85a6374
1758 add changeset ef1ea85a6374
1759 add changeset f9cafe1212c8
1759 add changeset f9cafe1212c8
1760 add changeset 911600dab2ae
1760 add changeset 911600dab2ae
1761 add changeset e8fc755d4d82
1761 add changeset e8fc755d4d82
1762 adding manifests
1762 adding manifests
1763 adding file changes
1763 adding file changes
1764 adding abc.txt revisions
1764 adding abc.txt revisions
1765 adding foo/Bar/file.txt revisions
1765 adding foo/Bar/file.txt revisions
1766 adding foo/file.txt revisions
1766 adding foo/file.txt revisions
1767 adding quux/file.py revisions
1767 adding quux/file.py revisions
1768 added 4 changesets with 4 changes to 4 files (+1 heads)
1768 added 4 changesets with 4 changes to 4 files (+1 heads)
1769 calling hook pretxnchangegroup.acl: hgext.acl.hook
1769 calling hook pretxnchangegroup.acl: hgext.acl.hook
1770 acl: checking access for user "astro"
1770 acl: checking access for user "astro"
1771 acl: acl.allow.branches enabled, 0 entries for user astro
1771 acl: acl.allow.branches enabled, 0 entries for user astro
1772 acl: acl.deny.branches not enabled
1772 acl: acl.deny.branches not enabled
1773 acl: acl.allow not enabled
1773 acl: acl.allow not enabled
1774 acl: acl.deny not enabled
1774 acl: acl.deny not enabled
1775 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1775 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1776 bundle2-input-part: total payload size 2068
1776 bundle2-input-part: total payload size 2068
1777 bundle2-input-part: total payload size 48
1777 bundle2-input-part: total payload size 48
1778 bundle2-input-bundle: 4 parts total
1778 bundle2-input-bundle: 4 parts total
1779 transaction abort!
1779 transaction abort!
1780 rollback completed
1780 rollback completed
1781 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1781 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1782 no rollback information available
1782 no rollback information available
1783 2:fb35475503ef
1783 2:fb35475503ef
1784
1784
1785 $ do_push george
1785 $ do_push george
1786 Pushing as user george
1786 Pushing as user george
1787 hgrc = """
1787 hgrc = """
1788 [hooks]
1788 [hooks]
1789 pretxnchangegroup.acl = python:hgext.acl.hook
1789 pretxnchangegroup.acl = python:hgext.acl.hook
1790 [acl]
1790 [acl]
1791 sources = push
1791 sources = push
1792 [extensions]
1792 [extensions]
1793 [acl.allow.branches]
1793 [acl.allow.branches]
1794 * = george
1794 * = george
1795 """
1795 """
1796 pushing to ../b
1796 pushing to ../b
1797 query 1; heads
1797 query 1; heads
1798 searching for changes
1798 searching for changes
1799 all remote heads known locally
1799 all remote heads known locally
1800 listing keys for "phases"
1800 listing keys for "phases"
1801 checking for updated bookmarks
1801 checking for updated bookmarks
1802 listing keys for "bookmarks"
1802 listing keys for "bookmarks"
1803 listing keys for "bookmarks"
1803 listing keys for "bookmarks"
1804 4 changesets found
1804 4 changesets found
1805 list of changesets:
1805 list of changesets:
1806 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1806 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1807 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1807 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1808 911600dab2ae7a9baff75958b84fe606851ce955
1808 911600dab2ae7a9baff75958b84fe606851ce955
1809 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1809 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1810 bundle2-output-bundle: "HG20", 5 parts total
1810 bundle2-output-bundle: "HG20", 5 parts total
1811 bundle2-output-part: "replycaps" 168 bytes payload
1811 bundle2-output-part: "replycaps" 178 bytes payload
1812 bundle2-output-part: "check:phases" 48 bytes payload
1812 bundle2-output-part: "check:phases" 48 bytes payload
1813 bundle2-output-part: "check:heads" streamed payload
1813 bundle2-output-part: "check:heads" streamed payload
1814 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1814 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1815 bundle2-output-part: "phase-heads" 48 bytes payload
1815 bundle2-output-part: "phase-heads" 48 bytes payload
1816 bundle2-input-bundle: with-transaction
1816 bundle2-input-bundle: with-transaction
1817 bundle2-input-part: "replycaps" supported
1817 bundle2-input-part: "replycaps" supported
1818 bundle2-input-part: total payload size 168
1818 bundle2-input-part: total payload size 178
1819 bundle2-input-part: "check:phases" supported
1819 bundle2-input-part: "check:phases" supported
1820 bundle2-input-part: total payload size 48
1820 bundle2-input-part: total payload size 48
1821 bundle2-input-part: "check:heads" supported
1821 bundle2-input-part: "check:heads" supported
1822 bundle2-input-part: total payload size 20
1822 bundle2-input-part: total payload size 20
1823 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1823 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1824 adding changesets
1824 adding changesets
1825 add changeset ef1ea85a6374
1825 add changeset ef1ea85a6374
1826 add changeset f9cafe1212c8
1826 add changeset f9cafe1212c8
1827 add changeset 911600dab2ae
1827 add changeset 911600dab2ae
1828 add changeset e8fc755d4d82
1828 add changeset e8fc755d4d82
1829 adding manifests
1829 adding manifests
1830 adding file changes
1830 adding file changes
1831 adding abc.txt revisions
1831 adding abc.txt revisions
1832 adding foo/Bar/file.txt revisions
1832 adding foo/Bar/file.txt revisions
1833 adding foo/file.txt revisions
1833 adding foo/file.txt revisions
1834 adding quux/file.py revisions
1834 adding quux/file.py revisions
1835 added 4 changesets with 4 changes to 4 files (+1 heads)
1835 added 4 changesets with 4 changes to 4 files (+1 heads)
1836 calling hook pretxnchangegroup.acl: hgext.acl.hook
1836 calling hook pretxnchangegroup.acl: hgext.acl.hook
1837 acl: checking access for user "george"
1837 acl: checking access for user "george"
1838 acl: acl.allow.branches enabled, 1 entries for user george
1838 acl: acl.allow.branches enabled, 1 entries for user george
1839 acl: acl.deny.branches not enabled
1839 acl: acl.deny.branches not enabled
1840 acl: acl.allow not enabled
1840 acl: acl.allow not enabled
1841 acl: acl.deny not enabled
1841 acl: acl.deny not enabled
1842 acl: branch access granted: "ef1ea85a6374" on branch "default"
1842 acl: branch access granted: "ef1ea85a6374" on branch "default"
1843 acl: path access granted: "ef1ea85a6374"
1843 acl: path access granted: "ef1ea85a6374"
1844 acl: branch access granted: "f9cafe1212c8" on branch "default"
1844 acl: branch access granted: "f9cafe1212c8" on branch "default"
1845 acl: path access granted: "f9cafe1212c8"
1845 acl: path access granted: "f9cafe1212c8"
1846 acl: branch access granted: "911600dab2ae" on branch "default"
1846 acl: branch access granted: "911600dab2ae" on branch "default"
1847 acl: path access granted: "911600dab2ae"
1847 acl: path access granted: "911600dab2ae"
1848 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1848 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1849 acl: path access granted: "e8fc755d4d82"
1849 acl: path access granted: "e8fc755d4d82"
1850 bundle2-input-part: total payload size 2068
1850 bundle2-input-part: total payload size 2068
1851 bundle2-input-part: "phase-heads" supported
1851 bundle2-input-part: "phase-heads" supported
1852 bundle2-input-part: total payload size 48
1852 bundle2-input-part: total payload size 48
1853 bundle2-input-bundle: 4 parts total
1853 bundle2-input-bundle: 4 parts total
1854 updating the branch cache
1854 updating the branch cache
1855 bundle2-output-bundle: "HG20", 1 parts total
1855 bundle2-output-bundle: "HG20", 1 parts total
1856 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1856 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1857 bundle2-input-bundle: no-transaction
1857 bundle2-input-bundle: no-transaction
1858 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1858 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1859 bundle2-input-bundle: 0 parts total
1859 bundle2-input-bundle: 0 parts total
1860 listing keys for "phases"
1860 listing keys for "phases"
1861 repository tip rolled back to revision 2 (undo push)
1861 repository tip rolled back to revision 2 (undo push)
1862 2:fb35475503ef
1862 2:fb35475503ef
1863
1863
1864
1864
1865 Branch acl conflicting allow
1865 Branch acl conflicting allow
1866 asterisk ends up applying to all branches and allowing george to
1866 asterisk ends up applying to all branches and allowing george to
1867 push foobar into the remote
1867 push foobar into the remote
1868
1868
1869 $ init_config
1869 $ init_config
1870 $ echo "[acl.allow.branches]" >> $config
1870 $ echo "[acl.allow.branches]" >> $config
1871 $ echo "foobar = astro" >> $config
1871 $ echo "foobar = astro" >> $config
1872 $ echo "* = george" >> $config
1872 $ echo "* = george" >> $config
1873 $ do_push george
1873 $ do_push george
1874 Pushing as user george
1874 Pushing as user george
1875 hgrc = """
1875 hgrc = """
1876 [hooks]
1876 [hooks]
1877 pretxnchangegroup.acl = python:hgext.acl.hook
1877 pretxnchangegroup.acl = python:hgext.acl.hook
1878 [acl]
1878 [acl]
1879 sources = push
1879 sources = push
1880 [extensions]
1880 [extensions]
1881 [acl.allow.branches]
1881 [acl.allow.branches]
1882 foobar = astro
1882 foobar = astro
1883 * = george
1883 * = george
1884 """
1884 """
1885 pushing to ../b
1885 pushing to ../b
1886 query 1; heads
1886 query 1; heads
1887 searching for changes
1887 searching for changes
1888 all remote heads known locally
1888 all remote heads known locally
1889 listing keys for "phases"
1889 listing keys for "phases"
1890 checking for updated bookmarks
1890 checking for updated bookmarks
1891 listing keys for "bookmarks"
1891 listing keys for "bookmarks"
1892 listing keys for "bookmarks"
1892 listing keys for "bookmarks"
1893 4 changesets found
1893 4 changesets found
1894 list of changesets:
1894 list of changesets:
1895 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1895 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1896 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1896 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1897 911600dab2ae7a9baff75958b84fe606851ce955
1897 911600dab2ae7a9baff75958b84fe606851ce955
1898 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1898 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1899 bundle2-output-bundle: "HG20", 5 parts total
1899 bundle2-output-bundle: "HG20", 5 parts total
1900 bundle2-output-part: "replycaps" 168 bytes payload
1900 bundle2-output-part: "replycaps" 178 bytes payload
1901 bundle2-output-part: "check:phases" 48 bytes payload
1901 bundle2-output-part: "check:phases" 48 bytes payload
1902 bundle2-output-part: "check:heads" streamed payload
1902 bundle2-output-part: "check:heads" streamed payload
1903 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1903 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1904 bundle2-output-part: "phase-heads" 48 bytes payload
1904 bundle2-output-part: "phase-heads" 48 bytes payload
1905 bundle2-input-bundle: with-transaction
1905 bundle2-input-bundle: with-transaction
1906 bundle2-input-part: "replycaps" supported
1906 bundle2-input-part: "replycaps" supported
1907 bundle2-input-part: total payload size 168
1907 bundle2-input-part: total payload size 178
1908 bundle2-input-part: "check:phases" supported
1908 bundle2-input-part: "check:phases" supported
1909 bundle2-input-part: total payload size 48
1909 bundle2-input-part: total payload size 48
1910 bundle2-input-part: "check:heads" supported
1910 bundle2-input-part: "check:heads" supported
1911 bundle2-input-part: total payload size 20
1911 bundle2-input-part: total payload size 20
1912 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1912 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1913 adding changesets
1913 adding changesets
1914 add changeset ef1ea85a6374
1914 add changeset ef1ea85a6374
1915 add changeset f9cafe1212c8
1915 add changeset f9cafe1212c8
1916 add changeset 911600dab2ae
1916 add changeset 911600dab2ae
1917 add changeset e8fc755d4d82
1917 add changeset e8fc755d4d82
1918 adding manifests
1918 adding manifests
1919 adding file changes
1919 adding file changes
1920 adding abc.txt revisions
1920 adding abc.txt revisions
1921 adding foo/Bar/file.txt revisions
1921 adding foo/Bar/file.txt revisions
1922 adding foo/file.txt revisions
1922 adding foo/file.txt revisions
1923 adding quux/file.py revisions
1923 adding quux/file.py revisions
1924 added 4 changesets with 4 changes to 4 files (+1 heads)
1924 added 4 changesets with 4 changes to 4 files (+1 heads)
1925 calling hook pretxnchangegroup.acl: hgext.acl.hook
1925 calling hook pretxnchangegroup.acl: hgext.acl.hook
1926 acl: checking access for user "george"
1926 acl: checking access for user "george"
1927 acl: acl.allow.branches enabled, 1 entries for user george
1927 acl: acl.allow.branches enabled, 1 entries for user george
1928 acl: acl.deny.branches not enabled
1928 acl: acl.deny.branches not enabled
1929 acl: acl.allow not enabled
1929 acl: acl.allow not enabled
1930 acl: acl.deny not enabled
1930 acl: acl.deny not enabled
1931 acl: branch access granted: "ef1ea85a6374" on branch "default"
1931 acl: branch access granted: "ef1ea85a6374" on branch "default"
1932 acl: path access granted: "ef1ea85a6374"
1932 acl: path access granted: "ef1ea85a6374"
1933 acl: branch access granted: "f9cafe1212c8" on branch "default"
1933 acl: branch access granted: "f9cafe1212c8" on branch "default"
1934 acl: path access granted: "f9cafe1212c8"
1934 acl: path access granted: "f9cafe1212c8"
1935 acl: branch access granted: "911600dab2ae" on branch "default"
1935 acl: branch access granted: "911600dab2ae" on branch "default"
1936 acl: path access granted: "911600dab2ae"
1936 acl: path access granted: "911600dab2ae"
1937 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1937 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1938 acl: path access granted: "e8fc755d4d82"
1938 acl: path access granted: "e8fc755d4d82"
1939 bundle2-input-part: total payload size 2068
1939 bundle2-input-part: total payload size 2068
1940 bundle2-input-part: "phase-heads" supported
1940 bundle2-input-part: "phase-heads" supported
1941 bundle2-input-part: total payload size 48
1941 bundle2-input-part: total payload size 48
1942 bundle2-input-bundle: 4 parts total
1942 bundle2-input-bundle: 4 parts total
1943 updating the branch cache
1943 updating the branch cache
1944 bundle2-output-bundle: "HG20", 1 parts total
1944 bundle2-output-bundle: "HG20", 1 parts total
1945 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1945 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1946 bundle2-input-bundle: no-transaction
1946 bundle2-input-bundle: no-transaction
1947 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1947 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1948 bundle2-input-bundle: 0 parts total
1948 bundle2-input-bundle: 0 parts total
1949 listing keys for "phases"
1949 listing keys for "phases"
1950 repository tip rolled back to revision 2 (undo push)
1950 repository tip rolled back to revision 2 (undo push)
1951 2:fb35475503ef
1951 2:fb35475503ef
1952
1952
1953 Branch acl conflicting deny
1953 Branch acl conflicting deny
1954
1954
1955 $ init_config
1955 $ init_config
1956 $ echo "[acl.deny.branches]" >> $config
1956 $ echo "[acl.deny.branches]" >> $config
1957 $ echo "foobar = astro" >> $config
1957 $ echo "foobar = astro" >> $config
1958 $ echo "default = astro" >> $config
1958 $ echo "default = astro" >> $config
1959 $ echo "* = george" >> $config
1959 $ echo "* = george" >> $config
1960 $ do_push george
1960 $ do_push george
1961 Pushing as user george
1961 Pushing as user george
1962 hgrc = """
1962 hgrc = """
1963 [hooks]
1963 [hooks]
1964 pretxnchangegroup.acl = python:hgext.acl.hook
1964 pretxnchangegroup.acl = python:hgext.acl.hook
1965 [acl]
1965 [acl]
1966 sources = push
1966 sources = push
1967 [extensions]
1967 [extensions]
1968 [acl.deny.branches]
1968 [acl.deny.branches]
1969 foobar = astro
1969 foobar = astro
1970 default = astro
1970 default = astro
1971 * = george
1971 * = george
1972 """
1972 """
1973 pushing to ../b
1973 pushing to ../b
1974 query 1; heads
1974 query 1; heads
1975 searching for changes
1975 searching for changes
1976 all remote heads known locally
1976 all remote heads known locally
1977 listing keys for "phases"
1977 listing keys for "phases"
1978 checking for updated bookmarks
1978 checking for updated bookmarks
1979 listing keys for "bookmarks"
1979 listing keys for "bookmarks"
1980 listing keys for "bookmarks"
1980 listing keys for "bookmarks"
1981 4 changesets found
1981 4 changesets found
1982 list of changesets:
1982 list of changesets:
1983 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1983 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1984 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1984 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1985 911600dab2ae7a9baff75958b84fe606851ce955
1985 911600dab2ae7a9baff75958b84fe606851ce955
1986 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1986 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1987 bundle2-output-bundle: "HG20", 5 parts total
1987 bundle2-output-bundle: "HG20", 5 parts total
1988 bundle2-output-part: "replycaps" 168 bytes payload
1988 bundle2-output-part: "replycaps" 178 bytes payload
1989 bundle2-output-part: "check:phases" 48 bytes payload
1989 bundle2-output-part: "check:phases" 48 bytes payload
1990 bundle2-output-part: "check:heads" streamed payload
1990 bundle2-output-part: "check:heads" streamed payload
1991 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1991 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1992 bundle2-output-part: "phase-heads" 48 bytes payload
1992 bundle2-output-part: "phase-heads" 48 bytes payload
1993 bundle2-input-bundle: with-transaction
1993 bundle2-input-bundle: with-transaction
1994 bundle2-input-part: "replycaps" supported
1994 bundle2-input-part: "replycaps" supported
1995 bundle2-input-part: total payload size 168
1995 bundle2-input-part: total payload size 178
1996 bundle2-input-part: "check:phases" supported
1996 bundle2-input-part: "check:phases" supported
1997 bundle2-input-part: total payload size 48
1997 bundle2-input-part: total payload size 48
1998 bundle2-input-part: "check:heads" supported
1998 bundle2-input-part: "check:heads" supported
1999 bundle2-input-part: total payload size 20
1999 bundle2-input-part: total payload size 20
2000 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
2000 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
2001 adding changesets
2001 adding changesets
2002 add changeset ef1ea85a6374
2002 add changeset ef1ea85a6374
2003 add changeset f9cafe1212c8
2003 add changeset f9cafe1212c8
2004 add changeset 911600dab2ae
2004 add changeset 911600dab2ae
2005 add changeset e8fc755d4d82
2005 add changeset e8fc755d4d82
2006 adding manifests
2006 adding manifests
2007 adding file changes
2007 adding file changes
2008 adding abc.txt revisions
2008 adding abc.txt revisions
2009 adding foo/Bar/file.txt revisions
2009 adding foo/Bar/file.txt revisions
2010 adding foo/file.txt revisions
2010 adding foo/file.txt revisions
2011 adding quux/file.py revisions
2011 adding quux/file.py revisions
2012 added 4 changesets with 4 changes to 4 files (+1 heads)
2012 added 4 changesets with 4 changes to 4 files (+1 heads)
2013 calling hook pretxnchangegroup.acl: hgext.acl.hook
2013 calling hook pretxnchangegroup.acl: hgext.acl.hook
2014 acl: checking access for user "george"
2014 acl: checking access for user "george"
2015 acl: acl.allow.branches not enabled
2015 acl: acl.allow.branches not enabled
2016 acl: acl.deny.branches enabled, 1 entries for user george
2016 acl: acl.deny.branches enabled, 1 entries for user george
2017 acl: acl.allow not enabled
2017 acl: acl.allow not enabled
2018 acl: acl.deny not enabled
2018 acl: acl.deny not enabled
2019 error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2019 error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2020 bundle2-input-part: total payload size 2068
2020 bundle2-input-part: total payload size 2068
2021 bundle2-input-part: total payload size 48
2021 bundle2-input-part: total payload size 48
2022 bundle2-input-bundle: 4 parts total
2022 bundle2-input-bundle: 4 parts total
2023 transaction abort!
2023 transaction abort!
2024 rollback completed
2024 rollback completed
2025 abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2025 abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2026 no rollback information available
2026 no rollback information available
2027 2:fb35475503ef
2027 2:fb35475503ef
2028
2028
2029 User 'astro' must not be denied
2029 User 'astro' must not be denied
2030
2030
2031 $ init_config
2031 $ init_config
2032 $ echo "[acl.deny.branches]" >> $config
2032 $ echo "[acl.deny.branches]" >> $config
2033 $ echo "default = !astro" >> $config
2033 $ echo "default = !astro" >> $config
2034 $ do_push astro
2034 $ do_push astro
2035 Pushing as user astro
2035 Pushing as user astro
2036 hgrc = """
2036 hgrc = """
2037 [hooks]
2037 [hooks]
2038 pretxnchangegroup.acl = python:hgext.acl.hook
2038 pretxnchangegroup.acl = python:hgext.acl.hook
2039 [acl]
2039 [acl]
2040 sources = push
2040 sources = push
2041 [extensions]
2041 [extensions]
2042 [acl.deny.branches]
2042 [acl.deny.branches]
2043 default = !astro
2043 default = !astro
2044 """
2044 """
2045 pushing to ../b
2045 pushing to ../b
2046 query 1; heads
2046 query 1; heads
2047 searching for changes
2047 searching for changes
2048 all remote heads known locally
2048 all remote heads known locally
2049 listing keys for "phases"
2049 listing keys for "phases"
2050 checking for updated bookmarks
2050 checking for updated bookmarks
2051 listing keys for "bookmarks"
2051 listing keys for "bookmarks"
2052 listing keys for "bookmarks"
2052 listing keys for "bookmarks"
2053 4 changesets found
2053 4 changesets found
2054 list of changesets:
2054 list of changesets:
2055 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
2055 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
2056 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
2056 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
2057 911600dab2ae7a9baff75958b84fe606851ce955
2057 911600dab2ae7a9baff75958b84fe606851ce955
2058 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
2058 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
2059 bundle2-output-bundle: "HG20", 5 parts total
2059 bundle2-output-bundle: "HG20", 5 parts total
2060 bundle2-output-part: "replycaps" 168 bytes payload
2060 bundle2-output-part: "replycaps" 178 bytes payload
2061 bundle2-output-part: "check:phases" 48 bytes payload
2061 bundle2-output-part: "check:phases" 48 bytes payload
2062 bundle2-output-part: "check:heads" streamed payload
2062 bundle2-output-part: "check:heads" streamed payload
2063 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
2063 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
2064 bundle2-output-part: "phase-heads" 48 bytes payload
2064 bundle2-output-part: "phase-heads" 48 bytes payload
2065 bundle2-input-bundle: with-transaction
2065 bundle2-input-bundle: with-transaction
2066 bundle2-input-part: "replycaps" supported
2066 bundle2-input-part: "replycaps" supported
2067 bundle2-input-part: total payload size 168
2067 bundle2-input-part: total payload size 178
2068 bundle2-input-part: "check:phases" supported
2068 bundle2-input-part: "check:phases" supported
2069 bundle2-input-part: total payload size 48
2069 bundle2-input-part: total payload size 48
2070 bundle2-input-part: "check:heads" supported
2070 bundle2-input-part: "check:heads" supported
2071 bundle2-input-part: total payload size 20
2071 bundle2-input-part: total payload size 20
2072 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
2072 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
2073 adding changesets
2073 adding changesets
2074 add changeset ef1ea85a6374
2074 add changeset ef1ea85a6374
2075 add changeset f9cafe1212c8
2075 add changeset f9cafe1212c8
2076 add changeset 911600dab2ae
2076 add changeset 911600dab2ae
2077 add changeset e8fc755d4d82
2077 add changeset e8fc755d4d82
2078 adding manifests
2078 adding manifests
2079 adding file changes
2079 adding file changes
2080 adding abc.txt revisions
2080 adding abc.txt revisions
2081 adding foo/Bar/file.txt revisions
2081 adding foo/Bar/file.txt revisions
2082 adding foo/file.txt revisions
2082 adding foo/file.txt revisions
2083 adding quux/file.py revisions
2083 adding quux/file.py revisions
2084 added 4 changesets with 4 changes to 4 files (+1 heads)
2084 added 4 changesets with 4 changes to 4 files (+1 heads)
2085 calling hook pretxnchangegroup.acl: hgext.acl.hook
2085 calling hook pretxnchangegroup.acl: hgext.acl.hook
2086 acl: checking access for user "astro"
2086 acl: checking access for user "astro"
2087 acl: acl.allow.branches not enabled
2087 acl: acl.allow.branches not enabled
2088 acl: acl.deny.branches enabled, 0 entries for user astro
2088 acl: acl.deny.branches enabled, 0 entries for user astro
2089 acl: acl.allow not enabled
2089 acl: acl.allow not enabled
2090 acl: acl.deny not enabled
2090 acl: acl.deny not enabled
2091 acl: branch access granted: "ef1ea85a6374" on branch "default"
2091 acl: branch access granted: "ef1ea85a6374" on branch "default"
2092 acl: path access granted: "ef1ea85a6374"
2092 acl: path access granted: "ef1ea85a6374"
2093 acl: branch access granted: "f9cafe1212c8" on branch "default"
2093 acl: branch access granted: "f9cafe1212c8" on branch "default"
2094 acl: path access granted: "f9cafe1212c8"
2094 acl: path access granted: "f9cafe1212c8"
2095 acl: branch access granted: "911600dab2ae" on branch "default"
2095 acl: branch access granted: "911600dab2ae" on branch "default"
2096 acl: path access granted: "911600dab2ae"
2096 acl: path access granted: "911600dab2ae"
2097 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
2097 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
2098 acl: path access granted: "e8fc755d4d82"
2098 acl: path access granted: "e8fc755d4d82"
2099 bundle2-input-part: total payload size 2068
2099 bundle2-input-part: total payload size 2068
2100 bundle2-input-part: "phase-heads" supported
2100 bundle2-input-part: "phase-heads" supported
2101 bundle2-input-part: total payload size 48
2101 bundle2-input-part: total payload size 48
2102 bundle2-input-bundle: 4 parts total
2102 bundle2-input-bundle: 4 parts total
2103 updating the branch cache
2103 updating the branch cache
2104 bundle2-output-bundle: "HG20", 1 parts total
2104 bundle2-output-bundle: "HG20", 1 parts total
2105 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
2105 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
2106 bundle2-input-bundle: no-transaction
2106 bundle2-input-bundle: no-transaction
2107 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
2107 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
2108 bundle2-input-bundle: 0 parts total
2108 bundle2-input-bundle: 0 parts total
2109 listing keys for "phases"
2109 listing keys for "phases"
2110 repository tip rolled back to revision 2 (undo push)
2110 repository tip rolled back to revision 2 (undo push)
2111 2:fb35475503ef
2111 2:fb35475503ef
2112
2112
2113
2113
2114 Non-astro users must be denied
2114 Non-astro users must be denied
2115
2115
2116 $ do_push george
2116 $ do_push george
2117 Pushing as user george
2117 Pushing as user george
2118 hgrc = """
2118 hgrc = """
2119 [hooks]
2119 [hooks]
2120 pretxnchangegroup.acl = python:hgext.acl.hook
2120 pretxnchangegroup.acl = python:hgext.acl.hook
2121 [acl]
2121 [acl]
2122 sources = push
2122 sources = push
2123 [extensions]
2123 [extensions]
2124 [acl.deny.branches]
2124 [acl.deny.branches]
2125 default = !astro
2125 default = !astro
2126 """
2126 """
2127 pushing to ../b
2127 pushing to ../b
2128 query 1; heads
2128 query 1; heads
2129 searching for changes
2129 searching for changes
2130 all remote heads known locally
2130 all remote heads known locally
2131 listing keys for "phases"
2131 listing keys for "phases"
2132 checking for updated bookmarks
2132 checking for updated bookmarks
2133 listing keys for "bookmarks"
2133 listing keys for "bookmarks"
2134 listing keys for "bookmarks"
2134 listing keys for "bookmarks"
2135 4 changesets found
2135 4 changesets found
2136 list of changesets:
2136 list of changesets:
2137 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
2137 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
2138 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
2138 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
2139 911600dab2ae7a9baff75958b84fe606851ce955
2139 911600dab2ae7a9baff75958b84fe606851ce955
2140 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
2140 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
2141 bundle2-output-bundle: "HG20", 5 parts total
2141 bundle2-output-bundle: "HG20", 5 parts total
2142 bundle2-output-part: "replycaps" 168 bytes payload
2142 bundle2-output-part: "replycaps" 178 bytes payload
2143 bundle2-output-part: "check:phases" 48 bytes payload
2143 bundle2-output-part: "check:phases" 48 bytes payload
2144 bundle2-output-part: "check:heads" streamed payload
2144 bundle2-output-part: "check:heads" streamed payload
2145 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
2145 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
2146 bundle2-output-part: "phase-heads" 48 bytes payload
2146 bundle2-output-part: "phase-heads" 48 bytes payload
2147 bundle2-input-bundle: with-transaction
2147 bundle2-input-bundle: with-transaction
2148 bundle2-input-part: "replycaps" supported
2148 bundle2-input-part: "replycaps" supported
2149 bundle2-input-part: total payload size 168
2149 bundle2-input-part: total payload size 178
2150 bundle2-input-part: "check:phases" supported
2150 bundle2-input-part: "check:phases" supported
2151 bundle2-input-part: total payload size 48
2151 bundle2-input-part: total payload size 48
2152 bundle2-input-part: "check:heads" supported
2152 bundle2-input-part: "check:heads" supported
2153 bundle2-input-part: total payload size 20
2153 bundle2-input-part: total payload size 20
2154 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
2154 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
2155 adding changesets
2155 adding changesets
2156 add changeset ef1ea85a6374
2156 add changeset ef1ea85a6374
2157 add changeset f9cafe1212c8
2157 add changeset f9cafe1212c8
2158 add changeset 911600dab2ae
2158 add changeset 911600dab2ae
2159 add changeset e8fc755d4d82
2159 add changeset e8fc755d4d82
2160 adding manifests
2160 adding manifests
2161 adding file changes
2161 adding file changes
2162 adding abc.txt revisions
2162 adding abc.txt revisions
2163 adding foo/Bar/file.txt revisions
2163 adding foo/Bar/file.txt revisions
2164 adding foo/file.txt revisions
2164 adding foo/file.txt revisions
2165 adding quux/file.py revisions
2165 adding quux/file.py revisions
2166 added 4 changesets with 4 changes to 4 files (+1 heads)
2166 added 4 changesets with 4 changes to 4 files (+1 heads)
2167 calling hook pretxnchangegroup.acl: hgext.acl.hook
2167 calling hook pretxnchangegroup.acl: hgext.acl.hook
2168 acl: checking access for user "george"
2168 acl: checking access for user "george"
2169 acl: acl.allow.branches not enabled
2169 acl: acl.allow.branches not enabled
2170 acl: acl.deny.branches enabled, 1 entries for user george
2170 acl: acl.deny.branches enabled, 1 entries for user george
2171 acl: acl.allow not enabled
2171 acl: acl.allow not enabled
2172 acl: acl.deny not enabled
2172 acl: acl.deny not enabled
2173 error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2173 error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2174 bundle2-input-part: total payload size 2068
2174 bundle2-input-part: total payload size 2068
2175 bundle2-input-part: total payload size 48
2175 bundle2-input-part: total payload size 48
2176 bundle2-input-bundle: 4 parts total
2176 bundle2-input-bundle: 4 parts total
2177 transaction abort!
2177 transaction abort!
2178 rollback completed
2178 rollback completed
2179 abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2179 abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2180 no rollback information available
2180 no rollback information available
2181 2:fb35475503ef
2181 2:fb35475503ef
2182
2182
2183
2183
@@ -1,995 +1,1010 b''
1 #require serve
1 #require serve
2
2
3 $ cat << EOF >> $HGRCPATH
3 $ cat << EOF >> $HGRCPATH
4 > [ui]
4 > [ui]
5 > logtemplate={rev}:{node|short} {desc|firstline}
5 > logtemplate={rev}:{node|short} {desc|firstline}
6 > [phases]
6 > [phases]
7 > publish=False
7 > publish=False
8 > [experimental]
8 > [experimental]
9 > evolution.createmarkers=True
9 > evolution.createmarkers=True
10 > evolution.exchange=True
10 > evolution.exchange=True
11 > EOF
11 > EOF
12
12
13 $ cat > $TESTTMP/hook.sh <<'EOF'
13 $ cat > $TESTTMP/hook.sh <<'EOF'
14 > echo "test-hook-bookmark: $HG_BOOKMARK: $HG_OLDNODE -> $HG_NODE"
14 > echo "test-hook-bookmark: $HG_BOOKMARK: $HG_OLDNODE -> $HG_NODE"
15 > EOF
15 > EOF
16 $ TESTHOOK="hooks.txnclose-bookmark.test=sh $TESTTMP/hook.sh"
16 $ TESTHOOK="hooks.txnclose-bookmark.test=sh $TESTTMP/hook.sh"
17
17
18 initialize
18 initialize
19
19
20 $ hg init a
20 $ hg init a
21 $ cd a
21 $ cd a
22 $ echo 'test' > test
22 $ echo 'test' > test
23 $ hg commit -Am'test'
23 $ hg commit -Am'test'
24 adding test
24 adding test
25
25
26 set bookmarks
26 set bookmarks
27
27
28 $ hg bookmark X
28 $ hg bookmark X
29 $ hg bookmark Y
29 $ hg bookmark Y
30 $ hg bookmark Z
30 $ hg bookmark Z
31
31
32 import bookmark by name
32 import bookmark by name
33
33
34 $ hg init ../b
34 $ hg init ../b
35 $ cd ../b
35 $ cd ../b
36 $ hg book Y
36 $ hg book Y
37 $ hg book
37 $ hg book
38 * Y -1:000000000000
38 * Y -1:000000000000
39 $ hg pull ../a --config "$TESTHOOK"
39 $ hg pull ../a --config "$TESTHOOK"
40 pulling from ../a
40 pulling from ../a
41 requesting all changes
41 requesting all changes
42 adding changesets
42 adding changesets
43 adding manifests
43 adding manifests
44 adding file changes
44 adding file changes
45 added 1 changesets with 1 changes to 1 files
45 added 1 changesets with 1 changes to 1 files
46 adding remote bookmark X
46 adding remote bookmark X
47 updating bookmark Y
47 updating bookmark Y
48 adding remote bookmark Z
48 adding remote bookmark Z
49 new changesets 4e3505fd9583
49 new changesets 4e3505fd9583
50 test-hook-bookmark: X: -> 4e3505fd95835d721066b76e75dbb8cc554d7f77
50 test-hook-bookmark: X: -> 4e3505fd95835d721066b76e75dbb8cc554d7f77
51 test-hook-bookmark: Y: 0000000000000000000000000000000000000000 -> 4e3505fd95835d721066b76e75dbb8cc554d7f77
51 test-hook-bookmark: Y: 0000000000000000000000000000000000000000 -> 4e3505fd95835d721066b76e75dbb8cc554d7f77
52 test-hook-bookmark: Z: -> 4e3505fd95835d721066b76e75dbb8cc554d7f77
52 test-hook-bookmark: Z: -> 4e3505fd95835d721066b76e75dbb8cc554d7f77
53 (run 'hg update' to get a working copy)
53 (run 'hg update' to get a working copy)
54 $ hg bookmarks
54 $ hg bookmarks
55 X 0:4e3505fd9583
55 X 0:4e3505fd9583
56 * Y 0:4e3505fd9583
56 * Y 0:4e3505fd9583
57 Z 0:4e3505fd9583
57 Z 0:4e3505fd9583
58 $ hg debugpushkey ../a namespaces
58 $ hg debugpushkey ../a namespaces
59 bookmarks
59 bookmarks
60 namespaces
60 namespaces
61 obsolete
61 obsolete
62 phases
62 phases
63 $ hg debugpushkey ../a bookmarks
63 $ hg debugpushkey ../a bookmarks
64 X 4e3505fd95835d721066b76e75dbb8cc554d7f77
64 X 4e3505fd95835d721066b76e75dbb8cc554d7f77
65 Y 4e3505fd95835d721066b76e75dbb8cc554d7f77
65 Y 4e3505fd95835d721066b76e75dbb8cc554d7f77
66 Z 4e3505fd95835d721066b76e75dbb8cc554d7f77
66 Z 4e3505fd95835d721066b76e75dbb8cc554d7f77
67
67
68 delete the bookmark to re-pull it
68 delete the bookmark to re-pull it
69
69
70 $ hg book -d X
70 $ hg book -d X
71 $ hg pull -B X ../a
71 $ hg pull -B X ../a
72 pulling from ../a
72 pulling from ../a
73 no changes found
73 no changes found
74 adding remote bookmark X
74 adding remote bookmark X
75
75
76 finally no-op pull
76 finally no-op pull
77
77
78 $ hg pull -B X ../a
78 $ hg pull -B X ../a
79 pulling from ../a
79 pulling from ../a
80 no changes found
80 no changes found
81 $ hg bookmark
81 $ hg bookmark
82 X 0:4e3505fd9583
82 X 0:4e3505fd9583
83 * Y 0:4e3505fd9583
83 * Y 0:4e3505fd9583
84 Z 0:4e3505fd9583
84 Z 0:4e3505fd9583
85
85
86 export bookmark by name
86 export bookmark by name
87
87
88 $ hg bookmark W
88 $ hg bookmark W
89 $ hg bookmark foo
89 $ hg bookmark foo
90 $ hg bookmark foobar
90 $ hg bookmark foobar
91 $ hg push -B W ../a
91 $ hg push -B W ../a
92 pushing to ../a
92 pushing to ../a
93 searching for changes
93 searching for changes
94 no changes found
94 no changes found
95 exporting bookmark W
95 exporting bookmark W
96 [1]
96 [1]
97 $ hg -R ../a bookmarks
97 $ hg -R ../a bookmarks
98 W -1:000000000000
98 W -1:000000000000
99 X 0:4e3505fd9583
99 X 0:4e3505fd9583
100 Y 0:4e3505fd9583
100 Y 0:4e3505fd9583
101 * Z 0:4e3505fd9583
101 * Z 0:4e3505fd9583
102
102
103 delete a remote bookmark
103 delete a remote bookmark
104
104
105 $ hg book -d W
105 $ hg book -d W
106 $ hg push -B W ../a --config "$TESTHOOK" --debug --config devel.bundle2.debug=yes
106 $ hg push -B W ../a --config "$TESTHOOK" --debug --config devel.bundle2.debug=yes
107 pushing to ../a
107 pushing to ../a
108 query 1; heads
108 query 1; heads
109 searching for changes
109 searching for changes
110 all remote heads known locally
110 all remote heads known locally
111 listing keys for "phases"
111 listing keys for "phases"
112 checking for updated bookmarks
112 checking for updated bookmarks
113 listing keys for "bookmarks"
113 listing keys for "bookmarks"
114 no changes found
114 no changes found
115 bundle2-output-bundle: "HG20", 3 parts total
115 bundle2-output-bundle: "HG20", 4 parts total
116 bundle2-output: start emission of HG20 stream
116 bundle2-output: start emission of HG20 stream
117 bundle2-output: bundle parameter:
117 bundle2-output: bundle parameter:
118 bundle2-output: start of parts
118 bundle2-output: start of parts
119 bundle2-output: bundle part: "replycaps"
119 bundle2-output: bundle part: "replycaps"
120 bundle2-output-part: "replycaps" 185 bytes payload
120 bundle2-output-part: "replycaps" 195 bytes payload
121 bundle2-output: part 0: "REPLYCAPS"
121 bundle2-output: part 0: "REPLYCAPS"
122 bundle2-output: header chunk size: 16
122 bundle2-output: header chunk size: 16
123 bundle2-output: payload chunk size: 185
123 bundle2-output: payload chunk size: 195
124 bundle2-output: closing payload chunk
125 bundle2-output: bundle part: "check:bookmarks"
126 bundle2-output-part: "check:bookmarks" 23 bytes payload
127 bundle2-output: part 1: "CHECK:BOOKMARKS"
128 bundle2-output: header chunk size: 22
129 bundle2-output: payload chunk size: 23
124 bundle2-output: closing payload chunk
130 bundle2-output: closing payload chunk
125 bundle2-output: bundle part: "check:phases"
131 bundle2-output: bundle part: "check:phases"
126 bundle2-output-part: "check:phases" 48 bytes payload
132 bundle2-output-part: "check:phases" 48 bytes payload
127 bundle2-output: part 1: "CHECK:PHASES"
133 bundle2-output: part 2: "CHECK:PHASES"
128 bundle2-output: header chunk size: 19
134 bundle2-output: header chunk size: 19
129 bundle2-output: payload chunk size: 48
135 bundle2-output: payload chunk size: 48
130 bundle2-output: closing payload chunk
136 bundle2-output: closing payload chunk
131 bundle2-output: bundle part: "pushkey"
137 bundle2-output: bundle part: "pushkey"
132 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
138 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
133 bundle2-output: part 2: "PUSHKEY"
139 bundle2-output: part 3: "PUSHKEY"
134 bundle2-output: header chunk size: 90
140 bundle2-output: header chunk size: 90
135 bundle2-output: closing payload chunk
141 bundle2-output: closing payload chunk
136 bundle2-output: end of bundle
142 bundle2-output: end of bundle
137 bundle2-input: start processing of HG20 stream
143 bundle2-input: start processing of HG20 stream
138 bundle2-input: reading bundle2 stream parameters
144 bundle2-input: reading bundle2 stream parameters
139 bundle2-input-bundle: with-transaction
145 bundle2-input-bundle: with-transaction
140 bundle2-input: start extraction of bundle2 parts
146 bundle2-input: start extraction of bundle2 parts
141 bundle2-input: part header size: 16
147 bundle2-input: part header size: 16
142 bundle2-input: part type: "REPLYCAPS"
148 bundle2-input: part type: "REPLYCAPS"
143 bundle2-input: part id: "0"
149 bundle2-input: part id: "0"
144 bundle2-input: part parameters: 0
150 bundle2-input: part parameters: 0
145 bundle2-input: found a handler for part replycaps
151 bundle2-input: found a handler for part replycaps
146 bundle2-input-part: "replycaps" supported
152 bundle2-input-part: "replycaps" supported
147 bundle2-input: payload chunk size: 185
153 bundle2-input: payload chunk size: 195
148 bundle2-input: payload chunk size: 0
154 bundle2-input: payload chunk size: 0
149 bundle2-input-part: total payload size 185
155 bundle2-input-part: total payload size 195
156 bundle2-input: part header size: 22
157 bundle2-input: part type: "CHECK:BOOKMARKS"
158 bundle2-input: part id: "1"
159 bundle2-input: part parameters: 0
160 bundle2-input: found a handler for part check:bookmarks
161 bundle2-input-part: "check:bookmarks" supported
162 bundle2-input: payload chunk size: 23
163 bundle2-input: payload chunk size: 0
164 bundle2-input-part: total payload size 23
150 bundle2-input: part header size: 19
165 bundle2-input: part header size: 19
151 bundle2-input: part type: "CHECK:PHASES"
166 bundle2-input: part type: "CHECK:PHASES"
152 bundle2-input: part id: "1"
167 bundle2-input: part id: "2"
153 bundle2-input: part parameters: 0
168 bundle2-input: part parameters: 0
154 bundle2-input: found a handler for part check:phases
169 bundle2-input: found a handler for part check:phases
155 bundle2-input-part: "check:phases" supported
170 bundle2-input-part: "check:phases" supported
156 bundle2-input: payload chunk size: 48
171 bundle2-input: payload chunk size: 48
157 bundle2-input: payload chunk size: 0
172 bundle2-input: payload chunk size: 0
158 bundle2-input-part: total payload size 48
173 bundle2-input-part: total payload size 48
159 bundle2-input: part header size: 90
174 bundle2-input: part header size: 90
160 bundle2-input: part type: "PUSHKEY"
175 bundle2-input: part type: "PUSHKEY"
161 bundle2-input: part id: "2"
176 bundle2-input: part id: "3"
162 bundle2-input: part parameters: 4
177 bundle2-input: part parameters: 4
163 bundle2-input: found a handler for part pushkey
178 bundle2-input: found a handler for part pushkey
164 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
179 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
165 pushing key for "bookmarks:W"
180 pushing key for "bookmarks:W"
166 bundle2-input: payload chunk size: 0
181 bundle2-input: payload chunk size: 0
167 bundle2-input: part header size: 0
182 bundle2-input: part header size: 0
168 bundle2-input: end of bundle2 stream
183 bundle2-input: end of bundle2 stream
169 bundle2-input-bundle: 2 parts total
184 bundle2-input-bundle: 3 parts total
170 running hook txnclose-bookmark.test: sh $TESTTMP/hook.sh
185 running hook txnclose-bookmark.test: sh $TESTTMP/hook.sh
171 test-hook-bookmark: W: 0000000000000000000000000000000000000000 ->
186 test-hook-bookmark: W: 0000000000000000000000000000000000000000 ->
172 bundle2-output-bundle: "HG20", 1 parts total
187 bundle2-output-bundle: "HG20", 1 parts total
173 bundle2-output: start emission of HG20 stream
188 bundle2-output: start emission of HG20 stream
174 bundle2-output: bundle parameter:
189 bundle2-output: bundle parameter:
175 bundle2-output: start of parts
190 bundle2-output: start of parts
176 bundle2-output: bundle part: "reply:pushkey"
191 bundle2-output: bundle part: "reply:pushkey"
177 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
192 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
178 bundle2-output: part 0: "REPLY:PUSHKEY"
193 bundle2-output: part 0: "REPLY:PUSHKEY"
179 bundle2-output: header chunk size: 43
194 bundle2-output: header chunk size: 43
180 bundle2-output: closing payload chunk
195 bundle2-output: closing payload chunk
181 bundle2-output: end of bundle
196 bundle2-output: end of bundle
182 bundle2-input: start processing of HG20 stream
197 bundle2-input: start processing of HG20 stream
183 bundle2-input: reading bundle2 stream parameters
198 bundle2-input: reading bundle2 stream parameters
184 bundle2-input-bundle: no-transaction
199 bundle2-input-bundle: no-transaction
185 bundle2-input: start extraction of bundle2 parts
200 bundle2-input: start extraction of bundle2 parts
186 bundle2-input: part header size: 43
201 bundle2-input: part header size: 43
187 bundle2-input: part type: "REPLY:PUSHKEY"
202 bundle2-input: part type: "REPLY:PUSHKEY"
188 bundle2-input: part id: "0"
203 bundle2-input: part id: "0"
189 bundle2-input: part parameters: 2
204 bundle2-input: part parameters: 2
190 bundle2-input: found a handler for part reply:pushkey
205 bundle2-input: found a handler for part reply:pushkey
191 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
206 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
192 bundle2-input: payload chunk size: 0
207 bundle2-input: payload chunk size: 0
193 bundle2-input: part header size: 0
208 bundle2-input: part header size: 0
194 bundle2-input: end of bundle2 stream
209 bundle2-input: end of bundle2 stream
195 bundle2-input-bundle: 0 parts total
210 bundle2-input-bundle: 0 parts total
196 deleting remote bookmark W
211 deleting remote bookmark W
197 listing keys for "phases"
212 listing keys for "phases"
198 [1]
213 [1]
199
214
200 export the active bookmark
215 export the active bookmark
201
216
202 $ hg bookmark V
217 $ hg bookmark V
203 $ hg push -B . ../a
218 $ hg push -B . ../a
204 pushing to ../a
219 pushing to ../a
205 searching for changes
220 searching for changes
206 no changes found
221 no changes found
207 exporting bookmark V
222 exporting bookmark V
208 [1]
223 [1]
209
224
210 exporting the active bookmark with 'push -B .'
225 exporting the active bookmark with 'push -B .'
211 demand that one of the bookmarks is activated
226 demand that one of the bookmarks is activated
212
227
213 $ hg update -r default
228 $ hg update -r default
214 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
229 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
215 (leaving bookmark V)
230 (leaving bookmark V)
216 $ hg push -B . ../a
231 $ hg push -B . ../a
217 abort: no active bookmark
232 abort: no active bookmark
218 [255]
233 [255]
219 $ hg update -r V
234 $ hg update -r V
220 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
235 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
221 (activating bookmark V)
236 (activating bookmark V)
222
237
223 delete the bookmark
238 delete the bookmark
224
239
225 $ hg book -d V
240 $ hg book -d V
226 $ hg push -B V ../a
241 $ hg push -B V ../a
227 pushing to ../a
242 pushing to ../a
228 searching for changes
243 searching for changes
229 no changes found
244 no changes found
230 deleting remote bookmark V
245 deleting remote bookmark V
231 [1]
246 [1]
232 $ hg up foobar
247 $ hg up foobar
233 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
248 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
234 (activating bookmark foobar)
249 (activating bookmark foobar)
235
250
236 push/pull name that doesn't exist
251 push/pull name that doesn't exist
237
252
238 $ hg push -B badname ../a
253 $ hg push -B badname ../a
239 pushing to ../a
254 pushing to ../a
240 searching for changes
255 searching for changes
241 bookmark badname does not exist on the local or remote repository!
256 bookmark badname does not exist on the local or remote repository!
242 no changes found
257 no changes found
243 [2]
258 [2]
244 $ hg pull -B anotherbadname ../a
259 $ hg pull -B anotherbadname ../a
245 pulling from ../a
260 pulling from ../a
246 abort: remote bookmark anotherbadname not found!
261 abort: remote bookmark anotherbadname not found!
247 [255]
262 [255]
248
263
249 divergent bookmarks
264 divergent bookmarks
250
265
251 $ cd ../a
266 $ cd ../a
252 $ echo c1 > f1
267 $ echo c1 > f1
253 $ hg ci -Am1
268 $ hg ci -Am1
254 adding f1
269 adding f1
255 $ hg book -f @
270 $ hg book -f @
256 $ hg book -f X
271 $ hg book -f X
257 $ hg book
272 $ hg book
258 @ 1:0d2164f0ce0d
273 @ 1:0d2164f0ce0d
259 * X 1:0d2164f0ce0d
274 * X 1:0d2164f0ce0d
260 Y 0:4e3505fd9583
275 Y 0:4e3505fd9583
261 Z 1:0d2164f0ce0d
276 Z 1:0d2164f0ce0d
262
277
263 $ cd ../b
278 $ cd ../b
264 $ hg up
279 $ hg up
265 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
280 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
266 updating bookmark foobar
281 updating bookmark foobar
267 $ echo c2 > f2
282 $ echo c2 > f2
268 $ hg ci -Am2
283 $ hg ci -Am2
269 adding f2
284 adding f2
270 $ hg book -if @
285 $ hg book -if @
271 $ hg book -if X
286 $ hg book -if X
272 $ hg book
287 $ hg book
273 @ 1:9b140be10808
288 @ 1:9b140be10808
274 X 1:9b140be10808
289 X 1:9b140be10808
275 Y 0:4e3505fd9583
290 Y 0:4e3505fd9583
276 Z 0:4e3505fd9583
291 Z 0:4e3505fd9583
277 foo -1:000000000000
292 foo -1:000000000000
278 * foobar 1:9b140be10808
293 * foobar 1:9b140be10808
279
294
280 $ hg pull --config paths.foo=../a foo --config "$TESTHOOK"
295 $ hg pull --config paths.foo=../a foo --config "$TESTHOOK"
281 pulling from $TESTTMP/a (glob)
296 pulling from $TESTTMP/a (glob)
282 searching for changes
297 searching for changes
283 adding changesets
298 adding changesets
284 adding manifests
299 adding manifests
285 adding file changes
300 adding file changes
286 added 1 changesets with 1 changes to 1 files (+1 heads)
301 added 1 changesets with 1 changes to 1 files (+1 heads)
287 divergent bookmark @ stored as @foo
302 divergent bookmark @ stored as @foo
288 divergent bookmark X stored as X@foo
303 divergent bookmark X stored as X@foo
289 updating bookmark Z
304 updating bookmark Z
290 new changesets 0d2164f0ce0d
305 new changesets 0d2164f0ce0d
291 test-hook-bookmark: @foo: -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
306 test-hook-bookmark: @foo: -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
292 test-hook-bookmark: X@foo: -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
307 test-hook-bookmark: X@foo: -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
293 test-hook-bookmark: Z: 4e3505fd95835d721066b76e75dbb8cc554d7f77 -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
308 test-hook-bookmark: Z: 4e3505fd95835d721066b76e75dbb8cc554d7f77 -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
294 (run 'hg heads' to see heads, 'hg merge' to merge)
309 (run 'hg heads' to see heads, 'hg merge' to merge)
295 $ hg book
310 $ hg book
296 @ 1:9b140be10808
311 @ 1:9b140be10808
297 @foo 2:0d2164f0ce0d
312 @foo 2:0d2164f0ce0d
298 X 1:9b140be10808
313 X 1:9b140be10808
299 X@foo 2:0d2164f0ce0d
314 X@foo 2:0d2164f0ce0d
300 Y 0:4e3505fd9583
315 Y 0:4e3505fd9583
301 Z 2:0d2164f0ce0d
316 Z 2:0d2164f0ce0d
302 foo -1:000000000000
317 foo -1:000000000000
303 * foobar 1:9b140be10808
318 * foobar 1:9b140be10808
304
319
305 (test that too many divergence of bookmark)
320 (test that too many divergence of bookmark)
306
321
307 $ $PYTHON $TESTDIR/seq.py 1 100 | while read i; do hg bookmarks -r 000000000000 "X@${i}"; done
322 $ $PYTHON $TESTDIR/seq.py 1 100 | while read i; do hg bookmarks -r 000000000000 "X@${i}"; done
308 $ hg pull ../a
323 $ hg pull ../a
309 pulling from ../a
324 pulling from ../a
310 searching for changes
325 searching for changes
311 no changes found
326 no changes found
312 warning: failed to assign numbered name to divergent bookmark X
327 warning: failed to assign numbered name to divergent bookmark X
313 divergent bookmark @ stored as @1
328 divergent bookmark @ stored as @1
314 $ hg bookmarks | grep '^ X' | grep -v ':000000000000'
329 $ hg bookmarks | grep '^ X' | grep -v ':000000000000'
315 X 1:9b140be10808
330 X 1:9b140be10808
316 X@foo 2:0d2164f0ce0d
331 X@foo 2:0d2164f0ce0d
317
332
318 (test that remotely diverged bookmarks are reused if they aren't changed)
333 (test that remotely diverged bookmarks are reused if they aren't changed)
319
334
320 $ hg bookmarks | grep '^ @'
335 $ hg bookmarks | grep '^ @'
321 @ 1:9b140be10808
336 @ 1:9b140be10808
322 @1 2:0d2164f0ce0d
337 @1 2:0d2164f0ce0d
323 @foo 2:0d2164f0ce0d
338 @foo 2:0d2164f0ce0d
324 $ hg pull ../a
339 $ hg pull ../a
325 pulling from ../a
340 pulling from ../a
326 searching for changes
341 searching for changes
327 no changes found
342 no changes found
328 warning: failed to assign numbered name to divergent bookmark X
343 warning: failed to assign numbered name to divergent bookmark X
329 divergent bookmark @ stored as @1
344 divergent bookmark @ stored as @1
330 $ hg bookmarks | grep '^ @'
345 $ hg bookmarks | grep '^ @'
331 @ 1:9b140be10808
346 @ 1:9b140be10808
332 @1 2:0d2164f0ce0d
347 @1 2:0d2164f0ce0d
333 @foo 2:0d2164f0ce0d
348 @foo 2:0d2164f0ce0d
334
349
335 $ $PYTHON $TESTDIR/seq.py 1 100 | while read i; do hg bookmarks -d "X@${i}"; done
350 $ $PYTHON $TESTDIR/seq.py 1 100 | while read i; do hg bookmarks -d "X@${i}"; done
336 $ hg bookmarks -d "@1"
351 $ hg bookmarks -d "@1"
337
352
338 $ hg push -f ../a
353 $ hg push -f ../a
339 pushing to ../a
354 pushing to ../a
340 searching for changes
355 searching for changes
341 adding changesets
356 adding changesets
342 adding manifests
357 adding manifests
343 adding file changes
358 adding file changes
344 added 1 changesets with 1 changes to 1 files (+1 heads)
359 added 1 changesets with 1 changes to 1 files (+1 heads)
345 $ hg -R ../a book
360 $ hg -R ../a book
346 @ 1:0d2164f0ce0d
361 @ 1:0d2164f0ce0d
347 * X 1:0d2164f0ce0d
362 * X 1:0d2164f0ce0d
348 Y 0:4e3505fd9583
363 Y 0:4e3505fd9583
349 Z 1:0d2164f0ce0d
364 Z 1:0d2164f0ce0d
350
365
351 explicit pull should overwrite the local version (issue4439)
366 explicit pull should overwrite the local version (issue4439)
352
367
353 $ hg update -r X
368 $ hg update -r X
354 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
369 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
355 (activating bookmark X)
370 (activating bookmark X)
356 $ hg pull --config paths.foo=../a foo -B . --config "$TESTHOOK"
371 $ hg pull --config paths.foo=../a foo -B . --config "$TESTHOOK"
357 pulling from $TESTTMP/a (glob)
372 pulling from $TESTTMP/a (glob)
358 no changes found
373 no changes found
359 divergent bookmark @ stored as @foo
374 divergent bookmark @ stored as @foo
360 importing bookmark X
375 importing bookmark X
361 test-hook-bookmark: @foo: 0d2164f0ce0d8f1d6f94351eba04b794909be66c -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
376 test-hook-bookmark: @foo: 0d2164f0ce0d8f1d6f94351eba04b794909be66c -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
362 test-hook-bookmark: X: 9b140be1080824d768c5a4691a564088eede71f9 -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
377 test-hook-bookmark: X: 9b140be1080824d768c5a4691a564088eede71f9 -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
363
378
364 reinstall state for further testing:
379 reinstall state for further testing:
365
380
366 $ hg book -fr 9b140be10808 X
381 $ hg book -fr 9b140be10808 X
367
382
368 revsets should not ignore divergent bookmarks
383 revsets should not ignore divergent bookmarks
369
384
370 $ hg bookmark -fr 1 Z
385 $ hg bookmark -fr 1 Z
371 $ hg log -r 'bookmark()' --template '{rev}:{node|short} {bookmarks}\n'
386 $ hg log -r 'bookmark()' --template '{rev}:{node|short} {bookmarks}\n'
372 0:4e3505fd9583 Y
387 0:4e3505fd9583 Y
373 1:9b140be10808 @ X Z foobar
388 1:9b140be10808 @ X Z foobar
374 2:0d2164f0ce0d @foo X@foo
389 2:0d2164f0ce0d @foo X@foo
375 $ hg log -r 'bookmark("X@foo")' --template '{rev}:{node|short} {bookmarks}\n'
390 $ hg log -r 'bookmark("X@foo")' --template '{rev}:{node|short} {bookmarks}\n'
376 2:0d2164f0ce0d @foo X@foo
391 2:0d2164f0ce0d @foo X@foo
377 $ hg log -r 'bookmark("re:X@foo")' --template '{rev}:{node|short} {bookmarks}\n'
392 $ hg log -r 'bookmark("re:X@foo")' --template '{rev}:{node|short} {bookmarks}\n'
378 2:0d2164f0ce0d @foo X@foo
393 2:0d2164f0ce0d @foo X@foo
379
394
380 update a remote bookmark from a non-head to a head
395 update a remote bookmark from a non-head to a head
381
396
382 $ hg up -q Y
397 $ hg up -q Y
383 $ echo c3 > f2
398 $ echo c3 > f2
384 $ hg ci -Am3
399 $ hg ci -Am3
385 adding f2
400 adding f2
386 created new head
401 created new head
387 $ hg push ../a --config "$TESTHOOK"
402 $ hg push ../a --config "$TESTHOOK"
388 pushing to ../a
403 pushing to ../a
389 searching for changes
404 searching for changes
390 adding changesets
405 adding changesets
391 adding manifests
406 adding manifests
392 adding file changes
407 adding file changes
393 added 1 changesets with 1 changes to 1 files (+1 heads)
408 added 1 changesets with 1 changes to 1 files (+1 heads)
394 test-hook-bookmark: Y: 4e3505fd95835d721066b76e75dbb8cc554d7f77 -> f6fc62dde3c0771e29704af56ba4d8af77abcc2f
409 test-hook-bookmark: Y: 4e3505fd95835d721066b76e75dbb8cc554d7f77 -> f6fc62dde3c0771e29704af56ba4d8af77abcc2f
395 updating bookmark Y
410 updating bookmark Y
396 $ hg -R ../a book
411 $ hg -R ../a book
397 @ 1:0d2164f0ce0d
412 @ 1:0d2164f0ce0d
398 * X 1:0d2164f0ce0d
413 * X 1:0d2164f0ce0d
399 Y 3:f6fc62dde3c0
414 Y 3:f6fc62dde3c0
400 Z 1:0d2164f0ce0d
415 Z 1:0d2164f0ce0d
401
416
402 update a bookmark in the middle of a client pulling changes
417 update a bookmark in the middle of a client pulling changes
403
418
404 $ cd ..
419 $ cd ..
405 $ hg clone -q a pull-race
420 $ hg clone -q a pull-race
406
421
407 We want to use http because it is stateless and therefore more susceptible to
422 We want to use http because it is stateless and therefore more susceptible to
408 race conditions
423 race conditions
409
424
410 $ hg serve -R pull-race -p $HGPORT -d --pid-file=pull-race.pid -E main-error.log
425 $ hg serve -R pull-race -p $HGPORT -d --pid-file=pull-race.pid -E main-error.log
411 $ cat pull-race.pid >> $DAEMON_PIDS
426 $ cat pull-race.pid >> $DAEMON_PIDS
412
427
413 $ cat <<EOF > $TESTTMP/out_makecommit.sh
428 $ cat <<EOF > $TESTTMP/out_makecommit.sh
414 > #!/bin/sh
429 > #!/bin/sh
415 > hg ci -Am5
430 > hg ci -Am5
416 > echo committed in pull-race
431 > echo committed in pull-race
417 > EOF
432 > EOF
418
433
419 $ hg clone -q http://localhost:$HGPORT/ pull-race2 --config "$TESTHOOK"
434 $ hg clone -q http://localhost:$HGPORT/ pull-race2 --config "$TESTHOOK"
420 test-hook-bookmark: @: -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
435 test-hook-bookmark: @: -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
421 test-hook-bookmark: X: -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
436 test-hook-bookmark: X: -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
422 test-hook-bookmark: Y: -> f6fc62dde3c0771e29704af56ba4d8af77abcc2f
437 test-hook-bookmark: Y: -> f6fc62dde3c0771e29704af56ba4d8af77abcc2f
423 test-hook-bookmark: Z: -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
438 test-hook-bookmark: Z: -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
424 $ cd pull-race
439 $ cd pull-race
425 $ hg up -q Y
440 $ hg up -q Y
426 $ echo c4 > f2
441 $ echo c4 > f2
427 $ hg ci -Am4
442 $ hg ci -Am4
428 $ echo c5 > f3
443 $ echo c5 > f3
429 $ cat <<EOF > .hg/hgrc
444 $ cat <<EOF > .hg/hgrc
430 > [hooks]
445 > [hooks]
431 > outgoing.makecommit = sh $TESTTMP/out_makecommit.sh
446 > outgoing.makecommit = sh $TESTTMP/out_makecommit.sh
432 > EOF
447 > EOF
433
448
434 (new config needs a server restart)
449 (new config needs a server restart)
435
450
436 $ cd ..
451 $ cd ..
437 $ killdaemons.py
452 $ killdaemons.py
438 $ hg serve -R pull-race -p $HGPORT -d --pid-file=pull-race.pid -E main-error.log
453 $ hg serve -R pull-race -p $HGPORT -d --pid-file=pull-race.pid -E main-error.log
439 $ cat pull-race.pid >> $DAEMON_PIDS
454 $ cat pull-race.pid >> $DAEMON_PIDS
440 $ cd pull-race2
455 $ cd pull-race2
441 $ hg -R $TESTTMP/pull-race book
456 $ hg -R $TESTTMP/pull-race book
442 @ 1:0d2164f0ce0d
457 @ 1:0d2164f0ce0d
443 X 1:0d2164f0ce0d
458 X 1:0d2164f0ce0d
444 * Y 4:b0a5eff05604
459 * Y 4:b0a5eff05604
445 Z 1:0d2164f0ce0d
460 Z 1:0d2164f0ce0d
446 $ hg pull
461 $ hg pull
447 pulling from http://localhost:$HGPORT/
462 pulling from http://localhost:$HGPORT/
448 searching for changes
463 searching for changes
449 adding changesets
464 adding changesets
450 adding manifests
465 adding manifests
451 adding file changes
466 adding file changes
452 added 1 changesets with 1 changes to 1 files
467 added 1 changesets with 1 changes to 1 files
453 updating bookmark Y
468 updating bookmark Y
454 new changesets b0a5eff05604
469 new changesets b0a5eff05604
455 (run 'hg update' to get a working copy)
470 (run 'hg update' to get a working copy)
456 $ hg book
471 $ hg book
457 * @ 1:0d2164f0ce0d
472 * @ 1:0d2164f0ce0d
458 X 1:0d2164f0ce0d
473 X 1:0d2164f0ce0d
459 Y 4:b0a5eff05604
474 Y 4:b0a5eff05604
460 Z 1:0d2164f0ce0d
475 Z 1:0d2164f0ce0d
461
476
462 Update a bookmark right after the initial lookup -B (issue4689)
477 Update a bookmark right after the initial lookup -B (issue4689)
463
478
464 $ echo c6 > ../pull-race/f3 # to be committed during the race
479 $ echo c6 > ../pull-race/f3 # to be committed during the race
465 $ cat <<EOF > $TESTTMP/listkeys_makecommit.sh
480 $ cat <<EOF > $TESTTMP/listkeys_makecommit.sh
466 > #!/bin/sh
481 > #!/bin/sh
467 > if hg st | grep -q M; then
482 > if hg st | grep -q M; then
468 > hg commit -m race
483 > hg commit -m race
469 > echo committed in pull-race
484 > echo committed in pull-race
470 > else
485 > else
471 > exit 0
486 > exit 0
472 > fi
487 > fi
473 > EOF
488 > EOF
474 $ cat <<EOF > ../pull-race/.hg/hgrc
489 $ cat <<EOF > ../pull-race/.hg/hgrc
475 > [hooks]
490 > [hooks]
476 > # If anything to commit, commit it right after the first key listing used
491 > # If anything to commit, commit it right after the first key listing used
477 > # during lookup. This makes the commit appear before the actual getbundle
492 > # during lookup. This makes the commit appear before the actual getbundle
478 > # call.
493 > # call.
479 > listkeys.makecommit= sh $TESTTMP/listkeys_makecommit.sh
494 > listkeys.makecommit= sh $TESTTMP/listkeys_makecommit.sh
480 > EOF
495 > EOF
481
496
482 (new config need server restart)
497 (new config need server restart)
483
498
484 $ killdaemons.py
499 $ killdaemons.py
485 $ hg serve -R ../pull-race -p $HGPORT -d --pid-file=../pull-race.pid -E main-error.log
500 $ hg serve -R ../pull-race -p $HGPORT -d --pid-file=../pull-race.pid -E main-error.log
486 $ cat ../pull-race.pid >> $DAEMON_PIDS
501 $ cat ../pull-race.pid >> $DAEMON_PIDS
487
502
488 $ hg -R $TESTTMP/pull-race book
503 $ hg -R $TESTTMP/pull-race book
489 @ 1:0d2164f0ce0d
504 @ 1:0d2164f0ce0d
490 X 1:0d2164f0ce0d
505 X 1:0d2164f0ce0d
491 * Y 5:35d1ef0a8d1b
506 * Y 5:35d1ef0a8d1b
492 Z 1:0d2164f0ce0d
507 Z 1:0d2164f0ce0d
493 $ hg update -r Y
508 $ hg update -r Y
494 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
509 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
495 (activating bookmark Y)
510 (activating bookmark Y)
496 $ hg pull -B .
511 $ hg pull -B .
497 pulling from http://localhost:$HGPORT/
512 pulling from http://localhost:$HGPORT/
498 searching for changes
513 searching for changes
499 adding changesets
514 adding changesets
500 adding manifests
515 adding manifests
501 adding file changes
516 adding file changes
502 added 1 changesets with 1 changes to 1 files
517 added 1 changesets with 1 changes to 1 files
503 updating bookmark Y
518 updating bookmark Y
504 new changesets 35d1ef0a8d1b
519 new changesets 35d1ef0a8d1b
505 (run 'hg update' to get a working copy)
520 (run 'hg update' to get a working copy)
506 $ hg book
521 $ hg book
507 @ 1:0d2164f0ce0d
522 @ 1:0d2164f0ce0d
508 X 1:0d2164f0ce0d
523 X 1:0d2164f0ce0d
509 * Y 5:35d1ef0a8d1b
524 * Y 5:35d1ef0a8d1b
510 Z 1:0d2164f0ce0d
525 Z 1:0d2164f0ce0d
511
526
512 (done with this section of the test)
527 (done with this section of the test)
513
528
514 $ killdaemons.py
529 $ killdaemons.py
515 $ cd ../b
530 $ cd ../b
516
531
517 diverging a remote bookmark fails
532 diverging a remote bookmark fails
518
533
519 $ hg up -q 4e3505fd9583
534 $ hg up -q 4e3505fd9583
520 $ echo c4 > f2
535 $ echo c4 > f2
521 $ hg ci -Am4
536 $ hg ci -Am4
522 adding f2
537 adding f2
523 created new head
538 created new head
524 $ echo c5 > f2
539 $ echo c5 > f2
525 $ hg ci -Am5
540 $ hg ci -Am5
526 $ hg log -G
541 $ hg log -G
527 @ 5:c922c0139ca0 5
542 @ 5:c922c0139ca0 5
528 |
543 |
529 o 4:4efff6d98829 4
544 o 4:4efff6d98829 4
530 |
545 |
531 | o 3:f6fc62dde3c0 3
546 | o 3:f6fc62dde3c0 3
532 |/
547 |/
533 | o 2:0d2164f0ce0d 1
548 | o 2:0d2164f0ce0d 1
534 |/
549 |/
535 | o 1:9b140be10808 2
550 | o 1:9b140be10808 2
536 |/
551 |/
537 o 0:4e3505fd9583 test
552 o 0:4e3505fd9583 test
538
553
539
554
540 $ hg book -f Y
555 $ hg book -f Y
541
556
542 $ cat <<EOF > ../a/.hg/hgrc
557 $ cat <<EOF > ../a/.hg/hgrc
543 > [web]
558 > [web]
544 > push_ssl = false
559 > push_ssl = false
545 > allow_push = *
560 > allow_push = *
546 > EOF
561 > EOF
547
562
548 $ hg serve -R ../a -p $HGPORT2 -d --pid-file=../hg2.pid
563 $ hg serve -R ../a -p $HGPORT2 -d --pid-file=../hg2.pid
549 $ cat ../hg2.pid >> $DAEMON_PIDS
564 $ cat ../hg2.pid >> $DAEMON_PIDS
550
565
551 $ hg push http://localhost:$HGPORT2/
566 $ hg push http://localhost:$HGPORT2/
552 pushing to http://localhost:$HGPORT2/
567 pushing to http://localhost:$HGPORT2/
553 searching for changes
568 searching for changes
554 abort: push creates new remote head c922c0139ca0 with bookmark 'Y'!
569 abort: push creates new remote head c922c0139ca0 with bookmark 'Y'!
555 (merge or see 'hg help push' for details about pushing new heads)
570 (merge or see 'hg help push' for details about pushing new heads)
556 [255]
571 [255]
557 $ hg -R ../a book
572 $ hg -R ../a book
558 @ 1:0d2164f0ce0d
573 @ 1:0d2164f0ce0d
559 * X 1:0d2164f0ce0d
574 * X 1:0d2164f0ce0d
560 Y 3:f6fc62dde3c0
575 Y 3:f6fc62dde3c0
561 Z 1:0d2164f0ce0d
576 Z 1:0d2164f0ce0d
562
577
563
578
564 Unrelated marker does not alter the decision
579 Unrelated marker does not alter the decision
565
580
566 $ hg debugobsolete aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
581 $ hg debugobsolete aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
567 $ hg push http://localhost:$HGPORT2/
582 $ hg push http://localhost:$HGPORT2/
568 pushing to http://localhost:$HGPORT2/
583 pushing to http://localhost:$HGPORT2/
569 searching for changes
584 searching for changes
570 abort: push creates new remote head c922c0139ca0 with bookmark 'Y'!
585 abort: push creates new remote head c922c0139ca0 with bookmark 'Y'!
571 (merge or see 'hg help push' for details about pushing new heads)
586 (merge or see 'hg help push' for details about pushing new heads)
572 [255]
587 [255]
573 $ hg -R ../a book
588 $ hg -R ../a book
574 @ 1:0d2164f0ce0d
589 @ 1:0d2164f0ce0d
575 * X 1:0d2164f0ce0d
590 * X 1:0d2164f0ce0d
576 Y 3:f6fc62dde3c0
591 Y 3:f6fc62dde3c0
577 Z 1:0d2164f0ce0d
592 Z 1:0d2164f0ce0d
578
593
579 Update to a successor works
594 Update to a successor works
580
595
581 $ hg id --debug -r 3
596 $ hg id --debug -r 3
582 f6fc62dde3c0771e29704af56ba4d8af77abcc2f
597 f6fc62dde3c0771e29704af56ba4d8af77abcc2f
583 $ hg id --debug -r 4
598 $ hg id --debug -r 4
584 4efff6d98829d9c824c621afd6e3f01865f5439f
599 4efff6d98829d9c824c621afd6e3f01865f5439f
585 $ hg id --debug -r 5
600 $ hg id --debug -r 5
586 c922c0139ca03858f655e4a2af4dd02796a63969 tip Y
601 c922c0139ca03858f655e4a2af4dd02796a63969 tip Y
587 $ hg debugobsolete f6fc62dde3c0771e29704af56ba4d8af77abcc2f cccccccccccccccccccccccccccccccccccccccc
602 $ hg debugobsolete f6fc62dde3c0771e29704af56ba4d8af77abcc2f cccccccccccccccccccccccccccccccccccccccc
588 obsoleted 1 changesets
603 obsoleted 1 changesets
589 $ hg debugobsolete cccccccccccccccccccccccccccccccccccccccc 4efff6d98829d9c824c621afd6e3f01865f5439f
604 $ hg debugobsolete cccccccccccccccccccccccccccccccccccccccc 4efff6d98829d9c824c621afd6e3f01865f5439f
590 $ hg push http://localhost:$HGPORT2/
605 $ hg push http://localhost:$HGPORT2/
591 pushing to http://localhost:$HGPORT2/
606 pushing to http://localhost:$HGPORT2/
592 searching for changes
607 searching for changes
593 remote: adding changesets
608 remote: adding changesets
594 remote: adding manifests
609 remote: adding manifests
595 remote: adding file changes
610 remote: adding file changes
596 remote: added 2 changesets with 2 changes to 1 files (+1 heads)
611 remote: added 2 changesets with 2 changes to 1 files (+1 heads)
597 remote: 2 new obsolescence markers
612 remote: 2 new obsolescence markers
598 remote: obsoleted 1 changesets
613 remote: obsoleted 1 changesets
599 updating bookmark Y
614 updating bookmark Y
600 $ hg -R ../a book
615 $ hg -R ../a book
601 @ 1:0d2164f0ce0d
616 @ 1:0d2164f0ce0d
602 * X 1:0d2164f0ce0d
617 * X 1:0d2164f0ce0d
603 Y 5:c922c0139ca0
618 Y 5:c922c0139ca0
604 Z 1:0d2164f0ce0d
619 Z 1:0d2164f0ce0d
605
620
606 hgweb
621 hgweb
607
622
608 $ cat <<EOF > .hg/hgrc
623 $ cat <<EOF > .hg/hgrc
609 > [web]
624 > [web]
610 > push_ssl = false
625 > push_ssl = false
611 > allow_push = *
626 > allow_push = *
612 > EOF
627 > EOF
613
628
614 $ hg serve -p $HGPORT -d --pid-file=../hg.pid -E errors.log
629 $ hg serve -p $HGPORT -d --pid-file=../hg.pid -E errors.log
615 $ cat ../hg.pid >> $DAEMON_PIDS
630 $ cat ../hg.pid >> $DAEMON_PIDS
616 $ cd ../a
631 $ cd ../a
617
632
618 $ hg debugpushkey http://localhost:$HGPORT/ namespaces
633 $ hg debugpushkey http://localhost:$HGPORT/ namespaces
619 bookmarks
634 bookmarks
620 namespaces
635 namespaces
621 obsolete
636 obsolete
622 phases
637 phases
623 $ hg debugpushkey http://localhost:$HGPORT/ bookmarks
638 $ hg debugpushkey http://localhost:$HGPORT/ bookmarks
624 @ 9b140be1080824d768c5a4691a564088eede71f9
639 @ 9b140be1080824d768c5a4691a564088eede71f9
625 X 9b140be1080824d768c5a4691a564088eede71f9
640 X 9b140be1080824d768c5a4691a564088eede71f9
626 Y c922c0139ca03858f655e4a2af4dd02796a63969
641 Y c922c0139ca03858f655e4a2af4dd02796a63969
627 Z 9b140be1080824d768c5a4691a564088eede71f9
642 Z 9b140be1080824d768c5a4691a564088eede71f9
628 foo 0000000000000000000000000000000000000000
643 foo 0000000000000000000000000000000000000000
629 foobar 9b140be1080824d768c5a4691a564088eede71f9
644 foobar 9b140be1080824d768c5a4691a564088eede71f9
630 $ hg out -B http://localhost:$HGPORT/
645 $ hg out -B http://localhost:$HGPORT/
631 comparing with http://localhost:$HGPORT/
646 comparing with http://localhost:$HGPORT/
632 searching for changed bookmarks
647 searching for changed bookmarks
633 @ 0d2164f0ce0d
648 @ 0d2164f0ce0d
634 X 0d2164f0ce0d
649 X 0d2164f0ce0d
635 Z 0d2164f0ce0d
650 Z 0d2164f0ce0d
636 foo
651 foo
637 foobar
652 foobar
638 $ hg push -B Z http://localhost:$HGPORT/
653 $ hg push -B Z http://localhost:$HGPORT/
639 pushing to http://localhost:$HGPORT/
654 pushing to http://localhost:$HGPORT/
640 searching for changes
655 searching for changes
641 no changes found
656 no changes found
642 updating bookmark Z
657 updating bookmark Z
643 [1]
658 [1]
644 $ hg book -d Z
659 $ hg book -d Z
645 $ hg in -B http://localhost:$HGPORT/
660 $ hg in -B http://localhost:$HGPORT/
646 comparing with http://localhost:$HGPORT/
661 comparing with http://localhost:$HGPORT/
647 searching for changed bookmarks
662 searching for changed bookmarks
648 @ 9b140be10808
663 @ 9b140be10808
649 X 9b140be10808
664 X 9b140be10808
650 Z 0d2164f0ce0d
665 Z 0d2164f0ce0d
651 foo 000000000000
666 foo 000000000000
652 foobar 9b140be10808
667 foobar 9b140be10808
653 $ hg pull -B Z http://localhost:$HGPORT/
668 $ hg pull -B Z http://localhost:$HGPORT/
654 pulling from http://localhost:$HGPORT/
669 pulling from http://localhost:$HGPORT/
655 no changes found
670 no changes found
656 divergent bookmark @ stored as @1
671 divergent bookmark @ stored as @1
657 divergent bookmark X stored as X@1
672 divergent bookmark X stored as X@1
658 adding remote bookmark Z
673 adding remote bookmark Z
659 adding remote bookmark foo
674 adding remote bookmark foo
660 adding remote bookmark foobar
675 adding remote bookmark foobar
661 $ hg clone http://localhost:$HGPORT/ cloned-bookmarks
676 $ hg clone http://localhost:$HGPORT/ cloned-bookmarks
662 requesting all changes
677 requesting all changes
663 adding changesets
678 adding changesets
664 adding manifests
679 adding manifests
665 adding file changes
680 adding file changes
666 added 5 changesets with 5 changes to 3 files (+2 heads)
681 added 5 changesets with 5 changes to 3 files (+2 heads)
667 2 new obsolescence markers
682 2 new obsolescence markers
668 new changesets 4e3505fd9583:c922c0139ca0
683 new changesets 4e3505fd9583:c922c0139ca0
669 updating to bookmark @
684 updating to bookmark @
670 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
685 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
671 $ hg -R cloned-bookmarks bookmarks
686 $ hg -R cloned-bookmarks bookmarks
672 * @ 1:9b140be10808
687 * @ 1:9b140be10808
673 X 1:9b140be10808
688 X 1:9b140be10808
674 Y 4:c922c0139ca0
689 Y 4:c922c0139ca0
675 Z 2:0d2164f0ce0d
690 Z 2:0d2164f0ce0d
676 foo -1:000000000000
691 foo -1:000000000000
677 foobar 1:9b140be10808
692 foobar 1:9b140be10808
678
693
679 $ cd ..
694 $ cd ..
680
695
681 Test to show result of bookmarks comparison
696 Test to show result of bookmarks comparison
682
697
683 $ mkdir bmcomparison
698 $ mkdir bmcomparison
684 $ cd bmcomparison
699 $ cd bmcomparison
685
700
686 $ hg init source
701 $ hg init source
687 $ hg -R source debugbuilddag '+2*2*3*4'
702 $ hg -R source debugbuilddag '+2*2*3*4'
688 $ hg -R source log -G --template '{rev}:{node|short}'
703 $ hg -R source log -G --template '{rev}:{node|short}'
689 o 4:e7bd5218ca15
704 o 4:e7bd5218ca15
690 |
705 |
691 | o 3:6100d3090acf
706 | o 3:6100d3090acf
692 |/
707 |/
693 | o 2:fa942426a6fd
708 | o 2:fa942426a6fd
694 |/
709 |/
695 | o 1:66f7d451a68b
710 | o 1:66f7d451a68b
696 |/
711 |/
697 o 0:1ea73414a91b
712 o 0:1ea73414a91b
698
713
699 $ hg -R source bookmarks -r 0 SAME
714 $ hg -R source bookmarks -r 0 SAME
700 $ hg -R source bookmarks -r 0 ADV_ON_REPO1
715 $ hg -R source bookmarks -r 0 ADV_ON_REPO1
701 $ hg -R source bookmarks -r 0 ADV_ON_REPO2
716 $ hg -R source bookmarks -r 0 ADV_ON_REPO2
702 $ hg -R source bookmarks -r 0 DIFF_ADV_ON_REPO1
717 $ hg -R source bookmarks -r 0 DIFF_ADV_ON_REPO1
703 $ hg -R source bookmarks -r 0 DIFF_ADV_ON_REPO2
718 $ hg -R source bookmarks -r 0 DIFF_ADV_ON_REPO2
704 $ hg -R source bookmarks -r 1 DIVERGED
719 $ hg -R source bookmarks -r 1 DIVERGED
705
720
706 $ hg clone -U source repo1
721 $ hg clone -U source repo1
707
722
708 (test that incoming/outgoing exit with 1, if there is no bookmark to
723 (test that incoming/outgoing exit with 1, if there is no bookmark to
709 be exchanged)
724 be exchanged)
710
725
711 $ hg -R repo1 incoming -B
726 $ hg -R repo1 incoming -B
712 comparing with $TESTTMP/bmcomparison/source (glob)
727 comparing with $TESTTMP/bmcomparison/source (glob)
713 searching for changed bookmarks
728 searching for changed bookmarks
714 no changed bookmarks found
729 no changed bookmarks found
715 [1]
730 [1]
716 $ hg -R repo1 outgoing -B
731 $ hg -R repo1 outgoing -B
717 comparing with $TESTTMP/bmcomparison/source (glob)
732 comparing with $TESTTMP/bmcomparison/source (glob)
718 searching for changed bookmarks
733 searching for changed bookmarks
719 no changed bookmarks found
734 no changed bookmarks found
720 [1]
735 [1]
721
736
722 $ hg -R repo1 bookmarks -f -r 1 ADD_ON_REPO1
737 $ hg -R repo1 bookmarks -f -r 1 ADD_ON_REPO1
723 $ hg -R repo1 bookmarks -f -r 2 ADV_ON_REPO1
738 $ hg -R repo1 bookmarks -f -r 2 ADV_ON_REPO1
724 $ hg -R repo1 bookmarks -f -r 3 DIFF_ADV_ON_REPO1
739 $ hg -R repo1 bookmarks -f -r 3 DIFF_ADV_ON_REPO1
725 $ hg -R repo1 bookmarks -f -r 3 DIFF_DIVERGED
740 $ hg -R repo1 bookmarks -f -r 3 DIFF_DIVERGED
726 $ hg -R repo1 -q --config extensions.mq= strip 4
741 $ hg -R repo1 -q --config extensions.mq= strip 4
727 $ hg -R repo1 log -G --template '{node|short} ({bookmarks})'
742 $ hg -R repo1 log -G --template '{node|short} ({bookmarks})'
728 o 6100d3090acf (DIFF_ADV_ON_REPO1 DIFF_DIVERGED)
743 o 6100d3090acf (DIFF_ADV_ON_REPO1 DIFF_DIVERGED)
729 |
744 |
730 | o fa942426a6fd (ADV_ON_REPO1)
745 | o fa942426a6fd (ADV_ON_REPO1)
731 |/
746 |/
732 | o 66f7d451a68b (ADD_ON_REPO1 DIVERGED)
747 | o 66f7d451a68b (ADD_ON_REPO1 DIVERGED)
733 |/
748 |/
734 o 1ea73414a91b (ADV_ON_REPO2 DIFF_ADV_ON_REPO2 SAME)
749 o 1ea73414a91b (ADV_ON_REPO2 DIFF_ADV_ON_REPO2 SAME)
735
750
736
751
737 $ hg clone -U source repo2
752 $ hg clone -U source repo2
738 $ hg -R repo2 bookmarks -f -r 1 ADD_ON_REPO2
753 $ hg -R repo2 bookmarks -f -r 1 ADD_ON_REPO2
739 $ hg -R repo2 bookmarks -f -r 1 ADV_ON_REPO2
754 $ hg -R repo2 bookmarks -f -r 1 ADV_ON_REPO2
740 $ hg -R repo2 bookmarks -f -r 2 DIVERGED
755 $ hg -R repo2 bookmarks -f -r 2 DIVERGED
741 $ hg -R repo2 bookmarks -f -r 4 DIFF_ADV_ON_REPO2
756 $ hg -R repo2 bookmarks -f -r 4 DIFF_ADV_ON_REPO2
742 $ hg -R repo2 bookmarks -f -r 4 DIFF_DIVERGED
757 $ hg -R repo2 bookmarks -f -r 4 DIFF_DIVERGED
743 $ hg -R repo2 -q --config extensions.mq= strip 3
758 $ hg -R repo2 -q --config extensions.mq= strip 3
744 $ hg -R repo2 log -G --template '{node|short} ({bookmarks})'
759 $ hg -R repo2 log -G --template '{node|short} ({bookmarks})'
745 o e7bd5218ca15 (DIFF_ADV_ON_REPO2 DIFF_DIVERGED)
760 o e7bd5218ca15 (DIFF_ADV_ON_REPO2 DIFF_DIVERGED)
746 |
761 |
747 | o fa942426a6fd (DIVERGED)
762 | o fa942426a6fd (DIVERGED)
748 |/
763 |/
749 | o 66f7d451a68b (ADD_ON_REPO2 ADV_ON_REPO2)
764 | o 66f7d451a68b (ADD_ON_REPO2 ADV_ON_REPO2)
750 |/
765 |/
751 o 1ea73414a91b (ADV_ON_REPO1 DIFF_ADV_ON_REPO1 SAME)
766 o 1ea73414a91b (ADV_ON_REPO1 DIFF_ADV_ON_REPO1 SAME)
752
767
753
768
754 (test that difference of bookmarks between repositories are fully shown)
769 (test that difference of bookmarks between repositories are fully shown)
755
770
756 $ hg -R repo1 incoming -B repo2 -v
771 $ hg -R repo1 incoming -B repo2 -v
757 comparing with repo2
772 comparing with repo2
758 searching for changed bookmarks
773 searching for changed bookmarks
759 ADD_ON_REPO2 66f7d451a68b added
774 ADD_ON_REPO2 66f7d451a68b added
760 ADV_ON_REPO2 66f7d451a68b advanced
775 ADV_ON_REPO2 66f7d451a68b advanced
761 DIFF_ADV_ON_REPO2 e7bd5218ca15 changed
776 DIFF_ADV_ON_REPO2 e7bd5218ca15 changed
762 DIFF_DIVERGED e7bd5218ca15 changed
777 DIFF_DIVERGED e7bd5218ca15 changed
763 DIVERGED fa942426a6fd diverged
778 DIVERGED fa942426a6fd diverged
764 $ hg -R repo1 outgoing -B repo2 -v
779 $ hg -R repo1 outgoing -B repo2 -v
765 comparing with repo2
780 comparing with repo2
766 searching for changed bookmarks
781 searching for changed bookmarks
767 ADD_ON_REPO1 66f7d451a68b added
782 ADD_ON_REPO1 66f7d451a68b added
768 ADD_ON_REPO2 deleted
783 ADD_ON_REPO2 deleted
769 ADV_ON_REPO1 fa942426a6fd advanced
784 ADV_ON_REPO1 fa942426a6fd advanced
770 DIFF_ADV_ON_REPO1 6100d3090acf advanced
785 DIFF_ADV_ON_REPO1 6100d3090acf advanced
771 DIFF_ADV_ON_REPO2 1ea73414a91b changed
786 DIFF_ADV_ON_REPO2 1ea73414a91b changed
772 DIFF_DIVERGED 6100d3090acf changed
787 DIFF_DIVERGED 6100d3090acf changed
773 DIVERGED 66f7d451a68b diverged
788 DIVERGED 66f7d451a68b diverged
774
789
775 $ hg -R repo2 incoming -B repo1 -v
790 $ hg -R repo2 incoming -B repo1 -v
776 comparing with repo1
791 comparing with repo1
777 searching for changed bookmarks
792 searching for changed bookmarks
778 ADD_ON_REPO1 66f7d451a68b added
793 ADD_ON_REPO1 66f7d451a68b added
779 ADV_ON_REPO1 fa942426a6fd advanced
794 ADV_ON_REPO1 fa942426a6fd advanced
780 DIFF_ADV_ON_REPO1 6100d3090acf changed
795 DIFF_ADV_ON_REPO1 6100d3090acf changed
781 DIFF_DIVERGED 6100d3090acf changed
796 DIFF_DIVERGED 6100d3090acf changed
782 DIVERGED 66f7d451a68b diverged
797 DIVERGED 66f7d451a68b diverged
783 $ hg -R repo2 outgoing -B repo1 -v
798 $ hg -R repo2 outgoing -B repo1 -v
784 comparing with repo1
799 comparing with repo1
785 searching for changed bookmarks
800 searching for changed bookmarks
786 ADD_ON_REPO1 deleted
801 ADD_ON_REPO1 deleted
787 ADD_ON_REPO2 66f7d451a68b added
802 ADD_ON_REPO2 66f7d451a68b added
788 ADV_ON_REPO2 66f7d451a68b advanced
803 ADV_ON_REPO2 66f7d451a68b advanced
789 DIFF_ADV_ON_REPO1 1ea73414a91b changed
804 DIFF_ADV_ON_REPO1 1ea73414a91b changed
790 DIFF_ADV_ON_REPO2 e7bd5218ca15 advanced
805 DIFF_ADV_ON_REPO2 e7bd5218ca15 advanced
791 DIFF_DIVERGED e7bd5218ca15 changed
806 DIFF_DIVERGED e7bd5218ca15 changed
792 DIVERGED fa942426a6fd diverged
807 DIVERGED fa942426a6fd diverged
793
808
794 $ cd ..
809 $ cd ..
795
810
796 Pushing a bookmark should only push the changes required by that
811 Pushing a bookmark should only push the changes required by that
797 bookmark, not all outgoing changes:
812 bookmark, not all outgoing changes:
798 $ hg clone http://localhost:$HGPORT/ addmarks
813 $ hg clone http://localhost:$HGPORT/ addmarks
799 requesting all changes
814 requesting all changes
800 adding changesets
815 adding changesets
801 adding manifests
816 adding manifests
802 adding file changes
817 adding file changes
803 added 5 changesets with 5 changes to 3 files (+2 heads)
818 added 5 changesets with 5 changes to 3 files (+2 heads)
804 2 new obsolescence markers
819 2 new obsolescence markers
805 new changesets 4e3505fd9583:c922c0139ca0
820 new changesets 4e3505fd9583:c922c0139ca0
806 updating to bookmark @
821 updating to bookmark @
807 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
822 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
808 $ cd addmarks
823 $ cd addmarks
809 $ echo foo > foo
824 $ echo foo > foo
810 $ hg add foo
825 $ hg add foo
811 $ hg commit -m 'add foo'
826 $ hg commit -m 'add foo'
812 $ echo bar > bar
827 $ echo bar > bar
813 $ hg add bar
828 $ hg add bar
814 $ hg commit -m 'add bar'
829 $ hg commit -m 'add bar'
815 $ hg co "tip^"
830 $ hg co "tip^"
816 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
831 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
817 (leaving bookmark @)
832 (leaving bookmark @)
818 $ hg book add-foo
833 $ hg book add-foo
819 $ hg book -r tip add-bar
834 $ hg book -r tip add-bar
820 Note: this push *must* push only a single changeset, as that's the point
835 Note: this push *must* push only a single changeset, as that's the point
821 of this test.
836 of this test.
822 $ hg push -B add-foo --traceback
837 $ hg push -B add-foo --traceback
823 pushing to http://localhost:$HGPORT/
838 pushing to http://localhost:$HGPORT/
824 searching for changes
839 searching for changes
825 remote: adding changesets
840 remote: adding changesets
826 remote: adding manifests
841 remote: adding manifests
827 remote: adding file changes
842 remote: adding file changes
828 remote: added 1 changesets with 1 changes to 1 files
843 remote: added 1 changesets with 1 changes to 1 files
829 exporting bookmark add-foo
844 exporting bookmark add-foo
830
845
831 pushing a new bookmark on a new head does not require -f if -B is specified
846 pushing a new bookmark on a new head does not require -f if -B is specified
832
847
833 $ hg up -q X
848 $ hg up -q X
834 $ hg book W
849 $ hg book W
835 $ echo c5 > f2
850 $ echo c5 > f2
836 $ hg ci -Am5
851 $ hg ci -Am5
837 created new head
852 created new head
838 $ hg push -B .
853 $ hg push -B .
839 pushing to http://localhost:$HGPORT/
854 pushing to http://localhost:$HGPORT/
840 searching for changes
855 searching for changes
841 remote: adding changesets
856 remote: adding changesets
842 remote: adding manifests
857 remote: adding manifests
843 remote: adding file changes
858 remote: adding file changes
844 remote: added 1 changesets with 1 changes to 1 files (+1 heads)
859 remote: added 1 changesets with 1 changes to 1 files (+1 heads)
845 exporting bookmark W
860 exporting bookmark W
846 $ hg -R ../b id -r W
861 $ hg -R ../b id -r W
847 cc978a373a53 tip W
862 cc978a373a53 tip W
848
863
849 pushing an existing but divergent bookmark with -B still requires -f
864 pushing an existing but divergent bookmark with -B still requires -f
850
865
851 $ hg clone -q . ../r
866 $ hg clone -q . ../r
852 $ hg up -q X
867 $ hg up -q X
853 $ echo 1 > f2
868 $ echo 1 > f2
854 $ hg ci -qAml
869 $ hg ci -qAml
855
870
856 $ cd ../r
871 $ cd ../r
857 $ hg up -q X
872 $ hg up -q X
858 $ echo 2 > f2
873 $ echo 2 > f2
859 $ hg ci -qAmr
874 $ hg ci -qAmr
860 $ hg push -B X
875 $ hg push -B X
861 pushing to $TESTTMP/addmarks (glob)
876 pushing to $TESTTMP/addmarks (glob)
862 searching for changes
877 searching for changes
863 remote has heads on branch 'default' that are not known locally: a2a606d9ff1b
878 remote has heads on branch 'default' that are not known locally: a2a606d9ff1b
864 abort: push creates new remote head 54694f811df9 with bookmark 'X'!
879 abort: push creates new remote head 54694f811df9 with bookmark 'X'!
865 (pull and merge or see 'hg help push' for details about pushing new heads)
880 (pull and merge or see 'hg help push' for details about pushing new heads)
866 [255]
881 [255]
867 $ cd ../addmarks
882 $ cd ../addmarks
868
883
869 Check summary output for incoming/outgoing bookmarks
884 Check summary output for incoming/outgoing bookmarks
870
885
871 $ hg bookmarks -d X
886 $ hg bookmarks -d X
872 $ hg bookmarks -d Y
887 $ hg bookmarks -d Y
873 $ hg summary --remote | grep '^remote:'
888 $ hg summary --remote | grep '^remote:'
874 remote: *, 2 incoming bookmarks, 1 outgoing bookmarks (glob)
889 remote: *, 2 incoming bookmarks, 1 outgoing bookmarks (glob)
875
890
876 $ cd ..
891 $ cd ..
877
892
878 pushing an unchanged bookmark should result in no changes
893 pushing an unchanged bookmark should result in no changes
879
894
880 $ hg init unchanged-a
895 $ hg init unchanged-a
881 $ hg init unchanged-b
896 $ hg init unchanged-b
882 $ cd unchanged-a
897 $ cd unchanged-a
883 $ echo initial > foo
898 $ echo initial > foo
884 $ hg commit -A -m initial
899 $ hg commit -A -m initial
885 adding foo
900 adding foo
886 $ hg bookmark @
901 $ hg bookmark @
887 $ hg push -B @ ../unchanged-b
902 $ hg push -B @ ../unchanged-b
888 pushing to ../unchanged-b
903 pushing to ../unchanged-b
889 searching for changes
904 searching for changes
890 adding changesets
905 adding changesets
891 adding manifests
906 adding manifests
892 adding file changes
907 adding file changes
893 added 1 changesets with 1 changes to 1 files
908 added 1 changesets with 1 changes to 1 files
894 exporting bookmark @
909 exporting bookmark @
895
910
896 $ hg push -B @ ../unchanged-b
911 $ hg push -B @ ../unchanged-b
897 pushing to ../unchanged-b
912 pushing to ../unchanged-b
898 searching for changes
913 searching for changes
899 no changes found
914 no changes found
900 [1]
915 [1]
901
916
902
917
903 Check hook preventing push (issue4455)
918 Check hook preventing push (issue4455)
904 ======================================
919 ======================================
905
920
906 $ hg bookmarks
921 $ hg bookmarks
907 * @ 0:55482a6fb4b1
922 * @ 0:55482a6fb4b1
908 $ hg log -G
923 $ hg log -G
909 @ 0:55482a6fb4b1 initial
924 @ 0:55482a6fb4b1 initial
910
925
911 $ hg init ../issue4455-dest
926 $ hg init ../issue4455-dest
912 $ hg push ../issue4455-dest # changesets only
927 $ hg push ../issue4455-dest # changesets only
913 pushing to ../issue4455-dest
928 pushing to ../issue4455-dest
914 searching for changes
929 searching for changes
915 adding changesets
930 adding changesets
916 adding manifests
931 adding manifests
917 adding file changes
932 adding file changes
918 added 1 changesets with 1 changes to 1 files
933 added 1 changesets with 1 changes to 1 files
919 $ cat >> .hg/hgrc << EOF
934 $ cat >> .hg/hgrc << EOF
920 > [paths]
935 > [paths]
921 > local=../issue4455-dest/
936 > local=../issue4455-dest/
922 > ssh=ssh://user@dummy/issue4455-dest
937 > ssh=ssh://user@dummy/issue4455-dest
923 > http=http://localhost:$HGPORT/
938 > http=http://localhost:$HGPORT/
924 > [ui]
939 > [ui]
925 > ssh=$PYTHON "$TESTDIR/dummyssh"
940 > ssh=$PYTHON "$TESTDIR/dummyssh"
926 > EOF
941 > EOF
927 $ cat >> ../issue4455-dest/.hg/hgrc << EOF
942 $ cat >> ../issue4455-dest/.hg/hgrc << EOF
928 > [hooks]
943 > [hooks]
929 > prepushkey=false
944 > prepushkey=false
930 > [web]
945 > [web]
931 > push_ssl = false
946 > push_ssl = false
932 > allow_push = *
947 > allow_push = *
933 > EOF
948 > EOF
934 $ killdaemons.py
949 $ killdaemons.py
935 $ hg serve -R ../issue4455-dest -p $HGPORT -d --pid-file=../issue4455.pid -E ../issue4455-error.log
950 $ hg serve -R ../issue4455-dest -p $HGPORT -d --pid-file=../issue4455.pid -E ../issue4455-error.log
936 $ cat ../issue4455.pid >> $DAEMON_PIDS
951 $ cat ../issue4455.pid >> $DAEMON_PIDS
937
952
938 Local push
953 Local push
939 ----------
954 ----------
940
955
941 $ hg push -B @ local
956 $ hg push -B @ local
942 pushing to $TESTTMP/issue4455-dest (glob)
957 pushing to $TESTTMP/issue4455-dest (glob)
943 searching for changes
958 searching for changes
944 no changes found
959 no changes found
945 pushkey-abort: prepushkey hook exited with status 1
960 pushkey-abort: prepushkey hook exited with status 1
946 abort: exporting bookmark @ failed!
961 abort: exporting bookmark @ failed!
947 [255]
962 [255]
948 $ hg -R ../issue4455-dest/ bookmarks
963 $ hg -R ../issue4455-dest/ bookmarks
949 no bookmarks set
964 no bookmarks set
950
965
951 Using ssh
966 Using ssh
952 ---------
967 ---------
953
968
954 $ hg push -B @ ssh # bundle2+
969 $ hg push -B @ ssh # bundle2+
955 pushing to ssh://user@dummy/issue4455-dest
970 pushing to ssh://user@dummy/issue4455-dest
956 searching for changes
971 searching for changes
957 no changes found
972 no changes found
958 remote: pushkey-abort: prepushkey hook exited with status 1
973 remote: pushkey-abort: prepushkey hook exited with status 1
959 abort: exporting bookmark @ failed!
974 abort: exporting bookmark @ failed!
960 [255]
975 [255]
961 $ hg -R ../issue4455-dest/ bookmarks
976 $ hg -R ../issue4455-dest/ bookmarks
962 no bookmarks set
977 no bookmarks set
963
978
964 $ hg push -B @ ssh --config devel.legacy.exchange=bundle1
979 $ hg push -B @ ssh --config devel.legacy.exchange=bundle1
965 pushing to ssh://user@dummy/issue4455-dest
980 pushing to ssh://user@dummy/issue4455-dest
966 searching for changes
981 searching for changes
967 no changes found
982 no changes found
968 remote: pushkey-abort: prepushkey hook exited with status 1
983 remote: pushkey-abort: prepushkey hook exited with status 1
969 exporting bookmark @ failed!
984 exporting bookmark @ failed!
970 [1]
985 [1]
971 $ hg -R ../issue4455-dest/ bookmarks
986 $ hg -R ../issue4455-dest/ bookmarks
972 no bookmarks set
987 no bookmarks set
973
988
974 Using http
989 Using http
975 ----------
990 ----------
976
991
977 $ hg push -B @ http # bundle2+
992 $ hg push -B @ http # bundle2+
978 pushing to http://localhost:$HGPORT/
993 pushing to http://localhost:$HGPORT/
979 searching for changes
994 searching for changes
980 no changes found
995 no changes found
981 remote: pushkey-abort: prepushkey hook exited with status 1
996 remote: pushkey-abort: prepushkey hook exited with status 1
982 abort: exporting bookmark @ failed!
997 abort: exporting bookmark @ failed!
983 [255]
998 [255]
984 $ hg -R ../issue4455-dest/ bookmarks
999 $ hg -R ../issue4455-dest/ bookmarks
985 no bookmarks set
1000 no bookmarks set
986
1001
987 $ hg push -B @ http --config devel.legacy.exchange=bundle1
1002 $ hg push -B @ http --config devel.legacy.exchange=bundle1
988 pushing to http://localhost:$HGPORT/
1003 pushing to http://localhost:$HGPORT/
989 searching for changes
1004 searching for changes
990 no changes found
1005 no changes found
991 remote: pushkey-abort: prepushkey hook exited with status 1
1006 remote: pushkey-abort: prepushkey hook exited with status 1
992 exporting bookmark @ failed!
1007 exporting bookmark @ failed!
993 [1]
1008 [1]
994 $ hg -R ../issue4455-dest/ bookmarks
1009 $ hg -R ../issue4455-dest/ bookmarks
995 no bookmarks set
1010 no bookmarks set
@@ -1,226 +1,227 b''
1 $ cat << EOF >> $HGRCPATH
1 $ cat << EOF >> $HGRCPATH
2 > [format]
2 > [format]
3 > usegeneraldelta=yes
3 > usegeneraldelta=yes
4 > EOF
4 > EOF
5
5
6 $ hg init debugrevlog
6 $ hg init debugrevlog
7 $ cd debugrevlog
7 $ cd debugrevlog
8 $ echo a > a
8 $ echo a > a
9 $ hg ci -Am adda
9 $ hg ci -Am adda
10 adding a
10 adding a
11 $ hg debugrevlog -m
11 $ hg debugrevlog -m
12 format : 1
12 format : 1
13 flags : inline, generaldelta
13 flags : inline, generaldelta
14
14
15 revisions : 1
15 revisions : 1
16 merges : 0 ( 0.00%)
16 merges : 0 ( 0.00%)
17 normal : 1 (100.00%)
17 normal : 1 (100.00%)
18 revisions : 1
18 revisions : 1
19 full : 1 (100.00%)
19 full : 1 (100.00%)
20 deltas : 0 ( 0.00%)
20 deltas : 0 ( 0.00%)
21 revision size : 44
21 revision size : 44
22 full : 44 (100.00%)
22 full : 44 (100.00%)
23 deltas : 0 ( 0.00%)
23 deltas : 0 ( 0.00%)
24
24
25 chunks : 1
25 chunks : 1
26 0x75 (u) : 1 (100.00%)
26 0x75 (u) : 1 (100.00%)
27 chunks size : 44
27 chunks size : 44
28 0x75 (u) : 44 (100.00%)
28 0x75 (u) : 44 (100.00%)
29
29
30 avg chain length : 0
30 avg chain length : 0
31 max chain length : 0
31 max chain length : 0
32 max chain reach : 44
32 max chain reach : 44
33 compression ratio : 0
33 compression ratio : 0
34
34
35 uncompressed data size (min/max/avg) : 43 / 43 / 43
35 uncompressed data size (min/max/avg) : 43 / 43 / 43
36 full revision size (min/max/avg) : 44 / 44 / 44
36 full revision size (min/max/avg) : 44 / 44 / 44
37 delta size (min/max/avg) : 0 / 0 / 0
37 delta size (min/max/avg) : 0 / 0 / 0
38
38
39 Test debugindex, with and without the --debug flag
39 Test debugindex, with and without the --debug flag
40 $ hg debugindex a
40 $ hg debugindex a
41 rev offset length ..... linkrev nodeid p1 p2 (re)
41 rev offset length ..... linkrev nodeid p1 p2 (re)
42 0 0 3 .... 0 b789fdd96dc2 000000000000 000000000000 (re)
42 0 0 3 .... 0 b789fdd96dc2 000000000000 000000000000 (re)
43 $ hg --debug debugindex a
43 $ hg --debug debugindex a
44 rev offset length ..... linkrev nodeid p1 p2 (re)
44 rev offset length ..... linkrev nodeid p1 p2 (re)
45 0 0 3 .... 0 b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 (re)
45 0 0 3 .... 0 b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 (re)
46 $ hg debugindex -f 1 a
46 $ hg debugindex -f 1 a
47 rev flag offset length size ..... link p1 p2 nodeid (re)
47 rev flag offset length size ..... link p1 p2 nodeid (re)
48 0 0000 0 3 2 .... 0 -1 -1 b789fdd96dc2 (re)
48 0 0000 0 3 2 .... 0 -1 -1 b789fdd96dc2 (re)
49 $ hg --debug debugindex -f 1 a
49 $ hg --debug debugindex -f 1 a
50 rev flag offset length size ..... link p1 p2 nodeid (re)
50 rev flag offset length size ..... link p1 p2 nodeid (re)
51 0 0000 0 3 2 .... 0 -1 -1 b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3 (re)
51 0 0000 0 3 2 .... 0 -1 -1 b789fdd96dc2f3bd229c1dd8eedf0fc60e2b68e3 (re)
52
52
53 debugdelta chain basic output
53 debugdelta chain basic output
54
54
55 $ hg debugdeltachain -m
55 $ hg debugdeltachain -m
56 rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio
56 rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio
57 0 1 1 -1 base 44 43 44 1.02326 44 0 0.00000
57 0 1 1 -1 base 44 43 44 1.02326 44 0 0.00000
58
58
59 $ hg debugdeltachain -m -T '{rev} {chainid} {chainlen}\n'
59 $ hg debugdeltachain -m -T '{rev} {chainid} {chainlen}\n'
60 0 1 1
60 0 1 1
61
61
62 $ hg debugdeltachain -m -Tjson
62 $ hg debugdeltachain -m -Tjson
63 [
63 [
64 {
64 {
65 "chainid": 1,
65 "chainid": 1,
66 "chainlen": 1,
66 "chainlen": 1,
67 "chainratio": 1.02325581395,
67 "chainratio": 1.02325581395,
68 "chainsize": 44,
68 "chainsize": 44,
69 "compsize": 44,
69 "compsize": 44,
70 "deltatype": "base",
70 "deltatype": "base",
71 "extradist": 0,
71 "extradist": 0,
72 "extraratio": 0.0,
72 "extraratio": 0.0,
73 "lindist": 44,
73 "lindist": 44,
74 "prevrev": -1,
74 "prevrev": -1,
75 "rev": 0,
75 "rev": 0,
76 "uncompsize": 43
76 "uncompsize": 43
77 }
77 }
78 ]
78 ]
79
79
80 debugdelta chain with sparse read enabled
80 debugdelta chain with sparse read enabled
81
81
82 $ cat >> $HGRCPATH <<EOF
82 $ cat >> $HGRCPATH <<EOF
83 > [experimental]
83 > [experimental]
84 > sparse-read = True
84 > sparse-read = True
85 > EOF
85 > EOF
86 $ hg debugdeltachain -m
86 $ hg debugdeltachain -m
87 rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio readsize largestblk rddensity
87 rev chain# chainlen prev delta size rawsize chainsize ratio lindist extradist extraratio readsize largestblk rddensity
88 0 1 1 -1 base 44 43 44 1.02326 44 0 0.00000 44 44 1.00000
88 0 1 1 -1 base 44 43 44 1.02326 44 0 0.00000 44 44 1.00000
89
89
90 $ hg debugdeltachain -m -T '{rev} {chainid} {chainlen} {readsize} {largestblock} {readdensity}\n'
90 $ hg debugdeltachain -m -T '{rev} {chainid} {chainlen} {readsize} {largestblock} {readdensity}\n'
91 0 1 1 44 44 1.0
91 0 1 1 44 44 1.0
92
92
93 $ hg debugdeltachain -m -Tjson
93 $ hg debugdeltachain -m -Tjson
94 [
94 [
95 {
95 {
96 "chainid": 1,
96 "chainid": 1,
97 "chainlen": 1,
97 "chainlen": 1,
98 "chainratio": 1.02325581395,
98 "chainratio": 1.02325581395,
99 "chainsize": 44,
99 "chainsize": 44,
100 "compsize": 44,
100 "compsize": 44,
101 "deltatype": "base",
101 "deltatype": "base",
102 "extradist": 0,
102 "extradist": 0,
103 "extraratio": 0.0,
103 "extraratio": 0.0,
104 "largestblock": 44,
104 "largestblock": 44,
105 "lindist": 44,
105 "lindist": 44,
106 "prevrev": -1,
106 "prevrev": -1,
107 "readdensity": 1.0,
107 "readdensity": 1.0,
108 "readsize": 44,
108 "readsize": 44,
109 "rev": 0,
109 "rev": 0,
110 "uncompsize": 43
110 "uncompsize": 43
111 }
111 }
112 ]
112 ]
113
113
114 Test max chain len
114 Test max chain len
115 $ cat >> $HGRCPATH << EOF
115 $ cat >> $HGRCPATH << EOF
116 > [format]
116 > [format]
117 > maxchainlen=4
117 > maxchainlen=4
118 > EOF
118 > EOF
119
119
120 $ printf "This test checks if maxchainlen config value is respected also it can serve as basic test for debugrevlog -d <file>.\n" >> a
120 $ printf "This test checks if maxchainlen config value is respected also it can serve as basic test for debugrevlog -d <file>.\n" >> a
121 $ hg ci -m a
121 $ hg ci -m a
122 $ printf "b\n" >> a
122 $ printf "b\n" >> a
123 $ hg ci -m a
123 $ hg ci -m a
124 $ printf "c\n" >> a
124 $ printf "c\n" >> a
125 $ hg ci -m a
125 $ hg ci -m a
126 $ printf "d\n" >> a
126 $ printf "d\n" >> a
127 $ hg ci -m a
127 $ hg ci -m a
128 $ printf "e\n" >> a
128 $ printf "e\n" >> a
129 $ hg ci -m a
129 $ hg ci -m a
130 $ printf "f\n" >> a
130 $ printf "f\n" >> a
131 $ hg ci -m a
131 $ hg ci -m a
132 $ printf 'g\n' >> a
132 $ printf 'g\n' >> a
133 $ hg ci -m a
133 $ hg ci -m a
134 $ printf 'h\n' >> a
134 $ printf 'h\n' >> a
135 $ hg ci -m a
135 $ hg ci -m a
136 $ hg debugrevlog -d a
136 $ hg debugrevlog -d a
137 # rev p1rev p2rev start end deltastart base p1 p2 rawsize totalsize compression heads chainlen
137 # rev p1rev p2rev start end deltastart base p1 p2 rawsize totalsize compression heads chainlen
138 0 -1 -1 0 ??? 0 0 0 0 ??? ???? ? 1 0 (glob)
138 0 -1 -1 0 ??? 0 0 0 0 ??? ???? ? 1 0 (glob)
139 1 0 -1 ??? ??? 0 0 0 0 ??? ???? ? 1 1 (glob)
139 1 0 -1 ??? ??? 0 0 0 0 ??? ???? ? 1 1 (glob)
140 2 1 -1 ??? ??? ??? ??? ??? 0 ??? ???? ? 1 2 (glob)
140 2 1 -1 ??? ??? ??? ??? ??? 0 ??? ???? ? 1 2 (glob)
141 3 2 -1 ??? ??? ??? ??? ??? 0 ??? ???? ? 1 3 (glob)
141 3 2 -1 ??? ??? ??? ??? ??? 0 ??? ???? ? 1 3 (glob)
142 4 3 -1 ??? ??? ??? ??? ??? 0 ??? ???? ? 1 4 (glob)
142 4 3 -1 ??? ??? ??? ??? ??? 0 ??? ???? ? 1 4 (glob)
143 5 4 -1 ??? ??? ??? ??? ??? 0 ??? ???? ? 1 0 (glob)
143 5 4 -1 ??? ??? ??? ??? ??? 0 ??? ???? ? 1 0 (glob)
144 6 5 -1 ??? ??? ??? ??? ??? 0 ??? ???? ? 1 1 (glob)
144 6 5 -1 ??? ??? ??? ??? ??? 0 ??? ???? ? 1 1 (glob)
145 7 6 -1 ??? ??? ??? ??? ??? 0 ??? ???? ? 1 2 (glob)
145 7 6 -1 ??? ??? ??? ??? ??? 0 ??? ???? ? 1 2 (glob)
146 8 7 -1 ??? ??? ??? ??? ??? 0 ??? ???? ? 1 3 (glob)
146 8 7 -1 ??? ??? ??? ??? ??? 0 ??? ???? ? 1 3 (glob)
147
147
148 Test WdirUnsupported exception
148 Test WdirUnsupported exception
149
149
150 $ hg debugdata -c ffffffffffffffffffffffffffffffffffffffff
150 $ hg debugdata -c ffffffffffffffffffffffffffffffffffffffff
151 abort: working directory revision cannot be specified
151 abort: working directory revision cannot be specified
152 [255]
152 [255]
153
153
154 Test cache warming command
154 Test cache warming command
155
155
156 $ rm -rf .hg/cache/
156 $ rm -rf .hg/cache/
157 $ hg debugupdatecaches --debug
157 $ hg debugupdatecaches --debug
158 updating the branch cache
158 updating the branch cache
159 $ ls -r .hg/cache/*
159 $ ls -r .hg/cache/*
160 .hg/cache/rbc-revs-v1
160 .hg/cache/rbc-revs-v1
161 .hg/cache/rbc-names-v1
161 .hg/cache/rbc-names-v1
162 .hg/cache/branch2-served
162 .hg/cache/branch2-served
163
163
164 $ cd ..
164 $ cd ..
165
165
166 Test internal debugstacktrace command
166 Test internal debugstacktrace command
167
167
168 $ cat > debugstacktrace.py << EOF
168 $ cat > debugstacktrace.py << EOF
169 > from __future__ import absolute_import
169 > from __future__ import absolute_import
170 > import sys
170 > import sys
171 > from mercurial import util
171 > from mercurial import util
172 > def f():
172 > def f():
173 > util.debugstacktrace(f=sys.stdout)
173 > util.debugstacktrace(f=sys.stdout)
174 > g()
174 > g()
175 > def g():
175 > def g():
176 > util.dst('hello from g\\n', skip=1)
176 > util.dst('hello from g\\n', skip=1)
177 > h()
177 > h()
178 > def h():
178 > def h():
179 > util.dst('hi ...\\nfrom h hidden in g', 1, depth=2)
179 > util.dst('hi ...\\nfrom h hidden in g', 1, depth=2)
180 > f()
180 > f()
181 > EOF
181 > EOF
182 $ $PYTHON debugstacktrace.py
182 $ $PYTHON debugstacktrace.py
183 stacktrace at:
183 stacktrace at:
184 debugstacktrace.py:12 in * (glob)
184 debugstacktrace.py:12 in * (glob)
185 debugstacktrace.py:5 in f
185 debugstacktrace.py:5 in f
186 hello from g at:
186 hello from g at:
187 debugstacktrace.py:12 in * (glob)
187 debugstacktrace.py:12 in * (glob)
188 debugstacktrace.py:6 in f
188 debugstacktrace.py:6 in f
189 hi ...
189 hi ...
190 from h hidden in g at:
190 from h hidden in g at:
191 debugstacktrace.py:6 in f
191 debugstacktrace.py:6 in f
192 debugstacktrace.py:9 in g
192 debugstacktrace.py:9 in g
193
193
194 Test debugcapabilities command:
194 Test debugcapabilities command:
195
195
196 $ hg debugcapabilities ./debugrevlog/
196 $ hg debugcapabilities ./debugrevlog/
197 Main capabilities:
197 Main capabilities:
198 branchmap
198 branchmap
199 $USUAL_BUNDLE2_CAPS$
199 $USUAL_BUNDLE2_CAPS$
200 getbundle
200 getbundle
201 known
201 known
202 lookup
202 lookup
203 pushkey
203 pushkey
204 unbundle
204 unbundle
205 Bundle2 capabilities:
205 Bundle2 capabilities:
206 HG20
206 HG20
207 bookmarks
207 changegroup
208 changegroup
208 01
209 01
209 02
210 02
210 digests
211 digests
211 md5
212 md5
212 sha1
213 sha1
213 sha512
214 sha512
214 error
215 error
215 abort
216 abort
216 unsupportedcontent
217 unsupportedcontent
217 pushraced
218 pushraced
218 pushkey
219 pushkey
219 hgtagsfnodes
220 hgtagsfnodes
220 listkeys
221 listkeys
221 phases
222 phases
222 heads
223 heads
223 pushkey
224 pushkey
224 remote-changegroup
225 remote-changegroup
225 http
226 http
226 https
227 https
@@ -1,910 +1,911 b''
1 #require killdaemons serve zstd
1 #require killdaemons serve zstd
2
2
3 Client version is embedded in HTTP request and is effectively dynamic. Pin the
3 Client version is embedded in HTTP request and is effectively dynamic. Pin the
4 version so behavior is deterministic.
4 version so behavior is deterministic.
5
5
6 $ cat > fakeversion.py << EOF
6 $ cat > fakeversion.py << EOF
7 > from mercurial import util
7 > from mercurial import util
8 > util.version = lambda: '4.2'
8 > util.version = lambda: '4.2'
9 > EOF
9 > EOF
10
10
11 $ cat >> $HGRCPATH << EOF
11 $ cat >> $HGRCPATH << EOF
12 > [extensions]
12 > [extensions]
13 > fakeversion = `pwd`/fakeversion.py
13 > fakeversion = `pwd`/fakeversion.py
14 > [devel]
14 > [devel]
15 > legacy.exchange = phases
15 > legacy.exchange = phases
16 > EOF
16 > EOF
17
17
18 $ hg init server0
18 $ hg init server0
19 $ cd server0
19 $ cd server0
20 $ touch foo
20 $ touch foo
21 $ hg -q commit -A -m initial
21 $ hg -q commit -A -m initial
22
22
23 Also disable compression because zstd is optional and causes output to vary
23 Also disable compression because zstd is optional and causes output to vary
24 and because debugging partial responses is hard when compression is involved
24 and because debugging partial responses is hard when compression is involved
25
25
26 $ cat > .hg/hgrc << EOF
26 $ cat > .hg/hgrc << EOF
27 > [extensions]
27 > [extensions]
28 > badserver = $TESTDIR/badserverext.py
28 > badserver = $TESTDIR/badserverext.py
29 > [server]
29 > [server]
30 > compressionengines = none
30 > compressionengines = none
31 > EOF
31 > EOF
32
32
33 Failure to accept() socket should result in connection related error message
33 Failure to accept() socket should result in connection related error message
34
34
35 $ hg serve --config badserver.closebeforeaccept=true -p $HGPORT -d --pid-file=hg.pid
35 $ hg serve --config badserver.closebeforeaccept=true -p $HGPORT -d --pid-file=hg.pid
36 $ cat hg.pid > $DAEMON_PIDS
36 $ cat hg.pid > $DAEMON_PIDS
37
37
38 $ hg clone http://localhost:$HGPORT/ clone
38 $ hg clone http://localhost:$HGPORT/ clone
39 abort: error: $ECONNRESET$
39 abort: error: $ECONNRESET$
40 [255]
40 [255]
41
41
42 (The server exits on its own, but there is a race between that and starting a new server.
42 (The server exits on its own, but there is a race between that and starting a new server.
43 So ensure the process is dead.)
43 So ensure the process is dead.)
44
44
45 $ killdaemons.py $DAEMON_PIDS
45 $ killdaemons.py $DAEMON_PIDS
46
46
47 Failure immediately after accept() should yield connection related error message
47 Failure immediately after accept() should yield connection related error message
48
48
49 $ hg serve --config badserver.closeafteraccept=true -p $HGPORT -d --pid-file=hg.pid
49 $ hg serve --config badserver.closeafteraccept=true -p $HGPORT -d --pid-file=hg.pid
50 $ cat hg.pid > $DAEMON_PIDS
50 $ cat hg.pid > $DAEMON_PIDS
51
51
52 TODO: this usually outputs good results, but sometimes emits abort:
52 TODO: this usually outputs good results, but sometimes emits abort:
53 error: '' on FreeBSD and OS X.
53 error: '' on FreeBSD and OS X.
54 What we ideally want are:
54 What we ideally want are:
55
55
56 abort: error: $ECONNRESET$
56 abort: error: $ECONNRESET$
57
57
58 The flakiness in this output was observable easily with
58 The flakiness in this output was observable easily with
59 --runs-per-test=20 on macOS 10.12 during the freeze for 4.2.
59 --runs-per-test=20 on macOS 10.12 during the freeze for 4.2.
60 $ hg clone http://localhost:$HGPORT/ clone
60 $ hg clone http://localhost:$HGPORT/ clone
61 abort: error: * (glob)
61 abort: error: * (glob)
62 [255]
62 [255]
63
63
64 $ killdaemons.py $DAEMON_PIDS
64 $ killdaemons.py $DAEMON_PIDS
65
65
66 Failure to read all bytes in initial HTTP request should yield connection related error message
66 Failure to read all bytes in initial HTTP request should yield connection related error message
67
67
68 $ hg serve --config badserver.closeafterrecvbytes=1 -p $HGPORT -d --pid-file=hg.pid -E error.log
68 $ hg serve --config badserver.closeafterrecvbytes=1 -p $HGPORT -d --pid-file=hg.pid -E error.log
69 $ cat hg.pid > $DAEMON_PIDS
69 $ cat hg.pid > $DAEMON_PIDS
70
70
71 $ hg clone http://localhost:$HGPORT/ clone
71 $ hg clone http://localhost:$HGPORT/ clone
72 abort: error: bad HTTP status line: ''
72 abort: error: bad HTTP status line: ''
73 [255]
73 [255]
74
74
75 $ killdaemons.py $DAEMON_PIDS
75 $ killdaemons.py $DAEMON_PIDS
76
76
77 $ cat error.log
77 $ cat error.log
78 readline(1 from 65537) -> (1) G
78 readline(1 from 65537) -> (1) G
79 read limit reached; closing socket
79 read limit reached; closing socket
80
80
81 $ rm -f error.log
81 $ rm -f error.log
82
82
83 Same failure, but server reads full HTTP request line
83 Same failure, but server reads full HTTP request line
84
84
85 $ hg serve --config badserver.closeafterrecvbytes=40 -p $HGPORT -d --pid-file=hg.pid -E error.log
85 $ hg serve --config badserver.closeafterrecvbytes=40 -p $HGPORT -d --pid-file=hg.pid -E error.log
86 $ cat hg.pid > $DAEMON_PIDS
86 $ cat hg.pid > $DAEMON_PIDS
87 $ hg clone http://localhost:$HGPORT/ clone
87 $ hg clone http://localhost:$HGPORT/ clone
88 abort: error: bad HTTP status line: ''
88 abort: error: bad HTTP status line: ''
89 [255]
89 [255]
90
90
91 $ killdaemons.py $DAEMON_PIDS
91 $ killdaemons.py $DAEMON_PIDS
92
92
93 $ cat error.log
93 $ cat error.log
94 readline(40 from 65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
94 readline(40 from 65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
95 readline(7 from -1) -> (7) Accept-
95 readline(7 from -1) -> (7) Accept-
96 read limit reached; closing socket
96 read limit reached; closing socket
97
97
98 $ rm -f error.log
98 $ rm -f error.log
99
99
100 Failure on subsequent HTTP request on the same socket (cmd?batch)
100 Failure on subsequent HTTP request on the same socket (cmd?batch)
101
101
102 $ hg serve --config badserver.closeafterrecvbytes=210 -p $HGPORT -d --pid-file=hg.pid -E error.log
102 $ hg serve --config badserver.closeafterrecvbytes=210 -p $HGPORT -d --pid-file=hg.pid -E error.log
103 $ cat hg.pid > $DAEMON_PIDS
103 $ cat hg.pid > $DAEMON_PIDS
104 $ hg clone http://localhost:$HGPORT/ clone
104 $ hg clone http://localhost:$HGPORT/ clone
105 abort: error: bad HTTP status line: ''
105 abort: error: bad HTTP status line: ''
106 [255]
106 [255]
107
107
108 $ killdaemons.py $DAEMON_PIDS
108 $ killdaemons.py $DAEMON_PIDS
109
109
110 $ cat error.log
110 $ cat error.log
111 readline(210 from 65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
111 readline(210 from 65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
112 readline(177 from -1) -> (27) Accept-Encoding: identity\r\n
112 readline(177 from -1) -> (27) Accept-Encoding: identity\r\n
113 readline(150 from -1) -> (35) accept: application/mercurial-0.1\r\n
113 readline(150 from -1) -> (35) accept: application/mercurial-0.1\r\n
114 readline(115 from -1) -> (2?) host: localhost:$HGPORT\r\n (glob)
114 readline(115 from -1) -> (2?) host: localhost:$HGPORT\r\n (glob)
115 readline(9? from -1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
115 readline(9? from -1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
116 readline(4? from -1) -> (2) \r\n (glob)
116 readline(4? from -1) -> (2) \r\n (glob)
117 write(36) -> HTTP/1.1 200 Script output follows\r\n
117 write(36) -> HTTP/1.1 200 Script output follows\r\n
118 write(23) -> Server: badhttpserver\r\n
118 write(23) -> Server: badhttpserver\r\n
119 write(37) -> Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
119 write(37) -> Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
120 write(41) -> Content-Type: application/mercurial-0.1\r\n
120 write(41) -> Content-Type: application/mercurial-0.1\r\n
121 write(21) -> Content-Length: 405\r\n
121 write(21) -> Content-Length: 417\r\n
122 write(2) -> \r\n
122 write(2) -> \r\n
123 write(405) -> lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 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 httpmediatype=0.1rx,0.1tx,0.2tx compression=none
123 write(417) -> lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 bundle2=HG20%0Abookmarks%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 httpmediatype=0.1rx,0.1tx,0.2tx compression=none
124 readline(4? from 65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n (glob)
124 readline(4? from 65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n (glob)
125 readline(1? from -1) -> (1?) Accept-Encoding* (glob)
125 readline(1? from -1) -> (1?) Accept-Encoding* (glob)
126 read limit reached; closing socket
126 read limit reached; closing socket
127 readline(210 from 65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n
127 readline(210 from 65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n
128 readline(184 from -1) -> (27) Accept-Encoding: identity\r\n
128 readline(184 from -1) -> (27) Accept-Encoding: identity\r\n
129 readline(157 from -1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
129 readline(157 from -1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
130 readline(128 from -1) -> (41) x-hgarg-1: cmds=heads+%3Bknown+nodes%3D\r\n
130 readline(128 from -1) -> (41) x-hgarg-1: cmds=heads+%3Bknown+nodes%3D\r\n
131 readline(87 from -1) -> (48) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$\r\n
131 readline(87 from -1) -> (48) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$\r\n
132 readline(39 from -1) -> (35) accept: application/mercurial-0.1\r\n
132 readline(39 from -1) -> (35) accept: application/mercurial-0.1\r\n
133 readline(4 from -1) -> (4) host
133 readline(4 from -1) -> (4) host
134 read limit reached; closing socket
134 read limit reached; closing socket
135
135
136 $ rm -f error.log
136 $ rm -f error.log
137
137
138 Failure to read getbundle HTTP request
138 Failure to read getbundle HTTP request
139
139
140 $ hg serve --config badserver.closeafterrecvbytes=292 -p $HGPORT -d --pid-file=hg.pid -E error.log
140 $ hg serve --config badserver.closeafterrecvbytes=292 -p $HGPORT -d --pid-file=hg.pid -E error.log
141 $ cat hg.pid > $DAEMON_PIDS
141 $ cat hg.pid > $DAEMON_PIDS
142 $ hg clone http://localhost:$HGPORT/ clone
142 $ hg clone http://localhost:$HGPORT/ clone
143 requesting all changes
143 requesting all changes
144 abort: error: bad HTTP status line: ''
144 abort: error: bad HTTP status line: ''
145 [255]
145 [255]
146
146
147 $ killdaemons.py $DAEMON_PIDS
147 $ killdaemons.py $DAEMON_PIDS
148
148
149 $ cat error.log
149 $ cat error.log
150 readline(292 from 65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
150 readline(292 from 65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
151 readline(259 from -1) -> (27) Accept-Encoding: identity\r\n
151 readline(259 from -1) -> (27) Accept-Encoding: identity\r\n
152 readline(232 from -1) -> (35) accept: application/mercurial-0.1\r\n
152 readline(232 from -1) -> (35) accept: application/mercurial-0.1\r\n
153 readline(197 from -1) -> (2?) host: localhost:$HGPORT\r\n (glob)
153 readline(197 from -1) -> (2?) host: localhost:$HGPORT\r\n (glob)
154 readline(17? from -1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
154 readline(17? from -1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
155 readline(12? from -1) -> (2) \r\n (glob)
155 readline(12? from -1) -> (2) \r\n (glob)
156 write(36) -> HTTP/1.1 200 Script output follows\r\n
156 write(36) -> HTTP/1.1 200 Script output follows\r\n
157 write(23) -> Server: badhttpserver\r\n
157 write(23) -> Server: badhttpserver\r\n
158 write(37) -> Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
158 write(37) -> Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
159 write(41) -> Content-Type: application/mercurial-0.1\r\n
159 write(41) -> Content-Type: application/mercurial-0.1\r\n
160 write(21) -> Content-Length: 405\r\n
160 readline(1 from -1) -> (1) x (?)
161 write(21) -> Content-Length: 417\r\n
161 write(2) -> \r\n
162 write(2) -> \r\n
162 write(405) -> lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 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 httpmediatype=0.1rx,0.1tx,0.2tx compression=none
163 write(417) -> lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 bundle2=HG20%0Abookmarks%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 httpmediatype=0.1rx,0.1tx,0.2tx compression=none
163 readline\(12[34] from 65537\) -> \(2[67]\) GET /\?cmd=batch HTTP/1.1\\r\\n (re)
164 readline\(12[34] from 65537\) -> \(2[67]\) GET /\?cmd=batch HTTP/1.1\\r\\n (re)
164 readline(9? from -1) -> (27) Accept-Encoding: identity\r\n (glob)
165 readline(9? from -1) -> (27) Accept-Encoding: identity\r\n (glob)
165 readline(7? from -1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n (glob)
166 readline(7? from -1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n (glob)
166 readline(4? from -1) -> (41) x-hgarg-1: cmds=heads+%3Bknown+nodes%3D\r\n (glob)
167 readline(4? from -1) -> (41) x-hgarg-1: cmds=heads+%3Bknown+nodes%3D\r\n (glob)
167 readline(1 from -1) -> (1) x (?)
168 readline(1 from -1) -> (1) x (?)
168 read limit reached; closing socket
169 read limit reached; closing socket
169 readline(292 from 65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n
170 readline(292 from 65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n
170 readline(266 from -1) -> (27) Accept-Encoding: identity\r\n
171 readline(266 from -1) -> (27) Accept-Encoding: identity\r\n
171 readline(239 from -1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
172 readline(239 from -1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
172 readline(210 from -1) -> (41) x-hgarg-1: cmds=heads+%3Bknown+nodes%3D\r\n
173 readline(210 from -1) -> (41) x-hgarg-1: cmds=heads+%3Bknown+nodes%3D\r\n
173 readline(169 from -1) -> (48) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$\r\n
174 readline(169 from -1) -> (48) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$\r\n
174 readline(121 from -1) -> (35) accept: application/mercurial-0.1\r\n
175 readline(121 from -1) -> (35) accept: application/mercurial-0.1\r\n
175 readline(86 from -1) -> (2?) host: localhost:$HGPORT\r\n (glob)
176 readline(86 from -1) -> (2?) host: localhost:$HGPORT\r\n (glob)
176 readline(6? from -1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
177 readline(6? from -1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
177 readline(1? from -1) -> (2) \r\n (glob)
178 readline(1? from -1) -> (2) \r\n (glob)
178 write(36) -> HTTP/1.1 200 Script output follows\r\n
179 write(36) -> HTTP/1.1 200 Script output follows\r\n
179 write(23) -> Server: badhttpserver\r\n
180 write(23) -> Server: badhttpserver\r\n
180 write(37) -> Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
181 write(37) -> Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
181 write(41) -> Content-Type: application/mercurial-0.1\r\n
182 write(41) -> Content-Type: application/mercurial-0.1\r\n
182 write(20) -> Content-Length: 42\r\n
183 write(20) -> Content-Length: 42\r\n
183 write(2) -> \r\n
184 write(2) -> \r\n
184 write(42) -> 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n;
185 write(42) -> 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n;
185 readline\(1[23] from 65537\) -> \(1[23]\) GET /\?cmd=ge.? (re)
186 readline\(1[23] from 65537\) -> \(1[23]\) GET /\?cmd=ge.? (re)
186 read limit reached; closing socket
187 read limit reached; closing socket
187 readline(292 from 65537) -> (30) GET /?cmd=getbundle HTTP/1.1\r\n
188 readline(292 from 65537) -> (30) GET /?cmd=getbundle HTTP/1.1\r\n
188 readline(262 from -1) -> (27) Accept-Encoding: identity\r\n
189 readline(262 from -1) -> (27) Accept-Encoding: identity\r\n
189 readline(235 from -1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
190 readline(235 from -1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
190 readline(206 from -1) -> (206) x-hgarg-1: bundlecaps=HG20%2Cbundle2%3DHG20%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Ali
191 readline(206 from -1) -> (206) x-hgarg-1: bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtag
191 read limit reached; closing socket
192 read limit reached; closing socket
192
193
193 $ rm -f error.log
194 $ rm -f error.log
194
195
195 Now do a variation using POST to send arguments
196 Now do a variation using POST to send arguments
196
197
197 $ hg serve --config experimental.httppostargs=true --config badserver.closeafterrecvbytes=315 -p $HGPORT -d --pid-file=hg.pid -E error.log
198 $ hg serve --config experimental.httppostargs=true --config badserver.closeafterrecvbytes=315 -p $HGPORT -d --pid-file=hg.pid -E error.log
198 $ cat hg.pid > $DAEMON_PIDS
199 $ cat hg.pid > $DAEMON_PIDS
199
200
200 $ hg clone http://localhost:$HGPORT/ clone
201 $ hg clone http://localhost:$HGPORT/ clone
201 abort: error: bad HTTP status line: ''
202 abort: error: bad HTTP status line: ''
202 [255]
203 [255]
203
204
204 $ killdaemons.py $DAEMON_PIDS
205 $ killdaemons.py $DAEMON_PIDS
205
206
206 $ cat error.log
207 $ cat error.log
207 readline(315 from 65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
208 readline(315 from 65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
208 readline(282 from -1) -> (27) Accept-Encoding: identity\r\n
209 readline(282 from -1) -> (27) Accept-Encoding: identity\r\n
209 readline(255 from -1) -> (35) accept: application/mercurial-0.1\r\n
210 readline(255 from -1) -> (35) accept: application/mercurial-0.1\r\n
210 readline(220 from -1) -> (2?) host: localhost:$HGPORT\r\n (glob)
211 readline(220 from -1) -> (2?) host: localhost:$HGPORT\r\n (glob)
211 readline(19? from -1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
212 readline(19? from -1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
212 readline(14? from -1) -> (2) \r\n (glob)
213 readline(14? from -1) -> (2) \r\n (glob)
213 write(36) -> HTTP/1.1 200 Script output follows\r\n
214 write(36) -> HTTP/1.1 200 Script output follows\r\n
214 write(23) -> Server: badhttpserver\r\n
215 write(23) -> Server: badhttpserver\r\n
215 write(37) -> Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
216 write(37) -> Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
216 write(41) -> Content-Type: application/mercurial-0.1\r\n
217 write(41) -> Content-Type: application/mercurial-0.1\r\n
217 write(21) -> Content-Length: 418\r\n
218 write(21) -> Content-Length: 430\r\n
218 write(2) -> \r\n
219 write(2) -> \r\n
219 write(418) -> lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 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 httppostargs httpmediatype=0.1rx,0.1tx,0.2tx compression=none
220 write(430) -> lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 bundle2=HG20%0Abookmarks%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 httppostargs httpmediatype=0.1rx,0.1tx,0.2tx compression=none
220 readline\(14[67] from 65537\) -> \(2[67]\) POST /\?cmd=batch HTTP/1.1\\r\\n (re)
221 readline\(14[67] from 65537\) -> \(2[67]\) POST /\?cmd=batch HTTP/1.1\\r\\n (re)
221 readline\(1(19|20) from -1\) -> \(27\) Accept-Encoding: identity\\r\\n (re)
222 readline\(1(19|20) from -1\) -> \(27\) Accept-Encoding: identity\\r\\n (re)
222 readline(9? from -1) -> (41) content-type: application/mercurial-0.1\r\n (glob)
223 readline(9? from -1) -> (41) content-type: application/mercurial-0.1\r\n (glob)
223 readline(5? from -1) -> (19) vary: X-HgProto-1\r\n (glob)
224 readline(5? from -1) -> (19) vary: X-HgProto-1\r\n (glob)
224 readline(3? from -1) -> (19) x-hgargs-post: 28\r\n (glob)
225 readline(3? from -1) -> (19) x-hgargs-post: 28\r\n (glob)
225 readline(1? from -1) -> (1?) x-hgproto-1: * (glob)
226 readline(1? from -1) -> (1?) x-hgproto-1: * (glob)
226 read limit reached; closing socket
227 read limit reached; closing socket
227 readline(315 from 65537) -> (27) POST /?cmd=batch HTTP/1.1\r\n
228 readline(315 from 65537) -> (27) POST /?cmd=batch HTTP/1.1\r\n
228 readline(288 from -1) -> (27) Accept-Encoding: identity\r\n
229 readline(288 from -1) -> (27) Accept-Encoding: identity\r\n
229 readline(261 from -1) -> (41) content-type: application/mercurial-0.1\r\n
230 readline(261 from -1) -> (41) content-type: application/mercurial-0.1\r\n
230 readline(220 from -1) -> (19) vary: X-HgProto-1\r\n
231 readline(220 from -1) -> (19) vary: X-HgProto-1\r\n
231 readline(201 from -1) -> (19) x-hgargs-post: 28\r\n
232 readline(201 from -1) -> (19) x-hgargs-post: 28\r\n
232 readline(182 from -1) -> (48) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$\r\n
233 readline(182 from -1) -> (48) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$\r\n
233 readline(134 from -1) -> (35) accept: application/mercurial-0.1\r\n
234 readline(134 from -1) -> (35) accept: application/mercurial-0.1\r\n
234 readline(99 from -1) -> (20) content-length: 28\r\n
235 readline(99 from -1) -> (20) content-length: 28\r\n
235 readline(79 from -1) -> (2?) host: localhost:$HGPORT\r\n (glob)
236 readline(79 from -1) -> (2?) host: localhost:$HGPORT\r\n (glob)
236 readline(5? from -1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
237 readline(5? from -1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n (glob)
237 readline(? from -1) -> (2) \r\n (glob)
238 readline(? from -1) -> (2) \r\n (glob)
238 read(? from 28) -> (?) cmds=* (glob)
239 read(? from 28) -> (?) cmds=* (glob)
239 read limit reached, closing socket
240 read limit reached, closing socket
240 write(36) -> HTTP/1.1 500 Internal Server Error\r\n
241 write(36) -> HTTP/1.1 500 Internal Server Error\r\n
241
242
242 $ rm -f error.log
243 $ rm -f error.log
243
244
244 Now move on to partial server responses
245 Now move on to partial server responses
245
246
246 Server sends a single character from the HTTP response line
247 Server sends a single character from the HTTP response line
247
248
248 $ hg serve --config badserver.closeaftersendbytes=1 -p $HGPORT -d --pid-file=hg.pid -E error.log
249 $ hg serve --config badserver.closeaftersendbytes=1 -p $HGPORT -d --pid-file=hg.pid -E error.log
249 $ cat hg.pid > $DAEMON_PIDS
250 $ cat hg.pid > $DAEMON_PIDS
250
251
251 $ hg clone http://localhost:$HGPORT/ clone
252 $ hg clone http://localhost:$HGPORT/ clone
252 abort: error: bad HTTP status line: H
253 abort: error: bad HTTP status line: H
253 [255]
254 [255]
254
255
255 $ killdaemons.py $DAEMON_PIDS
256 $ killdaemons.py $DAEMON_PIDS
256
257
257 $ cat error.log
258 $ cat error.log
258 readline(65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
259 readline(65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
259 readline(-1) -> (27) Accept-Encoding: identity\r\n
260 readline(-1) -> (27) Accept-Encoding: identity\r\n
260 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
261 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
261 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
262 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
262 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
263 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
263 readline(-1) -> (2) \r\n
264 readline(-1) -> (2) \r\n
264 write(1 from 36) -> (0) H
265 write(1 from 36) -> (0) H
265 write limit reached; closing socket
266 write limit reached; closing socket
266 write(36) -> HTTP/1.1 500 Internal Server Error\r\n
267 write(36) -> HTTP/1.1 500 Internal Server Error\r\n
267
268
268 $ rm -f error.log
269 $ rm -f error.log
269
270
270 Server sends an incomplete capabilities response body
271 Server sends an incomplete capabilities response body
271
272
272 $ hg serve --config badserver.closeaftersendbytes=180 -p $HGPORT -d --pid-file=hg.pid -E error.log
273 $ hg serve --config badserver.closeaftersendbytes=180 -p $HGPORT -d --pid-file=hg.pid -E error.log
273 $ cat hg.pid > $DAEMON_PIDS
274 $ cat hg.pid > $DAEMON_PIDS
274
275
275 $ hg clone http://localhost:$HGPORT/ clone
276 $ hg clone http://localhost:$HGPORT/ clone
276 abort: HTTP request error (incomplete response; expected 385 bytes got 20)
277 abort: HTTP request error (incomplete response; expected 397 bytes got 20)
277 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
278 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
278 [255]
279 [255]
279
280
280 $ killdaemons.py $DAEMON_PIDS
281 $ killdaemons.py $DAEMON_PIDS
281
282
282 $ cat error.log
283 $ cat error.log
283 readline(65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
284 readline(65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
284 readline(-1) -> (27) Accept-Encoding: identity\r\n
285 readline(-1) -> (27) Accept-Encoding: identity\r\n
285 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
286 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
286 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
287 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
287 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
288 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
288 readline(-1) -> (2) \r\n
289 readline(-1) -> (2) \r\n
289 write(36 from 36) -> (144) HTTP/1.1 200 Script output follows\r\n
290 write(36 from 36) -> (144) HTTP/1.1 200 Script output follows\r\n
290 write(23 from 23) -> (121) Server: badhttpserver\r\n
291 write(23 from 23) -> (121) Server: badhttpserver\r\n
291 write(37 from 37) -> (84) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
292 write(37 from 37) -> (84) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
292 write(41 from 41) -> (43) Content-Type: application/mercurial-0.1\r\n
293 write(41 from 41) -> (43) Content-Type: application/mercurial-0.1\r\n
293 write(21 from 21) -> (22) Content-Length: 405\r\n
294 write(21 from 21) -> (22) Content-Length: 417\r\n
294 write(2 from 2) -> (20) \r\n
295 write(2 from 2) -> (20) \r\n
295 write(20 from 405) -> (0) lookup changegroupsu
296 write(20 from 417) -> (0) lookup changegroupsu
296 write limit reached; closing socket
297 write limit reached; closing socket
297
298
298 $ rm -f error.log
299 $ rm -f error.log
299
300
300 Server sends incomplete headers for batch request
301 Server sends incomplete headers for batch request
301
302
302 $ hg serve --config badserver.closeaftersendbytes=695 -p $HGPORT -d --pid-file=hg.pid -E error.log
303 $ hg serve --config badserver.closeaftersendbytes=695 -p $HGPORT -d --pid-file=hg.pid -E error.log
303 $ cat hg.pid > $DAEMON_PIDS
304 $ cat hg.pid > $DAEMON_PIDS
304
305
305 TODO this output is horrible
306 TODO this output is horrible
306
307
307 $ hg clone http://localhost:$HGPORT/ clone
308 $ hg clone http://localhost:$HGPORT/ clone
308 abort: 'http://localhost:$HGPORT/' does not appear to be an hg repository:
309 abort: 'http://localhost:$HGPORT/' does not appear to be an hg repository:
309 ---%<--- (application/mercuria)
310 ---%<--- (applicat)
310
311
311 ---%<---
312 ---%<---
312 !
313 !
313 [255]
314 [255]
314
315
315 $ killdaemons.py $DAEMON_PIDS
316 $ killdaemons.py $DAEMON_PIDS
316
317
317 $ cat error.log
318 $ cat error.log
318 readline(65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
319 readline(65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
319 readline(-1) -> (27) Accept-Encoding: identity\r\n
320 readline(-1) -> (27) Accept-Encoding: identity\r\n
320 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
321 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
321 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
322 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
322 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
323 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
323 readline(-1) -> (2) \r\n
324 readline(-1) -> (2) \r\n
324 write(36 from 36) -> (659) HTTP/1.1 200 Script output follows\r\n
325 write(36 from 36) -> (659) HTTP/1.1 200 Script output follows\r\n
325 write(23 from 23) -> (636) Server: badhttpserver\r\n
326 write(23 from 23) -> (636) Server: badhttpserver\r\n
326 write(37 from 37) -> (599) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
327 write(37 from 37) -> (599) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
327 write(41 from 41) -> (558) Content-Type: application/mercurial-0.1\r\n
328 write(41 from 41) -> (558) Content-Type: application/mercurial-0.1\r\n
328 write(21 from 21) -> (537) Content-Length: 405\r\n
329 write(21 from 21) -> (537) Content-Length: 417\r\n
329 write(2 from 2) -> (535) \r\n
330 write(2 from 2) -> (535) \r\n
330 write(405 from 405) -> (130) lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 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 httpmediatype=0.1rx,0.1tx,0.2tx compression=none
331 write(417 from 417) -> (118) lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 bundle2=HG20%0Abookmarks%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 httpmediatype=0.1rx,0.1tx,0.2tx compression=none
331 readline(65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n
332 readline(65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n
332 readline(-1) -> (27) Accept-Encoding: identity\r\n
333 readline(-1) -> (27) Accept-Encoding: identity\r\n
333 readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
334 readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
334 readline(-1) -> (41) x-hgarg-1: cmds=heads+%3Bknown+nodes%3D\r\n
335 readline(-1) -> (41) x-hgarg-1: cmds=heads+%3Bknown+nodes%3D\r\n
335 readline(-1) -> (48) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$\r\n
336 readline(-1) -> (48) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$\r\n
336 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
337 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
337 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
338 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
338 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
339 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
339 readline(-1) -> (2) \r\n
340 readline(-1) -> (2) \r\n
340 write(36 from 36) -> (94) HTTP/1.1 200 Script output follows\r\n
341 write(36 from 36) -> (82) HTTP/1.1 200 Script output follows\r\n
341 write(23 from 23) -> (71) Server: badhttpserver\r\n
342 write(23 from 23) -> (59) Server: badhttpserver\r\n
342 write(37 from 37) -> (34) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
343 write(37 from 37) -> (22) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
343 write(34 from 41) -> (0) Content-Type: application/mercuria
344 write(22 from 41) -> (0) Content-Type: applicat
344 write limit reached; closing socket
345 write limit reached; closing socket
345 write(36) -> HTTP/1.1 500 Internal Server Error\r\n
346 write(36) -> HTTP/1.1 500 Internal Server Error\r\n
346
347
347 $ rm -f error.log
348 $ rm -f error.log
348
349
349 Server sends an incomplete HTTP response body to batch request
350 Server sends an incomplete HTTP response body to batch request
350
351
351 $ hg serve --config badserver.closeaftersendbytes=760 -p $HGPORT -d --pid-file=hg.pid -E error.log
352 $ hg serve --config badserver.closeaftersendbytes=760 -p $HGPORT -d --pid-file=hg.pid -E error.log
352 $ cat hg.pid > $DAEMON_PIDS
353 $ cat hg.pid > $DAEMON_PIDS
353
354
354 TODO client spews a stack due to uncaught ValueError in batch.results()
355 TODO client spews a stack due to uncaught ValueError in batch.results()
355 #if no-chg
356 #if no-chg
356 $ hg clone http://localhost:$HGPORT/ clone 2> /dev/null
357 $ hg clone http://localhost:$HGPORT/ clone 2> /dev/null
357 [1]
358 [1]
358 #else
359 #else
359 $ hg clone http://localhost:$HGPORT/ clone 2> /dev/null
360 $ hg clone http://localhost:$HGPORT/ clone 2> /dev/null
360 [255]
361 [255]
361 #endif
362 #endif
362
363
363 $ killdaemons.py $DAEMON_PIDS
364 $ killdaemons.py $DAEMON_PIDS
364
365
365 $ cat error.log
366 $ cat error.log
366 readline(65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
367 readline(65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
367 readline(-1) -> (27) Accept-Encoding: identity\r\n
368 readline(-1) -> (27) Accept-Encoding: identity\r\n
368 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
369 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
369 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
370 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
370 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
371 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
371 readline(-1) -> (2) \r\n
372 readline(-1) -> (2) \r\n
372 write(36 from 36) -> (724) HTTP/1.1 200 Script output follows\r\n
373 write(36 from 36) -> (724) HTTP/1.1 200 Script output follows\r\n
373 write(23 from 23) -> (701) Server: badhttpserver\r\n
374 write(23 from 23) -> (701) Server: badhttpserver\r\n
374 write(37 from 37) -> (664) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
375 write(37 from 37) -> (664) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
375 write(41 from 41) -> (623) Content-Type: application/mercurial-0.1\r\n
376 write(41 from 41) -> (623) Content-Type: application/mercurial-0.1\r\n
376 write(21 from 21) -> (602) Content-Length: 405\r\n
377 write(21 from 21) -> (602) Content-Length: 417\r\n
377 write(2 from 2) -> (600) \r\n
378 write(2 from 2) -> (600) \r\n
378 write(405 from 405) -> (195) lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 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 httpmediatype=0.1rx,0.1tx,0.2tx compression=none
379 write(417 from 417) -> (183) lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 bundle2=HG20%0Abookmarks%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 httpmediatype=0.1rx,0.1tx,0.2tx compression=none
379 readline(65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n
380 readline(65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n
380 readline(-1) -> (27) Accept-Encoding: identity\r\n
381 readline(-1) -> (27) Accept-Encoding: identity\r\n
381 readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
382 readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
382 readline(-1) -> (41) x-hgarg-1: cmds=heads+%3Bknown+nodes%3D\r\n
383 readline(-1) -> (41) x-hgarg-1: cmds=heads+%3Bknown+nodes%3D\r\n
383 readline(-1) -> (48) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$\r\n
384 readline(-1) -> (48) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$\r\n
384 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
385 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
385 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
386 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
386 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
387 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
387 readline(-1) -> (2) \r\n
388 readline(-1) -> (2) \r\n
388 write(36 from 36) -> (159) HTTP/1.1 200 Script output follows\r\n
389 write(36 from 36) -> (147) HTTP/1.1 200 Script output follows\r\n
389 write(23 from 23) -> (136) Server: badhttpserver\r\n
390 write(23 from 23) -> (124) Server: badhttpserver\r\n
390 write(37 from 37) -> (99) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
391 write(37 from 37) -> (87) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
391 write(41 from 41) -> (58) Content-Type: application/mercurial-0.1\r\n
392 write(41 from 41) -> (46) Content-Type: application/mercurial-0.1\r\n
392 write(20 from 20) -> (38) Content-Length: 42\r\n
393 write(20 from 20) -> (26) Content-Length: 42\r\n
393 write(2 from 2) -> (36) \r\n
394 write(2 from 2) -> (24) \r\n
394 write(36 from 42) -> (0) 96ee1d7354c4ad7372047672c36a1f561e3a
395 write(24 from 42) -> (0) 96ee1d7354c4ad7372047672
395 write limit reached; closing socket
396 write limit reached; closing socket
396
397
397 $ rm -f error.log
398 $ rm -f error.log
398
399
399 Server sends incomplete headers for getbundle response
400 Server sends incomplete headers for getbundle response
400
401
401 $ hg serve --config badserver.closeaftersendbytes=895 -p $HGPORT -d --pid-file=hg.pid -E error.log
402 $ hg serve --config badserver.closeaftersendbytes=895 -p $HGPORT -d --pid-file=hg.pid -E error.log
402 $ cat hg.pid > $DAEMON_PIDS
403 $ cat hg.pid > $DAEMON_PIDS
403
404
404 TODO this output is terrible
405 TODO this output is terrible
405
406
406 $ hg clone http://localhost:$HGPORT/ clone
407 $ hg clone http://localhost:$HGPORT/ clone
407 requesting all changes
408 requesting all changes
408 abort: 'http://localhost:$HGPORT/' does not appear to be an hg repository:
409 abort: 'http://localhost:$HGPORT/' does not appear to be an hg repository:
409 ---%<--- (application/mercuri)
410 ---%<--- (applica)
410
411
411 ---%<---
412 ---%<---
412 !
413 !
413 [255]
414 [255]
414
415
415 $ killdaemons.py $DAEMON_PIDS
416 $ killdaemons.py $DAEMON_PIDS
416
417
417 $ cat error.log
418 $ cat error.log
418 readline(65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
419 readline(65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
419 readline(-1) -> (27) Accept-Encoding: identity\r\n
420 readline(-1) -> (27) Accept-Encoding: identity\r\n
420 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
421 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
421 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
422 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
422 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
423 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
423 readline(-1) -> (2) \r\n
424 readline(-1) -> (2) \r\n
424 write(36 from 36) -> (859) HTTP/1.1 200 Script output follows\r\n
425 write(36 from 36) -> (859) HTTP/1.1 200 Script output follows\r\n
425 write(23 from 23) -> (836) Server: badhttpserver\r\n
426 write(23 from 23) -> (836) Server: badhttpserver\r\n
426 write(37 from 37) -> (799) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
427 write(37 from 37) -> (799) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
427 write(41 from 41) -> (758) Content-Type: application/mercurial-0.1\r\n
428 write(41 from 41) -> (758) Content-Type: application/mercurial-0.1\r\n
428 write(21 from 21) -> (737) Content-Length: 405\r\n
429 write(21 from 21) -> (737) Content-Length: 417\r\n
429 write(2 from 2) -> (735) \r\n
430 write(2 from 2) -> (735) \r\n
430 write(405 from 405) -> (330) lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 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 httpmediatype=0.1rx,0.1tx,0.2tx compression=none
431 write(417 from 417) -> (318) lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 bundle2=HG20%0Abookmarks%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 httpmediatype=0.1rx,0.1tx,0.2tx compression=none
431 readline(65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n
432 readline(65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n
432 readline(-1) -> (27) Accept-Encoding: identity\r\n
433 readline(-1) -> (27) Accept-Encoding: identity\r\n
433 readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
434 readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
434 readline(-1) -> (41) x-hgarg-1: cmds=heads+%3Bknown+nodes%3D\r\n
435 readline(-1) -> (41) x-hgarg-1: cmds=heads+%3Bknown+nodes%3D\r\n
435 readline(-1) -> (48) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$\r\n
436 readline(-1) -> (48) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$\r\n
436 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
437 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
437 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
438 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
438 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
439 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
439 readline(-1) -> (2) \r\n
440 readline(-1) -> (2) \r\n
440 write(36 from 36) -> (294) HTTP/1.1 200 Script output follows\r\n
441 write(36 from 36) -> (282) HTTP/1.1 200 Script output follows\r\n
441 write(23 from 23) -> (271) Server: badhttpserver\r\n
442 write(23 from 23) -> (259) Server: badhttpserver\r\n
442 write(37 from 37) -> (234) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
443 write(37 from 37) -> (222) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
443 write(41 from 41) -> (193) Content-Type: application/mercurial-0.1\r\n
444 write(41 from 41) -> (181) Content-Type: application/mercurial-0.1\r\n
444 write(20 from 20) -> (173) Content-Length: 42\r\n
445 write(20 from 20) -> (161) Content-Length: 42\r\n
445 write(2 from 2) -> (171) \r\n
446 write(2 from 2) -> (159) \r\n
446 write(42 from 42) -> (129) 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n;
447 write(42 from 42) -> (117) 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n;
447 readline(65537) -> (30) GET /?cmd=getbundle HTTP/1.1\r\n
448 readline(65537) -> (30) GET /?cmd=getbundle HTTP/1.1\r\n
448 readline(-1) -> (27) Accept-Encoding: identity\r\n
449 readline(-1) -> (27) Accept-Encoding: identity\r\n
449 readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
450 readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
450 readline(-1) -> (396) 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=96ee1d7354c4ad7372047672c36a1f561e3a6a4c&listkeys=phases%2Cbookmarks\r\n
451 readline(-1) -> (410) x-hgarg-1: bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps&cg=1&common=0000000000000000000000000000000000000000&heads=96ee1d7354c4ad7372047672c36a1f561e3a6a4c&listkeys=phases%2Cbookmarks\r\n
451 readline(-1) -> (48) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$\r\n
452 readline(-1) -> (48) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$\r\n
452 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
453 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
453 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
454 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
454 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
455 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
455 readline(-1) -> (2) \r\n
456 readline(-1) -> (2) \r\n
456 write(36 from 36) -> (93) HTTP/1.1 200 Script output follows\r\n
457 write(36 from 36) -> (81) HTTP/1.1 200 Script output follows\r\n
457 write(23 from 23) -> (70) Server: badhttpserver\r\n
458 write(23 from 23) -> (58) Server: badhttpserver\r\n
458 write(37 from 37) -> (33) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
459 write(37 from 37) -> (21) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
459 write(33 from 41) -> (0) Content-Type: application/mercuri
460 write(21 from 41) -> (0) Content-Type: applica
460 write limit reached; closing socket
461 write limit reached; closing socket
461 write(36) -> HTTP/1.1 500 Internal Server Error\r\n
462 write(36) -> HTTP/1.1 500 Internal Server Error\r\n
462
463
463 $ rm -f error.log
464 $ rm -f error.log
464
465
465 Server sends empty HTTP body for getbundle
466 Server sends empty HTTP body for getbundle
466
467
467 $ hg serve --config badserver.closeaftersendbytes=933 -p $HGPORT -d --pid-file=hg.pid -E error.log
468 $ hg serve --config badserver.closeaftersendbytes=945 -p $HGPORT -d --pid-file=hg.pid -E error.log
468 $ cat hg.pid > $DAEMON_PIDS
469 $ cat hg.pid > $DAEMON_PIDS
469
470
470 $ hg clone http://localhost:$HGPORT/ clone
471 $ hg clone http://localhost:$HGPORT/ clone
471 requesting all changes
472 requesting all changes
472 abort: HTTP request error (incomplete response)
473 abort: HTTP request error (incomplete response)
473 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
474 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
474 [255]
475 [255]
475
476
476 $ killdaemons.py $DAEMON_PIDS
477 $ killdaemons.py $DAEMON_PIDS
477
478
478 $ cat error.log
479 $ cat error.log
479 readline(65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
480 readline(65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
480 readline(-1) -> (27) Accept-Encoding: identity\r\n
481 readline(-1) -> (27) Accept-Encoding: identity\r\n
481 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
482 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
482 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
483 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
483 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
484 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
484 readline(-1) -> (2) \r\n
485 readline(-1) -> (2) \r\n
485 write(36 from 36) -> (897) HTTP/1.1 200 Script output follows\r\n
486 write(36 from 36) -> (909) HTTP/1.1 200 Script output follows\r\n
486 write(23 from 23) -> (874) Server: badhttpserver\r\n
487 write(23 from 23) -> (886) Server: badhttpserver\r\n
487 write(37 from 37) -> (837) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
488 write(37 from 37) -> (849) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
488 write(41 from 41) -> (796) Content-Type: application/mercurial-0.1\r\n
489 write(41 from 41) -> (808) Content-Type: application/mercurial-0.1\r\n
489 write(21 from 21) -> (775) Content-Length: 405\r\n
490 write(21 from 21) -> (787) Content-Length: 417\r\n
490 write(2 from 2) -> (773) \r\n
491 write(2 from 2) -> (785) \r\n
491 write(405 from 405) -> (368) lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 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 httpmediatype=0.1rx,0.1tx,0.2tx compression=none
492 write(417 from 417) -> (368) lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 bundle2=HG20%0Abookmarks%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 httpmediatype=0.1rx,0.1tx,0.2tx compression=none
492 readline(65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n
493 readline(65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n
493 readline(-1) -> (27) Accept-Encoding: identity\r\n
494 readline(-1) -> (27) Accept-Encoding: identity\r\n
494 readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
495 readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
495 readline(-1) -> (41) x-hgarg-1: cmds=heads+%3Bknown+nodes%3D\r\n
496 readline(-1) -> (41) x-hgarg-1: cmds=heads+%3Bknown+nodes%3D\r\n
496 readline(-1) -> (48) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$\r\n
497 readline(-1) -> (48) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$\r\n
497 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
498 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
498 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
499 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
499 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
500 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
500 readline(-1) -> (2) \r\n
501 readline(-1) -> (2) \r\n
501 write(36 from 36) -> (332) HTTP/1.1 200 Script output follows\r\n
502 write(36 from 36) -> (332) HTTP/1.1 200 Script output follows\r\n
502 write(23 from 23) -> (309) Server: badhttpserver\r\n
503 write(23 from 23) -> (309) Server: badhttpserver\r\n
503 write(37 from 37) -> (272) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
504 write(37 from 37) -> (272) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
504 write(41 from 41) -> (231) Content-Type: application/mercurial-0.1\r\n
505 write(41 from 41) -> (231) Content-Type: application/mercurial-0.1\r\n
505 write(20 from 20) -> (211) Content-Length: 42\r\n
506 write(20 from 20) -> (211) Content-Length: 42\r\n
506 write(2 from 2) -> (209) \r\n
507 write(2 from 2) -> (209) \r\n
507 write(42 from 42) -> (167) 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n;
508 write(42 from 42) -> (167) 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n;
508 readline(65537) -> (30) GET /?cmd=getbundle HTTP/1.1\r\n
509 readline(65537) -> (30) GET /?cmd=getbundle HTTP/1.1\r\n
509 readline(-1) -> (27) Accept-Encoding: identity\r\n
510 readline(-1) -> (27) Accept-Encoding: identity\r\n
510 readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
511 readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
511 readline(-1) -> (396) 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=96ee1d7354c4ad7372047672c36a1f561e3a6a4c&listkeys=phases%2Cbookmarks\r\n
512 readline(-1) -> (410) x-hgarg-1: bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps&cg=1&common=0000000000000000000000000000000000000000&heads=96ee1d7354c4ad7372047672c36a1f561e3a6a4c&listkeys=phases%2Cbookmarks\r\n
512 readline(-1) -> (48) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$\r\n
513 readline(-1) -> (48) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$\r\n
513 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
514 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
514 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
515 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
515 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
516 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
516 readline(-1) -> (2) \r\n
517 readline(-1) -> (2) \r\n
517 write(36 from 36) -> (131) HTTP/1.1 200 Script output follows\r\n
518 write(36 from 36) -> (131) HTTP/1.1 200 Script output follows\r\n
518 write(23 from 23) -> (108) Server: badhttpserver\r\n
519 write(23 from 23) -> (108) Server: badhttpserver\r\n
519 write(37 from 37) -> (71) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
520 write(37 from 37) -> (71) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
520 write(41 from 41) -> (30) Content-Type: application/mercurial-0.2\r\n
521 write(41 from 41) -> (30) Content-Type: application/mercurial-0.2\r\n
521 write(28 from 28) -> (2) Transfer-Encoding: chunked\r\n
522 write(28 from 28) -> (2) Transfer-Encoding: chunked\r\n
522 write(2 from 2) -> (0) \r\n
523 write(2 from 2) -> (0) \r\n
523 write limit reached; closing socket
524 write limit reached; closing socket
524 write(36) -> HTTP/1.1 500 Internal Server Error\r\n
525 write(36) -> HTTP/1.1 500 Internal Server Error\r\n
525
526
526 $ rm -f error.log
527 $ rm -f error.log
527
528
528 Server sends partial compression string
529 Server sends partial compression string
529
530
530 $ hg serve --config badserver.closeaftersendbytes=945 -p $HGPORT -d --pid-file=hg.pid -E error.log
531 $ hg serve --config badserver.closeaftersendbytes=957 -p $HGPORT -d --pid-file=hg.pid -E error.log
531 $ cat hg.pid > $DAEMON_PIDS
532 $ cat hg.pid > $DAEMON_PIDS
532
533
533 $ hg clone http://localhost:$HGPORT/ clone
534 $ hg clone http://localhost:$HGPORT/ clone
534 requesting all changes
535 requesting all changes
535 abort: HTTP request error (incomplete response; expected 1 bytes got 3)
536 abort: HTTP request error (incomplete response; expected 1 bytes got 3)
536 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
537 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
537 [255]
538 [255]
538
539
539 $ killdaemons.py $DAEMON_PIDS
540 $ killdaemons.py $DAEMON_PIDS
540
541
541 $ cat error.log
542 $ cat error.log
542 readline(65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
543 readline(65537) -> (33) GET /?cmd=capabilities HTTP/1.1\r\n
543 readline(-1) -> (27) Accept-Encoding: identity\r\n
544 readline(-1) -> (27) Accept-Encoding: identity\r\n
544 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
545 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
545 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
546 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
546 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
547 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
547 readline(-1) -> (2) \r\n
548 readline(-1) -> (2) \r\n
548 write(36 from 36) -> (909) HTTP/1.1 200 Script output follows\r\n
549 write(36 from 36) -> (921) HTTP/1.1 200 Script output follows\r\n
549 write(23 from 23) -> (886) Server: badhttpserver\r\n
550 write(23 from 23) -> (898) Server: badhttpserver\r\n
550 write(37 from 37) -> (849) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
551 write(37 from 37) -> (861) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
551 write(41 from 41) -> (808) Content-Type: application/mercurial-0.1\r\n
552 write(41 from 41) -> (820) Content-Type: application/mercurial-0.1\r\n
552 write(21 from 21) -> (787) Content-Length: 405\r\n
553 write(21 from 21) -> (799) Content-Length: 417\r\n
553 write(2 from 2) -> (785) \r\n
554 write(2 from 2) -> (797) \r\n
554 write(405 from 405) -> (380) lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 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 httpmediatype=0.1rx,0.1tx,0.2tx compression=none
555 write(417 from 417) -> (380) lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 bundle2=HG20%0Abookmarks%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 httpmediatype=0.1rx,0.1tx,0.2tx compression=none
555 readline(65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n
556 readline(65537) -> (26) GET /?cmd=batch HTTP/1.1\r\n
556 readline(-1) -> (27) Accept-Encoding: identity\r\n
557 readline(-1) -> (27) Accept-Encoding: identity\r\n
557 readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
558 readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
558 readline(-1) -> (41) x-hgarg-1: cmds=heads+%3Bknown+nodes%3D\r\n
559 readline(-1) -> (41) x-hgarg-1: cmds=heads+%3Bknown+nodes%3D\r\n
559 readline(-1) -> (48) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$\r\n
560 readline(-1) -> (48) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$\r\n
560 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
561 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
561 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
562 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
562 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
563 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
563 readline(-1) -> (2) \r\n
564 readline(-1) -> (2) \r\n
564 write(36 from 36) -> (344) HTTP/1.1 200 Script output follows\r\n
565 write(36 from 36) -> (344) HTTP/1.1 200 Script output follows\r\n
565 write(23 from 23) -> (321) Server: badhttpserver\r\n
566 write(23 from 23) -> (321) Server: badhttpserver\r\n
566 write(37 from 37) -> (284) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
567 write(37 from 37) -> (284) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
567 write(41 from 41) -> (243) Content-Type: application/mercurial-0.1\r\n
568 write(41 from 41) -> (243) Content-Type: application/mercurial-0.1\r\n
568 write(20 from 20) -> (223) Content-Length: 42\r\n
569 write(20 from 20) -> (223) Content-Length: 42\r\n
569 write(2 from 2) -> (221) \r\n
570 write(2 from 2) -> (221) \r\n
570 write(42 from 42) -> (179) 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n;
571 write(42 from 42) -> (179) 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n;
571 readline(65537) -> (30) GET /?cmd=getbundle HTTP/1.1\r\n
572 readline(65537) -> (30) GET /?cmd=getbundle HTTP/1.1\r\n
572 readline(-1) -> (27) Accept-Encoding: identity\r\n
573 readline(-1) -> (27) Accept-Encoding: identity\r\n
573 readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
574 readline(-1) -> (29) vary: X-HgArg-1,X-HgProto-1\r\n
574 readline(-1) -> (396) 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=96ee1d7354c4ad7372047672c36a1f561e3a6a4c&listkeys=phases%2Cbookmarks\r\n
575 readline(-1) -> (410) x-hgarg-1: bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps&cg=1&common=0000000000000000000000000000000000000000&heads=96ee1d7354c4ad7372047672c36a1f561e3a6a4c&listkeys=phases%2Cbookmarks\r\n
575 readline(-1) -> (48) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$\r\n
576 readline(-1) -> (48) x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$\r\n
576 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
577 readline(-1) -> (35) accept: application/mercurial-0.1\r\n
577 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
578 readline(-1) -> (2?) host: localhost:$HGPORT\r\n (glob)
578 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
579 readline(-1) -> (49) user-agent: mercurial/proto-1.0 (Mercurial 4.2)\r\n
579 readline(-1) -> (2) \r\n
580 readline(-1) -> (2) \r\n
580 write(36 from 36) -> (143) HTTP/1.1 200 Script output follows\r\n
581 write(36 from 36) -> (143) HTTP/1.1 200 Script output follows\r\n
581 write(23 from 23) -> (120) Server: badhttpserver\r\n
582 write(23 from 23) -> (120) Server: badhttpserver\r\n
582 write(37 from 37) -> (83) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
583 write(37 from 37) -> (83) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
583 write(41 from 41) -> (42) Content-Type: application/mercurial-0.2\r\n
584 write(41 from 41) -> (42) Content-Type: application/mercurial-0.2\r\n
584 write(28 from 28) -> (14) Transfer-Encoding: chunked\r\n
585 write(28 from 28) -> (14) Transfer-Encoding: chunked\r\n
585 write(2 from 2) -> (12) \r\n
586 write(2 from 2) -> (12) \r\n
586 write(6 from 6) -> (6) 1\\r\\n\x04\\r\\n (esc)
587 write(6 from 6) -> (6) 1\\r\\n\x04\\r\\n (esc)
587 write(6 from 9) -> (0) 4\r\nnon
588 write(6 from 9) -> (0) 4\r\nnon
588 write limit reached; closing socket
589 write limit reached; closing socket
589 write(27) -> 15\r\nInternal Server Error\r\n
590 write(27) -> 15\r\nInternal Server Error\r\n
590
591
591 $ rm -f error.log
592 $ rm -f error.log
592
593
593 Server sends partial bundle2 header magic
594 Server sends partial bundle2 header magic
594
595
595 $ hg serve --config badserver.closeaftersendbytes=954 -p $HGPORT -d --pid-file=hg.pid -E error.log
596 $ hg serve --config badserver.closeaftersendbytes=966 -p $HGPORT -d --pid-file=hg.pid -E error.log
596 $ cat hg.pid > $DAEMON_PIDS
597 $ cat hg.pid > $DAEMON_PIDS
597
598
598 $ hg clone http://localhost:$HGPORT/ clone
599 $ hg clone http://localhost:$HGPORT/ clone
599 requesting all changes
600 requesting all changes
600 abort: HTTP request error (incomplete response; expected 1 bytes got 3)
601 abort: HTTP request error (incomplete response; expected 1 bytes got 3)
601 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
602 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
602 [255]
603 [255]
603
604
604 $ killdaemons.py $DAEMON_PIDS
605 $ killdaemons.py $DAEMON_PIDS
605
606
606 $ tail -7 error.log
607 $ tail -7 error.log
607 write(28 from 28) -> (23) Transfer-Encoding: chunked\r\n
608 write(28 from 28) -> (23) Transfer-Encoding: chunked\r\n
608 write(2 from 2) -> (21) \r\n
609 write(2 from 2) -> (21) \r\n
609 write(6 from 6) -> (15) 1\\r\\n\x04\\r\\n (esc)
610 write(6 from 6) -> (15) 1\\r\\n\x04\\r\\n (esc)
610 write(9 from 9) -> (6) 4\r\nnone\r\n
611 write(9 from 9) -> (6) 4\r\nnone\r\n
611 write(6 from 9) -> (0) 4\r\nHG2
612 write(6 from 9) -> (0) 4\r\nHG2
612 write limit reached; closing socket
613 write limit reached; closing socket
613 write(27) -> 15\r\nInternal Server Error\r\n
614 write(27) -> 15\r\nInternal Server Error\r\n
614
615
615 $ rm -f error.log
616 $ rm -f error.log
616
617
617 Server sends incomplete bundle2 stream params length
618 Server sends incomplete bundle2 stream params length
618
619
619 $ hg serve --config badserver.closeaftersendbytes=963 -p $HGPORT -d --pid-file=hg.pid -E error.log
620 $ hg serve --config badserver.closeaftersendbytes=975 -p $HGPORT -d --pid-file=hg.pid -E error.log
620 $ cat hg.pid > $DAEMON_PIDS
621 $ cat hg.pid > $DAEMON_PIDS
621
622
622 $ hg clone http://localhost:$HGPORT/ clone
623 $ hg clone http://localhost:$HGPORT/ clone
623 requesting all changes
624 requesting all changes
624 abort: HTTP request error (incomplete response; expected 1 bytes got 3)
625 abort: HTTP request error (incomplete response; expected 1 bytes got 3)
625 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
626 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
626 [255]
627 [255]
627
628
628 $ killdaemons.py $DAEMON_PIDS
629 $ killdaemons.py $DAEMON_PIDS
629
630
630 $ tail -8 error.log
631 $ tail -8 error.log
631 write(28 from 28) -> (32) Transfer-Encoding: chunked\r\n
632 write(28 from 28) -> (32) Transfer-Encoding: chunked\r\n
632 write(2 from 2) -> (30) \r\n
633 write(2 from 2) -> (30) \r\n
633 write(6 from 6) -> (24) 1\\r\\n\x04\\r\\n (esc)
634 write(6 from 6) -> (24) 1\\r\\n\x04\\r\\n (esc)
634 write(9 from 9) -> (15) 4\r\nnone\r\n
635 write(9 from 9) -> (15) 4\r\nnone\r\n
635 write(9 from 9) -> (6) 4\r\nHG20\r\n
636 write(9 from 9) -> (6) 4\r\nHG20\r\n
636 write(6 from 9) -> (0) 4\\r\\n\x00\x00\x00 (esc)
637 write(6 from 9) -> (0) 4\\r\\n\x00\x00\x00 (esc)
637 write limit reached; closing socket
638 write limit reached; closing socket
638 write(27) -> 15\r\nInternal Server Error\r\n
639 write(27) -> 15\r\nInternal Server Error\r\n
639
640
640 $ rm -f error.log
641 $ rm -f error.log
641
642
642 Servers stops after bundle2 stream params header
643 Servers stops after bundle2 stream params header
643
644
644 $ hg serve --config badserver.closeaftersendbytes=966 -p $HGPORT -d --pid-file=hg.pid -E error.log
645 $ hg serve --config badserver.closeaftersendbytes=978 -p $HGPORT -d --pid-file=hg.pid -E error.log
645 $ cat hg.pid > $DAEMON_PIDS
646 $ cat hg.pid > $DAEMON_PIDS
646
647
647 $ hg clone http://localhost:$HGPORT/ clone
648 $ hg clone http://localhost:$HGPORT/ clone
648 requesting all changes
649 requesting all changes
649 abort: HTTP request error (incomplete response)
650 abort: HTTP request error (incomplete response)
650 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
651 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
651 [255]
652 [255]
652
653
653 $ killdaemons.py $DAEMON_PIDS
654 $ killdaemons.py $DAEMON_PIDS
654
655
655 $ tail -8 error.log
656 $ tail -8 error.log
656 write(28 from 28) -> (35) Transfer-Encoding: chunked\r\n
657 write(28 from 28) -> (35) Transfer-Encoding: chunked\r\n
657 write(2 from 2) -> (33) \r\n
658 write(2 from 2) -> (33) \r\n
658 write(6 from 6) -> (27) 1\\r\\n\x04\\r\\n (esc)
659 write(6 from 6) -> (27) 1\\r\\n\x04\\r\\n (esc)
659 write(9 from 9) -> (18) 4\r\nnone\r\n
660 write(9 from 9) -> (18) 4\r\nnone\r\n
660 write(9 from 9) -> (9) 4\r\nHG20\r\n
661 write(9 from 9) -> (9) 4\r\nHG20\r\n
661 write(9 from 9) -> (0) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
662 write(9 from 9) -> (0) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
662 write limit reached; closing socket
663 write limit reached; closing socket
663 write(27) -> 15\r\nInternal Server Error\r\n
664 write(27) -> 15\r\nInternal Server Error\r\n
664
665
665 $ rm -f error.log
666 $ rm -f error.log
666
667
667 Server stops sending after bundle2 part header length
668 Server stops sending after bundle2 part header length
668
669
669 $ hg serve --config badserver.closeaftersendbytes=975 -p $HGPORT -d --pid-file=hg.pid -E error.log
670 $ hg serve --config badserver.closeaftersendbytes=987 -p $HGPORT -d --pid-file=hg.pid -E error.log
670 $ cat hg.pid > $DAEMON_PIDS
671 $ cat hg.pid > $DAEMON_PIDS
671
672
672 $ hg clone http://localhost:$HGPORT/ clone
673 $ hg clone http://localhost:$HGPORT/ clone
673 requesting all changes
674 requesting all changes
674 abort: HTTP request error (incomplete response)
675 abort: HTTP request error (incomplete response)
675 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
676 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
676 [255]
677 [255]
677
678
678 $ killdaemons.py $DAEMON_PIDS
679 $ killdaemons.py $DAEMON_PIDS
679
680
680 $ tail -9 error.log
681 $ tail -9 error.log
681 write(28 from 28) -> (44) Transfer-Encoding: chunked\r\n
682 write(28 from 28) -> (44) Transfer-Encoding: chunked\r\n
682 write(2 from 2) -> (42) \r\n
683 write(2 from 2) -> (42) \r\n
683 write(6 from 6) -> (36) 1\\r\\n\x04\\r\\n (esc)
684 write(6 from 6) -> (36) 1\\r\\n\x04\\r\\n (esc)
684 write(9 from 9) -> (27) 4\r\nnone\r\n
685 write(9 from 9) -> (27) 4\r\nnone\r\n
685 write(9 from 9) -> (18) 4\r\nHG20\r\n
686 write(9 from 9) -> (18) 4\r\nHG20\r\n
686 write(9 from 9) -> (9) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
687 write(9 from 9) -> (9) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
687 write(9 from 9) -> (0) 4\\r\\n\x00\x00\x00)\\r\\n (esc)
688 write(9 from 9) -> (0) 4\\r\\n\x00\x00\x00)\\r\\n (esc)
688 write limit reached; closing socket
689 write limit reached; closing socket
689 write(27) -> 15\r\nInternal Server Error\r\n
690 write(27) -> 15\r\nInternal Server Error\r\n
690
691
691 $ rm -f error.log
692 $ rm -f error.log
692
693
693 Server stops sending after bundle2 part header
694 Server stops sending after bundle2 part header
694
695
695 $ hg serve --config badserver.closeaftersendbytes=1022 -p $HGPORT -d --pid-file=hg.pid -E error.log
696 $ hg serve --config badserver.closeaftersendbytes=1034 -p $HGPORT -d --pid-file=hg.pid -E error.log
696 $ cat hg.pid > $DAEMON_PIDS
697 $ cat hg.pid > $DAEMON_PIDS
697
698
698 $ hg clone http://localhost:$HGPORT/ clone
699 $ hg clone http://localhost:$HGPORT/ clone
699 requesting all changes
700 requesting all changes
700 adding changesets
701 adding changesets
701 transaction abort!
702 transaction abort!
702 rollback completed
703 rollback completed
703 abort: HTTP request error (incomplete response)
704 abort: HTTP request error (incomplete response)
704 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
705 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
705 [255]
706 [255]
706
707
707 $ killdaemons.py $DAEMON_PIDS
708 $ killdaemons.py $DAEMON_PIDS
708
709
709 $ tail -10 error.log
710 $ tail -10 error.log
710 write(28 from 28) -> (91) Transfer-Encoding: chunked\r\n
711 write(28 from 28) -> (91) Transfer-Encoding: chunked\r\n
711 write(2 from 2) -> (89) \r\n
712 write(2 from 2) -> (89) \r\n
712 write(6 from 6) -> (83) 1\\r\\n\x04\\r\\n (esc)
713 write(6 from 6) -> (83) 1\\r\\n\x04\\r\\n (esc)
713 write(9 from 9) -> (74) 4\r\nnone\r\n
714 write(9 from 9) -> (74) 4\r\nnone\r\n
714 write(9 from 9) -> (65) 4\r\nHG20\r\n
715 write(9 from 9) -> (65) 4\r\nHG20\r\n
715 write(9 from 9) -> (56) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
716 write(9 from 9) -> (56) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
716 write(9 from 9) -> (47) 4\\r\\n\x00\x00\x00)\\r\\n (esc)
717 write(9 from 9) -> (47) 4\\r\\n\x00\x00\x00)\\r\\n (esc)
717 write(47 from 47) -> (0) 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02 \x01version02nbchanges1\\r\\n (esc)
718 write(47 from 47) -> (0) 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02 \x01version02nbchanges1\\r\\n (esc)
718 write limit reached; closing socket
719 write limit reached; closing socket
719 write(27) -> 15\r\nInternal Server Error\r\n
720 write(27) -> 15\r\nInternal Server Error\r\n
720
721
721 $ rm -f error.log
722 $ rm -f error.log
722
723
723 Server stops after bundle2 part payload chunk size
724 Server stops after bundle2 part payload chunk size
724
725
725 $ hg serve --config badserver.closeaftersendbytes=1031 -p $HGPORT -d --pid-file=hg.pid -E error.log
726 $ hg serve --config badserver.closeaftersendbytes=1043 -p $HGPORT -d --pid-file=hg.pid -E error.log
726 $ cat hg.pid > $DAEMON_PIDS
727 $ cat hg.pid > $DAEMON_PIDS
727
728
728 $ hg clone http://localhost:$HGPORT/ clone
729 $ hg clone http://localhost:$HGPORT/ clone
729 requesting all changes
730 requesting all changes
730 adding changesets
731 adding changesets
731 transaction abort!
732 transaction abort!
732 rollback completed
733 rollback completed
733 abort: HTTP request error (incomplete response)
734 abort: HTTP request error (incomplete response)
734 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
735 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
735 [255]
736 [255]
736
737
737 $ killdaemons.py $DAEMON_PIDS
738 $ killdaemons.py $DAEMON_PIDS
738
739
739 $ tail -11 error.log
740 $ tail -11 error.log
740 write(28 from 28) -> (100) Transfer-Encoding: chunked\r\n
741 write(28 from 28) -> (100) Transfer-Encoding: chunked\r\n
741 write(2 from 2) -> (98) \r\n
742 write(2 from 2) -> (98) \r\n
742 write(6 from 6) -> (92) 1\\r\\n\x04\\r\\n (esc)
743 write(6 from 6) -> (92) 1\\r\\n\x04\\r\\n (esc)
743 write(9 from 9) -> (83) 4\r\nnone\r\n
744 write(9 from 9) -> (83) 4\r\nnone\r\n
744 write(9 from 9) -> (74) 4\r\nHG20\r\n
745 write(9 from 9) -> (74) 4\r\nHG20\r\n
745 write(9 from 9) -> (65) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
746 write(9 from 9) -> (65) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
746 write(9 from 9) -> (56) 4\\r\\n\x00\x00\x00)\\r\\n (esc)
747 write(9 from 9) -> (56) 4\\r\\n\x00\x00\x00)\\r\\n (esc)
747 write(47 from 47) -> (9) 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02 \x01version02nbchanges1\\r\\n (esc)
748 write(47 from 47) -> (9) 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02 \x01version02nbchanges1\\r\\n (esc)
748 write(9 from 9) -> (0) 4\\r\\n\x00\x00\x01\xd2\\r\\n (esc)
749 write(9 from 9) -> (0) 4\\r\\n\x00\x00\x01\xd2\\r\\n (esc)
749 write limit reached; closing socket
750 write limit reached; closing socket
750 write(27) -> 15\r\nInternal Server Error\r\n
751 write(27) -> 15\r\nInternal Server Error\r\n
751
752
752 $ rm -f error.log
753 $ rm -f error.log
753
754
754 Server stops sending in middle of bundle2 payload chunk
755 Server stops sending in middle of bundle2 payload chunk
755
756
756 $ hg serve --config badserver.closeaftersendbytes=1504 -p $HGPORT -d --pid-file=hg.pid -E error.log
757 $ hg serve --config badserver.closeaftersendbytes=1516 -p $HGPORT -d --pid-file=hg.pid -E error.log
757 $ cat hg.pid > $DAEMON_PIDS
758 $ cat hg.pid > $DAEMON_PIDS
758
759
759 $ hg clone http://localhost:$HGPORT/ clone
760 $ hg clone http://localhost:$HGPORT/ clone
760 requesting all changes
761 requesting all changes
761 adding changesets
762 adding changesets
762 transaction abort!
763 transaction abort!
763 rollback completed
764 rollback completed
764 abort: HTTP request error (incomplete response)
765 abort: HTTP request error (incomplete response)
765 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
766 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
766 [255]
767 [255]
767
768
768 $ killdaemons.py $DAEMON_PIDS
769 $ killdaemons.py $DAEMON_PIDS
769
770
770 $ tail -12 error.log
771 $ tail -12 error.log
771 write(28 from 28) -> (573) Transfer-Encoding: chunked\r\n
772 write(28 from 28) -> (573) Transfer-Encoding: chunked\r\n
772 write(2 from 2) -> (571) \r\n
773 write(2 from 2) -> (571) \r\n
773 write(6 from 6) -> (565) 1\\r\\n\x04\\r\\n (esc)
774 write(6 from 6) -> (565) 1\\r\\n\x04\\r\\n (esc)
774 write(9 from 9) -> (556) 4\r\nnone\r\n
775 write(9 from 9) -> (556) 4\r\nnone\r\n
775 write(9 from 9) -> (547) 4\r\nHG20\r\n
776 write(9 from 9) -> (547) 4\r\nHG20\r\n
776 write(9 from 9) -> (538) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
777 write(9 from 9) -> (538) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
777 write(9 from 9) -> (529) 4\\r\\n\x00\x00\x00)\\r\\n (esc)
778 write(9 from 9) -> (529) 4\\r\\n\x00\x00\x00)\\r\\n (esc)
778 write(47 from 47) -> (482) 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02 \x01version02nbchanges1\\r\\n (esc)
779 write(47 from 47) -> (482) 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02 \x01version02nbchanges1\\r\\n (esc)
779 write(9 from 9) -> (473) 4\\r\\n\x00\x00\x01\xd2\\r\\n (esc)
780 write(9 from 9) -> (473) 4\\r\\n\x00\x00\x01\xd2\\r\\n (esc)
780 write(473 from 473) -> (0) 1d2\\r\\n\x00\x00\x00\xb2\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50\\ntest\\n0 0\\nfoo\\n\\ninitial\x00\x00\x00\x00\x00\x00\x00\xa1j=\xf4\xde8\x8f<O\x8e(\xf4\xf9\xa8\x14)\x9a<\xbb_P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-foo\x00b80de5d138758541c5f05265ad144ab9fa86d1db\\n\x00\x00\x00\x00\x00\x00\x00\x07foo\x00\x00\x00h\xb8\\r\xe5\xd18u\x85A\xc5\xf0Re\xad\x14J\xb9\xfa\x86\xd1\xdb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\\r\\n (esc)
781 write(473 from 473) -> (0) 1d2\\r\\n\x00\x00\x00\xb2\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50\\ntest\\n0 0\\nfoo\\n\\ninitial\x00\x00\x00\x00\x00\x00\x00\xa1j=\xf4\xde8\x8f<O\x8e(\xf4\xf9\xa8\x14)\x9a<\xbb_P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-foo\x00b80de5d138758541c5f05265ad144ab9fa86d1db\\n\x00\x00\x00\x00\x00\x00\x00\x07foo\x00\x00\x00h\xb8\\r\xe5\xd18u\x85A\xc5\xf0Re\xad\x14J\xb9\xfa\x86\xd1\xdb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\\r\\n (esc)
781 write limit reached; closing socket
782 write limit reached; closing socket
782 write(27) -> 15\r\nInternal Server Error\r\n
783 write(27) -> 15\r\nInternal Server Error\r\n
783
784
784 $ rm -f error.log
785 $ rm -f error.log
785
786
786 Server stops sending after 0 length payload chunk size
787 Server stops sending after 0 length payload chunk size
787
788
788 $ hg serve --config badserver.closeaftersendbytes=1513 -p $HGPORT -d --pid-file=hg.pid -E error.log
789 $ hg serve --config badserver.closeaftersendbytes=1525 -p $HGPORT -d --pid-file=hg.pid -E error.log
789 $ cat hg.pid > $DAEMON_PIDS
790 $ cat hg.pid > $DAEMON_PIDS
790
791
791 $ hg clone http://localhost:$HGPORT/ clone
792 $ hg clone http://localhost:$HGPORT/ clone
792 requesting all changes
793 requesting all changes
793 adding changesets
794 adding changesets
794 adding manifests
795 adding manifests
795 adding file changes
796 adding file changes
796 added 1 changesets with 1 changes to 1 files
797 added 1 changesets with 1 changes to 1 files
797 transaction abort!
798 transaction abort!
798 rollback completed
799 rollback completed
799 abort: HTTP request error (incomplete response)
800 abort: HTTP request error (incomplete response)
800 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
801 (this may be an intermittent network failure; if the error persists, consider contacting the network or server operator)
801 [255]
802 [255]
802
803
803 $ killdaemons.py $DAEMON_PIDS
804 $ killdaemons.py $DAEMON_PIDS
804
805
805 $ tail -13 error.log
806 $ tail -13 error.log
806 write(28 from 28) -> (582) Transfer-Encoding: chunked\r\n
807 write(28 from 28) -> (582) Transfer-Encoding: chunked\r\n
807 write(2 from 2) -> (580) \r\n
808 write(2 from 2) -> (580) \r\n
808 write(6 from 6) -> (574) 1\\r\\n\x04\\r\\n (esc)
809 write(6 from 6) -> (574) 1\\r\\n\x04\\r\\n (esc)
809 write(9 from 9) -> (565) 4\r\nnone\r\n
810 write(9 from 9) -> (565) 4\r\nnone\r\n
810 write(9 from 9) -> (556) 4\r\nHG20\r\n
811 write(9 from 9) -> (556) 4\r\nHG20\r\n
811 write(9 from 9) -> (547) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
812 write(9 from 9) -> (547) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
812 write(9 from 9) -> (538) 4\\r\\n\x00\x00\x00)\\r\\n (esc)
813 write(9 from 9) -> (538) 4\\r\\n\x00\x00\x00)\\r\\n (esc)
813 write(47 from 47) -> (491) 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02 \x01version02nbchanges1\\r\\n (esc)
814 write(47 from 47) -> (491) 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02 \x01version02nbchanges1\\r\\n (esc)
814 write(9 from 9) -> (482) 4\\r\\n\x00\x00\x01\xd2\\r\\n (esc)
815 write(9 from 9) -> (482) 4\\r\\n\x00\x00\x01\xd2\\r\\n (esc)
815 write(473 from 473) -> (9) 1d2\\r\\n\x00\x00\x00\xb2\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50\\ntest\\n0 0\\nfoo\\n\\ninitial\x00\x00\x00\x00\x00\x00\x00\xa1j=\xf4\xde8\x8f<O\x8e(\xf4\xf9\xa8\x14)\x9a<\xbb_P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-foo\x00b80de5d138758541c5f05265ad144ab9fa86d1db\\n\x00\x00\x00\x00\x00\x00\x00\x07foo\x00\x00\x00h\xb8\\r\xe5\xd18u\x85A\xc5\xf0Re\xad\x14J\xb9\xfa\x86\xd1\xdb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\\r\\n (esc)
816 write(473 from 473) -> (9) 1d2\\r\\n\x00\x00\x00\xb2\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50\\ntest\\n0 0\\nfoo\\n\\ninitial\x00\x00\x00\x00\x00\x00\x00\xa1j=\xf4\xde8\x8f<O\x8e(\xf4\xf9\xa8\x14)\x9a<\xbb_P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-foo\x00b80de5d138758541c5f05265ad144ab9fa86d1db\\n\x00\x00\x00\x00\x00\x00\x00\x07foo\x00\x00\x00h\xb8\\r\xe5\xd18u\x85A\xc5\xf0Re\xad\x14J\xb9\xfa\x86\xd1\xdb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\\r\\n (esc)
816 write(9 from 9) -> (0) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
817 write(9 from 9) -> (0) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
817 write limit reached; closing socket
818 write limit reached; closing socket
818 write(27) -> 15\r\nInternal Server Error\r\n
819 write(27) -> 15\r\nInternal Server Error\r\n
819
820
820 $ rm -f error.log
821 $ rm -f error.log
821
822
822 Server stops sending after 0 part bundle part header (indicating end of bundle2 payload)
823 Server stops sending after 0 part bundle part header (indicating end of bundle2 payload)
823 This is before the 0 size chunked transfer part that signals end of HTTP response.
824 This is before the 0 size chunked transfer part that signals end of HTTP response.
824
825
825 $ hg serve --config badserver.closeaftersendbytes=1710 -p $HGPORT -d --pid-file=hg.pid -E error.log
826 $ hg serve --config badserver.closeaftersendbytes=1722 -p $HGPORT -d --pid-file=hg.pid -E error.log
826 $ cat hg.pid > $DAEMON_PIDS
827 $ cat hg.pid > $DAEMON_PIDS
827
828
828 $ hg clone http://localhost:$HGPORT/ clone
829 $ hg clone http://localhost:$HGPORT/ clone
829 requesting all changes
830 requesting all changes
830 adding changesets
831 adding changesets
831 adding manifests
832 adding manifests
832 adding file changes
833 adding file changes
833 added 1 changesets with 1 changes to 1 files
834 added 1 changesets with 1 changes to 1 files
834 new changesets 96ee1d7354c4
835 new changesets 96ee1d7354c4
835 updating to branch default
836 updating to branch default
836 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
837 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
837
838
838 $ killdaemons.py $DAEMON_PIDS
839 $ killdaemons.py $DAEMON_PIDS
839
840
840 $ tail -22 error.log
841 $ tail -22 error.log
841 write(28 from 28) -> (779) Transfer-Encoding: chunked\r\n
842 write(28 from 28) -> (779) Transfer-Encoding: chunked\r\n
842 write(2 from 2) -> (777) \r\n
843 write(2 from 2) -> (777) \r\n
843 write(6 from 6) -> (771) 1\\r\\n\x04\\r\\n (esc)
844 write(6 from 6) -> (771) 1\\r\\n\x04\\r\\n (esc)
844 write(9 from 9) -> (762) 4\r\nnone\r\n
845 write(9 from 9) -> (762) 4\r\nnone\r\n
845 write(9 from 9) -> (753) 4\r\nHG20\r\n
846 write(9 from 9) -> (753) 4\r\nHG20\r\n
846 write(9 from 9) -> (744) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
847 write(9 from 9) -> (744) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
847 write(9 from 9) -> (735) 4\\r\\n\x00\x00\x00)\\r\\n (esc)
848 write(9 from 9) -> (735) 4\\r\\n\x00\x00\x00)\\r\\n (esc)
848 write(47 from 47) -> (688) 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02 \x01version02nbchanges1\\r\\n (esc)
849 write(47 from 47) -> (688) 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02 \x01version02nbchanges1\\r\\n (esc)
849 write(9 from 9) -> (679) 4\\r\\n\x00\x00\x01\xd2\\r\\n (esc)
850 write(9 from 9) -> (679) 4\\r\\n\x00\x00\x01\xd2\\r\\n (esc)
850 write(473 from 473) -> (206) 1d2\\r\\n\x00\x00\x00\xb2\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50\\ntest\\n0 0\\nfoo\\n\\ninitial\x00\x00\x00\x00\x00\x00\x00\xa1j=\xf4\xde8\x8f<O\x8e(\xf4\xf9\xa8\x14)\x9a<\xbb_P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-foo\x00b80de5d138758541c5f05265ad144ab9fa86d1db\\n\x00\x00\x00\x00\x00\x00\x00\x07foo\x00\x00\x00h\xb8\\r\xe5\xd18u\x85A\xc5\xf0Re\xad\x14J\xb9\xfa\x86\xd1\xdb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\\r\\n (esc)
851 write(473 from 473) -> (206) 1d2\\r\\n\x00\x00\x00\xb2\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50\\ntest\\n0 0\\nfoo\\n\\ninitial\x00\x00\x00\x00\x00\x00\x00\xa1j=\xf4\xde8\x8f<O\x8e(\xf4\xf9\xa8\x14)\x9a<\xbb_P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-foo\x00b80de5d138758541c5f05265ad144ab9fa86d1db\\n\x00\x00\x00\x00\x00\x00\x00\x07foo\x00\x00\x00h\xb8\\r\xe5\xd18u\x85A\xc5\xf0Re\xad\x14J\xb9\xfa\x86\xd1\xdb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\\r\\n (esc)
851 write(9 from 9) -> (197) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
852 write(9 from 9) -> (197) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
852 write(9 from 9) -> (188) 4\\r\\n\x00\x00\x00 \\r\\n (esc)
853 write(9 from 9) -> (188) 4\\r\\n\x00\x00\x00 \\r\\n (esc)
853 write(38 from 38) -> (150) 20\\r\\n\x08LISTKEYS\x00\x00\x00\x01\x01\x00 \x06namespacephases\\r\\n (esc)
854 write(38 from 38) -> (150) 20\\r\\n\x08LISTKEYS\x00\x00\x00\x01\x01\x00 \x06namespacephases\\r\\n (esc)
854 write(9 from 9) -> (141) 4\\r\\n\x00\x00\x00:\\r\\n (esc)
855 write(9 from 9) -> (141) 4\\r\\n\x00\x00\x00:\\r\\n (esc)
855 write(64 from 64) -> (77) 3a\r\n96ee1d7354c4ad7372047672c36a1f561e3a6a4c 1\npublishing True\r\n
856 write(64 from 64) -> (77) 3a\r\n96ee1d7354c4ad7372047672c36a1f561e3a6a4c 1\npublishing True\r\n
856 write(9 from 9) -> (68) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
857 write(9 from 9) -> (68) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
857 write(9 from 9) -> (59) 4\\r\\n\x00\x00\x00#\\r\\n (esc)
858 write(9 from 9) -> (59) 4\\r\\n\x00\x00\x00#\\r\\n (esc)
858 write(41 from 41) -> (18) 23\\r\\n\x08LISTKEYS\x00\x00\x00\x02\x01\x00 namespacebookmarks\\r\\n (esc)
859 write(41 from 41) -> (18) 23\\r\\n\x08LISTKEYS\x00\x00\x00\x02\x01\x00 namespacebookmarks\\r\\n (esc)
859 write(9 from 9) -> (9) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
860 write(9 from 9) -> (9) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
860 write(9 from 9) -> (0) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
861 write(9 from 9) -> (0) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
861 write limit reached; closing socket
862 write limit reached; closing socket
862 write(27) -> 15\r\nInternal Server Error\r\n
863 write(27) -> 15\r\nInternal Server Error\r\n
863
864
864 $ rm -f error.log
865 $ rm -f error.log
865 $ rm -rf clone
866 $ rm -rf clone
866
867
867 Server sends a size 0 chunked-transfer size without terminating \r\n
868 Server sends a size 0 chunked-transfer size without terminating \r\n
868
869
869 $ hg serve --config badserver.closeaftersendbytes=1713 -p $HGPORT -d --pid-file=hg.pid -E error.log
870 $ hg serve --config badserver.closeaftersendbytes=1725 -p $HGPORT -d --pid-file=hg.pid -E error.log
870 $ cat hg.pid > $DAEMON_PIDS
871 $ cat hg.pid > $DAEMON_PIDS
871
872
872 $ hg clone http://localhost:$HGPORT/ clone
873 $ hg clone http://localhost:$HGPORT/ clone
873 requesting all changes
874 requesting all changes
874 adding changesets
875 adding changesets
875 adding manifests
876 adding manifests
876 adding file changes
877 adding file changes
877 added 1 changesets with 1 changes to 1 files
878 added 1 changesets with 1 changes to 1 files
878 new changesets 96ee1d7354c4
879 new changesets 96ee1d7354c4
879 updating to branch default
880 updating to branch default
880 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
881 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
881
882
882 $ killdaemons.py $DAEMON_PIDS
883 $ killdaemons.py $DAEMON_PIDS
883
884
884 $ tail -23 error.log
885 $ tail -23 error.log
885 write(28 from 28) -> (782) Transfer-Encoding: chunked\r\n
886 write(28 from 28) -> (782) Transfer-Encoding: chunked\r\n
886 write(2 from 2) -> (780) \r\n
887 write(2 from 2) -> (780) \r\n
887 write(6 from 6) -> (774) 1\\r\\n\x04\\r\\n (esc)
888 write(6 from 6) -> (774) 1\\r\\n\x04\\r\\n (esc)
888 write(9 from 9) -> (765) 4\r\nnone\r\n
889 write(9 from 9) -> (765) 4\r\nnone\r\n
889 write(9 from 9) -> (756) 4\r\nHG20\r\n
890 write(9 from 9) -> (756) 4\r\nHG20\r\n
890 write(9 from 9) -> (747) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
891 write(9 from 9) -> (747) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
891 write(9 from 9) -> (738) 4\\r\\n\x00\x00\x00)\\r\\n (esc)
892 write(9 from 9) -> (738) 4\\r\\n\x00\x00\x00)\\r\\n (esc)
892 write(47 from 47) -> (691) 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02 \x01version02nbchanges1\\r\\n (esc)
893 write(47 from 47) -> (691) 29\\r\\n\x0bCHANGEGROUP\x00\x00\x00\x00\x01\x01\x07\x02 \x01version02nbchanges1\\r\\n (esc)
893 write(9 from 9) -> (682) 4\\r\\n\x00\x00\x01\xd2\\r\\n (esc)
894 write(9 from 9) -> (682) 4\\r\\n\x00\x00\x01\xd2\\r\\n (esc)
894 write(473 from 473) -> (209) 1d2\\r\\n\x00\x00\x00\xb2\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50\\ntest\\n0 0\\nfoo\\n\\ninitial\x00\x00\x00\x00\x00\x00\x00\xa1j=\xf4\xde8\x8f<O\x8e(\xf4\xf9\xa8\x14)\x9a<\xbb_P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-foo\x00b80de5d138758541c5f05265ad144ab9fa86d1db\\n\x00\x00\x00\x00\x00\x00\x00\x07foo\x00\x00\x00h\xb8\\r\xe5\xd18u\x85A\xc5\xf0Re\xad\x14J\xb9\xfa\x86\xd1\xdb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\\r\\n (esc)
895 write(473 from 473) -> (209) 1d2\\r\\n\x00\x00\x00\xb2\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>6a3df4de388f3c4f8e28f4f9a814299a3cbb5f50\\ntest\\n0 0\\nfoo\\n\\ninitial\x00\x00\x00\x00\x00\x00\x00\xa1j=\xf4\xde8\x8f<O\x8e(\xf4\xf9\xa8\x14)\x9a<\xbb_P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00-foo\x00b80de5d138758541c5f05265ad144ab9fa86d1db\\n\x00\x00\x00\x00\x00\x00\x00\x07foo\x00\x00\x00h\xb8\\r\xe5\xd18u\x85A\xc5\xf0Re\xad\x14J\xb9\xfa\x86\xd1\xdb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL\x00\x00\x00\x00\x00\x00\x00\x00\\r\\n (esc)
895 write(9 from 9) -> (200) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
896 write(9 from 9) -> (200) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
896 write(9 from 9) -> (191) 4\\r\\n\x00\x00\x00 \\r\\n (esc)
897 write(9 from 9) -> (191) 4\\r\\n\x00\x00\x00 \\r\\n (esc)
897 write(38 from 38) -> (153) 20\\r\\n\x08LISTKEYS\x00\x00\x00\x01\x01\x00 \x06namespacephases\\r\\n (esc)
898 write(38 from 38) -> (153) 20\\r\\n\x08LISTKEYS\x00\x00\x00\x01\x01\x00 \x06namespacephases\\r\\n (esc)
898 write(9 from 9) -> (144) 4\\r\\n\x00\x00\x00:\\r\\n (esc)
899 write(9 from 9) -> (144) 4\\r\\n\x00\x00\x00:\\r\\n (esc)
899 write(64 from 64) -> (80) 3a\r\n96ee1d7354c4ad7372047672c36a1f561e3a6a4c 1\npublishing True\r\n
900 write(64 from 64) -> (80) 3a\r\n96ee1d7354c4ad7372047672c36a1f561e3a6a4c 1\npublishing True\r\n
900 write(9 from 9) -> (71) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
901 write(9 from 9) -> (71) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
901 write(9 from 9) -> (62) 4\\r\\n\x00\x00\x00#\\r\\n (esc)
902 write(9 from 9) -> (62) 4\\r\\n\x00\x00\x00#\\r\\n (esc)
902 write(41 from 41) -> (21) 23\\r\\n\x08LISTKEYS\x00\x00\x00\x02\x01\x00 namespacebookmarks\\r\\n (esc)
903 write(41 from 41) -> (21) 23\\r\\n\x08LISTKEYS\x00\x00\x00\x02\x01\x00 namespacebookmarks\\r\\n (esc)
903 write(9 from 9) -> (12) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
904 write(9 from 9) -> (12) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
904 write(9 from 9) -> (3) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
905 write(9 from 9) -> (3) 4\\r\\n\x00\x00\x00\x00\\r\\n (esc)
905 write(3 from 5) -> (0) 0\r\n
906 write(3 from 5) -> (0) 0\r\n
906 write limit reached; closing socket
907 write limit reached; closing socket
907 write(27) -> 15\r\nInternal Server Error\r\n
908 write(27) -> 15\r\nInternal Server Error\r\n
908
909
909 $ rm -f error.log
910 $ rm -f error.log
910 $ rm -rf clone
911 $ rm -rf clone
@@ -1,565 +1,565 b''
1 This test is a duplicate of 'test-http.t' feel free to factor out
1 This test is a duplicate of 'test-http.t' feel free to factor out
2 parts that are not bundle1/bundle2 specific.
2 parts that are not bundle1/bundle2 specific.
3
3
4 $ cat << EOF >> $HGRCPATH
4 $ cat << EOF >> $HGRCPATH
5 > [devel]
5 > [devel]
6 > # This test is dedicated to interaction through old bundle
6 > # This test is dedicated to interaction through old bundle
7 > legacy.exchange = bundle1
7 > legacy.exchange = bundle1
8 > [format] # temporary settings
8 > [format] # temporary settings
9 > usegeneraldelta=yes
9 > usegeneraldelta=yes
10 > EOF
10 > EOF
11
11
12
12
13 This test tries to exercise the ssh functionality with a dummy script
13 This test tries to exercise the ssh functionality with a dummy script
14
14
15 creating 'remote' repo
15 creating 'remote' repo
16
16
17 $ hg init remote
17 $ hg init remote
18 $ cd remote
18 $ cd remote
19 $ echo this > foo
19 $ echo this > foo
20 $ echo this > fooO
20 $ echo this > fooO
21 $ hg ci -A -m "init" foo fooO
21 $ hg ci -A -m "init" foo fooO
22
22
23 insert a closed branch (issue4428)
23 insert a closed branch (issue4428)
24
24
25 $ hg up null
25 $ hg up null
26 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
26 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
27 $ hg branch closed
27 $ hg branch closed
28 marked working directory as branch closed
28 marked working directory as branch closed
29 (branches are permanent and global, did you want a bookmark?)
29 (branches are permanent and global, did you want a bookmark?)
30 $ hg ci -mc0
30 $ hg ci -mc0
31 $ hg ci --close-branch -mc1
31 $ hg ci --close-branch -mc1
32 $ hg up -q default
32 $ hg up -q default
33
33
34 configure for serving
34 configure for serving
35
35
36 $ cat <<EOF > .hg/hgrc
36 $ cat <<EOF > .hg/hgrc
37 > [server]
37 > [server]
38 > uncompressed = True
38 > uncompressed = True
39 >
39 >
40 > [hooks]
40 > [hooks]
41 > changegroup = sh -c "printenv.py changegroup-in-remote 0 ../dummylog"
41 > changegroup = sh -c "printenv.py changegroup-in-remote 0 ../dummylog"
42 > EOF
42 > EOF
43 $ cd ..
43 $ cd ..
44
44
45 repo not found error
45 repo not found error
46
46
47 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/nonexistent local
47 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/nonexistent local
48 remote: abort: repository nonexistent not found!
48 remote: abort: repository nonexistent not found!
49 abort: no suitable response from remote hg!
49 abort: no suitable response from remote hg!
50 [255]
50 [255]
51
51
52 non-existent absolute path
52 non-existent absolute path
53
53
54 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy//`pwd`/nonexistent local
54 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy//`pwd`/nonexistent local
55 remote: abort: repository /$TESTTMP/nonexistent not found!
55 remote: abort: repository /$TESTTMP/nonexistent not found!
56 abort: no suitable response from remote hg!
56 abort: no suitable response from remote hg!
57 [255]
57 [255]
58
58
59 clone remote via stream
59 clone remote via stream
60
60
61 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" --stream ssh://user@dummy/remote local-stream
61 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" --stream ssh://user@dummy/remote local-stream
62 streaming all changes
62 streaming all changes
63 4 files to transfer, 602 bytes of data
63 4 files to transfer, 602 bytes of data
64 transferred 602 bytes in * seconds (*) (glob)
64 transferred 602 bytes in * seconds (*) (glob)
65 searching for changes
65 searching for changes
66 no changes found
66 no changes found
67 updating to branch default
67 updating to branch default
68 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
68 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
69 $ cd local-stream
69 $ cd local-stream
70 $ hg verify
70 $ hg verify
71 checking changesets
71 checking changesets
72 checking manifests
72 checking manifests
73 crosschecking files in changesets and manifests
73 crosschecking files in changesets and manifests
74 checking files
74 checking files
75 2 files, 3 changesets, 2 total revisions
75 2 files, 3 changesets, 2 total revisions
76 $ hg branches
76 $ hg branches
77 default 0:1160648e36ce
77 default 0:1160648e36ce
78 $ cd ..
78 $ cd ..
79
79
80 clone bookmarks via stream
80 clone bookmarks via stream
81
81
82 $ hg -R local-stream book mybook
82 $ hg -R local-stream book mybook
83 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" --stream ssh://user@dummy/local-stream stream2
83 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" --stream ssh://user@dummy/local-stream stream2
84 streaming all changes
84 streaming all changes
85 4 files to transfer, 602 bytes of data
85 4 files to transfer, 602 bytes of data
86 transferred 602 bytes in * seconds (*) (glob)
86 transferred 602 bytes in * seconds (*) (glob)
87 searching for changes
87 searching for changes
88 no changes found
88 no changes found
89 updating to branch default
89 updating to branch default
90 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
90 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
91 $ cd stream2
91 $ cd stream2
92 $ hg book
92 $ hg book
93 mybook 0:1160648e36ce
93 mybook 0:1160648e36ce
94 $ cd ..
94 $ cd ..
95 $ rm -rf local-stream stream2
95 $ rm -rf local-stream stream2
96
96
97 clone remote via pull
97 clone remote via pull
98
98
99 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote local
99 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote local
100 requesting all changes
100 requesting all changes
101 adding changesets
101 adding changesets
102 adding manifests
102 adding manifests
103 adding file changes
103 adding file changes
104 added 3 changesets with 2 changes to 2 files
104 added 3 changesets with 2 changes to 2 files
105 new changesets 1160648e36ce:ad076bfb429d
105 new changesets 1160648e36ce:ad076bfb429d
106 updating to branch default
106 updating to branch default
107 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
107 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
108
108
109 verify
109 verify
110
110
111 $ cd local
111 $ cd local
112 $ hg verify
112 $ hg verify
113 checking changesets
113 checking changesets
114 checking manifests
114 checking manifests
115 crosschecking files in changesets and manifests
115 crosschecking files in changesets and manifests
116 checking files
116 checking files
117 2 files, 3 changesets, 2 total revisions
117 2 files, 3 changesets, 2 total revisions
118 $ cat >> .hg/hgrc <<EOF
118 $ cat >> .hg/hgrc <<EOF
119 > [hooks]
119 > [hooks]
120 > changegroup = sh -c "printenv.py changegroup-in-local 0 ../dummylog"
120 > changegroup = sh -c "printenv.py changegroup-in-local 0 ../dummylog"
121 > EOF
121 > EOF
122
122
123 empty default pull
123 empty default pull
124
124
125 $ hg paths
125 $ hg paths
126 default = ssh://user@dummy/remote
126 default = ssh://user@dummy/remote
127 $ hg pull -e "\"$PYTHON\" \"$TESTDIR/dummyssh\""
127 $ hg pull -e "\"$PYTHON\" \"$TESTDIR/dummyssh\""
128 pulling from ssh://user@dummy/remote
128 pulling from ssh://user@dummy/remote
129 searching for changes
129 searching for changes
130 no changes found
130 no changes found
131
131
132 pull from wrong ssh URL
132 pull from wrong ssh URL
133
133
134 $ hg pull -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/doesnotexist
134 $ hg pull -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/doesnotexist
135 pulling from ssh://user@dummy/doesnotexist
135 pulling from ssh://user@dummy/doesnotexist
136 remote: abort: repository doesnotexist not found!
136 remote: abort: repository doesnotexist not found!
137 abort: no suitable response from remote hg!
137 abort: no suitable response from remote hg!
138 [255]
138 [255]
139
139
140 local change
140 local change
141
141
142 $ echo bleah > foo
142 $ echo bleah > foo
143 $ hg ci -m "add"
143 $ hg ci -m "add"
144
144
145 updating rc
145 updating rc
146
146
147 $ echo "default-push = ssh://user@dummy/remote" >> .hg/hgrc
147 $ echo "default-push = ssh://user@dummy/remote" >> .hg/hgrc
148 $ echo "[ui]" >> .hg/hgrc
148 $ echo "[ui]" >> .hg/hgrc
149 $ echo "ssh = \"$PYTHON\" \"$TESTDIR/dummyssh\"" >> .hg/hgrc
149 $ echo "ssh = \"$PYTHON\" \"$TESTDIR/dummyssh\"" >> .hg/hgrc
150
150
151 find outgoing
151 find outgoing
152
152
153 $ hg out ssh://user@dummy/remote
153 $ hg out ssh://user@dummy/remote
154 comparing with ssh://user@dummy/remote
154 comparing with ssh://user@dummy/remote
155 searching for changes
155 searching for changes
156 changeset: 3:a28a9d1a809c
156 changeset: 3:a28a9d1a809c
157 tag: tip
157 tag: tip
158 parent: 0:1160648e36ce
158 parent: 0:1160648e36ce
159 user: test
159 user: test
160 date: Thu Jan 01 00:00:00 1970 +0000
160 date: Thu Jan 01 00:00:00 1970 +0000
161 summary: add
161 summary: add
162
162
163
163
164 find incoming on the remote side
164 find incoming on the remote side
165
165
166 $ hg incoming -R ../remote -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/local
166 $ hg incoming -R ../remote -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/local
167 comparing with ssh://user@dummy/local
167 comparing with ssh://user@dummy/local
168 searching for changes
168 searching for changes
169 changeset: 3:a28a9d1a809c
169 changeset: 3:a28a9d1a809c
170 tag: tip
170 tag: tip
171 parent: 0:1160648e36ce
171 parent: 0:1160648e36ce
172 user: test
172 user: test
173 date: Thu Jan 01 00:00:00 1970 +0000
173 date: Thu Jan 01 00:00:00 1970 +0000
174 summary: add
174 summary: add
175
175
176
176
177 find incoming on the remote side (using absolute path)
177 find incoming on the remote side (using absolute path)
178
178
179 $ hg incoming -R ../remote -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" "ssh://user@dummy/`pwd`"
179 $ hg incoming -R ../remote -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" "ssh://user@dummy/`pwd`"
180 comparing with ssh://user@dummy/$TESTTMP/local
180 comparing with ssh://user@dummy/$TESTTMP/local
181 searching for changes
181 searching for changes
182 changeset: 3:a28a9d1a809c
182 changeset: 3:a28a9d1a809c
183 tag: tip
183 tag: tip
184 parent: 0:1160648e36ce
184 parent: 0:1160648e36ce
185 user: test
185 user: test
186 date: Thu Jan 01 00:00:00 1970 +0000
186 date: Thu Jan 01 00:00:00 1970 +0000
187 summary: add
187 summary: add
188
188
189
189
190 push
190 push
191
191
192 $ hg push
192 $ hg push
193 pushing to ssh://user@dummy/remote
193 pushing to ssh://user@dummy/remote
194 searching for changes
194 searching for changes
195 remote: adding changesets
195 remote: adding changesets
196 remote: adding manifests
196 remote: adding manifests
197 remote: adding file changes
197 remote: adding file changes
198 remote: added 1 changesets with 1 changes to 1 files
198 remote: added 1 changesets with 1 changes to 1 files
199 $ cd ../remote
199 $ cd ../remote
200
200
201 check remote tip
201 check remote tip
202
202
203 $ hg tip
203 $ hg tip
204 changeset: 3:a28a9d1a809c
204 changeset: 3:a28a9d1a809c
205 tag: tip
205 tag: tip
206 parent: 0:1160648e36ce
206 parent: 0:1160648e36ce
207 user: test
207 user: test
208 date: Thu Jan 01 00:00:00 1970 +0000
208 date: Thu Jan 01 00:00:00 1970 +0000
209 summary: add
209 summary: add
210
210
211 $ hg verify
211 $ hg verify
212 checking changesets
212 checking changesets
213 checking manifests
213 checking manifests
214 crosschecking files in changesets and manifests
214 crosschecking files in changesets and manifests
215 checking files
215 checking files
216 2 files, 4 changesets, 3 total revisions
216 2 files, 4 changesets, 3 total revisions
217 $ hg cat -r tip foo
217 $ hg cat -r tip foo
218 bleah
218 bleah
219 $ echo z > z
219 $ echo z > z
220 $ hg ci -A -m z z
220 $ hg ci -A -m z z
221 created new head
221 created new head
222
222
223 test pushkeys and bookmarks
223 test pushkeys and bookmarks
224
224
225 $ cd ../local
225 $ cd ../local
226 $ hg debugpushkey --config ui.ssh="\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote namespaces
226 $ hg debugpushkey --config ui.ssh="\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote namespaces
227 bookmarks
227 bookmarks
228 namespaces
228 namespaces
229 phases
229 phases
230 $ hg book foo -r 0
230 $ hg book foo -r 0
231 $ hg out -B
231 $ hg out -B
232 comparing with ssh://user@dummy/remote
232 comparing with ssh://user@dummy/remote
233 searching for changed bookmarks
233 searching for changed bookmarks
234 foo 1160648e36ce
234 foo 1160648e36ce
235 $ hg push -B foo
235 $ hg push -B foo
236 pushing to ssh://user@dummy/remote
236 pushing to ssh://user@dummy/remote
237 searching for changes
237 searching for changes
238 no changes found
238 no changes found
239 exporting bookmark foo
239 exporting bookmark foo
240 [1]
240 [1]
241 $ hg debugpushkey --config ui.ssh="\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote bookmarks
241 $ hg debugpushkey --config ui.ssh="\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote bookmarks
242 foo 1160648e36cec0054048a7edc4110c6f84fde594
242 foo 1160648e36cec0054048a7edc4110c6f84fde594
243 $ hg book -f foo
243 $ hg book -f foo
244 $ hg push --traceback
244 $ hg push --traceback
245 pushing to ssh://user@dummy/remote
245 pushing to ssh://user@dummy/remote
246 searching for changes
246 searching for changes
247 no changes found
247 no changes found
248 updating bookmark foo
248 updating bookmark foo
249 [1]
249 [1]
250 $ hg book -d foo
250 $ hg book -d foo
251 $ hg in -B
251 $ hg in -B
252 comparing with ssh://user@dummy/remote
252 comparing with ssh://user@dummy/remote
253 searching for changed bookmarks
253 searching for changed bookmarks
254 foo a28a9d1a809c
254 foo a28a9d1a809c
255 $ hg book -f -r 0 foo
255 $ hg book -f -r 0 foo
256 $ hg pull -B foo
256 $ hg pull -B foo
257 pulling from ssh://user@dummy/remote
257 pulling from ssh://user@dummy/remote
258 no changes found
258 no changes found
259 updating bookmark foo
259 updating bookmark foo
260 $ hg book -d foo
260 $ hg book -d foo
261 $ hg push -B foo
261 $ hg push -B foo
262 pushing to ssh://user@dummy/remote
262 pushing to ssh://user@dummy/remote
263 searching for changes
263 searching for changes
264 no changes found
264 no changes found
265 deleting remote bookmark foo
265 deleting remote bookmark foo
266 [1]
266 [1]
267
267
268 a bad, evil hook that prints to stdout
268 a bad, evil hook that prints to stdout
269
269
270 $ cat <<EOF > $TESTTMP/badhook
270 $ cat <<EOF > $TESTTMP/badhook
271 > import sys
271 > import sys
272 > sys.stdout.write("KABOOM\n")
272 > sys.stdout.write("KABOOM\n")
273 > EOF
273 > EOF
274
274
275 $ echo '[hooks]' >> ../remote/.hg/hgrc
275 $ echo '[hooks]' >> ../remote/.hg/hgrc
276 $ echo "changegroup.stdout = \"$PYTHON\" $TESTTMP/badhook" >> ../remote/.hg/hgrc
276 $ echo "changegroup.stdout = \"$PYTHON\" $TESTTMP/badhook" >> ../remote/.hg/hgrc
277 $ echo r > r
277 $ echo r > r
278 $ hg ci -A -m z r
278 $ hg ci -A -m z r
279
279
280 push should succeed even though it has an unexpected response
280 push should succeed even though it has an unexpected response
281
281
282 $ hg push
282 $ hg push
283 pushing to ssh://user@dummy/remote
283 pushing to ssh://user@dummy/remote
284 searching for changes
284 searching for changes
285 remote has heads on branch 'default' that are not known locally: 6c0482d977a3
285 remote has heads on branch 'default' that are not known locally: 6c0482d977a3
286 remote: adding changesets
286 remote: adding changesets
287 remote: adding manifests
287 remote: adding manifests
288 remote: adding file changes
288 remote: adding file changes
289 remote: added 1 changesets with 1 changes to 1 files
289 remote: added 1 changesets with 1 changes to 1 files
290 remote: KABOOM
290 remote: KABOOM
291 $ hg -R ../remote heads
291 $ hg -R ../remote heads
292 changeset: 5:1383141674ec
292 changeset: 5:1383141674ec
293 tag: tip
293 tag: tip
294 parent: 3:a28a9d1a809c
294 parent: 3:a28a9d1a809c
295 user: test
295 user: test
296 date: Thu Jan 01 00:00:00 1970 +0000
296 date: Thu Jan 01 00:00:00 1970 +0000
297 summary: z
297 summary: z
298
298
299 changeset: 4:6c0482d977a3
299 changeset: 4:6c0482d977a3
300 parent: 0:1160648e36ce
300 parent: 0:1160648e36ce
301 user: test
301 user: test
302 date: Thu Jan 01 00:00:00 1970 +0000
302 date: Thu Jan 01 00:00:00 1970 +0000
303 summary: z
303 summary: z
304
304
305
305
306 clone bookmarks
306 clone bookmarks
307
307
308 $ hg -R ../remote bookmark test
308 $ hg -R ../remote bookmark test
309 $ hg -R ../remote bookmarks
309 $ hg -R ../remote bookmarks
310 * test 4:6c0482d977a3
310 * test 4:6c0482d977a3
311 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote local-bookmarks
311 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote local-bookmarks
312 requesting all changes
312 requesting all changes
313 adding changesets
313 adding changesets
314 adding manifests
314 adding manifests
315 adding file changes
315 adding file changes
316 added 6 changesets with 5 changes to 4 files (+1 heads)
316 added 6 changesets with 5 changes to 4 files (+1 heads)
317 new changesets 1160648e36ce:1383141674ec
317 new changesets 1160648e36ce:1383141674ec
318 updating to branch default
318 updating to branch default
319 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
319 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
320 $ hg -R local-bookmarks bookmarks
320 $ hg -R local-bookmarks bookmarks
321 test 4:6c0482d977a3
321 test 4:6c0482d977a3
322
322
323 passwords in ssh urls are not supported
323 passwords in ssh urls are not supported
324 (we use a glob here because different Python versions give different
324 (we use a glob here because different Python versions give different
325 results here)
325 results here)
326
326
327 $ hg push ssh://user:erroneouspwd@dummy/remote
327 $ hg push ssh://user:erroneouspwd@dummy/remote
328 pushing to ssh://user:*@dummy/remote (glob)
328 pushing to ssh://user:*@dummy/remote (glob)
329 abort: password in URL not supported!
329 abort: password in URL not supported!
330 [255]
330 [255]
331
331
332 $ cd ..
332 $ cd ..
333
333
334 hide outer repo
334 hide outer repo
335 $ hg init
335 $ hg init
336
336
337 Test remote paths with spaces (issue2983):
337 Test remote paths with spaces (issue2983):
338
338
339 $ hg init --ssh "\"$PYTHON\" \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
339 $ hg init --ssh "\"$PYTHON\" \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
340 $ touch "$TESTTMP/a repo/test"
340 $ touch "$TESTTMP/a repo/test"
341 $ hg -R 'a repo' commit -A -m "test"
341 $ hg -R 'a repo' commit -A -m "test"
342 adding test
342 adding test
343 $ hg -R 'a repo' tag tag
343 $ hg -R 'a repo' tag tag
344 $ hg id --ssh "\"$PYTHON\" \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
344 $ hg id --ssh "\"$PYTHON\" \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
345 73649e48688a
345 73649e48688a
346
346
347 $ hg id --ssh "\"$PYTHON\" \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo#noNoNO"
347 $ hg id --ssh "\"$PYTHON\" \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo#noNoNO"
348 abort: unknown revision 'noNoNO'!
348 abort: unknown revision 'noNoNO'!
349 [255]
349 [255]
350
350
351 Test (non-)escaping of remote paths with spaces when cloning (issue3145):
351 Test (non-)escaping of remote paths with spaces when cloning (issue3145):
352
352
353 $ hg clone --ssh "\"$PYTHON\" \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
353 $ hg clone --ssh "\"$PYTHON\" \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
354 destination directory: a repo
354 destination directory: a repo
355 abort: destination 'a repo' is not empty
355 abort: destination 'a repo' is not empty
356 [255]
356 [255]
357
357
358 Test hg-ssh using a helper script that will restore PYTHONPATH (which might
358 Test hg-ssh using a helper script that will restore PYTHONPATH (which might
359 have been cleared by a hg.exe wrapper) and invoke hg-ssh with the right
359 have been cleared by a hg.exe wrapper) and invoke hg-ssh with the right
360 parameters:
360 parameters:
361
361
362 $ cat > ssh.sh << EOF
362 $ cat > ssh.sh << EOF
363 > userhost="\$1"
363 > userhost="\$1"
364 > SSH_ORIGINAL_COMMAND="\$2"
364 > SSH_ORIGINAL_COMMAND="\$2"
365 > export SSH_ORIGINAL_COMMAND
365 > export SSH_ORIGINAL_COMMAND
366 > PYTHONPATH="$PYTHONPATH"
366 > PYTHONPATH="$PYTHONPATH"
367 > export PYTHONPATH
367 > export PYTHONPATH
368 > "$PYTHON" "$TESTDIR/../contrib/hg-ssh" "$TESTTMP/a repo"
368 > "$PYTHON" "$TESTDIR/../contrib/hg-ssh" "$TESTTMP/a repo"
369 > EOF
369 > EOF
370
370
371 $ hg id --ssh "sh ssh.sh" "ssh://user@dummy/a repo"
371 $ hg id --ssh "sh ssh.sh" "ssh://user@dummy/a repo"
372 73649e48688a
372 73649e48688a
373
373
374 $ hg id --ssh "sh ssh.sh" "ssh://user@dummy/a'repo"
374 $ hg id --ssh "sh ssh.sh" "ssh://user@dummy/a'repo"
375 remote: Illegal repository "$TESTTMP/a'repo" (glob)
375 remote: Illegal repository "$TESTTMP/a'repo" (glob)
376 abort: no suitable response from remote hg!
376 abort: no suitable response from remote hg!
377 [255]
377 [255]
378
378
379 $ hg id --ssh "sh ssh.sh" --remotecmd hacking "ssh://user@dummy/a'repo"
379 $ hg id --ssh "sh ssh.sh" --remotecmd hacking "ssh://user@dummy/a'repo"
380 remote: Illegal command "hacking -R 'a'\''repo' serve --stdio"
380 remote: Illegal command "hacking -R 'a'\''repo' serve --stdio"
381 abort: no suitable response from remote hg!
381 abort: no suitable response from remote hg!
382 [255]
382 [255]
383
383
384 $ SSH_ORIGINAL_COMMAND="'hg' serve -R 'a'repo' --stdio" $PYTHON "$TESTDIR/../contrib/hg-ssh"
384 $ SSH_ORIGINAL_COMMAND="'hg' serve -R 'a'repo' --stdio" $PYTHON "$TESTDIR/../contrib/hg-ssh"
385 Illegal command "'hg' serve -R 'a'repo' --stdio": No closing quotation
385 Illegal command "'hg' serve -R 'a'repo' --stdio": No closing quotation
386 [255]
386 [255]
387
387
388 Test hg-ssh in read-only mode:
388 Test hg-ssh in read-only mode:
389
389
390 $ cat > ssh.sh << EOF
390 $ cat > ssh.sh << EOF
391 > userhost="\$1"
391 > userhost="\$1"
392 > SSH_ORIGINAL_COMMAND="\$2"
392 > SSH_ORIGINAL_COMMAND="\$2"
393 > export SSH_ORIGINAL_COMMAND
393 > export SSH_ORIGINAL_COMMAND
394 > PYTHONPATH="$PYTHONPATH"
394 > PYTHONPATH="$PYTHONPATH"
395 > export PYTHONPATH
395 > export PYTHONPATH
396 > "$PYTHON" "$TESTDIR/../contrib/hg-ssh" --read-only "$TESTTMP/remote"
396 > "$PYTHON" "$TESTDIR/../contrib/hg-ssh" --read-only "$TESTTMP/remote"
397 > EOF
397 > EOF
398
398
399 $ hg clone --ssh "sh ssh.sh" "ssh://user@dummy/$TESTTMP/remote" read-only-local
399 $ hg clone --ssh "sh ssh.sh" "ssh://user@dummy/$TESTTMP/remote" read-only-local
400 requesting all changes
400 requesting all changes
401 adding changesets
401 adding changesets
402 adding manifests
402 adding manifests
403 adding file changes
403 adding file changes
404 added 6 changesets with 5 changes to 4 files (+1 heads)
404 added 6 changesets with 5 changes to 4 files (+1 heads)
405 new changesets 1160648e36ce:1383141674ec
405 new changesets 1160648e36ce:1383141674ec
406 updating to branch default
406 updating to branch default
407 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
407 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
408
408
409 $ cd read-only-local
409 $ cd read-only-local
410 $ echo "baz" > bar
410 $ echo "baz" > bar
411 $ hg ci -A -m "unpushable commit" bar
411 $ hg ci -A -m "unpushable commit" bar
412 $ hg push --ssh "sh ../ssh.sh"
412 $ hg push --ssh "sh ../ssh.sh"
413 pushing to ssh://user@dummy/*/remote (glob)
413 pushing to ssh://user@dummy/*/remote (glob)
414 searching for changes
414 searching for changes
415 remote: Permission denied
415 remote: Permission denied
416 remote: abort: pretxnopen.hg-ssh hook failed
416 remote: abort: pretxnopen.hg-ssh hook failed
417 remote: Permission denied
417 remote: Permission denied
418 remote: pushkey-abort: prepushkey.hg-ssh hook failed
418 remote: pushkey-abort: prepushkey.hg-ssh hook failed
419 updating 6c0482d977a3 to public failed!
419 updating 6c0482d977a3 to public failed!
420 [1]
420 [1]
421
421
422 $ cd ..
422 $ cd ..
423
423
424 stderr from remote commands should be printed before stdout from local code (issue4336)
424 stderr from remote commands should be printed before stdout from local code (issue4336)
425
425
426 $ hg clone remote stderr-ordering
426 $ hg clone remote stderr-ordering
427 updating to branch default
427 updating to branch default
428 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
428 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
429 $ cd stderr-ordering
429 $ cd stderr-ordering
430 $ cat >> localwrite.py << EOF
430 $ cat >> localwrite.py << EOF
431 > from mercurial import exchange, extensions
431 > from mercurial import exchange, extensions
432 >
432 >
433 > def wrappedpush(orig, repo, *args, **kwargs):
433 > def wrappedpush(orig, repo, *args, **kwargs):
434 > res = orig(repo, *args, **kwargs)
434 > res = orig(repo, *args, **kwargs)
435 > repo.ui.write('local stdout\n')
435 > repo.ui.write('local stdout\n')
436 > return res
436 > return res
437 >
437 >
438 > def extsetup(ui):
438 > def extsetup(ui):
439 > extensions.wrapfunction(exchange, 'push', wrappedpush)
439 > extensions.wrapfunction(exchange, 'push', wrappedpush)
440 > EOF
440 > EOF
441
441
442 $ cat >> .hg/hgrc << EOF
442 $ cat >> .hg/hgrc << EOF
443 > [paths]
443 > [paths]
444 > default-push = ssh://user@dummy/remote
444 > default-push = ssh://user@dummy/remote
445 > [ui]
445 > [ui]
446 > ssh = "$PYTHON" "$TESTDIR/dummyssh"
446 > ssh = "$PYTHON" "$TESTDIR/dummyssh"
447 > [extensions]
447 > [extensions]
448 > localwrite = localwrite.py
448 > localwrite = localwrite.py
449 > EOF
449 > EOF
450
450
451 $ echo localwrite > foo
451 $ echo localwrite > foo
452 $ hg commit -m 'testing localwrite'
452 $ hg commit -m 'testing localwrite'
453 $ hg push
453 $ hg push
454 pushing to ssh://user@dummy/remote
454 pushing to ssh://user@dummy/remote
455 searching for changes
455 searching for changes
456 remote: adding changesets
456 remote: adding changesets
457 remote: adding manifests
457 remote: adding manifests
458 remote: adding file changes
458 remote: adding file changes
459 remote: added 1 changesets with 1 changes to 1 files
459 remote: added 1 changesets with 1 changes to 1 files
460 remote: KABOOM
460 remote: KABOOM
461 local stdout
461 local stdout
462
462
463 debug output
463 debug output
464
464
465 $ hg pull --debug ssh://user@dummy/remote
465 $ hg pull --debug ssh://user@dummy/remote
466 pulling from ssh://user@dummy/remote
466 pulling from ssh://user@dummy/remote
467 running .* ".*/dummyssh" ['"]user@dummy['"] ('|")hg -R remote serve --stdio('|") (re)
467 running .* ".*/dummyssh" ['"]user@dummy['"] ('|")hg -R remote serve --stdio('|") (re)
468 sending hello command
468 sending hello command
469 sending between command
469 sending between command
470 remote: 372
470 remote: 384
471 remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS$ unbundle=HG10GZ,HG10BZ,HG10UN
471 remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS$ unbundle=HG10GZ,HG10BZ,HG10UN
472 remote: 1
472 remote: 1
473 preparing listkeys for "bookmarks"
473 preparing listkeys for "bookmarks"
474 sending listkeys command
474 sending listkeys command
475 received listkey for "bookmarks": 45 bytes
475 received listkey for "bookmarks": 45 bytes
476 query 1; heads
476 query 1; heads
477 sending batch command
477 sending batch command
478 searching for changes
478 searching for changes
479 all remote heads known locally
479 all remote heads known locally
480 no changes found
480 no changes found
481 preparing listkeys for "phases"
481 preparing listkeys for "phases"
482 sending listkeys command
482 sending listkeys command
483 received listkey for "phases": 15 bytes
483 received listkey for "phases": 15 bytes
484 checking for updated bookmarks
484 checking for updated bookmarks
485
485
486 $ cd ..
486 $ cd ..
487
487
488 $ cat dummylog
488 $ cat dummylog
489 Got arguments 1:user@dummy 2:hg -R nonexistent serve --stdio
489 Got arguments 1:user@dummy 2:hg -R nonexistent serve --stdio
490 Got arguments 1:user@dummy 2:hg -R /$TESTTMP/nonexistent serve --stdio
490 Got arguments 1:user@dummy 2:hg -R /$TESTTMP/nonexistent serve --stdio
491 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
491 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
492 Got arguments 1:user@dummy 2:hg -R local-stream serve --stdio
492 Got arguments 1:user@dummy 2:hg -R local-stream serve --stdio
493 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
493 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
494 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
494 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
495 Got arguments 1:user@dummy 2:hg -R doesnotexist serve --stdio
495 Got arguments 1:user@dummy 2:hg -R doesnotexist serve --stdio
496 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
496 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
497 Got arguments 1:user@dummy 2:hg -R local serve --stdio
497 Got arguments 1:user@dummy 2:hg -R local serve --stdio
498 Got arguments 1:user@dummy 2:hg -R $TESTTMP/local serve --stdio
498 Got arguments 1:user@dummy 2:hg -R $TESTTMP/local serve --stdio
499 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
499 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
500 changegroup-in-remote hook: HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=a28a9d1a809cab7d4e2fde4bee738a9ede948b60 HG_NODE_LAST=a28a9d1a809cab7d4e2fde4bee738a9ede948b60 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:ssh:$LOCALIP
500 changegroup-in-remote hook: HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=a28a9d1a809cab7d4e2fde4bee738a9ede948b60 HG_NODE_LAST=a28a9d1a809cab7d4e2fde4bee738a9ede948b60 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:ssh:$LOCALIP
501 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
501 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
502 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
502 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
503 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
503 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
504 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
504 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
505 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
505 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
506 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
506 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
507 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
507 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
508 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
508 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
509 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
509 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
510 changegroup-in-remote hook: HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=1383141674ec756a6056f6a9097618482fe0f4a6 HG_NODE_LAST=1383141674ec756a6056f6a9097618482fe0f4a6 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:ssh:$LOCALIP
510 changegroup-in-remote hook: HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=1383141674ec756a6056f6a9097618482fe0f4a6 HG_NODE_LAST=1383141674ec756a6056f6a9097618482fe0f4a6 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:ssh:$LOCALIP
511 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
511 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
512 Got arguments 1:user@dummy 2:hg init 'a repo'
512 Got arguments 1:user@dummy 2:hg init 'a repo'
513 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
513 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
514 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
514 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
515 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
515 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
516 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
516 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
517 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
517 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
518 changegroup-in-remote hook: HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=65c38f4125f9602c8db4af56530cc221d93b8ef8 HG_NODE_LAST=65c38f4125f9602c8db4af56530cc221d93b8ef8 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:ssh:$LOCALIP
518 changegroup-in-remote hook: HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=65c38f4125f9602c8db4af56530cc221d93b8ef8 HG_NODE_LAST=65c38f4125f9602c8db4af56530cc221d93b8ef8 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:ssh:$LOCALIP
519 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
519 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
520
520
521 remote hook failure is attributed to remote
521 remote hook failure is attributed to remote
522
522
523 $ cat > $TESTTMP/failhook << EOF
523 $ cat > $TESTTMP/failhook << EOF
524 > def hook(ui, repo, **kwargs):
524 > def hook(ui, repo, **kwargs):
525 > ui.write('hook failure!\n')
525 > ui.write('hook failure!\n')
526 > ui.flush()
526 > ui.flush()
527 > return 1
527 > return 1
528 > EOF
528 > EOF
529
529
530 $ echo "pretxnchangegroup.fail = python:$TESTTMP/failhook:hook" >> remote/.hg/hgrc
530 $ echo "pretxnchangegroup.fail = python:$TESTTMP/failhook:hook" >> remote/.hg/hgrc
531
531
532 $ hg -q --config ui.ssh="\"$PYTHON\" $TESTDIR/dummyssh" clone ssh://user@dummy/remote hookout
532 $ hg -q --config ui.ssh="\"$PYTHON\" $TESTDIR/dummyssh" clone ssh://user@dummy/remote hookout
533 $ cd hookout
533 $ cd hookout
534 $ touch hookfailure
534 $ touch hookfailure
535 $ hg -q commit -A -m 'remote hook failure'
535 $ hg -q commit -A -m 'remote hook failure'
536 $ hg --config ui.ssh="\"$PYTHON\" $TESTDIR/dummyssh" push
536 $ hg --config ui.ssh="\"$PYTHON\" $TESTDIR/dummyssh" push
537 pushing to ssh://user@dummy/remote
537 pushing to ssh://user@dummy/remote
538 searching for changes
538 searching for changes
539 remote: adding changesets
539 remote: adding changesets
540 remote: adding manifests
540 remote: adding manifests
541 remote: adding file changes
541 remote: adding file changes
542 remote: added 1 changesets with 1 changes to 1 files
542 remote: added 1 changesets with 1 changes to 1 files
543 remote: hook failure!
543 remote: hook failure!
544 remote: transaction abort!
544 remote: transaction abort!
545 remote: rollback completed
545 remote: rollback completed
546 remote: abort: pretxnchangegroup.fail hook failed
546 remote: abort: pretxnchangegroup.fail hook failed
547 [1]
547 [1]
548
548
549 abort during pull is properly reported as such
549 abort during pull is properly reported as such
550
550
551 $ echo morefoo >> ../remote/foo
551 $ echo morefoo >> ../remote/foo
552 $ hg -R ../remote commit --message "more foo to be pulled"
552 $ hg -R ../remote commit --message "more foo to be pulled"
553 $ cat >> ../remote/.hg/hgrc << EOF
553 $ cat >> ../remote/.hg/hgrc << EOF
554 > [extensions]
554 > [extensions]
555 > crash = ${TESTDIR}/crashgetbundler.py
555 > crash = ${TESTDIR}/crashgetbundler.py
556 > EOF
556 > EOF
557 $ hg --config ui.ssh="\"$PYTHON\" $TESTDIR/dummyssh" pull
557 $ hg --config ui.ssh="\"$PYTHON\" $TESTDIR/dummyssh" pull
558 pulling from ssh://user@dummy/remote
558 pulling from ssh://user@dummy/remote
559 searching for changes
559 searching for changes
560 adding changesets
560 adding changesets
561 remote: abort: this is an exercise
561 remote: abort: this is an exercise
562 transaction abort!
562 transaction abort!
563 rollback completed
563 rollback completed
564 abort: stream ended unexpectedly (got 0 bytes, expected 4)
564 abort: stream ended unexpectedly (got 0 bytes, expected 4)
565 [255]
565 [255]
@@ -1,596 +1,596 b''
1
1
2 This test tries to exercise the ssh functionality with a dummy script
2 This test tries to exercise the ssh functionality with a dummy script
3
3
4 $ cat <<EOF >> $HGRCPATH
4 $ cat <<EOF >> $HGRCPATH
5 > [format]
5 > [format]
6 > usegeneraldelta=yes
6 > usegeneraldelta=yes
7 > EOF
7 > EOF
8
8
9 creating 'remote' repo
9 creating 'remote' repo
10
10
11 $ hg init remote
11 $ hg init remote
12 $ cd remote
12 $ cd remote
13 $ echo this > foo
13 $ echo this > foo
14 $ echo this > fooO
14 $ echo this > fooO
15 $ hg ci -A -m "init" foo fooO
15 $ hg ci -A -m "init" foo fooO
16
16
17 insert a closed branch (issue4428)
17 insert a closed branch (issue4428)
18
18
19 $ hg up null
19 $ hg up null
20 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
20 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
21 $ hg branch closed
21 $ hg branch closed
22 marked working directory as branch closed
22 marked working directory as branch closed
23 (branches are permanent and global, did you want a bookmark?)
23 (branches are permanent and global, did you want a bookmark?)
24 $ hg ci -mc0
24 $ hg ci -mc0
25 $ hg ci --close-branch -mc1
25 $ hg ci --close-branch -mc1
26 $ hg up -q default
26 $ hg up -q default
27
27
28 configure for serving
28 configure for serving
29
29
30 $ cat <<EOF > .hg/hgrc
30 $ cat <<EOF > .hg/hgrc
31 > [server]
31 > [server]
32 > uncompressed = True
32 > uncompressed = True
33 >
33 >
34 > [hooks]
34 > [hooks]
35 > changegroup = sh -c "printenv.py changegroup-in-remote 0 ../dummylog"
35 > changegroup = sh -c "printenv.py changegroup-in-remote 0 ../dummylog"
36 > EOF
36 > EOF
37 $ cd ..
37 $ cd ..
38
38
39 repo not found error
39 repo not found error
40
40
41 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/nonexistent local
41 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/nonexistent local
42 remote: abort: repository nonexistent not found!
42 remote: abort: repository nonexistent not found!
43 abort: no suitable response from remote hg!
43 abort: no suitable response from remote hg!
44 [255]
44 [255]
45
45
46 non-existent absolute path
46 non-existent absolute path
47
47
48 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/`pwd`/nonexistent local
48 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/`pwd`/nonexistent local
49 remote: abort: repository $TESTTMP/nonexistent not found!
49 remote: abort: repository $TESTTMP/nonexistent not found!
50 abort: no suitable response from remote hg!
50 abort: no suitable response from remote hg!
51 [255]
51 [255]
52
52
53 clone remote via stream
53 clone remote via stream
54
54
55 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" --stream ssh://user@dummy/remote local-stream
55 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" --stream ssh://user@dummy/remote local-stream
56 streaming all changes
56 streaming all changes
57 4 files to transfer, 602 bytes of data
57 4 files to transfer, 602 bytes of data
58 transferred 602 bytes in * seconds (*) (glob)
58 transferred 602 bytes in * seconds (*) (glob)
59 searching for changes
59 searching for changes
60 no changes found
60 no changes found
61 updating to branch default
61 updating to branch default
62 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
62 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
63 $ cd local-stream
63 $ cd local-stream
64 $ hg verify
64 $ hg verify
65 checking changesets
65 checking changesets
66 checking manifests
66 checking manifests
67 crosschecking files in changesets and manifests
67 crosschecking files in changesets and manifests
68 checking files
68 checking files
69 2 files, 3 changesets, 2 total revisions
69 2 files, 3 changesets, 2 total revisions
70 $ hg branches
70 $ hg branches
71 default 0:1160648e36ce
71 default 0:1160648e36ce
72 $ cd ..
72 $ cd ..
73
73
74 clone bookmarks via stream
74 clone bookmarks via stream
75
75
76 $ hg -R local-stream book mybook
76 $ hg -R local-stream book mybook
77 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" --stream ssh://user@dummy/local-stream stream2
77 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" --stream ssh://user@dummy/local-stream stream2
78 streaming all changes
78 streaming all changes
79 4 files to transfer, 602 bytes of data
79 4 files to transfer, 602 bytes of data
80 transferred 602 bytes in * seconds (*) (glob)
80 transferred 602 bytes in * seconds (*) (glob)
81 searching for changes
81 searching for changes
82 no changes found
82 no changes found
83 updating to branch default
83 updating to branch default
84 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
84 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
85 $ cd stream2
85 $ cd stream2
86 $ hg book
86 $ hg book
87 mybook 0:1160648e36ce
87 mybook 0:1160648e36ce
88 $ cd ..
88 $ cd ..
89 $ rm -rf local-stream stream2
89 $ rm -rf local-stream stream2
90
90
91 clone remote via pull
91 clone remote via pull
92
92
93 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote local
93 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote local
94 requesting all changes
94 requesting all changes
95 adding changesets
95 adding changesets
96 adding manifests
96 adding manifests
97 adding file changes
97 adding file changes
98 added 3 changesets with 2 changes to 2 files
98 added 3 changesets with 2 changes to 2 files
99 new changesets 1160648e36ce:ad076bfb429d
99 new changesets 1160648e36ce:ad076bfb429d
100 updating to branch default
100 updating to branch default
101 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
101 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
102
102
103 verify
103 verify
104
104
105 $ cd local
105 $ cd local
106 $ hg verify
106 $ hg verify
107 checking changesets
107 checking changesets
108 checking manifests
108 checking manifests
109 crosschecking files in changesets and manifests
109 crosschecking files in changesets and manifests
110 checking files
110 checking files
111 2 files, 3 changesets, 2 total revisions
111 2 files, 3 changesets, 2 total revisions
112 $ cat >> .hg/hgrc <<EOF
112 $ cat >> .hg/hgrc <<EOF
113 > [hooks]
113 > [hooks]
114 > changegroup = sh -c "printenv.py changegroup-in-local 0 ../dummylog"
114 > changegroup = sh -c "printenv.py changegroup-in-local 0 ../dummylog"
115 > EOF
115 > EOF
116
116
117 empty default pull
117 empty default pull
118
118
119 $ hg paths
119 $ hg paths
120 default = ssh://user@dummy/remote
120 default = ssh://user@dummy/remote
121 $ hg pull -e "\"$PYTHON\" \"$TESTDIR/dummyssh\""
121 $ hg pull -e "\"$PYTHON\" \"$TESTDIR/dummyssh\""
122 pulling from ssh://user@dummy/remote
122 pulling from ssh://user@dummy/remote
123 searching for changes
123 searching for changes
124 no changes found
124 no changes found
125
125
126 pull from wrong ssh URL
126 pull from wrong ssh URL
127
127
128 $ hg pull -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/doesnotexist
128 $ hg pull -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/doesnotexist
129 pulling from ssh://user@dummy/doesnotexist
129 pulling from ssh://user@dummy/doesnotexist
130 remote: abort: repository doesnotexist not found!
130 remote: abort: repository doesnotexist not found!
131 abort: no suitable response from remote hg!
131 abort: no suitable response from remote hg!
132 [255]
132 [255]
133
133
134 local change
134 local change
135
135
136 $ echo bleah > foo
136 $ echo bleah > foo
137 $ hg ci -m "add"
137 $ hg ci -m "add"
138
138
139 updating rc
139 updating rc
140
140
141 $ echo "default-push = ssh://user@dummy/remote" >> .hg/hgrc
141 $ echo "default-push = ssh://user@dummy/remote" >> .hg/hgrc
142 $ echo "[ui]" >> .hg/hgrc
142 $ echo "[ui]" >> .hg/hgrc
143 $ echo "ssh = \"$PYTHON\" \"$TESTDIR/dummyssh\"" >> .hg/hgrc
143 $ echo "ssh = \"$PYTHON\" \"$TESTDIR/dummyssh\"" >> .hg/hgrc
144
144
145 find outgoing
145 find outgoing
146
146
147 $ hg out ssh://user@dummy/remote
147 $ hg out ssh://user@dummy/remote
148 comparing with ssh://user@dummy/remote
148 comparing with ssh://user@dummy/remote
149 searching for changes
149 searching for changes
150 changeset: 3:a28a9d1a809c
150 changeset: 3:a28a9d1a809c
151 tag: tip
151 tag: tip
152 parent: 0:1160648e36ce
152 parent: 0:1160648e36ce
153 user: test
153 user: test
154 date: Thu Jan 01 00:00:00 1970 +0000
154 date: Thu Jan 01 00:00:00 1970 +0000
155 summary: add
155 summary: add
156
156
157
157
158 find incoming on the remote side
158 find incoming on the remote side
159
159
160 $ hg incoming -R ../remote -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/local
160 $ hg incoming -R ../remote -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/local
161 comparing with ssh://user@dummy/local
161 comparing with ssh://user@dummy/local
162 searching for changes
162 searching for changes
163 changeset: 3:a28a9d1a809c
163 changeset: 3:a28a9d1a809c
164 tag: tip
164 tag: tip
165 parent: 0:1160648e36ce
165 parent: 0:1160648e36ce
166 user: test
166 user: test
167 date: Thu Jan 01 00:00:00 1970 +0000
167 date: Thu Jan 01 00:00:00 1970 +0000
168 summary: add
168 summary: add
169
169
170
170
171 find incoming on the remote side (using absolute path)
171 find incoming on the remote side (using absolute path)
172
172
173 $ hg incoming -R ../remote -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" "ssh://user@dummy/`pwd`"
173 $ hg incoming -R ../remote -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" "ssh://user@dummy/`pwd`"
174 comparing with ssh://user@dummy/$TESTTMP/local
174 comparing with ssh://user@dummy/$TESTTMP/local
175 searching for changes
175 searching for changes
176 changeset: 3:a28a9d1a809c
176 changeset: 3:a28a9d1a809c
177 tag: tip
177 tag: tip
178 parent: 0:1160648e36ce
178 parent: 0:1160648e36ce
179 user: test
179 user: test
180 date: Thu Jan 01 00:00:00 1970 +0000
180 date: Thu Jan 01 00:00:00 1970 +0000
181 summary: add
181 summary: add
182
182
183
183
184 push
184 push
185
185
186 $ hg push
186 $ hg push
187 pushing to ssh://user@dummy/remote
187 pushing to ssh://user@dummy/remote
188 searching for changes
188 searching for changes
189 remote: adding changesets
189 remote: adding changesets
190 remote: adding manifests
190 remote: adding manifests
191 remote: adding file changes
191 remote: adding file changes
192 remote: added 1 changesets with 1 changes to 1 files
192 remote: added 1 changesets with 1 changes to 1 files
193 $ cd ../remote
193 $ cd ../remote
194
194
195 check remote tip
195 check remote tip
196
196
197 $ hg tip
197 $ hg tip
198 changeset: 3:a28a9d1a809c
198 changeset: 3:a28a9d1a809c
199 tag: tip
199 tag: tip
200 parent: 0:1160648e36ce
200 parent: 0:1160648e36ce
201 user: test
201 user: test
202 date: Thu Jan 01 00:00:00 1970 +0000
202 date: Thu Jan 01 00:00:00 1970 +0000
203 summary: add
203 summary: add
204
204
205 $ hg verify
205 $ hg verify
206 checking changesets
206 checking changesets
207 checking manifests
207 checking manifests
208 crosschecking files in changesets and manifests
208 crosschecking files in changesets and manifests
209 checking files
209 checking files
210 2 files, 4 changesets, 3 total revisions
210 2 files, 4 changesets, 3 total revisions
211 $ hg cat -r tip foo
211 $ hg cat -r tip foo
212 bleah
212 bleah
213 $ echo z > z
213 $ echo z > z
214 $ hg ci -A -m z z
214 $ hg ci -A -m z z
215 created new head
215 created new head
216
216
217 test pushkeys and bookmarks
217 test pushkeys and bookmarks
218
218
219 $ cd ../local
219 $ cd ../local
220 $ hg debugpushkey --config ui.ssh="\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote namespaces
220 $ hg debugpushkey --config ui.ssh="\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote namespaces
221 bookmarks
221 bookmarks
222 namespaces
222 namespaces
223 phases
223 phases
224 $ hg book foo -r 0
224 $ hg book foo -r 0
225 $ hg out -B
225 $ hg out -B
226 comparing with ssh://user@dummy/remote
226 comparing with ssh://user@dummy/remote
227 searching for changed bookmarks
227 searching for changed bookmarks
228 foo 1160648e36ce
228 foo 1160648e36ce
229 $ hg push -B foo
229 $ hg push -B foo
230 pushing to ssh://user@dummy/remote
230 pushing to ssh://user@dummy/remote
231 searching for changes
231 searching for changes
232 no changes found
232 no changes found
233 exporting bookmark foo
233 exporting bookmark foo
234 [1]
234 [1]
235 $ hg debugpushkey --config ui.ssh="\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote bookmarks
235 $ hg debugpushkey --config ui.ssh="\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote bookmarks
236 foo 1160648e36cec0054048a7edc4110c6f84fde594
236 foo 1160648e36cec0054048a7edc4110c6f84fde594
237 $ hg book -f foo
237 $ hg book -f foo
238 $ hg push --traceback
238 $ hg push --traceback
239 pushing to ssh://user@dummy/remote
239 pushing to ssh://user@dummy/remote
240 searching for changes
240 searching for changes
241 no changes found
241 no changes found
242 updating bookmark foo
242 updating bookmark foo
243 [1]
243 [1]
244 $ hg book -d foo
244 $ hg book -d foo
245 $ hg in -B
245 $ hg in -B
246 comparing with ssh://user@dummy/remote
246 comparing with ssh://user@dummy/remote
247 searching for changed bookmarks
247 searching for changed bookmarks
248 foo a28a9d1a809c
248 foo a28a9d1a809c
249 $ hg book -f -r 0 foo
249 $ hg book -f -r 0 foo
250 $ hg pull -B foo
250 $ hg pull -B foo
251 pulling from ssh://user@dummy/remote
251 pulling from ssh://user@dummy/remote
252 no changes found
252 no changes found
253 updating bookmark foo
253 updating bookmark foo
254 $ hg book -d foo
254 $ hg book -d foo
255 $ hg push -B foo
255 $ hg push -B foo
256 pushing to ssh://user@dummy/remote
256 pushing to ssh://user@dummy/remote
257 searching for changes
257 searching for changes
258 no changes found
258 no changes found
259 deleting remote bookmark foo
259 deleting remote bookmark foo
260 [1]
260 [1]
261
261
262 a bad, evil hook that prints to stdout
262 a bad, evil hook that prints to stdout
263
263
264 $ cat <<EOF > $TESTTMP/badhook
264 $ cat <<EOF > $TESTTMP/badhook
265 > import sys
265 > import sys
266 > sys.stdout.write("KABOOM\n")
266 > sys.stdout.write("KABOOM\n")
267 > EOF
267 > EOF
268
268
269 $ cat <<EOF > $TESTTMP/badpyhook.py
269 $ cat <<EOF > $TESTTMP/badpyhook.py
270 > import sys
270 > import sys
271 > def hook(ui, repo, hooktype, **kwargs):
271 > def hook(ui, repo, hooktype, **kwargs):
272 > sys.stdout.write("KABOOM IN PROCESS\n")
272 > sys.stdout.write("KABOOM IN PROCESS\n")
273 > EOF
273 > EOF
274
274
275 $ cat <<EOF >> ../remote/.hg/hgrc
275 $ cat <<EOF >> ../remote/.hg/hgrc
276 > [hooks]
276 > [hooks]
277 > changegroup.stdout = $PYTHON $TESTTMP/badhook
277 > changegroup.stdout = $PYTHON $TESTTMP/badhook
278 > changegroup.pystdout = python:$TESTTMP/badpyhook.py:hook
278 > changegroup.pystdout = python:$TESTTMP/badpyhook.py:hook
279 > EOF
279 > EOF
280 $ echo r > r
280 $ echo r > r
281 $ hg ci -A -m z r
281 $ hg ci -A -m z r
282
282
283 push should succeed even though it has an unexpected response
283 push should succeed even though it has an unexpected response
284
284
285 $ hg push
285 $ hg push
286 pushing to ssh://user@dummy/remote
286 pushing to ssh://user@dummy/remote
287 searching for changes
287 searching for changes
288 remote has heads on branch 'default' that are not known locally: 6c0482d977a3
288 remote has heads on branch 'default' that are not known locally: 6c0482d977a3
289 remote: adding changesets
289 remote: adding changesets
290 remote: adding manifests
290 remote: adding manifests
291 remote: adding file changes
291 remote: adding file changes
292 remote: added 1 changesets with 1 changes to 1 files
292 remote: added 1 changesets with 1 changes to 1 files
293 remote: KABOOM
293 remote: KABOOM
294 remote: KABOOM IN PROCESS
294 remote: KABOOM IN PROCESS
295 $ hg -R ../remote heads
295 $ hg -R ../remote heads
296 changeset: 5:1383141674ec
296 changeset: 5:1383141674ec
297 tag: tip
297 tag: tip
298 parent: 3:a28a9d1a809c
298 parent: 3:a28a9d1a809c
299 user: test
299 user: test
300 date: Thu Jan 01 00:00:00 1970 +0000
300 date: Thu Jan 01 00:00:00 1970 +0000
301 summary: z
301 summary: z
302
302
303 changeset: 4:6c0482d977a3
303 changeset: 4:6c0482d977a3
304 parent: 0:1160648e36ce
304 parent: 0:1160648e36ce
305 user: test
305 user: test
306 date: Thu Jan 01 00:00:00 1970 +0000
306 date: Thu Jan 01 00:00:00 1970 +0000
307 summary: z
307 summary: z
308
308
309
309
310 clone bookmarks
310 clone bookmarks
311
311
312 $ hg -R ../remote bookmark test
312 $ hg -R ../remote bookmark test
313 $ hg -R ../remote bookmarks
313 $ hg -R ../remote bookmarks
314 * test 4:6c0482d977a3
314 * test 4:6c0482d977a3
315 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote local-bookmarks
315 $ hg clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/remote local-bookmarks
316 requesting all changes
316 requesting all changes
317 adding changesets
317 adding changesets
318 adding manifests
318 adding manifests
319 adding file changes
319 adding file changes
320 added 6 changesets with 5 changes to 4 files (+1 heads)
320 added 6 changesets with 5 changes to 4 files (+1 heads)
321 new changesets 1160648e36ce:1383141674ec
321 new changesets 1160648e36ce:1383141674ec
322 updating to branch default
322 updating to branch default
323 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
323 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
324 $ hg -R local-bookmarks bookmarks
324 $ hg -R local-bookmarks bookmarks
325 test 4:6c0482d977a3
325 test 4:6c0482d977a3
326
326
327 passwords in ssh urls are not supported
327 passwords in ssh urls are not supported
328 (we use a glob here because different Python versions give different
328 (we use a glob here because different Python versions give different
329 results here)
329 results here)
330
330
331 $ hg push ssh://user:erroneouspwd@dummy/remote
331 $ hg push ssh://user:erroneouspwd@dummy/remote
332 pushing to ssh://user:*@dummy/remote (glob)
332 pushing to ssh://user:*@dummy/remote (glob)
333 abort: password in URL not supported!
333 abort: password in URL not supported!
334 [255]
334 [255]
335
335
336 $ cd ..
336 $ cd ..
337
337
338 hide outer repo
338 hide outer repo
339 $ hg init
339 $ hg init
340
340
341 Test remote paths with spaces (issue2983):
341 Test remote paths with spaces (issue2983):
342
342
343 $ hg init --ssh "\"$PYTHON\" \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
343 $ hg init --ssh "\"$PYTHON\" \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
344 $ touch "$TESTTMP/a repo/test"
344 $ touch "$TESTTMP/a repo/test"
345 $ hg -R 'a repo' commit -A -m "test"
345 $ hg -R 'a repo' commit -A -m "test"
346 adding test
346 adding test
347 $ hg -R 'a repo' tag tag
347 $ hg -R 'a repo' tag tag
348 $ hg id --ssh "\"$PYTHON\" \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
348 $ hg id --ssh "\"$PYTHON\" \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
349 73649e48688a
349 73649e48688a
350
350
351 $ hg id --ssh "\"$PYTHON\" \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo#noNoNO"
351 $ hg id --ssh "\"$PYTHON\" \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo#noNoNO"
352 abort: unknown revision 'noNoNO'!
352 abort: unknown revision 'noNoNO'!
353 [255]
353 [255]
354
354
355 Test (non-)escaping of remote paths with spaces when cloning (issue3145):
355 Test (non-)escaping of remote paths with spaces when cloning (issue3145):
356
356
357 $ hg clone --ssh "\"$PYTHON\" \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
357 $ hg clone --ssh "\"$PYTHON\" \"$TESTDIR/dummyssh\"" "ssh://user@dummy/a repo"
358 destination directory: a repo
358 destination directory: a repo
359 abort: destination 'a repo' is not empty
359 abort: destination 'a repo' is not empty
360 [255]
360 [255]
361
361
362 Make sure hg is really paranoid in serve --stdio mode. It used to be
362 Make sure hg is really paranoid in serve --stdio mode. It used to be
363 possible to get a debugger REPL by specifying a repo named --debugger.
363 possible to get a debugger REPL by specifying a repo named --debugger.
364 $ hg -R --debugger serve --stdio
364 $ hg -R --debugger serve --stdio
365 abort: potentially unsafe serve --stdio invocation: ['-R', '--debugger', 'serve', '--stdio']
365 abort: potentially unsafe serve --stdio invocation: ['-R', '--debugger', 'serve', '--stdio']
366 [255]
366 [255]
367 $ hg -R --config=ui.debugger=yes serve --stdio
367 $ hg -R --config=ui.debugger=yes serve --stdio
368 abort: potentially unsafe serve --stdio invocation: ['-R', '--config=ui.debugger=yes', 'serve', '--stdio']
368 abort: potentially unsafe serve --stdio invocation: ['-R', '--config=ui.debugger=yes', 'serve', '--stdio']
369 [255]
369 [255]
370 Abbreviations of 'serve' also don't work, to avoid shenanigans.
370 Abbreviations of 'serve' also don't work, to avoid shenanigans.
371 $ hg -R narf serv --stdio
371 $ hg -R narf serv --stdio
372 abort: potentially unsafe serve --stdio invocation: ['-R', 'narf', 'serv', '--stdio']
372 abort: potentially unsafe serve --stdio invocation: ['-R', 'narf', 'serv', '--stdio']
373 [255]
373 [255]
374
374
375 Test hg-ssh using a helper script that will restore PYTHONPATH (which might
375 Test hg-ssh using a helper script that will restore PYTHONPATH (which might
376 have been cleared by a hg.exe wrapper) and invoke hg-ssh with the right
376 have been cleared by a hg.exe wrapper) and invoke hg-ssh with the right
377 parameters:
377 parameters:
378
378
379 $ cat > ssh.sh << EOF
379 $ cat > ssh.sh << EOF
380 > userhost="\$1"
380 > userhost="\$1"
381 > SSH_ORIGINAL_COMMAND="\$2"
381 > SSH_ORIGINAL_COMMAND="\$2"
382 > export SSH_ORIGINAL_COMMAND
382 > export SSH_ORIGINAL_COMMAND
383 > PYTHONPATH="$PYTHONPATH"
383 > PYTHONPATH="$PYTHONPATH"
384 > export PYTHONPATH
384 > export PYTHONPATH
385 > "$PYTHON" "$TESTDIR/../contrib/hg-ssh" "$TESTTMP/a repo"
385 > "$PYTHON" "$TESTDIR/../contrib/hg-ssh" "$TESTTMP/a repo"
386 > EOF
386 > EOF
387
387
388 $ hg id --ssh "sh ssh.sh" "ssh://user@dummy/a repo"
388 $ hg id --ssh "sh ssh.sh" "ssh://user@dummy/a repo"
389 73649e48688a
389 73649e48688a
390
390
391 $ hg id --ssh "sh ssh.sh" "ssh://user@dummy/a'repo"
391 $ hg id --ssh "sh ssh.sh" "ssh://user@dummy/a'repo"
392 remote: Illegal repository "$TESTTMP/a'repo" (glob)
392 remote: Illegal repository "$TESTTMP/a'repo" (glob)
393 abort: no suitable response from remote hg!
393 abort: no suitable response from remote hg!
394 [255]
394 [255]
395
395
396 $ hg id --ssh "sh ssh.sh" --remotecmd hacking "ssh://user@dummy/a'repo"
396 $ hg id --ssh "sh ssh.sh" --remotecmd hacking "ssh://user@dummy/a'repo"
397 remote: Illegal command "hacking -R 'a'\''repo' serve --stdio"
397 remote: Illegal command "hacking -R 'a'\''repo' serve --stdio"
398 abort: no suitable response from remote hg!
398 abort: no suitable response from remote hg!
399 [255]
399 [255]
400
400
401 $ SSH_ORIGINAL_COMMAND="'hg' -R 'a'repo' serve --stdio" $PYTHON "$TESTDIR/../contrib/hg-ssh"
401 $ SSH_ORIGINAL_COMMAND="'hg' -R 'a'repo' serve --stdio" $PYTHON "$TESTDIR/../contrib/hg-ssh"
402 Illegal command "'hg' -R 'a'repo' serve --stdio": No closing quotation
402 Illegal command "'hg' -R 'a'repo' serve --stdio": No closing quotation
403 [255]
403 [255]
404
404
405 Test hg-ssh in read-only mode:
405 Test hg-ssh in read-only mode:
406
406
407 $ cat > ssh.sh << EOF
407 $ cat > ssh.sh << EOF
408 > userhost="\$1"
408 > userhost="\$1"
409 > SSH_ORIGINAL_COMMAND="\$2"
409 > SSH_ORIGINAL_COMMAND="\$2"
410 > export SSH_ORIGINAL_COMMAND
410 > export SSH_ORIGINAL_COMMAND
411 > PYTHONPATH="$PYTHONPATH"
411 > PYTHONPATH="$PYTHONPATH"
412 > export PYTHONPATH
412 > export PYTHONPATH
413 > "$PYTHON" "$TESTDIR/../contrib/hg-ssh" --read-only "$TESTTMP/remote"
413 > "$PYTHON" "$TESTDIR/../contrib/hg-ssh" --read-only "$TESTTMP/remote"
414 > EOF
414 > EOF
415
415
416 $ hg clone --ssh "sh ssh.sh" "ssh://user@dummy/$TESTTMP/remote" read-only-local
416 $ hg clone --ssh "sh ssh.sh" "ssh://user@dummy/$TESTTMP/remote" read-only-local
417 requesting all changes
417 requesting all changes
418 adding changesets
418 adding changesets
419 adding manifests
419 adding manifests
420 adding file changes
420 adding file changes
421 added 6 changesets with 5 changes to 4 files (+1 heads)
421 added 6 changesets with 5 changes to 4 files (+1 heads)
422 new changesets 1160648e36ce:1383141674ec
422 new changesets 1160648e36ce:1383141674ec
423 updating to branch default
423 updating to branch default
424 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
424 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
425
425
426 $ cd read-only-local
426 $ cd read-only-local
427 $ echo "baz" > bar
427 $ echo "baz" > bar
428 $ hg ci -A -m "unpushable commit" bar
428 $ hg ci -A -m "unpushable commit" bar
429 $ hg push --ssh "sh ../ssh.sh"
429 $ hg push --ssh "sh ../ssh.sh"
430 pushing to ssh://user@dummy/*/remote (glob)
430 pushing to ssh://user@dummy/*/remote (glob)
431 searching for changes
431 searching for changes
432 remote: Permission denied
432 remote: Permission denied
433 remote: pretxnopen.hg-ssh hook failed
433 remote: pretxnopen.hg-ssh hook failed
434 abort: push failed on remote
434 abort: push failed on remote
435 [255]
435 [255]
436
436
437 $ cd ..
437 $ cd ..
438
438
439 stderr from remote commands should be printed before stdout from local code (issue4336)
439 stderr from remote commands should be printed before stdout from local code (issue4336)
440
440
441 $ hg clone remote stderr-ordering
441 $ hg clone remote stderr-ordering
442 updating to branch default
442 updating to branch default
443 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
443 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
444 $ cd stderr-ordering
444 $ cd stderr-ordering
445 $ cat >> localwrite.py << EOF
445 $ cat >> localwrite.py << EOF
446 > from mercurial import exchange, extensions
446 > from mercurial import exchange, extensions
447 >
447 >
448 > def wrappedpush(orig, repo, *args, **kwargs):
448 > def wrappedpush(orig, repo, *args, **kwargs):
449 > res = orig(repo, *args, **kwargs)
449 > res = orig(repo, *args, **kwargs)
450 > repo.ui.write('local stdout\n')
450 > repo.ui.write('local stdout\n')
451 > return res
451 > return res
452 >
452 >
453 > def extsetup(ui):
453 > def extsetup(ui):
454 > extensions.wrapfunction(exchange, 'push', wrappedpush)
454 > extensions.wrapfunction(exchange, 'push', wrappedpush)
455 > EOF
455 > EOF
456
456
457 $ cat >> .hg/hgrc << EOF
457 $ cat >> .hg/hgrc << EOF
458 > [paths]
458 > [paths]
459 > default-push = ssh://user@dummy/remote
459 > default-push = ssh://user@dummy/remote
460 > [ui]
460 > [ui]
461 > ssh = "$PYTHON" "$TESTDIR/dummyssh"
461 > ssh = "$PYTHON" "$TESTDIR/dummyssh"
462 > [extensions]
462 > [extensions]
463 > localwrite = localwrite.py
463 > localwrite = localwrite.py
464 > EOF
464 > EOF
465
465
466 $ echo localwrite > foo
466 $ echo localwrite > foo
467 $ hg commit -m 'testing localwrite'
467 $ hg commit -m 'testing localwrite'
468 $ hg push
468 $ hg push
469 pushing to ssh://user@dummy/remote
469 pushing to ssh://user@dummy/remote
470 searching for changes
470 searching for changes
471 remote: adding changesets
471 remote: adding changesets
472 remote: adding manifests
472 remote: adding manifests
473 remote: adding file changes
473 remote: adding file changes
474 remote: added 1 changesets with 1 changes to 1 files
474 remote: added 1 changesets with 1 changes to 1 files
475 remote: KABOOM
475 remote: KABOOM
476 remote: KABOOM IN PROCESS
476 remote: KABOOM IN PROCESS
477 local stdout
477 local stdout
478
478
479 debug output
479 debug output
480
480
481 $ hg pull --debug ssh://user@dummy/remote
481 $ hg pull --debug ssh://user@dummy/remote
482 pulling from ssh://user@dummy/remote
482 pulling from ssh://user@dummy/remote
483 running .* ".*/dummyssh" ['"]user@dummy['"] ('|")hg -R remote serve --stdio('|") (re)
483 running .* ".*/dummyssh" ['"]user@dummy['"] ('|")hg -R remote serve --stdio('|") (re)
484 sending hello command
484 sending hello command
485 sending between command
485 sending between command
486 remote: 372
486 remote: 384
487 remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS$ unbundle=HG10GZ,HG10BZ,HG10UN
487 remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS$ unbundle=HG10GZ,HG10BZ,HG10UN
488 remote: 1
488 remote: 1
489 query 1; heads
489 query 1; heads
490 sending batch command
490 sending batch command
491 searching for changes
491 searching for changes
492 all remote heads known locally
492 all remote heads known locally
493 no changes found
493 no changes found
494 sending getbundle command
494 sending getbundle command
495 bundle2-input-bundle: with-transaction
495 bundle2-input-bundle: with-transaction
496 bundle2-input-part: "listkeys" (params: 1 mandatory) supported
496 bundle2-input-part: "listkeys" (params: 1 mandatory) supported
497 bundle2-input-part: total payload size 45
497 bundle2-input-part: total payload size 45
498 bundle2-input-part: "phase-heads" supported
498 bundle2-input-part: "phase-heads" supported
499 bundle2-input-part: total payload size 72
499 bundle2-input-part: total payload size 72
500 bundle2-input-bundle: 1 parts total
500 bundle2-input-bundle: 1 parts total
501 checking for updated bookmarks
501 checking for updated bookmarks
502
502
503 $ cd ..
503 $ cd ..
504
504
505 $ cat dummylog
505 $ cat dummylog
506 Got arguments 1:user@dummy 2:hg -R nonexistent serve --stdio
506 Got arguments 1:user@dummy 2:hg -R nonexistent serve --stdio
507 Got arguments 1:user@dummy 2:hg -R $TESTTMP/nonexistent serve --stdio
507 Got arguments 1:user@dummy 2:hg -R $TESTTMP/nonexistent serve --stdio
508 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
508 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
509 Got arguments 1:user@dummy 2:hg -R local-stream serve --stdio
509 Got arguments 1:user@dummy 2:hg -R local-stream serve --stdio
510 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
510 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
511 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
511 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
512 Got arguments 1:user@dummy 2:hg -R doesnotexist serve --stdio
512 Got arguments 1:user@dummy 2:hg -R doesnotexist serve --stdio
513 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
513 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
514 Got arguments 1:user@dummy 2:hg -R local serve --stdio
514 Got arguments 1:user@dummy 2:hg -R local serve --stdio
515 Got arguments 1:user@dummy 2:hg -R $TESTTMP/local serve --stdio
515 Got arguments 1:user@dummy 2:hg -R $TESTTMP/local serve --stdio
516 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
516 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
517 changegroup-in-remote hook: HG_BUNDLE2=1 HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=a28a9d1a809cab7d4e2fde4bee738a9ede948b60 HG_NODE_LAST=a28a9d1a809cab7d4e2fde4bee738a9ede948b60 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:ssh:$LOCALIP
517 changegroup-in-remote hook: HG_BUNDLE2=1 HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=a28a9d1a809cab7d4e2fde4bee738a9ede948b60 HG_NODE_LAST=a28a9d1a809cab7d4e2fde4bee738a9ede948b60 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:ssh:$LOCALIP
518 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
518 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
519 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
519 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
520 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
520 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
521 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
521 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
522 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
522 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
523 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
523 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
524 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
524 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
525 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
525 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
526 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
526 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
527 changegroup-in-remote hook: HG_BUNDLE2=1 HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=1383141674ec756a6056f6a9097618482fe0f4a6 HG_NODE_LAST=1383141674ec756a6056f6a9097618482fe0f4a6 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:ssh:$LOCALIP
527 changegroup-in-remote hook: HG_BUNDLE2=1 HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=1383141674ec756a6056f6a9097618482fe0f4a6 HG_NODE_LAST=1383141674ec756a6056f6a9097618482fe0f4a6 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:ssh:$LOCALIP
528 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
528 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
529 Got arguments 1:user@dummy 2:hg init 'a repo'
529 Got arguments 1:user@dummy 2:hg init 'a repo'
530 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
530 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
531 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
531 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
532 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
532 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
533 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
533 Got arguments 1:user@dummy 2:hg -R 'a repo' serve --stdio
534 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
534 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
535 changegroup-in-remote hook: HG_BUNDLE2=1 HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=65c38f4125f9602c8db4af56530cc221d93b8ef8 HG_NODE_LAST=65c38f4125f9602c8db4af56530cc221d93b8ef8 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:ssh:$LOCALIP
535 changegroup-in-remote hook: HG_BUNDLE2=1 HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=65c38f4125f9602c8db4af56530cc221d93b8ef8 HG_NODE_LAST=65c38f4125f9602c8db4af56530cc221d93b8ef8 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:ssh:$LOCALIP
536 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
536 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
537
537
538 remote hook failure is attributed to remote
538 remote hook failure is attributed to remote
539
539
540 $ cat > $TESTTMP/failhook << EOF
540 $ cat > $TESTTMP/failhook << EOF
541 > def hook(ui, repo, **kwargs):
541 > def hook(ui, repo, **kwargs):
542 > ui.write('hook failure!\n')
542 > ui.write('hook failure!\n')
543 > ui.flush()
543 > ui.flush()
544 > return 1
544 > return 1
545 > EOF
545 > EOF
546
546
547 $ echo "pretxnchangegroup.fail = python:$TESTTMP/failhook:hook" >> remote/.hg/hgrc
547 $ echo "pretxnchangegroup.fail = python:$TESTTMP/failhook:hook" >> remote/.hg/hgrc
548
548
549 $ hg -q --config ui.ssh="\"$PYTHON\" $TESTDIR/dummyssh" clone ssh://user@dummy/remote hookout
549 $ hg -q --config ui.ssh="\"$PYTHON\" $TESTDIR/dummyssh" clone ssh://user@dummy/remote hookout
550 $ cd hookout
550 $ cd hookout
551 $ touch hookfailure
551 $ touch hookfailure
552 $ hg -q commit -A -m 'remote hook failure'
552 $ hg -q commit -A -m 'remote hook failure'
553 $ hg --config ui.ssh="\"$PYTHON\" $TESTDIR/dummyssh" push
553 $ hg --config ui.ssh="\"$PYTHON\" $TESTDIR/dummyssh" push
554 pushing to ssh://user@dummy/remote
554 pushing to ssh://user@dummy/remote
555 searching for changes
555 searching for changes
556 remote: adding changesets
556 remote: adding changesets
557 remote: adding manifests
557 remote: adding manifests
558 remote: adding file changes
558 remote: adding file changes
559 remote: added 1 changesets with 1 changes to 1 files
559 remote: added 1 changesets with 1 changes to 1 files
560 remote: hook failure!
560 remote: hook failure!
561 remote: transaction abort!
561 remote: transaction abort!
562 remote: rollback completed
562 remote: rollback completed
563 remote: pretxnchangegroup.fail hook failed
563 remote: pretxnchangegroup.fail hook failed
564 abort: push failed on remote
564 abort: push failed on remote
565 [255]
565 [255]
566
566
567 abort during pull is properly reported as such
567 abort during pull is properly reported as such
568
568
569 $ echo morefoo >> ../remote/foo
569 $ echo morefoo >> ../remote/foo
570 $ hg -R ../remote commit --message "more foo to be pulled"
570 $ hg -R ../remote commit --message "more foo to be pulled"
571 $ cat >> ../remote/.hg/hgrc << EOF
571 $ cat >> ../remote/.hg/hgrc << EOF
572 > [extensions]
572 > [extensions]
573 > crash = ${TESTDIR}/crashgetbundler.py
573 > crash = ${TESTDIR}/crashgetbundler.py
574 > EOF
574 > EOF
575 $ hg --config ui.ssh="\"$PYTHON\" $TESTDIR/dummyssh" pull
575 $ hg --config ui.ssh="\"$PYTHON\" $TESTDIR/dummyssh" pull
576 pulling from ssh://user@dummy/remote
576 pulling from ssh://user@dummy/remote
577 searching for changes
577 searching for changes
578 remote: abort: this is an exercise
578 remote: abort: this is an exercise
579 abort: pull failed on remote
579 abort: pull failed on remote
580 [255]
580 [255]
581
581
582 abort with no error hint when there is a ssh problem when pulling
582 abort with no error hint when there is a ssh problem when pulling
583
583
584 $ hg pull ssh://brokenrepository -e "\"$PYTHON\" \"$TESTDIR/dummyssh\""
584 $ hg pull ssh://brokenrepository -e "\"$PYTHON\" \"$TESTDIR/dummyssh\""
585 pulling from ssh://brokenrepository/
585 pulling from ssh://brokenrepository/
586 abort: no suitable response from remote hg!
586 abort: no suitable response from remote hg!
587 [255]
587 [255]
588
588
589 abort with configured error hint when there is a ssh problem when pulling
589 abort with configured error hint when there is a ssh problem when pulling
590
590
591 $ hg pull ssh://brokenrepository -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" \
591 $ hg pull ssh://brokenrepository -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" \
592 > --config ui.ssherrorhint="Please see http://company/internalwiki/ssh.html"
592 > --config ui.ssherrorhint="Please see http://company/internalwiki/ssh.html"
593 pulling from ssh://brokenrepository/
593 pulling from ssh://brokenrepository/
594 abort: no suitable response from remote hg!
594 abort: no suitable response from remote hg!
595 (Please see http://company/internalwiki/ssh.html)
595 (Please see http://company/internalwiki/ssh.html)
596 [255]
596 [255]
General Comments 0
You need to be logged in to leave comments. Login now