##// END OF EJS Templates
bundle2: don't use debug message "no-transaction" with transaction
Martin von Zweigbergk -
r32975:560ceb65 default
parent child Browse files
Show More
@@ -1,1778 +1,1778 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
148 from __future__ import absolute_import
149
149
150 import errno
150 import errno
151 import re
151 import re
152 import string
152 import string
153 import struct
153 import struct
154 import sys
154 import sys
155
155
156 from .i18n import _
156 from .i18n import _
157 from . import (
157 from . import (
158 changegroup,
158 changegroup,
159 error,
159 error,
160 obsolete,
160 obsolete,
161 pushkey,
161 pushkey,
162 pycompat,
162 pycompat,
163 tags,
163 tags,
164 url,
164 url,
165 util,
165 util,
166 )
166 )
167
167
168 urlerr = util.urlerr
168 urlerr = util.urlerr
169 urlreq = util.urlreq
169 urlreq = util.urlreq
170
170
171 _pack = struct.pack
171 _pack = struct.pack
172 _unpack = struct.unpack
172 _unpack = struct.unpack
173
173
174 _fstreamparamsize = '>i'
174 _fstreamparamsize = '>i'
175 _fpartheadersize = '>i'
175 _fpartheadersize = '>i'
176 _fparttypesize = '>B'
176 _fparttypesize = '>B'
177 _fpartid = '>I'
177 _fpartid = '>I'
178 _fpayloadsize = '>i'
178 _fpayloadsize = '>i'
179 _fpartparamcount = '>BB'
179 _fpartparamcount = '>BB'
180
180
181 preferedchunksize = 4096
181 preferedchunksize = 4096
182
182
183 _parttypeforbidden = re.compile('[^a-zA-Z0-9_:-]')
183 _parttypeforbidden = re.compile('[^a-zA-Z0-9_:-]')
184
184
185 def outdebug(ui, message):
185 def outdebug(ui, message):
186 """debug regarding output stream (bundling)"""
186 """debug regarding output stream (bundling)"""
187 if ui.configbool('devel', 'bundle2.debug', False):
187 if ui.configbool('devel', 'bundle2.debug', False):
188 ui.debug('bundle2-output: %s\n' % message)
188 ui.debug('bundle2-output: %s\n' % message)
189
189
190 def indebug(ui, message):
190 def indebug(ui, message):
191 """debug on input stream (unbundling)"""
191 """debug on input stream (unbundling)"""
192 if ui.configbool('devel', 'bundle2.debug', False):
192 if ui.configbool('devel', 'bundle2.debug', False):
193 ui.debug('bundle2-input: %s\n' % message)
193 ui.debug('bundle2-input: %s\n' % message)
194
194
195 def validateparttype(parttype):
195 def validateparttype(parttype):
196 """raise ValueError if a parttype contains invalid character"""
196 """raise ValueError if a parttype contains invalid character"""
197 if _parttypeforbidden.search(parttype):
197 if _parttypeforbidden.search(parttype):
198 raise ValueError(parttype)
198 raise ValueError(parttype)
199
199
200 def _makefpartparamsizes(nbparams):
200 def _makefpartparamsizes(nbparams):
201 """return a struct format to read part parameter sizes
201 """return a struct format to read part parameter sizes
202
202
203 The number parameters is variable so we need to build that format
203 The number parameters is variable so we need to build that format
204 dynamically.
204 dynamically.
205 """
205 """
206 return '>'+('BB'*nbparams)
206 return '>'+('BB'*nbparams)
207
207
208 parthandlermapping = {}
208 parthandlermapping = {}
209
209
210 def parthandler(parttype, params=()):
210 def parthandler(parttype, params=()):
211 """decorator that register a function as a bundle2 part handler
211 """decorator that register a function as a bundle2 part handler
212
212
213 eg::
213 eg::
214
214
215 @parthandler('myparttype', ('mandatory', 'param', 'handled'))
215 @parthandler('myparttype', ('mandatory', 'param', 'handled'))
216 def myparttypehandler(...):
216 def myparttypehandler(...):
217 '''process a part of type "my part".'''
217 '''process a part of type "my part".'''
218 ...
218 ...
219 """
219 """
220 validateparttype(parttype)
220 validateparttype(parttype)
221 def _decorator(func):
221 def _decorator(func):
222 lparttype = parttype.lower() # enforce lower case matching.
222 lparttype = parttype.lower() # enforce lower case matching.
223 assert lparttype not in parthandlermapping
223 assert lparttype not in parthandlermapping
224 parthandlermapping[lparttype] = func
224 parthandlermapping[lparttype] = func
225 func.params = frozenset(params)
225 func.params = frozenset(params)
226 return func
226 return func
227 return _decorator
227 return _decorator
228
228
229 class unbundlerecords(object):
229 class unbundlerecords(object):
230 """keep record of what happens during and unbundle
230 """keep record of what happens during and unbundle
231
231
232 New records are added using `records.add('cat', obj)`. Where 'cat' is a
232 New records are added using `records.add('cat', obj)`. Where 'cat' is a
233 category of record and obj is an arbitrary object.
233 category of record and obj is an arbitrary object.
234
234
235 `records['cat']` will return all entries of this category 'cat'.
235 `records['cat']` will return all entries of this category 'cat'.
236
236
237 Iterating on the object itself will yield `('category', obj)` tuples
237 Iterating on the object itself will yield `('category', obj)` tuples
238 for all entries.
238 for all entries.
239
239
240 All iterations happens in chronological order.
240 All iterations happens in chronological order.
241 """
241 """
242
242
243 def __init__(self):
243 def __init__(self):
244 self._categories = {}
244 self._categories = {}
245 self._sequences = []
245 self._sequences = []
246 self._replies = {}
246 self._replies = {}
247
247
248 def add(self, category, entry, inreplyto=None):
248 def add(self, category, entry, inreplyto=None):
249 """add a new record of a given category.
249 """add a new record of a given category.
250
250
251 The entry can then be retrieved in the list returned by
251 The entry can then be retrieved in the list returned by
252 self['category']."""
252 self['category']."""
253 self._categories.setdefault(category, []).append(entry)
253 self._categories.setdefault(category, []).append(entry)
254 self._sequences.append((category, entry))
254 self._sequences.append((category, entry))
255 if inreplyto is not None:
255 if inreplyto is not None:
256 self.getreplies(inreplyto).add(category, entry)
256 self.getreplies(inreplyto).add(category, entry)
257
257
258 def getreplies(self, partid):
258 def getreplies(self, partid):
259 """get the records that are replies to a specific part"""
259 """get the records that are replies to a specific part"""
260 return self._replies.setdefault(partid, unbundlerecords())
260 return self._replies.setdefault(partid, unbundlerecords())
261
261
262 def __getitem__(self, cat):
262 def __getitem__(self, cat):
263 return tuple(self._categories.get(cat, ()))
263 return tuple(self._categories.get(cat, ()))
264
264
265 def __iter__(self):
265 def __iter__(self):
266 return iter(self._sequences)
266 return iter(self._sequences)
267
267
268 def __len__(self):
268 def __len__(self):
269 return len(self._sequences)
269 return len(self._sequences)
270
270
271 def __nonzero__(self):
271 def __nonzero__(self):
272 return bool(self._sequences)
272 return bool(self._sequences)
273
273
274 __bool__ = __nonzero__
274 __bool__ = __nonzero__
275
275
276 class bundleoperation(object):
276 class bundleoperation(object):
277 """an object that represents a single bundling process
277 """an object that represents a single bundling process
278
278
279 Its purpose is to carry unbundle-related objects and states.
279 Its purpose is to carry unbundle-related objects and states.
280
280
281 A new object should be created at the beginning of each bundle processing.
281 A new object should be created at the beginning of each bundle processing.
282 The object is to be returned by the processing function.
282 The object is to be returned by the processing function.
283
283
284 The object has very little content now it will ultimately contain:
284 The object has very little content now it will ultimately contain:
285 * an access to the repo the bundle is applied to,
285 * an access to the repo the bundle is applied to,
286 * a ui object,
286 * a ui object,
287 * a way to retrieve a transaction to add changes to the repo,
287 * a way to retrieve a transaction to add changes to the repo,
288 * a way to record the result of processing each part,
288 * a way to record the result of processing each part,
289 * a way to construct a bundle response when applicable.
289 * a way to construct a bundle response when applicable.
290 """
290 """
291
291
292 def __init__(self, repo, transactiongetter, captureoutput=True):
292 def __init__(self, repo, transactiongetter, captureoutput=True):
293 self.repo = repo
293 self.repo = repo
294 self.ui = repo.ui
294 self.ui = repo.ui
295 self.records = unbundlerecords()
295 self.records = unbundlerecords()
296 self.gettransaction = transactiongetter
296 self.gettransaction = transactiongetter
297 self.reply = None
297 self.reply = None
298 self.captureoutput = captureoutput
298 self.captureoutput = captureoutput
299
299
300 class TransactionUnavailable(RuntimeError):
300 class TransactionUnavailable(RuntimeError):
301 pass
301 pass
302
302
303 def _notransaction():
303 def _notransaction():
304 """default method to get a transaction while processing a bundle
304 """default method to get a transaction while processing a bundle
305
305
306 Raise an exception to highlight the fact that no transaction was expected
306 Raise an exception to highlight the fact that no transaction was expected
307 to be created"""
307 to be created"""
308 raise TransactionUnavailable()
308 raise TransactionUnavailable()
309
309
310 def applybundle(repo, unbundler, tr, source=None, url=None, op=None):
310 def applybundle(repo, unbundler, tr, source=None, url=None, op=None):
311 # transform me into unbundler.apply() as soon as the freeze is lifted
311 # transform me into unbundler.apply() as soon as the freeze is lifted
312 tr.hookargs['bundle2'] = '1'
312 tr.hookargs['bundle2'] = '1'
313 if source is not None and 'source' not in tr.hookargs:
313 if source is not None and 'source' not in tr.hookargs:
314 tr.hookargs['source'] = source
314 tr.hookargs['source'] = source
315 if url is not None and 'url' not in tr.hookargs:
315 if url is not None and 'url' not in tr.hookargs:
316 tr.hookargs['url'] = url
316 tr.hookargs['url'] = url
317 return processbundle(repo, unbundler, lambda: tr, op=op)
317 return processbundle(repo, unbundler, lambda: tr, op=op)
318
318
319 def processbundle(repo, unbundler, transactiongetter=None, op=None):
319 def processbundle(repo, unbundler, transactiongetter=None, op=None):
320 """This function process a bundle, apply effect to/from a repo
320 """This function process a bundle, apply effect to/from a repo
321
321
322 It iterates over each part then searches for and uses the proper handling
322 It iterates over each part then searches for and uses the proper handling
323 code to process the part. Parts are processed in order.
323 code to process the part. Parts are processed in order.
324
324
325 Unknown Mandatory part will abort the process.
325 Unknown Mandatory part will abort the process.
326
326
327 It is temporarily possible to provide a prebuilt bundleoperation to the
327 It is temporarily possible to provide a prebuilt bundleoperation to the
328 function. This is used to ensure output is properly propagated in case of
328 function. This is used to ensure output is properly propagated in case of
329 an error during the unbundling. This output capturing part will likely be
329 an error during the unbundling. This output capturing part will likely be
330 reworked and this ability will probably go away in the process.
330 reworked and this ability will probably go away in the process.
331 """
331 """
332 if op is None:
332 if op is None:
333 if transactiongetter is None:
333 if transactiongetter is None:
334 transactiongetter = _notransaction
334 transactiongetter = _notransaction
335 op = bundleoperation(repo, transactiongetter)
335 op = bundleoperation(repo, transactiongetter)
336 # todo:
336 # todo:
337 # - replace this is a init function soon.
337 # - replace this is a init function soon.
338 # - exception catching
338 # - exception catching
339 unbundler.params
339 unbundler.params
340 if repo.ui.debugflag:
340 if repo.ui.debugflag:
341 msg = ['bundle2-input-bundle:']
341 msg = ['bundle2-input-bundle:']
342 if unbundler.params:
342 if unbundler.params:
343 msg.append(' %i params')
343 msg.append(' %i params')
344 if op.gettransaction is None:
344 if op.gettransaction is None or op.gettransaction is _notransaction:
345 msg.append(' no-transaction')
345 msg.append(' no-transaction')
346 else:
346 else:
347 msg.append(' with-transaction')
347 msg.append(' with-transaction')
348 msg.append('\n')
348 msg.append('\n')
349 repo.ui.debug(''.join(msg))
349 repo.ui.debug(''.join(msg))
350 iterparts = enumerate(unbundler.iterparts())
350 iterparts = enumerate(unbundler.iterparts())
351 part = None
351 part = None
352 nbpart = 0
352 nbpart = 0
353 try:
353 try:
354 for nbpart, part in iterparts:
354 for nbpart, part in iterparts:
355 _processpart(op, part)
355 _processpart(op, part)
356 except Exception as exc:
356 except Exception as exc:
357 # Any exceptions seeking to the end of the bundle at this point are
357 # Any exceptions seeking to the end of the bundle at this point are
358 # almost certainly related to the underlying stream being bad.
358 # almost certainly related to the underlying stream being bad.
359 # And, chances are that the exception we're handling is related to
359 # And, chances are that the exception we're handling is related to
360 # getting in that bad state. So, we swallow the seeking error and
360 # getting in that bad state. So, we swallow the seeking error and
361 # re-raise the original error.
361 # re-raise the original error.
362 seekerror = False
362 seekerror = False
363 try:
363 try:
364 for nbpart, part in iterparts:
364 for nbpart, part in iterparts:
365 # consume the bundle content
365 # consume the bundle content
366 part.seek(0, 2)
366 part.seek(0, 2)
367 except Exception:
367 except Exception:
368 seekerror = True
368 seekerror = True
369
369
370 # Small hack to let caller code distinguish exceptions from bundle2
370 # Small hack to let caller code distinguish exceptions from bundle2
371 # processing from processing the old format. This is mostly
371 # processing from processing the old format. This is mostly
372 # needed to handle different return codes to unbundle according to the
372 # needed to handle different return codes to unbundle according to the
373 # type of bundle. We should probably clean up or drop this return code
373 # type of bundle. We should probably clean up or drop this return code
374 # craziness in a future version.
374 # craziness in a future version.
375 exc.duringunbundle2 = True
375 exc.duringunbundle2 = True
376 salvaged = []
376 salvaged = []
377 replycaps = None
377 replycaps = None
378 if op.reply is not None:
378 if op.reply is not None:
379 salvaged = op.reply.salvageoutput()
379 salvaged = op.reply.salvageoutput()
380 replycaps = op.reply.capabilities
380 replycaps = op.reply.capabilities
381 exc._replycaps = replycaps
381 exc._replycaps = replycaps
382 exc._bundle2salvagedoutput = salvaged
382 exc._bundle2salvagedoutput = salvaged
383
383
384 # Re-raising from a variable loses the original stack. So only use
384 # Re-raising from a variable loses the original stack. So only use
385 # that form if we need to.
385 # that form if we need to.
386 if seekerror:
386 if seekerror:
387 raise exc
387 raise exc
388 else:
388 else:
389 raise
389 raise
390 finally:
390 finally:
391 repo.ui.debug('bundle2-input-bundle: %i parts total\n' % nbpart)
391 repo.ui.debug('bundle2-input-bundle: %i parts total\n' % nbpart)
392
392
393 return op
393 return op
394
394
395 def _processpart(op, part):
395 def _processpart(op, part):
396 """process a single part from a bundle
396 """process a single part from a bundle
397
397
398 The part is guaranteed to have been fully consumed when the function exits
398 The part is guaranteed to have been fully consumed when the function exits
399 (even if an exception is raised)."""
399 (even if an exception is raised)."""
400 status = 'unknown' # used by debug output
400 status = 'unknown' # used by debug output
401 hardabort = False
401 hardabort = False
402 try:
402 try:
403 try:
403 try:
404 handler = parthandlermapping.get(part.type)
404 handler = parthandlermapping.get(part.type)
405 if handler is None:
405 if handler is None:
406 status = 'unsupported-type'
406 status = 'unsupported-type'
407 raise error.BundleUnknownFeatureError(parttype=part.type)
407 raise error.BundleUnknownFeatureError(parttype=part.type)
408 indebug(op.ui, 'found a handler for part %r' % part.type)
408 indebug(op.ui, 'found a handler for part %r' % part.type)
409 unknownparams = part.mandatorykeys - handler.params
409 unknownparams = part.mandatorykeys - handler.params
410 if unknownparams:
410 if unknownparams:
411 unknownparams = list(unknownparams)
411 unknownparams = list(unknownparams)
412 unknownparams.sort()
412 unknownparams.sort()
413 status = 'unsupported-params (%s)' % unknownparams
413 status = 'unsupported-params (%s)' % unknownparams
414 raise error.BundleUnknownFeatureError(parttype=part.type,
414 raise error.BundleUnknownFeatureError(parttype=part.type,
415 params=unknownparams)
415 params=unknownparams)
416 status = 'supported'
416 status = 'supported'
417 except error.BundleUnknownFeatureError as exc:
417 except error.BundleUnknownFeatureError as exc:
418 if part.mandatory: # mandatory parts
418 if part.mandatory: # mandatory parts
419 raise
419 raise
420 indebug(op.ui, 'ignoring unsupported advisory part %s' % exc)
420 indebug(op.ui, 'ignoring unsupported advisory part %s' % exc)
421 return # skip to part processing
421 return # skip to part processing
422 finally:
422 finally:
423 if op.ui.debugflag:
423 if op.ui.debugflag:
424 msg = ['bundle2-input-part: "%s"' % part.type]
424 msg = ['bundle2-input-part: "%s"' % part.type]
425 if not part.mandatory:
425 if not part.mandatory:
426 msg.append(' (advisory)')
426 msg.append(' (advisory)')
427 nbmp = len(part.mandatorykeys)
427 nbmp = len(part.mandatorykeys)
428 nbap = len(part.params) - nbmp
428 nbap = len(part.params) - nbmp
429 if nbmp or nbap:
429 if nbmp or nbap:
430 msg.append(' (params:')
430 msg.append(' (params:')
431 if nbmp:
431 if nbmp:
432 msg.append(' %i mandatory' % nbmp)
432 msg.append(' %i mandatory' % nbmp)
433 if nbap:
433 if nbap:
434 msg.append(' %i advisory' % nbmp)
434 msg.append(' %i advisory' % nbmp)
435 msg.append(')')
435 msg.append(')')
436 msg.append(' %s\n' % status)
436 msg.append(' %s\n' % status)
437 op.ui.debug(''.join(msg))
437 op.ui.debug(''.join(msg))
438
438
439 # handler is called outside the above try block so that we don't
439 # handler is called outside the above try block so that we don't
440 # risk catching KeyErrors from anything other than the
440 # risk catching KeyErrors from anything other than the
441 # parthandlermapping lookup (any KeyError raised by handler()
441 # parthandlermapping lookup (any KeyError raised by handler()
442 # itself represents a defect of a different variety).
442 # itself represents a defect of a different variety).
443 output = None
443 output = None
444 if op.captureoutput and op.reply is not None:
444 if op.captureoutput and op.reply is not None:
445 op.ui.pushbuffer(error=True, subproc=True)
445 op.ui.pushbuffer(error=True, subproc=True)
446 output = ''
446 output = ''
447 try:
447 try:
448 handler(op, part)
448 handler(op, part)
449 finally:
449 finally:
450 if output is not None:
450 if output is not None:
451 output = op.ui.popbuffer()
451 output = op.ui.popbuffer()
452 if output:
452 if output:
453 outpart = op.reply.newpart('output', data=output,
453 outpart = op.reply.newpart('output', data=output,
454 mandatory=False)
454 mandatory=False)
455 outpart.addparam('in-reply-to', str(part.id), mandatory=False)
455 outpart.addparam('in-reply-to', str(part.id), mandatory=False)
456 # If exiting or interrupted, do not attempt to seek the stream in the
456 # If exiting or interrupted, do not attempt to seek the stream in the
457 # finally block below. This makes abort faster.
457 # finally block below. This makes abort faster.
458 except (SystemExit, KeyboardInterrupt):
458 except (SystemExit, KeyboardInterrupt):
459 hardabort = True
459 hardabort = True
460 raise
460 raise
461 finally:
461 finally:
462 # consume the part content to not corrupt the stream.
462 # consume the part content to not corrupt the stream.
463 if not hardabort:
463 if not hardabort:
464 part.seek(0, 2)
464 part.seek(0, 2)
465
465
466
466
467 def decodecaps(blob):
467 def decodecaps(blob):
468 """decode a bundle2 caps bytes blob into a dictionary
468 """decode a bundle2 caps bytes blob into a dictionary
469
469
470 The blob is a list of capabilities (one per line)
470 The blob is a list of capabilities (one per line)
471 Capabilities may have values using a line of the form::
471 Capabilities may have values using a line of the form::
472
472
473 capability=value1,value2,value3
473 capability=value1,value2,value3
474
474
475 The values are always a list."""
475 The values are always a list."""
476 caps = {}
476 caps = {}
477 for line in blob.splitlines():
477 for line in blob.splitlines():
478 if not line:
478 if not line:
479 continue
479 continue
480 if '=' not in line:
480 if '=' not in line:
481 key, vals = line, ()
481 key, vals = line, ()
482 else:
482 else:
483 key, vals = line.split('=', 1)
483 key, vals = line.split('=', 1)
484 vals = vals.split(',')
484 vals = vals.split(',')
485 key = urlreq.unquote(key)
485 key = urlreq.unquote(key)
486 vals = [urlreq.unquote(v) for v in vals]
486 vals = [urlreq.unquote(v) for v in vals]
487 caps[key] = vals
487 caps[key] = vals
488 return caps
488 return caps
489
489
490 def encodecaps(caps):
490 def encodecaps(caps):
491 """encode a bundle2 caps dictionary into a bytes blob"""
491 """encode a bundle2 caps dictionary into a bytes blob"""
492 chunks = []
492 chunks = []
493 for ca in sorted(caps):
493 for ca in sorted(caps):
494 vals = caps[ca]
494 vals = caps[ca]
495 ca = urlreq.quote(ca)
495 ca = urlreq.quote(ca)
496 vals = [urlreq.quote(v) for v in vals]
496 vals = [urlreq.quote(v) for v in vals]
497 if vals:
497 if vals:
498 ca = "%s=%s" % (ca, ','.join(vals))
498 ca = "%s=%s" % (ca, ','.join(vals))
499 chunks.append(ca)
499 chunks.append(ca)
500 return '\n'.join(chunks)
500 return '\n'.join(chunks)
501
501
502 bundletypes = {
502 bundletypes = {
503 "": ("", 'UN'), # only when using unbundle on ssh and old http servers
503 "": ("", 'UN'), # only when using unbundle on ssh and old http servers
504 # since the unification ssh accepts a header but there
504 # since the unification ssh accepts a header but there
505 # is no capability signaling it.
505 # is no capability signaling it.
506 "HG20": (), # special-cased below
506 "HG20": (), # special-cased below
507 "HG10UN": ("HG10UN", 'UN'),
507 "HG10UN": ("HG10UN", 'UN'),
508 "HG10BZ": ("HG10", 'BZ'),
508 "HG10BZ": ("HG10", 'BZ'),
509 "HG10GZ": ("HG10GZ", 'GZ'),
509 "HG10GZ": ("HG10GZ", 'GZ'),
510 }
510 }
511
511
512 # hgweb uses this list to communicate its preferred type
512 # hgweb uses this list to communicate its preferred type
513 bundlepriority = ['HG10GZ', 'HG10BZ', 'HG10UN']
513 bundlepriority = ['HG10GZ', 'HG10BZ', 'HG10UN']
514
514
515 class bundle20(object):
515 class bundle20(object):
516 """represent an outgoing bundle2 container
516 """represent an outgoing bundle2 container
517
517
518 Use the `addparam` method to add stream level parameter. and `newpart` to
518 Use the `addparam` method to add stream level parameter. and `newpart` to
519 populate it. Then call `getchunks` to retrieve all the binary chunks of
519 populate it. Then call `getchunks` to retrieve all the binary chunks of
520 data that compose the bundle2 container."""
520 data that compose the bundle2 container."""
521
521
522 _magicstring = 'HG20'
522 _magicstring = 'HG20'
523
523
524 def __init__(self, ui, capabilities=()):
524 def __init__(self, ui, capabilities=()):
525 self.ui = ui
525 self.ui = ui
526 self._params = []
526 self._params = []
527 self._parts = []
527 self._parts = []
528 self.capabilities = dict(capabilities)
528 self.capabilities = dict(capabilities)
529 self._compengine = util.compengines.forbundletype('UN')
529 self._compengine = util.compengines.forbundletype('UN')
530 self._compopts = None
530 self._compopts = None
531
531
532 def setcompression(self, alg, compopts=None):
532 def setcompression(self, alg, compopts=None):
533 """setup core part compression to <alg>"""
533 """setup core part compression to <alg>"""
534 if alg in (None, 'UN'):
534 if alg in (None, 'UN'):
535 return
535 return
536 assert not any(n.lower() == 'compression' for n, v in self._params)
536 assert not any(n.lower() == 'compression' for n, v in self._params)
537 self.addparam('Compression', alg)
537 self.addparam('Compression', alg)
538 self._compengine = util.compengines.forbundletype(alg)
538 self._compengine = util.compengines.forbundletype(alg)
539 self._compopts = compopts
539 self._compopts = compopts
540
540
541 @property
541 @property
542 def nbparts(self):
542 def nbparts(self):
543 """total number of parts added to the bundler"""
543 """total number of parts added to the bundler"""
544 return len(self._parts)
544 return len(self._parts)
545
545
546 # methods used to defines the bundle2 content
546 # methods used to defines the bundle2 content
547 def addparam(self, name, value=None):
547 def addparam(self, name, value=None):
548 """add a stream level parameter"""
548 """add a stream level parameter"""
549 if not name:
549 if not name:
550 raise ValueError('empty parameter name')
550 raise ValueError('empty parameter name')
551 if name[0] not in string.letters:
551 if name[0] not in string.letters:
552 raise ValueError('non letter first character: %r' % name)
552 raise ValueError('non letter first character: %r' % name)
553 self._params.append((name, value))
553 self._params.append((name, value))
554
554
555 def addpart(self, part):
555 def addpart(self, part):
556 """add a new part to the bundle2 container
556 """add a new part to the bundle2 container
557
557
558 Parts contains the actual applicative payload."""
558 Parts contains the actual applicative payload."""
559 assert part.id is None
559 assert part.id is None
560 part.id = len(self._parts) # very cheap counter
560 part.id = len(self._parts) # very cheap counter
561 self._parts.append(part)
561 self._parts.append(part)
562
562
563 def newpart(self, typeid, *args, **kwargs):
563 def newpart(self, typeid, *args, **kwargs):
564 """create a new part and add it to the containers
564 """create a new part and add it to the containers
565
565
566 As the part is directly added to the containers. For now, this means
566 As the part is directly added to the containers. For now, this means
567 that any failure to properly initialize the part after calling
567 that any failure to properly initialize the part after calling
568 ``newpart`` should result in a failure of the whole bundling process.
568 ``newpart`` should result in a failure of the whole bundling process.
569
569
570 You can still fall back to manually create and add if you need better
570 You can still fall back to manually create and add if you need better
571 control."""
571 control."""
572 part = bundlepart(typeid, *args, **kwargs)
572 part = bundlepart(typeid, *args, **kwargs)
573 self.addpart(part)
573 self.addpart(part)
574 return part
574 return part
575
575
576 # methods used to generate the bundle2 stream
576 # methods used to generate the bundle2 stream
577 def getchunks(self):
577 def getchunks(self):
578 if self.ui.debugflag:
578 if self.ui.debugflag:
579 msg = ['bundle2-output-bundle: "%s",' % self._magicstring]
579 msg = ['bundle2-output-bundle: "%s",' % self._magicstring]
580 if self._params:
580 if self._params:
581 msg.append(' (%i params)' % len(self._params))
581 msg.append(' (%i params)' % len(self._params))
582 msg.append(' %i parts total\n' % len(self._parts))
582 msg.append(' %i parts total\n' % len(self._parts))
583 self.ui.debug(''.join(msg))
583 self.ui.debug(''.join(msg))
584 outdebug(self.ui, 'start emission of %s stream' % self._magicstring)
584 outdebug(self.ui, 'start emission of %s stream' % self._magicstring)
585 yield self._magicstring
585 yield self._magicstring
586 param = self._paramchunk()
586 param = self._paramchunk()
587 outdebug(self.ui, 'bundle parameter: %s' % param)
587 outdebug(self.ui, 'bundle parameter: %s' % param)
588 yield _pack(_fstreamparamsize, len(param))
588 yield _pack(_fstreamparamsize, len(param))
589 if param:
589 if param:
590 yield param
590 yield param
591 for chunk in self._compengine.compressstream(self._getcorechunk(),
591 for chunk in self._compengine.compressstream(self._getcorechunk(),
592 self._compopts):
592 self._compopts):
593 yield chunk
593 yield chunk
594
594
595 def _paramchunk(self):
595 def _paramchunk(self):
596 """return a encoded version of all stream parameters"""
596 """return a encoded version of all stream parameters"""
597 blocks = []
597 blocks = []
598 for par, value in self._params:
598 for par, value in self._params:
599 par = urlreq.quote(par)
599 par = urlreq.quote(par)
600 if value is not None:
600 if value is not None:
601 value = urlreq.quote(value)
601 value = urlreq.quote(value)
602 par = '%s=%s' % (par, value)
602 par = '%s=%s' % (par, value)
603 blocks.append(par)
603 blocks.append(par)
604 return ' '.join(blocks)
604 return ' '.join(blocks)
605
605
606 def _getcorechunk(self):
606 def _getcorechunk(self):
607 """yield chunk for the core part of the bundle
607 """yield chunk for the core part of the bundle
608
608
609 (all but headers and parameters)"""
609 (all but headers and parameters)"""
610 outdebug(self.ui, 'start of parts')
610 outdebug(self.ui, 'start of parts')
611 for part in self._parts:
611 for part in self._parts:
612 outdebug(self.ui, 'bundle part: "%s"' % part.type)
612 outdebug(self.ui, 'bundle part: "%s"' % part.type)
613 for chunk in part.getchunks(ui=self.ui):
613 for chunk in part.getchunks(ui=self.ui):
614 yield chunk
614 yield chunk
615 outdebug(self.ui, 'end of bundle')
615 outdebug(self.ui, 'end of bundle')
616 yield _pack(_fpartheadersize, 0)
616 yield _pack(_fpartheadersize, 0)
617
617
618
618
619 def salvageoutput(self):
619 def salvageoutput(self):
620 """return a list with a copy of all output parts in the bundle
620 """return a list with a copy of all output parts in the bundle
621
621
622 This is meant to be used during error handling to make sure we preserve
622 This is meant to be used during error handling to make sure we preserve
623 server output"""
623 server output"""
624 salvaged = []
624 salvaged = []
625 for part in self._parts:
625 for part in self._parts:
626 if part.type.startswith('output'):
626 if part.type.startswith('output'):
627 salvaged.append(part.copy())
627 salvaged.append(part.copy())
628 return salvaged
628 return salvaged
629
629
630
630
631 class unpackermixin(object):
631 class unpackermixin(object):
632 """A mixin to extract bytes and struct data from a stream"""
632 """A mixin to extract bytes and struct data from a stream"""
633
633
634 def __init__(self, fp):
634 def __init__(self, fp):
635 self._fp = fp
635 self._fp = fp
636
636
637 def _unpack(self, format):
637 def _unpack(self, format):
638 """unpack this struct format from the stream
638 """unpack this struct format from the stream
639
639
640 This method is meant for internal usage by the bundle2 protocol only.
640 This method is meant for internal usage by the bundle2 protocol only.
641 They directly manipulate the low level stream including bundle2 level
641 They directly manipulate the low level stream including bundle2 level
642 instruction.
642 instruction.
643
643
644 Do not use it to implement higher-level logic or methods."""
644 Do not use it to implement higher-level logic or methods."""
645 data = self._readexact(struct.calcsize(format))
645 data = self._readexact(struct.calcsize(format))
646 return _unpack(format, data)
646 return _unpack(format, data)
647
647
648 def _readexact(self, size):
648 def _readexact(self, size):
649 """read exactly <size> bytes from the stream
649 """read exactly <size> bytes from the stream
650
650
651 This method is meant for internal usage by the bundle2 protocol only.
651 This method is meant for internal usage by the bundle2 protocol only.
652 They directly manipulate the low level stream including bundle2 level
652 They directly manipulate the low level stream including bundle2 level
653 instruction.
653 instruction.
654
654
655 Do not use it to implement higher-level logic or methods."""
655 Do not use it to implement higher-level logic or methods."""
656 return changegroup.readexactly(self._fp, size)
656 return changegroup.readexactly(self._fp, size)
657
657
658 def getunbundler(ui, fp, magicstring=None):
658 def getunbundler(ui, fp, magicstring=None):
659 """return a valid unbundler object for a given magicstring"""
659 """return a valid unbundler object for a given magicstring"""
660 if magicstring is None:
660 if magicstring is None:
661 magicstring = changegroup.readexactly(fp, 4)
661 magicstring = changegroup.readexactly(fp, 4)
662 magic, version = magicstring[0:2], magicstring[2:4]
662 magic, version = magicstring[0:2], magicstring[2:4]
663 if magic != 'HG':
663 if magic != 'HG':
664 raise error.Abort(_('not a Mercurial bundle'))
664 raise error.Abort(_('not a Mercurial bundle'))
665 unbundlerclass = formatmap.get(version)
665 unbundlerclass = formatmap.get(version)
666 if unbundlerclass is None:
666 if unbundlerclass is None:
667 raise error.Abort(_('unknown bundle version %s') % version)
667 raise error.Abort(_('unknown bundle version %s') % version)
668 unbundler = unbundlerclass(ui, fp)
668 unbundler = unbundlerclass(ui, fp)
669 indebug(ui, 'start processing of %s stream' % magicstring)
669 indebug(ui, 'start processing of %s stream' % magicstring)
670 return unbundler
670 return unbundler
671
671
672 class unbundle20(unpackermixin):
672 class unbundle20(unpackermixin):
673 """interpret a bundle2 stream
673 """interpret a bundle2 stream
674
674
675 This class is fed with a binary stream and yields parts through its
675 This class is fed with a binary stream and yields parts through its
676 `iterparts` methods."""
676 `iterparts` methods."""
677
677
678 _magicstring = 'HG20'
678 _magicstring = 'HG20'
679
679
680 def __init__(self, ui, fp):
680 def __init__(self, ui, fp):
681 """If header is specified, we do not read it out of the stream."""
681 """If header is specified, we do not read it out of the stream."""
682 self.ui = ui
682 self.ui = ui
683 self._compengine = util.compengines.forbundletype('UN')
683 self._compengine = util.compengines.forbundletype('UN')
684 self._compressed = None
684 self._compressed = None
685 super(unbundle20, self).__init__(fp)
685 super(unbundle20, self).__init__(fp)
686
686
687 @util.propertycache
687 @util.propertycache
688 def params(self):
688 def params(self):
689 """dictionary of stream level parameters"""
689 """dictionary of stream level parameters"""
690 indebug(self.ui, 'reading bundle2 stream parameters')
690 indebug(self.ui, 'reading bundle2 stream parameters')
691 params = {}
691 params = {}
692 paramssize = self._unpack(_fstreamparamsize)[0]
692 paramssize = self._unpack(_fstreamparamsize)[0]
693 if paramssize < 0:
693 if paramssize < 0:
694 raise error.BundleValueError('negative bundle param size: %i'
694 raise error.BundleValueError('negative bundle param size: %i'
695 % paramssize)
695 % paramssize)
696 if paramssize:
696 if paramssize:
697 params = self._readexact(paramssize)
697 params = self._readexact(paramssize)
698 params = self._processallparams(params)
698 params = self._processallparams(params)
699 return params
699 return params
700
700
701 def _processallparams(self, paramsblock):
701 def _processallparams(self, paramsblock):
702 """"""
702 """"""
703 params = util.sortdict()
703 params = util.sortdict()
704 for p in paramsblock.split(' '):
704 for p in paramsblock.split(' '):
705 p = p.split('=', 1)
705 p = p.split('=', 1)
706 p = [urlreq.unquote(i) for i in p]
706 p = [urlreq.unquote(i) for i in p]
707 if len(p) < 2:
707 if len(p) < 2:
708 p.append(None)
708 p.append(None)
709 self._processparam(*p)
709 self._processparam(*p)
710 params[p[0]] = p[1]
710 params[p[0]] = p[1]
711 return params
711 return params
712
712
713
713
714 def _processparam(self, name, value):
714 def _processparam(self, name, value):
715 """process a parameter, applying its effect if needed
715 """process a parameter, applying its effect if needed
716
716
717 Parameter starting with a lower case letter are advisory and will be
717 Parameter starting with a lower case letter are advisory and will be
718 ignored when unknown. Those starting with an upper case letter are
718 ignored when unknown. Those starting with an upper case letter are
719 mandatory and will this function will raise a KeyError when unknown.
719 mandatory and will this function will raise a KeyError when unknown.
720
720
721 Note: no option are currently supported. Any input will be either
721 Note: no option are currently supported. Any input will be either
722 ignored or failing.
722 ignored or failing.
723 """
723 """
724 if not name:
724 if not name:
725 raise ValueError('empty parameter name')
725 raise ValueError('empty parameter name')
726 if name[0] not in string.letters:
726 if name[0] not in string.letters:
727 raise ValueError('non letter first character: %r' % name)
727 raise ValueError('non letter first character: %r' % name)
728 try:
728 try:
729 handler = b2streamparamsmap[name.lower()]
729 handler = b2streamparamsmap[name.lower()]
730 except KeyError:
730 except KeyError:
731 if name[0].islower():
731 if name[0].islower():
732 indebug(self.ui, "ignoring unknown parameter %r" % name)
732 indebug(self.ui, "ignoring unknown parameter %r" % name)
733 else:
733 else:
734 raise error.BundleUnknownFeatureError(params=(name,))
734 raise error.BundleUnknownFeatureError(params=(name,))
735 else:
735 else:
736 handler(self, name, value)
736 handler(self, name, value)
737
737
738 def _forwardchunks(self):
738 def _forwardchunks(self):
739 """utility to transfer a bundle2 as binary
739 """utility to transfer a bundle2 as binary
740
740
741 This is made necessary by the fact the 'getbundle' command over 'ssh'
741 This is made necessary by the fact the 'getbundle' command over 'ssh'
742 have no way to know then the reply end, relying on the bundle to be
742 have no way to know then the reply end, relying on the bundle to be
743 interpreted to know its end. This is terrible and we are sorry, but we
743 interpreted to know its end. This is terrible and we are sorry, but we
744 needed to move forward to get general delta enabled.
744 needed to move forward to get general delta enabled.
745 """
745 """
746 yield self._magicstring
746 yield self._magicstring
747 assert 'params' not in vars(self)
747 assert 'params' not in vars(self)
748 paramssize = self._unpack(_fstreamparamsize)[0]
748 paramssize = self._unpack(_fstreamparamsize)[0]
749 if paramssize < 0:
749 if paramssize < 0:
750 raise error.BundleValueError('negative bundle param size: %i'
750 raise error.BundleValueError('negative bundle param size: %i'
751 % paramssize)
751 % paramssize)
752 yield _pack(_fstreamparamsize, paramssize)
752 yield _pack(_fstreamparamsize, paramssize)
753 if paramssize:
753 if paramssize:
754 params = self._readexact(paramssize)
754 params = self._readexact(paramssize)
755 self._processallparams(params)
755 self._processallparams(params)
756 yield params
756 yield params
757 assert self._compengine.bundletype == 'UN'
757 assert self._compengine.bundletype == 'UN'
758 # From there, payload might need to be decompressed
758 # From there, payload might need to be decompressed
759 self._fp = self._compengine.decompressorreader(self._fp)
759 self._fp = self._compengine.decompressorreader(self._fp)
760 emptycount = 0
760 emptycount = 0
761 while emptycount < 2:
761 while emptycount < 2:
762 # so we can brainlessly loop
762 # so we can brainlessly loop
763 assert _fpartheadersize == _fpayloadsize
763 assert _fpartheadersize == _fpayloadsize
764 size = self._unpack(_fpartheadersize)[0]
764 size = self._unpack(_fpartheadersize)[0]
765 yield _pack(_fpartheadersize, size)
765 yield _pack(_fpartheadersize, size)
766 if size:
766 if size:
767 emptycount = 0
767 emptycount = 0
768 else:
768 else:
769 emptycount += 1
769 emptycount += 1
770 continue
770 continue
771 if size == flaginterrupt:
771 if size == flaginterrupt:
772 continue
772 continue
773 elif size < 0:
773 elif size < 0:
774 raise error.BundleValueError('negative chunk size: %i')
774 raise error.BundleValueError('negative chunk size: %i')
775 yield self._readexact(size)
775 yield self._readexact(size)
776
776
777
777
778 def iterparts(self):
778 def iterparts(self):
779 """yield all parts contained in the stream"""
779 """yield all parts contained in the stream"""
780 # make sure param have been loaded
780 # make sure param have been loaded
781 self.params
781 self.params
782 # From there, payload need to be decompressed
782 # From there, payload need to be decompressed
783 self._fp = self._compengine.decompressorreader(self._fp)
783 self._fp = self._compengine.decompressorreader(self._fp)
784 indebug(self.ui, 'start extraction of bundle2 parts')
784 indebug(self.ui, 'start extraction of bundle2 parts')
785 headerblock = self._readpartheader()
785 headerblock = self._readpartheader()
786 while headerblock is not None:
786 while headerblock is not None:
787 part = unbundlepart(self.ui, headerblock, self._fp)
787 part = unbundlepart(self.ui, headerblock, self._fp)
788 yield part
788 yield part
789 part.seek(0, 2)
789 part.seek(0, 2)
790 headerblock = self._readpartheader()
790 headerblock = self._readpartheader()
791 indebug(self.ui, 'end of bundle2 stream')
791 indebug(self.ui, 'end of bundle2 stream')
792
792
793 def _readpartheader(self):
793 def _readpartheader(self):
794 """reads a part header size and return the bytes blob
794 """reads a part header size and return the bytes blob
795
795
796 returns None if empty"""
796 returns None if empty"""
797 headersize = self._unpack(_fpartheadersize)[0]
797 headersize = self._unpack(_fpartheadersize)[0]
798 if headersize < 0:
798 if headersize < 0:
799 raise error.BundleValueError('negative part header size: %i'
799 raise error.BundleValueError('negative part header size: %i'
800 % headersize)
800 % headersize)
801 indebug(self.ui, 'part header size: %i' % headersize)
801 indebug(self.ui, 'part header size: %i' % headersize)
802 if headersize:
802 if headersize:
803 return self._readexact(headersize)
803 return self._readexact(headersize)
804 return None
804 return None
805
805
806 def compressed(self):
806 def compressed(self):
807 self.params # load params
807 self.params # load params
808 return self._compressed
808 return self._compressed
809
809
810 def close(self):
810 def close(self):
811 """close underlying file"""
811 """close underlying file"""
812 if util.safehasattr(self._fp, 'close'):
812 if util.safehasattr(self._fp, 'close'):
813 return self._fp.close()
813 return self._fp.close()
814
814
815 formatmap = {'20': unbundle20}
815 formatmap = {'20': unbundle20}
816
816
817 b2streamparamsmap = {}
817 b2streamparamsmap = {}
818
818
819 def b2streamparamhandler(name):
819 def b2streamparamhandler(name):
820 """register a handler for a stream level parameter"""
820 """register a handler for a stream level parameter"""
821 def decorator(func):
821 def decorator(func):
822 assert name not in formatmap
822 assert name not in formatmap
823 b2streamparamsmap[name] = func
823 b2streamparamsmap[name] = func
824 return func
824 return func
825 return decorator
825 return decorator
826
826
827 @b2streamparamhandler('compression')
827 @b2streamparamhandler('compression')
828 def processcompression(unbundler, param, value):
828 def processcompression(unbundler, param, value):
829 """read compression parameter and install payload decompression"""
829 """read compression parameter and install payload decompression"""
830 if value not in util.compengines.supportedbundletypes:
830 if value not in util.compengines.supportedbundletypes:
831 raise error.BundleUnknownFeatureError(params=(param,),
831 raise error.BundleUnknownFeatureError(params=(param,),
832 values=(value,))
832 values=(value,))
833 unbundler._compengine = util.compengines.forbundletype(value)
833 unbundler._compengine = util.compengines.forbundletype(value)
834 if value is not None:
834 if value is not None:
835 unbundler._compressed = True
835 unbundler._compressed = True
836
836
837 class bundlepart(object):
837 class bundlepart(object):
838 """A bundle2 part contains application level payload
838 """A bundle2 part contains application level payload
839
839
840 The part `type` is used to route the part to the application level
840 The part `type` is used to route the part to the application level
841 handler.
841 handler.
842
842
843 The part payload is contained in ``part.data``. It could be raw bytes or a
843 The part payload is contained in ``part.data``. It could be raw bytes or a
844 generator of byte chunks.
844 generator of byte chunks.
845
845
846 You can add parameters to the part using the ``addparam`` method.
846 You can add parameters to the part using the ``addparam`` method.
847 Parameters can be either mandatory (default) or advisory. Remote side
847 Parameters can be either mandatory (default) or advisory. Remote side
848 should be able to safely ignore the advisory ones.
848 should be able to safely ignore the advisory ones.
849
849
850 Both data and parameters cannot be modified after the generation has begun.
850 Both data and parameters cannot be modified after the generation has begun.
851 """
851 """
852
852
853 def __init__(self, parttype, mandatoryparams=(), advisoryparams=(),
853 def __init__(self, parttype, mandatoryparams=(), advisoryparams=(),
854 data='', mandatory=True):
854 data='', mandatory=True):
855 validateparttype(parttype)
855 validateparttype(parttype)
856 self.id = None
856 self.id = None
857 self.type = parttype
857 self.type = parttype
858 self._data = data
858 self._data = data
859 self._mandatoryparams = list(mandatoryparams)
859 self._mandatoryparams = list(mandatoryparams)
860 self._advisoryparams = list(advisoryparams)
860 self._advisoryparams = list(advisoryparams)
861 # checking for duplicated entries
861 # checking for duplicated entries
862 self._seenparams = set()
862 self._seenparams = set()
863 for pname, __ in self._mandatoryparams + self._advisoryparams:
863 for pname, __ in self._mandatoryparams + self._advisoryparams:
864 if pname in self._seenparams:
864 if pname in self._seenparams:
865 raise error.ProgrammingError('duplicated params: %s' % pname)
865 raise error.ProgrammingError('duplicated params: %s' % pname)
866 self._seenparams.add(pname)
866 self._seenparams.add(pname)
867 # status of the part's generation:
867 # status of the part's generation:
868 # - None: not started,
868 # - None: not started,
869 # - False: currently generated,
869 # - False: currently generated,
870 # - True: generation done.
870 # - True: generation done.
871 self._generated = None
871 self._generated = None
872 self.mandatory = mandatory
872 self.mandatory = mandatory
873
873
874 def __repr__(self):
874 def __repr__(self):
875 cls = "%s.%s" % (self.__class__.__module__, self.__class__.__name__)
875 cls = "%s.%s" % (self.__class__.__module__, self.__class__.__name__)
876 return ('<%s object at %x; id: %s; type: %s; mandatory: %s>'
876 return ('<%s object at %x; id: %s; type: %s; mandatory: %s>'
877 % (cls, id(self), self.id, self.type, self.mandatory))
877 % (cls, id(self), self.id, self.type, self.mandatory))
878
878
879 def copy(self):
879 def copy(self):
880 """return a copy of the part
880 """return a copy of the part
881
881
882 The new part have the very same content but no partid assigned yet.
882 The new part have the very same content but no partid assigned yet.
883 Parts with generated data cannot be copied."""
883 Parts with generated data cannot be copied."""
884 assert not util.safehasattr(self.data, 'next')
884 assert not util.safehasattr(self.data, 'next')
885 return self.__class__(self.type, self._mandatoryparams,
885 return self.__class__(self.type, self._mandatoryparams,
886 self._advisoryparams, self._data, self.mandatory)
886 self._advisoryparams, self._data, self.mandatory)
887
887
888 # methods used to defines the part content
888 # methods used to defines the part content
889 @property
889 @property
890 def data(self):
890 def data(self):
891 return self._data
891 return self._data
892
892
893 @data.setter
893 @data.setter
894 def data(self, data):
894 def data(self, data):
895 if self._generated is not None:
895 if self._generated is not None:
896 raise error.ReadOnlyPartError('part is being generated')
896 raise error.ReadOnlyPartError('part is being generated')
897 self._data = data
897 self._data = data
898
898
899 @property
899 @property
900 def mandatoryparams(self):
900 def mandatoryparams(self):
901 # make it an immutable tuple to force people through ``addparam``
901 # make it an immutable tuple to force people through ``addparam``
902 return tuple(self._mandatoryparams)
902 return tuple(self._mandatoryparams)
903
903
904 @property
904 @property
905 def advisoryparams(self):
905 def advisoryparams(self):
906 # make it an immutable tuple to force people through ``addparam``
906 # make it an immutable tuple to force people through ``addparam``
907 return tuple(self._advisoryparams)
907 return tuple(self._advisoryparams)
908
908
909 def addparam(self, name, value='', mandatory=True):
909 def addparam(self, name, value='', mandatory=True):
910 """add a parameter to the part
910 """add a parameter to the part
911
911
912 If 'mandatory' is set to True, the remote handler must claim support
912 If 'mandatory' is set to True, the remote handler must claim support
913 for this parameter or the unbundling will be aborted.
913 for this parameter or the unbundling will be aborted.
914
914
915 The 'name' and 'value' cannot exceed 255 bytes each.
915 The 'name' and 'value' cannot exceed 255 bytes each.
916 """
916 """
917 if self._generated is not None:
917 if self._generated is not None:
918 raise error.ReadOnlyPartError('part is being generated')
918 raise error.ReadOnlyPartError('part is being generated')
919 if name in self._seenparams:
919 if name in self._seenparams:
920 raise ValueError('duplicated params: %s' % name)
920 raise ValueError('duplicated params: %s' % name)
921 self._seenparams.add(name)
921 self._seenparams.add(name)
922 params = self._advisoryparams
922 params = self._advisoryparams
923 if mandatory:
923 if mandatory:
924 params = self._mandatoryparams
924 params = self._mandatoryparams
925 params.append((name, value))
925 params.append((name, value))
926
926
927 # methods used to generates the bundle2 stream
927 # methods used to generates the bundle2 stream
928 def getchunks(self, ui):
928 def getchunks(self, ui):
929 if self._generated is not None:
929 if self._generated is not None:
930 raise error.ProgrammingError('part can only be consumed once')
930 raise error.ProgrammingError('part can only be consumed once')
931 self._generated = False
931 self._generated = False
932
932
933 if ui.debugflag:
933 if ui.debugflag:
934 msg = ['bundle2-output-part: "%s"' % self.type]
934 msg = ['bundle2-output-part: "%s"' % self.type]
935 if not self.mandatory:
935 if not self.mandatory:
936 msg.append(' (advisory)')
936 msg.append(' (advisory)')
937 nbmp = len(self.mandatoryparams)
937 nbmp = len(self.mandatoryparams)
938 nbap = len(self.advisoryparams)
938 nbap = len(self.advisoryparams)
939 if nbmp or nbap:
939 if nbmp or nbap:
940 msg.append(' (params:')
940 msg.append(' (params:')
941 if nbmp:
941 if nbmp:
942 msg.append(' %i mandatory' % nbmp)
942 msg.append(' %i mandatory' % nbmp)
943 if nbap:
943 if nbap:
944 msg.append(' %i advisory' % nbmp)
944 msg.append(' %i advisory' % nbmp)
945 msg.append(')')
945 msg.append(')')
946 if not self.data:
946 if not self.data:
947 msg.append(' empty payload')
947 msg.append(' empty payload')
948 elif util.safehasattr(self.data, 'next'):
948 elif util.safehasattr(self.data, 'next'):
949 msg.append(' streamed payload')
949 msg.append(' streamed payload')
950 else:
950 else:
951 msg.append(' %i bytes payload' % len(self.data))
951 msg.append(' %i bytes payload' % len(self.data))
952 msg.append('\n')
952 msg.append('\n')
953 ui.debug(''.join(msg))
953 ui.debug(''.join(msg))
954
954
955 #### header
955 #### header
956 if self.mandatory:
956 if self.mandatory:
957 parttype = self.type.upper()
957 parttype = self.type.upper()
958 else:
958 else:
959 parttype = self.type.lower()
959 parttype = self.type.lower()
960 outdebug(ui, 'part %s: "%s"' % (self.id, parttype))
960 outdebug(ui, 'part %s: "%s"' % (self.id, parttype))
961 ## parttype
961 ## parttype
962 header = [_pack(_fparttypesize, len(parttype)),
962 header = [_pack(_fparttypesize, len(parttype)),
963 parttype, _pack(_fpartid, self.id),
963 parttype, _pack(_fpartid, self.id),
964 ]
964 ]
965 ## parameters
965 ## parameters
966 # count
966 # count
967 manpar = self.mandatoryparams
967 manpar = self.mandatoryparams
968 advpar = self.advisoryparams
968 advpar = self.advisoryparams
969 header.append(_pack(_fpartparamcount, len(manpar), len(advpar)))
969 header.append(_pack(_fpartparamcount, len(manpar), len(advpar)))
970 # size
970 # size
971 parsizes = []
971 parsizes = []
972 for key, value in manpar:
972 for key, value in manpar:
973 parsizes.append(len(key))
973 parsizes.append(len(key))
974 parsizes.append(len(value))
974 parsizes.append(len(value))
975 for key, value in advpar:
975 for key, value in advpar:
976 parsizes.append(len(key))
976 parsizes.append(len(key))
977 parsizes.append(len(value))
977 parsizes.append(len(value))
978 paramsizes = _pack(_makefpartparamsizes(len(parsizes) / 2), *parsizes)
978 paramsizes = _pack(_makefpartparamsizes(len(parsizes) / 2), *parsizes)
979 header.append(paramsizes)
979 header.append(paramsizes)
980 # key, value
980 # key, value
981 for key, value in manpar:
981 for key, value in manpar:
982 header.append(key)
982 header.append(key)
983 header.append(value)
983 header.append(value)
984 for key, value in advpar:
984 for key, value in advpar:
985 header.append(key)
985 header.append(key)
986 header.append(value)
986 header.append(value)
987 ## finalize header
987 ## finalize header
988 headerchunk = ''.join(header)
988 headerchunk = ''.join(header)
989 outdebug(ui, 'header chunk size: %i' % len(headerchunk))
989 outdebug(ui, 'header chunk size: %i' % len(headerchunk))
990 yield _pack(_fpartheadersize, len(headerchunk))
990 yield _pack(_fpartheadersize, len(headerchunk))
991 yield headerchunk
991 yield headerchunk
992 ## payload
992 ## payload
993 try:
993 try:
994 for chunk in self._payloadchunks():
994 for chunk in self._payloadchunks():
995 outdebug(ui, 'payload chunk size: %i' % len(chunk))
995 outdebug(ui, 'payload chunk size: %i' % len(chunk))
996 yield _pack(_fpayloadsize, len(chunk))
996 yield _pack(_fpayloadsize, len(chunk))
997 yield chunk
997 yield chunk
998 except GeneratorExit:
998 except GeneratorExit:
999 # GeneratorExit means that nobody is listening for our
999 # GeneratorExit means that nobody is listening for our
1000 # results anyway, so just bail quickly rather than trying
1000 # results anyway, so just bail quickly rather than trying
1001 # to produce an error part.
1001 # to produce an error part.
1002 ui.debug('bundle2-generatorexit\n')
1002 ui.debug('bundle2-generatorexit\n')
1003 raise
1003 raise
1004 except BaseException as exc:
1004 except BaseException as exc:
1005 # backup exception data for later
1005 # backup exception data for later
1006 ui.debug('bundle2-input-stream-interrupt: encoding exception %s'
1006 ui.debug('bundle2-input-stream-interrupt: encoding exception %s'
1007 % exc)
1007 % exc)
1008 tb = sys.exc_info()[2]
1008 tb = sys.exc_info()[2]
1009 msg = 'unexpected error: %s' % exc
1009 msg = 'unexpected error: %s' % exc
1010 interpart = bundlepart('error:abort', [('message', msg)],
1010 interpart = bundlepart('error:abort', [('message', msg)],
1011 mandatory=False)
1011 mandatory=False)
1012 interpart.id = 0
1012 interpart.id = 0
1013 yield _pack(_fpayloadsize, -1)
1013 yield _pack(_fpayloadsize, -1)
1014 for chunk in interpart.getchunks(ui=ui):
1014 for chunk in interpart.getchunks(ui=ui):
1015 yield chunk
1015 yield chunk
1016 outdebug(ui, 'closing payload chunk')
1016 outdebug(ui, 'closing payload chunk')
1017 # abort current part payload
1017 # abort current part payload
1018 yield _pack(_fpayloadsize, 0)
1018 yield _pack(_fpayloadsize, 0)
1019 pycompat.raisewithtb(exc, tb)
1019 pycompat.raisewithtb(exc, tb)
1020 # end of payload
1020 # end of payload
1021 outdebug(ui, 'closing payload chunk')
1021 outdebug(ui, 'closing payload chunk')
1022 yield _pack(_fpayloadsize, 0)
1022 yield _pack(_fpayloadsize, 0)
1023 self._generated = True
1023 self._generated = True
1024
1024
1025 def _payloadchunks(self):
1025 def _payloadchunks(self):
1026 """yield chunks of a the part payload
1026 """yield chunks of a the part payload
1027
1027
1028 Exists to handle the different methods to provide data to a part."""
1028 Exists to handle the different methods to provide data to a part."""
1029 # we only support fixed size data now.
1029 # we only support fixed size data now.
1030 # This will be improved in the future.
1030 # This will be improved in the future.
1031 if util.safehasattr(self.data, 'next'):
1031 if util.safehasattr(self.data, 'next'):
1032 buff = util.chunkbuffer(self.data)
1032 buff = util.chunkbuffer(self.data)
1033 chunk = buff.read(preferedchunksize)
1033 chunk = buff.read(preferedchunksize)
1034 while chunk:
1034 while chunk:
1035 yield chunk
1035 yield chunk
1036 chunk = buff.read(preferedchunksize)
1036 chunk = buff.read(preferedchunksize)
1037 elif len(self.data):
1037 elif len(self.data):
1038 yield self.data
1038 yield self.data
1039
1039
1040
1040
1041 flaginterrupt = -1
1041 flaginterrupt = -1
1042
1042
1043 class interrupthandler(unpackermixin):
1043 class interrupthandler(unpackermixin):
1044 """read one part and process it with restricted capability
1044 """read one part and process it with restricted capability
1045
1045
1046 This allows to transmit exception raised on the producer size during part
1046 This allows to transmit exception raised on the producer size during part
1047 iteration while the consumer is reading a part.
1047 iteration while the consumer is reading a part.
1048
1048
1049 Part processed in this manner only have access to a ui object,"""
1049 Part processed in this manner only have access to a ui object,"""
1050
1050
1051 def __init__(self, ui, fp):
1051 def __init__(self, ui, fp):
1052 super(interrupthandler, self).__init__(fp)
1052 super(interrupthandler, self).__init__(fp)
1053 self.ui = ui
1053 self.ui = ui
1054
1054
1055 def _readpartheader(self):
1055 def _readpartheader(self):
1056 """reads a part header size and return the bytes blob
1056 """reads a part header size and return the bytes blob
1057
1057
1058 returns None if empty"""
1058 returns None if empty"""
1059 headersize = self._unpack(_fpartheadersize)[0]
1059 headersize = self._unpack(_fpartheadersize)[0]
1060 if headersize < 0:
1060 if headersize < 0:
1061 raise error.BundleValueError('negative part header size: %i'
1061 raise error.BundleValueError('negative part header size: %i'
1062 % headersize)
1062 % headersize)
1063 indebug(self.ui, 'part header size: %i\n' % headersize)
1063 indebug(self.ui, 'part header size: %i\n' % headersize)
1064 if headersize:
1064 if headersize:
1065 return self._readexact(headersize)
1065 return self._readexact(headersize)
1066 return None
1066 return None
1067
1067
1068 def __call__(self):
1068 def __call__(self):
1069
1069
1070 self.ui.debug('bundle2-input-stream-interrupt:'
1070 self.ui.debug('bundle2-input-stream-interrupt:'
1071 ' opening out of band context\n')
1071 ' opening out of band context\n')
1072 indebug(self.ui, 'bundle2 stream interruption, looking for a part.')
1072 indebug(self.ui, 'bundle2 stream interruption, looking for a part.')
1073 headerblock = self._readpartheader()
1073 headerblock = self._readpartheader()
1074 if headerblock is None:
1074 if headerblock is None:
1075 indebug(self.ui, 'no part found during interruption.')
1075 indebug(self.ui, 'no part found during interruption.')
1076 return
1076 return
1077 part = unbundlepart(self.ui, headerblock, self._fp)
1077 part = unbundlepart(self.ui, headerblock, self._fp)
1078 op = interruptoperation(self.ui)
1078 op = interruptoperation(self.ui)
1079 _processpart(op, part)
1079 _processpart(op, part)
1080 self.ui.debug('bundle2-input-stream-interrupt:'
1080 self.ui.debug('bundle2-input-stream-interrupt:'
1081 ' closing out of band context\n')
1081 ' closing out of band context\n')
1082
1082
1083 class interruptoperation(object):
1083 class interruptoperation(object):
1084 """A limited operation to be use by part handler during interruption
1084 """A limited operation to be use by part handler during interruption
1085
1085
1086 It only have access to an ui object.
1086 It only have access to an ui object.
1087 """
1087 """
1088
1088
1089 def __init__(self, ui):
1089 def __init__(self, ui):
1090 self.ui = ui
1090 self.ui = ui
1091 self.reply = None
1091 self.reply = None
1092 self.captureoutput = False
1092 self.captureoutput = False
1093
1093
1094 @property
1094 @property
1095 def repo(self):
1095 def repo(self):
1096 raise error.ProgrammingError('no repo access from stream interruption')
1096 raise error.ProgrammingError('no repo access from stream interruption')
1097
1097
1098 def gettransaction(self):
1098 def gettransaction(self):
1099 raise TransactionUnavailable('no repo access from stream interruption')
1099 raise TransactionUnavailable('no repo access from stream interruption')
1100
1100
1101 class unbundlepart(unpackermixin):
1101 class unbundlepart(unpackermixin):
1102 """a bundle part read from a bundle"""
1102 """a bundle part read from a bundle"""
1103
1103
1104 def __init__(self, ui, header, fp):
1104 def __init__(self, ui, header, fp):
1105 super(unbundlepart, self).__init__(fp)
1105 super(unbundlepart, self).__init__(fp)
1106 self._seekable = (util.safehasattr(fp, 'seek') and
1106 self._seekable = (util.safehasattr(fp, 'seek') and
1107 util.safehasattr(fp, 'tell'))
1107 util.safehasattr(fp, 'tell'))
1108 self.ui = ui
1108 self.ui = ui
1109 # unbundle state attr
1109 # unbundle state attr
1110 self._headerdata = header
1110 self._headerdata = header
1111 self._headeroffset = 0
1111 self._headeroffset = 0
1112 self._initialized = False
1112 self._initialized = False
1113 self.consumed = False
1113 self.consumed = False
1114 # part data
1114 # part data
1115 self.id = None
1115 self.id = None
1116 self.type = None
1116 self.type = None
1117 self.mandatoryparams = None
1117 self.mandatoryparams = None
1118 self.advisoryparams = None
1118 self.advisoryparams = None
1119 self.params = None
1119 self.params = None
1120 self.mandatorykeys = ()
1120 self.mandatorykeys = ()
1121 self._payloadstream = None
1121 self._payloadstream = None
1122 self._readheader()
1122 self._readheader()
1123 self._mandatory = None
1123 self._mandatory = None
1124 self._chunkindex = [] #(payload, file) position tuples for chunk starts
1124 self._chunkindex = [] #(payload, file) position tuples for chunk starts
1125 self._pos = 0
1125 self._pos = 0
1126
1126
1127 def _fromheader(self, size):
1127 def _fromheader(self, size):
1128 """return the next <size> byte from the header"""
1128 """return the next <size> byte from the header"""
1129 offset = self._headeroffset
1129 offset = self._headeroffset
1130 data = self._headerdata[offset:(offset + size)]
1130 data = self._headerdata[offset:(offset + size)]
1131 self._headeroffset = offset + size
1131 self._headeroffset = offset + size
1132 return data
1132 return data
1133
1133
1134 def _unpackheader(self, format):
1134 def _unpackheader(self, format):
1135 """read given format from header
1135 """read given format from header
1136
1136
1137 This automatically compute the size of the format to read."""
1137 This automatically compute the size of the format to read."""
1138 data = self._fromheader(struct.calcsize(format))
1138 data = self._fromheader(struct.calcsize(format))
1139 return _unpack(format, data)
1139 return _unpack(format, data)
1140
1140
1141 def _initparams(self, mandatoryparams, advisoryparams):
1141 def _initparams(self, mandatoryparams, advisoryparams):
1142 """internal function to setup all logic related parameters"""
1142 """internal function to setup all logic related parameters"""
1143 # make it read only to prevent people touching it by mistake.
1143 # make it read only to prevent people touching it by mistake.
1144 self.mandatoryparams = tuple(mandatoryparams)
1144 self.mandatoryparams = tuple(mandatoryparams)
1145 self.advisoryparams = tuple(advisoryparams)
1145 self.advisoryparams = tuple(advisoryparams)
1146 # user friendly UI
1146 # user friendly UI
1147 self.params = util.sortdict(self.mandatoryparams)
1147 self.params = util.sortdict(self.mandatoryparams)
1148 self.params.update(self.advisoryparams)
1148 self.params.update(self.advisoryparams)
1149 self.mandatorykeys = frozenset(p[0] for p in mandatoryparams)
1149 self.mandatorykeys = frozenset(p[0] for p in mandatoryparams)
1150
1150
1151 def _payloadchunks(self, chunknum=0):
1151 def _payloadchunks(self, chunknum=0):
1152 '''seek to specified chunk and start yielding data'''
1152 '''seek to specified chunk and start yielding data'''
1153 if len(self._chunkindex) == 0:
1153 if len(self._chunkindex) == 0:
1154 assert chunknum == 0, 'Must start with chunk 0'
1154 assert chunknum == 0, 'Must start with chunk 0'
1155 self._chunkindex.append((0, self._tellfp()))
1155 self._chunkindex.append((0, self._tellfp()))
1156 else:
1156 else:
1157 assert chunknum < len(self._chunkindex), \
1157 assert chunknum < len(self._chunkindex), \
1158 'Unknown chunk %d' % chunknum
1158 'Unknown chunk %d' % chunknum
1159 self._seekfp(self._chunkindex[chunknum][1])
1159 self._seekfp(self._chunkindex[chunknum][1])
1160
1160
1161 pos = self._chunkindex[chunknum][0]
1161 pos = self._chunkindex[chunknum][0]
1162 payloadsize = self._unpack(_fpayloadsize)[0]
1162 payloadsize = self._unpack(_fpayloadsize)[0]
1163 indebug(self.ui, 'payload chunk size: %i' % payloadsize)
1163 indebug(self.ui, 'payload chunk size: %i' % payloadsize)
1164 while payloadsize:
1164 while payloadsize:
1165 if payloadsize == flaginterrupt:
1165 if payloadsize == flaginterrupt:
1166 # interruption detection, the handler will now read a
1166 # interruption detection, the handler will now read a
1167 # single part and process it.
1167 # single part and process it.
1168 interrupthandler(self.ui, self._fp)()
1168 interrupthandler(self.ui, self._fp)()
1169 elif payloadsize < 0:
1169 elif payloadsize < 0:
1170 msg = 'negative payload chunk size: %i' % payloadsize
1170 msg = 'negative payload chunk size: %i' % payloadsize
1171 raise error.BundleValueError(msg)
1171 raise error.BundleValueError(msg)
1172 else:
1172 else:
1173 result = self._readexact(payloadsize)
1173 result = self._readexact(payloadsize)
1174 chunknum += 1
1174 chunknum += 1
1175 pos += payloadsize
1175 pos += payloadsize
1176 if chunknum == len(self._chunkindex):
1176 if chunknum == len(self._chunkindex):
1177 self._chunkindex.append((pos, self._tellfp()))
1177 self._chunkindex.append((pos, self._tellfp()))
1178 yield result
1178 yield result
1179 payloadsize = self._unpack(_fpayloadsize)[0]
1179 payloadsize = self._unpack(_fpayloadsize)[0]
1180 indebug(self.ui, 'payload chunk size: %i' % payloadsize)
1180 indebug(self.ui, 'payload chunk size: %i' % payloadsize)
1181
1181
1182 def _findchunk(self, pos):
1182 def _findchunk(self, pos):
1183 '''for a given payload position, return a chunk number and offset'''
1183 '''for a given payload position, return a chunk number and offset'''
1184 for chunk, (ppos, fpos) in enumerate(self._chunkindex):
1184 for chunk, (ppos, fpos) in enumerate(self._chunkindex):
1185 if ppos == pos:
1185 if ppos == pos:
1186 return chunk, 0
1186 return chunk, 0
1187 elif ppos > pos:
1187 elif ppos > pos:
1188 return chunk - 1, pos - self._chunkindex[chunk - 1][0]
1188 return chunk - 1, pos - self._chunkindex[chunk - 1][0]
1189 raise ValueError('Unknown chunk')
1189 raise ValueError('Unknown chunk')
1190
1190
1191 def _readheader(self):
1191 def _readheader(self):
1192 """read the header and setup the object"""
1192 """read the header and setup the object"""
1193 typesize = self._unpackheader(_fparttypesize)[0]
1193 typesize = self._unpackheader(_fparttypesize)[0]
1194 self.type = self._fromheader(typesize)
1194 self.type = self._fromheader(typesize)
1195 indebug(self.ui, 'part type: "%s"' % self.type)
1195 indebug(self.ui, 'part type: "%s"' % self.type)
1196 self.id = self._unpackheader(_fpartid)[0]
1196 self.id = self._unpackheader(_fpartid)[0]
1197 indebug(self.ui, 'part id: "%s"' % self.id)
1197 indebug(self.ui, 'part id: "%s"' % self.id)
1198 # extract mandatory bit from type
1198 # extract mandatory bit from type
1199 self.mandatory = (self.type != self.type.lower())
1199 self.mandatory = (self.type != self.type.lower())
1200 self.type = self.type.lower()
1200 self.type = self.type.lower()
1201 ## reading parameters
1201 ## reading parameters
1202 # param count
1202 # param count
1203 mancount, advcount = self._unpackheader(_fpartparamcount)
1203 mancount, advcount = self._unpackheader(_fpartparamcount)
1204 indebug(self.ui, 'part parameters: %i' % (mancount + advcount))
1204 indebug(self.ui, 'part parameters: %i' % (mancount + advcount))
1205 # param size
1205 # param size
1206 fparamsizes = _makefpartparamsizes(mancount + advcount)
1206 fparamsizes = _makefpartparamsizes(mancount + advcount)
1207 paramsizes = self._unpackheader(fparamsizes)
1207 paramsizes = self._unpackheader(fparamsizes)
1208 # make it a list of couple again
1208 # make it a list of couple again
1209 paramsizes = zip(paramsizes[::2], paramsizes[1::2])
1209 paramsizes = zip(paramsizes[::2], paramsizes[1::2])
1210 # split mandatory from advisory
1210 # split mandatory from advisory
1211 mansizes = paramsizes[:mancount]
1211 mansizes = paramsizes[:mancount]
1212 advsizes = paramsizes[mancount:]
1212 advsizes = paramsizes[mancount:]
1213 # retrieve param value
1213 # retrieve param value
1214 manparams = []
1214 manparams = []
1215 for key, value in mansizes:
1215 for key, value in mansizes:
1216 manparams.append((self._fromheader(key), self._fromheader(value)))
1216 manparams.append((self._fromheader(key), self._fromheader(value)))
1217 advparams = []
1217 advparams = []
1218 for key, value in advsizes:
1218 for key, value in advsizes:
1219 advparams.append((self._fromheader(key), self._fromheader(value)))
1219 advparams.append((self._fromheader(key), self._fromheader(value)))
1220 self._initparams(manparams, advparams)
1220 self._initparams(manparams, advparams)
1221 ## part payload
1221 ## part payload
1222 self._payloadstream = util.chunkbuffer(self._payloadchunks())
1222 self._payloadstream = util.chunkbuffer(self._payloadchunks())
1223 # we read the data, tell it
1223 # we read the data, tell it
1224 self._initialized = True
1224 self._initialized = True
1225
1225
1226 def read(self, size=None):
1226 def read(self, size=None):
1227 """read payload data"""
1227 """read payload data"""
1228 if not self._initialized:
1228 if not self._initialized:
1229 self._readheader()
1229 self._readheader()
1230 if size is None:
1230 if size is None:
1231 data = self._payloadstream.read()
1231 data = self._payloadstream.read()
1232 else:
1232 else:
1233 data = self._payloadstream.read(size)
1233 data = self._payloadstream.read(size)
1234 self._pos += len(data)
1234 self._pos += len(data)
1235 if size is None or len(data) < size:
1235 if size is None or len(data) < size:
1236 if not self.consumed and self._pos:
1236 if not self.consumed and self._pos:
1237 self.ui.debug('bundle2-input-part: total payload size %i\n'
1237 self.ui.debug('bundle2-input-part: total payload size %i\n'
1238 % self._pos)
1238 % self._pos)
1239 self.consumed = True
1239 self.consumed = True
1240 return data
1240 return data
1241
1241
1242 def tell(self):
1242 def tell(self):
1243 return self._pos
1243 return self._pos
1244
1244
1245 def seek(self, offset, whence=0):
1245 def seek(self, offset, whence=0):
1246 if whence == 0:
1246 if whence == 0:
1247 newpos = offset
1247 newpos = offset
1248 elif whence == 1:
1248 elif whence == 1:
1249 newpos = self._pos + offset
1249 newpos = self._pos + offset
1250 elif whence == 2:
1250 elif whence == 2:
1251 if not self.consumed:
1251 if not self.consumed:
1252 self.read()
1252 self.read()
1253 newpos = self._chunkindex[-1][0] - offset
1253 newpos = self._chunkindex[-1][0] - offset
1254 else:
1254 else:
1255 raise ValueError('Unknown whence value: %r' % (whence,))
1255 raise ValueError('Unknown whence value: %r' % (whence,))
1256
1256
1257 if newpos > self._chunkindex[-1][0] and not self.consumed:
1257 if newpos > self._chunkindex[-1][0] and not self.consumed:
1258 self.read()
1258 self.read()
1259 if not 0 <= newpos <= self._chunkindex[-1][0]:
1259 if not 0 <= newpos <= self._chunkindex[-1][0]:
1260 raise ValueError('Offset out of range')
1260 raise ValueError('Offset out of range')
1261
1261
1262 if self._pos != newpos:
1262 if self._pos != newpos:
1263 chunk, internaloffset = self._findchunk(newpos)
1263 chunk, internaloffset = self._findchunk(newpos)
1264 self._payloadstream = util.chunkbuffer(self._payloadchunks(chunk))
1264 self._payloadstream = util.chunkbuffer(self._payloadchunks(chunk))
1265 adjust = self.read(internaloffset)
1265 adjust = self.read(internaloffset)
1266 if len(adjust) != internaloffset:
1266 if len(adjust) != internaloffset:
1267 raise error.Abort(_('Seek failed\n'))
1267 raise error.Abort(_('Seek failed\n'))
1268 self._pos = newpos
1268 self._pos = newpos
1269
1269
1270 def _seekfp(self, offset, whence=0):
1270 def _seekfp(self, offset, whence=0):
1271 """move the underlying file pointer
1271 """move the underlying file pointer
1272
1272
1273 This method is meant for internal usage by the bundle2 protocol only.
1273 This method is meant for internal usage by the bundle2 protocol only.
1274 They directly manipulate the low level stream including bundle2 level
1274 They directly manipulate the low level stream including bundle2 level
1275 instruction.
1275 instruction.
1276
1276
1277 Do not use it to implement higher-level logic or methods."""
1277 Do not use it to implement higher-level logic or methods."""
1278 if self._seekable:
1278 if self._seekable:
1279 return self._fp.seek(offset, whence)
1279 return self._fp.seek(offset, whence)
1280 else:
1280 else:
1281 raise NotImplementedError(_('File pointer is not seekable'))
1281 raise NotImplementedError(_('File pointer is not seekable'))
1282
1282
1283 def _tellfp(self):
1283 def _tellfp(self):
1284 """return the file offset, or None if file is not seekable
1284 """return the file offset, or None if file is not seekable
1285
1285
1286 This method is meant for internal usage by the bundle2 protocol only.
1286 This method is meant for internal usage by the bundle2 protocol only.
1287 They directly manipulate the low level stream including bundle2 level
1287 They directly manipulate the low level stream including bundle2 level
1288 instruction.
1288 instruction.
1289
1289
1290 Do not use it to implement higher-level logic or methods."""
1290 Do not use it to implement higher-level logic or methods."""
1291 if self._seekable:
1291 if self._seekable:
1292 try:
1292 try:
1293 return self._fp.tell()
1293 return self._fp.tell()
1294 except IOError as e:
1294 except IOError as e:
1295 if e.errno == errno.ESPIPE:
1295 if e.errno == errno.ESPIPE:
1296 self._seekable = False
1296 self._seekable = False
1297 else:
1297 else:
1298 raise
1298 raise
1299 return None
1299 return None
1300
1300
1301 # These are only the static capabilities.
1301 # These are only the static capabilities.
1302 # Check the 'getrepocaps' function for the rest.
1302 # Check the 'getrepocaps' function for the rest.
1303 capabilities = {'HG20': (),
1303 capabilities = {'HG20': (),
1304 'error': ('abort', 'unsupportedcontent', 'pushraced',
1304 'error': ('abort', 'unsupportedcontent', 'pushraced',
1305 'pushkey'),
1305 'pushkey'),
1306 'listkeys': (),
1306 'listkeys': (),
1307 'pushkey': (),
1307 'pushkey': (),
1308 'digests': tuple(sorted(util.DIGESTS.keys())),
1308 'digests': tuple(sorted(util.DIGESTS.keys())),
1309 'remote-changegroup': ('http', 'https'),
1309 'remote-changegroup': ('http', 'https'),
1310 'hgtagsfnodes': (),
1310 'hgtagsfnodes': (),
1311 }
1311 }
1312
1312
1313 def getrepocaps(repo, allowpushback=False):
1313 def getrepocaps(repo, allowpushback=False):
1314 """return the bundle2 capabilities for a given repo
1314 """return the bundle2 capabilities for a given repo
1315
1315
1316 Exists to allow extensions (like evolution) to mutate the capabilities.
1316 Exists to allow extensions (like evolution) to mutate the capabilities.
1317 """
1317 """
1318 caps = capabilities.copy()
1318 caps = capabilities.copy()
1319 caps['changegroup'] = tuple(sorted(
1319 caps['changegroup'] = tuple(sorted(
1320 changegroup.supportedincomingversions(repo)))
1320 changegroup.supportedincomingversions(repo)))
1321 if obsolete.isenabled(repo, obsolete.exchangeopt):
1321 if obsolete.isenabled(repo, obsolete.exchangeopt):
1322 supportedformat = tuple('V%i' % v for v in obsolete.formats)
1322 supportedformat = tuple('V%i' % v for v in obsolete.formats)
1323 caps['obsmarkers'] = supportedformat
1323 caps['obsmarkers'] = supportedformat
1324 if allowpushback:
1324 if allowpushback:
1325 caps['pushback'] = ()
1325 caps['pushback'] = ()
1326 cpmode = repo.ui.config('server', 'concurrent-push-mode', 'strict')
1326 cpmode = repo.ui.config('server', 'concurrent-push-mode', 'strict')
1327 if cpmode == 'check-related':
1327 if cpmode == 'check-related':
1328 caps['checkheads'] = ('related',)
1328 caps['checkheads'] = ('related',)
1329 return caps
1329 return caps
1330
1330
1331 def bundle2caps(remote):
1331 def bundle2caps(remote):
1332 """return the bundle capabilities of a peer as dict"""
1332 """return the bundle capabilities of a peer as dict"""
1333 raw = remote.capable('bundle2')
1333 raw = remote.capable('bundle2')
1334 if not raw and raw != '':
1334 if not raw and raw != '':
1335 return {}
1335 return {}
1336 capsblob = urlreq.unquote(remote.capable('bundle2'))
1336 capsblob = urlreq.unquote(remote.capable('bundle2'))
1337 return decodecaps(capsblob)
1337 return decodecaps(capsblob)
1338
1338
1339 def obsmarkersversion(caps):
1339 def obsmarkersversion(caps):
1340 """extract the list of supported obsmarkers versions from a bundle2caps dict
1340 """extract the list of supported obsmarkers versions from a bundle2caps dict
1341 """
1341 """
1342 obscaps = caps.get('obsmarkers', ())
1342 obscaps = caps.get('obsmarkers', ())
1343 return [int(c[1:]) for c in obscaps if c.startswith('V')]
1343 return [int(c[1:]) for c in obscaps if c.startswith('V')]
1344
1344
1345 def writenewbundle(ui, repo, source, filename, bundletype, outgoing, opts,
1345 def writenewbundle(ui, repo, source, filename, bundletype, outgoing, opts,
1346 vfs=None, compression=None, compopts=None):
1346 vfs=None, compression=None, compopts=None):
1347 if bundletype.startswith('HG10'):
1347 if bundletype.startswith('HG10'):
1348 cg = changegroup.getchangegroup(repo, source, outgoing, version='01')
1348 cg = changegroup.getchangegroup(repo, source, outgoing, version='01')
1349 return writebundle(ui, cg, filename, bundletype, vfs=vfs,
1349 return writebundle(ui, cg, filename, bundletype, vfs=vfs,
1350 compression=compression, compopts=compopts)
1350 compression=compression, compopts=compopts)
1351 elif not bundletype.startswith('HG20'):
1351 elif not bundletype.startswith('HG20'):
1352 raise error.ProgrammingError('unknown bundle type: %s' % bundletype)
1352 raise error.ProgrammingError('unknown bundle type: %s' % bundletype)
1353
1353
1354 caps = {}
1354 caps = {}
1355 if 'obsolescence' in opts:
1355 if 'obsolescence' in opts:
1356 caps['obsmarkers'] = ('V1',)
1356 caps['obsmarkers'] = ('V1',)
1357 bundle = bundle20(ui, caps)
1357 bundle = bundle20(ui, caps)
1358 bundle.setcompression(compression, compopts)
1358 bundle.setcompression(compression, compopts)
1359 _addpartsfromopts(ui, repo, bundle, source, outgoing, opts)
1359 _addpartsfromopts(ui, repo, bundle, source, outgoing, opts)
1360 chunkiter = bundle.getchunks()
1360 chunkiter = bundle.getchunks()
1361
1361
1362 return changegroup.writechunks(ui, chunkiter, filename, vfs=vfs)
1362 return changegroup.writechunks(ui, chunkiter, filename, vfs=vfs)
1363
1363
1364 def _addpartsfromopts(ui, repo, bundler, source, outgoing, opts):
1364 def _addpartsfromopts(ui, repo, bundler, source, outgoing, opts):
1365 # We should eventually reconcile this logic with the one behind
1365 # We should eventually reconcile this logic with the one behind
1366 # 'exchange.getbundle2partsgenerator'.
1366 # 'exchange.getbundle2partsgenerator'.
1367 #
1367 #
1368 # The type of input from 'getbundle' and 'writenewbundle' are a bit
1368 # The type of input from 'getbundle' and 'writenewbundle' are a bit
1369 # different right now. So we keep them separated for now for the sake of
1369 # different right now. So we keep them separated for now for the sake of
1370 # simplicity.
1370 # simplicity.
1371
1371
1372 # we always want a changegroup in such bundle
1372 # we always want a changegroup in such bundle
1373 cgversion = opts.get('cg.version')
1373 cgversion = opts.get('cg.version')
1374 if cgversion is None:
1374 if cgversion is None:
1375 cgversion = changegroup.safeversion(repo)
1375 cgversion = changegroup.safeversion(repo)
1376 cg = changegroup.getchangegroup(repo, source, outgoing,
1376 cg = changegroup.getchangegroup(repo, source, outgoing,
1377 version=cgversion)
1377 version=cgversion)
1378 part = bundler.newpart('changegroup', data=cg.getchunks())
1378 part = bundler.newpart('changegroup', data=cg.getchunks())
1379 part.addparam('version', cg.version)
1379 part.addparam('version', cg.version)
1380 if 'clcount' in cg.extras:
1380 if 'clcount' in cg.extras:
1381 part.addparam('nbchanges', str(cg.extras['clcount']),
1381 part.addparam('nbchanges', str(cg.extras['clcount']),
1382 mandatory=False)
1382 mandatory=False)
1383
1383
1384 addparttagsfnodescache(repo, bundler, outgoing)
1384 addparttagsfnodescache(repo, bundler, outgoing)
1385
1385
1386 if opts.get('obsolescence', False):
1386 if opts.get('obsolescence', False):
1387 obsmarkers = repo.obsstore.relevantmarkers(outgoing.missing)
1387 obsmarkers = repo.obsstore.relevantmarkers(outgoing.missing)
1388 buildobsmarkerspart(bundler, obsmarkers)
1388 buildobsmarkerspart(bundler, obsmarkers)
1389
1389
1390 def addparttagsfnodescache(repo, bundler, outgoing):
1390 def addparttagsfnodescache(repo, bundler, outgoing):
1391 # we include the tags fnode cache for the bundle changeset
1391 # we include the tags fnode cache for the bundle changeset
1392 # (as an optional parts)
1392 # (as an optional parts)
1393 cache = tags.hgtagsfnodescache(repo.unfiltered())
1393 cache = tags.hgtagsfnodescache(repo.unfiltered())
1394 chunks = []
1394 chunks = []
1395
1395
1396 # .hgtags fnodes are only relevant for head changesets. While we could
1396 # .hgtags fnodes are only relevant for head changesets. While we could
1397 # transfer values for all known nodes, there will likely be little to
1397 # transfer values for all known nodes, there will likely be little to
1398 # no benefit.
1398 # no benefit.
1399 #
1399 #
1400 # We don't bother using a generator to produce output data because
1400 # We don't bother using a generator to produce output data because
1401 # a) we only have 40 bytes per head and even esoteric numbers of heads
1401 # a) we only have 40 bytes per head and even esoteric numbers of heads
1402 # consume little memory (1M heads is 40MB) b) we don't want to send the
1402 # consume little memory (1M heads is 40MB) b) we don't want to send the
1403 # part if we don't have entries and knowing if we have entries requires
1403 # part if we don't have entries and knowing if we have entries requires
1404 # cache lookups.
1404 # cache lookups.
1405 for node in outgoing.missingheads:
1405 for node in outgoing.missingheads:
1406 # Don't compute missing, as this may slow down serving.
1406 # Don't compute missing, as this may slow down serving.
1407 fnode = cache.getfnode(node, computemissing=False)
1407 fnode = cache.getfnode(node, computemissing=False)
1408 if fnode is not None:
1408 if fnode is not None:
1409 chunks.extend([node, fnode])
1409 chunks.extend([node, fnode])
1410
1410
1411 if chunks:
1411 if chunks:
1412 bundler.newpart('hgtagsfnodes', data=''.join(chunks))
1412 bundler.newpart('hgtagsfnodes', data=''.join(chunks))
1413
1413
1414 def buildobsmarkerspart(bundler, markers):
1414 def buildobsmarkerspart(bundler, markers):
1415 """add an obsmarker part to the bundler with <markers>
1415 """add an obsmarker part to the bundler with <markers>
1416
1416
1417 No part is created if markers is empty.
1417 No part is created if markers is empty.
1418 Raises ValueError if the bundler doesn't support any known obsmarker format.
1418 Raises ValueError if the bundler doesn't support any known obsmarker format.
1419 """
1419 """
1420 if not markers:
1420 if not markers:
1421 return None
1421 return None
1422
1422
1423 remoteversions = obsmarkersversion(bundler.capabilities)
1423 remoteversions = obsmarkersversion(bundler.capabilities)
1424 version = obsolete.commonversion(remoteversions)
1424 version = obsolete.commonversion(remoteversions)
1425 if version is None:
1425 if version is None:
1426 raise ValueError('bundler does not support common obsmarker format')
1426 raise ValueError('bundler does not support common obsmarker format')
1427 stream = obsolete.encodemarkers(markers, True, version=version)
1427 stream = obsolete.encodemarkers(markers, True, version=version)
1428 return bundler.newpart('obsmarkers', data=stream)
1428 return bundler.newpart('obsmarkers', data=stream)
1429
1429
1430 def writebundle(ui, cg, filename, bundletype, vfs=None, compression=None,
1430 def writebundle(ui, cg, filename, bundletype, vfs=None, compression=None,
1431 compopts=None):
1431 compopts=None):
1432 """Write a bundle file and return its filename.
1432 """Write a bundle file and return its filename.
1433
1433
1434 Existing files will not be overwritten.
1434 Existing files will not be overwritten.
1435 If no filename is specified, a temporary file is created.
1435 If no filename is specified, a temporary file is created.
1436 bz2 compression can be turned off.
1436 bz2 compression can be turned off.
1437 The bundle file will be deleted in case of errors.
1437 The bundle file will be deleted in case of errors.
1438 """
1438 """
1439
1439
1440 if bundletype == "HG20":
1440 if bundletype == "HG20":
1441 bundle = bundle20(ui)
1441 bundle = bundle20(ui)
1442 bundle.setcompression(compression, compopts)
1442 bundle.setcompression(compression, compopts)
1443 part = bundle.newpart('changegroup', data=cg.getchunks())
1443 part = bundle.newpart('changegroup', data=cg.getchunks())
1444 part.addparam('version', cg.version)
1444 part.addparam('version', cg.version)
1445 if 'clcount' in cg.extras:
1445 if 'clcount' in cg.extras:
1446 part.addparam('nbchanges', str(cg.extras['clcount']),
1446 part.addparam('nbchanges', str(cg.extras['clcount']),
1447 mandatory=False)
1447 mandatory=False)
1448 chunkiter = bundle.getchunks()
1448 chunkiter = bundle.getchunks()
1449 else:
1449 else:
1450 # compression argument is only for the bundle2 case
1450 # compression argument is only for the bundle2 case
1451 assert compression is None
1451 assert compression is None
1452 if cg.version != '01':
1452 if cg.version != '01':
1453 raise error.Abort(_('old bundle types only supports v1 '
1453 raise error.Abort(_('old bundle types only supports v1 '
1454 'changegroups'))
1454 'changegroups'))
1455 header, comp = bundletypes[bundletype]
1455 header, comp = bundletypes[bundletype]
1456 if comp not in util.compengines.supportedbundletypes:
1456 if comp not in util.compengines.supportedbundletypes:
1457 raise error.Abort(_('unknown stream compression type: %s')
1457 raise error.Abort(_('unknown stream compression type: %s')
1458 % comp)
1458 % comp)
1459 compengine = util.compengines.forbundletype(comp)
1459 compengine = util.compengines.forbundletype(comp)
1460 def chunkiter():
1460 def chunkiter():
1461 yield header
1461 yield header
1462 for chunk in compengine.compressstream(cg.getchunks(), compopts):
1462 for chunk in compengine.compressstream(cg.getchunks(), compopts):
1463 yield chunk
1463 yield chunk
1464 chunkiter = chunkiter()
1464 chunkiter = chunkiter()
1465
1465
1466 # parse the changegroup data, otherwise we will block
1466 # parse the changegroup data, otherwise we will block
1467 # in case of sshrepo because we don't know the end of the stream
1467 # in case of sshrepo because we don't know the end of the stream
1468 return changegroup.writechunks(ui, chunkiter, filename, vfs=vfs)
1468 return changegroup.writechunks(ui, chunkiter, filename, vfs=vfs)
1469
1469
1470 @parthandler('changegroup', ('version', 'nbchanges', 'treemanifest'))
1470 @parthandler('changegroup', ('version', 'nbchanges', 'treemanifest'))
1471 def handlechangegroup(op, inpart):
1471 def handlechangegroup(op, inpart):
1472 """apply a changegroup part on the repo
1472 """apply a changegroup part on the repo
1473
1473
1474 This is a very early implementation that will massive rework before being
1474 This is a very early implementation that will massive rework before being
1475 inflicted to any end-user.
1475 inflicted to any end-user.
1476 """
1476 """
1477 tr = op.gettransaction()
1477 tr = op.gettransaction()
1478 unpackerversion = inpart.params.get('version', '01')
1478 unpackerversion = inpart.params.get('version', '01')
1479 # We should raise an appropriate exception here
1479 # We should raise an appropriate exception here
1480 cg = changegroup.getunbundler(unpackerversion, inpart, None)
1480 cg = changegroup.getunbundler(unpackerversion, inpart, None)
1481 # the source and url passed here are overwritten by the one contained in
1481 # the source and url passed here are overwritten by the one contained in
1482 # the transaction.hookargs argument. So 'bundle2' is a placeholder
1482 # the transaction.hookargs argument. So 'bundle2' is a placeholder
1483 nbchangesets = None
1483 nbchangesets = None
1484 if 'nbchanges' in inpart.params:
1484 if 'nbchanges' in inpart.params:
1485 nbchangesets = int(inpart.params.get('nbchanges'))
1485 nbchangesets = int(inpart.params.get('nbchanges'))
1486 if ('treemanifest' in inpart.params and
1486 if ('treemanifest' in inpart.params and
1487 'treemanifest' not in op.repo.requirements):
1487 'treemanifest' not in op.repo.requirements):
1488 if len(op.repo.changelog) != 0:
1488 if len(op.repo.changelog) != 0:
1489 raise error.Abort(_(
1489 raise error.Abort(_(
1490 "bundle contains tree manifests, but local repo is "
1490 "bundle contains tree manifests, but local repo is "
1491 "non-empty and does not use tree manifests"))
1491 "non-empty and does not use tree manifests"))
1492 op.repo.requirements.add('treemanifest')
1492 op.repo.requirements.add('treemanifest')
1493 op.repo._applyopenerreqs()
1493 op.repo._applyopenerreqs()
1494 op.repo._writerequirements()
1494 op.repo._writerequirements()
1495 ret = cg.apply(op.repo, tr, 'bundle2', 'bundle2',
1495 ret = cg.apply(op.repo, tr, 'bundle2', 'bundle2',
1496 expectedtotal=nbchangesets)
1496 expectedtotal=nbchangesets)
1497 op.records.add('changegroup', {'return': ret})
1497 op.records.add('changegroup', {'return': ret})
1498 if op.reply is not None:
1498 if op.reply is not None:
1499 # This is definitely not the final form of this
1499 # This is definitely not the final form of this
1500 # return. But one need to start somewhere.
1500 # return. But one need to start somewhere.
1501 part = op.reply.newpart('reply:changegroup', mandatory=False)
1501 part = op.reply.newpart('reply:changegroup', mandatory=False)
1502 part.addparam('in-reply-to', str(inpart.id), mandatory=False)
1502 part.addparam('in-reply-to', str(inpart.id), mandatory=False)
1503 part.addparam('return', '%i' % ret, mandatory=False)
1503 part.addparam('return', '%i' % ret, mandatory=False)
1504 assert not inpart.read()
1504 assert not inpart.read()
1505
1505
1506 _remotechangegroupparams = tuple(['url', 'size', 'digests'] +
1506 _remotechangegroupparams = tuple(['url', 'size', 'digests'] +
1507 ['digest:%s' % k for k in util.DIGESTS.keys()])
1507 ['digest:%s' % k for k in util.DIGESTS.keys()])
1508 @parthandler('remote-changegroup', _remotechangegroupparams)
1508 @parthandler('remote-changegroup', _remotechangegroupparams)
1509 def handleremotechangegroup(op, inpart):
1509 def handleremotechangegroup(op, inpart):
1510 """apply a bundle10 on the repo, given an url and validation information
1510 """apply a bundle10 on the repo, given an url and validation information
1511
1511
1512 All the information about the remote bundle to import are given as
1512 All the information about the remote bundle to import are given as
1513 parameters. The parameters include:
1513 parameters. The parameters include:
1514 - url: the url to the bundle10.
1514 - url: the url to the bundle10.
1515 - size: the bundle10 file size. It is used to validate what was
1515 - size: the bundle10 file size. It is used to validate what was
1516 retrieved by the client matches the server knowledge about the bundle.
1516 retrieved by the client matches the server knowledge about the bundle.
1517 - digests: a space separated list of the digest types provided as
1517 - digests: a space separated list of the digest types provided as
1518 parameters.
1518 parameters.
1519 - digest:<digest-type>: the hexadecimal representation of the digest with
1519 - digest:<digest-type>: the hexadecimal representation of the digest with
1520 that name. Like the size, it is used to validate what was retrieved by
1520 that name. Like the size, it is used to validate what was retrieved by
1521 the client matches what the server knows about the bundle.
1521 the client matches what the server knows about the bundle.
1522
1522
1523 When multiple digest types are given, all of them are checked.
1523 When multiple digest types are given, all of them are checked.
1524 """
1524 """
1525 try:
1525 try:
1526 raw_url = inpart.params['url']
1526 raw_url = inpart.params['url']
1527 except KeyError:
1527 except KeyError:
1528 raise error.Abort(_('remote-changegroup: missing "%s" param') % 'url')
1528 raise error.Abort(_('remote-changegroup: missing "%s" param') % 'url')
1529 parsed_url = util.url(raw_url)
1529 parsed_url = util.url(raw_url)
1530 if parsed_url.scheme not in capabilities['remote-changegroup']:
1530 if parsed_url.scheme not in capabilities['remote-changegroup']:
1531 raise error.Abort(_('remote-changegroup does not support %s urls') %
1531 raise error.Abort(_('remote-changegroup does not support %s urls') %
1532 parsed_url.scheme)
1532 parsed_url.scheme)
1533
1533
1534 try:
1534 try:
1535 size = int(inpart.params['size'])
1535 size = int(inpart.params['size'])
1536 except ValueError:
1536 except ValueError:
1537 raise error.Abort(_('remote-changegroup: invalid value for param "%s"')
1537 raise error.Abort(_('remote-changegroup: invalid value for param "%s"')
1538 % 'size')
1538 % 'size')
1539 except KeyError:
1539 except KeyError:
1540 raise error.Abort(_('remote-changegroup: missing "%s" param') % 'size')
1540 raise error.Abort(_('remote-changegroup: missing "%s" param') % 'size')
1541
1541
1542 digests = {}
1542 digests = {}
1543 for typ in inpart.params.get('digests', '').split():
1543 for typ in inpart.params.get('digests', '').split():
1544 param = 'digest:%s' % typ
1544 param = 'digest:%s' % typ
1545 try:
1545 try:
1546 value = inpart.params[param]
1546 value = inpart.params[param]
1547 except KeyError:
1547 except KeyError:
1548 raise error.Abort(_('remote-changegroup: missing "%s" param') %
1548 raise error.Abort(_('remote-changegroup: missing "%s" param') %
1549 param)
1549 param)
1550 digests[typ] = value
1550 digests[typ] = value
1551
1551
1552 real_part = util.digestchecker(url.open(op.ui, raw_url), size, digests)
1552 real_part = util.digestchecker(url.open(op.ui, raw_url), size, digests)
1553
1553
1554 tr = op.gettransaction()
1554 tr = op.gettransaction()
1555 from . import exchange
1555 from . import exchange
1556 cg = exchange.readbundle(op.repo.ui, real_part, raw_url)
1556 cg = exchange.readbundle(op.repo.ui, real_part, raw_url)
1557 if not isinstance(cg, changegroup.cg1unpacker):
1557 if not isinstance(cg, changegroup.cg1unpacker):
1558 raise error.Abort(_('%s: not a bundle version 1.0') %
1558 raise error.Abort(_('%s: not a bundle version 1.0') %
1559 util.hidepassword(raw_url))
1559 util.hidepassword(raw_url))
1560 ret = cg.apply(op.repo, tr, 'bundle2', 'bundle2')
1560 ret = cg.apply(op.repo, tr, 'bundle2', 'bundle2')
1561 op.records.add('changegroup', {'return': ret})
1561 op.records.add('changegroup', {'return': ret})
1562 if op.reply is not None:
1562 if op.reply is not None:
1563 # This is definitely not the final form of this
1563 # This is definitely not the final form of this
1564 # return. But one need to start somewhere.
1564 # return. But one need to start somewhere.
1565 part = op.reply.newpart('reply:changegroup')
1565 part = op.reply.newpart('reply:changegroup')
1566 part.addparam('in-reply-to', str(inpart.id), mandatory=False)
1566 part.addparam('in-reply-to', str(inpart.id), mandatory=False)
1567 part.addparam('return', '%i' % ret, mandatory=False)
1567 part.addparam('return', '%i' % ret, mandatory=False)
1568 try:
1568 try:
1569 real_part.validate()
1569 real_part.validate()
1570 except error.Abort as e:
1570 except error.Abort as e:
1571 raise error.Abort(_('bundle at %s is corrupted:\n%s') %
1571 raise error.Abort(_('bundle at %s is corrupted:\n%s') %
1572 (util.hidepassword(raw_url), str(e)))
1572 (util.hidepassword(raw_url), str(e)))
1573 assert not inpart.read()
1573 assert not inpart.read()
1574
1574
1575 @parthandler('reply:changegroup', ('return', 'in-reply-to'))
1575 @parthandler('reply:changegroup', ('return', 'in-reply-to'))
1576 def handlereplychangegroup(op, inpart):
1576 def handlereplychangegroup(op, inpart):
1577 ret = int(inpart.params['return'])
1577 ret = int(inpart.params['return'])
1578 replyto = int(inpart.params['in-reply-to'])
1578 replyto = int(inpart.params['in-reply-to'])
1579 op.records.add('changegroup', {'return': ret}, replyto)
1579 op.records.add('changegroup', {'return': ret}, replyto)
1580
1580
1581 @parthandler('check:heads')
1581 @parthandler('check:heads')
1582 def handlecheckheads(op, inpart):
1582 def handlecheckheads(op, inpart):
1583 """check that head of the repo did not change
1583 """check that head of the repo did not change
1584
1584
1585 This is used to detect a push race when using unbundle.
1585 This is used to detect a push race when using unbundle.
1586 This replaces the "heads" argument of unbundle."""
1586 This replaces the "heads" argument of unbundle."""
1587 h = inpart.read(20)
1587 h = inpart.read(20)
1588 heads = []
1588 heads = []
1589 while len(h) == 20:
1589 while len(h) == 20:
1590 heads.append(h)
1590 heads.append(h)
1591 h = inpart.read(20)
1591 h = inpart.read(20)
1592 assert not h
1592 assert not h
1593 # Trigger a transaction so that we are guaranteed to have the lock now.
1593 # Trigger a transaction so that we are guaranteed to have the lock now.
1594 if op.ui.configbool('experimental', 'bundle2lazylocking'):
1594 if op.ui.configbool('experimental', 'bundle2lazylocking'):
1595 op.gettransaction()
1595 op.gettransaction()
1596 if sorted(heads) != sorted(op.repo.heads()):
1596 if sorted(heads) != sorted(op.repo.heads()):
1597 raise error.PushRaced('repository changed while pushing - '
1597 raise error.PushRaced('repository changed while pushing - '
1598 'please try again')
1598 'please try again')
1599
1599
1600 @parthandler('check:updated-heads')
1600 @parthandler('check:updated-heads')
1601 def handlecheckupdatedheads(op, inpart):
1601 def handlecheckupdatedheads(op, inpart):
1602 """check for race on the heads touched by a push
1602 """check for race on the heads touched by a push
1603
1603
1604 This is similar to 'check:heads' but focus on the heads actually updated
1604 This is similar to 'check:heads' but focus on the heads actually updated
1605 during the push. If other activities happen on unrelated heads, it is
1605 during the push. If other activities happen on unrelated heads, it is
1606 ignored.
1606 ignored.
1607
1607
1608 This allow server with high traffic to avoid push contention as long as
1608 This allow server with high traffic to avoid push contention as long as
1609 unrelated parts of the graph are involved."""
1609 unrelated parts of the graph are involved."""
1610 h = inpart.read(20)
1610 h = inpart.read(20)
1611 heads = []
1611 heads = []
1612 while len(h) == 20:
1612 while len(h) == 20:
1613 heads.append(h)
1613 heads.append(h)
1614 h = inpart.read(20)
1614 h = inpart.read(20)
1615 assert not h
1615 assert not h
1616 # trigger a transaction so that we are guaranteed to have the lock now.
1616 # trigger a transaction so that we are guaranteed to have the lock now.
1617 if op.ui.configbool('experimental', 'bundle2lazylocking'):
1617 if op.ui.configbool('experimental', 'bundle2lazylocking'):
1618 op.gettransaction()
1618 op.gettransaction()
1619
1619
1620 currentheads = set()
1620 currentheads = set()
1621 for ls in op.repo.branchmap().itervalues():
1621 for ls in op.repo.branchmap().itervalues():
1622 currentheads.update(ls)
1622 currentheads.update(ls)
1623
1623
1624 for h in heads:
1624 for h in heads:
1625 if h not in currentheads:
1625 if h not in currentheads:
1626 raise error.PushRaced('repository changed while pushing - '
1626 raise error.PushRaced('repository changed while pushing - '
1627 'please try again')
1627 'please try again')
1628
1628
1629 @parthandler('output')
1629 @parthandler('output')
1630 def handleoutput(op, inpart):
1630 def handleoutput(op, inpart):
1631 """forward output captured on the server to the client"""
1631 """forward output captured on the server to the client"""
1632 for line in inpart.read().splitlines():
1632 for line in inpart.read().splitlines():
1633 op.ui.status(_('remote: %s\n') % line)
1633 op.ui.status(_('remote: %s\n') % line)
1634
1634
1635 @parthandler('replycaps')
1635 @parthandler('replycaps')
1636 def handlereplycaps(op, inpart):
1636 def handlereplycaps(op, inpart):
1637 """Notify that a reply bundle should be created
1637 """Notify that a reply bundle should be created
1638
1638
1639 The payload contains the capabilities information for the reply"""
1639 The payload contains the capabilities information for the reply"""
1640 caps = decodecaps(inpart.read())
1640 caps = decodecaps(inpart.read())
1641 if op.reply is None:
1641 if op.reply is None:
1642 op.reply = bundle20(op.ui, caps)
1642 op.reply = bundle20(op.ui, caps)
1643
1643
1644 class AbortFromPart(error.Abort):
1644 class AbortFromPart(error.Abort):
1645 """Sub-class of Abort that denotes an error from a bundle2 part."""
1645 """Sub-class of Abort that denotes an error from a bundle2 part."""
1646
1646
1647 @parthandler('error:abort', ('message', 'hint'))
1647 @parthandler('error:abort', ('message', 'hint'))
1648 def handleerrorabort(op, inpart):
1648 def handleerrorabort(op, inpart):
1649 """Used to transmit abort error over the wire"""
1649 """Used to transmit abort error over the wire"""
1650 raise AbortFromPart(inpart.params['message'],
1650 raise AbortFromPart(inpart.params['message'],
1651 hint=inpart.params.get('hint'))
1651 hint=inpart.params.get('hint'))
1652
1652
1653 @parthandler('error:pushkey', ('namespace', 'key', 'new', 'old', 'ret',
1653 @parthandler('error:pushkey', ('namespace', 'key', 'new', 'old', 'ret',
1654 'in-reply-to'))
1654 'in-reply-to'))
1655 def handleerrorpushkey(op, inpart):
1655 def handleerrorpushkey(op, inpart):
1656 """Used to transmit failure of a mandatory pushkey over the wire"""
1656 """Used to transmit failure of a mandatory pushkey over the wire"""
1657 kwargs = {}
1657 kwargs = {}
1658 for name in ('namespace', 'key', 'new', 'old', 'ret'):
1658 for name in ('namespace', 'key', 'new', 'old', 'ret'):
1659 value = inpart.params.get(name)
1659 value = inpart.params.get(name)
1660 if value is not None:
1660 if value is not None:
1661 kwargs[name] = value
1661 kwargs[name] = value
1662 raise error.PushkeyFailed(inpart.params['in-reply-to'], **kwargs)
1662 raise error.PushkeyFailed(inpart.params['in-reply-to'], **kwargs)
1663
1663
1664 @parthandler('error:unsupportedcontent', ('parttype', 'params'))
1664 @parthandler('error:unsupportedcontent', ('parttype', 'params'))
1665 def handleerrorunsupportedcontent(op, inpart):
1665 def handleerrorunsupportedcontent(op, inpart):
1666 """Used to transmit unknown content error over the wire"""
1666 """Used to transmit unknown content error over the wire"""
1667 kwargs = {}
1667 kwargs = {}
1668 parttype = inpart.params.get('parttype')
1668 parttype = inpart.params.get('parttype')
1669 if parttype is not None:
1669 if parttype is not None:
1670 kwargs['parttype'] = parttype
1670 kwargs['parttype'] = parttype
1671 params = inpart.params.get('params')
1671 params = inpart.params.get('params')
1672 if params is not None:
1672 if params is not None:
1673 kwargs['params'] = params.split('\0')
1673 kwargs['params'] = params.split('\0')
1674
1674
1675 raise error.BundleUnknownFeatureError(**kwargs)
1675 raise error.BundleUnknownFeatureError(**kwargs)
1676
1676
1677 @parthandler('error:pushraced', ('message',))
1677 @parthandler('error:pushraced', ('message',))
1678 def handleerrorpushraced(op, inpart):
1678 def handleerrorpushraced(op, inpart):
1679 """Used to transmit push race error over the wire"""
1679 """Used to transmit push race error over the wire"""
1680 raise error.ResponseError(_('push failed:'), inpart.params['message'])
1680 raise error.ResponseError(_('push failed:'), inpart.params['message'])
1681
1681
1682 @parthandler('listkeys', ('namespace',))
1682 @parthandler('listkeys', ('namespace',))
1683 def handlelistkeys(op, inpart):
1683 def handlelistkeys(op, inpart):
1684 """retrieve pushkey namespace content stored in a bundle2"""
1684 """retrieve pushkey namespace content stored in a bundle2"""
1685 namespace = inpart.params['namespace']
1685 namespace = inpart.params['namespace']
1686 r = pushkey.decodekeys(inpart.read())
1686 r = pushkey.decodekeys(inpart.read())
1687 op.records.add('listkeys', (namespace, r))
1687 op.records.add('listkeys', (namespace, r))
1688
1688
1689 @parthandler('pushkey', ('namespace', 'key', 'old', 'new'))
1689 @parthandler('pushkey', ('namespace', 'key', 'old', 'new'))
1690 def handlepushkey(op, inpart):
1690 def handlepushkey(op, inpart):
1691 """process a pushkey request"""
1691 """process a pushkey request"""
1692 dec = pushkey.decode
1692 dec = pushkey.decode
1693 namespace = dec(inpart.params['namespace'])
1693 namespace = dec(inpart.params['namespace'])
1694 key = dec(inpart.params['key'])
1694 key = dec(inpart.params['key'])
1695 old = dec(inpart.params['old'])
1695 old = dec(inpart.params['old'])
1696 new = dec(inpart.params['new'])
1696 new = dec(inpart.params['new'])
1697 # Grab the transaction to ensure that we have the lock before performing the
1697 # Grab the transaction to ensure that we have the lock before performing the
1698 # pushkey.
1698 # pushkey.
1699 if op.ui.configbool('experimental', 'bundle2lazylocking'):
1699 if op.ui.configbool('experimental', 'bundle2lazylocking'):
1700 op.gettransaction()
1700 op.gettransaction()
1701 ret = op.repo.pushkey(namespace, key, old, new)
1701 ret = op.repo.pushkey(namespace, key, old, new)
1702 record = {'namespace': namespace,
1702 record = {'namespace': namespace,
1703 'key': key,
1703 'key': key,
1704 'old': old,
1704 'old': old,
1705 'new': new}
1705 'new': new}
1706 op.records.add('pushkey', record)
1706 op.records.add('pushkey', record)
1707 if op.reply is not None:
1707 if op.reply is not None:
1708 rpart = op.reply.newpart('reply:pushkey')
1708 rpart = op.reply.newpart('reply:pushkey')
1709 rpart.addparam('in-reply-to', str(inpart.id), mandatory=False)
1709 rpart.addparam('in-reply-to', str(inpart.id), mandatory=False)
1710 rpart.addparam('return', '%i' % ret, mandatory=False)
1710 rpart.addparam('return', '%i' % ret, mandatory=False)
1711 if inpart.mandatory and not ret:
1711 if inpart.mandatory and not ret:
1712 kwargs = {}
1712 kwargs = {}
1713 for key in ('namespace', 'key', 'new', 'old', 'ret'):
1713 for key in ('namespace', 'key', 'new', 'old', 'ret'):
1714 if key in inpart.params:
1714 if key in inpart.params:
1715 kwargs[key] = inpart.params[key]
1715 kwargs[key] = inpart.params[key]
1716 raise error.PushkeyFailed(partid=str(inpart.id), **kwargs)
1716 raise error.PushkeyFailed(partid=str(inpart.id), **kwargs)
1717
1717
1718 @parthandler('reply:pushkey', ('return', 'in-reply-to'))
1718 @parthandler('reply:pushkey', ('return', 'in-reply-to'))
1719 def handlepushkeyreply(op, inpart):
1719 def handlepushkeyreply(op, inpart):
1720 """retrieve the result of a pushkey request"""
1720 """retrieve the result of a pushkey request"""
1721 ret = int(inpart.params['return'])
1721 ret = int(inpart.params['return'])
1722 partid = int(inpart.params['in-reply-to'])
1722 partid = int(inpart.params['in-reply-to'])
1723 op.records.add('pushkey', {'return': ret}, partid)
1723 op.records.add('pushkey', {'return': ret}, partid)
1724
1724
1725 @parthandler('obsmarkers')
1725 @parthandler('obsmarkers')
1726 def handleobsmarker(op, inpart):
1726 def handleobsmarker(op, inpart):
1727 """add a stream of obsmarkers to the repo"""
1727 """add a stream of obsmarkers to the repo"""
1728 tr = op.gettransaction()
1728 tr = op.gettransaction()
1729 markerdata = inpart.read()
1729 markerdata = inpart.read()
1730 if op.ui.config('experimental', 'obsmarkers-exchange-debug', False):
1730 if op.ui.config('experimental', 'obsmarkers-exchange-debug', False):
1731 op.ui.write(('obsmarker-exchange: %i bytes received\n')
1731 op.ui.write(('obsmarker-exchange: %i bytes received\n')
1732 % len(markerdata))
1732 % len(markerdata))
1733 # The mergemarkers call will crash if marker creation is not enabled.
1733 # The mergemarkers call will crash if marker creation is not enabled.
1734 # we want to avoid this if the part is advisory.
1734 # we want to avoid this if the part is advisory.
1735 if not inpart.mandatory and op.repo.obsstore.readonly:
1735 if not inpart.mandatory and op.repo.obsstore.readonly:
1736 op.repo.ui.debug('ignoring obsolescence markers, feature not enabled')
1736 op.repo.ui.debug('ignoring obsolescence markers, feature not enabled')
1737 return
1737 return
1738 new = op.repo.obsstore.mergemarkers(tr, markerdata)
1738 new = op.repo.obsstore.mergemarkers(tr, markerdata)
1739 op.repo.invalidatevolatilesets()
1739 op.repo.invalidatevolatilesets()
1740 if new:
1740 if new:
1741 op.repo.ui.status(_('%i new obsolescence markers\n') % new)
1741 op.repo.ui.status(_('%i new obsolescence markers\n') % new)
1742 op.records.add('obsmarkers', {'new': new})
1742 op.records.add('obsmarkers', {'new': new})
1743 if op.reply is not None:
1743 if op.reply is not None:
1744 rpart = op.reply.newpart('reply:obsmarkers')
1744 rpart = op.reply.newpart('reply:obsmarkers')
1745 rpart.addparam('in-reply-to', str(inpart.id), mandatory=False)
1745 rpart.addparam('in-reply-to', str(inpart.id), mandatory=False)
1746 rpart.addparam('new', '%i' % new, mandatory=False)
1746 rpart.addparam('new', '%i' % new, mandatory=False)
1747
1747
1748
1748
1749 @parthandler('reply:obsmarkers', ('new', 'in-reply-to'))
1749 @parthandler('reply:obsmarkers', ('new', 'in-reply-to'))
1750 def handleobsmarkerreply(op, inpart):
1750 def handleobsmarkerreply(op, inpart):
1751 """retrieve the result of a pushkey request"""
1751 """retrieve the result of a pushkey request"""
1752 ret = int(inpart.params['new'])
1752 ret = int(inpart.params['new'])
1753 partid = int(inpart.params['in-reply-to'])
1753 partid = int(inpart.params['in-reply-to'])
1754 op.records.add('obsmarkers', {'new': ret}, partid)
1754 op.records.add('obsmarkers', {'new': ret}, partid)
1755
1755
1756 @parthandler('hgtagsfnodes')
1756 @parthandler('hgtagsfnodes')
1757 def handlehgtagsfnodes(op, inpart):
1757 def handlehgtagsfnodes(op, inpart):
1758 """Applies .hgtags fnodes cache entries to the local repo.
1758 """Applies .hgtags fnodes cache entries to the local repo.
1759
1759
1760 Payload is pairs of 20 byte changeset nodes and filenodes.
1760 Payload is pairs of 20 byte changeset nodes and filenodes.
1761 """
1761 """
1762 # Grab the transaction so we ensure that we have the lock at this point.
1762 # Grab the transaction so we ensure that we have the lock at this point.
1763 if op.ui.configbool('experimental', 'bundle2lazylocking'):
1763 if op.ui.configbool('experimental', 'bundle2lazylocking'):
1764 op.gettransaction()
1764 op.gettransaction()
1765 cache = tags.hgtagsfnodescache(op.repo.unfiltered())
1765 cache = tags.hgtagsfnodescache(op.repo.unfiltered())
1766
1766
1767 count = 0
1767 count = 0
1768 while True:
1768 while True:
1769 node = inpart.read(20)
1769 node = inpart.read(20)
1770 fnode = inpart.read(20)
1770 fnode = inpart.read(20)
1771 if len(node) < 20 or len(fnode) < 20:
1771 if len(node) < 20 or len(fnode) < 20:
1772 op.ui.debug('ignoring incomplete received .hgtags fnodes data\n')
1772 op.ui.debug('ignoring incomplete received .hgtags fnodes data\n')
1773 break
1773 break
1774 cache.setfnode(node, fnode)
1774 cache.setfnode(node, fnode)
1775 count += 1
1775 count += 1
1776
1776
1777 cache.write()
1777 cache.write()
1778 op.ui.debug('applied %i hgtags fnodes cache entries\n' % count)
1778 op.ui.debug('applied %i hgtags fnodes cache entries\n' % count)
@@ -1,2131 +1,2131 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 updating to branch default
70 updating to branch default
71 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
71 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
72
72
73 $ config=b/.hg/hgrc
73 $ config=b/.hg/hgrc
74
74
75 Extension disabled for lack of a hook
75 Extension disabled for lack of a hook
76
76
77 $ do_push fred
77 $ do_push fred
78 Pushing as user fred
78 Pushing as user fred
79 hgrc = """
79 hgrc = """
80 """
80 """
81 pushing to ../b
81 pushing to ../b
82 query 1; heads
82 query 1; heads
83 searching for changes
83 searching for changes
84 all remote heads known locally
84 all remote heads known locally
85 listing keys for "phases"
85 listing keys for "phases"
86 checking for updated bookmarks
86 checking for updated bookmarks
87 listing keys for "bookmarks"
87 listing keys for "bookmarks"
88 listing keys for "bookmarks"
88 listing keys for "bookmarks"
89 3 changesets found
89 3 changesets found
90 list of changesets:
90 list of changesets:
91 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
91 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
92 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
92 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
93 911600dab2ae7a9baff75958b84fe606851ce955
93 911600dab2ae7a9baff75958b84fe606851ce955
94 bundle2-output-bundle: "HG20", 4 parts total
94 bundle2-output-bundle: "HG20", 4 parts total
95 bundle2-output-part: "replycaps" 155 bytes payload
95 bundle2-output-part: "replycaps" 155 bytes payload
96 bundle2-output-part: "check:heads" streamed payload
96 bundle2-output-part: "check:heads" streamed payload
97 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
97 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
98 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
98 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
99 bundle2-input-bundle: with-transaction
99 bundle2-input-bundle: with-transaction
100 bundle2-input-part: "replycaps" supported
100 bundle2-input-part: "replycaps" supported
101 bundle2-input-part: total payload size 155
101 bundle2-input-part: total payload size 155
102 bundle2-input-part: "check:heads" supported
102 bundle2-input-part: "check:heads" supported
103 bundle2-input-part: total payload size 20
103 bundle2-input-part: total payload size 20
104 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
104 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
105 adding changesets
105 adding changesets
106 add changeset ef1ea85a6374
106 add changeset ef1ea85a6374
107 add changeset f9cafe1212c8
107 add changeset f9cafe1212c8
108 add changeset 911600dab2ae
108 add changeset 911600dab2ae
109 adding manifests
109 adding manifests
110 adding file changes
110 adding file changes
111 adding foo/Bar/file.txt revisions
111 adding foo/Bar/file.txt revisions
112 adding foo/file.txt revisions
112 adding foo/file.txt revisions
113 adding quux/file.py revisions
113 adding quux/file.py revisions
114 added 3 changesets with 3 changes to 3 files
114 added 3 changesets with 3 changes to 3 files
115 bundle2-input-part: total payload size 1553
115 bundle2-input-part: total payload size 1553
116 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
116 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
117 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
117 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
118 bundle2-input-bundle: 3 parts total
118 bundle2-input-bundle: 3 parts total
119 updating the branch cache
119 updating the branch cache
120 bundle2-output-bundle: "HG20", 2 parts total
120 bundle2-output-bundle: "HG20", 2 parts total
121 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
121 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
122 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
122 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
123 bundle2-input-bundle: with-transaction
123 bundle2-input-bundle: no-transaction
124 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
124 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
125 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
125 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
126 bundle2-input-bundle: 1 parts total
126 bundle2-input-bundle: 1 parts total
127 listing keys for "phases"
127 listing keys for "phases"
128 repository tip rolled back to revision 0 (undo push)
128 repository tip rolled back to revision 0 (undo push)
129 0:6675d58eff77
129 0:6675d58eff77
130
130
131
131
132 $ echo '[hooks]' >> $config
132 $ echo '[hooks]' >> $config
133 $ echo 'pretxnchangegroup.acl = python:hgext.acl.hook' >> $config
133 $ echo 'pretxnchangegroup.acl = python:hgext.acl.hook' >> $config
134
134
135 Extension disabled for lack of acl.sources
135 Extension disabled for lack of acl.sources
136
136
137 $ do_push fred
137 $ do_push fred
138 Pushing as user fred
138 Pushing as user fred
139 hgrc = """
139 hgrc = """
140 [hooks]
140 [hooks]
141 pretxnchangegroup.acl = python:hgext.acl.hook
141 pretxnchangegroup.acl = python:hgext.acl.hook
142 """
142 """
143 pushing to ../b
143 pushing to ../b
144 query 1; heads
144 query 1; heads
145 searching for changes
145 searching for changes
146 all remote heads known locally
146 all remote heads known locally
147 listing keys for "phases"
147 listing keys for "phases"
148 checking for updated bookmarks
148 checking for updated bookmarks
149 listing keys for "bookmarks"
149 listing keys for "bookmarks"
150 listing keys for "bookmarks"
150 listing keys for "bookmarks"
151 3 changesets found
151 3 changesets found
152 list of changesets:
152 list of changesets:
153 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
153 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
154 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
154 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
155 911600dab2ae7a9baff75958b84fe606851ce955
155 911600dab2ae7a9baff75958b84fe606851ce955
156 bundle2-output-bundle: "HG20", 4 parts total
156 bundle2-output-bundle: "HG20", 4 parts total
157 bundle2-output-part: "replycaps" 155 bytes payload
157 bundle2-output-part: "replycaps" 155 bytes payload
158 bundle2-output-part: "check:heads" streamed payload
158 bundle2-output-part: "check:heads" streamed payload
159 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
159 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
160 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
160 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
161 bundle2-input-bundle: with-transaction
161 bundle2-input-bundle: with-transaction
162 bundle2-input-part: "replycaps" supported
162 bundle2-input-part: "replycaps" supported
163 bundle2-input-part: total payload size 155
163 bundle2-input-part: total payload size 155
164 bundle2-input-part: "check:heads" supported
164 bundle2-input-part: "check:heads" supported
165 bundle2-input-part: total payload size 20
165 bundle2-input-part: total payload size 20
166 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
166 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
167 adding changesets
167 adding changesets
168 add changeset ef1ea85a6374
168 add changeset ef1ea85a6374
169 add changeset f9cafe1212c8
169 add changeset f9cafe1212c8
170 add changeset 911600dab2ae
170 add changeset 911600dab2ae
171 adding manifests
171 adding manifests
172 adding file changes
172 adding file changes
173 adding foo/Bar/file.txt revisions
173 adding foo/Bar/file.txt revisions
174 adding foo/file.txt revisions
174 adding foo/file.txt revisions
175 adding quux/file.py revisions
175 adding quux/file.py revisions
176 added 3 changesets with 3 changes to 3 files
176 added 3 changesets with 3 changes to 3 files
177 calling hook pretxnchangegroup.acl: hgext.acl.hook
177 calling hook pretxnchangegroup.acl: hgext.acl.hook
178 acl: changes have source "push" - skipping
178 acl: changes have source "push" - skipping
179 bundle2-input-part: total payload size 1553
179 bundle2-input-part: total payload size 1553
180 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
180 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
181 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
181 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
182 bundle2-input-bundle: 3 parts total
182 bundle2-input-bundle: 3 parts total
183 updating the branch cache
183 updating the branch cache
184 bundle2-output-bundle: "HG20", 2 parts total
184 bundle2-output-bundle: "HG20", 2 parts total
185 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
185 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
186 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
186 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
187 bundle2-input-bundle: with-transaction
187 bundle2-input-bundle: no-transaction
188 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
188 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
189 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
189 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
190 bundle2-input-bundle: 1 parts total
190 bundle2-input-bundle: 1 parts total
191 listing keys for "phases"
191 listing keys for "phases"
192 repository tip rolled back to revision 0 (undo push)
192 repository tip rolled back to revision 0 (undo push)
193 0:6675d58eff77
193 0:6675d58eff77
194
194
195
195
196 No [acl.allow]/[acl.deny]
196 No [acl.allow]/[acl.deny]
197
197
198 $ echo '[acl]' >> $config
198 $ echo '[acl]' >> $config
199 $ echo 'sources = push' >> $config
199 $ echo 'sources = push' >> $config
200 $ do_push fred
200 $ do_push fred
201 Pushing as user fred
201 Pushing as user fred
202 hgrc = """
202 hgrc = """
203 [hooks]
203 [hooks]
204 pretxnchangegroup.acl = python:hgext.acl.hook
204 pretxnchangegroup.acl = python:hgext.acl.hook
205 [acl]
205 [acl]
206 sources = push
206 sources = push
207 """
207 """
208 pushing to ../b
208 pushing to ../b
209 query 1; heads
209 query 1; heads
210 searching for changes
210 searching for changes
211 all remote heads known locally
211 all remote heads known locally
212 listing keys for "phases"
212 listing keys for "phases"
213 checking for updated bookmarks
213 checking for updated bookmarks
214 listing keys for "bookmarks"
214 listing keys for "bookmarks"
215 listing keys for "bookmarks"
215 listing keys for "bookmarks"
216 3 changesets found
216 3 changesets found
217 list of changesets:
217 list of changesets:
218 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
218 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
219 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
219 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
220 911600dab2ae7a9baff75958b84fe606851ce955
220 911600dab2ae7a9baff75958b84fe606851ce955
221 bundle2-output-bundle: "HG20", 4 parts total
221 bundle2-output-bundle: "HG20", 4 parts total
222 bundle2-output-part: "replycaps" 155 bytes payload
222 bundle2-output-part: "replycaps" 155 bytes payload
223 bundle2-output-part: "check:heads" streamed payload
223 bundle2-output-part: "check:heads" streamed payload
224 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
224 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
225 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
225 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
226 bundle2-input-bundle: with-transaction
226 bundle2-input-bundle: with-transaction
227 bundle2-input-part: "replycaps" supported
227 bundle2-input-part: "replycaps" supported
228 bundle2-input-part: total payload size 155
228 bundle2-input-part: total payload size 155
229 bundle2-input-part: "check:heads" supported
229 bundle2-input-part: "check:heads" supported
230 bundle2-input-part: total payload size 20
230 bundle2-input-part: total payload size 20
231 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
231 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
232 adding changesets
232 adding changesets
233 add changeset ef1ea85a6374
233 add changeset ef1ea85a6374
234 add changeset f9cafe1212c8
234 add changeset f9cafe1212c8
235 add changeset 911600dab2ae
235 add changeset 911600dab2ae
236 adding manifests
236 adding manifests
237 adding file changes
237 adding file changes
238 adding foo/Bar/file.txt revisions
238 adding foo/Bar/file.txt revisions
239 adding foo/file.txt revisions
239 adding foo/file.txt revisions
240 adding quux/file.py revisions
240 adding quux/file.py revisions
241 added 3 changesets with 3 changes to 3 files
241 added 3 changesets with 3 changes to 3 files
242 calling hook pretxnchangegroup.acl: hgext.acl.hook
242 calling hook pretxnchangegroup.acl: hgext.acl.hook
243 acl: checking access for user "fred"
243 acl: checking access for user "fred"
244 acl: acl.allow.branches not enabled
244 acl: acl.allow.branches not enabled
245 acl: acl.deny.branches not enabled
245 acl: acl.deny.branches not enabled
246 acl: acl.allow not enabled
246 acl: acl.allow not enabled
247 acl: acl.deny not enabled
247 acl: acl.deny not enabled
248 acl: branch access granted: "ef1ea85a6374" on branch "default"
248 acl: branch access granted: "ef1ea85a6374" on branch "default"
249 acl: path access granted: "ef1ea85a6374"
249 acl: path access granted: "ef1ea85a6374"
250 acl: branch access granted: "f9cafe1212c8" on branch "default"
250 acl: branch access granted: "f9cafe1212c8" on branch "default"
251 acl: path access granted: "f9cafe1212c8"
251 acl: path access granted: "f9cafe1212c8"
252 acl: branch access granted: "911600dab2ae" on branch "default"
252 acl: branch access granted: "911600dab2ae" on branch "default"
253 acl: path access granted: "911600dab2ae"
253 acl: path access granted: "911600dab2ae"
254 bundle2-input-part: total payload size 1553
254 bundle2-input-part: total payload size 1553
255 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
255 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
256 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
256 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
257 bundle2-input-bundle: 3 parts total
257 bundle2-input-bundle: 3 parts total
258 updating the branch cache
258 updating the branch cache
259 bundle2-output-bundle: "HG20", 2 parts total
259 bundle2-output-bundle: "HG20", 2 parts total
260 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
260 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
261 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
261 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
262 bundle2-input-bundle: with-transaction
262 bundle2-input-bundle: no-transaction
263 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
263 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
264 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
264 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
265 bundle2-input-bundle: 1 parts total
265 bundle2-input-bundle: 1 parts total
266 listing keys for "phases"
266 listing keys for "phases"
267 repository tip rolled back to revision 0 (undo push)
267 repository tip rolled back to revision 0 (undo push)
268 0:6675d58eff77
268 0:6675d58eff77
269
269
270
270
271 Empty [acl.allow]
271 Empty [acl.allow]
272
272
273 $ echo '[acl.allow]' >> $config
273 $ echo '[acl.allow]' >> $config
274 $ do_push fred
274 $ do_push fred
275 Pushing as user fred
275 Pushing as user fred
276 hgrc = """
276 hgrc = """
277 [hooks]
277 [hooks]
278 pretxnchangegroup.acl = python:hgext.acl.hook
278 pretxnchangegroup.acl = python:hgext.acl.hook
279 [acl]
279 [acl]
280 sources = push
280 sources = push
281 [acl.allow]
281 [acl.allow]
282 """
282 """
283 pushing to ../b
283 pushing to ../b
284 query 1; heads
284 query 1; heads
285 searching for changes
285 searching for changes
286 all remote heads known locally
286 all remote heads known locally
287 listing keys for "phases"
287 listing keys for "phases"
288 checking for updated bookmarks
288 checking for updated bookmarks
289 listing keys for "bookmarks"
289 listing keys for "bookmarks"
290 listing keys for "bookmarks"
290 listing keys for "bookmarks"
291 3 changesets found
291 3 changesets found
292 list of changesets:
292 list of changesets:
293 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
293 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
294 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
294 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
295 911600dab2ae7a9baff75958b84fe606851ce955
295 911600dab2ae7a9baff75958b84fe606851ce955
296 bundle2-output-bundle: "HG20", 4 parts total
296 bundle2-output-bundle: "HG20", 4 parts total
297 bundle2-output-part: "replycaps" 155 bytes payload
297 bundle2-output-part: "replycaps" 155 bytes payload
298 bundle2-output-part: "check:heads" streamed payload
298 bundle2-output-part: "check:heads" streamed payload
299 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
299 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
300 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
300 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
301 bundle2-input-bundle: with-transaction
301 bundle2-input-bundle: with-transaction
302 bundle2-input-part: "replycaps" supported
302 bundle2-input-part: "replycaps" supported
303 bundle2-input-part: total payload size 155
303 bundle2-input-part: total payload size 155
304 bundle2-input-part: "check:heads" supported
304 bundle2-input-part: "check:heads" supported
305 bundle2-input-part: total payload size 20
305 bundle2-input-part: total payload size 20
306 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
306 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
307 adding changesets
307 adding changesets
308 add changeset ef1ea85a6374
308 add changeset ef1ea85a6374
309 add changeset f9cafe1212c8
309 add changeset f9cafe1212c8
310 add changeset 911600dab2ae
310 add changeset 911600dab2ae
311 adding manifests
311 adding manifests
312 adding file changes
312 adding file changes
313 adding foo/Bar/file.txt revisions
313 adding foo/Bar/file.txt revisions
314 adding foo/file.txt revisions
314 adding foo/file.txt revisions
315 adding quux/file.py revisions
315 adding quux/file.py revisions
316 added 3 changesets with 3 changes to 3 files
316 added 3 changesets with 3 changes to 3 files
317 calling hook pretxnchangegroup.acl: hgext.acl.hook
317 calling hook pretxnchangegroup.acl: hgext.acl.hook
318 acl: checking access for user "fred"
318 acl: checking access for user "fred"
319 acl: acl.allow.branches not enabled
319 acl: acl.allow.branches not enabled
320 acl: acl.deny.branches not enabled
320 acl: acl.deny.branches not enabled
321 acl: acl.allow enabled, 0 entries for user fred
321 acl: acl.allow enabled, 0 entries for user fred
322 acl: acl.deny not enabled
322 acl: acl.deny not enabled
323 acl: branch access granted: "ef1ea85a6374" on branch "default"
323 acl: branch access granted: "ef1ea85a6374" on branch "default"
324 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
324 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
325 bundle2-input-part: total payload size 1553
325 bundle2-input-part: total payload size 1553
326 bundle2-input-bundle: 3 parts total
326 bundle2-input-bundle: 3 parts total
327 transaction abort!
327 transaction abort!
328 rollback completed
328 rollback completed
329 abort: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
329 abort: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
330 no rollback information available
330 no rollback information available
331 0:6675d58eff77
331 0:6675d58eff77
332
332
333
333
334 fred is allowed inside foo/
334 fred is allowed inside foo/
335
335
336 $ echo 'foo/** = fred' >> $config
336 $ echo 'foo/** = fred' >> $config
337 $ do_push fred
337 $ do_push fred
338 Pushing as user fred
338 Pushing as user fred
339 hgrc = """
339 hgrc = """
340 [hooks]
340 [hooks]
341 pretxnchangegroup.acl = python:hgext.acl.hook
341 pretxnchangegroup.acl = python:hgext.acl.hook
342 [acl]
342 [acl]
343 sources = push
343 sources = push
344 [acl.allow]
344 [acl.allow]
345 foo/** = fred
345 foo/** = fred
346 """
346 """
347 pushing to ../b
347 pushing to ../b
348 query 1; heads
348 query 1; heads
349 searching for changes
349 searching for changes
350 all remote heads known locally
350 all remote heads known locally
351 listing keys for "phases"
351 listing keys for "phases"
352 checking for updated bookmarks
352 checking for updated bookmarks
353 listing keys for "bookmarks"
353 listing keys for "bookmarks"
354 listing keys for "bookmarks"
354 listing keys for "bookmarks"
355 3 changesets found
355 3 changesets found
356 list of changesets:
356 list of changesets:
357 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
357 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
358 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
358 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
359 911600dab2ae7a9baff75958b84fe606851ce955
359 911600dab2ae7a9baff75958b84fe606851ce955
360 bundle2-output-bundle: "HG20", 4 parts total
360 bundle2-output-bundle: "HG20", 4 parts total
361 bundle2-output-part: "replycaps" 155 bytes payload
361 bundle2-output-part: "replycaps" 155 bytes payload
362 bundle2-output-part: "check:heads" streamed payload
362 bundle2-output-part: "check:heads" streamed payload
363 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
363 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
364 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
364 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
365 bundle2-input-bundle: with-transaction
365 bundle2-input-bundle: with-transaction
366 bundle2-input-part: "replycaps" supported
366 bundle2-input-part: "replycaps" supported
367 bundle2-input-part: total payload size 155
367 bundle2-input-part: total payload size 155
368 bundle2-input-part: "check:heads" supported
368 bundle2-input-part: "check:heads" supported
369 bundle2-input-part: total payload size 20
369 bundle2-input-part: total payload size 20
370 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
370 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
371 adding changesets
371 adding changesets
372 add changeset ef1ea85a6374
372 add changeset ef1ea85a6374
373 add changeset f9cafe1212c8
373 add changeset f9cafe1212c8
374 add changeset 911600dab2ae
374 add changeset 911600dab2ae
375 adding manifests
375 adding manifests
376 adding file changes
376 adding file changes
377 adding foo/Bar/file.txt revisions
377 adding foo/Bar/file.txt revisions
378 adding foo/file.txt revisions
378 adding foo/file.txt revisions
379 adding quux/file.py revisions
379 adding quux/file.py revisions
380 added 3 changesets with 3 changes to 3 files
380 added 3 changesets with 3 changes to 3 files
381 calling hook pretxnchangegroup.acl: hgext.acl.hook
381 calling hook pretxnchangegroup.acl: hgext.acl.hook
382 acl: checking access for user "fred"
382 acl: checking access for user "fred"
383 acl: acl.allow.branches not enabled
383 acl: acl.allow.branches not enabled
384 acl: acl.deny.branches not enabled
384 acl: acl.deny.branches not enabled
385 acl: acl.allow enabled, 1 entries for user fred
385 acl: acl.allow enabled, 1 entries for user fred
386 acl: acl.deny not enabled
386 acl: acl.deny not enabled
387 acl: branch access granted: "ef1ea85a6374" on branch "default"
387 acl: branch access granted: "ef1ea85a6374" on branch "default"
388 acl: path access granted: "ef1ea85a6374"
388 acl: path access granted: "ef1ea85a6374"
389 acl: branch access granted: "f9cafe1212c8" on branch "default"
389 acl: branch access granted: "f9cafe1212c8" on branch "default"
390 acl: path access granted: "f9cafe1212c8"
390 acl: path access granted: "f9cafe1212c8"
391 acl: branch access granted: "911600dab2ae" on branch "default"
391 acl: branch access granted: "911600dab2ae" on branch "default"
392 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
392 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
393 bundle2-input-part: total payload size 1553
393 bundle2-input-part: total payload size 1553
394 bundle2-input-bundle: 3 parts total
394 bundle2-input-bundle: 3 parts total
395 transaction abort!
395 transaction abort!
396 rollback completed
396 rollback completed
397 abort: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
397 abort: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
398 no rollback information available
398 no rollback information available
399 0:6675d58eff77
399 0:6675d58eff77
400
400
401
401
402 Empty [acl.deny]
402 Empty [acl.deny]
403
403
404 $ echo '[acl.deny]' >> $config
404 $ echo '[acl.deny]' >> $config
405 $ do_push barney
405 $ do_push barney
406 Pushing as user barney
406 Pushing as user barney
407 hgrc = """
407 hgrc = """
408 [hooks]
408 [hooks]
409 pretxnchangegroup.acl = python:hgext.acl.hook
409 pretxnchangegroup.acl = python:hgext.acl.hook
410 [acl]
410 [acl]
411 sources = push
411 sources = push
412 [acl.allow]
412 [acl.allow]
413 foo/** = fred
413 foo/** = fred
414 [acl.deny]
414 [acl.deny]
415 """
415 """
416 pushing to ../b
416 pushing to ../b
417 query 1; heads
417 query 1; heads
418 searching for changes
418 searching for changes
419 all remote heads known locally
419 all remote heads known locally
420 listing keys for "phases"
420 listing keys for "phases"
421 checking for updated bookmarks
421 checking for updated bookmarks
422 listing keys for "bookmarks"
422 listing keys for "bookmarks"
423 listing keys for "bookmarks"
423 listing keys for "bookmarks"
424 3 changesets found
424 3 changesets found
425 list of changesets:
425 list of changesets:
426 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
426 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
427 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
427 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
428 911600dab2ae7a9baff75958b84fe606851ce955
428 911600dab2ae7a9baff75958b84fe606851ce955
429 bundle2-output-bundle: "HG20", 4 parts total
429 bundle2-output-bundle: "HG20", 4 parts total
430 bundle2-output-part: "replycaps" 155 bytes payload
430 bundle2-output-part: "replycaps" 155 bytes payload
431 bundle2-output-part: "check:heads" streamed payload
431 bundle2-output-part: "check:heads" streamed payload
432 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
432 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
433 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
433 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
434 bundle2-input-bundle: with-transaction
434 bundle2-input-bundle: with-transaction
435 bundle2-input-part: "replycaps" supported
435 bundle2-input-part: "replycaps" supported
436 bundle2-input-part: total payload size 155
436 bundle2-input-part: total payload size 155
437 bundle2-input-part: "check:heads" supported
437 bundle2-input-part: "check:heads" supported
438 bundle2-input-part: total payload size 20
438 bundle2-input-part: total payload size 20
439 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
439 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
440 adding changesets
440 adding changesets
441 add changeset ef1ea85a6374
441 add changeset ef1ea85a6374
442 add changeset f9cafe1212c8
442 add changeset f9cafe1212c8
443 add changeset 911600dab2ae
443 add changeset 911600dab2ae
444 adding manifests
444 adding manifests
445 adding file changes
445 adding file changes
446 adding foo/Bar/file.txt revisions
446 adding foo/Bar/file.txt revisions
447 adding foo/file.txt revisions
447 adding foo/file.txt revisions
448 adding quux/file.py revisions
448 adding quux/file.py revisions
449 added 3 changesets with 3 changes to 3 files
449 added 3 changesets with 3 changes to 3 files
450 calling hook pretxnchangegroup.acl: hgext.acl.hook
450 calling hook pretxnchangegroup.acl: hgext.acl.hook
451 acl: checking access for user "barney"
451 acl: checking access for user "barney"
452 acl: acl.allow.branches not enabled
452 acl: acl.allow.branches not enabled
453 acl: acl.deny.branches not enabled
453 acl: acl.deny.branches not enabled
454 acl: acl.allow enabled, 0 entries for user barney
454 acl: acl.allow enabled, 0 entries for user barney
455 acl: acl.deny enabled, 0 entries for user barney
455 acl: acl.deny enabled, 0 entries for user barney
456 acl: branch access granted: "ef1ea85a6374" on branch "default"
456 acl: branch access granted: "ef1ea85a6374" on branch "default"
457 error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
457 error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
458 bundle2-input-part: total payload size 1553
458 bundle2-input-part: total payload size 1553
459 bundle2-input-bundle: 3 parts total
459 bundle2-input-bundle: 3 parts total
460 transaction abort!
460 transaction abort!
461 rollback completed
461 rollback completed
462 abort: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
462 abort: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
463 no rollback information available
463 no rollback information available
464 0:6675d58eff77
464 0:6675d58eff77
465
465
466
466
467 fred is allowed inside foo/, but not foo/bar/ (case matters)
467 fred is allowed inside foo/, but not foo/bar/ (case matters)
468
468
469 $ echo 'foo/bar/** = fred' >> $config
469 $ echo 'foo/bar/** = fred' >> $config
470 $ do_push fred
470 $ do_push fred
471 Pushing as user fred
471 Pushing as user fred
472 hgrc = """
472 hgrc = """
473 [hooks]
473 [hooks]
474 pretxnchangegroup.acl = python:hgext.acl.hook
474 pretxnchangegroup.acl = python:hgext.acl.hook
475 [acl]
475 [acl]
476 sources = push
476 sources = push
477 [acl.allow]
477 [acl.allow]
478 foo/** = fred
478 foo/** = fred
479 [acl.deny]
479 [acl.deny]
480 foo/bar/** = fred
480 foo/bar/** = fred
481 """
481 """
482 pushing to ../b
482 pushing to ../b
483 query 1; heads
483 query 1; heads
484 searching for changes
484 searching for changes
485 all remote heads known locally
485 all remote heads known locally
486 listing keys for "phases"
486 listing keys for "phases"
487 checking for updated bookmarks
487 checking for updated bookmarks
488 listing keys for "bookmarks"
488 listing keys for "bookmarks"
489 listing keys for "bookmarks"
489 listing keys for "bookmarks"
490 3 changesets found
490 3 changesets found
491 list of changesets:
491 list of changesets:
492 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
492 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
493 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
493 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
494 911600dab2ae7a9baff75958b84fe606851ce955
494 911600dab2ae7a9baff75958b84fe606851ce955
495 bundle2-output-bundle: "HG20", 4 parts total
495 bundle2-output-bundle: "HG20", 4 parts total
496 bundle2-output-part: "replycaps" 155 bytes payload
496 bundle2-output-part: "replycaps" 155 bytes payload
497 bundle2-output-part: "check:heads" streamed payload
497 bundle2-output-part: "check:heads" streamed payload
498 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
498 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
499 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
499 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
500 bundle2-input-bundle: with-transaction
500 bundle2-input-bundle: with-transaction
501 bundle2-input-part: "replycaps" supported
501 bundle2-input-part: "replycaps" supported
502 bundle2-input-part: total payload size 155
502 bundle2-input-part: total payload size 155
503 bundle2-input-part: "check:heads" supported
503 bundle2-input-part: "check:heads" supported
504 bundle2-input-part: total payload size 20
504 bundle2-input-part: total payload size 20
505 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
505 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
506 adding changesets
506 adding changesets
507 add changeset ef1ea85a6374
507 add changeset ef1ea85a6374
508 add changeset f9cafe1212c8
508 add changeset f9cafe1212c8
509 add changeset 911600dab2ae
509 add changeset 911600dab2ae
510 adding manifests
510 adding manifests
511 adding file changes
511 adding file changes
512 adding foo/Bar/file.txt revisions
512 adding foo/Bar/file.txt revisions
513 adding foo/file.txt revisions
513 adding foo/file.txt revisions
514 adding quux/file.py revisions
514 adding quux/file.py revisions
515 added 3 changesets with 3 changes to 3 files
515 added 3 changesets with 3 changes to 3 files
516 calling hook pretxnchangegroup.acl: hgext.acl.hook
516 calling hook pretxnchangegroup.acl: hgext.acl.hook
517 acl: checking access for user "fred"
517 acl: checking access for user "fred"
518 acl: acl.allow.branches not enabled
518 acl: acl.allow.branches not enabled
519 acl: acl.deny.branches not enabled
519 acl: acl.deny.branches not enabled
520 acl: acl.allow enabled, 1 entries for user fred
520 acl: acl.allow enabled, 1 entries for user fred
521 acl: acl.deny enabled, 1 entries for user fred
521 acl: acl.deny enabled, 1 entries for user fred
522 acl: branch access granted: "ef1ea85a6374" on branch "default"
522 acl: branch access granted: "ef1ea85a6374" on branch "default"
523 acl: path access granted: "ef1ea85a6374"
523 acl: path access granted: "ef1ea85a6374"
524 acl: branch access granted: "f9cafe1212c8" on branch "default"
524 acl: branch access granted: "f9cafe1212c8" on branch "default"
525 acl: path access granted: "f9cafe1212c8"
525 acl: path access granted: "f9cafe1212c8"
526 acl: branch access granted: "911600dab2ae" on branch "default"
526 acl: branch access granted: "911600dab2ae" on branch "default"
527 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
527 error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
528 bundle2-input-part: total payload size 1553
528 bundle2-input-part: total payload size 1553
529 bundle2-input-bundle: 3 parts total
529 bundle2-input-bundle: 3 parts total
530 transaction abort!
530 transaction abort!
531 rollback completed
531 rollback completed
532 abort: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
532 abort: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae")
533 no rollback information available
533 no rollback information available
534 0:6675d58eff77
534 0:6675d58eff77
535
535
536
536
537 fred is allowed inside foo/, but not foo/Bar/
537 fred is allowed inside foo/, but not foo/Bar/
538
538
539 $ echo 'foo/Bar/** = fred' >> $config
539 $ echo 'foo/Bar/** = fred' >> $config
540 $ do_push fred
540 $ do_push fred
541 Pushing as user fred
541 Pushing as user fred
542 hgrc = """
542 hgrc = """
543 [hooks]
543 [hooks]
544 pretxnchangegroup.acl = python:hgext.acl.hook
544 pretxnchangegroup.acl = python:hgext.acl.hook
545 [acl]
545 [acl]
546 sources = push
546 sources = push
547 [acl.allow]
547 [acl.allow]
548 foo/** = fred
548 foo/** = fred
549 [acl.deny]
549 [acl.deny]
550 foo/bar/** = fred
550 foo/bar/** = fred
551 foo/Bar/** = fred
551 foo/Bar/** = fred
552 """
552 """
553 pushing to ../b
553 pushing to ../b
554 query 1; heads
554 query 1; heads
555 searching for changes
555 searching for changes
556 all remote heads known locally
556 all remote heads known locally
557 listing keys for "phases"
557 listing keys for "phases"
558 checking for updated bookmarks
558 checking for updated bookmarks
559 listing keys for "bookmarks"
559 listing keys for "bookmarks"
560 listing keys for "bookmarks"
560 listing keys for "bookmarks"
561 3 changesets found
561 3 changesets found
562 list of changesets:
562 list of changesets:
563 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
563 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
564 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
564 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
565 911600dab2ae7a9baff75958b84fe606851ce955
565 911600dab2ae7a9baff75958b84fe606851ce955
566 bundle2-output-bundle: "HG20", 4 parts total
566 bundle2-output-bundle: "HG20", 4 parts total
567 bundle2-output-part: "replycaps" 155 bytes payload
567 bundle2-output-part: "replycaps" 155 bytes payload
568 bundle2-output-part: "check:heads" streamed payload
568 bundle2-output-part: "check:heads" streamed payload
569 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
569 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
570 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
570 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
571 bundle2-input-bundle: with-transaction
571 bundle2-input-bundle: with-transaction
572 bundle2-input-part: "replycaps" supported
572 bundle2-input-part: "replycaps" supported
573 bundle2-input-part: total payload size 155
573 bundle2-input-part: total payload size 155
574 bundle2-input-part: "check:heads" supported
574 bundle2-input-part: "check:heads" supported
575 bundle2-input-part: total payload size 20
575 bundle2-input-part: total payload size 20
576 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
576 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
577 adding changesets
577 adding changesets
578 add changeset ef1ea85a6374
578 add changeset ef1ea85a6374
579 add changeset f9cafe1212c8
579 add changeset f9cafe1212c8
580 add changeset 911600dab2ae
580 add changeset 911600dab2ae
581 adding manifests
581 adding manifests
582 adding file changes
582 adding file changes
583 adding foo/Bar/file.txt revisions
583 adding foo/Bar/file.txt revisions
584 adding foo/file.txt revisions
584 adding foo/file.txt revisions
585 adding quux/file.py revisions
585 adding quux/file.py revisions
586 added 3 changesets with 3 changes to 3 files
586 added 3 changesets with 3 changes to 3 files
587 calling hook pretxnchangegroup.acl: hgext.acl.hook
587 calling hook pretxnchangegroup.acl: hgext.acl.hook
588 acl: checking access for user "fred"
588 acl: checking access for user "fred"
589 acl: acl.allow.branches not enabled
589 acl: acl.allow.branches not enabled
590 acl: acl.deny.branches not enabled
590 acl: acl.deny.branches not enabled
591 acl: acl.allow enabled, 1 entries for user fred
591 acl: acl.allow enabled, 1 entries for user fred
592 acl: acl.deny enabled, 2 entries for user fred
592 acl: acl.deny enabled, 2 entries for user fred
593 acl: branch access granted: "ef1ea85a6374" on branch "default"
593 acl: branch access granted: "ef1ea85a6374" on branch "default"
594 acl: path access granted: "ef1ea85a6374"
594 acl: path access granted: "ef1ea85a6374"
595 acl: branch access granted: "f9cafe1212c8" on branch "default"
595 acl: branch access granted: "f9cafe1212c8" on branch "default"
596 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
596 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
597 bundle2-input-part: total payload size 1553
597 bundle2-input-part: total payload size 1553
598 bundle2-input-bundle: 3 parts total
598 bundle2-input-bundle: 3 parts total
599 transaction abort!
599 transaction abort!
600 rollback completed
600 rollback completed
601 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
601 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
602 no rollback information available
602 no rollback information available
603 0:6675d58eff77
603 0:6675d58eff77
604
604
605
605
606 $ echo 'barney is not mentioned => not allowed anywhere'
606 $ echo 'barney is not mentioned => not allowed anywhere'
607 barney is not mentioned => not allowed anywhere
607 barney is not mentioned => not allowed anywhere
608 $ do_push barney
608 $ do_push barney
609 Pushing as user barney
609 Pushing as user barney
610 hgrc = """
610 hgrc = """
611 [hooks]
611 [hooks]
612 pretxnchangegroup.acl = python:hgext.acl.hook
612 pretxnchangegroup.acl = python:hgext.acl.hook
613 [acl]
613 [acl]
614 sources = push
614 sources = push
615 [acl.allow]
615 [acl.allow]
616 foo/** = fred
616 foo/** = fred
617 [acl.deny]
617 [acl.deny]
618 foo/bar/** = fred
618 foo/bar/** = fred
619 foo/Bar/** = fred
619 foo/Bar/** = fred
620 """
620 """
621 pushing to ../b
621 pushing to ../b
622 query 1; heads
622 query 1; heads
623 searching for changes
623 searching for changes
624 all remote heads known locally
624 all remote heads known locally
625 listing keys for "phases"
625 listing keys for "phases"
626 checking for updated bookmarks
626 checking for updated bookmarks
627 listing keys for "bookmarks"
627 listing keys for "bookmarks"
628 listing keys for "bookmarks"
628 listing keys for "bookmarks"
629 3 changesets found
629 3 changesets found
630 list of changesets:
630 list of changesets:
631 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
631 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
632 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
632 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
633 911600dab2ae7a9baff75958b84fe606851ce955
633 911600dab2ae7a9baff75958b84fe606851ce955
634 bundle2-output-bundle: "HG20", 4 parts total
634 bundle2-output-bundle: "HG20", 4 parts total
635 bundle2-output-part: "replycaps" 155 bytes payload
635 bundle2-output-part: "replycaps" 155 bytes payload
636 bundle2-output-part: "check:heads" streamed payload
636 bundle2-output-part: "check:heads" streamed payload
637 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
637 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
638 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
638 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
639 bundle2-input-bundle: with-transaction
639 bundle2-input-bundle: with-transaction
640 bundle2-input-part: "replycaps" supported
640 bundle2-input-part: "replycaps" supported
641 bundle2-input-part: total payload size 155
641 bundle2-input-part: total payload size 155
642 bundle2-input-part: "check:heads" supported
642 bundle2-input-part: "check:heads" supported
643 bundle2-input-part: total payload size 20
643 bundle2-input-part: total payload size 20
644 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
644 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
645 adding changesets
645 adding changesets
646 add changeset ef1ea85a6374
646 add changeset ef1ea85a6374
647 add changeset f9cafe1212c8
647 add changeset f9cafe1212c8
648 add changeset 911600dab2ae
648 add changeset 911600dab2ae
649 adding manifests
649 adding manifests
650 adding file changes
650 adding file changes
651 adding foo/Bar/file.txt revisions
651 adding foo/Bar/file.txt revisions
652 adding foo/file.txt revisions
652 adding foo/file.txt revisions
653 adding quux/file.py revisions
653 adding quux/file.py revisions
654 added 3 changesets with 3 changes to 3 files
654 added 3 changesets with 3 changes to 3 files
655 calling hook pretxnchangegroup.acl: hgext.acl.hook
655 calling hook pretxnchangegroup.acl: hgext.acl.hook
656 acl: checking access for user "barney"
656 acl: checking access for user "barney"
657 acl: acl.allow.branches not enabled
657 acl: acl.allow.branches not enabled
658 acl: acl.deny.branches not enabled
658 acl: acl.deny.branches not enabled
659 acl: acl.allow enabled, 0 entries for user barney
659 acl: acl.allow enabled, 0 entries for user barney
660 acl: acl.deny enabled, 0 entries for user barney
660 acl: acl.deny enabled, 0 entries for user barney
661 acl: branch access granted: "ef1ea85a6374" on branch "default"
661 acl: branch access granted: "ef1ea85a6374" on branch "default"
662 error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
662 error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
663 bundle2-input-part: total payload size 1553
663 bundle2-input-part: total payload size 1553
664 bundle2-input-bundle: 3 parts total
664 bundle2-input-bundle: 3 parts total
665 transaction abort!
665 transaction abort!
666 rollback completed
666 rollback completed
667 abort: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
667 abort: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
668 no rollback information available
668 no rollback information available
669 0:6675d58eff77
669 0:6675d58eff77
670
670
671
671
672 barney is allowed everywhere
672 barney is allowed everywhere
673
673
674 $ echo '[acl.allow]' >> $config
674 $ echo '[acl.allow]' >> $config
675 $ echo '** = barney' >> $config
675 $ echo '** = barney' >> $config
676 $ do_push barney
676 $ do_push barney
677 Pushing as user barney
677 Pushing as user barney
678 hgrc = """
678 hgrc = """
679 [hooks]
679 [hooks]
680 pretxnchangegroup.acl = python:hgext.acl.hook
680 pretxnchangegroup.acl = python:hgext.acl.hook
681 [acl]
681 [acl]
682 sources = push
682 sources = push
683 [acl.allow]
683 [acl.allow]
684 foo/** = fred
684 foo/** = fred
685 [acl.deny]
685 [acl.deny]
686 foo/bar/** = fred
686 foo/bar/** = fred
687 foo/Bar/** = fred
687 foo/Bar/** = fred
688 [acl.allow]
688 [acl.allow]
689 ** = barney
689 ** = barney
690 """
690 """
691 pushing to ../b
691 pushing to ../b
692 query 1; heads
692 query 1; heads
693 searching for changes
693 searching for changes
694 all remote heads known locally
694 all remote heads known locally
695 listing keys for "phases"
695 listing keys for "phases"
696 checking for updated bookmarks
696 checking for updated bookmarks
697 listing keys for "bookmarks"
697 listing keys for "bookmarks"
698 listing keys for "bookmarks"
698 listing keys for "bookmarks"
699 3 changesets found
699 3 changesets found
700 list of changesets:
700 list of changesets:
701 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
701 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
702 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
702 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
703 911600dab2ae7a9baff75958b84fe606851ce955
703 911600dab2ae7a9baff75958b84fe606851ce955
704 bundle2-output-bundle: "HG20", 4 parts total
704 bundle2-output-bundle: "HG20", 4 parts total
705 bundle2-output-part: "replycaps" 155 bytes payload
705 bundle2-output-part: "replycaps" 155 bytes payload
706 bundle2-output-part: "check:heads" streamed payload
706 bundle2-output-part: "check:heads" streamed payload
707 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
707 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
708 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
708 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
709 bundle2-input-bundle: with-transaction
709 bundle2-input-bundle: with-transaction
710 bundle2-input-part: "replycaps" supported
710 bundle2-input-part: "replycaps" supported
711 bundle2-input-part: total payload size 155
711 bundle2-input-part: total payload size 155
712 bundle2-input-part: "check:heads" supported
712 bundle2-input-part: "check:heads" supported
713 bundle2-input-part: total payload size 20
713 bundle2-input-part: total payload size 20
714 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
714 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
715 adding changesets
715 adding changesets
716 add changeset ef1ea85a6374
716 add changeset ef1ea85a6374
717 add changeset f9cafe1212c8
717 add changeset f9cafe1212c8
718 add changeset 911600dab2ae
718 add changeset 911600dab2ae
719 adding manifests
719 adding manifests
720 adding file changes
720 adding file changes
721 adding foo/Bar/file.txt revisions
721 adding foo/Bar/file.txt revisions
722 adding foo/file.txt revisions
722 adding foo/file.txt revisions
723 adding quux/file.py revisions
723 adding quux/file.py revisions
724 added 3 changesets with 3 changes to 3 files
724 added 3 changesets with 3 changes to 3 files
725 calling hook pretxnchangegroup.acl: hgext.acl.hook
725 calling hook pretxnchangegroup.acl: hgext.acl.hook
726 acl: checking access for user "barney"
726 acl: checking access for user "barney"
727 acl: acl.allow.branches not enabled
727 acl: acl.allow.branches not enabled
728 acl: acl.deny.branches not enabled
728 acl: acl.deny.branches not enabled
729 acl: acl.allow enabled, 1 entries for user barney
729 acl: acl.allow enabled, 1 entries for user barney
730 acl: acl.deny enabled, 0 entries for user barney
730 acl: acl.deny enabled, 0 entries for user barney
731 acl: branch access granted: "ef1ea85a6374" on branch "default"
731 acl: branch access granted: "ef1ea85a6374" on branch "default"
732 acl: path access granted: "ef1ea85a6374"
732 acl: path access granted: "ef1ea85a6374"
733 acl: branch access granted: "f9cafe1212c8" on branch "default"
733 acl: branch access granted: "f9cafe1212c8" on branch "default"
734 acl: path access granted: "f9cafe1212c8"
734 acl: path access granted: "f9cafe1212c8"
735 acl: branch access granted: "911600dab2ae" on branch "default"
735 acl: branch access granted: "911600dab2ae" on branch "default"
736 acl: path access granted: "911600dab2ae"
736 acl: path access granted: "911600dab2ae"
737 bundle2-input-part: total payload size 1553
737 bundle2-input-part: total payload size 1553
738 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
738 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
739 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
739 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
740 bundle2-input-bundle: 3 parts total
740 bundle2-input-bundle: 3 parts total
741 updating the branch cache
741 updating the branch cache
742 bundle2-output-bundle: "HG20", 2 parts total
742 bundle2-output-bundle: "HG20", 2 parts total
743 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
743 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
744 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
744 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
745 bundle2-input-bundle: with-transaction
745 bundle2-input-bundle: no-transaction
746 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
746 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
747 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
747 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
748 bundle2-input-bundle: 1 parts total
748 bundle2-input-bundle: 1 parts total
749 listing keys for "phases"
749 listing keys for "phases"
750 repository tip rolled back to revision 0 (undo push)
750 repository tip rolled back to revision 0 (undo push)
751 0:6675d58eff77
751 0:6675d58eff77
752
752
753
753
754 wilma can change files with a .txt extension
754 wilma can change files with a .txt extension
755
755
756 $ echo '**/*.txt = wilma' >> $config
756 $ echo '**/*.txt = wilma' >> $config
757 $ do_push wilma
757 $ do_push wilma
758 Pushing as user wilma
758 Pushing as user wilma
759 hgrc = """
759 hgrc = """
760 [hooks]
760 [hooks]
761 pretxnchangegroup.acl = python:hgext.acl.hook
761 pretxnchangegroup.acl = python:hgext.acl.hook
762 [acl]
762 [acl]
763 sources = push
763 sources = push
764 [acl.allow]
764 [acl.allow]
765 foo/** = fred
765 foo/** = fred
766 [acl.deny]
766 [acl.deny]
767 foo/bar/** = fred
767 foo/bar/** = fred
768 foo/Bar/** = fred
768 foo/Bar/** = fred
769 [acl.allow]
769 [acl.allow]
770 ** = barney
770 ** = barney
771 **/*.txt = wilma
771 **/*.txt = wilma
772 """
772 """
773 pushing to ../b
773 pushing to ../b
774 query 1; heads
774 query 1; heads
775 searching for changes
775 searching for changes
776 all remote heads known locally
776 all remote heads known locally
777 listing keys for "phases"
777 listing keys for "phases"
778 checking for updated bookmarks
778 checking for updated bookmarks
779 listing keys for "bookmarks"
779 listing keys for "bookmarks"
780 listing keys for "bookmarks"
780 listing keys for "bookmarks"
781 3 changesets found
781 3 changesets found
782 list of changesets:
782 list of changesets:
783 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
783 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
784 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
784 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
785 911600dab2ae7a9baff75958b84fe606851ce955
785 911600dab2ae7a9baff75958b84fe606851ce955
786 bundle2-output-bundle: "HG20", 4 parts total
786 bundle2-output-bundle: "HG20", 4 parts total
787 bundle2-output-part: "replycaps" 155 bytes payload
787 bundle2-output-part: "replycaps" 155 bytes payload
788 bundle2-output-part: "check:heads" streamed payload
788 bundle2-output-part: "check:heads" streamed payload
789 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
789 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
790 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
790 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
791 bundle2-input-bundle: with-transaction
791 bundle2-input-bundle: with-transaction
792 bundle2-input-part: "replycaps" supported
792 bundle2-input-part: "replycaps" supported
793 bundle2-input-part: total payload size 155
793 bundle2-input-part: total payload size 155
794 bundle2-input-part: "check:heads" supported
794 bundle2-input-part: "check:heads" supported
795 bundle2-input-part: total payload size 20
795 bundle2-input-part: total payload size 20
796 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
796 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
797 adding changesets
797 adding changesets
798 add changeset ef1ea85a6374
798 add changeset ef1ea85a6374
799 add changeset f9cafe1212c8
799 add changeset f9cafe1212c8
800 add changeset 911600dab2ae
800 add changeset 911600dab2ae
801 adding manifests
801 adding manifests
802 adding file changes
802 adding file changes
803 adding foo/Bar/file.txt revisions
803 adding foo/Bar/file.txt revisions
804 adding foo/file.txt revisions
804 adding foo/file.txt revisions
805 adding quux/file.py revisions
805 adding quux/file.py revisions
806 added 3 changesets with 3 changes to 3 files
806 added 3 changesets with 3 changes to 3 files
807 calling hook pretxnchangegroup.acl: hgext.acl.hook
807 calling hook pretxnchangegroup.acl: hgext.acl.hook
808 acl: checking access for user "wilma"
808 acl: checking access for user "wilma"
809 acl: acl.allow.branches not enabled
809 acl: acl.allow.branches not enabled
810 acl: acl.deny.branches not enabled
810 acl: acl.deny.branches not enabled
811 acl: acl.allow enabled, 1 entries for user wilma
811 acl: acl.allow enabled, 1 entries for user wilma
812 acl: acl.deny enabled, 0 entries for user wilma
812 acl: acl.deny enabled, 0 entries for user wilma
813 acl: branch access granted: "ef1ea85a6374" on branch "default"
813 acl: branch access granted: "ef1ea85a6374" on branch "default"
814 acl: path access granted: "ef1ea85a6374"
814 acl: path access granted: "ef1ea85a6374"
815 acl: branch access granted: "f9cafe1212c8" on branch "default"
815 acl: branch access granted: "f9cafe1212c8" on branch "default"
816 acl: path access granted: "f9cafe1212c8"
816 acl: path access granted: "f9cafe1212c8"
817 acl: branch access granted: "911600dab2ae" on branch "default"
817 acl: branch access granted: "911600dab2ae" on branch "default"
818 error: pretxnchangegroup.acl hook failed: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae")
818 error: pretxnchangegroup.acl hook failed: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae")
819 bundle2-input-part: total payload size 1553
819 bundle2-input-part: total payload size 1553
820 bundle2-input-bundle: 3 parts total
820 bundle2-input-bundle: 3 parts total
821 transaction abort!
821 transaction abort!
822 rollback completed
822 rollback completed
823 abort: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae")
823 abort: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae")
824 no rollback information available
824 no rollback information available
825 0:6675d58eff77
825 0:6675d58eff77
826
826
827
827
828 file specified by acl.config does not exist
828 file specified by acl.config does not exist
829
829
830 $ echo '[acl]' >> $config
830 $ echo '[acl]' >> $config
831 $ echo 'config = ../acl.config' >> $config
831 $ echo 'config = ../acl.config' >> $config
832 $ do_push barney
832 $ do_push barney
833 Pushing as user barney
833 Pushing as user barney
834 hgrc = """
834 hgrc = """
835 [hooks]
835 [hooks]
836 pretxnchangegroup.acl = python:hgext.acl.hook
836 pretxnchangegroup.acl = python:hgext.acl.hook
837 [acl]
837 [acl]
838 sources = push
838 sources = push
839 [acl.allow]
839 [acl.allow]
840 foo/** = fred
840 foo/** = fred
841 [acl.deny]
841 [acl.deny]
842 foo/bar/** = fred
842 foo/bar/** = fred
843 foo/Bar/** = fred
843 foo/Bar/** = fred
844 [acl.allow]
844 [acl.allow]
845 ** = barney
845 ** = barney
846 **/*.txt = wilma
846 **/*.txt = wilma
847 [acl]
847 [acl]
848 config = ../acl.config
848 config = ../acl.config
849 """
849 """
850 pushing to ../b
850 pushing to ../b
851 query 1; heads
851 query 1; heads
852 searching for changes
852 searching for changes
853 all remote heads known locally
853 all remote heads known locally
854 listing keys for "phases"
854 listing keys for "phases"
855 checking for updated bookmarks
855 checking for updated bookmarks
856 listing keys for "bookmarks"
856 listing keys for "bookmarks"
857 listing keys for "bookmarks"
857 listing keys for "bookmarks"
858 3 changesets found
858 3 changesets found
859 list of changesets:
859 list of changesets:
860 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
860 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
861 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
861 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
862 911600dab2ae7a9baff75958b84fe606851ce955
862 911600dab2ae7a9baff75958b84fe606851ce955
863 bundle2-output-bundle: "HG20", 4 parts total
863 bundle2-output-bundle: "HG20", 4 parts total
864 bundle2-output-part: "replycaps" 155 bytes payload
864 bundle2-output-part: "replycaps" 155 bytes payload
865 bundle2-output-part: "check:heads" streamed payload
865 bundle2-output-part: "check:heads" streamed payload
866 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
866 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
867 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
867 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
868 bundle2-input-bundle: with-transaction
868 bundle2-input-bundle: with-transaction
869 bundle2-input-part: "replycaps" supported
869 bundle2-input-part: "replycaps" supported
870 bundle2-input-part: total payload size 155
870 bundle2-input-part: total payload size 155
871 bundle2-input-part: "check:heads" supported
871 bundle2-input-part: "check:heads" supported
872 bundle2-input-part: total payload size 20
872 bundle2-input-part: total payload size 20
873 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
873 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
874 adding changesets
874 adding changesets
875 add changeset ef1ea85a6374
875 add changeset ef1ea85a6374
876 add changeset f9cafe1212c8
876 add changeset f9cafe1212c8
877 add changeset 911600dab2ae
877 add changeset 911600dab2ae
878 adding manifests
878 adding manifests
879 adding file changes
879 adding file changes
880 adding foo/Bar/file.txt revisions
880 adding foo/Bar/file.txt revisions
881 adding foo/file.txt revisions
881 adding foo/file.txt revisions
882 adding quux/file.py revisions
882 adding quux/file.py revisions
883 added 3 changesets with 3 changes to 3 files
883 added 3 changesets with 3 changes to 3 files
884 calling hook pretxnchangegroup.acl: hgext.acl.hook
884 calling hook pretxnchangegroup.acl: hgext.acl.hook
885 acl: checking access for user "barney"
885 acl: checking access for user "barney"
886 error: pretxnchangegroup.acl hook raised an exception: [Errno 2] No such file or directory: '../acl.config'
886 error: pretxnchangegroup.acl hook raised an exception: [Errno 2] No such file or directory: '../acl.config'
887 bundle2-input-part: total payload size 1553
887 bundle2-input-part: total payload size 1553
888 bundle2-input-bundle: 3 parts total
888 bundle2-input-bundle: 3 parts total
889 transaction abort!
889 transaction abort!
890 rollback completed
890 rollback completed
891 abort: No such file or directory: ../acl.config
891 abort: No such file or directory: ../acl.config
892 no rollback information available
892 no rollback information available
893 0:6675d58eff77
893 0:6675d58eff77
894
894
895
895
896 betty is allowed inside foo/ by a acl.config file
896 betty is allowed inside foo/ by a acl.config file
897
897
898 $ echo '[acl.allow]' >> acl.config
898 $ echo '[acl.allow]' >> acl.config
899 $ echo 'foo/** = betty' >> acl.config
899 $ echo 'foo/** = betty' >> acl.config
900 $ do_push betty
900 $ do_push betty
901 Pushing as user betty
901 Pushing as user betty
902 hgrc = """
902 hgrc = """
903 [hooks]
903 [hooks]
904 pretxnchangegroup.acl = python:hgext.acl.hook
904 pretxnchangegroup.acl = python:hgext.acl.hook
905 [acl]
905 [acl]
906 sources = push
906 sources = push
907 [acl.allow]
907 [acl.allow]
908 foo/** = fred
908 foo/** = fred
909 [acl.deny]
909 [acl.deny]
910 foo/bar/** = fred
910 foo/bar/** = fred
911 foo/Bar/** = fred
911 foo/Bar/** = fred
912 [acl.allow]
912 [acl.allow]
913 ** = barney
913 ** = barney
914 **/*.txt = wilma
914 **/*.txt = wilma
915 [acl]
915 [acl]
916 config = ../acl.config
916 config = ../acl.config
917 """
917 """
918 acl.config = """
918 acl.config = """
919 [acl.allow]
919 [acl.allow]
920 foo/** = betty
920 foo/** = betty
921 """
921 """
922 pushing to ../b
922 pushing to ../b
923 query 1; heads
923 query 1; heads
924 searching for changes
924 searching for changes
925 all remote heads known locally
925 all remote heads known locally
926 listing keys for "phases"
926 listing keys for "phases"
927 checking for updated bookmarks
927 checking for updated bookmarks
928 listing keys for "bookmarks"
928 listing keys for "bookmarks"
929 listing keys for "bookmarks"
929 listing keys for "bookmarks"
930 3 changesets found
930 3 changesets found
931 list of changesets:
931 list of changesets:
932 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
932 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
933 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
933 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
934 911600dab2ae7a9baff75958b84fe606851ce955
934 911600dab2ae7a9baff75958b84fe606851ce955
935 bundle2-output-bundle: "HG20", 4 parts total
935 bundle2-output-bundle: "HG20", 4 parts total
936 bundle2-output-part: "replycaps" 155 bytes payload
936 bundle2-output-part: "replycaps" 155 bytes payload
937 bundle2-output-part: "check:heads" streamed payload
937 bundle2-output-part: "check:heads" streamed payload
938 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
938 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
939 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
939 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
940 bundle2-input-bundle: with-transaction
940 bundle2-input-bundle: with-transaction
941 bundle2-input-part: "replycaps" supported
941 bundle2-input-part: "replycaps" supported
942 bundle2-input-part: total payload size 155
942 bundle2-input-part: total payload size 155
943 bundle2-input-part: "check:heads" supported
943 bundle2-input-part: "check:heads" supported
944 bundle2-input-part: total payload size 20
944 bundle2-input-part: total payload size 20
945 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
945 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
946 adding changesets
946 adding changesets
947 add changeset ef1ea85a6374
947 add changeset ef1ea85a6374
948 add changeset f9cafe1212c8
948 add changeset f9cafe1212c8
949 add changeset 911600dab2ae
949 add changeset 911600dab2ae
950 adding manifests
950 adding manifests
951 adding file changes
951 adding file changes
952 adding foo/Bar/file.txt revisions
952 adding foo/Bar/file.txt revisions
953 adding foo/file.txt revisions
953 adding foo/file.txt revisions
954 adding quux/file.py revisions
954 adding quux/file.py revisions
955 added 3 changesets with 3 changes to 3 files
955 added 3 changesets with 3 changes to 3 files
956 calling hook pretxnchangegroup.acl: hgext.acl.hook
956 calling hook pretxnchangegroup.acl: hgext.acl.hook
957 acl: checking access for user "betty"
957 acl: checking access for user "betty"
958 acl: acl.allow.branches not enabled
958 acl: acl.allow.branches not enabled
959 acl: acl.deny.branches not enabled
959 acl: acl.deny.branches not enabled
960 acl: acl.allow enabled, 1 entries for user betty
960 acl: acl.allow enabled, 1 entries for user betty
961 acl: acl.deny enabled, 0 entries for user betty
961 acl: acl.deny enabled, 0 entries for user betty
962 acl: branch access granted: "ef1ea85a6374" on branch "default"
962 acl: branch access granted: "ef1ea85a6374" on branch "default"
963 acl: path access granted: "ef1ea85a6374"
963 acl: path access granted: "ef1ea85a6374"
964 acl: branch access granted: "f9cafe1212c8" on branch "default"
964 acl: branch access granted: "f9cafe1212c8" on branch "default"
965 acl: path access granted: "f9cafe1212c8"
965 acl: path access granted: "f9cafe1212c8"
966 acl: branch access granted: "911600dab2ae" on branch "default"
966 acl: branch access granted: "911600dab2ae" on branch "default"
967 error: pretxnchangegroup.acl hook failed: acl: user "betty" not allowed on "quux/file.py" (changeset "911600dab2ae")
967 error: pretxnchangegroup.acl hook failed: acl: user "betty" not allowed on "quux/file.py" (changeset "911600dab2ae")
968 bundle2-input-part: total payload size 1553
968 bundle2-input-part: total payload size 1553
969 bundle2-input-bundle: 3 parts total
969 bundle2-input-bundle: 3 parts total
970 transaction abort!
970 transaction abort!
971 rollback completed
971 rollback completed
972 abort: acl: user "betty" not allowed on "quux/file.py" (changeset "911600dab2ae")
972 abort: acl: user "betty" not allowed on "quux/file.py" (changeset "911600dab2ae")
973 no rollback information available
973 no rollback information available
974 0:6675d58eff77
974 0:6675d58eff77
975
975
976
976
977 acl.config can set only [acl.allow]/[acl.deny]
977 acl.config can set only [acl.allow]/[acl.deny]
978
978
979 $ echo '[hooks]' >> acl.config
979 $ echo '[hooks]' >> acl.config
980 $ echo 'changegroup.acl = false' >> acl.config
980 $ echo 'changegroup.acl = false' >> acl.config
981 $ do_push barney
981 $ do_push barney
982 Pushing as user barney
982 Pushing as user barney
983 hgrc = """
983 hgrc = """
984 [hooks]
984 [hooks]
985 pretxnchangegroup.acl = python:hgext.acl.hook
985 pretxnchangegroup.acl = python:hgext.acl.hook
986 [acl]
986 [acl]
987 sources = push
987 sources = push
988 [acl.allow]
988 [acl.allow]
989 foo/** = fred
989 foo/** = fred
990 [acl.deny]
990 [acl.deny]
991 foo/bar/** = fred
991 foo/bar/** = fred
992 foo/Bar/** = fred
992 foo/Bar/** = fred
993 [acl.allow]
993 [acl.allow]
994 ** = barney
994 ** = barney
995 **/*.txt = wilma
995 **/*.txt = wilma
996 [acl]
996 [acl]
997 config = ../acl.config
997 config = ../acl.config
998 """
998 """
999 acl.config = """
999 acl.config = """
1000 [acl.allow]
1000 [acl.allow]
1001 foo/** = betty
1001 foo/** = betty
1002 [hooks]
1002 [hooks]
1003 changegroup.acl = false
1003 changegroup.acl = false
1004 """
1004 """
1005 pushing to ../b
1005 pushing to ../b
1006 query 1; heads
1006 query 1; heads
1007 searching for changes
1007 searching for changes
1008 all remote heads known locally
1008 all remote heads known locally
1009 listing keys for "phases"
1009 listing keys for "phases"
1010 checking for updated bookmarks
1010 checking for updated bookmarks
1011 listing keys for "bookmarks"
1011 listing keys for "bookmarks"
1012 listing keys for "bookmarks"
1012 listing keys for "bookmarks"
1013 3 changesets found
1013 3 changesets found
1014 list of changesets:
1014 list of changesets:
1015 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1015 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1016 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1016 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1017 911600dab2ae7a9baff75958b84fe606851ce955
1017 911600dab2ae7a9baff75958b84fe606851ce955
1018 bundle2-output-bundle: "HG20", 4 parts total
1018 bundle2-output-bundle: "HG20", 4 parts total
1019 bundle2-output-part: "replycaps" 155 bytes payload
1019 bundle2-output-part: "replycaps" 155 bytes payload
1020 bundle2-output-part: "check:heads" streamed payload
1020 bundle2-output-part: "check:heads" streamed payload
1021 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1021 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1022 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1022 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1023 bundle2-input-bundle: with-transaction
1023 bundle2-input-bundle: with-transaction
1024 bundle2-input-part: "replycaps" supported
1024 bundle2-input-part: "replycaps" supported
1025 bundle2-input-part: total payload size 155
1025 bundle2-input-part: total payload size 155
1026 bundle2-input-part: "check:heads" supported
1026 bundle2-input-part: "check:heads" supported
1027 bundle2-input-part: total payload size 20
1027 bundle2-input-part: total payload size 20
1028 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1028 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1029 adding changesets
1029 adding changesets
1030 add changeset ef1ea85a6374
1030 add changeset ef1ea85a6374
1031 add changeset f9cafe1212c8
1031 add changeset f9cafe1212c8
1032 add changeset 911600dab2ae
1032 add changeset 911600dab2ae
1033 adding manifests
1033 adding manifests
1034 adding file changes
1034 adding file changes
1035 adding foo/Bar/file.txt revisions
1035 adding foo/Bar/file.txt revisions
1036 adding foo/file.txt revisions
1036 adding foo/file.txt revisions
1037 adding quux/file.py revisions
1037 adding quux/file.py revisions
1038 added 3 changesets with 3 changes to 3 files
1038 added 3 changesets with 3 changes to 3 files
1039 calling hook pretxnchangegroup.acl: hgext.acl.hook
1039 calling hook pretxnchangegroup.acl: hgext.acl.hook
1040 acl: checking access for user "barney"
1040 acl: checking access for user "barney"
1041 acl: acl.allow.branches not enabled
1041 acl: acl.allow.branches not enabled
1042 acl: acl.deny.branches not enabled
1042 acl: acl.deny.branches not enabled
1043 acl: acl.allow enabled, 1 entries for user barney
1043 acl: acl.allow enabled, 1 entries for user barney
1044 acl: acl.deny enabled, 0 entries for user barney
1044 acl: acl.deny enabled, 0 entries for user barney
1045 acl: branch access granted: "ef1ea85a6374" on branch "default"
1045 acl: branch access granted: "ef1ea85a6374" on branch "default"
1046 acl: path access granted: "ef1ea85a6374"
1046 acl: path access granted: "ef1ea85a6374"
1047 acl: branch access granted: "f9cafe1212c8" on branch "default"
1047 acl: branch access granted: "f9cafe1212c8" on branch "default"
1048 acl: path access granted: "f9cafe1212c8"
1048 acl: path access granted: "f9cafe1212c8"
1049 acl: branch access granted: "911600dab2ae" on branch "default"
1049 acl: branch access granted: "911600dab2ae" on branch "default"
1050 acl: path access granted: "911600dab2ae"
1050 acl: path access granted: "911600dab2ae"
1051 bundle2-input-part: total payload size 1553
1051 bundle2-input-part: total payload size 1553
1052 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
1052 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
1053 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
1053 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
1054 bundle2-input-bundle: 3 parts total
1054 bundle2-input-bundle: 3 parts total
1055 updating the branch cache
1055 updating the branch cache
1056 bundle2-output-bundle: "HG20", 2 parts total
1056 bundle2-output-bundle: "HG20", 2 parts total
1057 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1057 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1058 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1058 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1059 bundle2-input-bundle: with-transaction
1059 bundle2-input-bundle: no-transaction
1060 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1060 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1061 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1061 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1062 bundle2-input-bundle: 1 parts total
1062 bundle2-input-bundle: 1 parts total
1063 listing keys for "phases"
1063 listing keys for "phases"
1064 repository tip rolled back to revision 0 (undo push)
1064 repository tip rolled back to revision 0 (undo push)
1065 0:6675d58eff77
1065 0:6675d58eff77
1066
1066
1067
1067
1068 asterisk
1068 asterisk
1069
1069
1070 $ init_config
1070 $ init_config
1071
1071
1072 asterisk test
1072 asterisk test
1073
1073
1074 $ echo '[acl.allow]' >> $config
1074 $ echo '[acl.allow]' >> $config
1075 $ echo "** = fred" >> $config
1075 $ echo "** = fred" >> $config
1076
1076
1077 fred is always allowed
1077 fred is always allowed
1078
1078
1079 $ do_push fred
1079 $ do_push fred
1080 Pushing as user fred
1080 Pushing as user fred
1081 hgrc = """
1081 hgrc = """
1082 [hooks]
1082 [hooks]
1083 pretxnchangegroup.acl = python:hgext.acl.hook
1083 pretxnchangegroup.acl = python:hgext.acl.hook
1084 [acl]
1084 [acl]
1085 sources = push
1085 sources = push
1086 [extensions]
1086 [extensions]
1087 [acl.allow]
1087 [acl.allow]
1088 ** = fred
1088 ** = fred
1089 """
1089 """
1090 pushing to ../b
1090 pushing to ../b
1091 query 1; heads
1091 query 1; heads
1092 searching for changes
1092 searching for changes
1093 all remote heads known locally
1093 all remote heads known locally
1094 listing keys for "phases"
1094 listing keys for "phases"
1095 checking for updated bookmarks
1095 checking for updated bookmarks
1096 listing keys for "bookmarks"
1096 listing keys for "bookmarks"
1097 listing keys for "bookmarks"
1097 listing keys for "bookmarks"
1098 3 changesets found
1098 3 changesets found
1099 list of changesets:
1099 list of changesets:
1100 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1100 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1101 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1101 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1102 911600dab2ae7a9baff75958b84fe606851ce955
1102 911600dab2ae7a9baff75958b84fe606851ce955
1103 bundle2-output-bundle: "HG20", 4 parts total
1103 bundle2-output-bundle: "HG20", 4 parts total
1104 bundle2-output-part: "replycaps" 155 bytes payload
1104 bundle2-output-part: "replycaps" 155 bytes payload
1105 bundle2-output-part: "check:heads" streamed payload
1105 bundle2-output-part: "check:heads" streamed payload
1106 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1106 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1107 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1107 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1108 bundle2-input-bundle: with-transaction
1108 bundle2-input-bundle: with-transaction
1109 bundle2-input-part: "replycaps" supported
1109 bundle2-input-part: "replycaps" supported
1110 bundle2-input-part: total payload size 155
1110 bundle2-input-part: total payload size 155
1111 bundle2-input-part: "check:heads" supported
1111 bundle2-input-part: "check:heads" supported
1112 bundle2-input-part: total payload size 20
1112 bundle2-input-part: total payload size 20
1113 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1113 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1114 adding changesets
1114 adding changesets
1115 add changeset ef1ea85a6374
1115 add changeset ef1ea85a6374
1116 add changeset f9cafe1212c8
1116 add changeset f9cafe1212c8
1117 add changeset 911600dab2ae
1117 add changeset 911600dab2ae
1118 adding manifests
1118 adding manifests
1119 adding file changes
1119 adding file changes
1120 adding foo/Bar/file.txt revisions
1120 adding foo/Bar/file.txt revisions
1121 adding foo/file.txt revisions
1121 adding foo/file.txt revisions
1122 adding quux/file.py revisions
1122 adding quux/file.py revisions
1123 added 3 changesets with 3 changes to 3 files
1123 added 3 changesets with 3 changes to 3 files
1124 calling hook pretxnchangegroup.acl: hgext.acl.hook
1124 calling hook pretxnchangegroup.acl: hgext.acl.hook
1125 acl: checking access for user "fred"
1125 acl: checking access for user "fred"
1126 acl: acl.allow.branches not enabled
1126 acl: acl.allow.branches not enabled
1127 acl: acl.deny.branches not enabled
1127 acl: acl.deny.branches not enabled
1128 acl: acl.allow enabled, 1 entries for user fred
1128 acl: acl.allow enabled, 1 entries for user fred
1129 acl: acl.deny not enabled
1129 acl: acl.deny not enabled
1130 acl: branch access granted: "ef1ea85a6374" on branch "default"
1130 acl: branch access granted: "ef1ea85a6374" on branch "default"
1131 acl: path access granted: "ef1ea85a6374"
1131 acl: path access granted: "ef1ea85a6374"
1132 acl: branch access granted: "f9cafe1212c8" on branch "default"
1132 acl: branch access granted: "f9cafe1212c8" on branch "default"
1133 acl: path access granted: "f9cafe1212c8"
1133 acl: path access granted: "f9cafe1212c8"
1134 acl: branch access granted: "911600dab2ae" on branch "default"
1134 acl: branch access granted: "911600dab2ae" on branch "default"
1135 acl: path access granted: "911600dab2ae"
1135 acl: path access granted: "911600dab2ae"
1136 bundle2-input-part: total payload size 1553
1136 bundle2-input-part: total payload size 1553
1137 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
1137 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
1138 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
1138 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
1139 bundle2-input-bundle: 3 parts total
1139 bundle2-input-bundle: 3 parts total
1140 updating the branch cache
1140 updating the branch cache
1141 bundle2-output-bundle: "HG20", 2 parts total
1141 bundle2-output-bundle: "HG20", 2 parts total
1142 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1142 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1143 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1143 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1144 bundle2-input-bundle: with-transaction
1144 bundle2-input-bundle: no-transaction
1145 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1145 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1146 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1146 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1147 bundle2-input-bundle: 1 parts total
1147 bundle2-input-bundle: 1 parts total
1148 listing keys for "phases"
1148 listing keys for "phases"
1149 repository tip rolled back to revision 0 (undo push)
1149 repository tip rolled back to revision 0 (undo push)
1150 0:6675d58eff77
1150 0:6675d58eff77
1151
1151
1152
1152
1153 $ echo '[acl.deny]' >> $config
1153 $ echo '[acl.deny]' >> $config
1154 $ echo "foo/Bar/** = *" >> $config
1154 $ echo "foo/Bar/** = *" >> $config
1155
1155
1156 no one is allowed inside foo/Bar/
1156 no one is allowed inside foo/Bar/
1157
1157
1158 $ do_push fred
1158 $ do_push fred
1159 Pushing as user fred
1159 Pushing as user fred
1160 hgrc = """
1160 hgrc = """
1161 [hooks]
1161 [hooks]
1162 pretxnchangegroup.acl = python:hgext.acl.hook
1162 pretxnchangegroup.acl = python:hgext.acl.hook
1163 [acl]
1163 [acl]
1164 sources = push
1164 sources = push
1165 [extensions]
1165 [extensions]
1166 [acl.allow]
1166 [acl.allow]
1167 ** = fred
1167 ** = fred
1168 [acl.deny]
1168 [acl.deny]
1169 foo/Bar/** = *
1169 foo/Bar/** = *
1170 """
1170 """
1171 pushing to ../b
1171 pushing to ../b
1172 query 1; heads
1172 query 1; heads
1173 searching for changes
1173 searching for changes
1174 all remote heads known locally
1174 all remote heads known locally
1175 listing keys for "phases"
1175 listing keys for "phases"
1176 checking for updated bookmarks
1176 checking for updated bookmarks
1177 listing keys for "bookmarks"
1177 listing keys for "bookmarks"
1178 listing keys for "bookmarks"
1178 listing keys for "bookmarks"
1179 3 changesets found
1179 3 changesets found
1180 list of changesets:
1180 list of changesets:
1181 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1181 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1182 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1182 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1183 911600dab2ae7a9baff75958b84fe606851ce955
1183 911600dab2ae7a9baff75958b84fe606851ce955
1184 bundle2-output-bundle: "HG20", 4 parts total
1184 bundle2-output-bundle: "HG20", 4 parts total
1185 bundle2-output-part: "replycaps" 155 bytes payload
1185 bundle2-output-part: "replycaps" 155 bytes payload
1186 bundle2-output-part: "check:heads" streamed payload
1186 bundle2-output-part: "check:heads" streamed payload
1187 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1187 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1188 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1188 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1189 bundle2-input-bundle: with-transaction
1189 bundle2-input-bundle: with-transaction
1190 bundle2-input-part: "replycaps" supported
1190 bundle2-input-part: "replycaps" supported
1191 bundle2-input-part: total payload size 155
1191 bundle2-input-part: total payload size 155
1192 bundle2-input-part: "check:heads" supported
1192 bundle2-input-part: "check:heads" supported
1193 bundle2-input-part: total payload size 20
1193 bundle2-input-part: total payload size 20
1194 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1194 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1195 adding changesets
1195 adding changesets
1196 add changeset ef1ea85a6374
1196 add changeset ef1ea85a6374
1197 add changeset f9cafe1212c8
1197 add changeset f9cafe1212c8
1198 add changeset 911600dab2ae
1198 add changeset 911600dab2ae
1199 adding manifests
1199 adding manifests
1200 adding file changes
1200 adding file changes
1201 adding foo/Bar/file.txt revisions
1201 adding foo/Bar/file.txt revisions
1202 adding foo/file.txt revisions
1202 adding foo/file.txt revisions
1203 adding quux/file.py revisions
1203 adding quux/file.py revisions
1204 added 3 changesets with 3 changes to 3 files
1204 added 3 changesets with 3 changes to 3 files
1205 calling hook pretxnchangegroup.acl: hgext.acl.hook
1205 calling hook pretxnchangegroup.acl: hgext.acl.hook
1206 acl: checking access for user "fred"
1206 acl: checking access for user "fred"
1207 acl: acl.allow.branches not enabled
1207 acl: acl.allow.branches not enabled
1208 acl: acl.deny.branches not enabled
1208 acl: acl.deny.branches not enabled
1209 acl: acl.allow enabled, 1 entries for user fred
1209 acl: acl.allow enabled, 1 entries for user fred
1210 acl: acl.deny enabled, 1 entries for user fred
1210 acl: acl.deny enabled, 1 entries for user fred
1211 acl: branch access granted: "ef1ea85a6374" on branch "default"
1211 acl: branch access granted: "ef1ea85a6374" on branch "default"
1212 acl: path access granted: "ef1ea85a6374"
1212 acl: path access granted: "ef1ea85a6374"
1213 acl: branch access granted: "f9cafe1212c8" on branch "default"
1213 acl: branch access granted: "f9cafe1212c8" on branch "default"
1214 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1214 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1215 bundle2-input-part: total payload size 1553
1215 bundle2-input-part: total payload size 1553
1216 bundle2-input-bundle: 3 parts total
1216 bundle2-input-bundle: 3 parts total
1217 transaction abort!
1217 transaction abort!
1218 rollback completed
1218 rollback completed
1219 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1219 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1220 no rollback information available
1220 no rollback information available
1221 0:6675d58eff77
1221 0:6675d58eff77
1222
1222
1223
1223
1224 Groups
1224 Groups
1225
1225
1226 $ init_config
1226 $ init_config
1227
1227
1228 OS-level groups
1228 OS-level groups
1229
1229
1230 $ echo '[acl.allow]' >> $config
1230 $ echo '[acl.allow]' >> $config
1231 $ echo "** = @group1" >> $config
1231 $ echo "** = @group1" >> $config
1232
1232
1233 @group1 is always allowed
1233 @group1 is always allowed
1234
1234
1235 $ do_push fred
1235 $ do_push fred
1236 Pushing as user fred
1236 Pushing as user fred
1237 hgrc = """
1237 hgrc = """
1238 [hooks]
1238 [hooks]
1239 pretxnchangegroup.acl = python:hgext.acl.hook
1239 pretxnchangegroup.acl = python:hgext.acl.hook
1240 [acl]
1240 [acl]
1241 sources = push
1241 sources = push
1242 [extensions]
1242 [extensions]
1243 [acl.allow]
1243 [acl.allow]
1244 ** = @group1
1244 ** = @group1
1245 """
1245 """
1246 pushing to ../b
1246 pushing to ../b
1247 query 1; heads
1247 query 1; heads
1248 searching for changes
1248 searching for changes
1249 all remote heads known locally
1249 all remote heads known locally
1250 listing keys for "phases"
1250 listing keys for "phases"
1251 checking for updated bookmarks
1251 checking for updated bookmarks
1252 listing keys for "bookmarks"
1252 listing keys for "bookmarks"
1253 listing keys for "bookmarks"
1253 listing keys for "bookmarks"
1254 3 changesets found
1254 3 changesets found
1255 list of changesets:
1255 list of changesets:
1256 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1256 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1257 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1257 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1258 911600dab2ae7a9baff75958b84fe606851ce955
1258 911600dab2ae7a9baff75958b84fe606851ce955
1259 bundle2-output-bundle: "HG20", 4 parts total
1259 bundle2-output-bundle: "HG20", 4 parts total
1260 bundle2-output-part: "replycaps" 155 bytes payload
1260 bundle2-output-part: "replycaps" 155 bytes payload
1261 bundle2-output-part: "check:heads" streamed payload
1261 bundle2-output-part: "check:heads" streamed payload
1262 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1262 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1263 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1263 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1264 bundle2-input-bundle: with-transaction
1264 bundle2-input-bundle: with-transaction
1265 bundle2-input-part: "replycaps" supported
1265 bundle2-input-part: "replycaps" supported
1266 bundle2-input-part: total payload size 155
1266 bundle2-input-part: total payload size 155
1267 bundle2-input-part: "check:heads" supported
1267 bundle2-input-part: "check:heads" supported
1268 bundle2-input-part: total payload size 20
1268 bundle2-input-part: total payload size 20
1269 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1269 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1270 adding changesets
1270 adding changesets
1271 add changeset ef1ea85a6374
1271 add changeset ef1ea85a6374
1272 add changeset f9cafe1212c8
1272 add changeset f9cafe1212c8
1273 add changeset 911600dab2ae
1273 add changeset 911600dab2ae
1274 adding manifests
1274 adding manifests
1275 adding file changes
1275 adding file changes
1276 adding foo/Bar/file.txt revisions
1276 adding foo/Bar/file.txt revisions
1277 adding foo/file.txt revisions
1277 adding foo/file.txt revisions
1278 adding quux/file.py revisions
1278 adding quux/file.py revisions
1279 added 3 changesets with 3 changes to 3 files
1279 added 3 changesets with 3 changes to 3 files
1280 calling hook pretxnchangegroup.acl: hgext.acl.hook
1280 calling hook pretxnchangegroup.acl: hgext.acl.hook
1281 acl: checking access for user "fred"
1281 acl: checking access for user "fred"
1282 acl: acl.allow.branches not enabled
1282 acl: acl.allow.branches not enabled
1283 acl: acl.deny.branches not enabled
1283 acl: acl.deny.branches not enabled
1284 acl: "group1" not defined in [acl.groups]
1284 acl: "group1" not defined in [acl.groups]
1285 acl: acl.allow enabled, 1 entries for user fred
1285 acl: acl.allow enabled, 1 entries for user fred
1286 acl: acl.deny not enabled
1286 acl: acl.deny not enabled
1287 acl: branch access granted: "ef1ea85a6374" on branch "default"
1287 acl: branch access granted: "ef1ea85a6374" on branch "default"
1288 acl: path access granted: "ef1ea85a6374"
1288 acl: path access granted: "ef1ea85a6374"
1289 acl: branch access granted: "f9cafe1212c8" on branch "default"
1289 acl: branch access granted: "f9cafe1212c8" on branch "default"
1290 acl: path access granted: "f9cafe1212c8"
1290 acl: path access granted: "f9cafe1212c8"
1291 acl: branch access granted: "911600dab2ae" on branch "default"
1291 acl: branch access granted: "911600dab2ae" on branch "default"
1292 acl: path access granted: "911600dab2ae"
1292 acl: path access granted: "911600dab2ae"
1293 bundle2-input-part: total payload size 1553
1293 bundle2-input-part: total payload size 1553
1294 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
1294 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
1295 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
1295 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
1296 bundle2-input-bundle: 3 parts total
1296 bundle2-input-bundle: 3 parts total
1297 updating the branch cache
1297 updating the branch cache
1298 bundle2-output-bundle: "HG20", 2 parts total
1298 bundle2-output-bundle: "HG20", 2 parts total
1299 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1299 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1300 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1300 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1301 bundle2-input-bundle: with-transaction
1301 bundle2-input-bundle: no-transaction
1302 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1302 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1303 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1303 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1304 bundle2-input-bundle: 1 parts total
1304 bundle2-input-bundle: 1 parts total
1305 listing keys for "phases"
1305 listing keys for "phases"
1306 repository tip rolled back to revision 0 (undo push)
1306 repository tip rolled back to revision 0 (undo push)
1307 0:6675d58eff77
1307 0:6675d58eff77
1308
1308
1309
1309
1310 $ echo '[acl.deny]' >> $config
1310 $ echo '[acl.deny]' >> $config
1311 $ echo "foo/Bar/** = @group1" >> $config
1311 $ echo "foo/Bar/** = @group1" >> $config
1312
1312
1313 @group is allowed inside anything but foo/Bar/
1313 @group is allowed inside anything but foo/Bar/
1314
1314
1315 $ do_push fred
1315 $ do_push fred
1316 Pushing as user fred
1316 Pushing as user fred
1317 hgrc = """
1317 hgrc = """
1318 [hooks]
1318 [hooks]
1319 pretxnchangegroup.acl = python:hgext.acl.hook
1319 pretxnchangegroup.acl = python:hgext.acl.hook
1320 [acl]
1320 [acl]
1321 sources = push
1321 sources = push
1322 [extensions]
1322 [extensions]
1323 [acl.allow]
1323 [acl.allow]
1324 ** = @group1
1324 ** = @group1
1325 [acl.deny]
1325 [acl.deny]
1326 foo/Bar/** = @group1
1326 foo/Bar/** = @group1
1327 """
1327 """
1328 pushing to ../b
1328 pushing to ../b
1329 query 1; heads
1329 query 1; heads
1330 searching for changes
1330 searching for changes
1331 all remote heads known locally
1331 all remote heads known locally
1332 listing keys for "phases"
1332 listing keys for "phases"
1333 checking for updated bookmarks
1333 checking for updated bookmarks
1334 listing keys for "bookmarks"
1334 listing keys for "bookmarks"
1335 listing keys for "bookmarks"
1335 listing keys for "bookmarks"
1336 3 changesets found
1336 3 changesets found
1337 list of changesets:
1337 list of changesets:
1338 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1338 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1339 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1339 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1340 911600dab2ae7a9baff75958b84fe606851ce955
1340 911600dab2ae7a9baff75958b84fe606851ce955
1341 bundle2-output-bundle: "HG20", 4 parts total
1341 bundle2-output-bundle: "HG20", 4 parts total
1342 bundle2-output-part: "replycaps" 155 bytes payload
1342 bundle2-output-part: "replycaps" 155 bytes payload
1343 bundle2-output-part: "check:heads" streamed payload
1343 bundle2-output-part: "check:heads" streamed payload
1344 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1344 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1345 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1345 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1346 bundle2-input-bundle: with-transaction
1346 bundle2-input-bundle: with-transaction
1347 bundle2-input-part: "replycaps" supported
1347 bundle2-input-part: "replycaps" supported
1348 bundle2-input-part: total payload size 155
1348 bundle2-input-part: total payload size 155
1349 bundle2-input-part: "check:heads" supported
1349 bundle2-input-part: "check:heads" supported
1350 bundle2-input-part: total payload size 20
1350 bundle2-input-part: total payload size 20
1351 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1351 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1352 adding changesets
1352 adding changesets
1353 add changeset ef1ea85a6374
1353 add changeset ef1ea85a6374
1354 add changeset f9cafe1212c8
1354 add changeset f9cafe1212c8
1355 add changeset 911600dab2ae
1355 add changeset 911600dab2ae
1356 adding manifests
1356 adding manifests
1357 adding file changes
1357 adding file changes
1358 adding foo/Bar/file.txt revisions
1358 adding foo/Bar/file.txt revisions
1359 adding foo/file.txt revisions
1359 adding foo/file.txt revisions
1360 adding quux/file.py revisions
1360 adding quux/file.py revisions
1361 added 3 changesets with 3 changes to 3 files
1361 added 3 changesets with 3 changes to 3 files
1362 calling hook pretxnchangegroup.acl: hgext.acl.hook
1362 calling hook pretxnchangegroup.acl: hgext.acl.hook
1363 acl: checking access for user "fred"
1363 acl: checking access for user "fred"
1364 acl: acl.allow.branches not enabled
1364 acl: acl.allow.branches not enabled
1365 acl: acl.deny.branches not enabled
1365 acl: acl.deny.branches not enabled
1366 acl: "group1" not defined in [acl.groups]
1366 acl: "group1" not defined in [acl.groups]
1367 acl: acl.allow enabled, 1 entries for user fred
1367 acl: acl.allow enabled, 1 entries for user fred
1368 acl: "group1" not defined in [acl.groups]
1368 acl: "group1" not defined in [acl.groups]
1369 acl: acl.deny enabled, 1 entries for user fred
1369 acl: acl.deny enabled, 1 entries for user fred
1370 acl: branch access granted: "ef1ea85a6374" on branch "default"
1370 acl: branch access granted: "ef1ea85a6374" on branch "default"
1371 acl: path access granted: "ef1ea85a6374"
1371 acl: path access granted: "ef1ea85a6374"
1372 acl: branch access granted: "f9cafe1212c8" on branch "default"
1372 acl: branch access granted: "f9cafe1212c8" on branch "default"
1373 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1373 error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1374 bundle2-input-part: total payload size 1553
1374 bundle2-input-part: total payload size 1553
1375 bundle2-input-bundle: 3 parts total
1375 bundle2-input-bundle: 3 parts total
1376 transaction abort!
1376 transaction abort!
1377 rollback completed
1377 rollback completed
1378 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1378 abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
1379 no rollback information available
1379 no rollback information available
1380 0:6675d58eff77
1380 0:6675d58eff77
1381
1381
1382
1382
1383 Invalid group
1383 Invalid group
1384
1384
1385 Disable the fakegroups trick to get real failures
1385 Disable the fakegroups trick to get real failures
1386
1386
1387 $ grep -v fakegroups $config > config.tmp
1387 $ grep -v fakegroups $config > config.tmp
1388 $ mv config.tmp $config
1388 $ mv config.tmp $config
1389 $ echo '[acl.allow]' >> $config
1389 $ echo '[acl.allow]' >> $config
1390 $ echo "** = @unlikelytoexist" >> $config
1390 $ echo "** = @unlikelytoexist" >> $config
1391 $ do_push fred 2>&1 | grep unlikelytoexist
1391 $ do_push fred 2>&1 | grep unlikelytoexist
1392 ** = @unlikelytoexist
1392 ** = @unlikelytoexist
1393 acl: "unlikelytoexist" not defined in [acl.groups]
1393 acl: "unlikelytoexist" not defined in [acl.groups]
1394 error: pretxnchangegroup.acl hook failed: group 'unlikelytoexist' is undefined
1394 error: pretxnchangegroup.acl hook failed: group 'unlikelytoexist' is undefined
1395 abort: group 'unlikelytoexist' is undefined
1395 abort: group 'unlikelytoexist' is undefined
1396
1396
1397
1397
1398 Branch acl tests setup
1398 Branch acl tests setup
1399
1399
1400 $ init_config
1400 $ init_config
1401 $ cd b
1401 $ cd b
1402 $ hg up
1402 $ hg up
1403 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1403 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1404 $ hg branch foobar
1404 $ hg branch foobar
1405 marked working directory as branch foobar
1405 marked working directory as branch foobar
1406 (branches are permanent and global, did you want a bookmark?)
1406 (branches are permanent and global, did you want a bookmark?)
1407 $ hg commit -m 'create foobar'
1407 $ hg commit -m 'create foobar'
1408 $ echo 'foo contents' > abc.txt
1408 $ echo 'foo contents' > abc.txt
1409 $ hg add abc.txt
1409 $ hg add abc.txt
1410 $ hg commit -m 'foobar contents'
1410 $ hg commit -m 'foobar contents'
1411 $ cd ..
1411 $ cd ..
1412 $ hg --cwd a pull ../b
1412 $ hg --cwd a pull ../b
1413 pulling from ../b
1413 pulling from ../b
1414 searching for changes
1414 searching for changes
1415 adding changesets
1415 adding changesets
1416 adding manifests
1416 adding manifests
1417 adding file changes
1417 adding file changes
1418 added 2 changesets with 1 changes to 1 files (+1 heads)
1418 added 2 changesets with 1 changes to 1 files (+1 heads)
1419 (run 'hg heads' to see heads)
1419 (run 'hg heads' to see heads)
1420
1420
1421 Create additional changeset on foobar branch
1421 Create additional changeset on foobar branch
1422
1422
1423 $ cd a
1423 $ cd a
1424 $ hg up -C foobar
1424 $ hg up -C foobar
1425 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
1425 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
1426 $ echo 'foo contents2' > abc.txt
1426 $ echo 'foo contents2' > abc.txt
1427 $ hg commit -m 'foobar contents2'
1427 $ hg commit -m 'foobar contents2'
1428 $ cd ..
1428 $ cd ..
1429
1429
1430
1430
1431 No branch acls specified
1431 No branch acls specified
1432
1432
1433 $ do_push astro
1433 $ do_push astro
1434 Pushing as user astro
1434 Pushing as user astro
1435 hgrc = """
1435 hgrc = """
1436 [hooks]
1436 [hooks]
1437 pretxnchangegroup.acl = python:hgext.acl.hook
1437 pretxnchangegroup.acl = python:hgext.acl.hook
1438 [acl]
1438 [acl]
1439 sources = push
1439 sources = push
1440 [extensions]
1440 [extensions]
1441 """
1441 """
1442 pushing to ../b
1442 pushing to ../b
1443 query 1; heads
1443 query 1; heads
1444 searching for changes
1444 searching for changes
1445 all remote heads known locally
1445 all remote heads known locally
1446 listing keys for "phases"
1446 listing keys for "phases"
1447 checking for updated bookmarks
1447 checking for updated bookmarks
1448 listing keys for "bookmarks"
1448 listing keys for "bookmarks"
1449 listing keys for "bookmarks"
1449 listing keys for "bookmarks"
1450 4 changesets found
1450 4 changesets found
1451 list of changesets:
1451 list of changesets:
1452 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1452 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1453 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1453 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1454 911600dab2ae7a9baff75958b84fe606851ce955
1454 911600dab2ae7a9baff75958b84fe606851ce955
1455 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1455 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1456 bundle2-output-bundle: "HG20", 5 parts total
1456 bundle2-output-bundle: "HG20", 5 parts total
1457 bundle2-output-part: "replycaps" 155 bytes payload
1457 bundle2-output-part: "replycaps" 155 bytes payload
1458 bundle2-output-part: "check:heads" streamed payload
1458 bundle2-output-part: "check:heads" streamed payload
1459 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1459 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1460 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1460 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1461 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1461 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1462 bundle2-input-bundle: with-transaction
1462 bundle2-input-bundle: with-transaction
1463 bundle2-input-part: "replycaps" supported
1463 bundle2-input-part: "replycaps" supported
1464 bundle2-input-part: total payload size 155
1464 bundle2-input-part: total payload size 155
1465 bundle2-input-part: "check:heads" supported
1465 bundle2-input-part: "check:heads" supported
1466 bundle2-input-part: total payload size 20
1466 bundle2-input-part: total payload size 20
1467 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1467 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1468 adding changesets
1468 adding changesets
1469 add changeset ef1ea85a6374
1469 add changeset ef1ea85a6374
1470 add changeset f9cafe1212c8
1470 add changeset f9cafe1212c8
1471 add changeset 911600dab2ae
1471 add changeset 911600dab2ae
1472 add changeset e8fc755d4d82
1472 add changeset e8fc755d4d82
1473 adding manifests
1473 adding manifests
1474 adding file changes
1474 adding file changes
1475 adding abc.txt revisions
1475 adding abc.txt revisions
1476 adding foo/Bar/file.txt revisions
1476 adding foo/Bar/file.txt revisions
1477 adding foo/file.txt revisions
1477 adding foo/file.txt revisions
1478 adding quux/file.py revisions
1478 adding quux/file.py revisions
1479 added 4 changesets with 4 changes to 4 files (+1 heads)
1479 added 4 changesets with 4 changes to 4 files (+1 heads)
1480 calling hook pretxnchangegroup.acl: hgext.acl.hook
1480 calling hook pretxnchangegroup.acl: hgext.acl.hook
1481 acl: checking access for user "astro"
1481 acl: checking access for user "astro"
1482 acl: acl.allow.branches not enabled
1482 acl: acl.allow.branches not enabled
1483 acl: acl.deny.branches not enabled
1483 acl: acl.deny.branches not enabled
1484 acl: acl.allow not enabled
1484 acl: acl.allow not enabled
1485 acl: acl.deny not enabled
1485 acl: acl.deny not enabled
1486 acl: branch access granted: "ef1ea85a6374" on branch "default"
1486 acl: branch access granted: "ef1ea85a6374" on branch "default"
1487 acl: path access granted: "ef1ea85a6374"
1487 acl: path access granted: "ef1ea85a6374"
1488 acl: branch access granted: "f9cafe1212c8" on branch "default"
1488 acl: branch access granted: "f9cafe1212c8" on branch "default"
1489 acl: path access granted: "f9cafe1212c8"
1489 acl: path access granted: "f9cafe1212c8"
1490 acl: branch access granted: "911600dab2ae" on branch "default"
1490 acl: branch access granted: "911600dab2ae" on branch "default"
1491 acl: path access granted: "911600dab2ae"
1491 acl: path access granted: "911600dab2ae"
1492 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1492 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1493 acl: path access granted: "e8fc755d4d82"
1493 acl: path access granted: "e8fc755d4d82"
1494 bundle2-input-part: total payload size 2068
1494 bundle2-input-part: total payload size 2068
1495 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
1495 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
1496 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
1496 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
1497 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
1497 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
1498 pushing key for "phases:e8fc755d4d8217ee5b0c2bb41558c40d43b92c01"
1498 pushing key for "phases:e8fc755d4d8217ee5b0c2bb41558c40d43b92c01"
1499 bundle2-input-bundle: 4 parts total
1499 bundle2-input-bundle: 4 parts total
1500 updating the branch cache
1500 updating the branch cache
1501 bundle2-output-bundle: "HG20", 3 parts total
1501 bundle2-output-bundle: "HG20", 3 parts total
1502 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1502 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1503 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1503 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1504 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1504 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1505 bundle2-input-bundle: with-transaction
1505 bundle2-input-bundle: no-transaction
1506 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1506 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1507 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1507 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1508 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1508 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1509 bundle2-input-bundle: 2 parts total
1509 bundle2-input-bundle: 2 parts total
1510 listing keys for "phases"
1510 listing keys for "phases"
1511 repository tip rolled back to revision 2 (undo push)
1511 repository tip rolled back to revision 2 (undo push)
1512 2:fb35475503ef
1512 2:fb35475503ef
1513
1513
1514
1514
1515 Branch acl deny test
1515 Branch acl deny test
1516
1516
1517 $ echo "[acl.deny.branches]" >> $config
1517 $ echo "[acl.deny.branches]" >> $config
1518 $ echo "foobar = *" >> $config
1518 $ echo "foobar = *" >> $config
1519 $ do_push astro
1519 $ do_push astro
1520 Pushing as user astro
1520 Pushing as user astro
1521 hgrc = """
1521 hgrc = """
1522 [hooks]
1522 [hooks]
1523 pretxnchangegroup.acl = python:hgext.acl.hook
1523 pretxnchangegroup.acl = python:hgext.acl.hook
1524 [acl]
1524 [acl]
1525 sources = push
1525 sources = push
1526 [extensions]
1526 [extensions]
1527 [acl.deny.branches]
1527 [acl.deny.branches]
1528 foobar = *
1528 foobar = *
1529 """
1529 """
1530 pushing to ../b
1530 pushing to ../b
1531 query 1; heads
1531 query 1; heads
1532 searching for changes
1532 searching for changes
1533 all remote heads known locally
1533 all remote heads known locally
1534 listing keys for "phases"
1534 listing keys for "phases"
1535 checking for updated bookmarks
1535 checking for updated bookmarks
1536 listing keys for "bookmarks"
1536 listing keys for "bookmarks"
1537 listing keys for "bookmarks"
1537 listing keys for "bookmarks"
1538 4 changesets found
1538 4 changesets found
1539 list of changesets:
1539 list of changesets:
1540 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1540 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1541 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1541 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1542 911600dab2ae7a9baff75958b84fe606851ce955
1542 911600dab2ae7a9baff75958b84fe606851ce955
1543 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1543 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1544 bundle2-output-bundle: "HG20", 5 parts total
1544 bundle2-output-bundle: "HG20", 5 parts total
1545 bundle2-output-part: "replycaps" 155 bytes payload
1545 bundle2-output-part: "replycaps" 155 bytes payload
1546 bundle2-output-part: "check:heads" streamed payload
1546 bundle2-output-part: "check:heads" streamed payload
1547 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1547 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1548 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1548 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1549 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1549 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1550 bundle2-input-bundle: with-transaction
1550 bundle2-input-bundle: with-transaction
1551 bundle2-input-part: "replycaps" supported
1551 bundle2-input-part: "replycaps" supported
1552 bundle2-input-part: total payload size 155
1552 bundle2-input-part: total payload size 155
1553 bundle2-input-part: "check:heads" supported
1553 bundle2-input-part: "check:heads" supported
1554 bundle2-input-part: total payload size 20
1554 bundle2-input-part: total payload size 20
1555 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1555 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1556 adding changesets
1556 adding changesets
1557 add changeset ef1ea85a6374
1557 add changeset ef1ea85a6374
1558 add changeset f9cafe1212c8
1558 add changeset f9cafe1212c8
1559 add changeset 911600dab2ae
1559 add changeset 911600dab2ae
1560 add changeset e8fc755d4d82
1560 add changeset e8fc755d4d82
1561 adding manifests
1561 adding manifests
1562 adding file changes
1562 adding file changes
1563 adding abc.txt revisions
1563 adding abc.txt revisions
1564 adding foo/Bar/file.txt revisions
1564 adding foo/Bar/file.txt revisions
1565 adding foo/file.txt revisions
1565 adding foo/file.txt revisions
1566 adding quux/file.py revisions
1566 adding quux/file.py revisions
1567 added 4 changesets with 4 changes to 4 files (+1 heads)
1567 added 4 changesets with 4 changes to 4 files (+1 heads)
1568 calling hook pretxnchangegroup.acl: hgext.acl.hook
1568 calling hook pretxnchangegroup.acl: hgext.acl.hook
1569 acl: checking access for user "astro"
1569 acl: checking access for user "astro"
1570 acl: acl.allow.branches not enabled
1570 acl: acl.allow.branches not enabled
1571 acl: acl.deny.branches enabled, 1 entries for user astro
1571 acl: acl.deny.branches enabled, 1 entries for user astro
1572 acl: acl.allow not enabled
1572 acl: acl.allow not enabled
1573 acl: acl.deny not enabled
1573 acl: acl.deny not enabled
1574 acl: branch access granted: "ef1ea85a6374" on branch "default"
1574 acl: branch access granted: "ef1ea85a6374" on branch "default"
1575 acl: path access granted: "ef1ea85a6374"
1575 acl: path access granted: "ef1ea85a6374"
1576 acl: branch access granted: "f9cafe1212c8" on branch "default"
1576 acl: branch access granted: "f9cafe1212c8" on branch "default"
1577 acl: path access granted: "f9cafe1212c8"
1577 acl: path access granted: "f9cafe1212c8"
1578 acl: branch access granted: "911600dab2ae" on branch "default"
1578 acl: branch access granted: "911600dab2ae" on branch "default"
1579 acl: path access granted: "911600dab2ae"
1579 acl: path access granted: "911600dab2ae"
1580 error: pretxnchangegroup.acl hook failed: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82")
1580 error: pretxnchangegroup.acl hook failed: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82")
1581 bundle2-input-part: total payload size 2068
1581 bundle2-input-part: total payload size 2068
1582 bundle2-input-bundle: 4 parts total
1582 bundle2-input-bundle: 4 parts total
1583 transaction abort!
1583 transaction abort!
1584 rollback completed
1584 rollback completed
1585 abort: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82")
1585 abort: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82")
1586 no rollback information available
1586 no rollback information available
1587 2:fb35475503ef
1587 2:fb35475503ef
1588
1588
1589
1589
1590 Branch acl empty allow test
1590 Branch acl empty allow test
1591
1591
1592 $ init_config
1592 $ init_config
1593 $ echo "[acl.allow.branches]" >> $config
1593 $ echo "[acl.allow.branches]" >> $config
1594 $ do_push astro
1594 $ do_push astro
1595 Pushing as user astro
1595 Pushing as user astro
1596 hgrc = """
1596 hgrc = """
1597 [hooks]
1597 [hooks]
1598 pretxnchangegroup.acl = python:hgext.acl.hook
1598 pretxnchangegroup.acl = python:hgext.acl.hook
1599 [acl]
1599 [acl]
1600 sources = push
1600 sources = push
1601 [extensions]
1601 [extensions]
1602 [acl.allow.branches]
1602 [acl.allow.branches]
1603 """
1603 """
1604 pushing to ../b
1604 pushing to ../b
1605 query 1; heads
1605 query 1; heads
1606 searching for changes
1606 searching for changes
1607 all remote heads known locally
1607 all remote heads known locally
1608 listing keys for "phases"
1608 listing keys for "phases"
1609 checking for updated bookmarks
1609 checking for updated bookmarks
1610 listing keys for "bookmarks"
1610 listing keys for "bookmarks"
1611 listing keys for "bookmarks"
1611 listing keys for "bookmarks"
1612 4 changesets found
1612 4 changesets found
1613 list of changesets:
1613 list of changesets:
1614 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1614 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1615 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1615 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1616 911600dab2ae7a9baff75958b84fe606851ce955
1616 911600dab2ae7a9baff75958b84fe606851ce955
1617 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1617 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1618 bundle2-output-bundle: "HG20", 5 parts total
1618 bundle2-output-bundle: "HG20", 5 parts total
1619 bundle2-output-part: "replycaps" 155 bytes payload
1619 bundle2-output-part: "replycaps" 155 bytes payload
1620 bundle2-output-part: "check:heads" streamed payload
1620 bundle2-output-part: "check:heads" streamed payload
1621 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1621 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1622 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1622 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1623 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1623 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1624 bundle2-input-bundle: with-transaction
1624 bundle2-input-bundle: with-transaction
1625 bundle2-input-part: "replycaps" supported
1625 bundle2-input-part: "replycaps" supported
1626 bundle2-input-part: total payload size 155
1626 bundle2-input-part: total payload size 155
1627 bundle2-input-part: "check:heads" supported
1627 bundle2-input-part: "check:heads" supported
1628 bundle2-input-part: total payload size 20
1628 bundle2-input-part: total payload size 20
1629 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1629 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1630 adding changesets
1630 adding changesets
1631 add changeset ef1ea85a6374
1631 add changeset ef1ea85a6374
1632 add changeset f9cafe1212c8
1632 add changeset f9cafe1212c8
1633 add changeset 911600dab2ae
1633 add changeset 911600dab2ae
1634 add changeset e8fc755d4d82
1634 add changeset e8fc755d4d82
1635 adding manifests
1635 adding manifests
1636 adding file changes
1636 adding file changes
1637 adding abc.txt revisions
1637 adding abc.txt revisions
1638 adding foo/Bar/file.txt revisions
1638 adding foo/Bar/file.txt revisions
1639 adding foo/file.txt revisions
1639 adding foo/file.txt revisions
1640 adding quux/file.py revisions
1640 adding quux/file.py revisions
1641 added 4 changesets with 4 changes to 4 files (+1 heads)
1641 added 4 changesets with 4 changes to 4 files (+1 heads)
1642 calling hook pretxnchangegroup.acl: hgext.acl.hook
1642 calling hook pretxnchangegroup.acl: hgext.acl.hook
1643 acl: checking access for user "astro"
1643 acl: checking access for user "astro"
1644 acl: acl.allow.branches enabled, 0 entries for user astro
1644 acl: acl.allow.branches enabled, 0 entries for user astro
1645 acl: acl.deny.branches not enabled
1645 acl: acl.deny.branches not enabled
1646 acl: acl.allow not enabled
1646 acl: acl.allow not enabled
1647 acl: acl.deny not enabled
1647 acl: acl.deny not enabled
1648 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1648 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1649 bundle2-input-part: total payload size 2068
1649 bundle2-input-part: total payload size 2068
1650 bundle2-input-bundle: 4 parts total
1650 bundle2-input-bundle: 4 parts total
1651 transaction abort!
1651 transaction abort!
1652 rollback completed
1652 rollback completed
1653 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1653 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1654 no rollback information available
1654 no rollback information available
1655 2:fb35475503ef
1655 2:fb35475503ef
1656
1656
1657
1657
1658 Branch acl allow other
1658 Branch acl allow other
1659
1659
1660 $ init_config
1660 $ init_config
1661 $ echo "[acl.allow.branches]" >> $config
1661 $ echo "[acl.allow.branches]" >> $config
1662 $ echo "* = george" >> $config
1662 $ echo "* = george" >> $config
1663 $ do_push astro
1663 $ do_push astro
1664 Pushing as user astro
1664 Pushing as user astro
1665 hgrc = """
1665 hgrc = """
1666 [hooks]
1666 [hooks]
1667 pretxnchangegroup.acl = python:hgext.acl.hook
1667 pretxnchangegroup.acl = python:hgext.acl.hook
1668 [acl]
1668 [acl]
1669 sources = push
1669 sources = push
1670 [extensions]
1670 [extensions]
1671 [acl.allow.branches]
1671 [acl.allow.branches]
1672 * = george
1672 * = george
1673 """
1673 """
1674 pushing to ../b
1674 pushing to ../b
1675 query 1; heads
1675 query 1; heads
1676 searching for changes
1676 searching for changes
1677 all remote heads known locally
1677 all remote heads known locally
1678 listing keys for "phases"
1678 listing keys for "phases"
1679 checking for updated bookmarks
1679 checking for updated bookmarks
1680 listing keys for "bookmarks"
1680 listing keys for "bookmarks"
1681 listing keys for "bookmarks"
1681 listing keys for "bookmarks"
1682 4 changesets found
1682 4 changesets found
1683 list of changesets:
1683 list of changesets:
1684 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1684 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1685 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1685 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1686 911600dab2ae7a9baff75958b84fe606851ce955
1686 911600dab2ae7a9baff75958b84fe606851ce955
1687 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1687 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1688 bundle2-output-bundle: "HG20", 5 parts total
1688 bundle2-output-bundle: "HG20", 5 parts total
1689 bundle2-output-part: "replycaps" 155 bytes payload
1689 bundle2-output-part: "replycaps" 155 bytes payload
1690 bundle2-output-part: "check:heads" streamed payload
1690 bundle2-output-part: "check:heads" streamed payload
1691 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1691 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1692 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1692 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1693 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1693 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1694 bundle2-input-bundle: with-transaction
1694 bundle2-input-bundle: with-transaction
1695 bundle2-input-part: "replycaps" supported
1695 bundle2-input-part: "replycaps" supported
1696 bundle2-input-part: total payload size 155
1696 bundle2-input-part: total payload size 155
1697 bundle2-input-part: "check:heads" supported
1697 bundle2-input-part: "check:heads" supported
1698 bundle2-input-part: total payload size 20
1698 bundle2-input-part: total payload size 20
1699 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1699 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1700 adding changesets
1700 adding changesets
1701 add changeset ef1ea85a6374
1701 add changeset ef1ea85a6374
1702 add changeset f9cafe1212c8
1702 add changeset f9cafe1212c8
1703 add changeset 911600dab2ae
1703 add changeset 911600dab2ae
1704 add changeset e8fc755d4d82
1704 add changeset e8fc755d4d82
1705 adding manifests
1705 adding manifests
1706 adding file changes
1706 adding file changes
1707 adding abc.txt revisions
1707 adding abc.txt revisions
1708 adding foo/Bar/file.txt revisions
1708 adding foo/Bar/file.txt revisions
1709 adding foo/file.txt revisions
1709 adding foo/file.txt revisions
1710 adding quux/file.py revisions
1710 adding quux/file.py revisions
1711 added 4 changesets with 4 changes to 4 files (+1 heads)
1711 added 4 changesets with 4 changes to 4 files (+1 heads)
1712 calling hook pretxnchangegroup.acl: hgext.acl.hook
1712 calling hook pretxnchangegroup.acl: hgext.acl.hook
1713 acl: checking access for user "astro"
1713 acl: checking access for user "astro"
1714 acl: acl.allow.branches enabled, 0 entries for user astro
1714 acl: acl.allow.branches enabled, 0 entries for user astro
1715 acl: acl.deny.branches not enabled
1715 acl: acl.deny.branches not enabled
1716 acl: acl.allow not enabled
1716 acl: acl.allow not enabled
1717 acl: acl.deny not enabled
1717 acl: acl.deny not enabled
1718 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1718 error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1719 bundle2-input-part: total payload size 2068
1719 bundle2-input-part: total payload size 2068
1720 bundle2-input-bundle: 4 parts total
1720 bundle2-input-bundle: 4 parts total
1721 transaction abort!
1721 transaction abort!
1722 rollback completed
1722 rollback completed
1723 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1723 abort: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374")
1724 no rollback information available
1724 no rollback information available
1725 2:fb35475503ef
1725 2:fb35475503ef
1726
1726
1727 $ do_push george
1727 $ do_push george
1728 Pushing as user george
1728 Pushing as user george
1729 hgrc = """
1729 hgrc = """
1730 [hooks]
1730 [hooks]
1731 pretxnchangegroup.acl = python:hgext.acl.hook
1731 pretxnchangegroup.acl = python:hgext.acl.hook
1732 [acl]
1732 [acl]
1733 sources = push
1733 sources = push
1734 [extensions]
1734 [extensions]
1735 [acl.allow.branches]
1735 [acl.allow.branches]
1736 * = george
1736 * = george
1737 """
1737 """
1738 pushing to ../b
1738 pushing to ../b
1739 query 1; heads
1739 query 1; heads
1740 searching for changes
1740 searching for changes
1741 all remote heads known locally
1741 all remote heads known locally
1742 listing keys for "phases"
1742 listing keys for "phases"
1743 checking for updated bookmarks
1743 checking for updated bookmarks
1744 listing keys for "bookmarks"
1744 listing keys for "bookmarks"
1745 listing keys for "bookmarks"
1745 listing keys for "bookmarks"
1746 4 changesets found
1746 4 changesets found
1747 list of changesets:
1747 list of changesets:
1748 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1748 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1749 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1749 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1750 911600dab2ae7a9baff75958b84fe606851ce955
1750 911600dab2ae7a9baff75958b84fe606851ce955
1751 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1751 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1752 bundle2-output-bundle: "HG20", 5 parts total
1752 bundle2-output-bundle: "HG20", 5 parts total
1753 bundle2-output-part: "replycaps" 155 bytes payload
1753 bundle2-output-part: "replycaps" 155 bytes payload
1754 bundle2-output-part: "check:heads" streamed payload
1754 bundle2-output-part: "check:heads" streamed payload
1755 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1755 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1756 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1756 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1757 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1757 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1758 bundle2-input-bundle: with-transaction
1758 bundle2-input-bundle: with-transaction
1759 bundle2-input-part: "replycaps" supported
1759 bundle2-input-part: "replycaps" supported
1760 bundle2-input-part: total payload size 155
1760 bundle2-input-part: total payload size 155
1761 bundle2-input-part: "check:heads" supported
1761 bundle2-input-part: "check:heads" supported
1762 bundle2-input-part: total payload size 20
1762 bundle2-input-part: total payload size 20
1763 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1763 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1764 adding changesets
1764 adding changesets
1765 add changeset ef1ea85a6374
1765 add changeset ef1ea85a6374
1766 add changeset f9cafe1212c8
1766 add changeset f9cafe1212c8
1767 add changeset 911600dab2ae
1767 add changeset 911600dab2ae
1768 add changeset e8fc755d4d82
1768 add changeset e8fc755d4d82
1769 adding manifests
1769 adding manifests
1770 adding file changes
1770 adding file changes
1771 adding abc.txt revisions
1771 adding abc.txt revisions
1772 adding foo/Bar/file.txt revisions
1772 adding foo/Bar/file.txt revisions
1773 adding foo/file.txt revisions
1773 adding foo/file.txt revisions
1774 adding quux/file.py revisions
1774 adding quux/file.py revisions
1775 added 4 changesets with 4 changes to 4 files (+1 heads)
1775 added 4 changesets with 4 changes to 4 files (+1 heads)
1776 calling hook pretxnchangegroup.acl: hgext.acl.hook
1776 calling hook pretxnchangegroup.acl: hgext.acl.hook
1777 acl: checking access for user "george"
1777 acl: checking access for user "george"
1778 acl: acl.allow.branches enabled, 1 entries for user george
1778 acl: acl.allow.branches enabled, 1 entries for user george
1779 acl: acl.deny.branches not enabled
1779 acl: acl.deny.branches not enabled
1780 acl: acl.allow not enabled
1780 acl: acl.allow not enabled
1781 acl: acl.deny not enabled
1781 acl: acl.deny not enabled
1782 acl: branch access granted: "ef1ea85a6374" on branch "default"
1782 acl: branch access granted: "ef1ea85a6374" on branch "default"
1783 acl: path access granted: "ef1ea85a6374"
1783 acl: path access granted: "ef1ea85a6374"
1784 acl: branch access granted: "f9cafe1212c8" on branch "default"
1784 acl: branch access granted: "f9cafe1212c8" on branch "default"
1785 acl: path access granted: "f9cafe1212c8"
1785 acl: path access granted: "f9cafe1212c8"
1786 acl: branch access granted: "911600dab2ae" on branch "default"
1786 acl: branch access granted: "911600dab2ae" on branch "default"
1787 acl: path access granted: "911600dab2ae"
1787 acl: path access granted: "911600dab2ae"
1788 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1788 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1789 acl: path access granted: "e8fc755d4d82"
1789 acl: path access granted: "e8fc755d4d82"
1790 bundle2-input-part: total payload size 2068
1790 bundle2-input-part: total payload size 2068
1791 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
1791 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
1792 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
1792 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
1793 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
1793 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
1794 pushing key for "phases:e8fc755d4d8217ee5b0c2bb41558c40d43b92c01"
1794 pushing key for "phases:e8fc755d4d8217ee5b0c2bb41558c40d43b92c01"
1795 bundle2-input-bundle: 4 parts total
1795 bundle2-input-bundle: 4 parts total
1796 updating the branch cache
1796 updating the branch cache
1797 bundle2-output-bundle: "HG20", 3 parts total
1797 bundle2-output-bundle: "HG20", 3 parts total
1798 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1798 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1799 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1799 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1800 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1800 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1801 bundle2-input-bundle: with-transaction
1801 bundle2-input-bundle: no-transaction
1802 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1802 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1803 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1803 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1804 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1804 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1805 bundle2-input-bundle: 2 parts total
1805 bundle2-input-bundle: 2 parts total
1806 listing keys for "phases"
1806 listing keys for "phases"
1807 repository tip rolled back to revision 2 (undo push)
1807 repository tip rolled back to revision 2 (undo push)
1808 2:fb35475503ef
1808 2:fb35475503ef
1809
1809
1810
1810
1811 Branch acl conflicting allow
1811 Branch acl conflicting allow
1812 asterisk ends up applying to all branches and allowing george to
1812 asterisk ends up applying to all branches and allowing george to
1813 push foobar into the remote
1813 push foobar into the remote
1814
1814
1815 $ init_config
1815 $ init_config
1816 $ echo "[acl.allow.branches]" >> $config
1816 $ echo "[acl.allow.branches]" >> $config
1817 $ echo "foobar = astro" >> $config
1817 $ echo "foobar = astro" >> $config
1818 $ echo "* = george" >> $config
1818 $ echo "* = george" >> $config
1819 $ do_push george
1819 $ do_push george
1820 Pushing as user george
1820 Pushing as user george
1821 hgrc = """
1821 hgrc = """
1822 [hooks]
1822 [hooks]
1823 pretxnchangegroup.acl = python:hgext.acl.hook
1823 pretxnchangegroup.acl = python:hgext.acl.hook
1824 [acl]
1824 [acl]
1825 sources = push
1825 sources = push
1826 [extensions]
1826 [extensions]
1827 [acl.allow.branches]
1827 [acl.allow.branches]
1828 foobar = astro
1828 foobar = astro
1829 * = george
1829 * = george
1830 """
1830 """
1831 pushing to ../b
1831 pushing to ../b
1832 query 1; heads
1832 query 1; heads
1833 searching for changes
1833 searching for changes
1834 all remote heads known locally
1834 all remote heads known locally
1835 listing keys for "phases"
1835 listing keys for "phases"
1836 checking for updated bookmarks
1836 checking for updated bookmarks
1837 listing keys for "bookmarks"
1837 listing keys for "bookmarks"
1838 listing keys for "bookmarks"
1838 listing keys for "bookmarks"
1839 4 changesets found
1839 4 changesets found
1840 list of changesets:
1840 list of changesets:
1841 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1841 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1842 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1842 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1843 911600dab2ae7a9baff75958b84fe606851ce955
1843 911600dab2ae7a9baff75958b84fe606851ce955
1844 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1844 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1845 bundle2-output-bundle: "HG20", 5 parts total
1845 bundle2-output-bundle: "HG20", 5 parts total
1846 bundle2-output-part: "replycaps" 155 bytes payload
1846 bundle2-output-part: "replycaps" 155 bytes payload
1847 bundle2-output-part: "check:heads" streamed payload
1847 bundle2-output-part: "check:heads" streamed payload
1848 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1848 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1849 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1849 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1850 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1850 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1851 bundle2-input-bundle: with-transaction
1851 bundle2-input-bundle: with-transaction
1852 bundle2-input-part: "replycaps" supported
1852 bundle2-input-part: "replycaps" supported
1853 bundle2-input-part: total payload size 155
1853 bundle2-input-part: total payload size 155
1854 bundle2-input-part: "check:heads" supported
1854 bundle2-input-part: "check:heads" supported
1855 bundle2-input-part: total payload size 20
1855 bundle2-input-part: total payload size 20
1856 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1856 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1857 adding changesets
1857 adding changesets
1858 add changeset ef1ea85a6374
1858 add changeset ef1ea85a6374
1859 add changeset f9cafe1212c8
1859 add changeset f9cafe1212c8
1860 add changeset 911600dab2ae
1860 add changeset 911600dab2ae
1861 add changeset e8fc755d4d82
1861 add changeset e8fc755d4d82
1862 adding manifests
1862 adding manifests
1863 adding file changes
1863 adding file changes
1864 adding abc.txt revisions
1864 adding abc.txt revisions
1865 adding foo/Bar/file.txt revisions
1865 adding foo/Bar/file.txt revisions
1866 adding foo/file.txt revisions
1866 adding foo/file.txt revisions
1867 adding quux/file.py revisions
1867 adding quux/file.py revisions
1868 added 4 changesets with 4 changes to 4 files (+1 heads)
1868 added 4 changesets with 4 changes to 4 files (+1 heads)
1869 calling hook pretxnchangegroup.acl: hgext.acl.hook
1869 calling hook pretxnchangegroup.acl: hgext.acl.hook
1870 acl: checking access for user "george"
1870 acl: checking access for user "george"
1871 acl: acl.allow.branches enabled, 1 entries for user george
1871 acl: acl.allow.branches enabled, 1 entries for user george
1872 acl: acl.deny.branches not enabled
1872 acl: acl.deny.branches not enabled
1873 acl: acl.allow not enabled
1873 acl: acl.allow not enabled
1874 acl: acl.deny not enabled
1874 acl: acl.deny not enabled
1875 acl: branch access granted: "ef1ea85a6374" on branch "default"
1875 acl: branch access granted: "ef1ea85a6374" on branch "default"
1876 acl: path access granted: "ef1ea85a6374"
1876 acl: path access granted: "ef1ea85a6374"
1877 acl: branch access granted: "f9cafe1212c8" on branch "default"
1877 acl: branch access granted: "f9cafe1212c8" on branch "default"
1878 acl: path access granted: "f9cafe1212c8"
1878 acl: path access granted: "f9cafe1212c8"
1879 acl: branch access granted: "911600dab2ae" on branch "default"
1879 acl: branch access granted: "911600dab2ae" on branch "default"
1880 acl: path access granted: "911600dab2ae"
1880 acl: path access granted: "911600dab2ae"
1881 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1881 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
1882 acl: path access granted: "e8fc755d4d82"
1882 acl: path access granted: "e8fc755d4d82"
1883 bundle2-input-part: total payload size 2068
1883 bundle2-input-part: total payload size 2068
1884 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
1884 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
1885 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
1885 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
1886 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
1886 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
1887 pushing key for "phases:e8fc755d4d8217ee5b0c2bb41558c40d43b92c01"
1887 pushing key for "phases:e8fc755d4d8217ee5b0c2bb41558c40d43b92c01"
1888 bundle2-input-bundle: 4 parts total
1888 bundle2-input-bundle: 4 parts total
1889 updating the branch cache
1889 updating the branch cache
1890 bundle2-output-bundle: "HG20", 3 parts total
1890 bundle2-output-bundle: "HG20", 3 parts total
1891 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1891 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
1892 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1892 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1893 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1893 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
1894 bundle2-input-bundle: with-transaction
1894 bundle2-input-bundle: no-transaction
1895 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1895 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
1896 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1896 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1897 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1897 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
1898 bundle2-input-bundle: 2 parts total
1898 bundle2-input-bundle: 2 parts total
1899 listing keys for "phases"
1899 listing keys for "phases"
1900 repository tip rolled back to revision 2 (undo push)
1900 repository tip rolled back to revision 2 (undo push)
1901 2:fb35475503ef
1901 2:fb35475503ef
1902
1902
1903 Branch acl conflicting deny
1903 Branch acl conflicting deny
1904
1904
1905 $ init_config
1905 $ init_config
1906 $ echo "[acl.deny.branches]" >> $config
1906 $ echo "[acl.deny.branches]" >> $config
1907 $ echo "foobar = astro" >> $config
1907 $ echo "foobar = astro" >> $config
1908 $ echo "default = astro" >> $config
1908 $ echo "default = astro" >> $config
1909 $ echo "* = george" >> $config
1909 $ echo "* = george" >> $config
1910 $ do_push george
1910 $ do_push george
1911 Pushing as user george
1911 Pushing as user george
1912 hgrc = """
1912 hgrc = """
1913 [hooks]
1913 [hooks]
1914 pretxnchangegroup.acl = python:hgext.acl.hook
1914 pretxnchangegroup.acl = python:hgext.acl.hook
1915 [acl]
1915 [acl]
1916 sources = push
1916 sources = push
1917 [extensions]
1917 [extensions]
1918 [acl.deny.branches]
1918 [acl.deny.branches]
1919 foobar = astro
1919 foobar = astro
1920 default = astro
1920 default = astro
1921 * = george
1921 * = george
1922 """
1922 """
1923 pushing to ../b
1923 pushing to ../b
1924 query 1; heads
1924 query 1; heads
1925 searching for changes
1925 searching for changes
1926 all remote heads known locally
1926 all remote heads known locally
1927 listing keys for "phases"
1927 listing keys for "phases"
1928 checking for updated bookmarks
1928 checking for updated bookmarks
1929 listing keys for "bookmarks"
1929 listing keys for "bookmarks"
1930 listing keys for "bookmarks"
1930 listing keys for "bookmarks"
1931 4 changesets found
1931 4 changesets found
1932 list of changesets:
1932 list of changesets:
1933 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1933 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
1934 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1934 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
1935 911600dab2ae7a9baff75958b84fe606851ce955
1935 911600dab2ae7a9baff75958b84fe606851ce955
1936 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1936 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
1937 bundle2-output-bundle: "HG20", 5 parts total
1937 bundle2-output-bundle: "HG20", 5 parts total
1938 bundle2-output-part: "replycaps" 155 bytes payload
1938 bundle2-output-part: "replycaps" 155 bytes payload
1939 bundle2-output-part: "check:heads" streamed payload
1939 bundle2-output-part: "check:heads" streamed payload
1940 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1940 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
1941 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1941 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1942 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1942 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
1943 bundle2-input-bundle: with-transaction
1943 bundle2-input-bundle: with-transaction
1944 bundle2-input-part: "replycaps" supported
1944 bundle2-input-part: "replycaps" supported
1945 bundle2-input-part: total payload size 155
1945 bundle2-input-part: total payload size 155
1946 bundle2-input-part: "check:heads" supported
1946 bundle2-input-part: "check:heads" supported
1947 bundle2-input-part: total payload size 20
1947 bundle2-input-part: total payload size 20
1948 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1948 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
1949 adding changesets
1949 adding changesets
1950 add changeset ef1ea85a6374
1950 add changeset ef1ea85a6374
1951 add changeset f9cafe1212c8
1951 add changeset f9cafe1212c8
1952 add changeset 911600dab2ae
1952 add changeset 911600dab2ae
1953 add changeset e8fc755d4d82
1953 add changeset e8fc755d4d82
1954 adding manifests
1954 adding manifests
1955 adding file changes
1955 adding file changes
1956 adding abc.txt revisions
1956 adding abc.txt revisions
1957 adding foo/Bar/file.txt revisions
1957 adding foo/Bar/file.txt revisions
1958 adding foo/file.txt revisions
1958 adding foo/file.txt revisions
1959 adding quux/file.py revisions
1959 adding quux/file.py revisions
1960 added 4 changesets with 4 changes to 4 files (+1 heads)
1960 added 4 changesets with 4 changes to 4 files (+1 heads)
1961 calling hook pretxnchangegroup.acl: hgext.acl.hook
1961 calling hook pretxnchangegroup.acl: hgext.acl.hook
1962 acl: checking access for user "george"
1962 acl: checking access for user "george"
1963 acl: acl.allow.branches not enabled
1963 acl: acl.allow.branches not enabled
1964 acl: acl.deny.branches enabled, 1 entries for user george
1964 acl: acl.deny.branches enabled, 1 entries for user george
1965 acl: acl.allow not enabled
1965 acl: acl.allow not enabled
1966 acl: acl.deny not enabled
1966 acl: acl.deny not enabled
1967 error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
1967 error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
1968 bundle2-input-part: total payload size 2068
1968 bundle2-input-part: total payload size 2068
1969 bundle2-input-bundle: 4 parts total
1969 bundle2-input-bundle: 4 parts total
1970 transaction abort!
1970 transaction abort!
1971 rollback completed
1971 rollback completed
1972 abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
1972 abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
1973 no rollback information available
1973 no rollback information available
1974 2:fb35475503ef
1974 2:fb35475503ef
1975
1975
1976 User 'astro' must not be denied
1976 User 'astro' must not be denied
1977
1977
1978 $ init_config
1978 $ init_config
1979 $ echo "[acl.deny.branches]" >> $config
1979 $ echo "[acl.deny.branches]" >> $config
1980 $ echo "default = !astro" >> $config
1980 $ echo "default = !astro" >> $config
1981 $ do_push astro
1981 $ do_push astro
1982 Pushing as user astro
1982 Pushing as user astro
1983 hgrc = """
1983 hgrc = """
1984 [hooks]
1984 [hooks]
1985 pretxnchangegroup.acl = python:hgext.acl.hook
1985 pretxnchangegroup.acl = python:hgext.acl.hook
1986 [acl]
1986 [acl]
1987 sources = push
1987 sources = push
1988 [extensions]
1988 [extensions]
1989 [acl.deny.branches]
1989 [acl.deny.branches]
1990 default = !astro
1990 default = !astro
1991 """
1991 """
1992 pushing to ../b
1992 pushing to ../b
1993 query 1; heads
1993 query 1; heads
1994 searching for changes
1994 searching for changes
1995 all remote heads known locally
1995 all remote heads known locally
1996 listing keys for "phases"
1996 listing keys for "phases"
1997 checking for updated bookmarks
1997 checking for updated bookmarks
1998 listing keys for "bookmarks"
1998 listing keys for "bookmarks"
1999 listing keys for "bookmarks"
1999 listing keys for "bookmarks"
2000 4 changesets found
2000 4 changesets found
2001 list of changesets:
2001 list of changesets:
2002 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
2002 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
2003 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
2003 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
2004 911600dab2ae7a9baff75958b84fe606851ce955
2004 911600dab2ae7a9baff75958b84fe606851ce955
2005 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
2005 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
2006 bundle2-output-bundle: "HG20", 5 parts total
2006 bundle2-output-bundle: "HG20", 5 parts total
2007 bundle2-output-part: "replycaps" 155 bytes payload
2007 bundle2-output-part: "replycaps" 155 bytes payload
2008 bundle2-output-part: "check:heads" streamed payload
2008 bundle2-output-part: "check:heads" streamed payload
2009 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
2009 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
2010 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
2010 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
2011 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
2011 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
2012 bundle2-input-bundle: with-transaction
2012 bundle2-input-bundle: with-transaction
2013 bundle2-input-part: "replycaps" supported
2013 bundle2-input-part: "replycaps" supported
2014 bundle2-input-part: total payload size 155
2014 bundle2-input-part: total payload size 155
2015 bundle2-input-part: "check:heads" supported
2015 bundle2-input-part: "check:heads" supported
2016 bundle2-input-part: total payload size 20
2016 bundle2-input-part: total payload size 20
2017 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
2017 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
2018 adding changesets
2018 adding changesets
2019 add changeset ef1ea85a6374
2019 add changeset ef1ea85a6374
2020 add changeset f9cafe1212c8
2020 add changeset f9cafe1212c8
2021 add changeset 911600dab2ae
2021 add changeset 911600dab2ae
2022 add changeset e8fc755d4d82
2022 add changeset e8fc755d4d82
2023 adding manifests
2023 adding manifests
2024 adding file changes
2024 adding file changes
2025 adding abc.txt revisions
2025 adding abc.txt revisions
2026 adding foo/Bar/file.txt revisions
2026 adding foo/Bar/file.txt revisions
2027 adding foo/file.txt revisions
2027 adding foo/file.txt revisions
2028 adding quux/file.py revisions
2028 adding quux/file.py revisions
2029 added 4 changesets with 4 changes to 4 files (+1 heads)
2029 added 4 changesets with 4 changes to 4 files (+1 heads)
2030 calling hook pretxnchangegroup.acl: hgext.acl.hook
2030 calling hook pretxnchangegroup.acl: hgext.acl.hook
2031 acl: checking access for user "astro"
2031 acl: checking access for user "astro"
2032 acl: acl.allow.branches not enabled
2032 acl: acl.allow.branches not enabled
2033 acl: acl.deny.branches enabled, 0 entries for user astro
2033 acl: acl.deny.branches enabled, 0 entries for user astro
2034 acl: acl.allow not enabled
2034 acl: acl.allow not enabled
2035 acl: acl.deny not enabled
2035 acl: acl.deny not enabled
2036 acl: branch access granted: "ef1ea85a6374" on branch "default"
2036 acl: branch access granted: "ef1ea85a6374" on branch "default"
2037 acl: path access granted: "ef1ea85a6374"
2037 acl: path access granted: "ef1ea85a6374"
2038 acl: branch access granted: "f9cafe1212c8" on branch "default"
2038 acl: branch access granted: "f9cafe1212c8" on branch "default"
2039 acl: path access granted: "f9cafe1212c8"
2039 acl: path access granted: "f9cafe1212c8"
2040 acl: branch access granted: "911600dab2ae" on branch "default"
2040 acl: branch access granted: "911600dab2ae" on branch "default"
2041 acl: path access granted: "911600dab2ae"
2041 acl: path access granted: "911600dab2ae"
2042 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
2042 acl: branch access granted: "e8fc755d4d82" on branch "foobar"
2043 acl: path access granted: "e8fc755d4d82"
2043 acl: path access granted: "e8fc755d4d82"
2044 bundle2-input-part: total payload size 2068
2044 bundle2-input-part: total payload size 2068
2045 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
2045 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
2046 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
2046 pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955"
2047 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
2047 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
2048 pushing key for "phases:e8fc755d4d8217ee5b0c2bb41558c40d43b92c01"
2048 pushing key for "phases:e8fc755d4d8217ee5b0c2bb41558c40d43b92c01"
2049 bundle2-input-bundle: 4 parts total
2049 bundle2-input-bundle: 4 parts total
2050 updating the branch cache
2050 updating the branch cache
2051 bundle2-output-bundle: "HG20", 3 parts total
2051 bundle2-output-bundle: "HG20", 3 parts total
2052 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
2052 bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload
2053 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
2053 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
2054 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
2054 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
2055 bundle2-input-bundle: with-transaction
2055 bundle2-input-bundle: no-transaction
2056 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
2056 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
2057 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
2057 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
2058 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
2058 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
2059 bundle2-input-bundle: 2 parts total
2059 bundle2-input-bundle: 2 parts total
2060 listing keys for "phases"
2060 listing keys for "phases"
2061 repository tip rolled back to revision 2 (undo push)
2061 repository tip rolled back to revision 2 (undo push)
2062 2:fb35475503ef
2062 2:fb35475503ef
2063
2063
2064
2064
2065 Non-astro users must be denied
2065 Non-astro users must be denied
2066
2066
2067 $ do_push george
2067 $ do_push george
2068 Pushing as user george
2068 Pushing as user george
2069 hgrc = """
2069 hgrc = """
2070 [hooks]
2070 [hooks]
2071 pretxnchangegroup.acl = python:hgext.acl.hook
2071 pretxnchangegroup.acl = python:hgext.acl.hook
2072 [acl]
2072 [acl]
2073 sources = push
2073 sources = push
2074 [extensions]
2074 [extensions]
2075 [acl.deny.branches]
2075 [acl.deny.branches]
2076 default = !astro
2076 default = !astro
2077 """
2077 """
2078 pushing to ../b
2078 pushing to ../b
2079 query 1; heads
2079 query 1; heads
2080 searching for changes
2080 searching for changes
2081 all remote heads known locally
2081 all remote heads known locally
2082 listing keys for "phases"
2082 listing keys for "phases"
2083 checking for updated bookmarks
2083 checking for updated bookmarks
2084 listing keys for "bookmarks"
2084 listing keys for "bookmarks"
2085 listing keys for "bookmarks"
2085 listing keys for "bookmarks"
2086 4 changesets found
2086 4 changesets found
2087 list of changesets:
2087 list of changesets:
2088 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
2088 ef1ea85a6374b77d6da9dcda9541f498f2d17df7
2089 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
2089 f9cafe1212c8c6fa1120d14a556e18cc44ff8bdd
2090 911600dab2ae7a9baff75958b84fe606851ce955
2090 911600dab2ae7a9baff75958b84fe606851ce955
2091 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
2091 e8fc755d4d8217ee5b0c2bb41558c40d43b92c01
2092 bundle2-output-bundle: "HG20", 5 parts total
2092 bundle2-output-bundle: "HG20", 5 parts total
2093 bundle2-output-part: "replycaps" 155 bytes payload
2093 bundle2-output-part: "replycaps" 155 bytes payload
2094 bundle2-output-part: "check:heads" streamed payload
2094 bundle2-output-part: "check:heads" streamed payload
2095 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
2095 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
2096 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
2096 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
2097 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
2097 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
2098 bundle2-input-bundle: with-transaction
2098 bundle2-input-bundle: with-transaction
2099 bundle2-input-part: "replycaps" supported
2099 bundle2-input-part: "replycaps" supported
2100 bundle2-input-part: total payload size 155
2100 bundle2-input-part: total payload size 155
2101 bundle2-input-part: "check:heads" supported
2101 bundle2-input-part: "check:heads" supported
2102 bundle2-input-part: total payload size 20
2102 bundle2-input-part: total payload size 20
2103 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
2103 bundle2-input-part: "changegroup" (params: 1 mandatory) supported
2104 adding changesets
2104 adding changesets
2105 add changeset ef1ea85a6374
2105 add changeset ef1ea85a6374
2106 add changeset f9cafe1212c8
2106 add changeset f9cafe1212c8
2107 add changeset 911600dab2ae
2107 add changeset 911600dab2ae
2108 add changeset e8fc755d4d82
2108 add changeset e8fc755d4d82
2109 adding manifests
2109 adding manifests
2110 adding file changes
2110 adding file changes
2111 adding abc.txt revisions
2111 adding abc.txt revisions
2112 adding foo/Bar/file.txt revisions
2112 adding foo/Bar/file.txt revisions
2113 adding foo/file.txt revisions
2113 adding foo/file.txt revisions
2114 adding quux/file.py revisions
2114 adding quux/file.py revisions
2115 added 4 changesets with 4 changes to 4 files (+1 heads)
2115 added 4 changesets with 4 changes to 4 files (+1 heads)
2116 calling hook pretxnchangegroup.acl: hgext.acl.hook
2116 calling hook pretxnchangegroup.acl: hgext.acl.hook
2117 acl: checking access for user "george"
2117 acl: checking access for user "george"
2118 acl: acl.allow.branches not enabled
2118 acl: acl.allow.branches not enabled
2119 acl: acl.deny.branches enabled, 1 entries for user george
2119 acl: acl.deny.branches enabled, 1 entries for user george
2120 acl: acl.allow not enabled
2120 acl: acl.allow not enabled
2121 acl: acl.deny not enabled
2121 acl: acl.deny not enabled
2122 error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2122 error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2123 bundle2-input-part: total payload size 2068
2123 bundle2-input-part: total payload size 2068
2124 bundle2-input-bundle: 4 parts total
2124 bundle2-input-bundle: 4 parts total
2125 transaction abort!
2125 transaction abort!
2126 rollback completed
2126 rollback completed
2127 abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2127 abort: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374")
2128 no rollback information available
2128 no rollback information available
2129 2:fb35475503ef
2129 2:fb35475503ef
2130
2130
2131
2131
General Comments 0
You need to be logged in to leave comments. Login now