##// END OF EJS Templates
bookmarks: fix pushkey compatibility mode (issue5777)...
Boris Feld -
r35829:e35320ce stable
parent child Browse files
Show More
@@ -1,2157 +1,2157
1 # bundle2.py - generic container format to transmit arbitrary data.
1 # bundle2.py - generic container format to transmit arbitrary data.
2 #
2 #
3 # Copyright 2013 Facebook, Inc.
3 # Copyright 2013 Facebook, Inc.
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7 """Handling of the new bundle2 format
7 """Handling of the new bundle2 format
8
8
9 The goal of bundle2 is to act as an atomically packet to transmit a set of
9 The goal of bundle2 is to act as an atomically packet to transmit a set of
10 payloads in an application agnostic way. It consist in a sequence of "parts"
10 payloads in an application agnostic way. It consist in a sequence of "parts"
11 that will be handed to and processed by the application layer.
11 that will be handed to and processed by the application layer.
12
12
13
13
14 General format architecture
14 General format architecture
15 ===========================
15 ===========================
16
16
17 The format is architectured as follow
17 The format is architectured as follow
18
18
19 - magic string
19 - magic string
20 - stream level parameters
20 - stream level parameters
21 - payload parts (any number)
21 - payload parts (any number)
22 - end of stream marker.
22 - end of stream marker.
23
23
24 the Binary format
24 the Binary format
25 ============================
25 ============================
26
26
27 All numbers are unsigned and big-endian.
27 All numbers are unsigned and big-endian.
28
28
29 stream level parameters
29 stream level parameters
30 ------------------------
30 ------------------------
31
31
32 Binary format is as follow
32 Binary format is as follow
33
33
34 :params size: int32
34 :params size: int32
35
35
36 The total number of Bytes used by the parameters
36 The total number of Bytes used by the parameters
37
37
38 :params value: arbitrary number of Bytes
38 :params value: arbitrary number of Bytes
39
39
40 A blob of `params size` containing the serialized version of all stream level
40 A blob of `params size` containing the serialized version of all stream level
41 parameters.
41 parameters.
42
42
43 The blob contains a space separated list of parameters. Parameters with value
43 The blob contains a space separated list of parameters. Parameters with value
44 are stored in the form `<name>=<value>`. Both name and value are urlquoted.
44 are stored in the form `<name>=<value>`. Both name and value are urlquoted.
45
45
46 Empty name are obviously forbidden.
46 Empty name are obviously forbidden.
47
47
48 Name MUST start with a letter. If this first letter is lower case, the
48 Name MUST start with a letter. If this first letter is lower case, the
49 parameter is advisory and can be safely ignored. However when the first
49 parameter is advisory and can be safely ignored. However when the first
50 letter is capital, the parameter is mandatory and the bundling process MUST
50 letter is capital, the parameter is mandatory and the bundling process MUST
51 stop if he is not able to proceed it.
51 stop if he is not able to proceed it.
52
52
53 Stream parameters use a simple textual format for two main reasons:
53 Stream parameters use a simple textual format for two main reasons:
54
54
55 - Stream level parameters should remain simple and we want to discourage any
55 - Stream level parameters should remain simple and we want to discourage any
56 crazy usage.
56 crazy usage.
57 - Textual data allow easy human inspection of a bundle2 header in case of
57 - Textual data allow easy human inspection of a bundle2 header in case of
58 troubles.
58 troubles.
59
59
60 Any Applicative level options MUST go into a bundle2 part instead.
60 Any Applicative level options MUST go into a bundle2 part instead.
61
61
62 Payload part
62 Payload part
63 ------------------------
63 ------------------------
64
64
65 Binary format is as follow
65 Binary format is as follow
66
66
67 :header size: int32
67 :header size: int32
68
68
69 The total number of Bytes used by the part header. When the header is empty
69 The total number of Bytes used by the part header. When the header is empty
70 (size = 0) this is interpreted as the end of stream marker.
70 (size = 0) this is interpreted as the end of stream marker.
71
71
72 :header:
72 :header:
73
73
74 The header defines how to interpret the part. It contains two piece of
74 The header defines how to interpret the part. It contains two piece of
75 data: the part type, and the part parameters.
75 data: the part type, and the part parameters.
76
76
77 The part type is used to route an application level handler, that can
77 The part type is used to route an application level handler, that can
78 interpret payload.
78 interpret payload.
79
79
80 Part parameters are passed to the application level handler. They are
80 Part parameters are passed to the application level handler. They are
81 meant to convey information that will help the application level object to
81 meant to convey information that will help the application level object to
82 interpret the part payload.
82 interpret the part payload.
83
83
84 The binary format of the header is has follow
84 The binary format of the header is has follow
85
85
86 :typesize: (one byte)
86 :typesize: (one byte)
87
87
88 :parttype: alphanumerical part name (restricted to [a-zA-Z0-9_:-]*)
88 :parttype: alphanumerical part name (restricted to [a-zA-Z0-9_:-]*)
89
89
90 :partid: A 32bits integer (unique in the bundle) that can be used to refer
90 :partid: A 32bits integer (unique in the bundle) that can be used to refer
91 to this part.
91 to this part.
92
92
93 :parameters:
93 :parameters:
94
94
95 Part's parameter may have arbitrary content, the binary structure is::
95 Part's parameter may have arbitrary content, the binary structure is::
96
96
97 <mandatory-count><advisory-count><param-sizes><param-data>
97 <mandatory-count><advisory-count><param-sizes><param-data>
98
98
99 :mandatory-count: 1 byte, number of mandatory parameters
99 :mandatory-count: 1 byte, number of mandatory parameters
100
100
101 :advisory-count: 1 byte, number of advisory parameters
101 :advisory-count: 1 byte, number of advisory parameters
102
102
103 :param-sizes:
103 :param-sizes:
104
104
105 N couple of bytes, where N is the total number of parameters. Each
105 N couple of bytes, where N is the total number of parameters. Each
106 couple contains (<size-of-key>, <size-of-value) for one parameter.
106 couple contains (<size-of-key>, <size-of-value) for one parameter.
107
107
108 :param-data:
108 :param-data:
109
109
110 A blob of bytes from which each parameter key and value can be
110 A blob of bytes from which each parameter key and value can be
111 retrieved using the list of size couples stored in the previous
111 retrieved using the list of size couples stored in the previous
112 field.
112 field.
113
113
114 Mandatory parameters comes first, then the advisory ones.
114 Mandatory parameters comes first, then the advisory ones.
115
115
116 Each parameter's key MUST be unique within the part.
116 Each parameter's key MUST be unique within the part.
117
117
118 :payload:
118 :payload:
119
119
120 payload is a series of `<chunksize><chunkdata>`.
120 payload is a series of `<chunksize><chunkdata>`.
121
121
122 `chunksize` is an int32, `chunkdata` are plain bytes (as much as
122 `chunksize` is an int32, `chunkdata` are plain bytes (as much as
123 `chunksize` says)` The payload part is concluded by a zero size chunk.
123 `chunksize` says)` The payload part is concluded by a zero size chunk.
124
124
125 The current implementation always produces either zero or one chunk.
125 The current implementation always produces either zero or one chunk.
126 This is an implementation limitation that will ultimately be lifted.
126 This is an implementation limitation that will ultimately be lifted.
127
127
128 `chunksize` can be negative to trigger special case processing. No such
128 `chunksize` can be negative to trigger special case processing. No such
129 processing is in place yet.
129 processing is in place yet.
130
130
131 Bundle processing
131 Bundle processing
132 ============================
132 ============================
133
133
134 Each part is processed in order using a "part handler". Handler are registered
134 Each part is processed in order using a "part handler". Handler are registered
135 for a certain part type.
135 for a certain part type.
136
136
137 The matching of a part to its handler is case insensitive. The case of the
137 The matching of a part to its handler is case insensitive. The case of the
138 part type is used to know if a part is mandatory or advisory. If the Part type
138 part type is used to know if a part is mandatory or advisory. If the Part type
139 contains any uppercase char it is considered mandatory. When no handler is
139 contains any uppercase char it is considered mandatory. When no handler is
140 known for a Mandatory part, the process is aborted and an exception is raised.
140 known for a Mandatory part, the process is aborted and an exception is raised.
141 If the part is advisory and no handler is known, the part is ignored. When the
141 If the part is advisory and no handler is known, the part is ignored. When the
142 process is aborted, the full bundle is still read from the stream to keep the
142 process is aborted, the full bundle is still read from the stream to keep the
143 channel usable. But none of the part read from an abort are processed. In the
143 channel usable. But none of the part read from an abort are processed. In the
144 future, dropping the stream may become an option for channel we do not care to
144 future, dropping the stream may become an option for channel we do not care to
145 preserve.
145 preserve.
146 """
146 """
147
147
148 from __future__ import absolute_import, division
148 from __future__ import absolute_import, division
149
149
150 import errno
150 import errno
151 import os
151 import os
152 import re
152 import re
153 import string
153 import string
154 import struct
154 import struct
155 import sys
155 import sys
156
156
157 from .i18n import _
157 from .i18n import _
158 from . import (
158 from . import (
159 bookmarks,
159 bookmarks,
160 changegroup,
160 changegroup,
161 error,
161 error,
162 node as nodemod,
162 node as nodemod,
163 obsolete,
163 obsolete,
164 phases,
164 phases,
165 pushkey,
165 pushkey,
166 pycompat,
166 pycompat,
167 streamclone,
167 streamclone,
168 tags,
168 tags,
169 url,
169 url,
170 util,
170 util,
171 )
171 )
172
172
173 urlerr = util.urlerr
173 urlerr = util.urlerr
174 urlreq = util.urlreq
174 urlreq = util.urlreq
175
175
176 _pack = struct.pack
176 _pack = struct.pack
177 _unpack = struct.unpack
177 _unpack = struct.unpack
178
178
179 _fstreamparamsize = '>i'
179 _fstreamparamsize = '>i'
180 _fpartheadersize = '>i'
180 _fpartheadersize = '>i'
181 _fparttypesize = '>B'
181 _fparttypesize = '>B'
182 _fpartid = '>I'
182 _fpartid = '>I'
183 _fpayloadsize = '>i'
183 _fpayloadsize = '>i'
184 _fpartparamcount = '>BB'
184 _fpartparamcount = '>BB'
185
185
186 preferedchunksize = 32768
186 preferedchunksize = 32768
187
187
188 _parttypeforbidden = re.compile('[^a-zA-Z0-9_:-]')
188 _parttypeforbidden = re.compile('[^a-zA-Z0-9_:-]')
189
189
190 def outdebug(ui, message):
190 def outdebug(ui, message):
191 """debug regarding output stream (bundling)"""
191 """debug regarding output stream (bundling)"""
192 if ui.configbool('devel', 'bundle2.debug'):
192 if ui.configbool('devel', 'bundle2.debug'):
193 ui.debug('bundle2-output: %s\n' % message)
193 ui.debug('bundle2-output: %s\n' % message)
194
194
195 def indebug(ui, message):
195 def indebug(ui, message):
196 """debug on input stream (unbundling)"""
196 """debug on input stream (unbundling)"""
197 if ui.configbool('devel', 'bundle2.debug'):
197 if ui.configbool('devel', 'bundle2.debug'):
198 ui.debug('bundle2-input: %s\n' % message)
198 ui.debug('bundle2-input: %s\n' % message)
199
199
200 def validateparttype(parttype):
200 def validateparttype(parttype):
201 """raise ValueError if a parttype contains invalid character"""
201 """raise ValueError if a parttype contains invalid character"""
202 if _parttypeforbidden.search(parttype):
202 if _parttypeforbidden.search(parttype):
203 raise ValueError(parttype)
203 raise ValueError(parttype)
204
204
205 def _makefpartparamsizes(nbparams):
205 def _makefpartparamsizes(nbparams):
206 """return a struct format to read part parameter sizes
206 """return a struct format to read part parameter sizes
207
207
208 The number parameters is variable so we need to build that format
208 The number parameters is variable so we need to build that format
209 dynamically.
209 dynamically.
210 """
210 """
211 return '>'+('BB'*nbparams)
211 return '>'+('BB'*nbparams)
212
212
213 parthandlermapping = {}
213 parthandlermapping = {}
214
214
215 def parthandler(parttype, params=()):
215 def parthandler(parttype, params=()):
216 """decorator that register a function as a bundle2 part handler
216 """decorator that register a function as a bundle2 part handler
217
217
218 eg::
218 eg::
219
219
220 @parthandler('myparttype', ('mandatory', 'param', 'handled'))
220 @parthandler('myparttype', ('mandatory', 'param', 'handled'))
221 def myparttypehandler(...):
221 def myparttypehandler(...):
222 '''process a part of type "my part".'''
222 '''process a part of type "my part".'''
223 ...
223 ...
224 """
224 """
225 validateparttype(parttype)
225 validateparttype(parttype)
226 def _decorator(func):
226 def _decorator(func):
227 lparttype = parttype.lower() # enforce lower case matching.
227 lparttype = parttype.lower() # enforce lower case matching.
228 assert lparttype not in parthandlermapping
228 assert lparttype not in parthandlermapping
229 parthandlermapping[lparttype] = func
229 parthandlermapping[lparttype] = func
230 func.params = frozenset(params)
230 func.params = frozenset(params)
231 return func
231 return func
232 return _decorator
232 return _decorator
233
233
234 class unbundlerecords(object):
234 class unbundlerecords(object):
235 """keep record of what happens during and unbundle
235 """keep record of what happens during and unbundle
236
236
237 New records are added using `records.add('cat', obj)`. Where 'cat' is a
237 New records are added using `records.add('cat', obj)`. Where 'cat' is a
238 category of record and obj is an arbitrary object.
238 category of record and obj is an arbitrary object.
239
239
240 `records['cat']` will return all entries of this category 'cat'.
240 `records['cat']` will return all entries of this category 'cat'.
241
241
242 Iterating on the object itself will yield `('category', obj)` tuples
242 Iterating on the object itself will yield `('category', obj)` tuples
243 for all entries.
243 for all entries.
244
244
245 All iterations happens in chronological order.
245 All iterations happens in chronological order.
246 """
246 """
247
247
248 def __init__(self):
248 def __init__(self):
249 self._categories = {}
249 self._categories = {}
250 self._sequences = []
250 self._sequences = []
251 self._replies = {}
251 self._replies = {}
252
252
253 def add(self, category, entry, inreplyto=None):
253 def add(self, category, entry, inreplyto=None):
254 """add a new record of a given category.
254 """add a new record of a given category.
255
255
256 The entry can then be retrieved in the list returned by
256 The entry can then be retrieved in the list returned by
257 self['category']."""
257 self['category']."""
258 self._categories.setdefault(category, []).append(entry)
258 self._categories.setdefault(category, []).append(entry)
259 self._sequences.append((category, entry))
259 self._sequences.append((category, entry))
260 if inreplyto is not None:
260 if inreplyto is not None:
261 self.getreplies(inreplyto).add(category, entry)
261 self.getreplies(inreplyto).add(category, entry)
262
262
263 def getreplies(self, partid):
263 def getreplies(self, partid):
264 """get the records that are replies to a specific part"""
264 """get the records that are replies to a specific part"""
265 return self._replies.setdefault(partid, unbundlerecords())
265 return self._replies.setdefault(partid, unbundlerecords())
266
266
267 def __getitem__(self, cat):
267 def __getitem__(self, cat):
268 return tuple(self._categories.get(cat, ()))
268 return tuple(self._categories.get(cat, ()))
269
269
270 def __iter__(self):
270 def __iter__(self):
271 return iter(self._sequences)
271 return iter(self._sequences)
272
272
273 def __len__(self):
273 def __len__(self):
274 return len(self._sequences)
274 return len(self._sequences)
275
275
276 def __nonzero__(self):
276 def __nonzero__(self):
277 return bool(self._sequences)
277 return bool(self._sequences)
278
278
279 __bool__ = __nonzero__
279 __bool__ = __nonzero__
280
280
281 class bundleoperation(object):
281 class bundleoperation(object):
282 """an object that represents a single bundling process
282 """an object that represents a single bundling process
283
283
284 Its purpose is to carry unbundle-related objects and states.
284 Its purpose is to carry unbundle-related objects and states.
285
285
286 A new object should be created at the beginning of each bundle processing.
286 A new object should be created at the beginning of each bundle processing.
287 The object is to be returned by the processing function.
287 The object is to be returned by the processing function.
288
288
289 The object has very little content now it will ultimately contain:
289 The object has very little content now it will ultimately contain:
290 * an access to the repo the bundle is applied to,
290 * an access to the repo the bundle is applied to,
291 * a ui object,
291 * a ui object,
292 * a way to retrieve a transaction to add changes to the repo,
292 * a way to retrieve a transaction to add changes to the repo,
293 * a way to record the result of processing each part,
293 * a way to record the result of processing each part,
294 * a way to construct a bundle response when applicable.
294 * a way to construct a bundle response when applicable.
295 """
295 """
296
296
297 def __init__(self, repo, transactiongetter, captureoutput=True):
297 def __init__(self, repo, transactiongetter, captureoutput=True):
298 self.repo = repo
298 self.repo = repo
299 self.ui = repo.ui
299 self.ui = repo.ui
300 self.records = unbundlerecords()
300 self.records = unbundlerecords()
301 self.reply = None
301 self.reply = None
302 self.captureoutput = captureoutput
302 self.captureoutput = captureoutput
303 self.hookargs = {}
303 self.hookargs = {}
304 self._gettransaction = transactiongetter
304 self._gettransaction = transactiongetter
305 # carries value that can modify part behavior
305 # carries value that can modify part behavior
306 self.modes = {}
306 self.modes = {}
307
307
308 def gettransaction(self):
308 def gettransaction(self):
309 transaction = self._gettransaction()
309 transaction = self._gettransaction()
310
310
311 if self.hookargs:
311 if self.hookargs:
312 # the ones added to the transaction supercede those added
312 # the ones added to the transaction supercede those added
313 # to the operation.
313 # to the operation.
314 self.hookargs.update(transaction.hookargs)
314 self.hookargs.update(transaction.hookargs)
315 transaction.hookargs = self.hookargs
315 transaction.hookargs = self.hookargs
316
316
317 # mark the hookargs as flushed. further attempts to add to
317 # mark the hookargs as flushed. further attempts to add to
318 # hookargs will result in an abort.
318 # hookargs will result in an abort.
319 self.hookargs = None
319 self.hookargs = None
320
320
321 return transaction
321 return transaction
322
322
323 def addhookargs(self, hookargs):
323 def addhookargs(self, hookargs):
324 if self.hookargs is None:
324 if self.hookargs is None:
325 raise error.ProgrammingError('attempted to add hookargs to '
325 raise error.ProgrammingError('attempted to add hookargs to '
326 'operation after transaction started')
326 'operation after transaction started')
327 self.hookargs.update(hookargs)
327 self.hookargs.update(hookargs)
328
328
329 class TransactionUnavailable(RuntimeError):
329 class TransactionUnavailable(RuntimeError):
330 pass
330 pass
331
331
332 def _notransaction():
332 def _notransaction():
333 """default method to get a transaction while processing a bundle
333 """default method to get a transaction while processing a bundle
334
334
335 Raise an exception to highlight the fact that no transaction was expected
335 Raise an exception to highlight the fact that no transaction was expected
336 to be created"""
336 to be created"""
337 raise TransactionUnavailable()
337 raise TransactionUnavailable()
338
338
339 def applybundle(repo, unbundler, tr, source=None, url=None, **kwargs):
339 def applybundle(repo, unbundler, tr, source=None, url=None, **kwargs):
340 # transform me into unbundler.apply() as soon as the freeze is lifted
340 # transform me into unbundler.apply() as soon as the freeze is lifted
341 if isinstance(unbundler, unbundle20):
341 if isinstance(unbundler, unbundle20):
342 tr.hookargs['bundle2'] = '1'
342 tr.hookargs['bundle2'] = '1'
343 if source is not None and 'source' not in tr.hookargs:
343 if source is not None and 'source' not in tr.hookargs:
344 tr.hookargs['source'] = source
344 tr.hookargs['source'] = source
345 if url is not None and 'url' not in tr.hookargs:
345 if url is not None and 'url' not in tr.hookargs:
346 tr.hookargs['url'] = url
346 tr.hookargs['url'] = url
347 return processbundle(repo, unbundler, lambda: tr)
347 return processbundle(repo, unbundler, lambda: tr)
348 else:
348 else:
349 # the transactiongetter won't be used, but we might as well set it
349 # the transactiongetter won't be used, but we might as well set it
350 op = bundleoperation(repo, lambda: tr)
350 op = bundleoperation(repo, lambda: tr)
351 _processchangegroup(op, unbundler, tr, source, url, **kwargs)
351 _processchangegroup(op, unbundler, tr, source, url, **kwargs)
352 return op
352 return op
353
353
354 class partiterator(object):
354 class partiterator(object):
355 def __init__(self, repo, op, unbundler):
355 def __init__(self, repo, op, unbundler):
356 self.repo = repo
356 self.repo = repo
357 self.op = op
357 self.op = op
358 self.unbundler = unbundler
358 self.unbundler = unbundler
359 self.iterator = None
359 self.iterator = None
360 self.count = 0
360 self.count = 0
361 self.current = None
361 self.current = None
362
362
363 def __enter__(self):
363 def __enter__(self):
364 def func():
364 def func():
365 itr = enumerate(self.unbundler.iterparts())
365 itr = enumerate(self.unbundler.iterparts())
366 for count, p in itr:
366 for count, p in itr:
367 self.count = count
367 self.count = count
368 self.current = p
368 self.current = p
369 yield p
369 yield p
370 p.consume()
370 p.consume()
371 self.current = None
371 self.current = None
372 self.iterator = func()
372 self.iterator = func()
373 return self.iterator
373 return self.iterator
374
374
375 def __exit__(self, type, exc, tb):
375 def __exit__(self, type, exc, tb):
376 if not self.iterator:
376 if not self.iterator:
377 return
377 return
378
378
379 # Only gracefully abort in a normal exception situation. User aborts
379 # Only gracefully abort in a normal exception situation. User aborts
380 # like Ctrl+C throw a KeyboardInterrupt which is not a base Exception,
380 # like Ctrl+C throw a KeyboardInterrupt which is not a base Exception,
381 # and should not gracefully cleanup.
381 # and should not gracefully cleanup.
382 if isinstance(exc, Exception):
382 if isinstance(exc, Exception):
383 # Any exceptions seeking to the end of the bundle at this point are
383 # Any exceptions seeking to the end of the bundle at this point are
384 # almost certainly related to the underlying stream being bad.
384 # almost certainly related to the underlying stream being bad.
385 # And, chances are that the exception we're handling is related to
385 # And, chances are that the exception we're handling is related to
386 # getting in that bad state. So, we swallow the seeking error and
386 # getting in that bad state. So, we swallow the seeking error and
387 # re-raise the original error.
387 # re-raise the original error.
388 seekerror = False
388 seekerror = False
389 try:
389 try:
390 if self.current:
390 if self.current:
391 # consume the part content to not corrupt the stream.
391 # consume the part content to not corrupt the stream.
392 self.current.consume()
392 self.current.consume()
393
393
394 for part in self.iterator:
394 for part in self.iterator:
395 # consume the bundle content
395 # consume the bundle content
396 part.consume()
396 part.consume()
397 except Exception:
397 except Exception:
398 seekerror = True
398 seekerror = True
399
399
400 # Small hack to let caller code distinguish exceptions from bundle2
400 # Small hack to let caller code distinguish exceptions from bundle2
401 # processing from processing the old format. This is mostly needed
401 # processing from processing the old format. This is mostly needed
402 # to handle different return codes to unbundle according to the type
402 # to handle different return codes to unbundle according to the type
403 # of bundle. We should probably clean up or drop this return code
403 # of bundle. We should probably clean up or drop this return code
404 # craziness in a future version.
404 # craziness in a future version.
405 exc.duringunbundle2 = True
405 exc.duringunbundle2 = True
406 salvaged = []
406 salvaged = []
407 replycaps = None
407 replycaps = None
408 if self.op.reply is not None:
408 if self.op.reply is not None:
409 salvaged = self.op.reply.salvageoutput()
409 salvaged = self.op.reply.salvageoutput()
410 replycaps = self.op.reply.capabilities
410 replycaps = self.op.reply.capabilities
411 exc._replycaps = replycaps
411 exc._replycaps = replycaps
412 exc._bundle2salvagedoutput = salvaged
412 exc._bundle2salvagedoutput = salvaged
413
413
414 # Re-raising from a variable loses the original stack. So only use
414 # Re-raising from a variable loses the original stack. So only use
415 # that form if we need to.
415 # that form if we need to.
416 if seekerror:
416 if seekerror:
417 raise exc
417 raise exc
418
418
419 self.repo.ui.debug('bundle2-input-bundle: %i parts total\n' %
419 self.repo.ui.debug('bundle2-input-bundle: %i parts total\n' %
420 self.count)
420 self.count)
421
421
422 def processbundle(repo, unbundler, transactiongetter=None, op=None):
422 def processbundle(repo, unbundler, transactiongetter=None, op=None):
423 """This function process a bundle, apply effect to/from a repo
423 """This function process a bundle, apply effect to/from a repo
424
424
425 It iterates over each part then searches for and uses the proper handling
425 It iterates over each part then searches for and uses the proper handling
426 code to process the part. Parts are processed in order.
426 code to process the part. Parts are processed in order.
427
427
428 Unknown Mandatory part will abort the process.
428 Unknown Mandatory part will abort the process.
429
429
430 It is temporarily possible to provide a prebuilt bundleoperation to the
430 It is temporarily possible to provide a prebuilt bundleoperation to the
431 function. This is used to ensure output is properly propagated in case of
431 function. This is used to ensure output is properly propagated in case of
432 an error during the unbundling. This output capturing part will likely be
432 an error during the unbundling. This output capturing part will likely be
433 reworked and this ability will probably go away in the process.
433 reworked and this ability will probably go away in the process.
434 """
434 """
435 if op is None:
435 if op is None:
436 if transactiongetter is None:
436 if transactiongetter is None:
437 transactiongetter = _notransaction
437 transactiongetter = _notransaction
438 op = bundleoperation(repo, transactiongetter)
438 op = bundleoperation(repo, transactiongetter)
439 # todo:
439 # todo:
440 # - replace this is a init function soon.
440 # - replace this is a init function soon.
441 # - exception catching
441 # - exception catching
442 unbundler.params
442 unbundler.params
443 if repo.ui.debugflag:
443 if repo.ui.debugflag:
444 msg = ['bundle2-input-bundle:']
444 msg = ['bundle2-input-bundle:']
445 if unbundler.params:
445 if unbundler.params:
446 msg.append(' %i params' % len(unbundler.params))
446 msg.append(' %i params' % len(unbundler.params))
447 if op._gettransaction is None or op._gettransaction is _notransaction:
447 if op._gettransaction is None or op._gettransaction is _notransaction:
448 msg.append(' no-transaction')
448 msg.append(' no-transaction')
449 else:
449 else:
450 msg.append(' with-transaction')
450 msg.append(' with-transaction')
451 msg.append('\n')
451 msg.append('\n')
452 repo.ui.debug(''.join(msg))
452 repo.ui.debug(''.join(msg))
453
453
454 processparts(repo, op, unbundler)
454 processparts(repo, op, unbundler)
455
455
456 return op
456 return op
457
457
458 def processparts(repo, op, unbundler):
458 def processparts(repo, op, unbundler):
459 with partiterator(repo, op, unbundler) as parts:
459 with partiterator(repo, op, unbundler) as parts:
460 for part in parts:
460 for part in parts:
461 _processpart(op, part)
461 _processpart(op, part)
462
462
463 def _processchangegroup(op, cg, tr, source, url, **kwargs):
463 def _processchangegroup(op, cg, tr, source, url, **kwargs):
464 ret = cg.apply(op.repo, tr, source, url, **kwargs)
464 ret = cg.apply(op.repo, tr, source, url, **kwargs)
465 op.records.add('changegroup', {
465 op.records.add('changegroup', {
466 'return': ret,
466 'return': ret,
467 })
467 })
468 return ret
468 return ret
469
469
470 def _gethandler(op, part):
470 def _gethandler(op, part):
471 status = 'unknown' # used by debug output
471 status = 'unknown' # used by debug output
472 try:
472 try:
473 handler = parthandlermapping.get(part.type)
473 handler = parthandlermapping.get(part.type)
474 if handler is None:
474 if handler is None:
475 status = 'unsupported-type'
475 status = 'unsupported-type'
476 raise error.BundleUnknownFeatureError(parttype=part.type)
476 raise error.BundleUnknownFeatureError(parttype=part.type)
477 indebug(op.ui, 'found a handler for part %s' % part.type)
477 indebug(op.ui, 'found a handler for part %s' % part.type)
478 unknownparams = part.mandatorykeys - handler.params
478 unknownparams = part.mandatorykeys - handler.params
479 if unknownparams:
479 if unknownparams:
480 unknownparams = list(unknownparams)
480 unknownparams = list(unknownparams)
481 unknownparams.sort()
481 unknownparams.sort()
482 status = 'unsupported-params (%s)' % ', '.join(unknownparams)
482 status = 'unsupported-params (%s)' % ', '.join(unknownparams)
483 raise error.BundleUnknownFeatureError(parttype=part.type,
483 raise error.BundleUnknownFeatureError(parttype=part.type,
484 params=unknownparams)
484 params=unknownparams)
485 status = 'supported'
485 status = 'supported'
486 except error.BundleUnknownFeatureError as exc:
486 except error.BundleUnknownFeatureError as exc:
487 if part.mandatory: # mandatory parts
487 if part.mandatory: # mandatory parts
488 raise
488 raise
489 indebug(op.ui, 'ignoring unsupported advisory part %s' % exc)
489 indebug(op.ui, 'ignoring unsupported advisory part %s' % exc)
490 return # skip to part processing
490 return # skip to part processing
491 finally:
491 finally:
492 if op.ui.debugflag:
492 if op.ui.debugflag:
493 msg = ['bundle2-input-part: "%s"' % part.type]
493 msg = ['bundle2-input-part: "%s"' % part.type]
494 if not part.mandatory:
494 if not part.mandatory:
495 msg.append(' (advisory)')
495 msg.append(' (advisory)')
496 nbmp = len(part.mandatorykeys)
496 nbmp = len(part.mandatorykeys)
497 nbap = len(part.params) - nbmp
497 nbap = len(part.params) - nbmp
498 if nbmp or nbap:
498 if nbmp or nbap:
499 msg.append(' (params:')
499 msg.append(' (params:')
500 if nbmp:
500 if nbmp:
501 msg.append(' %i mandatory' % nbmp)
501 msg.append(' %i mandatory' % nbmp)
502 if nbap:
502 if nbap:
503 msg.append(' %i advisory' % nbmp)
503 msg.append(' %i advisory' % nbmp)
504 msg.append(')')
504 msg.append(')')
505 msg.append(' %s\n' % status)
505 msg.append(' %s\n' % status)
506 op.ui.debug(''.join(msg))
506 op.ui.debug(''.join(msg))
507
507
508 return handler
508 return handler
509
509
510 def _processpart(op, part):
510 def _processpart(op, part):
511 """process a single part from a bundle
511 """process a single part from a bundle
512
512
513 The part is guaranteed to have been fully consumed when the function exits
513 The part is guaranteed to have been fully consumed when the function exits
514 (even if an exception is raised)."""
514 (even if an exception is raised)."""
515 handler = _gethandler(op, part)
515 handler = _gethandler(op, part)
516 if handler is None:
516 if handler is None:
517 return
517 return
518
518
519 # handler is called outside the above try block so that we don't
519 # handler is called outside the above try block so that we don't
520 # risk catching KeyErrors from anything other than the
520 # risk catching KeyErrors from anything other than the
521 # parthandlermapping lookup (any KeyError raised by handler()
521 # parthandlermapping lookup (any KeyError raised by handler()
522 # itself represents a defect of a different variety).
522 # itself represents a defect of a different variety).
523 output = None
523 output = None
524 if op.captureoutput and op.reply is not None:
524 if op.captureoutput and op.reply is not None:
525 op.ui.pushbuffer(error=True, subproc=True)
525 op.ui.pushbuffer(error=True, subproc=True)
526 output = ''
526 output = ''
527 try:
527 try:
528 handler(op, part)
528 handler(op, part)
529 finally:
529 finally:
530 if output is not None:
530 if output is not None:
531 output = op.ui.popbuffer()
531 output = op.ui.popbuffer()
532 if output:
532 if output:
533 outpart = op.reply.newpart('output', data=output,
533 outpart = op.reply.newpart('output', data=output,
534 mandatory=False)
534 mandatory=False)
535 outpart.addparam(
535 outpart.addparam(
536 'in-reply-to', pycompat.bytestr(part.id), mandatory=False)
536 'in-reply-to', pycompat.bytestr(part.id), mandatory=False)
537
537
538 def decodecaps(blob):
538 def decodecaps(blob):
539 """decode a bundle2 caps bytes blob into a dictionary
539 """decode a bundle2 caps bytes blob into a dictionary
540
540
541 The blob is a list of capabilities (one per line)
541 The blob is a list of capabilities (one per line)
542 Capabilities may have values using a line of the form::
542 Capabilities may have values using a line of the form::
543
543
544 capability=value1,value2,value3
544 capability=value1,value2,value3
545
545
546 The values are always a list."""
546 The values are always a list."""
547 caps = {}
547 caps = {}
548 for line in blob.splitlines():
548 for line in blob.splitlines():
549 if not line:
549 if not line:
550 continue
550 continue
551 if '=' not in line:
551 if '=' not in line:
552 key, vals = line, ()
552 key, vals = line, ()
553 else:
553 else:
554 key, vals = line.split('=', 1)
554 key, vals = line.split('=', 1)
555 vals = vals.split(',')
555 vals = vals.split(',')
556 key = urlreq.unquote(key)
556 key = urlreq.unquote(key)
557 vals = [urlreq.unquote(v) for v in vals]
557 vals = [urlreq.unquote(v) for v in vals]
558 caps[key] = vals
558 caps[key] = vals
559 return caps
559 return caps
560
560
561 def encodecaps(caps):
561 def encodecaps(caps):
562 """encode a bundle2 caps dictionary into a bytes blob"""
562 """encode a bundle2 caps dictionary into a bytes blob"""
563 chunks = []
563 chunks = []
564 for ca in sorted(caps):
564 for ca in sorted(caps):
565 vals = caps[ca]
565 vals = caps[ca]
566 ca = urlreq.quote(ca)
566 ca = urlreq.quote(ca)
567 vals = [urlreq.quote(v) for v in vals]
567 vals = [urlreq.quote(v) for v in vals]
568 if vals:
568 if vals:
569 ca = "%s=%s" % (ca, ','.join(vals))
569 ca = "%s=%s" % (ca, ','.join(vals))
570 chunks.append(ca)
570 chunks.append(ca)
571 return '\n'.join(chunks)
571 return '\n'.join(chunks)
572
572
573 bundletypes = {
573 bundletypes = {
574 "": ("", 'UN'), # only when using unbundle on ssh and old http servers
574 "": ("", 'UN'), # only when using unbundle on ssh and old http servers
575 # since the unification ssh accepts a header but there
575 # since the unification ssh accepts a header but there
576 # is no capability signaling it.
576 # is no capability signaling it.
577 "HG20": (), # special-cased below
577 "HG20": (), # special-cased below
578 "HG10UN": ("HG10UN", 'UN'),
578 "HG10UN": ("HG10UN", 'UN'),
579 "HG10BZ": ("HG10", 'BZ'),
579 "HG10BZ": ("HG10", 'BZ'),
580 "HG10GZ": ("HG10GZ", 'GZ'),
580 "HG10GZ": ("HG10GZ", 'GZ'),
581 }
581 }
582
582
583 # hgweb uses this list to communicate its preferred type
583 # hgweb uses this list to communicate its preferred type
584 bundlepriority = ['HG10GZ', 'HG10BZ', 'HG10UN']
584 bundlepriority = ['HG10GZ', 'HG10BZ', 'HG10UN']
585
585
586 class bundle20(object):
586 class bundle20(object):
587 """represent an outgoing bundle2 container
587 """represent an outgoing bundle2 container
588
588
589 Use the `addparam` method to add stream level parameter. and `newpart` to
589 Use the `addparam` method to add stream level parameter. and `newpart` to
590 populate it. Then call `getchunks` to retrieve all the binary chunks of
590 populate it. Then call `getchunks` to retrieve all the binary chunks of
591 data that compose the bundle2 container."""
591 data that compose the bundle2 container."""
592
592
593 _magicstring = 'HG20'
593 _magicstring = 'HG20'
594
594
595 def __init__(self, ui, capabilities=()):
595 def __init__(self, ui, capabilities=()):
596 self.ui = ui
596 self.ui = ui
597 self._params = []
597 self._params = []
598 self._parts = []
598 self._parts = []
599 self.capabilities = dict(capabilities)
599 self.capabilities = dict(capabilities)
600 self._compengine = util.compengines.forbundletype('UN')
600 self._compengine = util.compengines.forbundletype('UN')
601 self._compopts = None
601 self._compopts = None
602 # If compression is being handled by a consumer of the raw
602 # If compression is being handled by a consumer of the raw
603 # data (e.g. the wire protocol), unsetting this flag tells
603 # data (e.g. the wire protocol), unsetting this flag tells
604 # consumers that the bundle is best left uncompressed.
604 # consumers that the bundle is best left uncompressed.
605 self.prefercompressed = True
605 self.prefercompressed = True
606
606
607 def setcompression(self, alg, compopts=None):
607 def setcompression(self, alg, compopts=None):
608 """setup core part compression to <alg>"""
608 """setup core part compression to <alg>"""
609 if alg in (None, 'UN'):
609 if alg in (None, 'UN'):
610 return
610 return
611 assert not any(n.lower() == 'compression' for n, v in self._params)
611 assert not any(n.lower() == 'compression' for n, v in self._params)
612 self.addparam('Compression', alg)
612 self.addparam('Compression', alg)
613 self._compengine = util.compengines.forbundletype(alg)
613 self._compengine = util.compengines.forbundletype(alg)
614 self._compopts = compopts
614 self._compopts = compopts
615
615
616 @property
616 @property
617 def nbparts(self):
617 def nbparts(self):
618 """total number of parts added to the bundler"""
618 """total number of parts added to the bundler"""
619 return len(self._parts)
619 return len(self._parts)
620
620
621 # methods used to defines the bundle2 content
621 # methods used to defines the bundle2 content
622 def addparam(self, name, value=None):
622 def addparam(self, name, value=None):
623 """add a stream level parameter"""
623 """add a stream level parameter"""
624 if not name:
624 if not name:
625 raise ValueError(r'empty parameter name')
625 raise ValueError(r'empty parameter name')
626 if name[0:1] not in pycompat.bytestr(string.ascii_letters):
626 if name[0:1] not in pycompat.bytestr(string.ascii_letters):
627 raise ValueError(r'non letter first character: %s' % name)
627 raise ValueError(r'non letter first character: %s' % name)
628 self._params.append((name, value))
628 self._params.append((name, value))
629
629
630 def addpart(self, part):
630 def addpart(self, part):
631 """add a new part to the bundle2 container
631 """add a new part to the bundle2 container
632
632
633 Parts contains the actual applicative payload."""
633 Parts contains the actual applicative payload."""
634 assert part.id is None
634 assert part.id is None
635 part.id = len(self._parts) # very cheap counter
635 part.id = len(self._parts) # very cheap counter
636 self._parts.append(part)
636 self._parts.append(part)
637
637
638 def newpart(self, typeid, *args, **kwargs):
638 def newpart(self, typeid, *args, **kwargs):
639 """create a new part and add it to the containers
639 """create a new part and add it to the containers
640
640
641 As the part is directly added to the containers. For now, this means
641 As the part is directly added to the containers. For now, this means
642 that any failure to properly initialize the part after calling
642 that any failure to properly initialize the part after calling
643 ``newpart`` should result in a failure of the whole bundling process.
643 ``newpart`` should result in a failure of the whole bundling process.
644
644
645 You can still fall back to manually create and add if you need better
645 You can still fall back to manually create and add if you need better
646 control."""
646 control."""
647 part = bundlepart(typeid, *args, **kwargs)
647 part = bundlepart(typeid, *args, **kwargs)
648 self.addpart(part)
648 self.addpart(part)
649 return part
649 return part
650
650
651 # methods used to generate the bundle2 stream
651 # methods used to generate the bundle2 stream
652 def getchunks(self):
652 def getchunks(self):
653 if self.ui.debugflag:
653 if self.ui.debugflag:
654 msg = ['bundle2-output-bundle: "%s",' % self._magicstring]
654 msg = ['bundle2-output-bundle: "%s",' % self._magicstring]
655 if self._params:
655 if self._params:
656 msg.append(' (%i params)' % len(self._params))
656 msg.append(' (%i params)' % len(self._params))
657 msg.append(' %i parts total\n' % len(self._parts))
657 msg.append(' %i parts total\n' % len(self._parts))
658 self.ui.debug(''.join(msg))
658 self.ui.debug(''.join(msg))
659 outdebug(self.ui, 'start emission of %s stream' % self._magicstring)
659 outdebug(self.ui, 'start emission of %s stream' % self._magicstring)
660 yield self._magicstring
660 yield self._magicstring
661 param = self._paramchunk()
661 param = self._paramchunk()
662 outdebug(self.ui, 'bundle parameter: %s' % param)
662 outdebug(self.ui, 'bundle parameter: %s' % param)
663 yield _pack(_fstreamparamsize, len(param))
663 yield _pack(_fstreamparamsize, len(param))
664 if param:
664 if param:
665 yield param
665 yield param
666 for chunk in self._compengine.compressstream(self._getcorechunk(),
666 for chunk in self._compengine.compressstream(self._getcorechunk(),
667 self._compopts):
667 self._compopts):
668 yield chunk
668 yield chunk
669
669
670 def _paramchunk(self):
670 def _paramchunk(self):
671 """return a encoded version of all stream parameters"""
671 """return a encoded version of all stream parameters"""
672 blocks = []
672 blocks = []
673 for par, value in self._params:
673 for par, value in self._params:
674 par = urlreq.quote(par)
674 par = urlreq.quote(par)
675 if value is not None:
675 if value is not None:
676 value = urlreq.quote(value)
676 value = urlreq.quote(value)
677 par = '%s=%s' % (par, value)
677 par = '%s=%s' % (par, value)
678 blocks.append(par)
678 blocks.append(par)
679 return ' '.join(blocks)
679 return ' '.join(blocks)
680
680
681 def _getcorechunk(self):
681 def _getcorechunk(self):
682 """yield chunk for the core part of the bundle
682 """yield chunk for the core part of the bundle
683
683
684 (all but headers and parameters)"""
684 (all but headers and parameters)"""
685 outdebug(self.ui, 'start of parts')
685 outdebug(self.ui, 'start of parts')
686 for part in self._parts:
686 for part in self._parts:
687 outdebug(self.ui, 'bundle part: "%s"' % part.type)
687 outdebug(self.ui, 'bundle part: "%s"' % part.type)
688 for chunk in part.getchunks(ui=self.ui):
688 for chunk in part.getchunks(ui=self.ui):
689 yield chunk
689 yield chunk
690 outdebug(self.ui, 'end of bundle')
690 outdebug(self.ui, 'end of bundle')
691 yield _pack(_fpartheadersize, 0)
691 yield _pack(_fpartheadersize, 0)
692
692
693
693
694 def salvageoutput(self):
694 def salvageoutput(self):
695 """return a list with a copy of all output parts in the bundle
695 """return a list with a copy of all output parts in the bundle
696
696
697 This is meant to be used during error handling to make sure we preserve
697 This is meant to be used during error handling to make sure we preserve
698 server output"""
698 server output"""
699 salvaged = []
699 salvaged = []
700 for part in self._parts:
700 for part in self._parts:
701 if part.type.startswith('output'):
701 if part.type.startswith('output'):
702 salvaged.append(part.copy())
702 salvaged.append(part.copy())
703 return salvaged
703 return salvaged
704
704
705
705
706 class unpackermixin(object):
706 class unpackermixin(object):
707 """A mixin to extract bytes and struct data from a stream"""
707 """A mixin to extract bytes and struct data from a stream"""
708
708
709 def __init__(self, fp):
709 def __init__(self, fp):
710 self._fp = fp
710 self._fp = fp
711
711
712 def _unpack(self, format):
712 def _unpack(self, format):
713 """unpack this struct format from the stream
713 """unpack this struct format from the stream
714
714
715 This method is meant for internal usage by the bundle2 protocol only.
715 This method is meant for internal usage by the bundle2 protocol only.
716 They directly manipulate the low level stream including bundle2 level
716 They directly manipulate the low level stream including bundle2 level
717 instruction.
717 instruction.
718
718
719 Do not use it to implement higher-level logic or methods."""
719 Do not use it to implement higher-level logic or methods."""
720 data = self._readexact(struct.calcsize(format))
720 data = self._readexact(struct.calcsize(format))
721 return _unpack(format, data)
721 return _unpack(format, data)
722
722
723 def _readexact(self, size):
723 def _readexact(self, size):
724 """read exactly <size> bytes from the stream
724 """read exactly <size> bytes from the stream
725
725
726 This method is meant for internal usage by the bundle2 protocol only.
726 This method is meant for internal usage by the bundle2 protocol only.
727 They directly manipulate the low level stream including bundle2 level
727 They directly manipulate the low level stream including bundle2 level
728 instruction.
728 instruction.
729
729
730 Do not use it to implement higher-level logic or methods."""
730 Do not use it to implement higher-level logic or methods."""
731 return changegroup.readexactly(self._fp, size)
731 return changegroup.readexactly(self._fp, size)
732
732
733 def getunbundler(ui, fp, magicstring=None):
733 def getunbundler(ui, fp, magicstring=None):
734 """return a valid unbundler object for a given magicstring"""
734 """return a valid unbundler object for a given magicstring"""
735 if magicstring is None:
735 if magicstring is None:
736 magicstring = changegroup.readexactly(fp, 4)
736 magicstring = changegroup.readexactly(fp, 4)
737 magic, version = magicstring[0:2], magicstring[2:4]
737 magic, version = magicstring[0:2], magicstring[2:4]
738 if magic != 'HG':
738 if magic != 'HG':
739 ui.debug(
739 ui.debug(
740 "error: invalid magic: %r (version %r), should be 'HG'\n"
740 "error: invalid magic: %r (version %r), should be 'HG'\n"
741 % (magic, version))
741 % (magic, version))
742 raise error.Abort(_('not a Mercurial bundle'))
742 raise error.Abort(_('not a Mercurial bundle'))
743 unbundlerclass = formatmap.get(version)
743 unbundlerclass = formatmap.get(version)
744 if unbundlerclass is None:
744 if unbundlerclass is None:
745 raise error.Abort(_('unknown bundle version %s') % version)
745 raise error.Abort(_('unknown bundle version %s') % version)
746 unbundler = unbundlerclass(ui, fp)
746 unbundler = unbundlerclass(ui, fp)
747 indebug(ui, 'start processing of %s stream' % magicstring)
747 indebug(ui, 'start processing of %s stream' % magicstring)
748 return unbundler
748 return unbundler
749
749
750 class unbundle20(unpackermixin):
750 class unbundle20(unpackermixin):
751 """interpret a bundle2 stream
751 """interpret a bundle2 stream
752
752
753 This class is fed with a binary stream and yields parts through its
753 This class is fed with a binary stream and yields parts through its
754 `iterparts` methods."""
754 `iterparts` methods."""
755
755
756 _magicstring = 'HG20'
756 _magicstring = 'HG20'
757
757
758 def __init__(self, ui, fp):
758 def __init__(self, ui, fp):
759 """If header is specified, we do not read it out of the stream."""
759 """If header is specified, we do not read it out of the stream."""
760 self.ui = ui
760 self.ui = ui
761 self._compengine = util.compengines.forbundletype('UN')
761 self._compengine = util.compengines.forbundletype('UN')
762 self._compressed = None
762 self._compressed = None
763 super(unbundle20, self).__init__(fp)
763 super(unbundle20, self).__init__(fp)
764
764
765 @util.propertycache
765 @util.propertycache
766 def params(self):
766 def params(self):
767 """dictionary of stream level parameters"""
767 """dictionary of stream level parameters"""
768 indebug(self.ui, 'reading bundle2 stream parameters')
768 indebug(self.ui, 'reading bundle2 stream parameters')
769 params = {}
769 params = {}
770 paramssize = self._unpack(_fstreamparamsize)[0]
770 paramssize = self._unpack(_fstreamparamsize)[0]
771 if paramssize < 0:
771 if paramssize < 0:
772 raise error.BundleValueError('negative bundle param size: %i'
772 raise error.BundleValueError('negative bundle param size: %i'
773 % paramssize)
773 % paramssize)
774 if paramssize:
774 if paramssize:
775 params = self._readexact(paramssize)
775 params = self._readexact(paramssize)
776 params = self._processallparams(params)
776 params = self._processallparams(params)
777 return params
777 return params
778
778
779 def _processallparams(self, paramsblock):
779 def _processallparams(self, paramsblock):
780 """"""
780 """"""
781 params = util.sortdict()
781 params = util.sortdict()
782 for p in paramsblock.split(' '):
782 for p in paramsblock.split(' '):
783 p = p.split('=', 1)
783 p = p.split('=', 1)
784 p = [urlreq.unquote(i) for i in p]
784 p = [urlreq.unquote(i) for i in p]
785 if len(p) < 2:
785 if len(p) < 2:
786 p.append(None)
786 p.append(None)
787 self._processparam(*p)
787 self._processparam(*p)
788 params[p[0]] = p[1]
788 params[p[0]] = p[1]
789 return params
789 return params
790
790
791
791
792 def _processparam(self, name, value):
792 def _processparam(self, name, value):
793 """process a parameter, applying its effect if needed
793 """process a parameter, applying its effect if needed
794
794
795 Parameter starting with a lower case letter are advisory and will be
795 Parameter starting with a lower case letter are advisory and will be
796 ignored when unknown. Those starting with an upper case letter are
796 ignored when unknown. Those starting with an upper case letter are
797 mandatory and will this function will raise a KeyError when unknown.
797 mandatory and will this function will raise a KeyError when unknown.
798
798
799 Note: no option are currently supported. Any input will be either
799 Note: no option are currently supported. Any input will be either
800 ignored or failing.
800 ignored or failing.
801 """
801 """
802 if not name:
802 if not name:
803 raise ValueError(r'empty parameter name')
803 raise ValueError(r'empty parameter name')
804 if name[0:1] not in pycompat.bytestr(string.ascii_letters):
804 if name[0:1] not in pycompat.bytestr(string.ascii_letters):
805 raise ValueError(r'non letter first character: %s' % name)
805 raise ValueError(r'non letter first character: %s' % name)
806 try:
806 try:
807 handler = b2streamparamsmap[name.lower()]
807 handler = b2streamparamsmap[name.lower()]
808 except KeyError:
808 except KeyError:
809 if name[0:1].islower():
809 if name[0:1].islower():
810 indebug(self.ui, "ignoring unknown parameter %s" % name)
810 indebug(self.ui, "ignoring unknown parameter %s" % name)
811 else:
811 else:
812 raise error.BundleUnknownFeatureError(params=(name,))
812 raise error.BundleUnknownFeatureError(params=(name,))
813 else:
813 else:
814 handler(self, name, value)
814 handler(self, name, value)
815
815
816 def _forwardchunks(self):
816 def _forwardchunks(self):
817 """utility to transfer a bundle2 as binary
817 """utility to transfer a bundle2 as binary
818
818
819 This is made necessary by the fact the 'getbundle' command over 'ssh'
819 This is made necessary by the fact the 'getbundle' command over 'ssh'
820 have no way to know then the reply end, relying on the bundle to be
820 have no way to know then the reply end, relying on the bundle to be
821 interpreted to know its end. This is terrible and we are sorry, but we
821 interpreted to know its end. This is terrible and we are sorry, but we
822 needed to move forward to get general delta enabled.
822 needed to move forward to get general delta enabled.
823 """
823 """
824 yield self._magicstring
824 yield self._magicstring
825 assert 'params' not in vars(self)
825 assert 'params' not in vars(self)
826 paramssize = self._unpack(_fstreamparamsize)[0]
826 paramssize = self._unpack(_fstreamparamsize)[0]
827 if paramssize < 0:
827 if paramssize < 0:
828 raise error.BundleValueError('negative bundle param size: %i'
828 raise error.BundleValueError('negative bundle param size: %i'
829 % paramssize)
829 % paramssize)
830 yield _pack(_fstreamparamsize, paramssize)
830 yield _pack(_fstreamparamsize, paramssize)
831 if paramssize:
831 if paramssize:
832 params = self._readexact(paramssize)
832 params = self._readexact(paramssize)
833 self._processallparams(params)
833 self._processallparams(params)
834 yield params
834 yield params
835 assert self._compengine.bundletype == 'UN'
835 assert self._compengine.bundletype == 'UN'
836 # From there, payload might need to be decompressed
836 # From there, payload might need to be decompressed
837 self._fp = self._compengine.decompressorreader(self._fp)
837 self._fp = self._compengine.decompressorreader(self._fp)
838 emptycount = 0
838 emptycount = 0
839 while emptycount < 2:
839 while emptycount < 2:
840 # so we can brainlessly loop
840 # so we can brainlessly loop
841 assert _fpartheadersize == _fpayloadsize
841 assert _fpartheadersize == _fpayloadsize
842 size = self._unpack(_fpartheadersize)[0]
842 size = self._unpack(_fpartheadersize)[0]
843 yield _pack(_fpartheadersize, size)
843 yield _pack(_fpartheadersize, size)
844 if size:
844 if size:
845 emptycount = 0
845 emptycount = 0
846 else:
846 else:
847 emptycount += 1
847 emptycount += 1
848 continue
848 continue
849 if size == flaginterrupt:
849 if size == flaginterrupt:
850 continue
850 continue
851 elif size < 0:
851 elif size < 0:
852 raise error.BundleValueError('negative chunk size: %i')
852 raise error.BundleValueError('negative chunk size: %i')
853 yield self._readexact(size)
853 yield self._readexact(size)
854
854
855
855
856 def iterparts(self, seekable=False):
856 def iterparts(self, seekable=False):
857 """yield all parts contained in the stream"""
857 """yield all parts contained in the stream"""
858 cls = seekableunbundlepart if seekable else unbundlepart
858 cls = seekableunbundlepart if seekable else unbundlepart
859 # make sure param have been loaded
859 # make sure param have been loaded
860 self.params
860 self.params
861 # From there, payload need to be decompressed
861 # From there, payload need to be decompressed
862 self._fp = self._compengine.decompressorreader(self._fp)
862 self._fp = self._compengine.decompressorreader(self._fp)
863 indebug(self.ui, 'start extraction of bundle2 parts')
863 indebug(self.ui, 'start extraction of bundle2 parts')
864 headerblock = self._readpartheader()
864 headerblock = self._readpartheader()
865 while headerblock is not None:
865 while headerblock is not None:
866 part = cls(self.ui, headerblock, self._fp)
866 part = cls(self.ui, headerblock, self._fp)
867 yield part
867 yield part
868 # Ensure part is fully consumed so we can start reading the next
868 # Ensure part is fully consumed so we can start reading the next
869 # part.
869 # part.
870 part.consume()
870 part.consume()
871
871
872 headerblock = self._readpartheader()
872 headerblock = self._readpartheader()
873 indebug(self.ui, 'end of bundle2 stream')
873 indebug(self.ui, 'end of bundle2 stream')
874
874
875 def _readpartheader(self):
875 def _readpartheader(self):
876 """reads a part header size and return the bytes blob
876 """reads a part header size and return the bytes blob
877
877
878 returns None if empty"""
878 returns None if empty"""
879 headersize = self._unpack(_fpartheadersize)[0]
879 headersize = self._unpack(_fpartheadersize)[0]
880 if headersize < 0:
880 if headersize < 0:
881 raise error.BundleValueError('negative part header size: %i'
881 raise error.BundleValueError('negative part header size: %i'
882 % headersize)
882 % headersize)
883 indebug(self.ui, 'part header size: %i' % headersize)
883 indebug(self.ui, 'part header size: %i' % headersize)
884 if headersize:
884 if headersize:
885 return self._readexact(headersize)
885 return self._readexact(headersize)
886 return None
886 return None
887
887
888 def compressed(self):
888 def compressed(self):
889 self.params # load params
889 self.params # load params
890 return self._compressed
890 return self._compressed
891
891
892 def close(self):
892 def close(self):
893 """close underlying file"""
893 """close underlying file"""
894 if util.safehasattr(self._fp, 'close'):
894 if util.safehasattr(self._fp, 'close'):
895 return self._fp.close()
895 return self._fp.close()
896
896
897 formatmap = {'20': unbundle20}
897 formatmap = {'20': unbundle20}
898
898
899 b2streamparamsmap = {}
899 b2streamparamsmap = {}
900
900
901 def b2streamparamhandler(name):
901 def b2streamparamhandler(name):
902 """register a handler for a stream level parameter"""
902 """register a handler for a stream level parameter"""
903 def decorator(func):
903 def decorator(func):
904 assert name not in formatmap
904 assert name not in formatmap
905 b2streamparamsmap[name] = func
905 b2streamparamsmap[name] = func
906 return func
906 return func
907 return decorator
907 return decorator
908
908
909 @b2streamparamhandler('compression')
909 @b2streamparamhandler('compression')
910 def processcompression(unbundler, param, value):
910 def processcompression(unbundler, param, value):
911 """read compression parameter and install payload decompression"""
911 """read compression parameter and install payload decompression"""
912 if value not in util.compengines.supportedbundletypes:
912 if value not in util.compengines.supportedbundletypes:
913 raise error.BundleUnknownFeatureError(params=(param,),
913 raise error.BundleUnknownFeatureError(params=(param,),
914 values=(value,))
914 values=(value,))
915 unbundler._compengine = util.compengines.forbundletype(value)
915 unbundler._compengine = util.compengines.forbundletype(value)
916 if value is not None:
916 if value is not None:
917 unbundler._compressed = True
917 unbundler._compressed = True
918
918
919 class bundlepart(object):
919 class bundlepart(object):
920 """A bundle2 part contains application level payload
920 """A bundle2 part contains application level payload
921
921
922 The part `type` is used to route the part to the application level
922 The part `type` is used to route the part to the application level
923 handler.
923 handler.
924
924
925 The part payload is contained in ``part.data``. It could be raw bytes or a
925 The part payload is contained in ``part.data``. It could be raw bytes or a
926 generator of byte chunks.
926 generator of byte chunks.
927
927
928 You can add parameters to the part using the ``addparam`` method.
928 You can add parameters to the part using the ``addparam`` method.
929 Parameters can be either mandatory (default) or advisory. Remote side
929 Parameters can be either mandatory (default) or advisory. Remote side
930 should be able to safely ignore the advisory ones.
930 should be able to safely ignore the advisory ones.
931
931
932 Both data and parameters cannot be modified after the generation has begun.
932 Both data and parameters cannot be modified after the generation has begun.
933 """
933 """
934
934
935 def __init__(self, parttype, mandatoryparams=(), advisoryparams=(),
935 def __init__(self, parttype, mandatoryparams=(), advisoryparams=(),
936 data='', mandatory=True):
936 data='', mandatory=True):
937 validateparttype(parttype)
937 validateparttype(parttype)
938 self.id = None
938 self.id = None
939 self.type = parttype
939 self.type = parttype
940 self._data = data
940 self._data = data
941 self._mandatoryparams = list(mandatoryparams)
941 self._mandatoryparams = list(mandatoryparams)
942 self._advisoryparams = list(advisoryparams)
942 self._advisoryparams = list(advisoryparams)
943 # checking for duplicated entries
943 # checking for duplicated entries
944 self._seenparams = set()
944 self._seenparams = set()
945 for pname, __ in self._mandatoryparams + self._advisoryparams:
945 for pname, __ in self._mandatoryparams + self._advisoryparams:
946 if pname in self._seenparams:
946 if pname in self._seenparams:
947 raise error.ProgrammingError('duplicated params: %s' % pname)
947 raise error.ProgrammingError('duplicated params: %s' % pname)
948 self._seenparams.add(pname)
948 self._seenparams.add(pname)
949 # status of the part's generation:
949 # status of the part's generation:
950 # - None: not started,
950 # - None: not started,
951 # - False: currently generated,
951 # - False: currently generated,
952 # - True: generation done.
952 # - True: generation done.
953 self._generated = None
953 self._generated = None
954 self.mandatory = mandatory
954 self.mandatory = mandatory
955
955
956 def __repr__(self):
956 def __repr__(self):
957 cls = "%s.%s" % (self.__class__.__module__, self.__class__.__name__)
957 cls = "%s.%s" % (self.__class__.__module__, self.__class__.__name__)
958 return ('<%s object at %x; id: %s; type: %s; mandatory: %s>'
958 return ('<%s object at %x; id: %s; type: %s; mandatory: %s>'
959 % (cls, id(self), self.id, self.type, self.mandatory))
959 % (cls, id(self), self.id, self.type, self.mandatory))
960
960
961 def copy(self):
961 def copy(self):
962 """return a copy of the part
962 """return a copy of the part
963
963
964 The new part have the very same content but no partid assigned yet.
964 The new part have the very same content but no partid assigned yet.
965 Parts with generated data cannot be copied."""
965 Parts with generated data cannot be copied."""
966 assert not util.safehasattr(self.data, 'next')
966 assert not util.safehasattr(self.data, 'next')
967 return self.__class__(self.type, self._mandatoryparams,
967 return self.__class__(self.type, self._mandatoryparams,
968 self._advisoryparams, self._data, self.mandatory)
968 self._advisoryparams, self._data, self.mandatory)
969
969
970 # methods used to defines the part content
970 # methods used to defines the part content
971 @property
971 @property
972 def data(self):
972 def data(self):
973 return self._data
973 return self._data
974
974
975 @data.setter
975 @data.setter
976 def data(self, data):
976 def data(self, data):
977 if self._generated is not None:
977 if self._generated is not None:
978 raise error.ReadOnlyPartError('part is being generated')
978 raise error.ReadOnlyPartError('part is being generated')
979 self._data = data
979 self._data = data
980
980
981 @property
981 @property
982 def mandatoryparams(self):
982 def mandatoryparams(self):
983 # make it an immutable tuple to force people through ``addparam``
983 # make it an immutable tuple to force people through ``addparam``
984 return tuple(self._mandatoryparams)
984 return tuple(self._mandatoryparams)
985
985
986 @property
986 @property
987 def advisoryparams(self):
987 def advisoryparams(self):
988 # make it an immutable tuple to force people through ``addparam``
988 # make it an immutable tuple to force people through ``addparam``
989 return tuple(self._advisoryparams)
989 return tuple(self._advisoryparams)
990
990
991 def addparam(self, name, value='', mandatory=True):
991 def addparam(self, name, value='', mandatory=True):
992 """add a parameter to the part
992 """add a parameter to the part
993
993
994 If 'mandatory' is set to True, the remote handler must claim support
994 If 'mandatory' is set to True, the remote handler must claim support
995 for this parameter or the unbundling will be aborted.
995 for this parameter or the unbundling will be aborted.
996
996
997 The 'name' and 'value' cannot exceed 255 bytes each.
997 The 'name' and 'value' cannot exceed 255 bytes each.
998 """
998 """
999 if self._generated is not None:
999 if self._generated is not None:
1000 raise error.ReadOnlyPartError('part is being generated')
1000 raise error.ReadOnlyPartError('part is being generated')
1001 if name in self._seenparams:
1001 if name in self._seenparams:
1002 raise ValueError('duplicated params: %s' % name)
1002 raise ValueError('duplicated params: %s' % name)
1003 self._seenparams.add(name)
1003 self._seenparams.add(name)
1004 params = self._advisoryparams
1004 params = self._advisoryparams
1005 if mandatory:
1005 if mandatory:
1006 params = self._mandatoryparams
1006 params = self._mandatoryparams
1007 params.append((name, value))
1007 params.append((name, value))
1008
1008
1009 # methods used to generates the bundle2 stream
1009 # methods used to generates the bundle2 stream
1010 def getchunks(self, ui):
1010 def getchunks(self, ui):
1011 if self._generated is not None:
1011 if self._generated is not None:
1012 raise error.ProgrammingError('part can only be consumed once')
1012 raise error.ProgrammingError('part can only be consumed once')
1013 self._generated = False
1013 self._generated = False
1014
1014
1015 if ui.debugflag:
1015 if ui.debugflag:
1016 msg = ['bundle2-output-part: "%s"' % self.type]
1016 msg = ['bundle2-output-part: "%s"' % self.type]
1017 if not self.mandatory:
1017 if not self.mandatory:
1018 msg.append(' (advisory)')
1018 msg.append(' (advisory)')
1019 nbmp = len(self.mandatoryparams)
1019 nbmp = len(self.mandatoryparams)
1020 nbap = len(self.advisoryparams)
1020 nbap = len(self.advisoryparams)
1021 if nbmp or nbap:
1021 if nbmp or nbap:
1022 msg.append(' (params:')
1022 msg.append(' (params:')
1023 if nbmp:
1023 if nbmp:
1024 msg.append(' %i mandatory' % nbmp)
1024 msg.append(' %i mandatory' % nbmp)
1025 if nbap:
1025 if nbap:
1026 msg.append(' %i advisory' % nbmp)
1026 msg.append(' %i advisory' % nbmp)
1027 msg.append(')')
1027 msg.append(')')
1028 if not self.data:
1028 if not self.data:
1029 msg.append(' empty payload')
1029 msg.append(' empty payload')
1030 elif (util.safehasattr(self.data, 'next')
1030 elif (util.safehasattr(self.data, 'next')
1031 or util.safehasattr(self.data, '__next__')):
1031 or util.safehasattr(self.data, '__next__')):
1032 msg.append(' streamed payload')
1032 msg.append(' streamed payload')
1033 else:
1033 else:
1034 msg.append(' %i bytes payload' % len(self.data))
1034 msg.append(' %i bytes payload' % len(self.data))
1035 msg.append('\n')
1035 msg.append('\n')
1036 ui.debug(''.join(msg))
1036 ui.debug(''.join(msg))
1037
1037
1038 #### header
1038 #### header
1039 if self.mandatory:
1039 if self.mandatory:
1040 parttype = self.type.upper()
1040 parttype = self.type.upper()
1041 else:
1041 else:
1042 parttype = self.type.lower()
1042 parttype = self.type.lower()
1043 outdebug(ui, 'part %s: "%s"' % (pycompat.bytestr(self.id), parttype))
1043 outdebug(ui, 'part %s: "%s"' % (pycompat.bytestr(self.id), parttype))
1044 ## parttype
1044 ## parttype
1045 header = [_pack(_fparttypesize, len(parttype)),
1045 header = [_pack(_fparttypesize, len(parttype)),
1046 parttype, _pack(_fpartid, self.id),
1046 parttype, _pack(_fpartid, self.id),
1047 ]
1047 ]
1048 ## parameters
1048 ## parameters
1049 # count
1049 # count
1050 manpar = self.mandatoryparams
1050 manpar = self.mandatoryparams
1051 advpar = self.advisoryparams
1051 advpar = self.advisoryparams
1052 header.append(_pack(_fpartparamcount, len(manpar), len(advpar)))
1052 header.append(_pack(_fpartparamcount, len(manpar), len(advpar)))
1053 # size
1053 # size
1054 parsizes = []
1054 parsizes = []
1055 for key, value in manpar:
1055 for key, value in manpar:
1056 parsizes.append(len(key))
1056 parsizes.append(len(key))
1057 parsizes.append(len(value))
1057 parsizes.append(len(value))
1058 for key, value in advpar:
1058 for key, value in advpar:
1059 parsizes.append(len(key))
1059 parsizes.append(len(key))
1060 parsizes.append(len(value))
1060 parsizes.append(len(value))
1061 paramsizes = _pack(_makefpartparamsizes(len(parsizes) // 2), *parsizes)
1061 paramsizes = _pack(_makefpartparamsizes(len(parsizes) // 2), *parsizes)
1062 header.append(paramsizes)
1062 header.append(paramsizes)
1063 # key, value
1063 # key, value
1064 for key, value in manpar:
1064 for key, value in manpar:
1065 header.append(key)
1065 header.append(key)
1066 header.append(value)
1066 header.append(value)
1067 for key, value in advpar:
1067 for key, value in advpar:
1068 header.append(key)
1068 header.append(key)
1069 header.append(value)
1069 header.append(value)
1070 ## finalize header
1070 ## finalize header
1071 try:
1071 try:
1072 headerchunk = ''.join(header)
1072 headerchunk = ''.join(header)
1073 except TypeError:
1073 except TypeError:
1074 raise TypeError(r'Found a non-bytes trying to '
1074 raise TypeError(r'Found a non-bytes trying to '
1075 r'build bundle part header: %r' % header)
1075 r'build bundle part header: %r' % header)
1076 outdebug(ui, 'header chunk size: %i' % len(headerchunk))
1076 outdebug(ui, 'header chunk size: %i' % len(headerchunk))
1077 yield _pack(_fpartheadersize, len(headerchunk))
1077 yield _pack(_fpartheadersize, len(headerchunk))
1078 yield headerchunk
1078 yield headerchunk
1079 ## payload
1079 ## payload
1080 try:
1080 try:
1081 for chunk in self._payloadchunks():
1081 for chunk in self._payloadchunks():
1082 outdebug(ui, 'payload chunk size: %i' % len(chunk))
1082 outdebug(ui, 'payload chunk size: %i' % len(chunk))
1083 yield _pack(_fpayloadsize, len(chunk))
1083 yield _pack(_fpayloadsize, len(chunk))
1084 yield chunk
1084 yield chunk
1085 except GeneratorExit:
1085 except GeneratorExit:
1086 # GeneratorExit means that nobody is listening for our
1086 # GeneratorExit means that nobody is listening for our
1087 # results anyway, so just bail quickly rather than trying
1087 # results anyway, so just bail quickly rather than trying
1088 # to produce an error part.
1088 # to produce an error part.
1089 ui.debug('bundle2-generatorexit\n')
1089 ui.debug('bundle2-generatorexit\n')
1090 raise
1090 raise
1091 except BaseException as exc:
1091 except BaseException as exc:
1092 bexc = util.forcebytestr(exc)
1092 bexc = util.forcebytestr(exc)
1093 # backup exception data for later
1093 # backup exception data for later
1094 ui.debug('bundle2-input-stream-interrupt: encoding exception %s'
1094 ui.debug('bundle2-input-stream-interrupt: encoding exception %s'
1095 % bexc)
1095 % bexc)
1096 tb = sys.exc_info()[2]
1096 tb = sys.exc_info()[2]
1097 msg = 'unexpected error: %s' % bexc
1097 msg = 'unexpected error: %s' % bexc
1098 interpart = bundlepart('error:abort', [('message', msg)],
1098 interpart = bundlepart('error:abort', [('message', msg)],
1099 mandatory=False)
1099 mandatory=False)
1100 interpart.id = 0
1100 interpart.id = 0
1101 yield _pack(_fpayloadsize, -1)
1101 yield _pack(_fpayloadsize, -1)
1102 for chunk in interpart.getchunks(ui=ui):
1102 for chunk in interpart.getchunks(ui=ui):
1103 yield chunk
1103 yield chunk
1104 outdebug(ui, 'closing payload chunk')
1104 outdebug(ui, 'closing payload chunk')
1105 # abort current part payload
1105 # abort current part payload
1106 yield _pack(_fpayloadsize, 0)
1106 yield _pack(_fpayloadsize, 0)
1107 pycompat.raisewithtb(exc, tb)
1107 pycompat.raisewithtb(exc, tb)
1108 # end of payload
1108 # end of payload
1109 outdebug(ui, 'closing payload chunk')
1109 outdebug(ui, 'closing payload chunk')
1110 yield _pack(_fpayloadsize, 0)
1110 yield _pack(_fpayloadsize, 0)
1111 self._generated = True
1111 self._generated = True
1112
1112
1113 def _payloadchunks(self):
1113 def _payloadchunks(self):
1114 """yield chunks of a the part payload
1114 """yield chunks of a the part payload
1115
1115
1116 Exists to handle the different methods to provide data to a part."""
1116 Exists to handle the different methods to provide data to a part."""
1117 # we only support fixed size data now.
1117 # we only support fixed size data now.
1118 # This will be improved in the future.
1118 # This will be improved in the future.
1119 if (util.safehasattr(self.data, 'next')
1119 if (util.safehasattr(self.data, 'next')
1120 or util.safehasattr(self.data, '__next__')):
1120 or util.safehasattr(self.data, '__next__')):
1121 buff = util.chunkbuffer(self.data)
1121 buff = util.chunkbuffer(self.data)
1122 chunk = buff.read(preferedchunksize)
1122 chunk = buff.read(preferedchunksize)
1123 while chunk:
1123 while chunk:
1124 yield chunk
1124 yield chunk
1125 chunk = buff.read(preferedchunksize)
1125 chunk = buff.read(preferedchunksize)
1126 elif len(self.data):
1126 elif len(self.data):
1127 yield self.data
1127 yield self.data
1128
1128
1129
1129
1130 flaginterrupt = -1
1130 flaginterrupt = -1
1131
1131
1132 class interrupthandler(unpackermixin):
1132 class interrupthandler(unpackermixin):
1133 """read one part and process it with restricted capability
1133 """read one part and process it with restricted capability
1134
1134
1135 This allows to transmit exception raised on the producer size during part
1135 This allows to transmit exception raised on the producer size during part
1136 iteration while the consumer is reading a part.
1136 iteration while the consumer is reading a part.
1137
1137
1138 Part processed in this manner only have access to a ui object,"""
1138 Part processed in this manner only have access to a ui object,"""
1139
1139
1140 def __init__(self, ui, fp):
1140 def __init__(self, ui, fp):
1141 super(interrupthandler, self).__init__(fp)
1141 super(interrupthandler, self).__init__(fp)
1142 self.ui = ui
1142 self.ui = ui
1143
1143
1144 def _readpartheader(self):
1144 def _readpartheader(self):
1145 """reads a part header size and return the bytes blob
1145 """reads a part header size and return the bytes blob
1146
1146
1147 returns None if empty"""
1147 returns None if empty"""
1148 headersize = self._unpack(_fpartheadersize)[0]
1148 headersize = self._unpack(_fpartheadersize)[0]
1149 if headersize < 0:
1149 if headersize < 0:
1150 raise error.BundleValueError('negative part header size: %i'
1150 raise error.BundleValueError('negative part header size: %i'
1151 % headersize)
1151 % headersize)
1152 indebug(self.ui, 'part header size: %i\n' % headersize)
1152 indebug(self.ui, 'part header size: %i\n' % headersize)
1153 if headersize:
1153 if headersize:
1154 return self._readexact(headersize)
1154 return self._readexact(headersize)
1155 return None
1155 return None
1156
1156
1157 def __call__(self):
1157 def __call__(self):
1158
1158
1159 self.ui.debug('bundle2-input-stream-interrupt:'
1159 self.ui.debug('bundle2-input-stream-interrupt:'
1160 ' opening out of band context\n')
1160 ' opening out of band context\n')
1161 indebug(self.ui, 'bundle2 stream interruption, looking for a part.')
1161 indebug(self.ui, 'bundle2 stream interruption, looking for a part.')
1162 headerblock = self._readpartheader()
1162 headerblock = self._readpartheader()
1163 if headerblock is None:
1163 if headerblock is None:
1164 indebug(self.ui, 'no part found during interruption.')
1164 indebug(self.ui, 'no part found during interruption.')
1165 return
1165 return
1166 part = unbundlepart(self.ui, headerblock, self._fp)
1166 part = unbundlepart(self.ui, headerblock, self._fp)
1167 op = interruptoperation(self.ui)
1167 op = interruptoperation(self.ui)
1168 hardabort = False
1168 hardabort = False
1169 try:
1169 try:
1170 _processpart(op, part)
1170 _processpart(op, part)
1171 except (SystemExit, KeyboardInterrupt):
1171 except (SystemExit, KeyboardInterrupt):
1172 hardabort = True
1172 hardabort = True
1173 raise
1173 raise
1174 finally:
1174 finally:
1175 if not hardabort:
1175 if not hardabort:
1176 part.consume()
1176 part.consume()
1177 self.ui.debug('bundle2-input-stream-interrupt:'
1177 self.ui.debug('bundle2-input-stream-interrupt:'
1178 ' closing out of band context\n')
1178 ' closing out of band context\n')
1179
1179
1180 class interruptoperation(object):
1180 class interruptoperation(object):
1181 """A limited operation to be use by part handler during interruption
1181 """A limited operation to be use by part handler during interruption
1182
1182
1183 It only have access to an ui object.
1183 It only have access to an ui object.
1184 """
1184 """
1185
1185
1186 def __init__(self, ui):
1186 def __init__(self, ui):
1187 self.ui = ui
1187 self.ui = ui
1188 self.reply = None
1188 self.reply = None
1189 self.captureoutput = False
1189 self.captureoutput = False
1190
1190
1191 @property
1191 @property
1192 def repo(self):
1192 def repo(self):
1193 raise error.ProgrammingError('no repo access from stream interruption')
1193 raise error.ProgrammingError('no repo access from stream interruption')
1194
1194
1195 def gettransaction(self):
1195 def gettransaction(self):
1196 raise TransactionUnavailable('no repo access from stream interruption')
1196 raise TransactionUnavailable('no repo access from stream interruption')
1197
1197
1198 def decodepayloadchunks(ui, fh):
1198 def decodepayloadchunks(ui, fh):
1199 """Reads bundle2 part payload data into chunks.
1199 """Reads bundle2 part payload data into chunks.
1200
1200
1201 Part payload data consists of framed chunks. This function takes
1201 Part payload data consists of framed chunks. This function takes
1202 a file handle and emits those chunks.
1202 a file handle and emits those chunks.
1203 """
1203 """
1204 dolog = ui.configbool('devel', 'bundle2.debug')
1204 dolog = ui.configbool('devel', 'bundle2.debug')
1205 debug = ui.debug
1205 debug = ui.debug
1206
1206
1207 headerstruct = struct.Struct(_fpayloadsize)
1207 headerstruct = struct.Struct(_fpayloadsize)
1208 headersize = headerstruct.size
1208 headersize = headerstruct.size
1209 unpack = headerstruct.unpack
1209 unpack = headerstruct.unpack
1210
1210
1211 readexactly = changegroup.readexactly
1211 readexactly = changegroup.readexactly
1212 read = fh.read
1212 read = fh.read
1213
1213
1214 chunksize = unpack(readexactly(fh, headersize))[0]
1214 chunksize = unpack(readexactly(fh, headersize))[0]
1215 indebug(ui, 'payload chunk size: %i' % chunksize)
1215 indebug(ui, 'payload chunk size: %i' % chunksize)
1216
1216
1217 # changegroup.readexactly() is inlined below for performance.
1217 # changegroup.readexactly() is inlined below for performance.
1218 while chunksize:
1218 while chunksize:
1219 if chunksize >= 0:
1219 if chunksize >= 0:
1220 s = read(chunksize)
1220 s = read(chunksize)
1221 if len(s) < chunksize:
1221 if len(s) < chunksize:
1222 raise error.Abort(_('stream ended unexpectedly '
1222 raise error.Abort(_('stream ended unexpectedly '
1223 ' (got %d bytes, expected %d)') %
1223 ' (got %d bytes, expected %d)') %
1224 (len(s), chunksize))
1224 (len(s), chunksize))
1225
1225
1226 yield s
1226 yield s
1227 elif chunksize == flaginterrupt:
1227 elif chunksize == flaginterrupt:
1228 # Interrupt "signal" detected. The regular stream is interrupted
1228 # Interrupt "signal" detected. The regular stream is interrupted
1229 # and a bundle2 part follows. Consume it.
1229 # and a bundle2 part follows. Consume it.
1230 interrupthandler(ui, fh)()
1230 interrupthandler(ui, fh)()
1231 else:
1231 else:
1232 raise error.BundleValueError(
1232 raise error.BundleValueError(
1233 'negative payload chunk size: %s' % chunksize)
1233 'negative payload chunk size: %s' % chunksize)
1234
1234
1235 s = read(headersize)
1235 s = read(headersize)
1236 if len(s) < headersize:
1236 if len(s) < headersize:
1237 raise error.Abort(_('stream ended unexpectedly '
1237 raise error.Abort(_('stream ended unexpectedly '
1238 ' (got %d bytes, expected %d)') %
1238 ' (got %d bytes, expected %d)') %
1239 (len(s), chunksize))
1239 (len(s), chunksize))
1240
1240
1241 chunksize = unpack(s)[0]
1241 chunksize = unpack(s)[0]
1242
1242
1243 # indebug() inlined for performance.
1243 # indebug() inlined for performance.
1244 if dolog:
1244 if dolog:
1245 debug('bundle2-input: payload chunk size: %i\n' % chunksize)
1245 debug('bundle2-input: payload chunk size: %i\n' % chunksize)
1246
1246
1247 class unbundlepart(unpackermixin):
1247 class unbundlepart(unpackermixin):
1248 """a bundle part read from a bundle"""
1248 """a bundle part read from a bundle"""
1249
1249
1250 def __init__(self, ui, header, fp):
1250 def __init__(self, ui, header, fp):
1251 super(unbundlepart, self).__init__(fp)
1251 super(unbundlepart, self).__init__(fp)
1252 self._seekable = (util.safehasattr(fp, 'seek') and
1252 self._seekable = (util.safehasattr(fp, 'seek') and
1253 util.safehasattr(fp, 'tell'))
1253 util.safehasattr(fp, 'tell'))
1254 self.ui = ui
1254 self.ui = ui
1255 # unbundle state attr
1255 # unbundle state attr
1256 self._headerdata = header
1256 self._headerdata = header
1257 self._headeroffset = 0
1257 self._headeroffset = 0
1258 self._initialized = False
1258 self._initialized = False
1259 self.consumed = False
1259 self.consumed = False
1260 # part data
1260 # part data
1261 self.id = None
1261 self.id = None
1262 self.type = None
1262 self.type = None
1263 self.mandatoryparams = None
1263 self.mandatoryparams = None
1264 self.advisoryparams = None
1264 self.advisoryparams = None
1265 self.params = None
1265 self.params = None
1266 self.mandatorykeys = ()
1266 self.mandatorykeys = ()
1267 self._readheader()
1267 self._readheader()
1268 self._mandatory = None
1268 self._mandatory = None
1269 self._pos = 0
1269 self._pos = 0
1270
1270
1271 def _fromheader(self, size):
1271 def _fromheader(self, size):
1272 """return the next <size> byte from the header"""
1272 """return the next <size> byte from the header"""
1273 offset = self._headeroffset
1273 offset = self._headeroffset
1274 data = self._headerdata[offset:(offset + size)]
1274 data = self._headerdata[offset:(offset + size)]
1275 self._headeroffset = offset + size
1275 self._headeroffset = offset + size
1276 return data
1276 return data
1277
1277
1278 def _unpackheader(self, format):
1278 def _unpackheader(self, format):
1279 """read given format from header
1279 """read given format from header
1280
1280
1281 This automatically compute the size of the format to read."""
1281 This automatically compute the size of the format to read."""
1282 data = self._fromheader(struct.calcsize(format))
1282 data = self._fromheader(struct.calcsize(format))
1283 return _unpack(format, data)
1283 return _unpack(format, data)
1284
1284
1285 def _initparams(self, mandatoryparams, advisoryparams):
1285 def _initparams(self, mandatoryparams, advisoryparams):
1286 """internal function to setup all logic related parameters"""
1286 """internal function to setup all logic related parameters"""
1287 # make it read only to prevent people touching it by mistake.
1287 # make it read only to prevent people touching it by mistake.
1288 self.mandatoryparams = tuple(mandatoryparams)
1288 self.mandatoryparams = tuple(mandatoryparams)
1289 self.advisoryparams = tuple(advisoryparams)
1289 self.advisoryparams = tuple(advisoryparams)
1290 # user friendly UI
1290 # user friendly UI
1291 self.params = util.sortdict(self.mandatoryparams)
1291 self.params = util.sortdict(self.mandatoryparams)
1292 self.params.update(self.advisoryparams)
1292 self.params.update(self.advisoryparams)
1293 self.mandatorykeys = frozenset(p[0] for p in mandatoryparams)
1293 self.mandatorykeys = frozenset(p[0] for p in mandatoryparams)
1294
1294
1295 def _readheader(self):
1295 def _readheader(self):
1296 """read the header and setup the object"""
1296 """read the header and setup the object"""
1297 typesize = self._unpackheader(_fparttypesize)[0]
1297 typesize = self._unpackheader(_fparttypesize)[0]
1298 self.type = self._fromheader(typesize)
1298 self.type = self._fromheader(typesize)
1299 indebug(self.ui, 'part type: "%s"' % self.type)
1299 indebug(self.ui, 'part type: "%s"' % self.type)
1300 self.id = self._unpackheader(_fpartid)[0]
1300 self.id = self._unpackheader(_fpartid)[0]
1301 indebug(self.ui, 'part id: "%s"' % pycompat.bytestr(self.id))
1301 indebug(self.ui, 'part id: "%s"' % pycompat.bytestr(self.id))
1302 # extract mandatory bit from type
1302 # extract mandatory bit from type
1303 self.mandatory = (self.type != self.type.lower())
1303 self.mandatory = (self.type != self.type.lower())
1304 self.type = self.type.lower()
1304 self.type = self.type.lower()
1305 ## reading parameters
1305 ## reading parameters
1306 # param count
1306 # param count
1307 mancount, advcount = self._unpackheader(_fpartparamcount)
1307 mancount, advcount = self._unpackheader(_fpartparamcount)
1308 indebug(self.ui, 'part parameters: %i' % (mancount + advcount))
1308 indebug(self.ui, 'part parameters: %i' % (mancount + advcount))
1309 # param size
1309 # param size
1310 fparamsizes = _makefpartparamsizes(mancount + advcount)
1310 fparamsizes = _makefpartparamsizes(mancount + advcount)
1311 paramsizes = self._unpackheader(fparamsizes)
1311 paramsizes = self._unpackheader(fparamsizes)
1312 # make it a list of couple again
1312 # make it a list of couple again
1313 paramsizes = list(zip(paramsizes[::2], paramsizes[1::2]))
1313 paramsizes = list(zip(paramsizes[::2], paramsizes[1::2]))
1314 # split mandatory from advisory
1314 # split mandatory from advisory
1315 mansizes = paramsizes[:mancount]
1315 mansizes = paramsizes[:mancount]
1316 advsizes = paramsizes[mancount:]
1316 advsizes = paramsizes[mancount:]
1317 # retrieve param value
1317 # retrieve param value
1318 manparams = []
1318 manparams = []
1319 for key, value in mansizes:
1319 for key, value in mansizes:
1320 manparams.append((self._fromheader(key), self._fromheader(value)))
1320 manparams.append((self._fromheader(key), self._fromheader(value)))
1321 advparams = []
1321 advparams = []
1322 for key, value in advsizes:
1322 for key, value in advsizes:
1323 advparams.append((self._fromheader(key), self._fromheader(value)))
1323 advparams.append((self._fromheader(key), self._fromheader(value)))
1324 self._initparams(manparams, advparams)
1324 self._initparams(manparams, advparams)
1325 ## part payload
1325 ## part payload
1326 self._payloadstream = util.chunkbuffer(self._payloadchunks())
1326 self._payloadstream = util.chunkbuffer(self._payloadchunks())
1327 # we read the data, tell it
1327 # we read the data, tell it
1328 self._initialized = True
1328 self._initialized = True
1329
1329
1330 def _payloadchunks(self):
1330 def _payloadchunks(self):
1331 """Generator of decoded chunks in the payload."""
1331 """Generator of decoded chunks in the payload."""
1332 return decodepayloadchunks(self.ui, self._fp)
1332 return decodepayloadchunks(self.ui, self._fp)
1333
1333
1334 def consume(self):
1334 def consume(self):
1335 """Read the part payload until completion.
1335 """Read the part payload until completion.
1336
1336
1337 By consuming the part data, the underlying stream read offset will
1337 By consuming the part data, the underlying stream read offset will
1338 be advanced to the next part (or end of stream).
1338 be advanced to the next part (or end of stream).
1339 """
1339 """
1340 if self.consumed:
1340 if self.consumed:
1341 return
1341 return
1342
1342
1343 chunk = self.read(32768)
1343 chunk = self.read(32768)
1344 while chunk:
1344 while chunk:
1345 self._pos += len(chunk)
1345 self._pos += len(chunk)
1346 chunk = self.read(32768)
1346 chunk = self.read(32768)
1347
1347
1348 def read(self, size=None):
1348 def read(self, size=None):
1349 """read payload data"""
1349 """read payload data"""
1350 if not self._initialized:
1350 if not self._initialized:
1351 self._readheader()
1351 self._readheader()
1352 if size is None:
1352 if size is None:
1353 data = self._payloadstream.read()
1353 data = self._payloadstream.read()
1354 else:
1354 else:
1355 data = self._payloadstream.read(size)
1355 data = self._payloadstream.read(size)
1356 self._pos += len(data)
1356 self._pos += len(data)
1357 if size is None or len(data) < size:
1357 if size is None or len(data) < size:
1358 if not self.consumed and self._pos:
1358 if not self.consumed and self._pos:
1359 self.ui.debug('bundle2-input-part: total payload size %i\n'
1359 self.ui.debug('bundle2-input-part: total payload size %i\n'
1360 % self._pos)
1360 % self._pos)
1361 self.consumed = True
1361 self.consumed = True
1362 return data
1362 return data
1363
1363
1364 class seekableunbundlepart(unbundlepart):
1364 class seekableunbundlepart(unbundlepart):
1365 """A bundle2 part in a bundle that is seekable.
1365 """A bundle2 part in a bundle that is seekable.
1366
1366
1367 Regular ``unbundlepart`` instances can only be read once. This class
1367 Regular ``unbundlepart`` instances can only be read once. This class
1368 extends ``unbundlepart`` to enable bi-directional seeking within the
1368 extends ``unbundlepart`` to enable bi-directional seeking within the
1369 part.
1369 part.
1370
1370
1371 Bundle2 part data consists of framed chunks. Offsets when seeking
1371 Bundle2 part data consists of framed chunks. Offsets when seeking
1372 refer to the decoded data, not the offsets in the underlying bundle2
1372 refer to the decoded data, not the offsets in the underlying bundle2
1373 stream.
1373 stream.
1374
1374
1375 To facilitate quickly seeking within the decoded data, instances of this
1375 To facilitate quickly seeking within the decoded data, instances of this
1376 class maintain a mapping between offsets in the underlying stream and
1376 class maintain a mapping between offsets in the underlying stream and
1377 the decoded payload. This mapping will consume memory in proportion
1377 the decoded payload. This mapping will consume memory in proportion
1378 to the number of chunks within the payload (which almost certainly
1378 to the number of chunks within the payload (which almost certainly
1379 increases in proportion with the size of the part).
1379 increases in proportion with the size of the part).
1380 """
1380 """
1381 def __init__(self, ui, header, fp):
1381 def __init__(self, ui, header, fp):
1382 # (payload, file) offsets for chunk starts.
1382 # (payload, file) offsets for chunk starts.
1383 self._chunkindex = []
1383 self._chunkindex = []
1384
1384
1385 super(seekableunbundlepart, self).__init__(ui, header, fp)
1385 super(seekableunbundlepart, self).__init__(ui, header, fp)
1386
1386
1387 def _payloadchunks(self, chunknum=0):
1387 def _payloadchunks(self, chunknum=0):
1388 '''seek to specified chunk and start yielding data'''
1388 '''seek to specified chunk and start yielding data'''
1389 if len(self._chunkindex) == 0:
1389 if len(self._chunkindex) == 0:
1390 assert chunknum == 0, 'Must start with chunk 0'
1390 assert chunknum == 0, 'Must start with chunk 0'
1391 self._chunkindex.append((0, self._tellfp()))
1391 self._chunkindex.append((0, self._tellfp()))
1392 else:
1392 else:
1393 assert chunknum < len(self._chunkindex), \
1393 assert chunknum < len(self._chunkindex), \
1394 'Unknown chunk %d' % chunknum
1394 'Unknown chunk %d' % chunknum
1395 self._seekfp(self._chunkindex[chunknum][1])
1395 self._seekfp(self._chunkindex[chunknum][1])
1396
1396
1397 pos = self._chunkindex[chunknum][0]
1397 pos = self._chunkindex[chunknum][0]
1398
1398
1399 for chunk in decodepayloadchunks(self.ui, self._fp):
1399 for chunk in decodepayloadchunks(self.ui, self._fp):
1400 chunknum += 1
1400 chunknum += 1
1401 pos += len(chunk)
1401 pos += len(chunk)
1402 if chunknum == len(self._chunkindex):
1402 if chunknum == len(self._chunkindex):
1403 self._chunkindex.append((pos, self._tellfp()))
1403 self._chunkindex.append((pos, self._tellfp()))
1404
1404
1405 yield chunk
1405 yield chunk
1406
1406
1407 def _findchunk(self, pos):
1407 def _findchunk(self, pos):
1408 '''for a given payload position, return a chunk number and offset'''
1408 '''for a given payload position, return a chunk number and offset'''
1409 for chunk, (ppos, fpos) in enumerate(self._chunkindex):
1409 for chunk, (ppos, fpos) in enumerate(self._chunkindex):
1410 if ppos == pos:
1410 if ppos == pos:
1411 return chunk, 0
1411 return chunk, 0
1412 elif ppos > pos:
1412 elif ppos > pos:
1413 return chunk - 1, pos - self._chunkindex[chunk - 1][0]
1413 return chunk - 1, pos - self._chunkindex[chunk - 1][0]
1414 raise ValueError('Unknown chunk')
1414 raise ValueError('Unknown chunk')
1415
1415
1416 def tell(self):
1416 def tell(self):
1417 return self._pos
1417 return self._pos
1418
1418
1419 def seek(self, offset, whence=os.SEEK_SET):
1419 def seek(self, offset, whence=os.SEEK_SET):
1420 if whence == os.SEEK_SET:
1420 if whence == os.SEEK_SET:
1421 newpos = offset
1421 newpos = offset
1422 elif whence == os.SEEK_CUR:
1422 elif whence == os.SEEK_CUR:
1423 newpos = self._pos + offset
1423 newpos = self._pos + offset
1424 elif whence == os.SEEK_END:
1424 elif whence == os.SEEK_END:
1425 if not self.consumed:
1425 if not self.consumed:
1426 # Can't use self.consume() here because it advances self._pos.
1426 # Can't use self.consume() here because it advances self._pos.
1427 chunk = self.read(32768)
1427 chunk = self.read(32768)
1428 while chunk:
1428 while chunk:
1429 chunk = self.read(32768)
1429 chunk = self.read(32768)
1430 newpos = self._chunkindex[-1][0] - offset
1430 newpos = self._chunkindex[-1][0] - offset
1431 else:
1431 else:
1432 raise ValueError('Unknown whence value: %r' % (whence,))
1432 raise ValueError('Unknown whence value: %r' % (whence,))
1433
1433
1434 if newpos > self._chunkindex[-1][0] and not self.consumed:
1434 if newpos > self._chunkindex[-1][0] and not self.consumed:
1435 # Can't use self.consume() here because it advances self._pos.
1435 # Can't use self.consume() here because it advances self._pos.
1436 chunk = self.read(32768)
1436 chunk = self.read(32768)
1437 while chunk:
1437 while chunk:
1438 chunk = self.read(32668)
1438 chunk = self.read(32668)
1439
1439
1440 if not 0 <= newpos <= self._chunkindex[-1][0]:
1440 if not 0 <= newpos <= self._chunkindex[-1][0]:
1441 raise ValueError('Offset out of range')
1441 raise ValueError('Offset out of range')
1442
1442
1443 if self._pos != newpos:
1443 if self._pos != newpos:
1444 chunk, internaloffset = self._findchunk(newpos)
1444 chunk, internaloffset = self._findchunk(newpos)
1445 self._payloadstream = util.chunkbuffer(self._payloadchunks(chunk))
1445 self._payloadstream = util.chunkbuffer(self._payloadchunks(chunk))
1446 adjust = self.read(internaloffset)
1446 adjust = self.read(internaloffset)
1447 if len(adjust) != internaloffset:
1447 if len(adjust) != internaloffset:
1448 raise error.Abort(_('Seek failed\n'))
1448 raise error.Abort(_('Seek failed\n'))
1449 self._pos = newpos
1449 self._pos = newpos
1450
1450
1451 def _seekfp(self, offset, whence=0):
1451 def _seekfp(self, offset, whence=0):
1452 """move the underlying file pointer
1452 """move the underlying file pointer
1453
1453
1454 This method is meant for internal usage by the bundle2 protocol only.
1454 This method is meant for internal usage by the bundle2 protocol only.
1455 They directly manipulate the low level stream including bundle2 level
1455 They directly manipulate the low level stream including bundle2 level
1456 instruction.
1456 instruction.
1457
1457
1458 Do not use it to implement higher-level logic or methods."""
1458 Do not use it to implement higher-level logic or methods."""
1459 if self._seekable:
1459 if self._seekable:
1460 return self._fp.seek(offset, whence)
1460 return self._fp.seek(offset, whence)
1461 else:
1461 else:
1462 raise NotImplementedError(_('File pointer is not seekable'))
1462 raise NotImplementedError(_('File pointer is not seekable'))
1463
1463
1464 def _tellfp(self):
1464 def _tellfp(self):
1465 """return the file offset, or None if file is not seekable
1465 """return the file offset, or None if file is not seekable
1466
1466
1467 This method is meant for internal usage by the bundle2 protocol only.
1467 This method is meant for internal usage by the bundle2 protocol only.
1468 They directly manipulate the low level stream including bundle2 level
1468 They directly manipulate the low level stream including bundle2 level
1469 instruction.
1469 instruction.
1470
1470
1471 Do not use it to implement higher-level logic or methods."""
1471 Do not use it to implement higher-level logic or methods."""
1472 if self._seekable:
1472 if self._seekable:
1473 try:
1473 try:
1474 return self._fp.tell()
1474 return self._fp.tell()
1475 except IOError as e:
1475 except IOError as e:
1476 if e.errno == errno.ESPIPE:
1476 if e.errno == errno.ESPIPE:
1477 self._seekable = False
1477 self._seekable = False
1478 else:
1478 else:
1479 raise
1479 raise
1480 return None
1480 return None
1481
1481
1482 # These are only the static capabilities.
1482 # These are only the static capabilities.
1483 # Check the 'getrepocaps' function for the rest.
1483 # Check the 'getrepocaps' function for the rest.
1484 capabilities = {'HG20': (),
1484 capabilities = {'HG20': (),
1485 'bookmarks': (),
1485 'bookmarks': (),
1486 'error': ('abort', 'unsupportedcontent', 'pushraced',
1486 'error': ('abort', 'unsupportedcontent', 'pushraced',
1487 'pushkey'),
1487 'pushkey'),
1488 'listkeys': (),
1488 'listkeys': (),
1489 'pushkey': (),
1489 'pushkey': (),
1490 'digests': tuple(sorted(util.DIGESTS.keys())),
1490 'digests': tuple(sorted(util.DIGESTS.keys())),
1491 'remote-changegroup': ('http', 'https'),
1491 'remote-changegroup': ('http', 'https'),
1492 'hgtagsfnodes': (),
1492 'hgtagsfnodes': (),
1493 'phases': ('heads',),
1493 'phases': ('heads',),
1494 'stream': ('v2',),
1494 'stream': ('v2',),
1495 }
1495 }
1496
1496
1497 def getrepocaps(repo, allowpushback=False, role=None):
1497 def getrepocaps(repo, allowpushback=False, role=None):
1498 """return the bundle2 capabilities for a given repo
1498 """return the bundle2 capabilities for a given repo
1499
1499
1500 Exists to allow extensions (like evolution) to mutate the capabilities.
1500 Exists to allow extensions (like evolution) to mutate the capabilities.
1501
1501
1502 The returned value is used for servers advertising their capabilities as
1502 The returned value is used for servers advertising their capabilities as
1503 well as clients advertising their capabilities to servers as part of
1503 well as clients advertising their capabilities to servers as part of
1504 bundle2 requests. The ``role`` argument specifies which is which.
1504 bundle2 requests. The ``role`` argument specifies which is which.
1505 """
1505 """
1506 if role not in ('client', 'server'):
1506 if role not in ('client', 'server'):
1507 raise error.ProgrammingError('role argument must be client or server')
1507 raise error.ProgrammingError('role argument must be client or server')
1508
1508
1509 caps = capabilities.copy()
1509 caps = capabilities.copy()
1510 caps['changegroup'] = tuple(sorted(
1510 caps['changegroup'] = tuple(sorted(
1511 changegroup.supportedincomingversions(repo)))
1511 changegroup.supportedincomingversions(repo)))
1512 if obsolete.isenabled(repo, obsolete.exchangeopt):
1512 if obsolete.isenabled(repo, obsolete.exchangeopt):
1513 supportedformat = tuple('V%i' % v for v in obsolete.formats)
1513 supportedformat = tuple('V%i' % v for v in obsolete.formats)
1514 caps['obsmarkers'] = supportedformat
1514 caps['obsmarkers'] = supportedformat
1515 if allowpushback:
1515 if allowpushback:
1516 caps['pushback'] = ()
1516 caps['pushback'] = ()
1517 cpmode = repo.ui.config('server', 'concurrent-push-mode')
1517 cpmode = repo.ui.config('server', 'concurrent-push-mode')
1518 if cpmode == 'check-related':
1518 if cpmode == 'check-related':
1519 caps['checkheads'] = ('related',)
1519 caps['checkheads'] = ('related',)
1520 if 'phases' in repo.ui.configlist('devel', 'legacy.exchange'):
1520 if 'phases' in repo.ui.configlist('devel', 'legacy.exchange'):
1521 caps.pop('phases')
1521 caps.pop('phases')
1522
1522
1523 # Don't advertise stream clone support in server mode if not configured.
1523 # Don't advertise stream clone support in server mode if not configured.
1524 if role == 'server':
1524 if role == 'server':
1525 streamsupported = repo.ui.configbool('server', 'uncompressed',
1525 streamsupported = repo.ui.configbool('server', 'uncompressed',
1526 untrusted=True)
1526 untrusted=True)
1527 featuresupported = repo.ui.configbool('experimental', 'bundle2.stream')
1527 featuresupported = repo.ui.configbool('experimental', 'bundle2.stream')
1528
1528
1529 if not streamsupported or not featuresupported:
1529 if not streamsupported or not featuresupported:
1530 caps.pop('stream')
1530 caps.pop('stream')
1531 # Else always advertise support on client, because payload support
1531 # Else always advertise support on client, because payload support
1532 # should always be advertised.
1532 # should always be advertised.
1533
1533
1534 return caps
1534 return caps
1535
1535
1536 def bundle2caps(remote):
1536 def bundle2caps(remote):
1537 """return the bundle capabilities of a peer as dict"""
1537 """return the bundle capabilities of a peer as dict"""
1538 raw = remote.capable('bundle2')
1538 raw = remote.capable('bundle2')
1539 if not raw and raw != '':
1539 if not raw and raw != '':
1540 return {}
1540 return {}
1541 capsblob = urlreq.unquote(remote.capable('bundle2'))
1541 capsblob = urlreq.unquote(remote.capable('bundle2'))
1542 return decodecaps(capsblob)
1542 return decodecaps(capsblob)
1543
1543
1544 def obsmarkersversion(caps):
1544 def obsmarkersversion(caps):
1545 """extract the list of supported obsmarkers versions from a bundle2caps dict
1545 """extract the list of supported obsmarkers versions from a bundle2caps dict
1546 """
1546 """
1547 obscaps = caps.get('obsmarkers', ())
1547 obscaps = caps.get('obsmarkers', ())
1548 return [int(c[1:]) for c in obscaps if c.startswith('V')]
1548 return [int(c[1:]) for c in obscaps if c.startswith('V')]
1549
1549
1550 def writenewbundle(ui, repo, source, filename, bundletype, outgoing, opts,
1550 def writenewbundle(ui, repo, source, filename, bundletype, outgoing, opts,
1551 vfs=None, compression=None, compopts=None):
1551 vfs=None, compression=None, compopts=None):
1552 if bundletype.startswith('HG10'):
1552 if bundletype.startswith('HG10'):
1553 cg = changegroup.makechangegroup(repo, outgoing, '01', source)
1553 cg = changegroup.makechangegroup(repo, outgoing, '01', source)
1554 return writebundle(ui, cg, filename, bundletype, vfs=vfs,
1554 return writebundle(ui, cg, filename, bundletype, vfs=vfs,
1555 compression=compression, compopts=compopts)
1555 compression=compression, compopts=compopts)
1556 elif not bundletype.startswith('HG20'):
1556 elif not bundletype.startswith('HG20'):
1557 raise error.ProgrammingError('unknown bundle type: %s' % bundletype)
1557 raise error.ProgrammingError('unknown bundle type: %s' % bundletype)
1558
1558
1559 caps = {}
1559 caps = {}
1560 if 'obsolescence' in opts:
1560 if 'obsolescence' in opts:
1561 caps['obsmarkers'] = ('V1',)
1561 caps['obsmarkers'] = ('V1',)
1562 bundle = bundle20(ui, caps)
1562 bundle = bundle20(ui, caps)
1563 bundle.setcompression(compression, compopts)
1563 bundle.setcompression(compression, compopts)
1564 _addpartsfromopts(ui, repo, bundle, source, outgoing, opts)
1564 _addpartsfromopts(ui, repo, bundle, source, outgoing, opts)
1565 chunkiter = bundle.getchunks()
1565 chunkiter = bundle.getchunks()
1566
1566
1567 return changegroup.writechunks(ui, chunkiter, filename, vfs=vfs)
1567 return changegroup.writechunks(ui, chunkiter, filename, vfs=vfs)
1568
1568
1569 def _addpartsfromopts(ui, repo, bundler, source, outgoing, opts):
1569 def _addpartsfromopts(ui, repo, bundler, source, outgoing, opts):
1570 # We should eventually reconcile this logic with the one behind
1570 # We should eventually reconcile this logic with the one behind
1571 # 'exchange.getbundle2partsgenerator'.
1571 # 'exchange.getbundle2partsgenerator'.
1572 #
1572 #
1573 # The type of input from 'getbundle' and 'writenewbundle' are a bit
1573 # The type of input from 'getbundle' and 'writenewbundle' are a bit
1574 # different right now. So we keep them separated for now for the sake of
1574 # different right now. So we keep them separated for now for the sake of
1575 # simplicity.
1575 # simplicity.
1576
1576
1577 # we always want a changegroup in such bundle
1577 # we always want a changegroup in such bundle
1578 cgversion = opts.get('cg.version')
1578 cgversion = opts.get('cg.version')
1579 if cgversion is None:
1579 if cgversion is None:
1580 cgversion = changegroup.safeversion(repo)
1580 cgversion = changegroup.safeversion(repo)
1581 cg = changegroup.makechangegroup(repo, outgoing, cgversion, source)
1581 cg = changegroup.makechangegroup(repo, outgoing, cgversion, source)
1582 part = bundler.newpart('changegroup', data=cg.getchunks())
1582 part = bundler.newpart('changegroup', data=cg.getchunks())
1583 part.addparam('version', cg.version)
1583 part.addparam('version', cg.version)
1584 if 'clcount' in cg.extras:
1584 if 'clcount' in cg.extras:
1585 part.addparam('nbchanges', '%d' % cg.extras['clcount'],
1585 part.addparam('nbchanges', '%d' % cg.extras['clcount'],
1586 mandatory=False)
1586 mandatory=False)
1587 if opts.get('phases') and repo.revs('%ln and secret()',
1587 if opts.get('phases') and repo.revs('%ln and secret()',
1588 outgoing.missingheads):
1588 outgoing.missingheads):
1589 part.addparam('targetphase', '%d' % phases.secret, mandatory=False)
1589 part.addparam('targetphase', '%d' % phases.secret, mandatory=False)
1590
1590
1591 addparttagsfnodescache(repo, bundler, outgoing)
1591 addparttagsfnodescache(repo, bundler, outgoing)
1592
1592
1593 if opts.get('obsolescence', False):
1593 if opts.get('obsolescence', False):
1594 obsmarkers = repo.obsstore.relevantmarkers(outgoing.missing)
1594 obsmarkers = repo.obsstore.relevantmarkers(outgoing.missing)
1595 buildobsmarkerspart(bundler, obsmarkers)
1595 buildobsmarkerspart(bundler, obsmarkers)
1596
1596
1597 if opts.get('phases', False):
1597 if opts.get('phases', False):
1598 headsbyphase = phases.subsetphaseheads(repo, outgoing.missing)
1598 headsbyphase = phases.subsetphaseheads(repo, outgoing.missing)
1599 phasedata = phases.binaryencode(headsbyphase)
1599 phasedata = phases.binaryencode(headsbyphase)
1600 bundler.newpart('phase-heads', data=phasedata)
1600 bundler.newpart('phase-heads', data=phasedata)
1601
1601
1602 def addparttagsfnodescache(repo, bundler, outgoing):
1602 def addparttagsfnodescache(repo, bundler, outgoing):
1603 # we include the tags fnode cache for the bundle changeset
1603 # we include the tags fnode cache for the bundle changeset
1604 # (as an optional parts)
1604 # (as an optional parts)
1605 cache = tags.hgtagsfnodescache(repo.unfiltered())
1605 cache = tags.hgtagsfnodescache(repo.unfiltered())
1606 chunks = []
1606 chunks = []
1607
1607
1608 # .hgtags fnodes are only relevant for head changesets. While we could
1608 # .hgtags fnodes are only relevant for head changesets. While we could
1609 # transfer values for all known nodes, there will likely be little to
1609 # transfer values for all known nodes, there will likely be little to
1610 # no benefit.
1610 # no benefit.
1611 #
1611 #
1612 # We don't bother using a generator to produce output data because
1612 # We don't bother using a generator to produce output data because
1613 # a) we only have 40 bytes per head and even esoteric numbers of heads
1613 # a) we only have 40 bytes per head and even esoteric numbers of heads
1614 # consume little memory (1M heads is 40MB) b) we don't want to send the
1614 # consume little memory (1M heads is 40MB) b) we don't want to send the
1615 # part if we don't have entries and knowing if we have entries requires
1615 # part if we don't have entries and knowing if we have entries requires
1616 # cache lookups.
1616 # cache lookups.
1617 for node in outgoing.missingheads:
1617 for node in outgoing.missingheads:
1618 # Don't compute missing, as this may slow down serving.
1618 # Don't compute missing, as this may slow down serving.
1619 fnode = cache.getfnode(node, computemissing=False)
1619 fnode = cache.getfnode(node, computemissing=False)
1620 if fnode is not None:
1620 if fnode is not None:
1621 chunks.extend([node, fnode])
1621 chunks.extend([node, fnode])
1622
1622
1623 if chunks:
1623 if chunks:
1624 bundler.newpart('hgtagsfnodes', data=''.join(chunks))
1624 bundler.newpart('hgtagsfnodes', data=''.join(chunks))
1625
1625
1626 def buildobsmarkerspart(bundler, markers):
1626 def buildobsmarkerspart(bundler, markers):
1627 """add an obsmarker part to the bundler with <markers>
1627 """add an obsmarker part to the bundler with <markers>
1628
1628
1629 No part is created if markers is empty.
1629 No part is created if markers is empty.
1630 Raises ValueError if the bundler doesn't support any known obsmarker format.
1630 Raises ValueError if the bundler doesn't support any known obsmarker format.
1631 """
1631 """
1632 if not markers:
1632 if not markers:
1633 return None
1633 return None
1634
1634
1635 remoteversions = obsmarkersversion(bundler.capabilities)
1635 remoteversions = obsmarkersversion(bundler.capabilities)
1636 version = obsolete.commonversion(remoteversions)
1636 version = obsolete.commonversion(remoteversions)
1637 if version is None:
1637 if version is None:
1638 raise ValueError('bundler does not support common obsmarker format')
1638 raise ValueError('bundler does not support common obsmarker format')
1639 stream = obsolete.encodemarkers(markers, True, version=version)
1639 stream = obsolete.encodemarkers(markers, True, version=version)
1640 return bundler.newpart('obsmarkers', data=stream)
1640 return bundler.newpart('obsmarkers', data=stream)
1641
1641
1642 def writebundle(ui, cg, filename, bundletype, vfs=None, compression=None,
1642 def writebundle(ui, cg, filename, bundletype, vfs=None, compression=None,
1643 compopts=None):
1643 compopts=None):
1644 """Write a bundle file and return its filename.
1644 """Write a bundle file and return its filename.
1645
1645
1646 Existing files will not be overwritten.
1646 Existing files will not be overwritten.
1647 If no filename is specified, a temporary file is created.
1647 If no filename is specified, a temporary file is created.
1648 bz2 compression can be turned off.
1648 bz2 compression can be turned off.
1649 The bundle file will be deleted in case of errors.
1649 The bundle file will be deleted in case of errors.
1650 """
1650 """
1651
1651
1652 if bundletype == "HG20":
1652 if bundletype == "HG20":
1653 bundle = bundle20(ui)
1653 bundle = bundle20(ui)
1654 bundle.setcompression(compression, compopts)
1654 bundle.setcompression(compression, compopts)
1655 part = bundle.newpart('changegroup', data=cg.getchunks())
1655 part = bundle.newpart('changegroup', data=cg.getchunks())
1656 part.addparam('version', cg.version)
1656 part.addparam('version', cg.version)
1657 if 'clcount' in cg.extras:
1657 if 'clcount' in cg.extras:
1658 part.addparam('nbchanges', '%d' % cg.extras['clcount'],
1658 part.addparam('nbchanges', '%d' % cg.extras['clcount'],
1659 mandatory=False)
1659 mandatory=False)
1660 chunkiter = bundle.getchunks()
1660 chunkiter = bundle.getchunks()
1661 else:
1661 else:
1662 # compression argument is only for the bundle2 case
1662 # compression argument is only for the bundle2 case
1663 assert compression is None
1663 assert compression is None
1664 if cg.version != '01':
1664 if cg.version != '01':
1665 raise error.Abort(_('old bundle types only supports v1 '
1665 raise error.Abort(_('old bundle types only supports v1 '
1666 'changegroups'))
1666 'changegroups'))
1667 header, comp = bundletypes[bundletype]
1667 header, comp = bundletypes[bundletype]
1668 if comp not in util.compengines.supportedbundletypes:
1668 if comp not in util.compengines.supportedbundletypes:
1669 raise error.Abort(_('unknown stream compression type: %s')
1669 raise error.Abort(_('unknown stream compression type: %s')
1670 % comp)
1670 % comp)
1671 compengine = util.compengines.forbundletype(comp)
1671 compengine = util.compengines.forbundletype(comp)
1672 def chunkiter():
1672 def chunkiter():
1673 yield header
1673 yield header
1674 for chunk in compengine.compressstream(cg.getchunks(), compopts):
1674 for chunk in compengine.compressstream(cg.getchunks(), compopts):
1675 yield chunk
1675 yield chunk
1676 chunkiter = chunkiter()
1676 chunkiter = chunkiter()
1677
1677
1678 # parse the changegroup data, otherwise we will block
1678 # parse the changegroup data, otherwise we will block
1679 # in case of sshrepo because we don't know the end of the stream
1679 # in case of sshrepo because we don't know the end of the stream
1680 return changegroup.writechunks(ui, chunkiter, filename, vfs=vfs)
1680 return changegroup.writechunks(ui, chunkiter, filename, vfs=vfs)
1681
1681
1682 def combinechangegroupresults(op):
1682 def combinechangegroupresults(op):
1683 """logic to combine 0 or more addchangegroup results into one"""
1683 """logic to combine 0 or more addchangegroup results into one"""
1684 results = [r.get('return', 0)
1684 results = [r.get('return', 0)
1685 for r in op.records['changegroup']]
1685 for r in op.records['changegroup']]
1686 changedheads = 0
1686 changedheads = 0
1687 result = 1
1687 result = 1
1688 for ret in results:
1688 for ret in results:
1689 # If any changegroup result is 0, return 0
1689 # If any changegroup result is 0, return 0
1690 if ret == 0:
1690 if ret == 0:
1691 result = 0
1691 result = 0
1692 break
1692 break
1693 if ret < -1:
1693 if ret < -1:
1694 changedheads += ret + 1
1694 changedheads += ret + 1
1695 elif ret > 1:
1695 elif ret > 1:
1696 changedheads += ret - 1
1696 changedheads += ret - 1
1697 if changedheads > 0:
1697 if changedheads > 0:
1698 result = 1 + changedheads
1698 result = 1 + changedheads
1699 elif changedheads < 0:
1699 elif changedheads < 0:
1700 result = -1 + changedheads
1700 result = -1 + changedheads
1701 return result
1701 return result
1702
1702
1703 @parthandler('changegroup', ('version', 'nbchanges', 'treemanifest',
1703 @parthandler('changegroup', ('version', 'nbchanges', 'treemanifest',
1704 'targetphase'))
1704 'targetphase'))
1705 def handlechangegroup(op, inpart):
1705 def handlechangegroup(op, inpart):
1706 """apply a changegroup part on the repo
1706 """apply a changegroup part on the repo
1707
1707
1708 This is a very early implementation that will massive rework before being
1708 This is a very early implementation that will massive rework before being
1709 inflicted to any end-user.
1709 inflicted to any end-user.
1710 """
1710 """
1711 tr = op.gettransaction()
1711 tr = op.gettransaction()
1712 unpackerversion = inpart.params.get('version', '01')
1712 unpackerversion = inpart.params.get('version', '01')
1713 # We should raise an appropriate exception here
1713 # We should raise an appropriate exception here
1714 cg = changegroup.getunbundler(unpackerversion, inpart, None)
1714 cg = changegroup.getunbundler(unpackerversion, inpart, None)
1715 # the source and url passed here are overwritten by the one contained in
1715 # the source and url passed here are overwritten by the one contained in
1716 # the transaction.hookargs argument. So 'bundle2' is a placeholder
1716 # the transaction.hookargs argument. So 'bundle2' is a placeholder
1717 nbchangesets = None
1717 nbchangesets = None
1718 if 'nbchanges' in inpart.params:
1718 if 'nbchanges' in inpart.params:
1719 nbchangesets = int(inpart.params.get('nbchanges'))
1719 nbchangesets = int(inpart.params.get('nbchanges'))
1720 if ('treemanifest' in inpart.params and
1720 if ('treemanifest' in inpart.params and
1721 'treemanifest' not in op.repo.requirements):
1721 'treemanifest' not in op.repo.requirements):
1722 if len(op.repo.changelog) != 0:
1722 if len(op.repo.changelog) != 0:
1723 raise error.Abort(_(
1723 raise error.Abort(_(
1724 "bundle contains tree manifests, but local repo is "
1724 "bundle contains tree manifests, but local repo is "
1725 "non-empty and does not use tree manifests"))
1725 "non-empty and does not use tree manifests"))
1726 op.repo.requirements.add('treemanifest')
1726 op.repo.requirements.add('treemanifest')
1727 op.repo._applyopenerreqs()
1727 op.repo._applyopenerreqs()
1728 op.repo._writerequirements()
1728 op.repo._writerequirements()
1729 extrakwargs = {}
1729 extrakwargs = {}
1730 targetphase = inpart.params.get('targetphase')
1730 targetphase = inpart.params.get('targetphase')
1731 if targetphase is not None:
1731 if targetphase is not None:
1732 extrakwargs['targetphase'] = int(targetphase)
1732 extrakwargs['targetphase'] = int(targetphase)
1733 ret = _processchangegroup(op, cg, tr, 'bundle2', 'bundle2',
1733 ret = _processchangegroup(op, cg, tr, 'bundle2', 'bundle2',
1734 expectedtotal=nbchangesets, **extrakwargs)
1734 expectedtotal=nbchangesets, **extrakwargs)
1735 if op.reply is not None:
1735 if op.reply is not None:
1736 # This is definitely not the final form of this
1736 # This is definitely not the final form of this
1737 # return. But one need to start somewhere.
1737 # return. But one need to start somewhere.
1738 part = op.reply.newpart('reply:changegroup', mandatory=False)
1738 part = op.reply.newpart('reply:changegroup', mandatory=False)
1739 part.addparam(
1739 part.addparam(
1740 'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False)
1740 'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False)
1741 part.addparam('return', '%i' % ret, mandatory=False)
1741 part.addparam('return', '%i' % ret, mandatory=False)
1742 assert not inpart.read()
1742 assert not inpart.read()
1743
1743
1744 _remotechangegroupparams = tuple(['url', 'size', 'digests'] +
1744 _remotechangegroupparams = tuple(['url', 'size', 'digests'] +
1745 ['digest:%s' % k for k in util.DIGESTS.keys()])
1745 ['digest:%s' % k for k in util.DIGESTS.keys()])
1746 @parthandler('remote-changegroup', _remotechangegroupparams)
1746 @parthandler('remote-changegroup', _remotechangegroupparams)
1747 def handleremotechangegroup(op, inpart):
1747 def handleremotechangegroup(op, inpart):
1748 """apply a bundle10 on the repo, given an url and validation information
1748 """apply a bundle10 on the repo, given an url and validation information
1749
1749
1750 All the information about the remote bundle to import are given as
1750 All the information about the remote bundle to import are given as
1751 parameters. The parameters include:
1751 parameters. The parameters include:
1752 - url: the url to the bundle10.
1752 - url: the url to the bundle10.
1753 - size: the bundle10 file size. It is used to validate what was
1753 - size: the bundle10 file size. It is used to validate what was
1754 retrieved by the client matches the server knowledge about the bundle.
1754 retrieved by the client matches the server knowledge about the bundle.
1755 - digests: a space separated list of the digest types provided as
1755 - digests: a space separated list of the digest types provided as
1756 parameters.
1756 parameters.
1757 - digest:<digest-type>: the hexadecimal representation of the digest with
1757 - digest:<digest-type>: the hexadecimal representation of the digest with
1758 that name. Like the size, it is used to validate what was retrieved by
1758 that name. Like the size, it is used to validate what was retrieved by
1759 the client matches what the server knows about the bundle.
1759 the client matches what the server knows about the bundle.
1760
1760
1761 When multiple digest types are given, all of them are checked.
1761 When multiple digest types are given, all of them are checked.
1762 """
1762 """
1763 try:
1763 try:
1764 raw_url = inpart.params['url']
1764 raw_url = inpart.params['url']
1765 except KeyError:
1765 except KeyError:
1766 raise error.Abort(_('remote-changegroup: missing "%s" param') % 'url')
1766 raise error.Abort(_('remote-changegroup: missing "%s" param') % 'url')
1767 parsed_url = util.url(raw_url)
1767 parsed_url = util.url(raw_url)
1768 if parsed_url.scheme not in capabilities['remote-changegroup']:
1768 if parsed_url.scheme not in capabilities['remote-changegroup']:
1769 raise error.Abort(_('remote-changegroup does not support %s urls') %
1769 raise error.Abort(_('remote-changegroup does not support %s urls') %
1770 parsed_url.scheme)
1770 parsed_url.scheme)
1771
1771
1772 try:
1772 try:
1773 size = int(inpart.params['size'])
1773 size = int(inpart.params['size'])
1774 except ValueError:
1774 except ValueError:
1775 raise error.Abort(_('remote-changegroup: invalid value for param "%s"')
1775 raise error.Abort(_('remote-changegroup: invalid value for param "%s"')
1776 % 'size')
1776 % 'size')
1777 except KeyError:
1777 except KeyError:
1778 raise error.Abort(_('remote-changegroup: missing "%s" param') % 'size')
1778 raise error.Abort(_('remote-changegroup: missing "%s" param') % 'size')
1779
1779
1780 digests = {}
1780 digests = {}
1781 for typ in inpart.params.get('digests', '').split():
1781 for typ in inpart.params.get('digests', '').split():
1782 param = 'digest:%s' % typ
1782 param = 'digest:%s' % typ
1783 try:
1783 try:
1784 value = inpart.params[param]
1784 value = inpart.params[param]
1785 except KeyError:
1785 except KeyError:
1786 raise error.Abort(_('remote-changegroup: missing "%s" param') %
1786 raise error.Abort(_('remote-changegroup: missing "%s" param') %
1787 param)
1787 param)
1788 digests[typ] = value
1788 digests[typ] = value
1789
1789
1790 real_part = util.digestchecker(url.open(op.ui, raw_url), size, digests)
1790 real_part = util.digestchecker(url.open(op.ui, raw_url), size, digests)
1791
1791
1792 tr = op.gettransaction()
1792 tr = op.gettransaction()
1793 from . import exchange
1793 from . import exchange
1794 cg = exchange.readbundle(op.repo.ui, real_part, raw_url)
1794 cg = exchange.readbundle(op.repo.ui, real_part, raw_url)
1795 if not isinstance(cg, changegroup.cg1unpacker):
1795 if not isinstance(cg, changegroup.cg1unpacker):
1796 raise error.Abort(_('%s: not a bundle version 1.0') %
1796 raise error.Abort(_('%s: not a bundle version 1.0') %
1797 util.hidepassword(raw_url))
1797 util.hidepassword(raw_url))
1798 ret = _processchangegroup(op, cg, tr, 'bundle2', 'bundle2')
1798 ret = _processchangegroup(op, cg, tr, 'bundle2', 'bundle2')
1799 if op.reply is not None:
1799 if op.reply is not None:
1800 # This is definitely not the final form of this
1800 # This is definitely not the final form of this
1801 # return. But one need to start somewhere.
1801 # return. But one need to start somewhere.
1802 part = op.reply.newpart('reply:changegroup')
1802 part = op.reply.newpart('reply:changegroup')
1803 part.addparam(
1803 part.addparam(
1804 'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False)
1804 'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False)
1805 part.addparam('return', '%i' % ret, mandatory=False)
1805 part.addparam('return', '%i' % ret, mandatory=False)
1806 try:
1806 try:
1807 real_part.validate()
1807 real_part.validate()
1808 except error.Abort as e:
1808 except error.Abort as e:
1809 raise error.Abort(_('bundle at %s is corrupted:\n%s') %
1809 raise error.Abort(_('bundle at %s is corrupted:\n%s') %
1810 (util.hidepassword(raw_url), str(e)))
1810 (util.hidepassword(raw_url), str(e)))
1811 assert not inpart.read()
1811 assert not inpart.read()
1812
1812
1813 @parthandler('reply:changegroup', ('return', 'in-reply-to'))
1813 @parthandler('reply:changegroup', ('return', 'in-reply-to'))
1814 def handlereplychangegroup(op, inpart):
1814 def handlereplychangegroup(op, inpart):
1815 ret = int(inpart.params['return'])
1815 ret = int(inpart.params['return'])
1816 replyto = int(inpart.params['in-reply-to'])
1816 replyto = int(inpart.params['in-reply-to'])
1817 op.records.add('changegroup', {'return': ret}, replyto)
1817 op.records.add('changegroup', {'return': ret}, replyto)
1818
1818
1819 @parthandler('check:bookmarks')
1819 @parthandler('check:bookmarks')
1820 def handlecheckbookmarks(op, inpart):
1820 def handlecheckbookmarks(op, inpart):
1821 """check location of bookmarks
1821 """check location of bookmarks
1822
1822
1823 This part is to be used to detect push race regarding bookmark, it
1823 This part is to be used to detect push race regarding bookmark, it
1824 contains binary encoded (bookmark, node) tuple. If the local state does
1824 contains binary encoded (bookmark, node) tuple. If the local state does
1825 not marks the one in the part, a PushRaced exception is raised
1825 not marks the one in the part, a PushRaced exception is raised
1826 """
1826 """
1827 bookdata = bookmarks.binarydecode(inpart)
1827 bookdata = bookmarks.binarydecode(inpart)
1828
1828
1829 msgstandard = ('repository changed while pushing - please try again '
1829 msgstandard = ('repository changed while pushing - please try again '
1830 '(bookmark "%s" move from %s to %s)')
1830 '(bookmark "%s" move from %s to %s)')
1831 msgmissing = ('repository changed while pushing - please try again '
1831 msgmissing = ('repository changed while pushing - please try again '
1832 '(bookmark "%s" is missing, expected %s)')
1832 '(bookmark "%s" is missing, expected %s)')
1833 msgexist = ('repository changed while pushing - please try again '
1833 msgexist = ('repository changed while pushing - please try again '
1834 '(bookmark "%s" set on %s, expected missing)')
1834 '(bookmark "%s" set on %s, expected missing)')
1835 for book, node in bookdata:
1835 for book, node in bookdata:
1836 currentnode = op.repo._bookmarks.get(book)
1836 currentnode = op.repo._bookmarks.get(book)
1837 if currentnode != node:
1837 if currentnode != node:
1838 if node is None:
1838 if node is None:
1839 finalmsg = msgexist % (book, nodemod.short(currentnode))
1839 finalmsg = msgexist % (book, nodemod.short(currentnode))
1840 elif currentnode is None:
1840 elif currentnode is None:
1841 finalmsg = msgmissing % (book, nodemod.short(node))
1841 finalmsg = msgmissing % (book, nodemod.short(node))
1842 else:
1842 else:
1843 finalmsg = msgstandard % (book, nodemod.short(node),
1843 finalmsg = msgstandard % (book, nodemod.short(node),
1844 nodemod.short(currentnode))
1844 nodemod.short(currentnode))
1845 raise error.PushRaced(finalmsg)
1845 raise error.PushRaced(finalmsg)
1846
1846
1847 @parthandler('check:heads')
1847 @parthandler('check:heads')
1848 def handlecheckheads(op, inpart):
1848 def handlecheckheads(op, inpart):
1849 """check that head of the repo did not change
1849 """check that head of the repo did not change
1850
1850
1851 This is used to detect a push race when using unbundle.
1851 This is used to detect a push race when using unbundle.
1852 This replaces the "heads" argument of unbundle."""
1852 This replaces the "heads" argument of unbundle."""
1853 h = inpart.read(20)
1853 h = inpart.read(20)
1854 heads = []
1854 heads = []
1855 while len(h) == 20:
1855 while len(h) == 20:
1856 heads.append(h)
1856 heads.append(h)
1857 h = inpart.read(20)
1857 h = inpart.read(20)
1858 assert not h
1858 assert not h
1859 # Trigger a transaction so that we are guaranteed to have the lock now.
1859 # Trigger a transaction so that we are guaranteed to have the lock now.
1860 if op.ui.configbool('experimental', 'bundle2lazylocking'):
1860 if op.ui.configbool('experimental', 'bundle2lazylocking'):
1861 op.gettransaction()
1861 op.gettransaction()
1862 if sorted(heads) != sorted(op.repo.heads()):
1862 if sorted(heads) != sorted(op.repo.heads()):
1863 raise error.PushRaced('repository changed while pushing - '
1863 raise error.PushRaced('repository changed while pushing - '
1864 'please try again')
1864 'please try again')
1865
1865
1866 @parthandler('check:updated-heads')
1866 @parthandler('check:updated-heads')
1867 def handlecheckupdatedheads(op, inpart):
1867 def handlecheckupdatedheads(op, inpart):
1868 """check for race on the heads touched by a push
1868 """check for race on the heads touched by a push
1869
1869
1870 This is similar to 'check:heads' but focus on the heads actually updated
1870 This is similar to 'check:heads' but focus on the heads actually updated
1871 during the push. If other activities happen on unrelated heads, it is
1871 during the push. If other activities happen on unrelated heads, it is
1872 ignored.
1872 ignored.
1873
1873
1874 This allow server with high traffic to avoid push contention as long as
1874 This allow server with high traffic to avoid push contention as long as
1875 unrelated parts of the graph are involved."""
1875 unrelated parts of the graph are involved."""
1876 h = inpart.read(20)
1876 h = inpart.read(20)
1877 heads = []
1877 heads = []
1878 while len(h) == 20:
1878 while len(h) == 20:
1879 heads.append(h)
1879 heads.append(h)
1880 h = inpart.read(20)
1880 h = inpart.read(20)
1881 assert not h
1881 assert not h
1882 # trigger a transaction so that we are guaranteed to have the lock now.
1882 # trigger a transaction so that we are guaranteed to have the lock now.
1883 if op.ui.configbool('experimental', 'bundle2lazylocking'):
1883 if op.ui.configbool('experimental', 'bundle2lazylocking'):
1884 op.gettransaction()
1884 op.gettransaction()
1885
1885
1886 currentheads = set()
1886 currentheads = set()
1887 for ls in op.repo.branchmap().itervalues():
1887 for ls in op.repo.branchmap().itervalues():
1888 currentheads.update(ls)
1888 currentheads.update(ls)
1889
1889
1890 for h in heads:
1890 for h in heads:
1891 if h not in currentheads:
1891 if h not in currentheads:
1892 raise error.PushRaced('repository changed while pushing - '
1892 raise error.PushRaced('repository changed while pushing - '
1893 'please try again')
1893 'please try again')
1894
1894
1895 @parthandler('check:phases')
1895 @parthandler('check:phases')
1896 def handlecheckphases(op, inpart):
1896 def handlecheckphases(op, inpart):
1897 """check that phase boundaries of the repository did not change
1897 """check that phase boundaries of the repository did not change
1898
1898
1899 This is used to detect a push race.
1899 This is used to detect a push race.
1900 """
1900 """
1901 phasetonodes = phases.binarydecode(inpart)
1901 phasetonodes = phases.binarydecode(inpart)
1902 unfi = op.repo.unfiltered()
1902 unfi = op.repo.unfiltered()
1903 cl = unfi.changelog
1903 cl = unfi.changelog
1904 phasecache = unfi._phasecache
1904 phasecache = unfi._phasecache
1905 msg = ('repository changed while pushing - please try again '
1905 msg = ('repository changed while pushing - please try again '
1906 '(%s is %s expected %s)')
1906 '(%s is %s expected %s)')
1907 for expectedphase, nodes in enumerate(phasetonodes):
1907 for expectedphase, nodes in enumerate(phasetonodes):
1908 for n in nodes:
1908 for n in nodes:
1909 actualphase = phasecache.phase(unfi, cl.rev(n))
1909 actualphase = phasecache.phase(unfi, cl.rev(n))
1910 if actualphase != expectedphase:
1910 if actualphase != expectedphase:
1911 finalmsg = msg % (nodemod.short(n),
1911 finalmsg = msg % (nodemod.short(n),
1912 phases.phasenames[actualphase],
1912 phases.phasenames[actualphase],
1913 phases.phasenames[expectedphase])
1913 phases.phasenames[expectedphase])
1914 raise error.PushRaced(finalmsg)
1914 raise error.PushRaced(finalmsg)
1915
1915
1916 @parthandler('output')
1916 @parthandler('output')
1917 def handleoutput(op, inpart):
1917 def handleoutput(op, inpart):
1918 """forward output captured on the server to the client"""
1918 """forward output captured on the server to the client"""
1919 for line in inpart.read().splitlines():
1919 for line in inpart.read().splitlines():
1920 op.ui.status(_('remote: %s\n') % line)
1920 op.ui.status(_('remote: %s\n') % line)
1921
1921
1922 @parthandler('replycaps')
1922 @parthandler('replycaps')
1923 def handlereplycaps(op, inpart):
1923 def handlereplycaps(op, inpart):
1924 """Notify that a reply bundle should be created
1924 """Notify that a reply bundle should be created
1925
1925
1926 The payload contains the capabilities information for the reply"""
1926 The payload contains the capabilities information for the reply"""
1927 caps = decodecaps(inpart.read())
1927 caps = decodecaps(inpart.read())
1928 if op.reply is None:
1928 if op.reply is None:
1929 op.reply = bundle20(op.ui, caps)
1929 op.reply = bundle20(op.ui, caps)
1930
1930
1931 class AbortFromPart(error.Abort):
1931 class AbortFromPart(error.Abort):
1932 """Sub-class of Abort that denotes an error from a bundle2 part."""
1932 """Sub-class of Abort that denotes an error from a bundle2 part."""
1933
1933
1934 @parthandler('error:abort', ('message', 'hint'))
1934 @parthandler('error:abort', ('message', 'hint'))
1935 def handleerrorabort(op, inpart):
1935 def handleerrorabort(op, inpart):
1936 """Used to transmit abort error over the wire"""
1936 """Used to transmit abort error over the wire"""
1937 raise AbortFromPart(inpart.params['message'],
1937 raise AbortFromPart(inpart.params['message'],
1938 hint=inpart.params.get('hint'))
1938 hint=inpart.params.get('hint'))
1939
1939
1940 @parthandler('error:pushkey', ('namespace', 'key', 'new', 'old', 'ret',
1940 @parthandler('error:pushkey', ('namespace', 'key', 'new', 'old', 'ret',
1941 'in-reply-to'))
1941 'in-reply-to'))
1942 def handleerrorpushkey(op, inpart):
1942 def handleerrorpushkey(op, inpart):
1943 """Used to transmit failure of a mandatory pushkey over the wire"""
1943 """Used to transmit failure of a mandatory pushkey over the wire"""
1944 kwargs = {}
1944 kwargs = {}
1945 for name in ('namespace', 'key', 'new', 'old', 'ret'):
1945 for name in ('namespace', 'key', 'new', 'old', 'ret'):
1946 value = inpart.params.get(name)
1946 value = inpart.params.get(name)
1947 if value is not None:
1947 if value is not None:
1948 kwargs[name] = value
1948 kwargs[name] = value
1949 raise error.PushkeyFailed(inpart.params['in-reply-to'], **kwargs)
1949 raise error.PushkeyFailed(inpart.params['in-reply-to'], **kwargs)
1950
1950
1951 @parthandler('error:unsupportedcontent', ('parttype', 'params'))
1951 @parthandler('error:unsupportedcontent', ('parttype', 'params'))
1952 def handleerrorunsupportedcontent(op, inpart):
1952 def handleerrorunsupportedcontent(op, inpart):
1953 """Used to transmit unknown content error over the wire"""
1953 """Used to transmit unknown content error over the wire"""
1954 kwargs = {}
1954 kwargs = {}
1955 parttype = inpart.params.get('parttype')
1955 parttype = inpart.params.get('parttype')
1956 if parttype is not None:
1956 if parttype is not None:
1957 kwargs['parttype'] = parttype
1957 kwargs['parttype'] = parttype
1958 params = inpart.params.get('params')
1958 params = inpart.params.get('params')
1959 if params is not None:
1959 if params is not None:
1960 kwargs['params'] = params.split('\0')
1960 kwargs['params'] = params.split('\0')
1961
1961
1962 raise error.BundleUnknownFeatureError(**kwargs)
1962 raise error.BundleUnknownFeatureError(**kwargs)
1963
1963
1964 @parthandler('error:pushraced', ('message',))
1964 @parthandler('error:pushraced', ('message',))
1965 def handleerrorpushraced(op, inpart):
1965 def handleerrorpushraced(op, inpart):
1966 """Used to transmit push race error over the wire"""
1966 """Used to transmit push race error over the wire"""
1967 raise error.ResponseError(_('push failed:'), inpart.params['message'])
1967 raise error.ResponseError(_('push failed:'), inpart.params['message'])
1968
1968
1969 @parthandler('listkeys', ('namespace',))
1969 @parthandler('listkeys', ('namespace',))
1970 def handlelistkeys(op, inpart):
1970 def handlelistkeys(op, inpart):
1971 """retrieve pushkey namespace content stored in a bundle2"""
1971 """retrieve pushkey namespace content stored in a bundle2"""
1972 namespace = inpart.params['namespace']
1972 namespace = inpart.params['namespace']
1973 r = pushkey.decodekeys(inpart.read())
1973 r = pushkey.decodekeys(inpart.read())
1974 op.records.add('listkeys', (namespace, r))
1974 op.records.add('listkeys', (namespace, r))
1975
1975
1976 @parthandler('pushkey', ('namespace', 'key', 'old', 'new'))
1976 @parthandler('pushkey', ('namespace', 'key', 'old', 'new'))
1977 def handlepushkey(op, inpart):
1977 def handlepushkey(op, inpart):
1978 """process a pushkey request"""
1978 """process a pushkey request"""
1979 dec = pushkey.decode
1979 dec = pushkey.decode
1980 namespace = dec(inpart.params['namespace'])
1980 namespace = dec(inpart.params['namespace'])
1981 key = dec(inpart.params['key'])
1981 key = dec(inpart.params['key'])
1982 old = dec(inpart.params['old'])
1982 old = dec(inpart.params['old'])
1983 new = dec(inpart.params['new'])
1983 new = dec(inpart.params['new'])
1984 # Grab the transaction to ensure that we have the lock before performing the
1984 # Grab the transaction to ensure that we have the lock before performing the
1985 # pushkey.
1985 # pushkey.
1986 if op.ui.configbool('experimental', 'bundle2lazylocking'):
1986 if op.ui.configbool('experimental', 'bundle2lazylocking'):
1987 op.gettransaction()
1987 op.gettransaction()
1988 ret = op.repo.pushkey(namespace, key, old, new)
1988 ret = op.repo.pushkey(namespace, key, old, new)
1989 record = {'namespace': namespace,
1989 record = {'namespace': namespace,
1990 'key': key,
1990 'key': key,
1991 'old': old,
1991 'old': old,
1992 'new': new}
1992 'new': new}
1993 op.records.add('pushkey', record)
1993 op.records.add('pushkey', record)
1994 if op.reply is not None:
1994 if op.reply is not None:
1995 rpart = op.reply.newpart('reply:pushkey')
1995 rpart = op.reply.newpart('reply:pushkey')
1996 rpart.addparam(
1996 rpart.addparam(
1997 'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False)
1997 'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False)
1998 rpart.addparam('return', '%i' % ret, mandatory=False)
1998 rpart.addparam('return', '%i' % ret, mandatory=False)
1999 if inpart.mandatory and not ret:
1999 if inpart.mandatory and not ret:
2000 kwargs = {}
2000 kwargs = {}
2001 for key in ('namespace', 'key', 'new', 'old', 'ret'):
2001 for key in ('namespace', 'key', 'new', 'old', 'ret'):
2002 if key in inpart.params:
2002 if key in inpart.params:
2003 kwargs[key] = inpart.params[key]
2003 kwargs[key] = inpart.params[key]
2004 raise error.PushkeyFailed(partid=str(inpart.id), **kwargs)
2004 raise error.PushkeyFailed(partid=str(inpart.id), **kwargs)
2005
2005
2006 @parthandler('bookmarks')
2006 @parthandler('bookmarks')
2007 def handlebookmark(op, inpart):
2007 def handlebookmark(op, inpart):
2008 """transmit bookmark information
2008 """transmit bookmark information
2009
2009
2010 The part contains binary encoded bookmark information.
2010 The part contains binary encoded bookmark information.
2011
2011
2012 The exact behavior of this part can be controlled by the 'bookmarks' mode
2012 The exact behavior of this part can be controlled by the 'bookmarks' mode
2013 on the bundle operation.
2013 on the bundle operation.
2014
2014
2015 When mode is 'apply' (the default) the bookmark information is applied as
2015 When mode is 'apply' (the default) the bookmark information is applied as
2016 is to the unbundling repository. Make sure a 'check:bookmarks' part is
2016 is to the unbundling repository. Make sure a 'check:bookmarks' part is
2017 issued earlier to check for push races in such update. This behavior is
2017 issued earlier to check for push races in such update. This behavior is
2018 suitable for pushing.
2018 suitable for pushing.
2019
2019
2020 When mode is 'records', the information is recorded into the 'bookmarks'
2020 When mode is 'records', the information is recorded into the 'bookmarks'
2021 records of the bundle operation. This behavior is suitable for pulling.
2021 records of the bundle operation. This behavior is suitable for pulling.
2022 """
2022 """
2023 changes = bookmarks.binarydecode(inpart)
2023 changes = bookmarks.binarydecode(inpart)
2024
2024
2025 pushkeycompat = op.repo.ui.configbool('server', 'bookmarks-pushkey-compat')
2025 pushkeycompat = op.repo.ui.configbool('server', 'bookmarks-pushkey-compat')
2026 bookmarksmode = op.modes.get('bookmarks', 'apply')
2026 bookmarksmode = op.modes.get('bookmarks', 'apply')
2027
2027
2028 if bookmarksmode == 'apply':
2028 if bookmarksmode == 'apply':
2029 tr = op.gettransaction()
2029 tr = op.gettransaction()
2030 bookstore = op.repo._bookmarks
2030 bookstore = op.repo._bookmarks
2031 if pushkeycompat:
2031 if pushkeycompat:
2032 allhooks = []
2032 allhooks = []
2033 for book, node in changes:
2033 for book, node in changes:
2034 hookargs = tr.hookargs.copy()
2034 hookargs = tr.hookargs.copy()
2035 hookargs['pushkeycompat'] = '1'
2035 hookargs['pushkeycompat'] = '1'
2036 hookargs['namespace'] = 'bookmark'
2036 hookargs['namespace'] = 'bookmarks'
2037 hookargs['key'] = book
2037 hookargs['key'] = book
2038 hookargs['old'] = nodemod.hex(bookstore.get(book, ''))
2038 hookargs['old'] = nodemod.hex(bookstore.get(book, ''))
2039 hookargs['new'] = nodemod.hex(node if node is not None else '')
2039 hookargs['new'] = nodemod.hex(node if node is not None else '')
2040 allhooks.append(hookargs)
2040 allhooks.append(hookargs)
2041
2041
2042 for hookargs in allhooks:
2042 for hookargs in allhooks:
2043 op.repo.hook('prepushkey', throw=True, **hookargs)
2043 op.repo.hook('prepushkey', throw=True, **hookargs)
2044
2044
2045 bookstore.applychanges(op.repo, op.gettransaction(), changes)
2045 bookstore.applychanges(op.repo, op.gettransaction(), changes)
2046
2046
2047 if pushkeycompat:
2047 if pushkeycompat:
2048 def runhook():
2048 def runhook():
2049 for hookargs in allhooks:
2049 for hookargs in allhooks:
2050 op.repo.hook('pushkey', **hookargs)
2050 op.repo.hook('pushkey', **hookargs)
2051 op.repo._afterlock(runhook)
2051 op.repo._afterlock(runhook)
2052
2052
2053 elif bookmarksmode == 'records':
2053 elif bookmarksmode == 'records':
2054 for book, node in changes:
2054 for book, node in changes:
2055 record = {'bookmark': book, 'node': node}
2055 record = {'bookmark': book, 'node': node}
2056 op.records.add('bookmarks', record)
2056 op.records.add('bookmarks', record)
2057 else:
2057 else:
2058 raise error.ProgrammingError('unkown bookmark mode: %s' % bookmarksmode)
2058 raise error.ProgrammingError('unkown bookmark mode: %s' % bookmarksmode)
2059
2059
2060 @parthandler('phase-heads')
2060 @parthandler('phase-heads')
2061 def handlephases(op, inpart):
2061 def handlephases(op, inpart):
2062 """apply phases from bundle part to repo"""
2062 """apply phases from bundle part to repo"""
2063 headsbyphase = phases.binarydecode(inpart)
2063 headsbyphase = phases.binarydecode(inpart)
2064 phases.updatephases(op.repo.unfiltered(), op.gettransaction, headsbyphase)
2064 phases.updatephases(op.repo.unfiltered(), op.gettransaction, headsbyphase)
2065
2065
2066 @parthandler('reply:pushkey', ('return', 'in-reply-to'))
2066 @parthandler('reply:pushkey', ('return', 'in-reply-to'))
2067 def handlepushkeyreply(op, inpart):
2067 def handlepushkeyreply(op, inpart):
2068 """retrieve the result of a pushkey request"""
2068 """retrieve the result of a pushkey request"""
2069 ret = int(inpart.params['return'])
2069 ret = int(inpart.params['return'])
2070 partid = int(inpart.params['in-reply-to'])
2070 partid = int(inpart.params['in-reply-to'])
2071 op.records.add('pushkey', {'return': ret}, partid)
2071 op.records.add('pushkey', {'return': ret}, partid)
2072
2072
2073 @parthandler('obsmarkers')
2073 @parthandler('obsmarkers')
2074 def handleobsmarker(op, inpart):
2074 def handleobsmarker(op, inpart):
2075 """add a stream of obsmarkers to the repo"""
2075 """add a stream of obsmarkers to the repo"""
2076 tr = op.gettransaction()
2076 tr = op.gettransaction()
2077 markerdata = inpart.read()
2077 markerdata = inpart.read()
2078 if op.ui.config('experimental', 'obsmarkers-exchange-debug'):
2078 if op.ui.config('experimental', 'obsmarkers-exchange-debug'):
2079 op.ui.write(('obsmarker-exchange: %i bytes received\n')
2079 op.ui.write(('obsmarker-exchange: %i bytes received\n')
2080 % len(markerdata))
2080 % len(markerdata))
2081 # The mergemarkers call will crash if marker creation is not enabled.
2081 # The mergemarkers call will crash if marker creation is not enabled.
2082 # we want to avoid this if the part is advisory.
2082 # we want to avoid this if the part is advisory.
2083 if not inpart.mandatory and op.repo.obsstore.readonly:
2083 if not inpart.mandatory and op.repo.obsstore.readonly:
2084 op.repo.ui.debug('ignoring obsolescence markers, feature not enabled\n')
2084 op.repo.ui.debug('ignoring obsolescence markers, feature not enabled\n')
2085 return
2085 return
2086 new = op.repo.obsstore.mergemarkers(tr, markerdata)
2086 new = op.repo.obsstore.mergemarkers(tr, markerdata)
2087 op.repo.invalidatevolatilesets()
2087 op.repo.invalidatevolatilesets()
2088 if new:
2088 if new:
2089 op.repo.ui.status(_('%i new obsolescence markers\n') % new)
2089 op.repo.ui.status(_('%i new obsolescence markers\n') % new)
2090 op.records.add('obsmarkers', {'new': new})
2090 op.records.add('obsmarkers', {'new': new})
2091 if op.reply is not None:
2091 if op.reply is not None:
2092 rpart = op.reply.newpart('reply:obsmarkers')
2092 rpart = op.reply.newpart('reply:obsmarkers')
2093 rpart.addparam(
2093 rpart.addparam(
2094 'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False)
2094 'in-reply-to', pycompat.bytestr(inpart.id), mandatory=False)
2095 rpart.addparam('new', '%i' % new, mandatory=False)
2095 rpart.addparam('new', '%i' % new, mandatory=False)
2096
2096
2097
2097
2098 @parthandler('reply:obsmarkers', ('new', 'in-reply-to'))
2098 @parthandler('reply:obsmarkers', ('new', 'in-reply-to'))
2099 def handleobsmarkerreply(op, inpart):
2099 def handleobsmarkerreply(op, inpart):
2100 """retrieve the result of a pushkey request"""
2100 """retrieve the result of a pushkey request"""
2101 ret = int(inpart.params['new'])
2101 ret = int(inpart.params['new'])
2102 partid = int(inpart.params['in-reply-to'])
2102 partid = int(inpart.params['in-reply-to'])
2103 op.records.add('obsmarkers', {'new': ret}, partid)
2103 op.records.add('obsmarkers', {'new': ret}, partid)
2104
2104
2105 @parthandler('hgtagsfnodes')
2105 @parthandler('hgtagsfnodes')
2106 def handlehgtagsfnodes(op, inpart):
2106 def handlehgtagsfnodes(op, inpart):
2107 """Applies .hgtags fnodes cache entries to the local repo.
2107 """Applies .hgtags fnodes cache entries to the local repo.
2108
2108
2109 Payload is pairs of 20 byte changeset nodes and filenodes.
2109 Payload is pairs of 20 byte changeset nodes and filenodes.
2110 """
2110 """
2111 # Grab the transaction so we ensure that we have the lock at this point.
2111 # Grab the transaction so we ensure that we have the lock at this point.
2112 if op.ui.configbool('experimental', 'bundle2lazylocking'):
2112 if op.ui.configbool('experimental', 'bundle2lazylocking'):
2113 op.gettransaction()
2113 op.gettransaction()
2114 cache = tags.hgtagsfnodescache(op.repo.unfiltered())
2114 cache = tags.hgtagsfnodescache(op.repo.unfiltered())
2115
2115
2116 count = 0
2116 count = 0
2117 while True:
2117 while True:
2118 node = inpart.read(20)
2118 node = inpart.read(20)
2119 fnode = inpart.read(20)
2119 fnode = inpart.read(20)
2120 if len(node) < 20 or len(fnode) < 20:
2120 if len(node) < 20 or len(fnode) < 20:
2121 op.ui.debug('ignoring incomplete received .hgtags fnodes data\n')
2121 op.ui.debug('ignoring incomplete received .hgtags fnodes data\n')
2122 break
2122 break
2123 cache.setfnode(node, fnode)
2123 cache.setfnode(node, fnode)
2124 count += 1
2124 count += 1
2125
2125
2126 cache.write()
2126 cache.write()
2127 op.ui.debug('applied %i hgtags fnodes cache entries\n' % count)
2127 op.ui.debug('applied %i hgtags fnodes cache entries\n' % count)
2128
2128
2129 @parthandler('pushvars')
2129 @parthandler('pushvars')
2130 def bundle2getvars(op, part):
2130 def bundle2getvars(op, part):
2131 '''unbundle a bundle2 containing shellvars on the server'''
2131 '''unbundle a bundle2 containing shellvars on the server'''
2132 # An option to disable unbundling on server-side for security reasons
2132 # An option to disable unbundling on server-side for security reasons
2133 if op.ui.configbool('push', 'pushvars.server'):
2133 if op.ui.configbool('push', 'pushvars.server'):
2134 hookargs = {}
2134 hookargs = {}
2135 for key, value in part.advisoryparams:
2135 for key, value in part.advisoryparams:
2136 key = key.upper()
2136 key = key.upper()
2137 # We want pushed variables to have USERVAR_ prepended so we know
2137 # We want pushed variables to have USERVAR_ prepended so we know
2138 # they came from the --pushvar flag.
2138 # they came from the --pushvar flag.
2139 key = "USERVAR_" + key
2139 key = "USERVAR_" + key
2140 hookargs[key] = value
2140 hookargs[key] = value
2141 op.addhookargs(hookargs)
2141 op.addhookargs(hookargs)
2142
2142
2143 @parthandler('stream2', ('requirements', 'filecount', 'bytecount'))
2143 @parthandler('stream2', ('requirements', 'filecount', 'bytecount'))
2144 def handlestreamv2bundle(op, part):
2144 def handlestreamv2bundle(op, part):
2145
2145
2146 requirements = part.params['requirements'].split()
2146 requirements = part.params['requirements'].split()
2147 filecount = int(part.params['filecount'])
2147 filecount = int(part.params['filecount'])
2148 bytecount = int(part.params['bytecount'])
2148 bytecount = int(part.params['bytecount'])
2149
2149
2150 repo = op.repo
2150 repo = op.repo
2151 if len(repo):
2151 if len(repo):
2152 msg = _('cannot apply stream clone to non empty repository')
2152 msg = _('cannot apply stream clone to non empty repository')
2153 raise error.Abort(msg)
2153 raise error.Abort(msg)
2154
2154
2155 repo.ui.debug('applying stream bundle\n')
2155 repo.ui.debug('applying stream bundle\n')
2156 streamclone.applybundlev2(repo, part, filecount, bytecount,
2156 streamclone.applybundlev2(repo, part, filecount, bytecount,
2157 requirements)
2157 requirements)
@@ -1,1175 +1,1244
1 #testcases b2-pushkey b2-binary
1 #testcases b2-pushkey b2-binary
2
2
3 #if b2-pushkey
3 #if b2-pushkey
4 $ cat << EOF >> $HGRCPATH
4 $ cat << EOF >> $HGRCPATH
5 > [devel]
5 > [devel]
6 > legacy.exchange=bookmarks
6 > legacy.exchange=bookmarks
7 > EOF
7 > EOF
8 #endif
8 #endif
9
9
10 #require serve
10 #require serve
11
11
12 $ cat << EOF >> $HGRCPATH
12 $ cat << EOF >> $HGRCPATH
13 > [ui]
13 > [ui]
14 > logtemplate={rev}:{node|short} {desc|firstline}
14 > logtemplate={rev}:{node|short} {desc|firstline}
15 > [phases]
15 > [phases]
16 > publish=False
16 > publish=False
17 > [experimental]
17 > [experimental]
18 > evolution.createmarkers=True
18 > evolution.createmarkers=True
19 > evolution.exchange=True
19 > evolution.exchange=True
20 > EOF
20 > EOF
21
21
22 $ cat > $TESTTMP/hook.sh <<'EOF'
22 $ cat > $TESTTMP/hook.sh <<'EOF'
23 > echo "test-hook-bookmark: $HG_BOOKMARK: $HG_OLDNODE -> $HG_NODE"
23 > echo "test-hook-bookmark: $HG_BOOKMARK: $HG_OLDNODE -> $HG_NODE"
24 > EOF
24 > EOF
25 $ TESTHOOK="hooks.txnclose-bookmark.test=sh $TESTTMP/hook.sh"
25 $ TESTHOOK="hooks.txnclose-bookmark.test=sh $TESTTMP/hook.sh"
26
26
27 initialize
27 initialize
28
28
29 $ hg init a
29 $ hg init a
30 $ cd a
30 $ cd a
31 $ echo 'test' > test
31 $ echo 'test' > test
32 $ hg commit -Am'test'
32 $ hg commit -Am'test'
33 adding test
33 adding test
34
34
35 set bookmarks
35 set bookmarks
36
36
37 $ hg bookmark X
37 $ hg bookmark X
38 $ hg bookmark Y
38 $ hg bookmark Y
39 $ hg bookmark Z
39 $ hg bookmark Z
40
40
41 import bookmark by name
41 import bookmark by name
42
42
43 $ hg init ../b
43 $ hg init ../b
44 $ cd ../b
44 $ cd ../b
45 $ hg book Y
45 $ hg book Y
46 $ hg book
46 $ hg book
47 * Y -1:000000000000
47 * Y -1:000000000000
48 $ hg pull ../a --config "$TESTHOOK"
48 $ hg pull ../a --config "$TESTHOOK"
49 pulling from ../a
49 pulling from ../a
50 requesting all changes
50 requesting all changes
51 adding changesets
51 adding changesets
52 adding manifests
52 adding manifests
53 adding file changes
53 adding file changes
54 added 1 changesets with 1 changes to 1 files
54 added 1 changesets with 1 changes to 1 files
55 adding remote bookmark X
55 adding remote bookmark X
56 updating bookmark Y
56 updating bookmark Y
57 adding remote bookmark Z
57 adding remote bookmark Z
58 new changesets 4e3505fd9583
58 new changesets 4e3505fd9583
59 test-hook-bookmark: X: -> 4e3505fd95835d721066b76e75dbb8cc554d7f77
59 test-hook-bookmark: X: -> 4e3505fd95835d721066b76e75dbb8cc554d7f77
60 test-hook-bookmark: Y: 0000000000000000000000000000000000000000 -> 4e3505fd95835d721066b76e75dbb8cc554d7f77
60 test-hook-bookmark: Y: 0000000000000000000000000000000000000000 -> 4e3505fd95835d721066b76e75dbb8cc554d7f77
61 test-hook-bookmark: Z: -> 4e3505fd95835d721066b76e75dbb8cc554d7f77
61 test-hook-bookmark: Z: -> 4e3505fd95835d721066b76e75dbb8cc554d7f77
62 (run 'hg update' to get a working copy)
62 (run 'hg update' to get a working copy)
63 $ hg bookmarks
63 $ hg bookmarks
64 X 0:4e3505fd9583
64 X 0:4e3505fd9583
65 * Y 0:4e3505fd9583
65 * Y 0:4e3505fd9583
66 Z 0:4e3505fd9583
66 Z 0:4e3505fd9583
67 $ hg debugpushkey ../a namespaces
67 $ hg debugpushkey ../a namespaces
68 bookmarks
68 bookmarks
69 namespaces
69 namespaces
70 obsolete
70 obsolete
71 phases
71 phases
72 $ hg debugpushkey ../a bookmarks
72 $ hg debugpushkey ../a bookmarks
73 X 4e3505fd95835d721066b76e75dbb8cc554d7f77
73 X 4e3505fd95835d721066b76e75dbb8cc554d7f77
74 Y 4e3505fd95835d721066b76e75dbb8cc554d7f77
74 Y 4e3505fd95835d721066b76e75dbb8cc554d7f77
75 Z 4e3505fd95835d721066b76e75dbb8cc554d7f77
75 Z 4e3505fd95835d721066b76e75dbb8cc554d7f77
76
76
77 delete the bookmark to re-pull it
77 delete the bookmark to re-pull it
78
78
79 $ hg book -d X
79 $ hg book -d X
80 $ hg pull -B X ../a
80 $ hg pull -B X ../a
81 pulling from ../a
81 pulling from ../a
82 no changes found
82 no changes found
83 adding remote bookmark X
83 adding remote bookmark X
84
84
85 finally no-op pull
85 finally no-op pull
86
86
87 $ hg pull -B X ../a
87 $ hg pull -B X ../a
88 pulling from ../a
88 pulling from ../a
89 no changes found
89 no changes found
90 $ hg bookmark
90 $ hg bookmark
91 X 0:4e3505fd9583
91 X 0:4e3505fd9583
92 * Y 0:4e3505fd9583
92 * Y 0:4e3505fd9583
93 Z 0:4e3505fd9583
93 Z 0:4e3505fd9583
94
94
95 export bookmark by name
95 export bookmark by name
96
96
97 $ hg bookmark W
97 $ hg bookmark W
98 $ hg bookmark foo
98 $ hg bookmark foo
99 $ hg bookmark foobar
99 $ hg bookmark foobar
100 $ hg push -B W ../a
100 $ hg push -B W ../a
101 pushing to ../a
101 pushing to ../a
102 searching for changes
102 searching for changes
103 no changes found
103 no changes found
104 exporting bookmark W
104 exporting bookmark W
105 [1]
105 [1]
106 $ hg -R ../a bookmarks
106 $ hg -R ../a bookmarks
107 W -1:000000000000
107 W -1:000000000000
108 X 0:4e3505fd9583
108 X 0:4e3505fd9583
109 Y 0:4e3505fd9583
109 Y 0:4e3505fd9583
110 * Z 0:4e3505fd9583
110 * Z 0:4e3505fd9583
111
111
112 delete a remote bookmark
112 delete a remote bookmark
113
113
114 $ hg book -d W
114 $ hg book -d W
115
115
116 #if b2-pushkey
116 #if b2-pushkey
117
117
118 $ hg push -B W ../a --config "$TESTHOOK" --debug --config devel.bundle2.debug=yes
118 $ hg push -B W ../a --config "$TESTHOOK" --debug --config devel.bundle2.debug=yes
119 pushing to ../a
119 pushing to ../a
120 query 1; heads
120 query 1; heads
121 searching for changes
121 searching for changes
122 all remote heads known locally
122 all remote heads known locally
123 listing keys for "phases"
123 listing keys for "phases"
124 checking for updated bookmarks
124 checking for updated bookmarks
125 listing keys for "bookmarks"
125 listing keys for "bookmarks"
126 no changes found
126 no changes found
127 bundle2-output-bundle: "HG20", 4 parts total
127 bundle2-output-bundle: "HG20", 4 parts total
128 bundle2-output: start emission of HG20 stream
128 bundle2-output: start emission of HG20 stream
129 bundle2-output: bundle parameter:
129 bundle2-output: bundle parameter:
130 bundle2-output: start of parts
130 bundle2-output: start of parts
131 bundle2-output: bundle part: "replycaps"
131 bundle2-output: bundle part: "replycaps"
132 bundle2-output-part: "replycaps" 205 bytes payload
132 bundle2-output-part: "replycaps" 205 bytes payload
133 bundle2-output: part 0: "REPLYCAPS"
133 bundle2-output: part 0: "REPLYCAPS"
134 bundle2-output: header chunk size: 16
134 bundle2-output: header chunk size: 16
135 bundle2-output: payload chunk size: 205
135 bundle2-output: payload chunk size: 205
136 bundle2-output: closing payload chunk
136 bundle2-output: closing payload chunk
137 bundle2-output: bundle part: "check:bookmarks"
137 bundle2-output: bundle part: "check:bookmarks"
138 bundle2-output-part: "check:bookmarks" 23 bytes payload
138 bundle2-output-part: "check:bookmarks" 23 bytes payload
139 bundle2-output: part 1: "CHECK:BOOKMARKS"
139 bundle2-output: part 1: "CHECK:BOOKMARKS"
140 bundle2-output: header chunk size: 22
140 bundle2-output: header chunk size: 22
141 bundle2-output: payload chunk size: 23
141 bundle2-output: payload chunk size: 23
142 bundle2-output: closing payload chunk
142 bundle2-output: closing payload chunk
143 bundle2-output: bundle part: "check:phases"
143 bundle2-output: bundle part: "check:phases"
144 bundle2-output-part: "check:phases" 48 bytes payload
144 bundle2-output-part: "check:phases" 48 bytes payload
145 bundle2-output: part 2: "CHECK:PHASES"
145 bundle2-output: part 2: "CHECK:PHASES"
146 bundle2-output: header chunk size: 19
146 bundle2-output: header chunk size: 19
147 bundle2-output: payload chunk size: 48
147 bundle2-output: payload chunk size: 48
148 bundle2-output: closing payload chunk
148 bundle2-output: closing payload chunk
149 bundle2-output: bundle part: "pushkey"
149 bundle2-output: bundle part: "pushkey"
150 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
150 bundle2-output-part: "pushkey" (params: 4 mandatory) empty payload
151 bundle2-output: part 3: "PUSHKEY"
151 bundle2-output: part 3: "PUSHKEY"
152 bundle2-output: header chunk size: 90
152 bundle2-output: header chunk size: 90
153 bundle2-output: closing payload chunk
153 bundle2-output: closing payload chunk
154 bundle2-output: end of bundle
154 bundle2-output: end of bundle
155 bundle2-input: start processing of HG20 stream
155 bundle2-input: start processing of HG20 stream
156 bundle2-input: reading bundle2 stream parameters
156 bundle2-input: reading bundle2 stream parameters
157 bundle2-input-bundle: with-transaction
157 bundle2-input-bundle: with-transaction
158 bundle2-input: start extraction of bundle2 parts
158 bundle2-input: start extraction of bundle2 parts
159 bundle2-input: part header size: 16
159 bundle2-input: part header size: 16
160 bundle2-input: part type: "REPLYCAPS"
160 bundle2-input: part type: "REPLYCAPS"
161 bundle2-input: part id: "0"
161 bundle2-input: part id: "0"
162 bundle2-input: part parameters: 0
162 bundle2-input: part parameters: 0
163 bundle2-input: found a handler for part replycaps
163 bundle2-input: found a handler for part replycaps
164 bundle2-input-part: "replycaps" supported
164 bundle2-input-part: "replycaps" supported
165 bundle2-input: payload chunk size: 205
165 bundle2-input: payload chunk size: 205
166 bundle2-input: payload chunk size: 0
166 bundle2-input: payload chunk size: 0
167 bundle2-input-part: total payload size 205
167 bundle2-input-part: total payload size 205
168 bundle2-input: part header size: 22
168 bundle2-input: part header size: 22
169 bundle2-input: part type: "CHECK:BOOKMARKS"
169 bundle2-input: part type: "CHECK:BOOKMARKS"
170 bundle2-input: part id: "1"
170 bundle2-input: part id: "1"
171 bundle2-input: part parameters: 0
171 bundle2-input: part parameters: 0
172 bundle2-input: found a handler for part check:bookmarks
172 bundle2-input: found a handler for part check:bookmarks
173 bundle2-input-part: "check:bookmarks" supported
173 bundle2-input-part: "check:bookmarks" supported
174 bundle2-input: payload chunk size: 23
174 bundle2-input: payload chunk size: 23
175 bundle2-input: payload chunk size: 0
175 bundle2-input: payload chunk size: 0
176 bundle2-input-part: total payload size 23
176 bundle2-input-part: total payload size 23
177 bundle2-input: part header size: 19
177 bundle2-input: part header size: 19
178 bundle2-input: part type: "CHECK:PHASES"
178 bundle2-input: part type: "CHECK:PHASES"
179 bundle2-input: part id: "2"
179 bundle2-input: part id: "2"
180 bundle2-input: part parameters: 0
180 bundle2-input: part parameters: 0
181 bundle2-input: found a handler for part check:phases
181 bundle2-input: found a handler for part check:phases
182 bundle2-input-part: "check:phases" supported
182 bundle2-input-part: "check:phases" supported
183 bundle2-input: payload chunk size: 48
183 bundle2-input: payload chunk size: 48
184 bundle2-input: payload chunk size: 0
184 bundle2-input: payload chunk size: 0
185 bundle2-input-part: total payload size 48
185 bundle2-input-part: total payload size 48
186 bundle2-input: part header size: 90
186 bundle2-input: part header size: 90
187 bundle2-input: part type: "PUSHKEY"
187 bundle2-input: part type: "PUSHKEY"
188 bundle2-input: part id: "3"
188 bundle2-input: part id: "3"
189 bundle2-input: part parameters: 4
189 bundle2-input: part parameters: 4
190 bundle2-input: found a handler for part pushkey
190 bundle2-input: found a handler for part pushkey
191 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
191 bundle2-input-part: "pushkey" (params: 4 mandatory) supported
192 pushing key for "bookmarks:W"
192 pushing key for "bookmarks:W"
193 bundle2-input: payload chunk size: 0
193 bundle2-input: payload chunk size: 0
194 bundle2-input: part header size: 0
194 bundle2-input: part header size: 0
195 bundle2-input: end of bundle2 stream
195 bundle2-input: end of bundle2 stream
196 bundle2-input-bundle: 3 parts total
196 bundle2-input-bundle: 3 parts total
197 running hook txnclose-bookmark.test: sh $TESTTMP/hook.sh
197 running hook txnclose-bookmark.test: sh $TESTTMP/hook.sh
198 test-hook-bookmark: W: 0000000000000000000000000000000000000000 ->
198 test-hook-bookmark: W: 0000000000000000000000000000000000000000 ->
199 bundle2-output-bundle: "HG20", 1 parts total
199 bundle2-output-bundle: "HG20", 1 parts total
200 bundle2-output: start emission of HG20 stream
200 bundle2-output: start emission of HG20 stream
201 bundle2-output: bundle parameter:
201 bundle2-output: bundle parameter:
202 bundle2-output: start of parts
202 bundle2-output: start of parts
203 bundle2-output: bundle part: "reply:pushkey"
203 bundle2-output: bundle part: "reply:pushkey"
204 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
204 bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload
205 bundle2-output: part 0: "REPLY:PUSHKEY"
205 bundle2-output: part 0: "REPLY:PUSHKEY"
206 bundle2-output: header chunk size: 43
206 bundle2-output: header chunk size: 43
207 bundle2-output: closing payload chunk
207 bundle2-output: closing payload chunk
208 bundle2-output: end of bundle
208 bundle2-output: end of bundle
209 bundle2-input: start processing of HG20 stream
209 bundle2-input: start processing of HG20 stream
210 bundle2-input: reading bundle2 stream parameters
210 bundle2-input: reading bundle2 stream parameters
211 bundle2-input-bundle: no-transaction
211 bundle2-input-bundle: no-transaction
212 bundle2-input: start extraction of bundle2 parts
212 bundle2-input: start extraction of bundle2 parts
213 bundle2-input: part header size: 43
213 bundle2-input: part header size: 43
214 bundle2-input: part type: "REPLY:PUSHKEY"
214 bundle2-input: part type: "REPLY:PUSHKEY"
215 bundle2-input: part id: "0"
215 bundle2-input: part id: "0"
216 bundle2-input: part parameters: 2
216 bundle2-input: part parameters: 2
217 bundle2-input: found a handler for part reply:pushkey
217 bundle2-input: found a handler for part reply:pushkey
218 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
218 bundle2-input-part: "reply:pushkey" (params: 0 advisory) supported
219 bundle2-input: payload chunk size: 0
219 bundle2-input: payload chunk size: 0
220 bundle2-input: part header size: 0
220 bundle2-input: part header size: 0
221 bundle2-input: end of bundle2 stream
221 bundle2-input: end of bundle2 stream
222 bundle2-input-bundle: 0 parts total
222 bundle2-input-bundle: 0 parts total
223 deleting remote bookmark W
223 deleting remote bookmark W
224 listing keys for "phases"
224 listing keys for "phases"
225 [1]
225 [1]
226
226
227 #endif
227 #endif
228 #if b2-binary
228 #if b2-binary
229
229
230 $ hg push -B W ../a --config "$TESTHOOK" --debug --config devel.bundle2.debug=yes
230 $ hg push -B W ../a --config "$TESTHOOK" --debug --config devel.bundle2.debug=yes
231 pushing to ../a
231 pushing to ../a
232 query 1; heads
232 query 1; heads
233 searching for changes
233 searching for changes
234 all remote heads known locally
234 all remote heads known locally
235 listing keys for "phases"
235 listing keys for "phases"
236 checking for updated bookmarks
236 checking for updated bookmarks
237 listing keys for "bookmarks"
237 listing keys for "bookmarks"
238 no changes found
238 no changes found
239 bundle2-output-bundle: "HG20", 4 parts total
239 bundle2-output-bundle: "HG20", 4 parts total
240 bundle2-output: start emission of HG20 stream
240 bundle2-output: start emission of HG20 stream
241 bundle2-output: bundle parameter:
241 bundle2-output: bundle parameter:
242 bundle2-output: start of parts
242 bundle2-output: start of parts
243 bundle2-output: bundle part: "replycaps"
243 bundle2-output: bundle part: "replycaps"
244 bundle2-output-part: "replycaps" 205 bytes payload
244 bundle2-output-part: "replycaps" 205 bytes payload
245 bundle2-output: part 0: "REPLYCAPS"
245 bundle2-output: part 0: "REPLYCAPS"
246 bundle2-output: header chunk size: 16
246 bundle2-output: header chunk size: 16
247 bundle2-output: payload chunk size: 205
247 bundle2-output: payload chunk size: 205
248 bundle2-output: closing payload chunk
248 bundle2-output: closing payload chunk
249 bundle2-output: bundle part: "check:bookmarks"
249 bundle2-output: bundle part: "check:bookmarks"
250 bundle2-output-part: "check:bookmarks" 23 bytes payload
250 bundle2-output-part: "check:bookmarks" 23 bytes payload
251 bundle2-output: part 1: "CHECK:BOOKMARKS"
251 bundle2-output: part 1: "CHECK:BOOKMARKS"
252 bundle2-output: header chunk size: 22
252 bundle2-output: header chunk size: 22
253 bundle2-output: payload chunk size: 23
253 bundle2-output: payload chunk size: 23
254 bundle2-output: closing payload chunk
254 bundle2-output: closing payload chunk
255 bundle2-output: bundle part: "check:phases"
255 bundle2-output: bundle part: "check:phases"
256 bundle2-output-part: "check:phases" 48 bytes payload
256 bundle2-output-part: "check:phases" 48 bytes payload
257 bundle2-output: part 2: "CHECK:PHASES"
257 bundle2-output: part 2: "CHECK:PHASES"
258 bundle2-output: header chunk size: 19
258 bundle2-output: header chunk size: 19
259 bundle2-output: payload chunk size: 48
259 bundle2-output: payload chunk size: 48
260 bundle2-output: closing payload chunk
260 bundle2-output: closing payload chunk
261 bundle2-output: bundle part: "bookmarks"
261 bundle2-output: bundle part: "bookmarks"
262 bundle2-output-part: "bookmarks" 23 bytes payload
262 bundle2-output-part: "bookmarks" 23 bytes payload
263 bundle2-output: part 3: "BOOKMARKS"
263 bundle2-output: part 3: "BOOKMARKS"
264 bundle2-output: header chunk size: 16
264 bundle2-output: header chunk size: 16
265 bundle2-output: payload chunk size: 23
265 bundle2-output: payload chunk size: 23
266 bundle2-output: closing payload chunk
266 bundle2-output: closing payload chunk
267 bundle2-output: end of bundle
267 bundle2-output: end of bundle
268 bundle2-input: start processing of HG20 stream
268 bundle2-input: start processing of HG20 stream
269 bundle2-input: reading bundle2 stream parameters
269 bundle2-input: reading bundle2 stream parameters
270 bundle2-input-bundle: with-transaction
270 bundle2-input-bundle: with-transaction
271 bundle2-input: start extraction of bundle2 parts
271 bundle2-input: start extraction of bundle2 parts
272 bundle2-input: part header size: 16
272 bundle2-input: part header size: 16
273 bundle2-input: part type: "REPLYCAPS"
273 bundle2-input: part type: "REPLYCAPS"
274 bundle2-input: part id: "0"
274 bundle2-input: part id: "0"
275 bundle2-input: part parameters: 0
275 bundle2-input: part parameters: 0
276 bundle2-input: found a handler for part replycaps
276 bundle2-input: found a handler for part replycaps
277 bundle2-input-part: "replycaps" supported
277 bundle2-input-part: "replycaps" supported
278 bundle2-input: payload chunk size: 205
278 bundle2-input: payload chunk size: 205
279 bundle2-input: payload chunk size: 0
279 bundle2-input: payload chunk size: 0
280 bundle2-input-part: total payload size 205
280 bundle2-input-part: total payload size 205
281 bundle2-input: part header size: 22
281 bundle2-input: part header size: 22
282 bundle2-input: part type: "CHECK:BOOKMARKS"
282 bundle2-input: part type: "CHECK:BOOKMARKS"
283 bundle2-input: part id: "1"
283 bundle2-input: part id: "1"
284 bundle2-input: part parameters: 0
284 bundle2-input: part parameters: 0
285 bundle2-input: found a handler for part check:bookmarks
285 bundle2-input: found a handler for part check:bookmarks
286 bundle2-input-part: "check:bookmarks" supported
286 bundle2-input-part: "check:bookmarks" supported
287 bundle2-input: payload chunk size: 23
287 bundle2-input: payload chunk size: 23
288 bundle2-input: payload chunk size: 0
288 bundle2-input: payload chunk size: 0
289 bundle2-input-part: total payload size 23
289 bundle2-input-part: total payload size 23
290 bundle2-input: part header size: 19
290 bundle2-input: part header size: 19
291 bundle2-input: part type: "CHECK:PHASES"
291 bundle2-input: part type: "CHECK:PHASES"
292 bundle2-input: part id: "2"
292 bundle2-input: part id: "2"
293 bundle2-input: part parameters: 0
293 bundle2-input: part parameters: 0
294 bundle2-input: found a handler for part check:phases
294 bundle2-input: found a handler for part check:phases
295 bundle2-input-part: "check:phases" supported
295 bundle2-input-part: "check:phases" supported
296 bundle2-input: payload chunk size: 48
296 bundle2-input: payload chunk size: 48
297 bundle2-input: payload chunk size: 0
297 bundle2-input: payload chunk size: 0
298 bundle2-input-part: total payload size 48
298 bundle2-input-part: total payload size 48
299 bundle2-input: part header size: 16
299 bundle2-input: part header size: 16
300 bundle2-input: part type: "BOOKMARKS"
300 bundle2-input: part type: "BOOKMARKS"
301 bundle2-input: part id: "3"
301 bundle2-input: part id: "3"
302 bundle2-input: part parameters: 0
302 bundle2-input: part parameters: 0
303 bundle2-input: found a handler for part bookmarks
303 bundle2-input: found a handler for part bookmarks
304 bundle2-input-part: "bookmarks" supported
304 bundle2-input-part: "bookmarks" supported
305 bundle2-input: payload chunk size: 23
305 bundle2-input: payload chunk size: 23
306 bundle2-input: payload chunk size: 0
306 bundle2-input: payload chunk size: 0
307 bundle2-input-part: total payload size 23
307 bundle2-input-part: total payload size 23
308 bundle2-input: part header size: 0
308 bundle2-input: part header size: 0
309 bundle2-input: end of bundle2 stream
309 bundle2-input: end of bundle2 stream
310 bundle2-input-bundle: 3 parts total
310 bundle2-input-bundle: 3 parts total
311 running hook txnclose-bookmark.test: sh $TESTTMP/hook.sh
311 running hook txnclose-bookmark.test: sh $TESTTMP/hook.sh
312 test-hook-bookmark: W: 0000000000000000000000000000000000000000 ->
312 test-hook-bookmark: W: 0000000000000000000000000000000000000000 ->
313 bundle2-output-bundle: "HG20", 0 parts total
313 bundle2-output-bundle: "HG20", 0 parts total
314 bundle2-output: start emission of HG20 stream
314 bundle2-output: start emission of HG20 stream
315 bundle2-output: bundle parameter:
315 bundle2-output: bundle parameter:
316 bundle2-output: start of parts
316 bundle2-output: start of parts
317 bundle2-output: end of bundle
317 bundle2-output: end of bundle
318 bundle2-input: start processing of HG20 stream
318 bundle2-input: start processing of HG20 stream
319 bundle2-input: reading bundle2 stream parameters
319 bundle2-input: reading bundle2 stream parameters
320 bundle2-input-bundle: no-transaction
320 bundle2-input-bundle: no-transaction
321 bundle2-input: start extraction of bundle2 parts
321 bundle2-input: start extraction of bundle2 parts
322 bundle2-input: part header size: 0
322 bundle2-input: part header size: 0
323 bundle2-input: end of bundle2 stream
323 bundle2-input: end of bundle2 stream
324 bundle2-input-bundle: 0 parts total
324 bundle2-input-bundle: 0 parts total
325 deleting remote bookmark W
325 deleting remote bookmark W
326 listing keys for "phases"
326 listing keys for "phases"
327 [1]
327 [1]
328
328
329 #endif
329 #endif
330
330
331 export the active bookmark
331 export the active bookmark
332
332
333 $ hg bookmark V
333 $ hg bookmark V
334 $ hg push -B . ../a
334 $ hg push -B . ../a
335 pushing to ../a
335 pushing to ../a
336 searching for changes
336 searching for changes
337 no changes found
337 no changes found
338 exporting bookmark V
338 exporting bookmark V
339 [1]
339 [1]
340
340
341 exporting the active bookmark with 'push -B .'
341 exporting the active bookmark with 'push -B .'
342 demand that one of the bookmarks is activated
342 demand that one of the bookmarks is activated
343
343
344 $ hg update -r default
344 $ hg update -r default
345 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
345 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
346 (leaving bookmark V)
346 (leaving bookmark V)
347 $ hg push -B . ../a
347 $ hg push -B . ../a
348 abort: no active bookmark
348 abort: no active bookmark
349 [255]
349 [255]
350 $ hg update -r V
350 $ hg update -r V
351 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
351 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
352 (activating bookmark V)
352 (activating bookmark V)
353
353
354 delete the bookmark
354 delete the bookmark
355
355
356 $ hg book -d V
356 $ hg book -d V
357 $ hg push -B V ../a
357 $ hg push -B V ../a
358 pushing to ../a
358 pushing to ../a
359 searching for changes
359 searching for changes
360 no changes found
360 no changes found
361 deleting remote bookmark V
361 deleting remote bookmark V
362 [1]
362 [1]
363 $ hg up foobar
363 $ hg up foobar
364 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
364 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
365 (activating bookmark foobar)
365 (activating bookmark foobar)
366
366
367 push/pull name that doesn't exist
367 push/pull name that doesn't exist
368
368
369 $ hg push -B badname ../a
369 $ hg push -B badname ../a
370 pushing to ../a
370 pushing to ../a
371 searching for changes
371 searching for changes
372 bookmark badname does not exist on the local or remote repository!
372 bookmark badname does not exist on the local or remote repository!
373 no changes found
373 no changes found
374 [2]
374 [2]
375 $ hg pull -B anotherbadname ../a
375 $ hg pull -B anotherbadname ../a
376 pulling from ../a
376 pulling from ../a
377 abort: remote bookmark anotherbadname not found!
377 abort: remote bookmark anotherbadname not found!
378 [255]
378 [255]
379
379
380 divergent bookmarks
380 divergent bookmarks
381
381
382 $ cd ../a
382 $ cd ../a
383 $ echo c1 > f1
383 $ echo c1 > f1
384 $ hg ci -Am1
384 $ hg ci -Am1
385 adding f1
385 adding f1
386 $ hg book -f @
386 $ hg book -f @
387 $ hg book -f X
387 $ hg book -f X
388 $ hg book
388 $ hg book
389 @ 1:0d2164f0ce0d
389 @ 1:0d2164f0ce0d
390 * X 1:0d2164f0ce0d
390 * X 1:0d2164f0ce0d
391 Y 0:4e3505fd9583
391 Y 0:4e3505fd9583
392 Z 1:0d2164f0ce0d
392 Z 1:0d2164f0ce0d
393
393
394 $ cd ../b
394 $ cd ../b
395 $ hg up
395 $ hg up
396 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
396 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
397 updating bookmark foobar
397 updating bookmark foobar
398 $ echo c2 > f2
398 $ echo c2 > f2
399 $ hg ci -Am2
399 $ hg ci -Am2
400 adding f2
400 adding f2
401 $ hg book -if @
401 $ hg book -if @
402 $ hg book -if X
402 $ hg book -if X
403 $ hg book
403 $ hg book
404 @ 1:9b140be10808
404 @ 1:9b140be10808
405 X 1:9b140be10808
405 X 1:9b140be10808
406 Y 0:4e3505fd9583
406 Y 0:4e3505fd9583
407 Z 0:4e3505fd9583
407 Z 0:4e3505fd9583
408 foo -1:000000000000
408 foo -1:000000000000
409 * foobar 1:9b140be10808
409 * foobar 1:9b140be10808
410
410
411 $ hg pull --config paths.foo=../a foo --config "$TESTHOOK"
411 $ hg pull --config paths.foo=../a foo --config "$TESTHOOK"
412 pulling from $TESTTMP/a
412 pulling from $TESTTMP/a
413 searching for changes
413 searching for changes
414 adding changesets
414 adding changesets
415 adding manifests
415 adding manifests
416 adding file changes
416 adding file changes
417 added 1 changesets with 1 changes to 1 files (+1 heads)
417 added 1 changesets with 1 changes to 1 files (+1 heads)
418 divergent bookmark @ stored as @foo
418 divergent bookmark @ stored as @foo
419 divergent bookmark X stored as X@foo
419 divergent bookmark X stored as X@foo
420 updating bookmark Z
420 updating bookmark Z
421 new changesets 0d2164f0ce0d
421 new changesets 0d2164f0ce0d
422 test-hook-bookmark: @foo: -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
422 test-hook-bookmark: @foo: -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
423 test-hook-bookmark: X@foo: -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
423 test-hook-bookmark: X@foo: -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
424 test-hook-bookmark: Z: 4e3505fd95835d721066b76e75dbb8cc554d7f77 -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
424 test-hook-bookmark: Z: 4e3505fd95835d721066b76e75dbb8cc554d7f77 -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
425 (run 'hg heads' to see heads, 'hg merge' to merge)
425 (run 'hg heads' to see heads, 'hg merge' to merge)
426 $ hg book
426 $ hg book
427 @ 1:9b140be10808
427 @ 1:9b140be10808
428 @foo 2:0d2164f0ce0d
428 @foo 2:0d2164f0ce0d
429 X 1:9b140be10808
429 X 1:9b140be10808
430 X@foo 2:0d2164f0ce0d
430 X@foo 2:0d2164f0ce0d
431 Y 0:4e3505fd9583
431 Y 0:4e3505fd9583
432 Z 2:0d2164f0ce0d
432 Z 2:0d2164f0ce0d
433 foo -1:000000000000
433 foo -1:000000000000
434 * foobar 1:9b140be10808
434 * foobar 1:9b140be10808
435
435
436 (test that too many divergence of bookmark)
436 (test that too many divergence of bookmark)
437
437
438 $ $PYTHON $TESTDIR/seq.py 1 100 | while read i; do hg bookmarks -r 000000000000 "X@${i}"; done
438 $ $PYTHON $TESTDIR/seq.py 1 100 | while read i; do hg bookmarks -r 000000000000 "X@${i}"; done
439 $ hg pull ../a
439 $ hg pull ../a
440 pulling from ../a
440 pulling from ../a
441 searching for changes
441 searching for changes
442 no changes found
442 no changes found
443 warning: failed to assign numbered name to divergent bookmark X
443 warning: failed to assign numbered name to divergent bookmark X
444 divergent bookmark @ stored as @1
444 divergent bookmark @ stored as @1
445 $ hg bookmarks | grep '^ X' | grep -v ':000000000000'
445 $ hg bookmarks | grep '^ X' | grep -v ':000000000000'
446 X 1:9b140be10808
446 X 1:9b140be10808
447 X@foo 2:0d2164f0ce0d
447 X@foo 2:0d2164f0ce0d
448
448
449 (test that remotely diverged bookmarks are reused if they aren't changed)
449 (test that remotely diverged bookmarks are reused if they aren't changed)
450
450
451 $ hg bookmarks | grep '^ @'
451 $ hg bookmarks | grep '^ @'
452 @ 1:9b140be10808
452 @ 1:9b140be10808
453 @1 2:0d2164f0ce0d
453 @1 2:0d2164f0ce0d
454 @foo 2:0d2164f0ce0d
454 @foo 2:0d2164f0ce0d
455 $ hg pull ../a
455 $ hg pull ../a
456 pulling from ../a
456 pulling from ../a
457 searching for changes
457 searching for changes
458 no changes found
458 no changes found
459 warning: failed to assign numbered name to divergent bookmark X
459 warning: failed to assign numbered name to divergent bookmark X
460 divergent bookmark @ stored as @1
460 divergent bookmark @ stored as @1
461 $ hg bookmarks | grep '^ @'
461 $ hg bookmarks | grep '^ @'
462 @ 1:9b140be10808
462 @ 1:9b140be10808
463 @1 2:0d2164f0ce0d
463 @1 2:0d2164f0ce0d
464 @foo 2:0d2164f0ce0d
464 @foo 2:0d2164f0ce0d
465
465
466 $ $PYTHON $TESTDIR/seq.py 1 100 | while read i; do hg bookmarks -d "X@${i}"; done
466 $ $PYTHON $TESTDIR/seq.py 1 100 | while read i; do hg bookmarks -d "X@${i}"; done
467 $ hg bookmarks -d "@1"
467 $ hg bookmarks -d "@1"
468
468
469 $ hg push -f ../a
469 $ hg push -f ../a
470 pushing to ../a
470 pushing to ../a
471 searching for changes
471 searching for changes
472 adding changesets
472 adding changesets
473 adding manifests
473 adding manifests
474 adding file changes
474 adding file changes
475 added 1 changesets with 1 changes to 1 files (+1 heads)
475 added 1 changesets with 1 changes to 1 files (+1 heads)
476 $ hg -R ../a book
476 $ hg -R ../a book
477 @ 1:0d2164f0ce0d
477 @ 1:0d2164f0ce0d
478 * X 1:0d2164f0ce0d
478 * X 1:0d2164f0ce0d
479 Y 0:4e3505fd9583
479 Y 0:4e3505fd9583
480 Z 1:0d2164f0ce0d
480 Z 1:0d2164f0ce0d
481
481
482 explicit pull should overwrite the local version (issue4439)
482 explicit pull should overwrite the local version (issue4439)
483
483
484 $ hg update -r X
484 $ hg update -r X
485 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
485 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
486 (activating bookmark X)
486 (activating bookmark X)
487 $ hg pull --config paths.foo=../a foo -B . --config "$TESTHOOK"
487 $ hg pull --config paths.foo=../a foo -B . --config "$TESTHOOK"
488 pulling from $TESTTMP/a
488 pulling from $TESTTMP/a
489 no changes found
489 no changes found
490 divergent bookmark @ stored as @foo
490 divergent bookmark @ stored as @foo
491 importing bookmark X
491 importing bookmark X
492 test-hook-bookmark: @foo: 0d2164f0ce0d8f1d6f94351eba04b794909be66c -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
492 test-hook-bookmark: @foo: 0d2164f0ce0d8f1d6f94351eba04b794909be66c -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
493 test-hook-bookmark: X: 9b140be1080824d768c5a4691a564088eede71f9 -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
493 test-hook-bookmark: X: 9b140be1080824d768c5a4691a564088eede71f9 -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
494
494
495 reinstall state for further testing:
495 reinstall state for further testing:
496
496
497 $ hg book -fr 9b140be10808 X
497 $ hg book -fr 9b140be10808 X
498
498
499 revsets should not ignore divergent bookmarks
499 revsets should not ignore divergent bookmarks
500
500
501 $ hg bookmark -fr 1 Z
501 $ hg bookmark -fr 1 Z
502 $ hg log -r 'bookmark()' --template '{rev}:{node|short} {bookmarks}\n'
502 $ hg log -r 'bookmark()' --template '{rev}:{node|short} {bookmarks}\n'
503 0:4e3505fd9583 Y
503 0:4e3505fd9583 Y
504 1:9b140be10808 @ X Z foobar
504 1:9b140be10808 @ X Z foobar
505 2:0d2164f0ce0d @foo X@foo
505 2:0d2164f0ce0d @foo X@foo
506 $ hg log -r 'bookmark("X@foo")' --template '{rev}:{node|short} {bookmarks}\n'
506 $ hg log -r 'bookmark("X@foo")' --template '{rev}:{node|short} {bookmarks}\n'
507 2:0d2164f0ce0d @foo X@foo
507 2:0d2164f0ce0d @foo X@foo
508 $ hg log -r 'bookmark("re:X@foo")' --template '{rev}:{node|short} {bookmarks}\n'
508 $ hg log -r 'bookmark("re:X@foo")' --template '{rev}:{node|short} {bookmarks}\n'
509 2:0d2164f0ce0d @foo X@foo
509 2:0d2164f0ce0d @foo X@foo
510
510
511 update a remote bookmark from a non-head to a head
511 update a remote bookmark from a non-head to a head
512
512
513 $ hg up -q Y
513 $ hg up -q Y
514 $ echo c3 > f2
514 $ echo c3 > f2
515 $ hg ci -Am3
515 $ hg ci -Am3
516 adding f2
516 adding f2
517 created new head
517 created new head
518 $ hg push ../a --config "$TESTHOOK"
518 $ hg push ../a --config "$TESTHOOK"
519 pushing to ../a
519 pushing to ../a
520 searching for changes
520 searching for changes
521 adding changesets
521 adding changesets
522 adding manifests
522 adding manifests
523 adding file changes
523 adding file changes
524 added 1 changesets with 1 changes to 1 files (+1 heads)
524 added 1 changesets with 1 changes to 1 files (+1 heads)
525 test-hook-bookmark: Y: 4e3505fd95835d721066b76e75dbb8cc554d7f77 -> f6fc62dde3c0771e29704af56ba4d8af77abcc2f
525 test-hook-bookmark: Y: 4e3505fd95835d721066b76e75dbb8cc554d7f77 -> f6fc62dde3c0771e29704af56ba4d8af77abcc2f
526 updating bookmark Y
526 updating bookmark Y
527 $ hg -R ../a book
527 $ hg -R ../a book
528 @ 1:0d2164f0ce0d
528 @ 1:0d2164f0ce0d
529 * X 1:0d2164f0ce0d
529 * X 1:0d2164f0ce0d
530 Y 3:f6fc62dde3c0
530 Y 3:f6fc62dde3c0
531 Z 1:0d2164f0ce0d
531 Z 1:0d2164f0ce0d
532
532
533 update a bookmark in the middle of a client pulling changes
533 update a bookmark in the middle of a client pulling changes
534
534
535 $ cd ..
535 $ cd ..
536 $ hg clone -q a pull-race
536 $ hg clone -q a pull-race
537
537
538 We want to use http because it is stateless and therefore more susceptible to
538 We want to use http because it is stateless and therefore more susceptible to
539 race conditions
539 race conditions
540
540
541 $ hg serve -R pull-race -p $HGPORT -d --pid-file=pull-race.pid -E main-error.log
541 $ hg serve -R pull-race -p $HGPORT -d --pid-file=pull-race.pid -E main-error.log
542 $ cat pull-race.pid >> $DAEMON_PIDS
542 $ cat pull-race.pid >> $DAEMON_PIDS
543
543
544 $ cat <<EOF > $TESTTMP/out_makecommit.sh
544 $ cat <<EOF > $TESTTMP/out_makecommit.sh
545 > #!/bin/sh
545 > #!/bin/sh
546 > hg ci -Am5
546 > hg ci -Am5
547 > echo committed in pull-race
547 > echo committed in pull-race
548 > EOF
548 > EOF
549
549
550 $ hg clone -q http://localhost:$HGPORT/ pull-race2 --config "$TESTHOOK"
550 $ hg clone -q http://localhost:$HGPORT/ pull-race2 --config "$TESTHOOK"
551 test-hook-bookmark: @: -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
551 test-hook-bookmark: @: -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
552 test-hook-bookmark: X: -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
552 test-hook-bookmark: X: -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
553 test-hook-bookmark: Y: -> f6fc62dde3c0771e29704af56ba4d8af77abcc2f
553 test-hook-bookmark: Y: -> f6fc62dde3c0771e29704af56ba4d8af77abcc2f
554 test-hook-bookmark: Z: -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
554 test-hook-bookmark: Z: -> 0d2164f0ce0d8f1d6f94351eba04b794909be66c
555 $ cd pull-race
555 $ cd pull-race
556 $ hg up -q Y
556 $ hg up -q Y
557 $ echo c4 > f2
557 $ echo c4 > f2
558 $ hg ci -Am4
558 $ hg ci -Am4
559 $ echo c5 > f3
559 $ echo c5 > f3
560 $ cat <<EOF > .hg/hgrc
560 $ cat <<EOF > .hg/hgrc
561 > [hooks]
561 > [hooks]
562 > outgoing.makecommit = sh $TESTTMP/out_makecommit.sh
562 > outgoing.makecommit = sh $TESTTMP/out_makecommit.sh
563 > EOF
563 > EOF
564
564
565 (new config needs a server restart)
565 (new config needs a server restart)
566
566
567 $ cd ..
567 $ cd ..
568 $ killdaemons.py
568 $ killdaemons.py
569 $ hg serve -R pull-race -p $HGPORT -d --pid-file=pull-race.pid -E main-error.log
569 $ hg serve -R pull-race -p $HGPORT -d --pid-file=pull-race.pid -E main-error.log
570 $ cat pull-race.pid >> $DAEMON_PIDS
570 $ cat pull-race.pid >> $DAEMON_PIDS
571 $ cd pull-race2
571 $ cd pull-race2
572 $ hg -R $TESTTMP/pull-race book
572 $ hg -R $TESTTMP/pull-race book
573 @ 1:0d2164f0ce0d
573 @ 1:0d2164f0ce0d
574 X 1:0d2164f0ce0d
574 X 1:0d2164f0ce0d
575 * Y 4:b0a5eff05604
575 * Y 4:b0a5eff05604
576 Z 1:0d2164f0ce0d
576 Z 1:0d2164f0ce0d
577 $ hg pull
577 $ hg pull
578 pulling from http://localhost:$HGPORT/
578 pulling from http://localhost:$HGPORT/
579 searching for changes
579 searching for changes
580 adding changesets
580 adding changesets
581 adding manifests
581 adding manifests
582 adding file changes
582 adding file changes
583 added 1 changesets with 1 changes to 1 files
583 added 1 changesets with 1 changes to 1 files
584 updating bookmark Y
584 updating bookmark Y
585 new changesets b0a5eff05604
585 new changesets b0a5eff05604
586 (run 'hg update' to get a working copy)
586 (run 'hg update' to get a working copy)
587 $ hg book
587 $ hg book
588 * @ 1:0d2164f0ce0d
588 * @ 1:0d2164f0ce0d
589 X 1:0d2164f0ce0d
589 X 1:0d2164f0ce0d
590 Y 4:b0a5eff05604
590 Y 4:b0a5eff05604
591 Z 1:0d2164f0ce0d
591 Z 1:0d2164f0ce0d
592
592
593 Update a bookmark right after the initial lookup -B (issue4689)
593 Update a bookmark right after the initial lookup -B (issue4689)
594
594
595 $ echo c6 > ../pull-race/f3 # to be committed during the race
595 $ echo c6 > ../pull-race/f3 # to be committed during the race
596 $ cat <<EOF > $TESTTMP/listkeys_makecommit.sh
596 $ cat <<EOF > $TESTTMP/listkeys_makecommit.sh
597 > #!/bin/sh
597 > #!/bin/sh
598 > if hg st | grep -q M; then
598 > if hg st | grep -q M; then
599 > hg commit -m race
599 > hg commit -m race
600 > echo committed in pull-race
600 > echo committed in pull-race
601 > else
601 > else
602 > exit 0
602 > exit 0
603 > fi
603 > fi
604 > EOF
604 > EOF
605 $ cat <<EOF > ../pull-race/.hg/hgrc
605 $ cat <<EOF > ../pull-race/.hg/hgrc
606 > [hooks]
606 > [hooks]
607 > # If anything to commit, commit it right after the first key listing used
607 > # If anything to commit, commit it right after the first key listing used
608 > # during lookup. This makes the commit appear before the actual getbundle
608 > # during lookup. This makes the commit appear before the actual getbundle
609 > # call.
609 > # call.
610 > listkeys.makecommit= sh $TESTTMP/listkeys_makecommit.sh
610 > listkeys.makecommit= sh $TESTTMP/listkeys_makecommit.sh
611 > EOF
611 > EOF
612
612
613 (new config need server restart)
613 (new config need server restart)
614
614
615 $ killdaemons.py
615 $ killdaemons.py
616 $ hg serve -R ../pull-race -p $HGPORT -d --pid-file=../pull-race.pid -E main-error.log
616 $ hg serve -R ../pull-race -p $HGPORT -d --pid-file=../pull-race.pid -E main-error.log
617 $ cat ../pull-race.pid >> $DAEMON_PIDS
617 $ cat ../pull-race.pid >> $DAEMON_PIDS
618
618
619 $ hg -R $TESTTMP/pull-race book
619 $ hg -R $TESTTMP/pull-race book
620 @ 1:0d2164f0ce0d
620 @ 1:0d2164f0ce0d
621 X 1:0d2164f0ce0d
621 X 1:0d2164f0ce0d
622 * Y 5:35d1ef0a8d1b
622 * Y 5:35d1ef0a8d1b
623 Z 1:0d2164f0ce0d
623 Z 1:0d2164f0ce0d
624 $ hg update -r Y
624 $ hg update -r Y
625 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
625 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
626 (activating bookmark Y)
626 (activating bookmark Y)
627 $ hg pull -B .
627 $ hg pull -B .
628 pulling from http://localhost:$HGPORT/
628 pulling from http://localhost:$HGPORT/
629 searching for changes
629 searching for changes
630 adding changesets
630 adding changesets
631 adding manifests
631 adding manifests
632 adding file changes
632 adding file changes
633 added 1 changesets with 1 changes to 1 files
633 added 1 changesets with 1 changes to 1 files
634 updating bookmark Y
634 updating bookmark Y
635 new changesets 35d1ef0a8d1b
635 new changesets 35d1ef0a8d1b
636 (run 'hg update' to get a working copy)
636 (run 'hg update' to get a working copy)
637 $ hg book
637 $ hg book
638 @ 1:0d2164f0ce0d
638 @ 1:0d2164f0ce0d
639 X 1:0d2164f0ce0d
639 X 1:0d2164f0ce0d
640 * Y 5:35d1ef0a8d1b
640 * Y 5:35d1ef0a8d1b
641 Z 1:0d2164f0ce0d
641 Z 1:0d2164f0ce0d
642
642
643 (done with this section of the test)
643 (done with this section of the test)
644
644
645 $ killdaemons.py
645 $ killdaemons.py
646 $ cd ../b
646 $ cd ../b
647
647
648 diverging a remote bookmark fails
648 diverging a remote bookmark fails
649
649
650 $ hg up -q 4e3505fd9583
650 $ hg up -q 4e3505fd9583
651 $ echo c4 > f2
651 $ echo c4 > f2
652 $ hg ci -Am4
652 $ hg ci -Am4
653 adding f2
653 adding f2
654 created new head
654 created new head
655 $ echo c5 > f2
655 $ echo c5 > f2
656 $ hg ci -Am5
656 $ hg ci -Am5
657 $ hg log -G
657 $ hg log -G
658 @ 5:c922c0139ca0 5
658 @ 5:c922c0139ca0 5
659 |
659 |
660 o 4:4efff6d98829 4
660 o 4:4efff6d98829 4
661 |
661 |
662 | o 3:f6fc62dde3c0 3
662 | o 3:f6fc62dde3c0 3
663 |/
663 |/
664 | o 2:0d2164f0ce0d 1
664 | o 2:0d2164f0ce0d 1
665 |/
665 |/
666 | o 1:9b140be10808 2
666 | o 1:9b140be10808 2
667 |/
667 |/
668 o 0:4e3505fd9583 test
668 o 0:4e3505fd9583 test
669
669
670
670
671 $ hg book -f Y
671 $ hg book -f Y
672
672
673 $ cat <<EOF > ../a/.hg/hgrc
673 $ cat <<EOF > ../a/.hg/hgrc
674 > [web]
674 > [web]
675 > push_ssl = false
675 > push_ssl = false
676 > allow_push = *
676 > allow_push = *
677 > EOF
677 > EOF
678
678
679 $ hg serve -R ../a -p $HGPORT2 -d --pid-file=../hg2.pid
679 $ hg serve -R ../a -p $HGPORT2 -d --pid-file=../hg2.pid
680 $ cat ../hg2.pid >> $DAEMON_PIDS
680 $ cat ../hg2.pid >> $DAEMON_PIDS
681
681
682 $ hg push http://localhost:$HGPORT2/
682 $ hg push http://localhost:$HGPORT2/
683 pushing to http://localhost:$HGPORT2/
683 pushing to http://localhost:$HGPORT2/
684 searching for changes
684 searching for changes
685 abort: push creates new remote head c922c0139ca0 with bookmark 'Y'!
685 abort: push creates new remote head c922c0139ca0 with bookmark 'Y'!
686 (merge or see 'hg help push' for details about pushing new heads)
686 (merge or see 'hg help push' for details about pushing new heads)
687 [255]
687 [255]
688 $ hg -R ../a book
688 $ hg -R ../a book
689 @ 1:0d2164f0ce0d
689 @ 1:0d2164f0ce0d
690 * X 1:0d2164f0ce0d
690 * X 1:0d2164f0ce0d
691 Y 3:f6fc62dde3c0
691 Y 3:f6fc62dde3c0
692 Z 1:0d2164f0ce0d
692 Z 1:0d2164f0ce0d
693
693
694
694
695 Unrelated marker does not alter the decision
695 Unrelated marker does not alter the decision
696
696
697 $ hg debugobsolete aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
697 $ hg debugobsolete aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
698 $ hg push http://localhost:$HGPORT2/
698 $ hg push http://localhost:$HGPORT2/
699 pushing to http://localhost:$HGPORT2/
699 pushing to http://localhost:$HGPORT2/
700 searching for changes
700 searching for changes
701 abort: push creates new remote head c922c0139ca0 with bookmark 'Y'!
701 abort: push creates new remote head c922c0139ca0 with bookmark 'Y'!
702 (merge or see 'hg help push' for details about pushing new heads)
702 (merge or see 'hg help push' for details about pushing new heads)
703 [255]
703 [255]
704 $ hg -R ../a book
704 $ hg -R ../a book
705 @ 1:0d2164f0ce0d
705 @ 1:0d2164f0ce0d
706 * X 1:0d2164f0ce0d
706 * X 1:0d2164f0ce0d
707 Y 3:f6fc62dde3c0
707 Y 3:f6fc62dde3c0
708 Z 1:0d2164f0ce0d
708 Z 1:0d2164f0ce0d
709
709
710 Update to a successor works
710 Update to a successor works
711
711
712 $ hg id --debug -r 3
712 $ hg id --debug -r 3
713 f6fc62dde3c0771e29704af56ba4d8af77abcc2f
713 f6fc62dde3c0771e29704af56ba4d8af77abcc2f
714 $ hg id --debug -r 4
714 $ hg id --debug -r 4
715 4efff6d98829d9c824c621afd6e3f01865f5439f
715 4efff6d98829d9c824c621afd6e3f01865f5439f
716 $ hg id --debug -r 5
716 $ hg id --debug -r 5
717 c922c0139ca03858f655e4a2af4dd02796a63969 tip Y
717 c922c0139ca03858f655e4a2af4dd02796a63969 tip Y
718 $ hg debugobsolete f6fc62dde3c0771e29704af56ba4d8af77abcc2f cccccccccccccccccccccccccccccccccccccccc
718 $ hg debugobsolete f6fc62dde3c0771e29704af56ba4d8af77abcc2f cccccccccccccccccccccccccccccccccccccccc
719 obsoleted 1 changesets
719 obsoleted 1 changesets
720 $ hg debugobsolete cccccccccccccccccccccccccccccccccccccccc 4efff6d98829d9c824c621afd6e3f01865f5439f
720 $ hg debugobsolete cccccccccccccccccccccccccccccccccccccccc 4efff6d98829d9c824c621afd6e3f01865f5439f
721 $ hg push http://localhost:$HGPORT2/
721 $ hg push http://localhost:$HGPORT2/
722 pushing to http://localhost:$HGPORT2/
722 pushing to http://localhost:$HGPORT2/
723 searching for changes
723 searching for changes
724 remote: adding changesets
724 remote: adding changesets
725 remote: adding manifests
725 remote: adding manifests
726 remote: adding file changes
726 remote: adding file changes
727 remote: added 2 changesets with 2 changes to 1 files (+1 heads)
727 remote: added 2 changesets with 2 changes to 1 files (+1 heads)
728 remote: 2 new obsolescence markers
728 remote: 2 new obsolescence markers
729 remote: obsoleted 1 changesets
729 remote: obsoleted 1 changesets
730 updating bookmark Y
730 updating bookmark Y
731 $ hg -R ../a book
731 $ hg -R ../a book
732 @ 1:0d2164f0ce0d
732 @ 1:0d2164f0ce0d
733 * X 1:0d2164f0ce0d
733 * X 1:0d2164f0ce0d
734 Y 5:c922c0139ca0
734 Y 5:c922c0139ca0
735 Z 1:0d2164f0ce0d
735 Z 1:0d2164f0ce0d
736
736
737 hgweb
737 hgweb
738
738
739 $ cat <<EOF > .hg/hgrc
739 $ cat <<EOF > .hg/hgrc
740 > [web]
740 > [web]
741 > push_ssl = false
741 > push_ssl = false
742 > allow_push = *
742 > allow_push = *
743 > EOF
743 > EOF
744
744
745 $ hg serve -p $HGPORT -d --pid-file=../hg.pid -E errors.log
745 $ hg serve -p $HGPORT -d --pid-file=../hg.pid -E errors.log
746 $ cat ../hg.pid >> $DAEMON_PIDS
746 $ cat ../hg.pid >> $DAEMON_PIDS
747 $ cd ../a
747 $ cd ../a
748
748
749 $ hg debugpushkey http://localhost:$HGPORT/ namespaces
749 $ hg debugpushkey http://localhost:$HGPORT/ namespaces
750 bookmarks
750 bookmarks
751 namespaces
751 namespaces
752 obsolete
752 obsolete
753 phases
753 phases
754 $ hg debugpushkey http://localhost:$HGPORT/ bookmarks
754 $ hg debugpushkey http://localhost:$HGPORT/ bookmarks
755 @ 9b140be1080824d768c5a4691a564088eede71f9
755 @ 9b140be1080824d768c5a4691a564088eede71f9
756 X 9b140be1080824d768c5a4691a564088eede71f9
756 X 9b140be1080824d768c5a4691a564088eede71f9
757 Y c922c0139ca03858f655e4a2af4dd02796a63969
757 Y c922c0139ca03858f655e4a2af4dd02796a63969
758 Z 9b140be1080824d768c5a4691a564088eede71f9
758 Z 9b140be1080824d768c5a4691a564088eede71f9
759 foo 0000000000000000000000000000000000000000
759 foo 0000000000000000000000000000000000000000
760 foobar 9b140be1080824d768c5a4691a564088eede71f9
760 foobar 9b140be1080824d768c5a4691a564088eede71f9
761 $ hg out -B http://localhost:$HGPORT/
761 $ hg out -B http://localhost:$HGPORT/
762 comparing with http://localhost:$HGPORT/
762 comparing with http://localhost:$HGPORT/
763 searching for changed bookmarks
763 searching for changed bookmarks
764 @ 0d2164f0ce0d
764 @ 0d2164f0ce0d
765 X 0d2164f0ce0d
765 X 0d2164f0ce0d
766 Z 0d2164f0ce0d
766 Z 0d2164f0ce0d
767 foo
767 foo
768 foobar
768 foobar
769 $ hg push -B Z http://localhost:$HGPORT/
769 $ hg push -B Z http://localhost:$HGPORT/
770 pushing to http://localhost:$HGPORT/
770 pushing to http://localhost:$HGPORT/
771 searching for changes
771 searching for changes
772 no changes found
772 no changes found
773 updating bookmark Z
773 updating bookmark Z
774 [1]
774 [1]
775 $ hg book -d Z
775 $ hg book -d Z
776 $ hg in -B http://localhost:$HGPORT/
776 $ hg in -B http://localhost:$HGPORT/
777 comparing with http://localhost:$HGPORT/
777 comparing with http://localhost:$HGPORT/
778 searching for changed bookmarks
778 searching for changed bookmarks
779 @ 9b140be10808
779 @ 9b140be10808
780 X 9b140be10808
780 X 9b140be10808
781 Z 0d2164f0ce0d
781 Z 0d2164f0ce0d
782 foo 000000000000
782 foo 000000000000
783 foobar 9b140be10808
783 foobar 9b140be10808
784 $ hg pull -B Z http://localhost:$HGPORT/
784 $ hg pull -B Z http://localhost:$HGPORT/
785 pulling from http://localhost:$HGPORT/
785 pulling from http://localhost:$HGPORT/
786 no changes found
786 no changes found
787 divergent bookmark @ stored as @1
787 divergent bookmark @ stored as @1
788 divergent bookmark X stored as X@1
788 divergent bookmark X stored as X@1
789 adding remote bookmark Z
789 adding remote bookmark Z
790 adding remote bookmark foo
790 adding remote bookmark foo
791 adding remote bookmark foobar
791 adding remote bookmark foobar
792 $ hg clone http://localhost:$HGPORT/ cloned-bookmarks
792 $ hg clone http://localhost:$HGPORT/ cloned-bookmarks
793 requesting all changes
793 requesting all changes
794 adding changesets
794 adding changesets
795 adding manifests
795 adding manifests
796 adding file changes
796 adding file changes
797 added 5 changesets with 5 changes to 3 files (+2 heads)
797 added 5 changesets with 5 changes to 3 files (+2 heads)
798 2 new obsolescence markers
798 2 new obsolescence markers
799 new changesets 4e3505fd9583:c922c0139ca0
799 new changesets 4e3505fd9583:c922c0139ca0
800 updating to bookmark @
800 updating to bookmark @
801 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
801 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
802 $ hg -R cloned-bookmarks bookmarks
802 $ hg -R cloned-bookmarks bookmarks
803 * @ 1:9b140be10808
803 * @ 1:9b140be10808
804 X 1:9b140be10808
804 X 1:9b140be10808
805 Y 4:c922c0139ca0
805 Y 4:c922c0139ca0
806 Z 2:0d2164f0ce0d
806 Z 2:0d2164f0ce0d
807 foo -1:000000000000
807 foo -1:000000000000
808 foobar 1:9b140be10808
808 foobar 1:9b140be10808
809
809
810 $ cd ..
810 $ cd ..
811
811
812 Test to show result of bookmarks comparison
812 Test to show result of bookmarks comparison
813
813
814 $ mkdir bmcomparison
814 $ mkdir bmcomparison
815 $ cd bmcomparison
815 $ cd bmcomparison
816
816
817 $ hg init source
817 $ hg init source
818 $ hg -R source debugbuilddag '+2*2*3*4'
818 $ hg -R source debugbuilddag '+2*2*3*4'
819 $ hg -R source log -G --template '{rev}:{node|short}'
819 $ hg -R source log -G --template '{rev}:{node|short}'
820 o 4:e7bd5218ca15
820 o 4:e7bd5218ca15
821 |
821 |
822 | o 3:6100d3090acf
822 | o 3:6100d3090acf
823 |/
823 |/
824 | o 2:fa942426a6fd
824 | o 2:fa942426a6fd
825 |/
825 |/
826 | o 1:66f7d451a68b
826 | o 1:66f7d451a68b
827 |/
827 |/
828 o 0:1ea73414a91b
828 o 0:1ea73414a91b
829
829
830 $ hg -R source bookmarks -r 0 SAME
830 $ hg -R source bookmarks -r 0 SAME
831 $ hg -R source bookmarks -r 0 ADV_ON_REPO1
831 $ hg -R source bookmarks -r 0 ADV_ON_REPO1
832 $ hg -R source bookmarks -r 0 ADV_ON_REPO2
832 $ hg -R source bookmarks -r 0 ADV_ON_REPO2
833 $ hg -R source bookmarks -r 0 DIFF_ADV_ON_REPO1
833 $ hg -R source bookmarks -r 0 DIFF_ADV_ON_REPO1
834 $ hg -R source bookmarks -r 0 DIFF_ADV_ON_REPO2
834 $ hg -R source bookmarks -r 0 DIFF_ADV_ON_REPO2
835 $ hg -R source bookmarks -r 1 DIVERGED
835 $ hg -R source bookmarks -r 1 DIVERGED
836
836
837 $ hg clone -U source repo1
837 $ hg clone -U source repo1
838
838
839 (test that incoming/outgoing exit with 1, if there is no bookmark to
839 (test that incoming/outgoing exit with 1, if there is no bookmark to
840 be exchanged)
840 be exchanged)
841
841
842 $ hg -R repo1 incoming -B
842 $ hg -R repo1 incoming -B
843 comparing with $TESTTMP/bmcomparison/source
843 comparing with $TESTTMP/bmcomparison/source
844 searching for changed bookmarks
844 searching for changed bookmarks
845 no changed bookmarks found
845 no changed bookmarks found
846 [1]
846 [1]
847 $ hg -R repo1 outgoing -B
847 $ hg -R repo1 outgoing -B
848 comparing with $TESTTMP/bmcomparison/source
848 comparing with $TESTTMP/bmcomparison/source
849 searching for changed bookmarks
849 searching for changed bookmarks
850 no changed bookmarks found
850 no changed bookmarks found
851 [1]
851 [1]
852
852
853 $ hg -R repo1 bookmarks -f -r 1 ADD_ON_REPO1
853 $ hg -R repo1 bookmarks -f -r 1 ADD_ON_REPO1
854 $ hg -R repo1 bookmarks -f -r 2 ADV_ON_REPO1
854 $ hg -R repo1 bookmarks -f -r 2 ADV_ON_REPO1
855 $ hg -R repo1 bookmarks -f -r 3 DIFF_ADV_ON_REPO1
855 $ hg -R repo1 bookmarks -f -r 3 DIFF_ADV_ON_REPO1
856 $ hg -R repo1 bookmarks -f -r 3 DIFF_DIVERGED
856 $ hg -R repo1 bookmarks -f -r 3 DIFF_DIVERGED
857 $ hg -R repo1 -q --config extensions.mq= strip 4
857 $ hg -R repo1 -q --config extensions.mq= strip 4
858 $ hg -R repo1 log -G --template '{node|short} ({bookmarks})'
858 $ hg -R repo1 log -G --template '{node|short} ({bookmarks})'
859 o 6100d3090acf (DIFF_ADV_ON_REPO1 DIFF_DIVERGED)
859 o 6100d3090acf (DIFF_ADV_ON_REPO1 DIFF_DIVERGED)
860 |
860 |
861 | o fa942426a6fd (ADV_ON_REPO1)
861 | o fa942426a6fd (ADV_ON_REPO1)
862 |/
862 |/
863 | o 66f7d451a68b (ADD_ON_REPO1 DIVERGED)
863 | o 66f7d451a68b (ADD_ON_REPO1 DIVERGED)
864 |/
864 |/
865 o 1ea73414a91b (ADV_ON_REPO2 DIFF_ADV_ON_REPO2 SAME)
865 o 1ea73414a91b (ADV_ON_REPO2 DIFF_ADV_ON_REPO2 SAME)
866
866
867
867
868 $ hg clone -U source repo2
868 $ hg clone -U source repo2
869 $ hg -R repo2 bookmarks -f -r 1 ADD_ON_REPO2
869 $ hg -R repo2 bookmarks -f -r 1 ADD_ON_REPO2
870 $ hg -R repo2 bookmarks -f -r 1 ADV_ON_REPO2
870 $ hg -R repo2 bookmarks -f -r 1 ADV_ON_REPO2
871 $ hg -R repo2 bookmarks -f -r 2 DIVERGED
871 $ hg -R repo2 bookmarks -f -r 2 DIVERGED
872 $ hg -R repo2 bookmarks -f -r 4 DIFF_ADV_ON_REPO2
872 $ hg -R repo2 bookmarks -f -r 4 DIFF_ADV_ON_REPO2
873 $ hg -R repo2 bookmarks -f -r 4 DIFF_DIVERGED
873 $ hg -R repo2 bookmarks -f -r 4 DIFF_DIVERGED
874 $ hg -R repo2 -q --config extensions.mq= strip 3
874 $ hg -R repo2 -q --config extensions.mq= strip 3
875 $ hg -R repo2 log -G --template '{node|short} ({bookmarks})'
875 $ hg -R repo2 log -G --template '{node|short} ({bookmarks})'
876 o e7bd5218ca15 (DIFF_ADV_ON_REPO2 DIFF_DIVERGED)
876 o e7bd5218ca15 (DIFF_ADV_ON_REPO2 DIFF_DIVERGED)
877 |
877 |
878 | o fa942426a6fd (DIVERGED)
878 | o fa942426a6fd (DIVERGED)
879 |/
879 |/
880 | o 66f7d451a68b (ADD_ON_REPO2 ADV_ON_REPO2)
880 | o 66f7d451a68b (ADD_ON_REPO2 ADV_ON_REPO2)
881 |/
881 |/
882 o 1ea73414a91b (ADV_ON_REPO1 DIFF_ADV_ON_REPO1 SAME)
882 o 1ea73414a91b (ADV_ON_REPO1 DIFF_ADV_ON_REPO1 SAME)
883
883
884
884
885 (test that difference of bookmarks between repositories are fully shown)
885 (test that difference of bookmarks between repositories are fully shown)
886
886
887 $ hg -R repo1 incoming -B repo2 -v
887 $ hg -R repo1 incoming -B repo2 -v
888 comparing with repo2
888 comparing with repo2
889 searching for changed bookmarks
889 searching for changed bookmarks
890 ADD_ON_REPO2 66f7d451a68b added
890 ADD_ON_REPO2 66f7d451a68b added
891 ADV_ON_REPO2 66f7d451a68b advanced
891 ADV_ON_REPO2 66f7d451a68b advanced
892 DIFF_ADV_ON_REPO2 e7bd5218ca15 changed
892 DIFF_ADV_ON_REPO2 e7bd5218ca15 changed
893 DIFF_DIVERGED e7bd5218ca15 changed
893 DIFF_DIVERGED e7bd5218ca15 changed
894 DIVERGED fa942426a6fd diverged
894 DIVERGED fa942426a6fd diverged
895 $ hg -R repo1 outgoing -B repo2 -v
895 $ hg -R repo1 outgoing -B repo2 -v
896 comparing with repo2
896 comparing with repo2
897 searching for changed bookmarks
897 searching for changed bookmarks
898 ADD_ON_REPO1 66f7d451a68b added
898 ADD_ON_REPO1 66f7d451a68b added
899 ADD_ON_REPO2 deleted
899 ADD_ON_REPO2 deleted
900 ADV_ON_REPO1 fa942426a6fd advanced
900 ADV_ON_REPO1 fa942426a6fd advanced
901 DIFF_ADV_ON_REPO1 6100d3090acf advanced
901 DIFF_ADV_ON_REPO1 6100d3090acf advanced
902 DIFF_ADV_ON_REPO2 1ea73414a91b changed
902 DIFF_ADV_ON_REPO2 1ea73414a91b changed
903 DIFF_DIVERGED 6100d3090acf changed
903 DIFF_DIVERGED 6100d3090acf changed
904 DIVERGED 66f7d451a68b diverged
904 DIVERGED 66f7d451a68b diverged
905
905
906 $ hg -R repo2 incoming -B repo1 -v
906 $ hg -R repo2 incoming -B repo1 -v
907 comparing with repo1
907 comparing with repo1
908 searching for changed bookmarks
908 searching for changed bookmarks
909 ADD_ON_REPO1 66f7d451a68b added
909 ADD_ON_REPO1 66f7d451a68b added
910 ADV_ON_REPO1 fa942426a6fd advanced
910 ADV_ON_REPO1 fa942426a6fd advanced
911 DIFF_ADV_ON_REPO1 6100d3090acf changed
911 DIFF_ADV_ON_REPO1 6100d3090acf changed
912 DIFF_DIVERGED 6100d3090acf changed
912 DIFF_DIVERGED 6100d3090acf changed
913 DIVERGED 66f7d451a68b diverged
913 DIVERGED 66f7d451a68b diverged
914 $ hg -R repo2 outgoing -B repo1 -v
914 $ hg -R repo2 outgoing -B repo1 -v
915 comparing with repo1
915 comparing with repo1
916 searching for changed bookmarks
916 searching for changed bookmarks
917 ADD_ON_REPO1 deleted
917 ADD_ON_REPO1 deleted
918 ADD_ON_REPO2 66f7d451a68b added
918 ADD_ON_REPO2 66f7d451a68b added
919 ADV_ON_REPO2 66f7d451a68b advanced
919 ADV_ON_REPO2 66f7d451a68b advanced
920 DIFF_ADV_ON_REPO1 1ea73414a91b changed
920 DIFF_ADV_ON_REPO1 1ea73414a91b changed
921 DIFF_ADV_ON_REPO2 e7bd5218ca15 advanced
921 DIFF_ADV_ON_REPO2 e7bd5218ca15 advanced
922 DIFF_DIVERGED e7bd5218ca15 changed
922 DIFF_DIVERGED e7bd5218ca15 changed
923 DIVERGED fa942426a6fd diverged
923 DIVERGED fa942426a6fd diverged
924
924
925 $ cd ..
925 $ cd ..
926
926
927 Pushing a bookmark should only push the changes required by that
927 Pushing a bookmark should only push the changes required by that
928 bookmark, not all outgoing changes:
928 bookmark, not all outgoing changes:
929 $ hg clone http://localhost:$HGPORT/ addmarks
929 $ hg clone http://localhost:$HGPORT/ addmarks
930 requesting all changes
930 requesting all changes
931 adding changesets
931 adding changesets
932 adding manifests
932 adding manifests
933 adding file changes
933 adding file changes
934 added 5 changesets with 5 changes to 3 files (+2 heads)
934 added 5 changesets with 5 changes to 3 files (+2 heads)
935 2 new obsolescence markers
935 2 new obsolescence markers
936 new changesets 4e3505fd9583:c922c0139ca0
936 new changesets 4e3505fd9583:c922c0139ca0
937 updating to bookmark @
937 updating to bookmark @
938 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
938 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
939 $ cd addmarks
939 $ cd addmarks
940 $ echo foo > foo
940 $ echo foo > foo
941 $ hg add foo
941 $ hg add foo
942 $ hg commit -m 'add foo'
942 $ hg commit -m 'add foo'
943 $ echo bar > bar
943 $ echo bar > bar
944 $ hg add bar
944 $ hg add bar
945 $ hg commit -m 'add bar'
945 $ hg commit -m 'add bar'
946 $ hg co "tip^"
946 $ hg co "tip^"
947 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
947 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
948 (leaving bookmark @)
948 (leaving bookmark @)
949 $ hg book add-foo
949 $ hg book add-foo
950 $ hg book -r tip add-bar
950 $ hg book -r tip add-bar
951 Note: this push *must* push only a single changeset, as that's the point
951 Note: this push *must* push only a single changeset, as that's the point
952 of this test.
952 of this test.
953 $ hg push -B add-foo --traceback
953 $ hg push -B add-foo --traceback
954 pushing to http://localhost:$HGPORT/
954 pushing to http://localhost:$HGPORT/
955 searching for changes
955 searching for changes
956 remote: adding changesets
956 remote: adding changesets
957 remote: adding manifests
957 remote: adding manifests
958 remote: adding file changes
958 remote: adding file changes
959 remote: added 1 changesets with 1 changes to 1 files
959 remote: added 1 changesets with 1 changes to 1 files
960 exporting bookmark add-foo
960 exporting bookmark add-foo
961
961
962 pushing a new bookmark on a new head does not require -f if -B is specified
962 pushing a new bookmark on a new head does not require -f if -B is specified
963
963
964 $ hg up -q X
964 $ hg up -q X
965 $ hg book W
965 $ hg book W
966 $ echo c5 > f2
966 $ echo c5 > f2
967 $ hg ci -Am5
967 $ hg ci -Am5
968 created new head
968 created new head
969 $ hg push -B .
969 $ hg push -B .
970 pushing to http://localhost:$HGPORT/
970 pushing to http://localhost:$HGPORT/
971 searching for changes
971 searching for changes
972 remote: adding changesets
972 remote: adding changesets
973 remote: adding manifests
973 remote: adding manifests
974 remote: adding file changes
974 remote: adding file changes
975 remote: added 1 changesets with 1 changes to 1 files (+1 heads)
975 remote: added 1 changesets with 1 changes to 1 files (+1 heads)
976 exporting bookmark W
976 exporting bookmark W
977 $ hg -R ../b id -r W
977 $ hg -R ../b id -r W
978 cc978a373a53 tip W
978 cc978a373a53 tip W
979
979
980 pushing an existing but divergent bookmark with -B still requires -f
980 pushing an existing but divergent bookmark with -B still requires -f
981
981
982 $ hg clone -q . ../r
982 $ hg clone -q . ../r
983 $ hg up -q X
983 $ hg up -q X
984 $ echo 1 > f2
984 $ echo 1 > f2
985 $ hg ci -qAml
985 $ hg ci -qAml
986
986
987 $ cd ../r
987 $ cd ../r
988 $ hg up -q X
988 $ hg up -q X
989 $ echo 2 > f2
989 $ echo 2 > f2
990 $ hg ci -qAmr
990 $ hg ci -qAmr
991 $ hg push -B X
991 $ hg push -B X
992 pushing to $TESTTMP/addmarks
992 pushing to $TESTTMP/addmarks
993 searching for changes
993 searching for changes
994 remote has heads on branch 'default' that are not known locally: a2a606d9ff1b
994 remote has heads on branch 'default' that are not known locally: a2a606d9ff1b
995 abort: push creates new remote head 54694f811df9 with bookmark 'X'!
995 abort: push creates new remote head 54694f811df9 with bookmark 'X'!
996 (pull and merge or see 'hg help push' for details about pushing new heads)
996 (pull and merge or see 'hg help push' for details about pushing new heads)
997 [255]
997 [255]
998 $ cd ../addmarks
998 $ cd ../addmarks
999
999
1000 Check summary output for incoming/outgoing bookmarks
1000 Check summary output for incoming/outgoing bookmarks
1001
1001
1002 $ hg bookmarks -d X
1002 $ hg bookmarks -d X
1003 $ hg bookmarks -d Y
1003 $ hg bookmarks -d Y
1004 $ hg summary --remote | grep '^remote:'
1004 $ hg summary --remote | grep '^remote:'
1005 remote: *, 2 incoming bookmarks, 1 outgoing bookmarks (glob)
1005 remote: *, 2 incoming bookmarks, 1 outgoing bookmarks (glob)
1006
1006
1007 $ cd ..
1007 $ cd ..
1008
1008
1009 pushing an unchanged bookmark should result in no changes
1009 pushing an unchanged bookmark should result in no changes
1010
1010
1011 $ hg init unchanged-a
1011 $ hg init unchanged-a
1012 $ hg init unchanged-b
1012 $ hg init unchanged-b
1013 $ cd unchanged-a
1013 $ cd unchanged-a
1014 $ echo initial > foo
1014 $ echo initial > foo
1015 $ hg commit -A -m initial
1015 $ hg commit -A -m initial
1016 adding foo
1016 adding foo
1017 $ hg bookmark @
1017 $ hg bookmark @
1018 $ hg push -B @ ../unchanged-b
1018 $ hg push -B @ ../unchanged-b
1019 pushing to ../unchanged-b
1019 pushing to ../unchanged-b
1020 searching for changes
1020 searching for changes
1021 adding changesets
1021 adding changesets
1022 adding manifests
1022 adding manifests
1023 adding file changes
1023 adding file changes
1024 added 1 changesets with 1 changes to 1 files
1024 added 1 changesets with 1 changes to 1 files
1025 exporting bookmark @
1025 exporting bookmark @
1026
1026
1027 $ hg push -B @ ../unchanged-b
1027 $ hg push -B @ ../unchanged-b
1028 pushing to ../unchanged-b
1028 pushing to ../unchanged-b
1029 searching for changes
1029 searching for changes
1030 no changes found
1030 no changes found
1031 [1]
1031 [1]
1032
1032
1033
1033
1034 Check hook preventing push (issue4455)
1034 Check hook preventing push (issue4455)
1035 ======================================
1035 ======================================
1036
1036
1037 $ hg bookmarks
1037 $ hg bookmarks
1038 * @ 0:55482a6fb4b1
1038 * @ 0:55482a6fb4b1
1039 $ hg log -G
1039 $ hg log -G
1040 @ 0:55482a6fb4b1 initial
1040 @ 0:55482a6fb4b1 initial
1041
1041
1042 $ hg init ../issue4455-dest
1042 $ hg init ../issue4455-dest
1043 $ hg push ../issue4455-dest # changesets only
1043 $ hg push ../issue4455-dest # changesets only
1044 pushing to ../issue4455-dest
1044 pushing to ../issue4455-dest
1045 searching for changes
1045 searching for changes
1046 adding changesets
1046 adding changesets
1047 adding manifests
1047 adding manifests
1048 adding file changes
1048 adding file changes
1049 added 1 changesets with 1 changes to 1 files
1049 added 1 changesets with 1 changes to 1 files
1050 $ cat >> .hg/hgrc << EOF
1050 $ cat >> .hg/hgrc << EOF
1051 > [paths]
1051 > [paths]
1052 > local=../issue4455-dest/
1052 > local=../issue4455-dest/
1053 > ssh=ssh://user@dummy/issue4455-dest
1053 > ssh=ssh://user@dummy/issue4455-dest
1054 > http=http://localhost:$HGPORT/
1054 > http=http://localhost:$HGPORT/
1055 > [ui]
1055 > [ui]
1056 > ssh=$PYTHON "$TESTDIR/dummyssh"
1056 > ssh=$PYTHON "$TESTDIR/dummyssh"
1057 > EOF
1057 > EOF
1058 $ cat >> ../issue4455-dest/.hg/hgrc << EOF
1058 $ cat >> ../issue4455-dest/.hg/hgrc << EOF
1059 > [hooks]
1059 > [hooks]
1060 > prepushkey=false
1060 > prepushkey=false
1061 > [web]
1061 > [web]
1062 > push_ssl = false
1062 > push_ssl = false
1063 > allow_push = *
1063 > allow_push = *
1064 > EOF
1064 > EOF
1065 $ killdaemons.py
1065 $ killdaemons.py
1066 $ hg serve -R ../issue4455-dest -p $HGPORT -d --pid-file=../issue4455.pid -E ../issue4455-error.log
1066 $ hg serve -R ../issue4455-dest -p $HGPORT -d --pid-file=../issue4455.pid -E ../issue4455-error.log
1067 $ cat ../issue4455.pid >> $DAEMON_PIDS
1067 $ cat ../issue4455.pid >> $DAEMON_PIDS
1068
1068
1069 Local push
1069 Local push
1070 ----------
1070 ----------
1071
1071
1072 #if b2-pushkey
1072 #if b2-pushkey
1073
1073
1074 $ hg push -B @ local
1074 $ hg push -B @ local
1075 pushing to $TESTTMP/issue4455-dest
1075 pushing to $TESTTMP/issue4455-dest
1076 searching for changes
1076 searching for changes
1077 no changes found
1077 no changes found
1078 pushkey-abort: prepushkey hook exited with status 1
1078 pushkey-abort: prepushkey hook exited with status 1
1079 abort: exporting bookmark @ failed!
1079 abort: exporting bookmark @ failed!
1080 [255]
1080 [255]
1081
1081
1082 #endif
1082 #endif
1083 #if b2-binary
1083 #if b2-binary
1084
1084
1085 $ hg push -B @ local
1085 $ hg push -B @ local
1086 pushing to $TESTTMP/issue4455-dest
1086 pushing to $TESTTMP/issue4455-dest
1087 searching for changes
1087 searching for changes
1088 no changes found
1088 no changes found
1089 abort: prepushkey hook exited with status 1
1089 abort: prepushkey hook exited with status 1
1090 [255]
1090 [255]
1091
1091
1092 #endif
1092 #endif
1093
1093
1094 $ hg -R ../issue4455-dest/ bookmarks
1094 $ hg -R ../issue4455-dest/ bookmarks
1095 no bookmarks set
1095 no bookmarks set
1096
1096
1097 Using ssh
1097 Using ssh
1098 ---------
1098 ---------
1099
1099
1100 #if b2-pushkey
1100 #if b2-pushkey
1101
1101
1102 $ hg push -B @ ssh # bundle2+
1102 $ hg push -B @ ssh # bundle2+
1103 pushing to ssh://user@dummy/issue4455-dest
1103 pushing to ssh://user@dummy/issue4455-dest
1104 searching for changes
1104 searching for changes
1105 no changes found
1105 no changes found
1106 remote: pushkey-abort: prepushkey hook exited with status 1
1106 remote: pushkey-abort: prepushkey hook exited with status 1
1107 abort: exporting bookmark @ failed!
1107 abort: exporting bookmark @ failed!
1108 [255]
1108 [255]
1109
1109
1110 $ hg -R ../issue4455-dest/ bookmarks
1110 $ hg -R ../issue4455-dest/ bookmarks
1111 no bookmarks set
1111 no bookmarks set
1112
1112
1113 $ hg push -B @ ssh --config devel.legacy.exchange=bundle1
1113 $ hg push -B @ ssh --config devel.legacy.exchange=bundle1
1114 pushing to ssh://user@dummy/issue4455-dest
1114 pushing to ssh://user@dummy/issue4455-dest
1115 searching for changes
1115 searching for changes
1116 no changes found
1116 no changes found
1117 remote: pushkey-abort: prepushkey hook exited with status 1
1117 remote: pushkey-abort: prepushkey hook exited with status 1
1118 exporting bookmark @ failed!
1118 exporting bookmark @ failed!
1119 [1]
1119 [1]
1120
1120
1121 #endif
1121 #endif
1122 #if b2-binary
1122 #if b2-binary
1123
1123
1124 $ hg push -B @ ssh # bundle2+
1124 $ hg push -B @ ssh # bundle2+
1125 pushing to ssh://user@dummy/issue4455-dest
1125 pushing to ssh://user@dummy/issue4455-dest
1126 searching for changes
1126 searching for changes
1127 no changes found
1127 no changes found
1128 remote: prepushkey hook exited with status 1
1128 remote: prepushkey hook exited with status 1
1129 abort: push failed on remote
1129 abort: push failed on remote
1130 [255]
1130 [255]
1131
1131
1132 #endif
1132 #endif
1133
1133
1134 $ hg -R ../issue4455-dest/ bookmarks
1134 $ hg -R ../issue4455-dest/ bookmarks
1135 no bookmarks set
1135 no bookmarks set
1136
1136
1137 Using http
1137 Using http
1138 ----------
1138 ----------
1139
1139
1140 #if b2-pushkey
1140 #if b2-pushkey
1141 $ hg push -B @ http # bundle2+
1141 $ hg push -B @ http # bundle2+
1142 pushing to http://localhost:$HGPORT/
1142 pushing to http://localhost:$HGPORT/
1143 searching for changes
1143 searching for changes
1144 no changes found
1144 no changes found
1145 remote: pushkey-abort: prepushkey hook exited with status 1
1145 remote: pushkey-abort: prepushkey hook exited with status 1
1146 abort: exporting bookmark @ failed!
1146 abort: exporting bookmark @ failed!
1147 [255]
1147 [255]
1148
1148
1149 $ hg -R ../issue4455-dest/ bookmarks
1149 $ hg -R ../issue4455-dest/ bookmarks
1150 no bookmarks set
1150 no bookmarks set
1151
1151
1152 $ hg push -B @ http --config devel.legacy.exchange=bundle1
1152 $ hg push -B @ http --config devel.legacy.exchange=bundle1
1153 pushing to http://localhost:$HGPORT/
1153 pushing to http://localhost:$HGPORT/
1154 searching for changes
1154 searching for changes
1155 no changes found
1155 no changes found
1156 remote: pushkey-abort: prepushkey hook exited with status 1
1156 remote: pushkey-abort: prepushkey hook exited with status 1
1157 exporting bookmark @ failed!
1157 exporting bookmark @ failed!
1158 [1]
1158 [1]
1159
1159
1160 #endif
1160 #endif
1161
1161
1162 #if b2-binary
1162 #if b2-binary
1163
1163
1164 $ hg push -B @ ssh # bundle2+
1164 $ hg push -B @ ssh # bundle2+
1165 pushing to ssh://user@dummy/issue4455-dest
1165 pushing to ssh://user@dummy/issue4455-dest
1166 searching for changes
1166 searching for changes
1167 no changes found
1167 no changes found
1168 remote: prepushkey hook exited with status 1
1168 remote: prepushkey hook exited with status 1
1169 abort: push failed on remote
1169 abort: push failed on remote
1170 [255]
1170 [255]
1171
1171
1172 #endif
1172 #endif
1173
1173
1174 $ hg -R ../issue4455-dest/ bookmarks
1174 $ hg -R ../issue4455-dest/ bookmarks
1175 no bookmarks set
1175 no bookmarks set
1176
1177 $ cd ..
1178
1179 Test that pre-pushkey compat for bookmark works as expected (issue5777)
1180
1181 $ cat << EOF >> $HGRCPATH
1182 > [ui]
1183 > ssh="$PYTHON" "$TESTDIR/dummyssh"
1184 > [server]
1185 > bookmarks-pushkey-compat = yes
1186 > EOF
1187
1188 $ hg init server
1189 $ echo foo > server/a
1190 $ hg -R server book foo
1191 $ hg -R server commit -Am a
1192 adding a
1193 $ hg clone ssh://user@dummy/server client
1194 requesting all changes
1195 adding changesets
1196 adding manifests
1197 adding file changes
1198 added 1 changesets with 1 changes to 1 files
1199 new changesets 79513d0d7716
1200 updating to branch default
1201 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1202
1203 Forbid bookmark move on the server
1204
1205 $ cat << EOF >> server/.hg/hgrc
1206 > [hooks]
1207 > prepushkey.no-bm-move= echo \$HG_NAMESPACE | grep -v bookmarks
1208 > EOF
1209
1210 pushing changeset is okay
1211
1212 $ echo bar >> client/a
1213 $ hg -R client commit -m b
1214 $ hg -R client push
1215 pushing to ssh://user@dummy/server
1216 searching for changes
1217 remote: adding changesets
1218 remote: adding manifests
1219 remote: adding file changes
1220 remote: added 1 changesets with 1 changes to 1 files
1221
1222 attempt to move the bookmark is rejected
1223
1224 $ hg -R client book foo -r .
1225 moving bookmark 'foo' forward from 79513d0d7716
1226
1227 #if b2-pushkey
1228 $ hg -R client push
1229 pushing to ssh://user@dummy/server
1230 searching for changes
1231 no changes found
1232 remote: pushkey-abort: prepushkey.no-bm-move hook exited with status 1
1233 abort: updating bookmark foo failed!
1234 [255]
1235 #endif
1236 #if b2-binary
1237 $ hg -R client push
1238 pushing to ssh://user@dummy/server
1239 searching for changes
1240 no changes found
1241 remote: prepushkey.no-bm-move hook exited with status 1
1242 abort: push failed on remote
1243 [255]
1244 #endif
@@ -1,1148 +1,1148
1 Test exchange of common information using bundle2
1 Test exchange of common information using bundle2
2
2
3
3
4 $ getmainid() {
4 $ getmainid() {
5 > hg -R main log --template '{node}\n' --rev "$1"
5 > hg -R main log --template '{node}\n' --rev "$1"
6 > }
6 > }
7
7
8 enable obsolescence
8 enable obsolescence
9
9
10 $ cp $HGRCPATH $TESTTMP/hgrc.orig
10 $ cp $HGRCPATH $TESTTMP/hgrc.orig
11 $ cat > $TESTTMP/bundle2-pushkey-hook.sh << EOF
11 $ cat > $TESTTMP/bundle2-pushkey-hook.sh << EOF
12 > echo pushkey: lock state after \"\$HG_NAMESPACE\"
12 > echo pushkey: lock state after \"\$HG_NAMESPACE\"
13 > hg debuglock
13 > hg debuglock
14 > EOF
14 > EOF
15
15
16 $ cat >> $HGRCPATH << EOF
16 $ cat >> $HGRCPATH << EOF
17 > [experimental]
17 > [experimental]
18 > evolution.createmarkers=True
18 > evolution.createmarkers=True
19 > evolution.exchange=True
19 > evolution.exchange=True
20 > bundle2-output-capture=True
20 > bundle2-output-capture=True
21 > [ui]
21 > [ui]
22 > ssh="$PYTHON" "$TESTDIR/dummyssh"
22 > ssh="$PYTHON" "$TESTDIR/dummyssh"
23 > logtemplate={rev}:{node|short} {phase} {author} {bookmarks} {desc|firstline}
23 > logtemplate={rev}:{node|short} {phase} {author} {bookmarks} {desc|firstline}
24 > [web]
24 > [web]
25 > push_ssl = false
25 > push_ssl = false
26 > allow_push = *
26 > allow_push = *
27 > [phases]
27 > [phases]
28 > publish=False
28 > publish=False
29 > [hooks]
29 > [hooks]
30 > pretxnclose.tip = hg log -r tip -T "pre-close-tip:{node|short} {phase} {bookmarks}\n"
30 > pretxnclose.tip = hg log -r tip -T "pre-close-tip:{node|short} {phase} {bookmarks}\n"
31 > txnclose.tip = hg log -r tip -T "postclose-tip:{node|short} {phase} {bookmarks}\n"
31 > txnclose.tip = hg log -r tip -T "postclose-tip:{node|short} {phase} {bookmarks}\n"
32 > txnclose.env = sh -c "HG_LOCAL= printenv.py txnclose"
32 > txnclose.env = sh -c "HG_LOCAL= printenv.py txnclose"
33 > pushkey= sh "$TESTTMP/bundle2-pushkey-hook.sh"
33 > pushkey= sh "$TESTTMP/bundle2-pushkey-hook.sh"
34 > EOF
34 > EOF
35
35
36 The extension requires a repo (currently unused)
36 The extension requires a repo (currently unused)
37
37
38 $ hg init main
38 $ hg init main
39 $ cd main
39 $ cd main
40 $ touch a
40 $ touch a
41 $ hg add a
41 $ hg add a
42 $ hg commit -m 'a'
42 $ hg commit -m 'a'
43 pre-close-tip:3903775176ed draft
43 pre-close-tip:3903775176ed draft
44 postclose-tip:3903775176ed draft
44 postclose-tip:3903775176ed draft
45 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_PHASES_MOVED=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
45 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_PHASES_MOVED=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
46
46
47 $ hg unbundle $TESTDIR/bundles/rebase.hg
47 $ hg unbundle $TESTDIR/bundles/rebase.hg
48 adding changesets
48 adding changesets
49 adding manifests
49 adding manifests
50 adding file changes
50 adding file changes
51 added 8 changesets with 7 changes to 7 files (+3 heads)
51 added 8 changesets with 7 changes to 7 files (+3 heads)
52 pre-close-tip:02de42196ebe draft
52 pre-close-tip:02de42196ebe draft
53 new changesets cd010b8cd998:02de42196ebe
53 new changesets cd010b8cd998:02de42196ebe
54 postclose-tip:02de42196ebe draft
54 postclose-tip:02de42196ebe draft
55 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NODE=cd010b8cd998f3981a5a8115f94f8da4ab506089 HG_NODE_LAST=02de42196ebee42ef284b6780a87cdc96e8eaab6 HG_PHASES_MOVED=1 HG_SOURCE=unbundle HG_TXNID=TXN:$ID$ HG_TXNNAME=unbundle
55 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NODE=cd010b8cd998f3981a5a8115f94f8da4ab506089 HG_NODE_LAST=02de42196ebee42ef284b6780a87cdc96e8eaab6 HG_PHASES_MOVED=1 HG_SOURCE=unbundle HG_TXNID=TXN:$ID$ HG_TXNNAME=unbundle
56 bundle:*/tests/bundles/rebase.hg HG_URL=bundle:*/tests/bundles/rebase.hg (glob)
56 bundle:*/tests/bundles/rebase.hg HG_URL=bundle:*/tests/bundles/rebase.hg (glob)
57 (run 'hg heads' to see heads, 'hg merge' to merge)
57 (run 'hg heads' to see heads, 'hg merge' to merge)
58
58
59 $ cd ..
59 $ cd ..
60
60
61 Real world exchange
61 Real world exchange
62 =====================
62 =====================
63
63
64 Add more obsolescence information
64 Add more obsolescence information
65
65
66 $ hg -R main debugobsolete -d '0 0' 1111111111111111111111111111111111111111 `getmainid 9520eea781bc`
66 $ hg -R main debugobsolete -d '0 0' 1111111111111111111111111111111111111111 `getmainid 9520eea781bc`
67 pre-close-tip:02de42196ebe draft
67 pre-close-tip:02de42196ebe draft
68 postclose-tip:02de42196ebe draft
68 postclose-tip:02de42196ebe draft
69 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=debugobsolete
69 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=debugobsolete
70 $ hg -R main debugobsolete -d '0 0' 2222222222222222222222222222222222222222 `getmainid 24b6387c8c8c`
70 $ hg -R main debugobsolete -d '0 0' 2222222222222222222222222222222222222222 `getmainid 24b6387c8c8c`
71 pre-close-tip:02de42196ebe draft
71 pre-close-tip:02de42196ebe draft
72 postclose-tip:02de42196ebe draft
72 postclose-tip:02de42196ebe draft
73 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=debugobsolete
73 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=debugobsolete
74
74
75 clone --pull
75 clone --pull
76
76
77 $ hg -R main phase --public cd010b8cd998
77 $ hg -R main phase --public cd010b8cd998
78 pre-close-tip:02de42196ebe draft
78 pre-close-tip:02de42196ebe draft
79 postclose-tip:02de42196ebe draft
79 postclose-tip:02de42196ebe draft
80 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_PHASES_MOVED=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=phase
80 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_PHASES_MOVED=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=phase
81 $ hg clone main other --pull --rev 9520eea781bc
81 $ hg clone main other --pull --rev 9520eea781bc
82 adding changesets
82 adding changesets
83 adding manifests
83 adding manifests
84 adding file changes
84 adding file changes
85 added 2 changesets with 2 changes to 2 files
85 added 2 changesets with 2 changes to 2 files
86 1 new obsolescence markers
86 1 new obsolescence markers
87 pre-close-tip:9520eea781bc draft
87 pre-close-tip:9520eea781bc draft
88 new changesets cd010b8cd998:9520eea781bc
88 new changesets cd010b8cd998:9520eea781bc
89 postclose-tip:9520eea781bc draft
89 postclose-tip:9520eea781bc draft
90 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_NODE=cd010b8cd998f3981a5a8115f94f8da4ab506089 HG_NODE_LAST=9520eea781bcca16c1e15acc0ba14335a0e8e5ba HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_TXNNAME=pull
90 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_NODE=cd010b8cd998f3981a5a8115f94f8da4ab506089 HG_NODE_LAST=9520eea781bcca16c1e15acc0ba14335a0e8e5ba HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_TXNNAME=pull
91 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
91 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
92 updating to branch default
92 updating to branch default
93 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
93 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
94 $ hg -R other log -G
94 $ hg -R other log -G
95 @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
95 @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
96 |
96 |
97 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
97 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
98
98
99 $ hg -R other debugobsolete
99 $ hg -R other debugobsolete
100 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
100 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
101
101
102 pull
102 pull
103
103
104 $ hg -R main phase --public 9520eea781bc
104 $ hg -R main phase --public 9520eea781bc
105 pre-close-tip:02de42196ebe draft
105 pre-close-tip:02de42196ebe draft
106 postclose-tip:02de42196ebe draft
106 postclose-tip:02de42196ebe draft
107 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_PHASES_MOVED=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=phase
107 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_PHASES_MOVED=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=phase
108 $ hg -R other pull -r 24b6387c8c8c
108 $ hg -R other pull -r 24b6387c8c8c
109 pulling from $TESTTMP/main
109 pulling from $TESTTMP/main
110 searching for changes
110 searching for changes
111 adding changesets
111 adding changesets
112 adding manifests
112 adding manifests
113 adding file changes
113 adding file changes
114 added 1 changesets with 1 changes to 1 files (+1 heads)
114 added 1 changesets with 1 changes to 1 files (+1 heads)
115 1 new obsolescence markers
115 1 new obsolescence markers
116 pre-close-tip:24b6387c8c8c draft
116 pre-close-tip:24b6387c8c8c draft
117 new changesets 24b6387c8c8c
117 new changesets 24b6387c8c8c
118 postclose-tip:24b6387c8c8c draft
118 postclose-tip:24b6387c8c8c draft
119 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_NODE=24b6387c8c8cae37178880f3fa95ded3cb1cf785 HG_NODE_LAST=24b6387c8c8cae37178880f3fa95ded3cb1cf785 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_TXNNAME=pull
119 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_NODE=24b6387c8c8cae37178880f3fa95ded3cb1cf785 HG_NODE_LAST=24b6387c8c8cae37178880f3fa95ded3cb1cf785 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_TXNNAME=pull
120 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
120 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
121 (run 'hg heads' to see heads, 'hg merge' to merge)
121 (run 'hg heads' to see heads, 'hg merge' to merge)
122 $ hg -R other log -G
122 $ hg -R other log -G
123 o 2:24b6387c8c8c draft Nicolas Dumazet <nicdumz.commits@gmail.com> F
123 o 2:24b6387c8c8c draft Nicolas Dumazet <nicdumz.commits@gmail.com> F
124 |
124 |
125 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
125 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
126 |/
126 |/
127 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
127 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
128
128
129 $ hg -R other debugobsolete
129 $ hg -R other debugobsolete
130 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
130 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
131 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
131 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
132
132
133 pull empty (with phase movement)
133 pull empty (with phase movement)
134
134
135 $ hg -R main phase --public 24b6387c8c8c
135 $ hg -R main phase --public 24b6387c8c8c
136 pre-close-tip:02de42196ebe draft
136 pre-close-tip:02de42196ebe draft
137 postclose-tip:02de42196ebe draft
137 postclose-tip:02de42196ebe draft
138 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_PHASES_MOVED=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=phase
138 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_PHASES_MOVED=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=phase
139 $ hg -R other pull -r 24b6387c8c8c
139 $ hg -R other pull -r 24b6387c8c8c
140 pulling from $TESTTMP/main
140 pulling from $TESTTMP/main
141 no changes found
141 no changes found
142 pre-close-tip:24b6387c8c8c public
142 pre-close-tip:24b6387c8c8c public
143 postclose-tip:24b6387c8c8c public
143 postclose-tip:24b6387c8c8c public
144 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=0 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_TXNNAME=pull
144 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=0 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_TXNNAME=pull
145 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
145 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
146 $ hg -R other log -G
146 $ hg -R other log -G
147 o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
147 o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
148 |
148 |
149 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
149 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
150 |/
150 |/
151 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
151 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
152
152
153 $ hg -R other debugobsolete
153 $ hg -R other debugobsolete
154 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
154 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
155 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
155 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
156
156
157 pull empty
157 pull empty
158
158
159 $ hg -R other pull -r 24b6387c8c8c
159 $ hg -R other pull -r 24b6387c8c8c
160 pulling from $TESTTMP/main
160 pulling from $TESTTMP/main
161 no changes found
161 no changes found
162 pre-close-tip:24b6387c8c8c public
162 pre-close-tip:24b6387c8c8c public
163 postclose-tip:24b6387c8c8c public
163 postclose-tip:24b6387c8c8c public
164 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=0 HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_TXNNAME=pull
164 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=0 HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_TXNNAME=pull
165 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
165 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
166 $ hg -R other log -G
166 $ hg -R other log -G
167 o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
167 o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
168 |
168 |
169 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
169 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
170 |/
170 |/
171 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
171 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
172
172
173 $ hg -R other debugobsolete
173 $ hg -R other debugobsolete
174 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
174 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
175 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
175 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
176
176
177 add extra data to test their exchange during push
177 add extra data to test their exchange during push
178
178
179 $ hg -R main bookmark --rev eea13746799a book_eea1
179 $ hg -R main bookmark --rev eea13746799a book_eea1
180 pre-close-tip:02de42196ebe draft
180 pre-close-tip:02de42196ebe draft
181 postclose-tip:02de42196ebe draft
181 postclose-tip:02de42196ebe draft
182 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
182 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
183 $ hg -R main debugobsolete -d '0 0' 3333333333333333333333333333333333333333 `getmainid eea13746799a`
183 $ hg -R main debugobsolete -d '0 0' 3333333333333333333333333333333333333333 `getmainid eea13746799a`
184 pre-close-tip:02de42196ebe draft
184 pre-close-tip:02de42196ebe draft
185 postclose-tip:02de42196ebe draft
185 postclose-tip:02de42196ebe draft
186 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=debugobsolete
186 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=debugobsolete
187 $ hg -R main bookmark --rev 02de42196ebe book_02de
187 $ hg -R main bookmark --rev 02de42196ebe book_02de
188 pre-close-tip:02de42196ebe draft book_02de
188 pre-close-tip:02de42196ebe draft book_02de
189 postclose-tip:02de42196ebe draft book_02de
189 postclose-tip:02de42196ebe draft book_02de
190 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
190 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
191 $ hg -R main debugobsolete -d '0 0' 4444444444444444444444444444444444444444 `getmainid 02de42196ebe`
191 $ hg -R main debugobsolete -d '0 0' 4444444444444444444444444444444444444444 `getmainid 02de42196ebe`
192 pre-close-tip:02de42196ebe draft book_02de
192 pre-close-tip:02de42196ebe draft book_02de
193 postclose-tip:02de42196ebe draft book_02de
193 postclose-tip:02de42196ebe draft book_02de
194 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=debugobsolete
194 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=debugobsolete
195 $ hg -R main bookmark --rev 42ccdea3bb16 book_42cc
195 $ hg -R main bookmark --rev 42ccdea3bb16 book_42cc
196 pre-close-tip:02de42196ebe draft book_02de
196 pre-close-tip:02de42196ebe draft book_02de
197 postclose-tip:02de42196ebe draft book_02de
197 postclose-tip:02de42196ebe draft book_02de
198 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
198 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
199 $ hg -R main debugobsolete -d '0 0' 5555555555555555555555555555555555555555 `getmainid 42ccdea3bb16`
199 $ hg -R main debugobsolete -d '0 0' 5555555555555555555555555555555555555555 `getmainid 42ccdea3bb16`
200 pre-close-tip:02de42196ebe draft book_02de
200 pre-close-tip:02de42196ebe draft book_02de
201 postclose-tip:02de42196ebe draft book_02de
201 postclose-tip:02de42196ebe draft book_02de
202 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=debugobsolete
202 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=debugobsolete
203 $ hg -R main bookmark --rev 5fddd98957c8 book_5fdd
203 $ hg -R main bookmark --rev 5fddd98957c8 book_5fdd
204 pre-close-tip:02de42196ebe draft book_02de
204 pre-close-tip:02de42196ebe draft book_02de
205 postclose-tip:02de42196ebe draft book_02de
205 postclose-tip:02de42196ebe draft book_02de
206 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
206 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
207 $ hg -R main debugobsolete -d '0 0' 6666666666666666666666666666666666666666 `getmainid 5fddd98957c8`
207 $ hg -R main debugobsolete -d '0 0' 6666666666666666666666666666666666666666 `getmainid 5fddd98957c8`
208 pre-close-tip:02de42196ebe draft book_02de
208 pre-close-tip:02de42196ebe draft book_02de
209 postclose-tip:02de42196ebe draft book_02de
209 postclose-tip:02de42196ebe draft book_02de
210 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=debugobsolete
210 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=debugobsolete
211 $ hg -R main bookmark --rev 32af7686d403 book_32af
211 $ hg -R main bookmark --rev 32af7686d403 book_32af
212 pre-close-tip:02de42196ebe draft book_02de
212 pre-close-tip:02de42196ebe draft book_02de
213 postclose-tip:02de42196ebe draft book_02de
213 postclose-tip:02de42196ebe draft book_02de
214 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
214 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
215 $ hg -R main debugobsolete -d '0 0' 7777777777777777777777777777777777777777 `getmainid 32af7686d403`
215 $ hg -R main debugobsolete -d '0 0' 7777777777777777777777777777777777777777 `getmainid 32af7686d403`
216 pre-close-tip:02de42196ebe draft book_02de
216 pre-close-tip:02de42196ebe draft book_02de
217 postclose-tip:02de42196ebe draft book_02de
217 postclose-tip:02de42196ebe draft book_02de
218 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=debugobsolete
218 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=debugobsolete
219
219
220 $ hg -R other bookmark --rev cd010b8cd998 book_eea1
220 $ hg -R other bookmark --rev cd010b8cd998 book_eea1
221 pre-close-tip:24b6387c8c8c public
221 pre-close-tip:24b6387c8c8c public
222 postclose-tip:24b6387c8c8c public
222 postclose-tip:24b6387c8c8c public
223 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
223 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
224 $ hg -R other bookmark --rev cd010b8cd998 book_02de
224 $ hg -R other bookmark --rev cd010b8cd998 book_02de
225 pre-close-tip:24b6387c8c8c public
225 pre-close-tip:24b6387c8c8c public
226 postclose-tip:24b6387c8c8c public
226 postclose-tip:24b6387c8c8c public
227 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
227 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
228 $ hg -R other bookmark --rev cd010b8cd998 book_42cc
228 $ hg -R other bookmark --rev cd010b8cd998 book_42cc
229 pre-close-tip:24b6387c8c8c public
229 pre-close-tip:24b6387c8c8c public
230 postclose-tip:24b6387c8c8c public
230 postclose-tip:24b6387c8c8c public
231 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
231 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
232 $ hg -R other bookmark --rev cd010b8cd998 book_5fdd
232 $ hg -R other bookmark --rev cd010b8cd998 book_5fdd
233 pre-close-tip:24b6387c8c8c public
233 pre-close-tip:24b6387c8c8c public
234 postclose-tip:24b6387c8c8c public
234 postclose-tip:24b6387c8c8c public
235 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
235 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
236 $ hg -R other bookmark --rev cd010b8cd998 book_32af
236 $ hg -R other bookmark --rev cd010b8cd998 book_32af
237 pre-close-tip:24b6387c8c8c public
237 pre-close-tip:24b6387c8c8c public
238 postclose-tip:24b6387c8c8c public
238 postclose-tip:24b6387c8c8c public
239 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
239 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
240
240
241 $ hg -R main phase --public eea13746799a
241 $ hg -R main phase --public eea13746799a
242 pre-close-tip:02de42196ebe draft book_02de
242 pre-close-tip:02de42196ebe draft book_02de
243 postclose-tip:02de42196ebe draft book_02de
243 postclose-tip:02de42196ebe draft book_02de
244 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_PHASES_MOVED=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=phase
244 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_PHASES_MOVED=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=phase
245
245
246 push
246 push
247 $ hg -R main push other --rev eea13746799a --bookmark book_eea1
247 $ hg -R main push other --rev eea13746799a --bookmark book_eea1
248 pushing to other
248 pushing to other
249 searching for changes
249 searching for changes
250 remote: adding changesets
250 remote: adding changesets
251 remote: adding manifests
251 remote: adding manifests
252 remote: adding file changes
252 remote: adding file changes
253 remote: added 1 changesets with 0 changes to 0 files (-1 heads)
253 remote: added 1 changesets with 0 changes to 0 files (-1 heads)
254 remote: 1 new obsolescence markers
254 remote: 1 new obsolescence markers
255 remote: pre-close-tip:eea13746799a public book_eea1
255 remote: pre-close-tip:eea13746799a public book_eea1
256 remote: pushkey: lock state after "bookmark"
256 remote: pushkey: lock state after "bookmarks"
257 remote: lock: free
257 remote: lock: free
258 remote: wlock: free
258 remote: wlock: free
259 remote: postclose-tip:eea13746799a public book_eea1
259 remote: postclose-tip:eea13746799a public book_eea1
260 remote: txnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_NODE=eea13746799a9e0bfd88f29d3c2e9dc9389f524f HG_NODE_LAST=eea13746799a9e0bfd88f29d3c2e9dc9389f524f HG_PHASES_MOVED=1 HG_SOURCE=push HG_TXNID=TXN:$ID$ HG_TXNNAME=push HG_URL=file:$TESTTMP/other
260 remote: txnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_NODE=eea13746799a9e0bfd88f29d3c2e9dc9389f524f HG_NODE_LAST=eea13746799a9e0bfd88f29d3c2e9dc9389f524f HG_PHASES_MOVED=1 HG_SOURCE=push HG_TXNID=TXN:$ID$ HG_TXNNAME=push HG_URL=file:$TESTTMP/other
261 updating bookmark book_eea1
261 updating bookmark book_eea1
262 pre-close-tip:02de42196ebe draft book_02de
262 pre-close-tip:02de42196ebe draft book_02de
263 postclose-tip:02de42196ebe draft book_02de
263 postclose-tip:02de42196ebe draft book_02de
264 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_SOURCE=push-response HG_TXNID=TXN:$ID$ HG_TXNNAME=push-response
264 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_SOURCE=push-response HG_TXNID=TXN:$ID$ HG_TXNNAME=push-response
265 file:/*/$TESTTMP/other HG_URL=file:$TESTTMP/other (glob)
265 file:/*/$TESTTMP/other HG_URL=file:$TESTTMP/other (glob)
266 $ hg -R other log -G
266 $ hg -R other log -G
267 o 3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> book_eea1 G
267 o 3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> book_eea1 G
268 |\
268 |\
269 | o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
269 | o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
270 | |
270 | |
271 @ | 1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
271 @ | 1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
272 |/
272 |/
273 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_02de book_32af book_42cc book_5fdd A
273 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_02de book_32af book_42cc book_5fdd A
274
274
275 $ hg -R other debugobsolete
275 $ hg -R other debugobsolete
276 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
276 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
277 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
277 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
278 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
278 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
279
279
280 pull over ssh
280 pull over ssh
281
281
282 $ hg -R other pull ssh://user@dummy/main -r 02de42196ebe --bookmark book_02de
282 $ hg -R other pull ssh://user@dummy/main -r 02de42196ebe --bookmark book_02de
283 pulling from ssh://user@dummy/main
283 pulling from ssh://user@dummy/main
284 searching for changes
284 searching for changes
285 adding changesets
285 adding changesets
286 adding manifests
286 adding manifests
287 adding file changes
287 adding file changes
288 added 1 changesets with 1 changes to 1 files (+1 heads)
288 added 1 changesets with 1 changes to 1 files (+1 heads)
289 1 new obsolescence markers
289 1 new obsolescence markers
290 updating bookmark book_02de
290 updating bookmark book_02de
291 pre-close-tip:02de42196ebe draft book_02de
291 pre-close-tip:02de42196ebe draft book_02de
292 new changesets 02de42196ebe
292 new changesets 02de42196ebe
293 postclose-tip:02de42196ebe draft book_02de
293 postclose-tip:02de42196ebe draft book_02de
294 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_NODE=02de42196ebee42ef284b6780a87cdc96e8eaab6 HG_NODE_LAST=02de42196ebee42ef284b6780a87cdc96e8eaab6 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_TXNNAME=pull
294 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_NODE=02de42196ebee42ef284b6780a87cdc96e8eaab6 HG_NODE_LAST=02de42196ebee42ef284b6780a87cdc96e8eaab6 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_TXNNAME=pull
295 ssh://user@dummy/main HG_URL=ssh://user@dummy/main
295 ssh://user@dummy/main HG_URL=ssh://user@dummy/main
296 (run 'hg heads' to see heads, 'hg merge' to merge)
296 (run 'hg heads' to see heads, 'hg merge' to merge)
297 $ hg -R other debugobsolete
297 $ hg -R other debugobsolete
298 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
298 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
299 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
299 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
300 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
300 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
301 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
301 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
302
302
303 pull over http
303 pull over http
304
304
305 $ hg serve -R main -p $HGPORT -d --pid-file=main.pid -E main-error.log
305 $ hg serve -R main -p $HGPORT -d --pid-file=main.pid -E main-error.log
306 $ cat main.pid >> $DAEMON_PIDS
306 $ cat main.pid >> $DAEMON_PIDS
307
307
308 $ hg -R other pull http://localhost:$HGPORT/ -r 42ccdea3bb16 --bookmark book_42cc
308 $ hg -R other pull http://localhost:$HGPORT/ -r 42ccdea3bb16 --bookmark book_42cc
309 pulling from http://localhost:$HGPORT/
309 pulling from http://localhost:$HGPORT/
310 searching for changes
310 searching for changes
311 adding changesets
311 adding changesets
312 adding manifests
312 adding manifests
313 adding file changes
313 adding file changes
314 added 1 changesets with 1 changes to 1 files (+1 heads)
314 added 1 changesets with 1 changes to 1 files (+1 heads)
315 1 new obsolescence markers
315 1 new obsolescence markers
316 updating bookmark book_42cc
316 updating bookmark book_42cc
317 pre-close-tip:42ccdea3bb16 draft book_42cc
317 pre-close-tip:42ccdea3bb16 draft book_42cc
318 new changesets 42ccdea3bb16
318 new changesets 42ccdea3bb16
319 postclose-tip:42ccdea3bb16 draft book_42cc
319 postclose-tip:42ccdea3bb16 draft book_42cc
320 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_NODE=42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 HG_NODE_LAST=42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_TXNNAME=pull
320 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_NODE=42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 HG_NODE_LAST=42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_TXNNAME=pull
321 http://localhost:$HGPORT/ HG_URL=http://localhost:$HGPORT/
321 http://localhost:$HGPORT/ HG_URL=http://localhost:$HGPORT/
322 (run 'hg heads .' to see heads, 'hg merge' to merge)
322 (run 'hg heads .' to see heads, 'hg merge' to merge)
323 $ cat main-error.log
323 $ cat main-error.log
324 $ hg -R other debugobsolete
324 $ hg -R other debugobsolete
325 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
325 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
326 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
326 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
327 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
327 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
328 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
328 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
329 5555555555555555555555555555555555555555 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
329 5555555555555555555555555555555555555555 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
330
330
331 push over ssh
331 push over ssh
332
332
333 $ hg -R main push ssh://user@dummy/other -r 5fddd98957c8 --bookmark book_5fdd
333 $ hg -R main push ssh://user@dummy/other -r 5fddd98957c8 --bookmark book_5fdd
334 pushing to ssh://user@dummy/other
334 pushing to ssh://user@dummy/other
335 searching for changes
335 searching for changes
336 remote: adding changesets
336 remote: adding changesets
337 remote: adding manifests
337 remote: adding manifests
338 remote: adding file changes
338 remote: adding file changes
339 remote: added 1 changesets with 1 changes to 1 files
339 remote: added 1 changesets with 1 changes to 1 files
340 remote: 1 new obsolescence markers
340 remote: 1 new obsolescence markers
341 remote: pre-close-tip:5fddd98957c8 draft book_5fdd
341 remote: pre-close-tip:5fddd98957c8 draft book_5fdd
342 remote: pushkey: lock state after "bookmark"
342 remote: pushkey: lock state after "bookmarks"
343 remote: lock: free
343 remote: lock: free
344 remote: wlock: free
344 remote: wlock: free
345 remote: postclose-tip:5fddd98957c8 draft book_5fdd
345 remote: postclose-tip:5fddd98957c8 draft book_5fdd
346 remote: txnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_NODE=5fddd98957c8a54a4d436dfe1da9d87f21a1b97b HG_NODE_LAST=5fddd98957c8a54a4d436dfe1da9d87f21a1b97b HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_TXNNAME=serve HG_URL=remote:ssh:$LOCALIP
346 remote: txnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_NODE=5fddd98957c8a54a4d436dfe1da9d87f21a1b97b HG_NODE_LAST=5fddd98957c8a54a4d436dfe1da9d87f21a1b97b HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_TXNNAME=serve HG_URL=remote:ssh:$LOCALIP
347 updating bookmark book_5fdd
347 updating bookmark book_5fdd
348 pre-close-tip:02de42196ebe draft book_02de
348 pre-close-tip:02de42196ebe draft book_02de
349 postclose-tip:02de42196ebe draft book_02de
349 postclose-tip:02de42196ebe draft book_02de
350 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_SOURCE=push-response HG_TXNID=TXN:$ID$ HG_TXNNAME=push-response
350 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_SOURCE=push-response HG_TXNID=TXN:$ID$ HG_TXNNAME=push-response
351 ssh://user@dummy/other HG_URL=ssh://user@dummy/other
351 ssh://user@dummy/other HG_URL=ssh://user@dummy/other
352 $ hg -R other log -G
352 $ hg -R other log -G
353 o 6:5fddd98957c8 draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_5fdd C
353 o 6:5fddd98957c8 draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_5fdd C
354 |
354 |
355 o 5:42ccdea3bb16 draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_42cc B
355 o 5:42ccdea3bb16 draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_42cc B
356 |
356 |
357 | o 4:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_02de H
357 | o 4:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_02de H
358 | |
358 | |
359 | | o 3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> book_eea1 G
359 | | o 3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> book_eea1 G
360 | |/|
360 | |/|
361 | o | 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
361 | o | 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
362 |/ /
362 |/ /
363 | @ 1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
363 | @ 1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
364 |/
364 |/
365 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_32af A
365 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_32af A
366
366
367 $ hg -R other debugobsolete
367 $ hg -R other debugobsolete
368 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
368 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
369 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
369 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
370 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
370 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
371 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
371 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
372 5555555555555555555555555555555555555555 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
372 5555555555555555555555555555555555555555 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
373 6666666666666666666666666666666666666666 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
373 6666666666666666666666666666666666666666 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
374
374
375 push over http
375 push over http
376
376
377 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
377 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
378 $ cat other.pid >> $DAEMON_PIDS
378 $ cat other.pid >> $DAEMON_PIDS
379
379
380 $ hg -R main phase --public 32af7686d403
380 $ hg -R main phase --public 32af7686d403
381 pre-close-tip:02de42196ebe draft book_02de
381 pre-close-tip:02de42196ebe draft book_02de
382 postclose-tip:02de42196ebe draft book_02de
382 postclose-tip:02de42196ebe draft book_02de
383 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_PHASES_MOVED=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=phase
383 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_PHASES_MOVED=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=phase
384 $ hg -R main push http://localhost:$HGPORT2/ -r 32af7686d403 --bookmark book_32af
384 $ hg -R main push http://localhost:$HGPORT2/ -r 32af7686d403 --bookmark book_32af
385 pushing to http://localhost:$HGPORT2/
385 pushing to http://localhost:$HGPORT2/
386 searching for changes
386 searching for changes
387 remote: adding changesets
387 remote: adding changesets
388 remote: adding manifests
388 remote: adding manifests
389 remote: adding file changes
389 remote: adding file changes
390 remote: added 1 changesets with 1 changes to 1 files
390 remote: added 1 changesets with 1 changes to 1 files
391 remote: 1 new obsolescence markers
391 remote: 1 new obsolescence markers
392 remote: pre-close-tip:32af7686d403 public book_32af
392 remote: pre-close-tip:32af7686d403 public book_32af
393 remote: pushkey: lock state after "bookmark"
393 remote: pushkey: lock state after "bookmarks"
394 remote: lock: free
394 remote: lock: free
395 remote: wlock: free
395 remote: wlock: free
396 remote: postclose-tip:32af7686d403 public book_32af
396 remote: postclose-tip:32af7686d403 public book_32af
397 remote: txnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_NODE=32af7686d403cf45b5d95f2d70cebea587ac806a HG_NODE_LAST=32af7686d403cf45b5d95f2d70cebea587ac806a HG_PHASES_MOVED=1 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_TXNNAME=serve HG_URL=remote:http:$LOCALIP: (glob)
397 remote: txnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_NEW_OBSMARKERS=1 HG_NODE=32af7686d403cf45b5d95f2d70cebea587ac806a HG_NODE_LAST=32af7686d403cf45b5d95f2d70cebea587ac806a HG_PHASES_MOVED=1 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_TXNNAME=serve HG_URL=remote:http:$LOCALIP: (glob)
398 updating bookmark book_32af
398 updating bookmark book_32af
399 pre-close-tip:02de42196ebe draft book_02de
399 pre-close-tip:02de42196ebe draft book_02de
400 postclose-tip:02de42196ebe draft book_02de
400 postclose-tip:02de42196ebe draft book_02de
401 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_SOURCE=push-response HG_TXNID=TXN:$ID$ HG_TXNNAME=push-response
401 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_SOURCE=push-response HG_TXNID=TXN:$ID$ HG_TXNNAME=push-response
402 http://localhost:$HGPORT2/ HG_URL=http://localhost:$HGPORT2/
402 http://localhost:$HGPORT2/ HG_URL=http://localhost:$HGPORT2/
403 $ cat other-error.log
403 $ cat other-error.log
404
404
405 Check final content.
405 Check final content.
406
406
407 $ hg -R other log -G
407 $ hg -R other log -G
408 o 7:32af7686d403 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_32af D
408 o 7:32af7686d403 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_32af D
409 |
409 |
410 o 6:5fddd98957c8 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_5fdd C
410 o 6:5fddd98957c8 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_5fdd C
411 |
411 |
412 o 5:42ccdea3bb16 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_42cc B
412 o 5:42ccdea3bb16 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_42cc B
413 |
413 |
414 | o 4:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_02de H
414 | o 4:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_02de H
415 | |
415 | |
416 | | o 3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> book_eea1 G
416 | | o 3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> book_eea1 G
417 | |/|
417 | |/|
418 | o | 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
418 | o | 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
419 |/ /
419 |/ /
420 | @ 1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
420 | @ 1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
421 |/
421 |/
422 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
422 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
423
423
424 $ hg -R other debugobsolete
424 $ hg -R other debugobsolete
425 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
425 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
426 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
426 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
427 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
427 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
428 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
428 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
429 5555555555555555555555555555555555555555 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
429 5555555555555555555555555555555555555555 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
430 6666666666666666666666666666666666666666 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
430 6666666666666666666666666666666666666666 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
431 7777777777777777777777777777777777777777 32af7686d403cf45b5d95f2d70cebea587ac806a 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
431 7777777777777777777777777777777777777777 32af7686d403cf45b5d95f2d70cebea587ac806a 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
432
432
433 (check that no 'pending' files remain)
433 (check that no 'pending' files remain)
434
434
435 $ ls -1 other/.hg/bookmarks*
435 $ ls -1 other/.hg/bookmarks*
436 other/.hg/bookmarks
436 other/.hg/bookmarks
437 $ ls -1 other/.hg/store/phaseroots*
437 $ ls -1 other/.hg/store/phaseroots*
438 other/.hg/store/phaseroots
438 other/.hg/store/phaseroots
439 $ ls -1 other/.hg/store/00changelog.i*
439 $ ls -1 other/.hg/store/00changelog.i*
440 other/.hg/store/00changelog.i
440 other/.hg/store/00changelog.i
441
441
442 Error Handling
442 Error Handling
443 ==============
443 ==============
444
444
445 Check that errors are properly returned to the client during push.
445 Check that errors are properly returned to the client during push.
446
446
447 Setting up
447 Setting up
448
448
449 $ cat > failpush.py << EOF
449 $ cat > failpush.py << EOF
450 > """A small extension that makes push fails when using bundle2
450 > """A small extension that makes push fails when using bundle2
451 >
451 >
452 > used to test error handling in bundle2
452 > used to test error handling in bundle2
453 > """
453 > """
454 >
454 >
455 > from mercurial import error
455 > from mercurial import error
456 > from mercurial import bundle2
456 > from mercurial import bundle2
457 > from mercurial import exchange
457 > from mercurial import exchange
458 > from mercurial import extensions
458 > from mercurial import extensions
459 > from mercurial import registrar
459 > from mercurial import registrar
460 > cmdtable = {}
460 > cmdtable = {}
461 > command = registrar.command(cmdtable)
461 > command = registrar.command(cmdtable)
462 >
462 >
463 > configtable = {}
463 > configtable = {}
464 > configitem = registrar.configitem(configtable)
464 > configitem = registrar.configitem(configtable)
465 > configitem('failpush', 'reason',
465 > configitem('failpush', 'reason',
466 > default=None,
466 > default=None,
467 > )
467 > )
468 >
468 >
469 > def _pushbundle2failpart(pushop, bundler):
469 > def _pushbundle2failpart(pushop, bundler):
470 > reason = pushop.ui.config('failpush', 'reason')
470 > reason = pushop.ui.config('failpush', 'reason')
471 > part = None
471 > part = None
472 > if reason == 'abort':
472 > if reason == 'abort':
473 > bundler.newpart('test:abort')
473 > bundler.newpart('test:abort')
474 > if reason == 'unknown':
474 > if reason == 'unknown':
475 > bundler.newpart('test:unknown')
475 > bundler.newpart('test:unknown')
476 > if reason == 'race':
476 > if reason == 'race':
477 > # 20 Bytes of crap
477 > # 20 Bytes of crap
478 > bundler.newpart('check:heads', data='01234567890123456789')
478 > bundler.newpart('check:heads', data='01234567890123456789')
479 >
479 >
480 > @bundle2.parthandler("test:abort")
480 > @bundle2.parthandler("test:abort")
481 > def handleabort(op, part):
481 > def handleabort(op, part):
482 > raise error.Abort('Abandon ship!', hint="don't panic")
482 > raise error.Abort('Abandon ship!', hint="don't panic")
483 >
483 >
484 > def uisetup(ui):
484 > def uisetup(ui):
485 > exchange.b2partsgenmapping['failpart'] = _pushbundle2failpart
485 > exchange.b2partsgenmapping['failpart'] = _pushbundle2failpart
486 > exchange.b2partsgenorder.insert(0, 'failpart')
486 > exchange.b2partsgenorder.insert(0, 'failpart')
487 >
487 >
488 > EOF
488 > EOF
489
489
490 $ cd main
490 $ cd main
491 $ hg up tip
491 $ hg up tip
492 3 files updated, 0 files merged, 1 files removed, 0 files unresolved
492 3 files updated, 0 files merged, 1 files removed, 0 files unresolved
493 $ echo 'I' > I
493 $ echo 'I' > I
494 $ hg add I
494 $ hg add I
495 $ hg ci -m 'I'
495 $ hg ci -m 'I'
496 pre-close-tip:e7ec4e813ba6 draft
496 pre-close-tip:e7ec4e813ba6 draft
497 postclose-tip:e7ec4e813ba6 draft
497 postclose-tip:e7ec4e813ba6 draft
498 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
498 txnclose hook: HG_HOOKNAME=txnclose.env HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
499 $ hg id
499 $ hg id
500 e7ec4e813ba6 tip
500 e7ec4e813ba6 tip
501 $ cd ..
501 $ cd ..
502
502
503 $ cat << EOF >> $HGRCPATH
503 $ cat << EOF >> $HGRCPATH
504 > [extensions]
504 > [extensions]
505 > failpush=$TESTTMP/failpush.py
505 > failpush=$TESTTMP/failpush.py
506 > EOF
506 > EOF
507
507
508 $ killdaemons.py
508 $ killdaemons.py
509 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
509 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
510 $ cat other.pid >> $DAEMON_PIDS
510 $ cat other.pid >> $DAEMON_PIDS
511
511
512 Doing the actual push: Abort error
512 Doing the actual push: Abort error
513
513
514 $ cat << EOF >> $HGRCPATH
514 $ cat << EOF >> $HGRCPATH
515 > [failpush]
515 > [failpush]
516 > reason = abort
516 > reason = abort
517 > EOF
517 > EOF
518
518
519 $ hg -R main push other -r e7ec4e813ba6
519 $ hg -R main push other -r e7ec4e813ba6
520 pushing to other
520 pushing to other
521 searching for changes
521 searching for changes
522 abort: Abandon ship!
522 abort: Abandon ship!
523 (don't panic)
523 (don't panic)
524 [255]
524 [255]
525
525
526 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
526 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
527 pushing to ssh://user@dummy/other
527 pushing to ssh://user@dummy/other
528 searching for changes
528 searching for changes
529 remote: Abandon ship!
529 remote: Abandon ship!
530 remote: (don't panic)
530 remote: (don't panic)
531 abort: push failed on remote
531 abort: push failed on remote
532 [255]
532 [255]
533
533
534 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
534 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
535 pushing to http://localhost:$HGPORT2/
535 pushing to http://localhost:$HGPORT2/
536 searching for changes
536 searching for changes
537 remote: Abandon ship!
537 remote: Abandon ship!
538 remote: (don't panic)
538 remote: (don't panic)
539 abort: push failed on remote
539 abort: push failed on remote
540 [255]
540 [255]
541
541
542
542
543 Doing the actual push: unknown mandatory parts
543 Doing the actual push: unknown mandatory parts
544
544
545 $ cat << EOF >> $HGRCPATH
545 $ cat << EOF >> $HGRCPATH
546 > [failpush]
546 > [failpush]
547 > reason = unknown
547 > reason = unknown
548 > EOF
548 > EOF
549
549
550 $ hg -R main push other -r e7ec4e813ba6
550 $ hg -R main push other -r e7ec4e813ba6
551 pushing to other
551 pushing to other
552 searching for changes
552 searching for changes
553 abort: missing support for test:unknown
553 abort: missing support for test:unknown
554 [255]
554 [255]
555
555
556 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
556 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
557 pushing to ssh://user@dummy/other
557 pushing to ssh://user@dummy/other
558 searching for changes
558 searching for changes
559 abort: missing support for test:unknown
559 abort: missing support for test:unknown
560 [255]
560 [255]
561
561
562 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
562 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
563 pushing to http://localhost:$HGPORT2/
563 pushing to http://localhost:$HGPORT2/
564 searching for changes
564 searching for changes
565 abort: missing support for test:unknown
565 abort: missing support for test:unknown
566 [255]
566 [255]
567
567
568 Doing the actual push: race
568 Doing the actual push: race
569
569
570 $ cat << EOF >> $HGRCPATH
570 $ cat << EOF >> $HGRCPATH
571 > [failpush]
571 > [failpush]
572 > reason = race
572 > reason = race
573 > EOF
573 > EOF
574
574
575 $ hg -R main push other -r e7ec4e813ba6
575 $ hg -R main push other -r e7ec4e813ba6
576 pushing to other
576 pushing to other
577 searching for changes
577 searching for changes
578 abort: push failed:
578 abort: push failed:
579 'repository changed while pushing - please try again'
579 'repository changed while pushing - please try again'
580 [255]
580 [255]
581
581
582 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
582 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
583 pushing to ssh://user@dummy/other
583 pushing to ssh://user@dummy/other
584 searching for changes
584 searching for changes
585 abort: push failed:
585 abort: push failed:
586 'repository changed while pushing - please try again'
586 'repository changed while pushing - please try again'
587 [255]
587 [255]
588
588
589 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
589 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
590 pushing to http://localhost:$HGPORT2/
590 pushing to http://localhost:$HGPORT2/
591 searching for changes
591 searching for changes
592 abort: push failed:
592 abort: push failed:
593 'repository changed while pushing - please try again'
593 'repository changed while pushing - please try again'
594 [255]
594 [255]
595
595
596 Doing the actual push: hook abort
596 Doing the actual push: hook abort
597
597
598 $ cat << EOF >> $HGRCPATH
598 $ cat << EOF >> $HGRCPATH
599 > [failpush]
599 > [failpush]
600 > reason =
600 > reason =
601 > [hooks]
601 > [hooks]
602 > pretxnclose.failpush = sh -c "echo 'You shall not pass!'; false"
602 > pretxnclose.failpush = sh -c "echo 'You shall not pass!'; false"
603 > txnabort.failpush = sh -c "echo 'Cleaning up the mess...'"
603 > txnabort.failpush = sh -c "echo 'Cleaning up the mess...'"
604 > EOF
604 > EOF
605
605
606 $ killdaemons.py
606 $ killdaemons.py
607 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
607 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
608 $ cat other.pid >> $DAEMON_PIDS
608 $ cat other.pid >> $DAEMON_PIDS
609
609
610 $ hg -R main push other -r e7ec4e813ba6
610 $ hg -R main push other -r e7ec4e813ba6
611 pushing to other
611 pushing to other
612 searching for changes
612 searching for changes
613 remote: adding changesets
613 remote: adding changesets
614 remote: adding manifests
614 remote: adding manifests
615 remote: adding file changes
615 remote: adding file changes
616 remote: added 1 changesets with 1 changes to 1 files
616 remote: added 1 changesets with 1 changes to 1 files
617 remote: pre-close-tip:e7ec4e813ba6 draft
617 remote: pre-close-tip:e7ec4e813ba6 draft
618 remote: You shall not pass!
618 remote: You shall not pass!
619 remote: transaction abort!
619 remote: transaction abort!
620 remote: Cleaning up the mess...
620 remote: Cleaning up the mess...
621 remote: rollback completed
621 remote: rollback completed
622 abort: pretxnclose.failpush hook exited with status 1
622 abort: pretxnclose.failpush hook exited with status 1
623 [255]
623 [255]
624
624
625 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
625 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
626 pushing to ssh://user@dummy/other
626 pushing to ssh://user@dummy/other
627 searching for changes
627 searching for changes
628 remote: adding changesets
628 remote: adding changesets
629 remote: adding manifests
629 remote: adding manifests
630 remote: adding file changes
630 remote: adding file changes
631 remote: added 1 changesets with 1 changes to 1 files
631 remote: added 1 changesets with 1 changes to 1 files
632 remote: pre-close-tip:e7ec4e813ba6 draft
632 remote: pre-close-tip:e7ec4e813ba6 draft
633 remote: You shall not pass!
633 remote: You shall not pass!
634 remote: transaction abort!
634 remote: transaction abort!
635 remote: Cleaning up the mess...
635 remote: Cleaning up the mess...
636 remote: rollback completed
636 remote: rollback completed
637 remote: pretxnclose.failpush hook exited with status 1
637 remote: pretxnclose.failpush hook exited with status 1
638 abort: push failed on remote
638 abort: push failed on remote
639 [255]
639 [255]
640
640
641 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
641 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
642 pushing to http://localhost:$HGPORT2/
642 pushing to http://localhost:$HGPORT2/
643 searching for changes
643 searching for changes
644 remote: adding changesets
644 remote: adding changesets
645 remote: adding manifests
645 remote: adding manifests
646 remote: adding file changes
646 remote: adding file changes
647 remote: added 1 changesets with 1 changes to 1 files
647 remote: added 1 changesets with 1 changes to 1 files
648 remote: pre-close-tip:e7ec4e813ba6 draft
648 remote: pre-close-tip:e7ec4e813ba6 draft
649 remote: You shall not pass!
649 remote: You shall not pass!
650 remote: transaction abort!
650 remote: transaction abort!
651 remote: Cleaning up the mess...
651 remote: Cleaning up the mess...
652 remote: rollback completed
652 remote: rollback completed
653 remote: pretxnclose.failpush hook exited with status 1
653 remote: pretxnclose.failpush hook exited with status 1
654 abort: push failed on remote
654 abort: push failed on remote
655 [255]
655 [255]
656
656
657 (check that no 'pending' files remain)
657 (check that no 'pending' files remain)
658
658
659 $ ls -1 other/.hg/bookmarks*
659 $ ls -1 other/.hg/bookmarks*
660 other/.hg/bookmarks
660 other/.hg/bookmarks
661 $ ls -1 other/.hg/store/phaseroots*
661 $ ls -1 other/.hg/store/phaseroots*
662 other/.hg/store/phaseroots
662 other/.hg/store/phaseroots
663 $ ls -1 other/.hg/store/00changelog.i*
663 $ ls -1 other/.hg/store/00changelog.i*
664 other/.hg/store/00changelog.i
664 other/.hg/store/00changelog.i
665
665
666 Check error from hook during the unbundling process itself
666 Check error from hook during the unbundling process itself
667
667
668 $ cat << EOF >> $HGRCPATH
668 $ cat << EOF >> $HGRCPATH
669 > pretxnchangegroup = sh -c "echo 'Fail early!'; false"
669 > pretxnchangegroup = sh -c "echo 'Fail early!'; false"
670 > EOF
670 > EOF
671 $ killdaemons.py # reload http config
671 $ killdaemons.py # reload http config
672 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
672 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
673 $ cat other.pid >> $DAEMON_PIDS
673 $ cat other.pid >> $DAEMON_PIDS
674
674
675 $ hg -R main push other -r e7ec4e813ba6
675 $ hg -R main push other -r e7ec4e813ba6
676 pushing to other
676 pushing to other
677 searching for changes
677 searching for changes
678 remote: adding changesets
678 remote: adding changesets
679 remote: adding manifests
679 remote: adding manifests
680 remote: adding file changes
680 remote: adding file changes
681 remote: added 1 changesets with 1 changes to 1 files
681 remote: added 1 changesets with 1 changes to 1 files
682 remote: Fail early!
682 remote: Fail early!
683 remote: transaction abort!
683 remote: transaction abort!
684 remote: Cleaning up the mess...
684 remote: Cleaning up the mess...
685 remote: rollback completed
685 remote: rollback completed
686 abort: pretxnchangegroup hook exited with status 1
686 abort: pretxnchangegroup hook exited with status 1
687 [255]
687 [255]
688 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
688 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
689 pushing to ssh://user@dummy/other
689 pushing to ssh://user@dummy/other
690 searching for changes
690 searching for changes
691 remote: adding changesets
691 remote: adding changesets
692 remote: adding manifests
692 remote: adding manifests
693 remote: adding file changes
693 remote: adding file changes
694 remote: added 1 changesets with 1 changes to 1 files
694 remote: added 1 changesets with 1 changes to 1 files
695 remote: Fail early!
695 remote: Fail early!
696 remote: transaction abort!
696 remote: transaction abort!
697 remote: Cleaning up the mess...
697 remote: Cleaning up the mess...
698 remote: rollback completed
698 remote: rollback completed
699 remote: pretxnchangegroup hook exited with status 1
699 remote: pretxnchangegroup hook exited with status 1
700 abort: push failed on remote
700 abort: push failed on remote
701 [255]
701 [255]
702 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
702 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
703 pushing to http://localhost:$HGPORT2/
703 pushing to http://localhost:$HGPORT2/
704 searching for changes
704 searching for changes
705 remote: adding changesets
705 remote: adding changesets
706 remote: adding manifests
706 remote: adding manifests
707 remote: adding file changes
707 remote: adding file changes
708 remote: added 1 changesets with 1 changes to 1 files
708 remote: added 1 changesets with 1 changes to 1 files
709 remote: Fail early!
709 remote: Fail early!
710 remote: transaction abort!
710 remote: transaction abort!
711 remote: Cleaning up the mess...
711 remote: Cleaning up the mess...
712 remote: rollback completed
712 remote: rollback completed
713 remote: pretxnchangegroup hook exited with status 1
713 remote: pretxnchangegroup hook exited with status 1
714 abort: push failed on remote
714 abort: push failed on remote
715 [255]
715 [255]
716
716
717 Check output capture control.
717 Check output capture control.
718
718
719 (should be still forced for http, disabled for local and ssh)
719 (should be still forced for http, disabled for local and ssh)
720
720
721 $ cat >> $HGRCPATH << EOF
721 $ cat >> $HGRCPATH << EOF
722 > [experimental]
722 > [experimental]
723 > bundle2-output-capture=False
723 > bundle2-output-capture=False
724 > EOF
724 > EOF
725
725
726 $ hg -R main push other -r e7ec4e813ba6
726 $ hg -R main push other -r e7ec4e813ba6
727 pushing to other
727 pushing to other
728 searching for changes
728 searching for changes
729 adding changesets
729 adding changesets
730 adding manifests
730 adding manifests
731 adding file changes
731 adding file changes
732 added 1 changesets with 1 changes to 1 files
732 added 1 changesets with 1 changes to 1 files
733 Fail early!
733 Fail early!
734 transaction abort!
734 transaction abort!
735 Cleaning up the mess...
735 Cleaning up the mess...
736 rollback completed
736 rollback completed
737 abort: pretxnchangegroup hook exited with status 1
737 abort: pretxnchangegroup hook exited with status 1
738 [255]
738 [255]
739 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
739 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
740 pushing to ssh://user@dummy/other
740 pushing to ssh://user@dummy/other
741 searching for changes
741 searching for changes
742 remote: adding changesets
742 remote: adding changesets
743 remote: adding manifests
743 remote: adding manifests
744 remote: adding file changes
744 remote: adding file changes
745 remote: added 1 changesets with 1 changes to 1 files
745 remote: added 1 changesets with 1 changes to 1 files
746 remote: Fail early!
746 remote: Fail early!
747 remote: transaction abort!
747 remote: transaction abort!
748 remote: Cleaning up the mess...
748 remote: Cleaning up the mess...
749 remote: rollback completed
749 remote: rollback completed
750 remote: pretxnchangegroup hook exited with status 1
750 remote: pretxnchangegroup hook exited with status 1
751 abort: push failed on remote
751 abort: push failed on remote
752 [255]
752 [255]
753 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
753 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
754 pushing to http://localhost:$HGPORT2/
754 pushing to http://localhost:$HGPORT2/
755 searching for changes
755 searching for changes
756 remote: adding changesets
756 remote: adding changesets
757 remote: adding manifests
757 remote: adding manifests
758 remote: adding file changes
758 remote: adding file changes
759 remote: added 1 changesets with 1 changes to 1 files
759 remote: added 1 changesets with 1 changes to 1 files
760 remote: Fail early!
760 remote: Fail early!
761 remote: transaction abort!
761 remote: transaction abort!
762 remote: Cleaning up the mess...
762 remote: Cleaning up the mess...
763 remote: rollback completed
763 remote: rollback completed
764 remote: pretxnchangegroup hook exited with status 1
764 remote: pretxnchangegroup hook exited with status 1
765 abort: push failed on remote
765 abort: push failed on remote
766 [255]
766 [255]
767
767
768 Check abort from mandatory pushkey
768 Check abort from mandatory pushkey
769
769
770 $ cat > mandatorypart.py << EOF
770 $ cat > mandatorypart.py << EOF
771 > from mercurial import exchange
771 > from mercurial import exchange
772 > from mercurial import pushkey
772 > from mercurial import pushkey
773 > from mercurial import node
773 > from mercurial import node
774 > from mercurial import error
774 > from mercurial import error
775 > @exchange.b2partsgenerator('failingpuskey')
775 > @exchange.b2partsgenerator('failingpuskey')
776 > def addfailingpushey(pushop, bundler):
776 > def addfailingpushey(pushop, bundler):
777 > enc = pushkey.encode
777 > enc = pushkey.encode
778 > part = bundler.newpart('pushkey')
778 > part = bundler.newpart('pushkey')
779 > part.addparam('namespace', enc('phases'))
779 > part.addparam('namespace', enc('phases'))
780 > part.addparam('key', enc(pushop.repo['cd010b8cd998'].hex()))
780 > part.addparam('key', enc(pushop.repo['cd010b8cd998'].hex()))
781 > part.addparam('old', enc(str(0))) # successful update
781 > part.addparam('old', enc(str(0))) # successful update
782 > part.addparam('new', enc(str(0)))
782 > part.addparam('new', enc(str(0)))
783 > def fail(pushop, exc):
783 > def fail(pushop, exc):
784 > raise error.Abort('Correct phase push failed (because hooks)')
784 > raise error.Abort('Correct phase push failed (because hooks)')
785 > pushop.pkfailcb[part.id] = fail
785 > pushop.pkfailcb[part.id] = fail
786 > EOF
786 > EOF
787 $ cat >> $HGRCPATH << EOF
787 $ cat >> $HGRCPATH << EOF
788 > [hooks]
788 > [hooks]
789 > pretxnchangegroup=
789 > pretxnchangegroup=
790 > pretxnclose.failpush=
790 > pretxnclose.failpush=
791 > prepushkey.failpush = sh -c "echo 'do not push the key !'; false"
791 > prepushkey.failpush = sh -c "echo 'do not push the key !'; false"
792 > [extensions]
792 > [extensions]
793 > mandatorypart=$TESTTMP/mandatorypart.py
793 > mandatorypart=$TESTTMP/mandatorypart.py
794 > EOF
794 > EOF
795 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS # reload http config
795 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS # reload http config
796 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
796 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
797 $ cat other.pid >> $DAEMON_PIDS
797 $ cat other.pid >> $DAEMON_PIDS
798
798
799 (Failure from a hook)
799 (Failure from a hook)
800
800
801 $ hg -R main push other -r e7ec4e813ba6
801 $ hg -R main push other -r e7ec4e813ba6
802 pushing to other
802 pushing to other
803 searching for changes
803 searching for changes
804 adding changesets
804 adding changesets
805 adding manifests
805 adding manifests
806 adding file changes
806 adding file changes
807 added 1 changesets with 1 changes to 1 files
807 added 1 changesets with 1 changes to 1 files
808 do not push the key !
808 do not push the key !
809 pushkey-abort: prepushkey.failpush hook exited with status 1
809 pushkey-abort: prepushkey.failpush hook exited with status 1
810 transaction abort!
810 transaction abort!
811 Cleaning up the mess...
811 Cleaning up the mess...
812 rollback completed
812 rollback completed
813 abort: Correct phase push failed (because hooks)
813 abort: Correct phase push failed (because hooks)
814 [255]
814 [255]
815 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
815 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
816 pushing to ssh://user@dummy/other
816 pushing to ssh://user@dummy/other
817 searching for changes
817 searching for changes
818 remote: adding changesets
818 remote: adding changesets
819 remote: adding manifests
819 remote: adding manifests
820 remote: adding file changes
820 remote: adding file changes
821 remote: added 1 changesets with 1 changes to 1 files
821 remote: added 1 changesets with 1 changes to 1 files
822 remote: do not push the key !
822 remote: do not push the key !
823 remote: pushkey-abort: prepushkey.failpush hook exited with status 1
823 remote: pushkey-abort: prepushkey.failpush hook exited with status 1
824 remote: transaction abort!
824 remote: transaction abort!
825 remote: Cleaning up the mess...
825 remote: Cleaning up the mess...
826 remote: rollback completed
826 remote: rollback completed
827 abort: Correct phase push failed (because hooks)
827 abort: Correct phase push failed (because hooks)
828 [255]
828 [255]
829 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
829 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
830 pushing to http://localhost:$HGPORT2/
830 pushing to http://localhost:$HGPORT2/
831 searching for changes
831 searching for changes
832 remote: adding changesets
832 remote: adding changesets
833 remote: adding manifests
833 remote: adding manifests
834 remote: adding file changes
834 remote: adding file changes
835 remote: added 1 changesets with 1 changes to 1 files
835 remote: added 1 changesets with 1 changes to 1 files
836 remote: do not push the key !
836 remote: do not push the key !
837 remote: pushkey-abort: prepushkey.failpush hook exited with status 1
837 remote: pushkey-abort: prepushkey.failpush hook exited with status 1
838 remote: transaction abort!
838 remote: transaction abort!
839 remote: Cleaning up the mess...
839 remote: Cleaning up the mess...
840 remote: rollback completed
840 remote: rollback completed
841 abort: Correct phase push failed (because hooks)
841 abort: Correct phase push failed (because hooks)
842 [255]
842 [255]
843
843
844 (Failure from a the pushkey)
844 (Failure from a the pushkey)
845
845
846 $ cat > mandatorypart.py << EOF
846 $ cat > mandatorypart.py << EOF
847 > from mercurial import exchange
847 > from mercurial import exchange
848 > from mercurial import pushkey
848 > from mercurial import pushkey
849 > from mercurial import node
849 > from mercurial import node
850 > from mercurial import error
850 > from mercurial import error
851 > @exchange.b2partsgenerator('failingpuskey')
851 > @exchange.b2partsgenerator('failingpuskey')
852 > def addfailingpushey(pushop, bundler):
852 > def addfailingpushey(pushop, bundler):
853 > enc = pushkey.encode
853 > enc = pushkey.encode
854 > part = bundler.newpart('pushkey')
854 > part = bundler.newpart('pushkey')
855 > part.addparam('namespace', enc('phases'))
855 > part.addparam('namespace', enc('phases'))
856 > part.addparam('key', enc(pushop.repo['cd010b8cd998'].hex()))
856 > part.addparam('key', enc(pushop.repo['cd010b8cd998'].hex()))
857 > part.addparam('old', enc(str(4))) # will fail
857 > part.addparam('old', enc(str(4))) # will fail
858 > part.addparam('new', enc(str(3)))
858 > part.addparam('new', enc(str(3)))
859 > def fail(pushop, exc):
859 > def fail(pushop, exc):
860 > raise error.Abort('Clown phase push failed')
860 > raise error.Abort('Clown phase push failed')
861 > pushop.pkfailcb[part.id] = fail
861 > pushop.pkfailcb[part.id] = fail
862 > EOF
862 > EOF
863 $ cat >> $HGRCPATH << EOF
863 $ cat >> $HGRCPATH << EOF
864 > [hooks]
864 > [hooks]
865 > prepushkey.failpush =
865 > prepushkey.failpush =
866 > EOF
866 > EOF
867 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS # reload http config
867 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS # reload http config
868 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
868 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
869 $ cat other.pid >> $DAEMON_PIDS
869 $ cat other.pid >> $DAEMON_PIDS
870
870
871 $ hg -R main push other -r e7ec4e813ba6
871 $ hg -R main push other -r e7ec4e813ba6
872 pushing to other
872 pushing to other
873 searching for changes
873 searching for changes
874 adding changesets
874 adding changesets
875 adding manifests
875 adding manifests
876 adding file changes
876 adding file changes
877 added 1 changesets with 1 changes to 1 files
877 added 1 changesets with 1 changes to 1 files
878 transaction abort!
878 transaction abort!
879 Cleaning up the mess...
879 Cleaning up the mess...
880 rollback completed
880 rollback completed
881 pushkey: lock state after "phases"
881 pushkey: lock state after "phases"
882 lock: free
882 lock: free
883 wlock: free
883 wlock: free
884 abort: Clown phase push failed
884 abort: Clown phase push failed
885 [255]
885 [255]
886 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
886 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
887 pushing to ssh://user@dummy/other
887 pushing to ssh://user@dummy/other
888 searching for changes
888 searching for changes
889 remote: adding changesets
889 remote: adding changesets
890 remote: adding manifests
890 remote: adding manifests
891 remote: adding file changes
891 remote: adding file changes
892 remote: added 1 changesets with 1 changes to 1 files
892 remote: added 1 changesets with 1 changes to 1 files
893 remote: transaction abort!
893 remote: transaction abort!
894 remote: Cleaning up the mess...
894 remote: Cleaning up the mess...
895 remote: rollback completed
895 remote: rollback completed
896 remote: pushkey: lock state after "phases"
896 remote: pushkey: lock state after "phases"
897 remote: lock: free
897 remote: lock: free
898 remote: wlock: free
898 remote: wlock: free
899 abort: Clown phase push failed
899 abort: Clown phase push failed
900 [255]
900 [255]
901 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
901 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
902 pushing to http://localhost:$HGPORT2/
902 pushing to http://localhost:$HGPORT2/
903 searching for changes
903 searching for changes
904 remote: adding changesets
904 remote: adding changesets
905 remote: adding manifests
905 remote: adding manifests
906 remote: adding file changes
906 remote: adding file changes
907 remote: added 1 changesets with 1 changes to 1 files
907 remote: added 1 changesets with 1 changes to 1 files
908 remote: transaction abort!
908 remote: transaction abort!
909 remote: Cleaning up the mess...
909 remote: Cleaning up the mess...
910 remote: rollback completed
910 remote: rollback completed
911 remote: pushkey: lock state after "phases"
911 remote: pushkey: lock state after "phases"
912 remote: lock: free
912 remote: lock: free
913 remote: wlock: free
913 remote: wlock: free
914 abort: Clown phase push failed
914 abort: Clown phase push failed
915 [255]
915 [255]
916
916
917 Test lazily acquiring the lock during unbundle
917 Test lazily acquiring the lock during unbundle
918 $ cp $TESTTMP/hgrc.orig $HGRCPATH
918 $ cp $TESTTMP/hgrc.orig $HGRCPATH
919 $ cat >> $HGRCPATH <<EOF
919 $ cat >> $HGRCPATH <<EOF
920 > [ui]
920 > [ui]
921 > ssh="$PYTHON" "$TESTDIR/dummyssh"
921 > ssh="$PYTHON" "$TESTDIR/dummyssh"
922 > EOF
922 > EOF
923
923
924 $ cat >> $TESTTMP/locktester.py <<EOF
924 $ cat >> $TESTTMP/locktester.py <<EOF
925 > import os
925 > import os
926 > from mercurial import extensions, bundle2, util
926 > from mercurial import extensions, bundle2, util
927 > def checklock(orig, repo, *args, **kwargs):
927 > def checklock(orig, repo, *args, **kwargs):
928 > if repo.svfs.lexists("lock"):
928 > if repo.svfs.lexists("lock"):
929 > raise util.Abort("Lock should not be taken")
929 > raise util.Abort("Lock should not be taken")
930 > return orig(repo, *args, **kwargs)
930 > return orig(repo, *args, **kwargs)
931 > def extsetup(ui):
931 > def extsetup(ui):
932 > extensions.wrapfunction(bundle2, 'processbundle', checklock)
932 > extensions.wrapfunction(bundle2, 'processbundle', checklock)
933 > EOF
933 > EOF
934
934
935 $ hg init lazylock
935 $ hg init lazylock
936 $ cat >> lazylock/.hg/hgrc <<EOF
936 $ cat >> lazylock/.hg/hgrc <<EOF
937 > [extensions]
937 > [extensions]
938 > locktester=$TESTTMP/locktester.py
938 > locktester=$TESTTMP/locktester.py
939 > EOF
939 > EOF
940
940
941 $ hg clone -q ssh://user@dummy/lazylock lazylockclient
941 $ hg clone -q ssh://user@dummy/lazylock lazylockclient
942 $ cd lazylockclient
942 $ cd lazylockclient
943 $ touch a && hg ci -Aqm a
943 $ touch a && hg ci -Aqm a
944 $ hg push
944 $ hg push
945 pushing to ssh://user@dummy/lazylock
945 pushing to ssh://user@dummy/lazylock
946 searching for changes
946 searching for changes
947 remote: Lock should not be taken
947 remote: Lock should not be taken
948 abort: push failed on remote
948 abort: push failed on remote
949 [255]
949 [255]
950
950
951 $ cat >> ../lazylock/.hg/hgrc <<EOF
951 $ cat >> ../lazylock/.hg/hgrc <<EOF
952 > [experimental]
952 > [experimental]
953 > bundle2lazylocking=True
953 > bundle2lazylocking=True
954 > EOF
954 > EOF
955 $ hg push
955 $ hg push
956 pushing to ssh://user@dummy/lazylock
956 pushing to ssh://user@dummy/lazylock
957 searching for changes
957 searching for changes
958 remote: adding changesets
958 remote: adding changesets
959 remote: adding manifests
959 remote: adding manifests
960 remote: adding file changes
960 remote: adding file changes
961 remote: added 1 changesets with 1 changes to 1 files
961 remote: added 1 changesets with 1 changes to 1 files
962
962
963 $ cd ..
963 $ cd ..
964
964
965 Servers can disable bundle1 for clone/pull operations
965 Servers can disable bundle1 for clone/pull operations
966
966
967 $ killdaemons.py
967 $ killdaemons.py
968 $ hg init bundle2onlyserver
968 $ hg init bundle2onlyserver
969 $ cd bundle2onlyserver
969 $ cd bundle2onlyserver
970 $ cat > .hg/hgrc << EOF
970 $ cat > .hg/hgrc << EOF
971 > [server]
971 > [server]
972 > bundle1.pull = false
972 > bundle1.pull = false
973 > EOF
973 > EOF
974
974
975 $ touch foo
975 $ touch foo
976 $ hg -q commit -A -m initial
976 $ hg -q commit -A -m initial
977
977
978 $ hg serve -p $HGPORT -d --pid-file=hg.pid
978 $ hg serve -p $HGPORT -d --pid-file=hg.pid
979 $ cat hg.pid >> $DAEMON_PIDS
979 $ cat hg.pid >> $DAEMON_PIDS
980
980
981 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT/ not-bundle2
981 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT/ not-bundle2
982 requesting all changes
982 requesting all changes
983 abort: remote error:
983 abort: remote error:
984 incompatible Mercurial client; bundle2 required
984 incompatible Mercurial client; bundle2 required
985 (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
985 (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
986 [255]
986 [255]
987 $ killdaemons.py
987 $ killdaemons.py
988 $ cd ..
988 $ cd ..
989
989
990 bundle1 can still pull non-generaldelta repos when generaldelta bundle1 disabled
990 bundle1 can still pull non-generaldelta repos when generaldelta bundle1 disabled
991
991
992 $ hg --config format.usegeneraldelta=false init notgdserver
992 $ hg --config format.usegeneraldelta=false init notgdserver
993 $ cd notgdserver
993 $ cd notgdserver
994 $ cat > .hg/hgrc << EOF
994 $ cat > .hg/hgrc << EOF
995 > [server]
995 > [server]
996 > bundle1gd.pull = false
996 > bundle1gd.pull = false
997 > EOF
997 > EOF
998
998
999 $ touch foo
999 $ touch foo
1000 $ hg -q commit -A -m initial
1000 $ hg -q commit -A -m initial
1001 $ hg serve -p $HGPORT -d --pid-file=hg.pid
1001 $ hg serve -p $HGPORT -d --pid-file=hg.pid
1002 $ cat hg.pid >> $DAEMON_PIDS
1002 $ cat hg.pid >> $DAEMON_PIDS
1003
1003
1004 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT/ not-bundle2-1
1004 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT/ not-bundle2-1
1005 requesting all changes
1005 requesting all changes
1006 adding changesets
1006 adding changesets
1007 adding manifests
1007 adding manifests
1008 adding file changes
1008 adding file changes
1009 added 1 changesets with 1 changes to 1 files
1009 added 1 changesets with 1 changes to 1 files
1010 new changesets 96ee1d7354c4
1010 new changesets 96ee1d7354c4
1011 updating to branch default
1011 updating to branch default
1012 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1012 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1013
1013
1014 $ killdaemons.py
1014 $ killdaemons.py
1015 $ cd ../bundle2onlyserver
1015 $ cd ../bundle2onlyserver
1016
1016
1017 bundle1 pull can be disabled for generaldelta repos only
1017 bundle1 pull can be disabled for generaldelta repos only
1018
1018
1019 $ cat > .hg/hgrc << EOF
1019 $ cat > .hg/hgrc << EOF
1020 > [server]
1020 > [server]
1021 > bundle1gd.pull = false
1021 > bundle1gd.pull = false
1022 > EOF
1022 > EOF
1023
1023
1024 $ hg serve -p $HGPORT -d --pid-file=hg.pid
1024 $ hg serve -p $HGPORT -d --pid-file=hg.pid
1025 $ cat hg.pid >> $DAEMON_PIDS
1025 $ cat hg.pid >> $DAEMON_PIDS
1026 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT/ not-bundle2
1026 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT/ not-bundle2
1027 requesting all changes
1027 requesting all changes
1028 abort: remote error:
1028 abort: remote error:
1029 incompatible Mercurial client; bundle2 required
1029 incompatible Mercurial client; bundle2 required
1030 (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
1030 (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
1031 [255]
1031 [255]
1032
1032
1033 $ killdaemons.py
1033 $ killdaemons.py
1034
1034
1035 Verify the global server.bundle1 option works
1035 Verify the global server.bundle1 option works
1036
1036
1037 $ cd ..
1037 $ cd ..
1038 $ cat > bundle2onlyserver/.hg/hgrc << EOF
1038 $ cat > bundle2onlyserver/.hg/hgrc << EOF
1039 > [server]
1039 > [server]
1040 > bundle1 = false
1040 > bundle1 = false
1041 > EOF
1041 > EOF
1042 $ hg serve -R bundle2onlyserver -p $HGPORT -d --pid-file=hg.pid
1042 $ hg serve -R bundle2onlyserver -p $HGPORT -d --pid-file=hg.pid
1043 $ cat hg.pid >> $DAEMON_PIDS
1043 $ cat hg.pid >> $DAEMON_PIDS
1044 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT not-bundle2
1044 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT not-bundle2
1045 requesting all changes
1045 requesting all changes
1046 abort: remote error:
1046 abort: remote error:
1047 incompatible Mercurial client; bundle2 required
1047 incompatible Mercurial client; bundle2 required
1048 (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
1048 (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
1049 [255]
1049 [255]
1050 $ killdaemons.py
1050 $ killdaemons.py
1051
1051
1052 $ hg --config devel.legacy.exchange=bundle1 clone ssh://user@dummy/bundle2onlyserver not-bundle2-ssh
1052 $ hg --config devel.legacy.exchange=bundle1 clone ssh://user@dummy/bundle2onlyserver not-bundle2-ssh
1053 requesting all changes
1053 requesting all changes
1054 adding changesets
1054 adding changesets
1055 remote: abort: incompatible Mercurial client; bundle2 required
1055 remote: abort: incompatible Mercurial client; bundle2 required
1056 remote: (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
1056 remote: (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
1057 transaction abort!
1057 transaction abort!
1058 rollback completed
1058 rollback completed
1059 abort: stream ended unexpectedly (got 0 bytes, expected 4)
1059 abort: stream ended unexpectedly (got 0 bytes, expected 4)
1060 [255]
1060 [255]
1061
1061
1062 $ cat > bundle2onlyserver/.hg/hgrc << EOF
1062 $ cat > bundle2onlyserver/.hg/hgrc << EOF
1063 > [server]
1063 > [server]
1064 > bundle1gd = false
1064 > bundle1gd = false
1065 > EOF
1065 > EOF
1066 $ hg serve -R bundle2onlyserver -p $HGPORT -d --pid-file=hg.pid
1066 $ hg serve -R bundle2onlyserver -p $HGPORT -d --pid-file=hg.pid
1067 $ cat hg.pid >> $DAEMON_PIDS
1067 $ cat hg.pid >> $DAEMON_PIDS
1068
1068
1069 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT/ not-bundle2
1069 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT/ not-bundle2
1070 requesting all changes
1070 requesting all changes
1071 abort: remote error:
1071 abort: remote error:
1072 incompatible Mercurial client; bundle2 required
1072 incompatible Mercurial client; bundle2 required
1073 (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
1073 (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
1074 [255]
1074 [255]
1075
1075
1076 $ killdaemons.py
1076 $ killdaemons.py
1077
1077
1078 $ cd notgdserver
1078 $ cd notgdserver
1079 $ cat > .hg/hgrc << EOF
1079 $ cat > .hg/hgrc << EOF
1080 > [server]
1080 > [server]
1081 > bundle1gd = false
1081 > bundle1gd = false
1082 > EOF
1082 > EOF
1083 $ hg serve -p $HGPORT -d --pid-file=hg.pid
1083 $ hg serve -p $HGPORT -d --pid-file=hg.pid
1084 $ cat hg.pid >> $DAEMON_PIDS
1084 $ cat hg.pid >> $DAEMON_PIDS
1085
1085
1086 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT/ not-bundle2-2
1086 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT/ not-bundle2-2
1087 requesting all changes
1087 requesting all changes
1088 adding changesets
1088 adding changesets
1089 adding manifests
1089 adding manifests
1090 adding file changes
1090 adding file changes
1091 added 1 changesets with 1 changes to 1 files
1091 added 1 changesets with 1 changes to 1 files
1092 new changesets 96ee1d7354c4
1092 new changesets 96ee1d7354c4
1093 updating to branch default
1093 updating to branch default
1094 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1094 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1095
1095
1096 $ killdaemons.py
1096 $ killdaemons.py
1097 $ cd ../bundle2onlyserver
1097 $ cd ../bundle2onlyserver
1098
1098
1099 Verify bundle1 pushes can be disabled
1099 Verify bundle1 pushes can be disabled
1100
1100
1101 $ cat > .hg/hgrc << EOF
1101 $ cat > .hg/hgrc << EOF
1102 > [server]
1102 > [server]
1103 > bundle1.push = false
1103 > bundle1.push = false
1104 > [web]
1104 > [web]
1105 > allow_push = *
1105 > allow_push = *
1106 > push_ssl = false
1106 > push_ssl = false
1107 > EOF
1107 > EOF
1108
1108
1109 $ hg serve -p $HGPORT -d --pid-file=hg.pid -E error.log
1109 $ hg serve -p $HGPORT -d --pid-file=hg.pid -E error.log
1110 $ cat hg.pid >> $DAEMON_PIDS
1110 $ cat hg.pid >> $DAEMON_PIDS
1111 $ cd ..
1111 $ cd ..
1112
1112
1113 $ hg clone http://localhost:$HGPORT bundle2-only
1113 $ hg clone http://localhost:$HGPORT bundle2-only
1114 requesting all changes
1114 requesting all changes
1115 adding changesets
1115 adding changesets
1116 adding manifests
1116 adding manifests
1117 adding file changes
1117 adding file changes
1118 added 1 changesets with 1 changes to 1 files
1118 added 1 changesets with 1 changes to 1 files
1119 new changesets 96ee1d7354c4
1119 new changesets 96ee1d7354c4
1120 updating to branch default
1120 updating to branch default
1121 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1121 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1122 $ cd bundle2-only
1122 $ cd bundle2-only
1123 $ echo commit > foo
1123 $ echo commit > foo
1124 $ hg commit -m commit
1124 $ hg commit -m commit
1125 $ hg --config devel.legacy.exchange=bundle1 push
1125 $ hg --config devel.legacy.exchange=bundle1 push
1126 pushing to http://localhost:$HGPORT/
1126 pushing to http://localhost:$HGPORT/
1127 searching for changes
1127 searching for changes
1128 abort: remote error:
1128 abort: remote error:
1129 incompatible Mercurial client; bundle2 required
1129 incompatible Mercurial client; bundle2 required
1130 (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
1130 (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
1131 [255]
1131 [255]
1132
1132
1133 (also check with ssh)
1133 (also check with ssh)
1134
1134
1135 $ hg --config devel.legacy.exchange=bundle1 push ssh://user@dummy/bundle2onlyserver
1135 $ hg --config devel.legacy.exchange=bundle1 push ssh://user@dummy/bundle2onlyserver
1136 pushing to ssh://user@dummy/bundle2onlyserver
1136 pushing to ssh://user@dummy/bundle2onlyserver
1137 searching for changes
1137 searching for changes
1138 remote: abort: incompatible Mercurial client; bundle2 required
1138 remote: abort: incompatible Mercurial client; bundle2 required
1139 remote: (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
1139 remote: (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
1140 [1]
1140 [1]
1141
1141
1142 $ hg push
1142 $ hg push
1143 pushing to http://localhost:$HGPORT/
1143 pushing to http://localhost:$HGPORT/
1144 searching for changes
1144 searching for changes
1145 remote: adding changesets
1145 remote: adding changesets
1146 remote: adding manifests
1146 remote: adding manifests
1147 remote: adding file changes
1147 remote: adding file changes
1148 remote: added 1 changesets with 1 changes to 1 files
1148 remote: added 1 changesets with 1 changes to 1 files
@@ -1,937 +1,937
1 commit hooks can see env vars
1 commit hooks can see env vars
2 (and post-transaction one are run unlocked)
2 (and post-transaction one are run unlocked)
3
3
4
4
5 $ cat > $TESTTMP/txnabort.checkargs.py <<EOF
5 $ cat > $TESTTMP/txnabort.checkargs.py <<EOF
6 > def showargs(ui, repo, hooktype, **kwargs):
6 > def showargs(ui, repo, hooktype, **kwargs):
7 > ui.write('%s Python hook: %s\n' % (hooktype, ','.join(sorted(kwargs))))
7 > ui.write('%s Python hook: %s\n' % (hooktype, ','.join(sorted(kwargs))))
8 > EOF
8 > EOF
9
9
10 $ hg init a
10 $ hg init a
11 $ cd a
11 $ cd a
12 $ cat > .hg/hgrc <<EOF
12 $ cat > .hg/hgrc <<EOF
13 > [hooks]
13 > [hooks]
14 > commit = sh -c "HG_LOCAL= HG_TAG= printenv.py commit"
14 > commit = sh -c "HG_LOCAL= HG_TAG= printenv.py commit"
15 > commit.b = sh -c "HG_LOCAL= HG_TAG= printenv.py commit.b"
15 > commit.b = sh -c "HG_LOCAL= HG_TAG= printenv.py commit.b"
16 > precommit = sh -c "HG_LOCAL= HG_NODE= HG_TAG= printenv.py precommit"
16 > precommit = sh -c "HG_LOCAL= HG_NODE= HG_TAG= printenv.py precommit"
17 > pretxncommit = sh -c "HG_LOCAL= HG_TAG= printenv.py pretxncommit"
17 > pretxncommit = sh -c "HG_LOCAL= HG_TAG= printenv.py pretxncommit"
18 > pretxncommit.tip = hg -q tip
18 > pretxncommit.tip = hg -q tip
19 > pre-identify = sh -c "printenv.py pre-identify 1"
19 > pre-identify = sh -c "printenv.py pre-identify 1"
20 > pre-cat = sh -c "printenv.py pre-cat"
20 > pre-cat = sh -c "printenv.py pre-cat"
21 > post-cat = sh -c "printenv.py post-cat"
21 > post-cat = sh -c "printenv.py post-cat"
22 > pretxnopen = sh -c "HG_LOCAL= HG_TAG= printenv.py pretxnopen"
22 > pretxnopen = sh -c "HG_LOCAL= HG_TAG= printenv.py pretxnopen"
23 > pretxnclose = sh -c "HG_LOCAL= HG_TAG= printenv.py pretxnclose"
23 > pretxnclose = sh -c "HG_LOCAL= HG_TAG= printenv.py pretxnclose"
24 > txnclose = sh -c "HG_LOCAL= HG_TAG= printenv.py txnclose"
24 > txnclose = sh -c "HG_LOCAL= HG_TAG= printenv.py txnclose"
25 > txnabort.0 = python:$TESTTMP/txnabort.checkargs.py:showargs
25 > txnabort.0 = python:$TESTTMP/txnabort.checkargs.py:showargs
26 > txnabort.1 = sh -c "HG_LOCAL= HG_TAG= printenv.py txnabort"
26 > txnabort.1 = sh -c "HG_LOCAL= HG_TAG= printenv.py txnabort"
27 > txnclose.checklock = sh -c "hg debuglock > /dev/null"
27 > txnclose.checklock = sh -c "hg debuglock > /dev/null"
28 > EOF
28 > EOF
29 $ echo a > a
29 $ echo a > a
30 $ hg add a
30 $ hg add a
31 $ hg commit -m a
31 $ hg commit -m a
32 precommit hook: HG_HOOKNAME=precommit HG_HOOKTYPE=precommit HG_PARENT1=0000000000000000000000000000000000000000
32 precommit hook: HG_HOOKNAME=precommit HG_HOOKTYPE=precommit HG_PARENT1=0000000000000000000000000000000000000000
33 pretxnopen hook: HG_HOOKNAME=pretxnopen HG_HOOKTYPE=pretxnopen HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
33 pretxnopen hook: HG_HOOKNAME=pretxnopen HG_HOOKTYPE=pretxnopen HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
34 pretxncommit hook: HG_HOOKNAME=pretxncommit HG_HOOKTYPE=pretxncommit HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000 HG_PENDING=$TESTTMP/a
34 pretxncommit hook: HG_HOOKNAME=pretxncommit HG_HOOKTYPE=pretxncommit HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000 HG_PENDING=$TESTTMP/a
35 0:cb9a9f314b8b
35 0:cb9a9f314b8b
36 pretxnclose hook: HG_HOOKNAME=pretxnclose HG_HOOKTYPE=pretxnclose HG_PENDING=$TESTTMP/a HG_PHASES_MOVED=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
36 pretxnclose hook: HG_HOOKNAME=pretxnclose HG_HOOKTYPE=pretxnclose HG_PENDING=$TESTTMP/a HG_PHASES_MOVED=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
37 txnclose hook: HG_HOOKNAME=txnclose HG_HOOKTYPE=txnclose HG_PHASES_MOVED=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
37 txnclose hook: HG_HOOKNAME=txnclose HG_HOOKTYPE=txnclose HG_PHASES_MOVED=1 HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
38 commit hook: HG_HOOKNAME=commit HG_HOOKTYPE=commit HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000
38 commit hook: HG_HOOKNAME=commit HG_HOOKTYPE=commit HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000
39 commit.b hook: HG_HOOKNAME=commit.b HG_HOOKTYPE=commit HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000
39 commit.b hook: HG_HOOKNAME=commit.b HG_HOOKTYPE=commit HG_NODE=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PARENT1=0000000000000000000000000000000000000000
40
40
41 $ hg clone . ../b
41 $ hg clone . ../b
42 updating to branch default
42 updating to branch default
43 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
43 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
44 $ cd ../b
44 $ cd ../b
45
45
46 changegroup hooks can see env vars
46 changegroup hooks can see env vars
47
47
48 $ cat > .hg/hgrc <<EOF
48 $ cat > .hg/hgrc <<EOF
49 > [hooks]
49 > [hooks]
50 > prechangegroup = sh -c "printenv.py prechangegroup"
50 > prechangegroup = sh -c "printenv.py prechangegroup"
51 > changegroup = sh -c "printenv.py changegroup"
51 > changegroup = sh -c "printenv.py changegroup"
52 > incoming = sh -c "printenv.py incoming"
52 > incoming = sh -c "printenv.py incoming"
53 > EOF
53 > EOF
54
54
55 pretxncommit and commit hooks can see both parents of merge
55 pretxncommit and commit hooks can see both parents of merge
56
56
57 $ cd ../a
57 $ cd ../a
58 $ echo b >> a
58 $ echo b >> a
59 $ hg commit -m a1 -d "1 0"
59 $ hg commit -m a1 -d "1 0"
60 precommit hook: HG_HOOKNAME=precommit HG_HOOKTYPE=precommit HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
60 precommit hook: HG_HOOKNAME=precommit HG_HOOKTYPE=precommit HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
61 pretxnopen hook: HG_HOOKNAME=pretxnopen HG_HOOKTYPE=pretxnopen HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
61 pretxnopen hook: HG_HOOKNAME=pretxnopen HG_HOOKTYPE=pretxnopen HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
62 pretxncommit hook: HG_HOOKNAME=pretxncommit HG_HOOKTYPE=pretxncommit HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PENDING=$TESTTMP/a
62 pretxncommit hook: HG_HOOKNAME=pretxncommit HG_HOOKTYPE=pretxncommit HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PENDING=$TESTTMP/a
63 1:ab228980c14d
63 1:ab228980c14d
64 pretxnclose hook: HG_HOOKNAME=pretxnclose HG_HOOKTYPE=pretxnclose HG_PENDING=$TESTTMP/a HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
64 pretxnclose hook: HG_HOOKNAME=pretxnclose HG_HOOKTYPE=pretxnclose HG_PENDING=$TESTTMP/a HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
65 txnclose hook: HG_HOOKNAME=txnclose HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
65 txnclose hook: HG_HOOKNAME=txnclose HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
66 commit hook: HG_HOOKNAME=commit HG_HOOKTYPE=commit HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
66 commit hook: HG_HOOKNAME=commit HG_HOOKTYPE=commit HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
67 commit.b hook: HG_HOOKNAME=commit.b HG_HOOKTYPE=commit HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
67 commit.b hook: HG_HOOKNAME=commit.b HG_HOOKTYPE=commit HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
68 $ hg update -C 0
68 $ hg update -C 0
69 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
69 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
70 $ echo b > b
70 $ echo b > b
71 $ hg add b
71 $ hg add b
72 $ hg commit -m b -d '1 0'
72 $ hg commit -m b -d '1 0'
73 precommit hook: HG_HOOKNAME=precommit HG_HOOKTYPE=precommit HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
73 precommit hook: HG_HOOKNAME=precommit HG_HOOKTYPE=precommit HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
74 pretxnopen hook: HG_HOOKNAME=pretxnopen HG_HOOKTYPE=pretxnopen HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
74 pretxnopen hook: HG_HOOKNAME=pretxnopen HG_HOOKTYPE=pretxnopen HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
75 pretxncommit hook: HG_HOOKNAME=pretxncommit HG_HOOKTYPE=pretxncommit HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PENDING=$TESTTMP/a
75 pretxncommit hook: HG_HOOKNAME=pretxncommit HG_HOOKTYPE=pretxncommit HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b HG_PENDING=$TESTTMP/a
76 2:ee9deb46ab31
76 2:ee9deb46ab31
77 pretxnclose hook: HG_HOOKNAME=pretxnclose HG_HOOKTYPE=pretxnclose HG_PENDING=$TESTTMP/a HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
77 pretxnclose hook: HG_HOOKNAME=pretxnclose HG_HOOKTYPE=pretxnclose HG_PENDING=$TESTTMP/a HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
78 created new head
78 created new head
79 txnclose hook: HG_HOOKNAME=txnclose HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
79 txnclose hook: HG_HOOKNAME=txnclose HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
80 commit hook: HG_HOOKNAME=commit HG_HOOKTYPE=commit HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
80 commit hook: HG_HOOKNAME=commit HG_HOOKTYPE=commit HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
81 commit.b hook: HG_HOOKNAME=commit.b HG_HOOKTYPE=commit HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
81 commit.b hook: HG_HOOKNAME=commit.b HG_HOOKTYPE=commit HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT1=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b
82 $ hg merge 1
82 $ hg merge 1
83 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
83 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
84 (branch merge, don't forget to commit)
84 (branch merge, don't forget to commit)
85 $ hg commit -m merge -d '2 0'
85 $ hg commit -m merge -d '2 0'
86 precommit hook: HG_HOOKNAME=precommit HG_HOOKTYPE=precommit HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
86 precommit hook: HG_HOOKNAME=precommit HG_HOOKTYPE=precommit HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
87 pretxnopen hook: HG_HOOKNAME=pretxnopen HG_HOOKTYPE=pretxnopen HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
87 pretxnopen hook: HG_HOOKNAME=pretxnopen HG_HOOKTYPE=pretxnopen HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
88 pretxncommit hook: HG_HOOKNAME=pretxncommit HG_HOOKTYPE=pretxncommit HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd HG_PENDING=$TESTTMP/a
88 pretxncommit hook: HG_HOOKNAME=pretxncommit HG_HOOKTYPE=pretxncommit HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd HG_PENDING=$TESTTMP/a
89 3:07f3376c1e65
89 3:07f3376c1e65
90 pretxnclose hook: HG_HOOKNAME=pretxnclose HG_HOOKTYPE=pretxnclose HG_PENDING=$TESTTMP/a HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
90 pretxnclose hook: HG_HOOKNAME=pretxnclose HG_HOOKTYPE=pretxnclose HG_PENDING=$TESTTMP/a HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
91 txnclose hook: HG_HOOKNAME=txnclose HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
91 txnclose hook: HG_HOOKNAME=txnclose HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
92 commit hook: HG_HOOKNAME=commit HG_HOOKTYPE=commit HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
92 commit hook: HG_HOOKNAME=commit HG_HOOKTYPE=commit HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
93 commit.b hook: HG_HOOKNAME=commit.b HG_HOOKTYPE=commit HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
93 commit.b hook: HG_HOOKNAME=commit.b HG_HOOKTYPE=commit HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PARENT1=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_PARENT2=ab228980c14deea8b9555d91c9581127383e40fd
94
94
95 test generic hooks
95 test generic hooks
96
96
97 $ hg id
97 $ hg id
98 pre-identify hook: HG_ARGS=id HG_HOOKNAME=pre-identify HG_HOOKTYPE=pre-identify HG_OPTS={'bookmarks': None, 'branch': None, 'id': None, 'insecure': None, 'num': None, 'remotecmd': '', 'rev': '', 'ssh': '', 'tags': None, 'template': ''} HG_PATS=[]
98 pre-identify hook: HG_ARGS=id HG_HOOKNAME=pre-identify HG_HOOKTYPE=pre-identify HG_OPTS={'bookmarks': None, 'branch': None, 'id': None, 'insecure': None, 'num': None, 'remotecmd': '', 'rev': '', 'ssh': '', 'tags': None, 'template': ''} HG_PATS=[]
99 abort: pre-identify hook exited with status 1
99 abort: pre-identify hook exited with status 1
100 [255]
100 [255]
101 $ hg cat b
101 $ hg cat b
102 pre-cat hook: HG_ARGS=cat b HG_HOOKNAME=pre-cat HG_HOOKTYPE=pre-cat HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': '', 'template': ''} HG_PATS=['b']
102 pre-cat hook: HG_ARGS=cat b HG_HOOKNAME=pre-cat HG_HOOKTYPE=pre-cat HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': '', 'template': ''} HG_PATS=['b']
103 b
103 b
104 post-cat hook: HG_ARGS=cat b HG_HOOKNAME=post-cat HG_HOOKTYPE=post-cat HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': '', 'template': ''} HG_PATS=['b'] HG_RESULT=0
104 post-cat hook: HG_ARGS=cat b HG_HOOKNAME=post-cat HG_HOOKTYPE=post-cat HG_OPTS={'decode': None, 'exclude': [], 'include': [], 'output': '', 'rev': '', 'template': ''} HG_PATS=['b'] HG_RESULT=0
105
105
106 $ cd ../b
106 $ cd ../b
107 $ hg pull ../a
107 $ hg pull ../a
108 pulling from ../a
108 pulling from ../a
109 searching for changes
109 searching for changes
110 prechangegroup hook: HG_HOOKNAME=prechangegroup HG_HOOKTYPE=prechangegroup HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_URL=file:$TESTTMP/a
110 prechangegroup hook: HG_HOOKNAME=prechangegroup HG_HOOKTYPE=prechangegroup HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_URL=file:$TESTTMP/a
111 adding changesets
111 adding changesets
112 adding manifests
112 adding manifests
113 adding file changes
113 adding file changes
114 added 3 changesets with 2 changes to 2 files
114 added 3 changesets with 2 changes to 2 files
115 new changesets ab228980c14d:07f3376c1e65
115 new changesets ab228980c14d:07f3376c1e65
116 changegroup hook: HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_NODE_LAST=07f3376c1e655977439df2a814e3cc14b27abac2 HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_URL=file:$TESTTMP/a
116 changegroup hook: HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_NODE_LAST=07f3376c1e655977439df2a814e3cc14b27abac2 HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_URL=file:$TESTTMP/a
117 incoming hook: HG_HOOKNAME=incoming HG_HOOKTYPE=incoming HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_URL=file:$TESTTMP/a
117 incoming hook: HG_HOOKNAME=incoming HG_HOOKTYPE=incoming HG_NODE=ab228980c14deea8b9555d91c9581127383e40fd HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_URL=file:$TESTTMP/a
118 incoming hook: HG_HOOKNAME=incoming HG_HOOKTYPE=incoming HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_URL=file:$TESTTMP/a
118 incoming hook: HG_HOOKNAME=incoming HG_HOOKTYPE=incoming HG_NODE=ee9deb46ab31e4cc3310f3cf0c3d668e4d8fffc2 HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_URL=file:$TESTTMP/a
119 incoming hook: HG_HOOKNAME=incoming HG_HOOKTYPE=incoming HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_URL=file:$TESTTMP/a
119 incoming hook: HG_HOOKNAME=incoming HG_HOOKTYPE=incoming HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_URL=file:$TESTTMP/a
120 (run 'hg update' to get a working copy)
120 (run 'hg update' to get a working copy)
121
121
122 tag hooks can see env vars
122 tag hooks can see env vars
123
123
124 $ cd ../a
124 $ cd ../a
125 $ cat >> .hg/hgrc <<EOF
125 $ cat >> .hg/hgrc <<EOF
126 > pretag = sh -c "printenv.py pretag"
126 > pretag = sh -c "printenv.py pretag"
127 > tag = sh -c "HG_PARENT1= HG_PARENT2= printenv.py tag"
127 > tag = sh -c "HG_PARENT1= HG_PARENT2= printenv.py tag"
128 > EOF
128 > EOF
129 $ hg tag -d '3 0' a
129 $ hg tag -d '3 0' a
130 pretag hook: HG_HOOKNAME=pretag HG_HOOKTYPE=pretag HG_LOCAL=0 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_TAG=a
130 pretag hook: HG_HOOKNAME=pretag HG_HOOKTYPE=pretag HG_LOCAL=0 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_TAG=a
131 precommit hook: HG_HOOKNAME=precommit HG_HOOKTYPE=precommit HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
131 precommit hook: HG_HOOKNAME=precommit HG_HOOKTYPE=precommit HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
132 pretxnopen hook: HG_HOOKNAME=pretxnopen HG_HOOKTYPE=pretxnopen HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
132 pretxnopen hook: HG_HOOKNAME=pretxnopen HG_HOOKTYPE=pretxnopen HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
133 pretxncommit hook: HG_HOOKNAME=pretxncommit HG_HOOKTYPE=pretxncommit HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PENDING=$TESTTMP/a
133 pretxncommit hook: HG_HOOKNAME=pretxncommit HG_HOOKTYPE=pretxncommit HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2 HG_PENDING=$TESTTMP/a
134 4:539e4b31b6dc
134 4:539e4b31b6dc
135 pretxnclose hook: HG_HOOKNAME=pretxnclose HG_HOOKTYPE=pretxnclose HG_PENDING=$TESTTMP/a HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
135 pretxnclose hook: HG_HOOKNAME=pretxnclose HG_HOOKTYPE=pretxnclose HG_PENDING=$TESTTMP/a HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
136 tag hook: HG_HOOKNAME=tag HG_HOOKTYPE=tag HG_LOCAL=0 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_TAG=a
136 tag hook: HG_HOOKNAME=tag HG_HOOKTYPE=tag HG_LOCAL=0 HG_NODE=07f3376c1e655977439df2a814e3cc14b27abac2 HG_TAG=a
137 txnclose hook: HG_HOOKNAME=txnclose HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
137 txnclose hook: HG_HOOKNAME=txnclose HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
138 commit hook: HG_HOOKNAME=commit HG_HOOKTYPE=commit HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
138 commit hook: HG_HOOKNAME=commit HG_HOOKTYPE=commit HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
139 commit.b hook: HG_HOOKNAME=commit.b HG_HOOKTYPE=commit HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
139 commit.b hook: HG_HOOKNAME=commit.b HG_HOOKTYPE=commit HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PARENT1=07f3376c1e655977439df2a814e3cc14b27abac2
140 $ hg tag -l la
140 $ hg tag -l la
141 pretag hook: HG_HOOKNAME=pretag HG_HOOKTYPE=pretag HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=la
141 pretag hook: HG_HOOKNAME=pretag HG_HOOKTYPE=pretag HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=la
142 tag hook: HG_HOOKNAME=tag HG_HOOKTYPE=tag HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=la
142 tag hook: HG_HOOKNAME=tag HG_HOOKTYPE=tag HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=la
143
143
144 pretag hook can forbid tagging
144 pretag hook can forbid tagging
145
145
146 $ cat >> .hg/hgrc <<EOF
146 $ cat >> .hg/hgrc <<EOF
147 > pretag.forbid = sh -c "printenv.py pretag.forbid 1"
147 > pretag.forbid = sh -c "printenv.py pretag.forbid 1"
148 > EOF
148 > EOF
149 $ hg tag -d '4 0' fa
149 $ hg tag -d '4 0' fa
150 pretag hook: HG_HOOKNAME=pretag HG_HOOKTYPE=pretag HG_LOCAL=0 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fa
150 pretag hook: HG_HOOKNAME=pretag HG_HOOKTYPE=pretag HG_LOCAL=0 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fa
151 pretag.forbid hook: HG_HOOKNAME=pretag.forbid HG_HOOKTYPE=pretag HG_LOCAL=0 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fa
151 pretag.forbid hook: HG_HOOKNAME=pretag.forbid HG_HOOKTYPE=pretag HG_LOCAL=0 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fa
152 abort: pretag.forbid hook exited with status 1
152 abort: pretag.forbid hook exited with status 1
153 [255]
153 [255]
154 $ hg tag -l fla
154 $ hg tag -l fla
155 pretag hook: HG_HOOKNAME=pretag HG_HOOKTYPE=pretag HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fla
155 pretag hook: HG_HOOKNAME=pretag HG_HOOKTYPE=pretag HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fla
156 pretag.forbid hook: HG_HOOKNAME=pretag.forbid HG_HOOKTYPE=pretag HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fla
156 pretag.forbid hook: HG_HOOKNAME=pretag.forbid HG_HOOKTYPE=pretag HG_LOCAL=1 HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_TAG=fla
157 abort: pretag.forbid hook exited with status 1
157 abort: pretag.forbid hook exited with status 1
158 [255]
158 [255]
159
159
160 pretxncommit hook can see changeset, can roll back txn, changeset no
160 pretxncommit hook can see changeset, can roll back txn, changeset no
161 more there after
161 more there after
162
162
163 $ cat >> .hg/hgrc <<EOF
163 $ cat >> .hg/hgrc <<EOF
164 > pretxncommit.forbid0 = sh -c "hg tip -q"
164 > pretxncommit.forbid0 = sh -c "hg tip -q"
165 > pretxncommit.forbid1 = sh -c "printenv.py pretxncommit.forbid 1"
165 > pretxncommit.forbid1 = sh -c "printenv.py pretxncommit.forbid 1"
166 > EOF
166 > EOF
167 $ echo z > z
167 $ echo z > z
168 $ hg add z
168 $ hg add z
169 $ hg -q tip
169 $ hg -q tip
170 4:539e4b31b6dc
170 4:539e4b31b6dc
171 $ hg commit -m 'fail' -d '4 0'
171 $ hg commit -m 'fail' -d '4 0'
172 precommit hook: HG_HOOKNAME=precommit HG_HOOKTYPE=precommit HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
172 precommit hook: HG_HOOKNAME=precommit HG_HOOKTYPE=precommit HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
173 pretxnopen hook: HG_HOOKNAME=pretxnopen HG_HOOKTYPE=pretxnopen HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
173 pretxnopen hook: HG_HOOKNAME=pretxnopen HG_HOOKTYPE=pretxnopen HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
174 pretxncommit hook: HG_HOOKNAME=pretxncommit HG_HOOKTYPE=pretxncommit HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/a
174 pretxncommit hook: HG_HOOKNAME=pretxncommit HG_HOOKTYPE=pretxncommit HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/a
175 5:6f611f8018c1
175 5:6f611f8018c1
176 5:6f611f8018c1
176 5:6f611f8018c1
177 pretxncommit.forbid hook: HG_HOOKNAME=pretxncommit.forbid1 HG_HOOKTYPE=pretxncommit HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/a
177 pretxncommit.forbid hook: HG_HOOKNAME=pretxncommit.forbid1 HG_HOOKTYPE=pretxncommit HG_NODE=6f611f8018c10e827fee6bd2bc807f937e761567 HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/a
178 transaction abort!
178 transaction abort!
179 txnabort Python hook: txnid,txnname
179 txnabort Python hook: txnid,txnname
180 txnabort hook: HG_HOOKNAME=txnabort.1 HG_HOOKTYPE=txnabort HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
180 txnabort hook: HG_HOOKNAME=txnabort.1 HG_HOOKTYPE=txnabort HG_TXNID=TXN:$ID$ HG_TXNNAME=commit
181 rollback completed
181 rollback completed
182 abort: pretxncommit.forbid1 hook exited with status 1
182 abort: pretxncommit.forbid1 hook exited with status 1
183 [255]
183 [255]
184 $ hg -q tip
184 $ hg -q tip
185 4:539e4b31b6dc
185 4:539e4b31b6dc
186
186
187 (Check that no 'changelog.i.a' file were left behind)
187 (Check that no 'changelog.i.a' file were left behind)
188
188
189 $ ls -1 .hg/store/
189 $ ls -1 .hg/store/
190 00changelog.i
190 00changelog.i
191 00manifest.i
191 00manifest.i
192 data
192 data
193 fncache
193 fncache
194 journal.phaseroots
194 journal.phaseroots
195 phaseroots
195 phaseroots
196 undo
196 undo
197 undo.backup.fncache
197 undo.backup.fncache
198 undo.backupfiles
198 undo.backupfiles
199 undo.phaseroots
199 undo.phaseroots
200
200
201
201
202 precommit hook can prevent commit
202 precommit hook can prevent commit
203
203
204 $ cat >> .hg/hgrc <<EOF
204 $ cat >> .hg/hgrc <<EOF
205 > precommit.forbid = sh -c "printenv.py precommit.forbid 1"
205 > precommit.forbid = sh -c "printenv.py precommit.forbid 1"
206 > EOF
206 > EOF
207 $ hg commit -m 'fail' -d '4 0'
207 $ hg commit -m 'fail' -d '4 0'
208 precommit hook: HG_HOOKNAME=precommit HG_HOOKTYPE=precommit HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
208 precommit hook: HG_HOOKNAME=precommit HG_HOOKTYPE=precommit HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
209 precommit.forbid hook: HG_HOOKNAME=precommit.forbid HG_HOOKTYPE=precommit HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
209 precommit.forbid hook: HG_HOOKNAME=precommit.forbid HG_HOOKTYPE=precommit HG_PARENT1=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10
210 abort: precommit.forbid hook exited with status 1
210 abort: precommit.forbid hook exited with status 1
211 [255]
211 [255]
212 $ hg -q tip
212 $ hg -q tip
213 4:539e4b31b6dc
213 4:539e4b31b6dc
214
214
215 preupdate hook can prevent update
215 preupdate hook can prevent update
216
216
217 $ cat >> .hg/hgrc <<EOF
217 $ cat >> .hg/hgrc <<EOF
218 > preupdate = sh -c "printenv.py preupdate"
218 > preupdate = sh -c "printenv.py preupdate"
219 > EOF
219 > EOF
220 $ hg update 1
220 $ hg update 1
221 preupdate hook: HG_HOOKNAME=preupdate HG_HOOKTYPE=preupdate HG_PARENT1=ab228980c14d
221 preupdate hook: HG_HOOKNAME=preupdate HG_HOOKTYPE=preupdate HG_PARENT1=ab228980c14d
222 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
222 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
223
223
224 update hook
224 update hook
225
225
226 $ cat >> .hg/hgrc <<EOF
226 $ cat >> .hg/hgrc <<EOF
227 > update = sh -c "printenv.py update"
227 > update = sh -c "printenv.py update"
228 > EOF
228 > EOF
229 $ hg update
229 $ hg update
230 preupdate hook: HG_HOOKNAME=preupdate HG_HOOKTYPE=preupdate HG_PARENT1=539e4b31b6dc
230 preupdate hook: HG_HOOKNAME=preupdate HG_HOOKTYPE=preupdate HG_PARENT1=539e4b31b6dc
231 update hook: HG_ERROR=0 HG_HOOKNAME=update HG_HOOKTYPE=update HG_PARENT1=539e4b31b6dc
231 update hook: HG_ERROR=0 HG_HOOKNAME=update HG_HOOKTYPE=update HG_PARENT1=539e4b31b6dc
232 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
232 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
233
233
234 pushkey hook
234 pushkey hook
235
235
236 $ cat >> .hg/hgrc <<EOF
236 $ cat >> .hg/hgrc <<EOF
237 > pushkey = sh -c "printenv.py pushkey"
237 > pushkey = sh -c "printenv.py pushkey"
238 > EOF
238 > EOF
239 $ cd ../b
239 $ cd ../b
240 $ hg bookmark -r null foo
240 $ hg bookmark -r null foo
241 $ hg push -B foo ../a
241 $ hg push -B foo ../a
242 pushing to ../a
242 pushing to ../a
243 searching for changes
243 searching for changes
244 no changes found
244 no changes found
245 pretxnopen hook: HG_HOOKNAME=pretxnopen HG_HOOKTYPE=pretxnopen HG_TXNID=TXN:$ID$ HG_TXNNAME=push
245 pretxnopen hook: HG_HOOKNAME=pretxnopen HG_HOOKTYPE=pretxnopen HG_TXNID=TXN:$ID$ HG_TXNNAME=push
246 pretxnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_HOOKNAME=pretxnclose HG_HOOKTYPE=pretxnclose HG_PENDING=$TESTTMP/a HG_SOURCE=push HG_TXNID=TXN:$ID$ HG_TXNNAME=push HG_URL=file:$TESTTMP/a
246 pretxnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_HOOKNAME=pretxnclose HG_HOOKTYPE=pretxnclose HG_PENDING=$TESTTMP/a HG_SOURCE=push HG_TXNID=TXN:$ID$ HG_TXNNAME=push HG_URL=file:$TESTTMP/a
247 pushkey hook: HG_BUNDLE2=1 HG_HOOKNAME=pushkey HG_HOOKTYPE=pushkey HG_KEY=foo HG_NAMESPACE=bookmark HG_NEW=0000000000000000000000000000000000000000 HG_PUSHKEYCOMPAT=1 HG_SOURCE=push HG_TXNID=TXN:$ID$ HG_URL=file:$TESTTMP/a
247 pushkey hook: HG_BUNDLE2=1 HG_HOOKNAME=pushkey HG_HOOKTYPE=pushkey HG_KEY=foo HG_NAMESPACE=bookmarks HG_NEW=0000000000000000000000000000000000000000 HG_PUSHKEYCOMPAT=1 HG_SOURCE=push HG_TXNID=TXN:$ID$ HG_URL=file:$TESTTMP/a
248 txnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_HOOKNAME=txnclose HG_HOOKTYPE=txnclose HG_SOURCE=push HG_TXNID=TXN:$ID$ HG_TXNNAME=push HG_URL=file:$TESTTMP/a
248 txnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_HOOKNAME=txnclose HG_HOOKTYPE=txnclose HG_SOURCE=push HG_TXNID=TXN:$ID$ HG_TXNNAME=push HG_URL=file:$TESTTMP/a
249 exporting bookmark foo
249 exporting bookmark foo
250 [1]
250 [1]
251 $ cd ../a
251 $ cd ../a
252
252
253 listkeys hook
253 listkeys hook
254
254
255 $ cat >> .hg/hgrc <<EOF
255 $ cat >> .hg/hgrc <<EOF
256 > listkeys = sh -c "printenv.py listkeys"
256 > listkeys = sh -c "printenv.py listkeys"
257 > EOF
257 > EOF
258 $ hg bookmark -r null bar
258 $ hg bookmark -r null bar
259 pretxnopen hook: HG_HOOKNAME=pretxnopen HG_HOOKTYPE=pretxnopen HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
259 pretxnopen hook: HG_HOOKNAME=pretxnopen HG_HOOKTYPE=pretxnopen HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
260 pretxnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=pretxnclose HG_HOOKTYPE=pretxnclose HG_PENDING=$TESTTMP/a HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
260 pretxnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=pretxnclose HG_HOOKTYPE=pretxnclose HG_PENDING=$TESTTMP/a HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
261 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
261 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
262 $ cd ../b
262 $ cd ../b
263 $ hg pull -B bar ../a
263 $ hg pull -B bar ../a
264 pulling from ../a
264 pulling from ../a
265 listkeys hook: HG_HOOKNAME=listkeys HG_HOOKTYPE=listkeys HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
265 listkeys hook: HG_HOOKNAME=listkeys HG_HOOKTYPE=listkeys HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
266 no changes found
266 no changes found
267 adding remote bookmark bar
267 adding remote bookmark bar
268 $ cd ../a
268 $ cd ../a
269
269
270 test that prepushkey can prevent incoming keys
270 test that prepushkey can prevent incoming keys
271
271
272 $ cat >> .hg/hgrc <<EOF
272 $ cat >> .hg/hgrc <<EOF
273 > prepushkey = sh -c "printenv.py prepushkey.forbid 1"
273 > prepushkey = sh -c "printenv.py prepushkey.forbid 1"
274 > EOF
274 > EOF
275 $ cd ../b
275 $ cd ../b
276 $ hg bookmark -r null baz
276 $ hg bookmark -r null baz
277 $ hg push -B baz ../a
277 $ hg push -B baz ../a
278 pushing to ../a
278 pushing to ../a
279 searching for changes
279 searching for changes
280 listkeys hook: HG_HOOKNAME=listkeys HG_HOOKTYPE=listkeys HG_NAMESPACE=phases HG_VALUES={'cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b': '1', 'publishing': 'True'}
280 listkeys hook: HG_HOOKNAME=listkeys HG_HOOKTYPE=listkeys HG_NAMESPACE=phases HG_VALUES={'cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b': '1', 'publishing': 'True'}
281 listkeys hook: HG_HOOKNAME=listkeys HG_HOOKTYPE=listkeys HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
281 listkeys hook: HG_HOOKNAME=listkeys HG_HOOKTYPE=listkeys HG_NAMESPACE=bookmarks HG_VALUES={'bar': '0000000000000000000000000000000000000000', 'foo': '0000000000000000000000000000000000000000'}
282 no changes found
282 no changes found
283 pretxnopen hook: HG_HOOKNAME=pretxnopen HG_HOOKTYPE=pretxnopen HG_TXNID=TXN:$ID$ HG_TXNNAME=push
283 pretxnopen hook: HG_HOOKNAME=pretxnopen HG_HOOKTYPE=pretxnopen HG_TXNID=TXN:$ID$ HG_TXNNAME=push
284 prepushkey.forbid hook: HG_BUNDLE2=1 HG_HOOKNAME=prepushkey HG_HOOKTYPE=prepushkey HG_KEY=baz HG_NAMESPACE=bookmark HG_NEW=0000000000000000000000000000000000000000 HG_PUSHKEYCOMPAT=1 HG_SOURCE=push HG_TXNID=TXN:$ID$ HG_URL=file:$TESTTMP/a
284 prepushkey.forbid hook: HG_BUNDLE2=1 HG_HOOKNAME=prepushkey HG_HOOKTYPE=prepushkey HG_KEY=baz HG_NAMESPACE=bookmarks HG_NEW=0000000000000000000000000000000000000000 HG_PUSHKEYCOMPAT=1 HG_SOURCE=push HG_TXNID=TXN:$ID$ HG_URL=file:$TESTTMP/a
285 abort: prepushkey hook exited with status 1
285 abort: prepushkey hook exited with status 1
286 [255]
286 [255]
287 $ cd ../a
287 $ cd ../a
288
288
289 test that prelistkeys can prevent listing keys
289 test that prelistkeys can prevent listing keys
290
290
291 $ cat >> .hg/hgrc <<EOF
291 $ cat >> .hg/hgrc <<EOF
292 > prelistkeys = sh -c "printenv.py prelistkeys.forbid 1"
292 > prelistkeys = sh -c "printenv.py prelistkeys.forbid 1"
293 > EOF
293 > EOF
294 $ hg bookmark -r null quux
294 $ hg bookmark -r null quux
295 pretxnopen hook: HG_HOOKNAME=pretxnopen HG_HOOKTYPE=pretxnopen HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
295 pretxnopen hook: HG_HOOKNAME=pretxnopen HG_HOOKTYPE=pretxnopen HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
296 pretxnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=pretxnclose HG_HOOKTYPE=pretxnclose HG_PENDING=$TESTTMP/a HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
296 pretxnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=pretxnclose HG_HOOKTYPE=pretxnclose HG_PENDING=$TESTTMP/a HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
297 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
297 txnclose hook: HG_BOOKMARK_MOVED=1 HG_HOOKNAME=txnclose HG_HOOKTYPE=txnclose HG_TXNID=TXN:$ID$ HG_TXNNAME=bookmark
298 $ cd ../b
298 $ cd ../b
299 $ hg pull -B quux ../a
299 $ hg pull -B quux ../a
300 pulling from ../a
300 pulling from ../a
301 prelistkeys.forbid hook: HG_HOOKNAME=prelistkeys HG_HOOKTYPE=prelistkeys HG_NAMESPACE=bookmarks
301 prelistkeys.forbid hook: HG_HOOKNAME=prelistkeys HG_HOOKTYPE=prelistkeys HG_NAMESPACE=bookmarks
302 abort: prelistkeys hook exited with status 1
302 abort: prelistkeys hook exited with status 1
303 [255]
303 [255]
304 $ cd ../a
304 $ cd ../a
305 $ rm .hg/hgrc
305 $ rm .hg/hgrc
306
306
307 prechangegroup hook can prevent incoming changes
307 prechangegroup hook can prevent incoming changes
308
308
309 $ cd ../b
309 $ cd ../b
310 $ hg -q tip
310 $ hg -q tip
311 3:07f3376c1e65
311 3:07f3376c1e65
312 $ cat > .hg/hgrc <<EOF
312 $ cat > .hg/hgrc <<EOF
313 > [hooks]
313 > [hooks]
314 > prechangegroup.forbid = sh -c "printenv.py prechangegroup.forbid 1"
314 > prechangegroup.forbid = sh -c "printenv.py prechangegroup.forbid 1"
315 > EOF
315 > EOF
316 $ hg pull ../a
316 $ hg pull ../a
317 pulling from ../a
317 pulling from ../a
318 searching for changes
318 searching for changes
319 prechangegroup.forbid hook: HG_HOOKNAME=prechangegroup.forbid HG_HOOKTYPE=prechangegroup HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_URL=file:$TESTTMP/a
319 prechangegroup.forbid hook: HG_HOOKNAME=prechangegroup.forbid HG_HOOKTYPE=prechangegroup HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_URL=file:$TESTTMP/a
320 abort: prechangegroup.forbid hook exited with status 1
320 abort: prechangegroup.forbid hook exited with status 1
321 [255]
321 [255]
322
322
323 pretxnchangegroup hook can see incoming changes, can roll back txn,
323 pretxnchangegroup hook can see incoming changes, can roll back txn,
324 incoming changes no longer there after
324 incoming changes no longer there after
325
325
326 $ cat > .hg/hgrc <<EOF
326 $ cat > .hg/hgrc <<EOF
327 > [hooks]
327 > [hooks]
328 > pretxnchangegroup.forbid0 = hg tip -q
328 > pretxnchangegroup.forbid0 = hg tip -q
329 > pretxnchangegroup.forbid1 = sh -c "printenv.py pretxnchangegroup.forbid 1"
329 > pretxnchangegroup.forbid1 = sh -c "printenv.py pretxnchangegroup.forbid 1"
330 > EOF
330 > EOF
331 $ hg pull ../a
331 $ hg pull ../a
332 pulling from ../a
332 pulling from ../a
333 searching for changes
333 searching for changes
334 adding changesets
334 adding changesets
335 adding manifests
335 adding manifests
336 adding file changes
336 adding file changes
337 added 1 changesets with 1 changes to 1 files
337 added 1 changesets with 1 changes to 1 files
338 4:539e4b31b6dc
338 4:539e4b31b6dc
339 pretxnchangegroup.forbid hook: HG_HOOKNAME=pretxnchangegroup.forbid1 HG_HOOKTYPE=pretxnchangegroup HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_NODE_LAST=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/b HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_URL=file:$TESTTMP/a
339 pretxnchangegroup.forbid hook: HG_HOOKNAME=pretxnchangegroup.forbid1 HG_HOOKTYPE=pretxnchangegroup HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_NODE_LAST=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_PENDING=$TESTTMP/b HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_URL=file:$TESTTMP/a
340 transaction abort!
340 transaction abort!
341 rollback completed
341 rollback completed
342 abort: pretxnchangegroup.forbid1 hook exited with status 1
342 abort: pretxnchangegroup.forbid1 hook exited with status 1
343 [255]
343 [255]
344 $ hg -q tip
344 $ hg -q tip
345 3:07f3376c1e65
345 3:07f3376c1e65
346
346
347 outgoing hooks can see env vars
347 outgoing hooks can see env vars
348
348
349 $ rm .hg/hgrc
349 $ rm .hg/hgrc
350 $ cat > ../a/.hg/hgrc <<EOF
350 $ cat > ../a/.hg/hgrc <<EOF
351 > [hooks]
351 > [hooks]
352 > preoutgoing = sh -c "printenv.py preoutgoing"
352 > preoutgoing = sh -c "printenv.py preoutgoing"
353 > outgoing = sh -c "printenv.py outgoing"
353 > outgoing = sh -c "printenv.py outgoing"
354 > EOF
354 > EOF
355 $ hg pull ../a
355 $ hg pull ../a
356 pulling from ../a
356 pulling from ../a
357 searching for changes
357 searching for changes
358 preoutgoing hook: HG_HOOKNAME=preoutgoing HG_HOOKTYPE=preoutgoing HG_SOURCE=pull
358 preoutgoing hook: HG_HOOKNAME=preoutgoing HG_HOOKTYPE=preoutgoing HG_SOURCE=pull
359 outgoing hook: HG_HOOKNAME=outgoing HG_HOOKTYPE=outgoing HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_SOURCE=pull
359 outgoing hook: HG_HOOKNAME=outgoing HG_HOOKTYPE=outgoing HG_NODE=539e4b31b6dc99b3cfbaa6b53cbc1c1f9a1e3a10 HG_SOURCE=pull
360 adding changesets
360 adding changesets
361 adding manifests
361 adding manifests
362 adding file changes
362 adding file changes
363 added 1 changesets with 1 changes to 1 files
363 added 1 changesets with 1 changes to 1 files
364 adding remote bookmark quux
364 adding remote bookmark quux
365 new changesets 539e4b31b6dc
365 new changesets 539e4b31b6dc
366 (run 'hg update' to get a working copy)
366 (run 'hg update' to get a working copy)
367 $ hg rollback
367 $ hg rollback
368 repository tip rolled back to revision 3 (undo pull)
368 repository tip rolled back to revision 3 (undo pull)
369
369
370 preoutgoing hook can prevent outgoing changes
370 preoutgoing hook can prevent outgoing changes
371
371
372 $ cat >> ../a/.hg/hgrc <<EOF
372 $ cat >> ../a/.hg/hgrc <<EOF
373 > preoutgoing.forbid = sh -c "printenv.py preoutgoing.forbid 1"
373 > preoutgoing.forbid = sh -c "printenv.py preoutgoing.forbid 1"
374 > EOF
374 > EOF
375 $ hg pull ../a
375 $ hg pull ../a
376 pulling from ../a
376 pulling from ../a
377 searching for changes
377 searching for changes
378 preoutgoing hook: HG_HOOKNAME=preoutgoing HG_HOOKTYPE=preoutgoing HG_SOURCE=pull
378 preoutgoing hook: HG_HOOKNAME=preoutgoing HG_HOOKTYPE=preoutgoing HG_SOURCE=pull
379 preoutgoing.forbid hook: HG_HOOKNAME=preoutgoing.forbid HG_HOOKTYPE=preoutgoing HG_SOURCE=pull
379 preoutgoing.forbid hook: HG_HOOKNAME=preoutgoing.forbid HG_HOOKTYPE=preoutgoing HG_SOURCE=pull
380 abort: preoutgoing.forbid hook exited with status 1
380 abort: preoutgoing.forbid hook exited with status 1
381 [255]
381 [255]
382
382
383 outgoing hooks work for local clones
383 outgoing hooks work for local clones
384
384
385 $ cd ..
385 $ cd ..
386 $ cat > a/.hg/hgrc <<EOF
386 $ cat > a/.hg/hgrc <<EOF
387 > [hooks]
387 > [hooks]
388 > preoutgoing = sh -c "printenv.py preoutgoing"
388 > preoutgoing = sh -c "printenv.py preoutgoing"
389 > outgoing = sh -c "printenv.py outgoing"
389 > outgoing = sh -c "printenv.py outgoing"
390 > EOF
390 > EOF
391 $ hg clone a c
391 $ hg clone a c
392 preoutgoing hook: HG_HOOKNAME=preoutgoing HG_HOOKTYPE=preoutgoing HG_SOURCE=clone
392 preoutgoing hook: HG_HOOKNAME=preoutgoing HG_HOOKTYPE=preoutgoing HG_SOURCE=clone
393 outgoing hook: HG_HOOKNAME=outgoing HG_HOOKTYPE=outgoing HG_NODE=0000000000000000000000000000000000000000 HG_SOURCE=clone
393 outgoing hook: HG_HOOKNAME=outgoing HG_HOOKTYPE=outgoing HG_NODE=0000000000000000000000000000000000000000 HG_SOURCE=clone
394 updating to branch default
394 updating to branch default
395 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
395 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
396 $ rm -rf c
396 $ rm -rf c
397
397
398 preoutgoing hook can prevent outgoing changes for local clones
398 preoutgoing hook can prevent outgoing changes for local clones
399
399
400 $ cat >> a/.hg/hgrc <<EOF
400 $ cat >> a/.hg/hgrc <<EOF
401 > preoutgoing.forbid = sh -c "printenv.py preoutgoing.forbid 1"
401 > preoutgoing.forbid = sh -c "printenv.py preoutgoing.forbid 1"
402 > EOF
402 > EOF
403 $ hg clone a zzz
403 $ hg clone a zzz
404 preoutgoing hook: HG_HOOKNAME=preoutgoing HG_HOOKTYPE=preoutgoing HG_SOURCE=clone
404 preoutgoing hook: HG_HOOKNAME=preoutgoing HG_HOOKTYPE=preoutgoing HG_SOURCE=clone
405 preoutgoing.forbid hook: HG_HOOKNAME=preoutgoing.forbid HG_HOOKTYPE=preoutgoing HG_SOURCE=clone
405 preoutgoing.forbid hook: HG_HOOKNAME=preoutgoing.forbid HG_HOOKTYPE=preoutgoing HG_SOURCE=clone
406 abort: preoutgoing.forbid hook exited with status 1
406 abort: preoutgoing.forbid hook exited with status 1
407 [255]
407 [255]
408
408
409 $ cd "$TESTTMP/b"
409 $ cd "$TESTTMP/b"
410
410
411 $ cat > hooktests.py <<EOF
411 $ cat > hooktests.py <<EOF
412 > from __future__ import print_function
412 > from __future__ import print_function
413 > from mercurial import error
413 > from mercurial import error
414 >
414 >
415 > uncallable = 0
415 > uncallable = 0
416 >
416 >
417 > def printargs(ui, args):
417 > def printargs(ui, args):
418 > a = list(args.items())
418 > a = list(args.items())
419 > a.sort()
419 > a.sort()
420 > ui.write('hook args:\n')
420 > ui.write('hook args:\n')
421 > for k, v in a:
421 > for k, v in a:
422 > ui.write(' %s %s\n' % (k, v))
422 > ui.write(' %s %s\n' % (k, v))
423 >
423 >
424 > def passhook(ui, repo, **args):
424 > def passhook(ui, repo, **args):
425 > printargs(ui, args)
425 > printargs(ui, args)
426 >
426 >
427 > def failhook(ui, repo, **args):
427 > def failhook(ui, repo, **args):
428 > printargs(ui, args)
428 > printargs(ui, args)
429 > return True
429 > return True
430 >
430 >
431 > class LocalException(Exception):
431 > class LocalException(Exception):
432 > pass
432 > pass
433 >
433 >
434 > def raisehook(**args):
434 > def raisehook(**args):
435 > raise LocalException('exception from hook')
435 > raise LocalException('exception from hook')
436 >
436 >
437 > def aborthook(**args):
437 > def aborthook(**args):
438 > raise error.Abort('raise abort from hook')
438 > raise error.Abort('raise abort from hook')
439 >
439 >
440 > def brokenhook(**args):
440 > def brokenhook(**args):
441 > return 1 + {}
441 > return 1 + {}
442 >
442 >
443 > def verbosehook(ui, **args):
443 > def verbosehook(ui, **args):
444 > ui.note('verbose output from hook\n')
444 > ui.note('verbose output from hook\n')
445 >
445 >
446 > def printtags(ui, repo, **args):
446 > def printtags(ui, repo, **args):
447 > ui.write('%s\n' % sorted(repo.tags()))
447 > ui.write('%s\n' % sorted(repo.tags()))
448 >
448 >
449 > class container:
449 > class container:
450 > unreachable = 1
450 > unreachable = 1
451 > EOF
451 > EOF
452
452
453 $ cat > syntaxerror.py << EOF
453 $ cat > syntaxerror.py << EOF
454 > (foo
454 > (foo
455 > EOF
455 > EOF
456
456
457 test python hooks
457 test python hooks
458
458
459 #if windows
459 #if windows
460 $ PYTHONPATH="$TESTTMP/b;$PYTHONPATH"
460 $ PYTHONPATH="$TESTTMP/b;$PYTHONPATH"
461 #else
461 #else
462 $ PYTHONPATH="$TESTTMP/b:$PYTHONPATH"
462 $ PYTHONPATH="$TESTTMP/b:$PYTHONPATH"
463 #endif
463 #endif
464 $ export PYTHONPATH
464 $ export PYTHONPATH
465
465
466 $ echo '[hooks]' > ../a/.hg/hgrc
466 $ echo '[hooks]' > ../a/.hg/hgrc
467 $ echo 'preoutgoing.broken = python:hooktests.brokenhook' >> ../a/.hg/hgrc
467 $ echo 'preoutgoing.broken = python:hooktests.brokenhook' >> ../a/.hg/hgrc
468 $ hg pull ../a 2>&1 | grep 'raised an exception'
468 $ hg pull ../a 2>&1 | grep 'raised an exception'
469 error: preoutgoing.broken hook raised an exception: unsupported operand type(s) for +: 'int' and 'dict'
469 error: preoutgoing.broken hook raised an exception: unsupported operand type(s) for +: 'int' and 'dict'
470
470
471 $ echo '[hooks]' > ../a/.hg/hgrc
471 $ echo '[hooks]' > ../a/.hg/hgrc
472 $ echo 'preoutgoing.raise = python:hooktests.raisehook' >> ../a/.hg/hgrc
472 $ echo 'preoutgoing.raise = python:hooktests.raisehook' >> ../a/.hg/hgrc
473 $ hg pull ../a 2>&1 | grep 'raised an exception'
473 $ hg pull ../a 2>&1 | grep 'raised an exception'
474 error: preoutgoing.raise hook raised an exception: exception from hook
474 error: preoutgoing.raise hook raised an exception: exception from hook
475
475
476 $ echo '[hooks]' > ../a/.hg/hgrc
476 $ echo '[hooks]' > ../a/.hg/hgrc
477 $ echo 'preoutgoing.abort = python:hooktests.aborthook' >> ../a/.hg/hgrc
477 $ echo 'preoutgoing.abort = python:hooktests.aborthook' >> ../a/.hg/hgrc
478 $ hg pull ../a
478 $ hg pull ../a
479 pulling from ../a
479 pulling from ../a
480 searching for changes
480 searching for changes
481 error: preoutgoing.abort hook failed: raise abort from hook
481 error: preoutgoing.abort hook failed: raise abort from hook
482 abort: raise abort from hook
482 abort: raise abort from hook
483 [255]
483 [255]
484
484
485 $ echo '[hooks]' > ../a/.hg/hgrc
485 $ echo '[hooks]' > ../a/.hg/hgrc
486 $ echo 'preoutgoing.fail = python:hooktests.failhook' >> ../a/.hg/hgrc
486 $ echo 'preoutgoing.fail = python:hooktests.failhook' >> ../a/.hg/hgrc
487 $ hg pull ../a
487 $ hg pull ../a
488 pulling from ../a
488 pulling from ../a
489 searching for changes
489 searching for changes
490 hook args:
490 hook args:
491 hooktype preoutgoing
491 hooktype preoutgoing
492 source pull
492 source pull
493 abort: preoutgoing.fail hook failed
493 abort: preoutgoing.fail hook failed
494 [255]
494 [255]
495
495
496 $ echo '[hooks]' > ../a/.hg/hgrc
496 $ echo '[hooks]' > ../a/.hg/hgrc
497 $ echo 'preoutgoing.uncallable = python:hooktests.uncallable' >> ../a/.hg/hgrc
497 $ echo 'preoutgoing.uncallable = python:hooktests.uncallable' >> ../a/.hg/hgrc
498 $ hg pull ../a
498 $ hg pull ../a
499 pulling from ../a
499 pulling from ../a
500 searching for changes
500 searching for changes
501 abort: preoutgoing.uncallable hook is invalid: "hooktests.uncallable" is not callable
501 abort: preoutgoing.uncallable hook is invalid: "hooktests.uncallable" is not callable
502 [255]
502 [255]
503
503
504 $ echo '[hooks]' > ../a/.hg/hgrc
504 $ echo '[hooks]' > ../a/.hg/hgrc
505 $ echo 'preoutgoing.nohook = python:hooktests.nohook' >> ../a/.hg/hgrc
505 $ echo 'preoutgoing.nohook = python:hooktests.nohook' >> ../a/.hg/hgrc
506 $ hg pull ../a
506 $ hg pull ../a
507 pulling from ../a
507 pulling from ../a
508 searching for changes
508 searching for changes
509 abort: preoutgoing.nohook hook is invalid: "hooktests.nohook" is not defined
509 abort: preoutgoing.nohook hook is invalid: "hooktests.nohook" is not defined
510 [255]
510 [255]
511
511
512 $ echo '[hooks]' > ../a/.hg/hgrc
512 $ echo '[hooks]' > ../a/.hg/hgrc
513 $ echo 'preoutgoing.nomodule = python:nomodule' >> ../a/.hg/hgrc
513 $ echo 'preoutgoing.nomodule = python:nomodule' >> ../a/.hg/hgrc
514 $ hg pull ../a
514 $ hg pull ../a
515 pulling from ../a
515 pulling from ../a
516 searching for changes
516 searching for changes
517 abort: preoutgoing.nomodule hook is invalid: "nomodule" not in a module
517 abort: preoutgoing.nomodule hook is invalid: "nomodule" not in a module
518 [255]
518 [255]
519
519
520 $ echo '[hooks]' > ../a/.hg/hgrc
520 $ echo '[hooks]' > ../a/.hg/hgrc
521 $ echo 'preoutgoing.badmodule = python:nomodule.nowhere' >> ../a/.hg/hgrc
521 $ echo 'preoutgoing.badmodule = python:nomodule.nowhere' >> ../a/.hg/hgrc
522 $ hg pull ../a
522 $ hg pull ../a
523 pulling from ../a
523 pulling from ../a
524 searching for changes
524 searching for changes
525 abort: preoutgoing.badmodule hook is invalid: import of "nomodule" failed
525 abort: preoutgoing.badmodule hook is invalid: import of "nomodule" failed
526 (run with --traceback for stack trace)
526 (run with --traceback for stack trace)
527 [255]
527 [255]
528
528
529 $ echo '[hooks]' > ../a/.hg/hgrc
529 $ echo '[hooks]' > ../a/.hg/hgrc
530 $ echo 'preoutgoing.unreachable = python:hooktests.container.unreachable' >> ../a/.hg/hgrc
530 $ echo 'preoutgoing.unreachable = python:hooktests.container.unreachable' >> ../a/.hg/hgrc
531 $ hg pull ../a
531 $ hg pull ../a
532 pulling from ../a
532 pulling from ../a
533 searching for changes
533 searching for changes
534 abort: preoutgoing.unreachable hook is invalid: import of "hooktests.container" failed
534 abort: preoutgoing.unreachable hook is invalid: import of "hooktests.container" failed
535 (run with --traceback for stack trace)
535 (run with --traceback for stack trace)
536 [255]
536 [255]
537
537
538 $ echo '[hooks]' > ../a/.hg/hgrc
538 $ echo '[hooks]' > ../a/.hg/hgrc
539 $ echo 'preoutgoing.syntaxerror = python:syntaxerror.syntaxerror' >> ../a/.hg/hgrc
539 $ echo 'preoutgoing.syntaxerror = python:syntaxerror.syntaxerror' >> ../a/.hg/hgrc
540 $ hg pull ../a
540 $ hg pull ../a
541 pulling from ../a
541 pulling from ../a
542 searching for changes
542 searching for changes
543 abort: preoutgoing.syntaxerror hook is invalid: import of "syntaxerror" failed
543 abort: preoutgoing.syntaxerror hook is invalid: import of "syntaxerror" failed
544 (run with --traceback for stack trace)
544 (run with --traceback for stack trace)
545 [255]
545 [255]
546
546
547 The second egrep is to filter out lines like ' ^', which are slightly
547 The second egrep is to filter out lines like ' ^', which are slightly
548 different between Python 2.6 and Python 2.7.
548 different between Python 2.6 and Python 2.7.
549 $ hg pull ../a --traceback 2>&1 | egrep -v '^( +File| [_a-zA-Z*(])' | egrep -v '^( )+(\^)?$'
549 $ hg pull ../a --traceback 2>&1 | egrep -v '^( +File| [_a-zA-Z*(])' | egrep -v '^( )+(\^)?$'
550 pulling from ../a
550 pulling from ../a
551 searching for changes
551 searching for changes
552 exception from first failed import attempt:
552 exception from first failed import attempt:
553 Traceback (most recent call last):
553 Traceback (most recent call last):
554 SyntaxError: * (glob)
554 SyntaxError: * (glob)
555 exception from second failed import attempt:
555 exception from second failed import attempt:
556 Traceback (most recent call last):
556 Traceback (most recent call last):
557 ImportError: No module named hgext_syntaxerror
557 ImportError: No module named hgext_syntaxerror
558 Traceback (most recent call last):
558 Traceback (most recent call last):
559 HookLoadError: preoutgoing.syntaxerror hook is invalid: import of "syntaxerror" failed
559 HookLoadError: preoutgoing.syntaxerror hook is invalid: import of "syntaxerror" failed
560 abort: preoutgoing.syntaxerror hook is invalid: import of "syntaxerror" failed
560 abort: preoutgoing.syntaxerror hook is invalid: import of "syntaxerror" failed
561
561
562 $ echo '[hooks]' > ../a/.hg/hgrc
562 $ echo '[hooks]' > ../a/.hg/hgrc
563 $ echo 'preoutgoing.pass = python:hooktests.passhook' >> ../a/.hg/hgrc
563 $ echo 'preoutgoing.pass = python:hooktests.passhook' >> ../a/.hg/hgrc
564 $ hg pull ../a
564 $ hg pull ../a
565 pulling from ../a
565 pulling from ../a
566 searching for changes
566 searching for changes
567 hook args:
567 hook args:
568 hooktype preoutgoing
568 hooktype preoutgoing
569 source pull
569 source pull
570 adding changesets
570 adding changesets
571 adding manifests
571 adding manifests
572 adding file changes
572 adding file changes
573 added 1 changesets with 1 changes to 1 files
573 added 1 changesets with 1 changes to 1 files
574 adding remote bookmark quux
574 adding remote bookmark quux
575 new changesets 539e4b31b6dc
575 new changesets 539e4b31b6dc
576 (run 'hg update' to get a working copy)
576 (run 'hg update' to get a working copy)
577
577
578 post- python hooks that fail to *run* don't cause an abort
578 post- python hooks that fail to *run* don't cause an abort
579 $ rm ../a/.hg/hgrc
579 $ rm ../a/.hg/hgrc
580 $ echo '[hooks]' > .hg/hgrc
580 $ echo '[hooks]' > .hg/hgrc
581 $ echo 'post-pull.broken = python:hooktests.brokenhook' >> .hg/hgrc
581 $ echo 'post-pull.broken = python:hooktests.brokenhook' >> .hg/hgrc
582 $ hg pull ../a
582 $ hg pull ../a
583 pulling from ../a
583 pulling from ../a
584 searching for changes
584 searching for changes
585 no changes found
585 no changes found
586 error: post-pull.broken hook raised an exception: unsupported operand type(s) for +: 'int' and 'dict'
586 error: post-pull.broken hook raised an exception: unsupported operand type(s) for +: 'int' and 'dict'
587 (run with --traceback for stack trace)
587 (run with --traceback for stack trace)
588
588
589 but post- python hooks that fail to *load* do
589 but post- python hooks that fail to *load* do
590 $ echo '[hooks]' > .hg/hgrc
590 $ echo '[hooks]' > .hg/hgrc
591 $ echo 'post-pull.nomodule = python:nomodule' >> .hg/hgrc
591 $ echo 'post-pull.nomodule = python:nomodule' >> .hg/hgrc
592 $ hg pull ../a
592 $ hg pull ../a
593 pulling from ../a
593 pulling from ../a
594 searching for changes
594 searching for changes
595 no changes found
595 no changes found
596 abort: post-pull.nomodule hook is invalid: "nomodule" not in a module
596 abort: post-pull.nomodule hook is invalid: "nomodule" not in a module
597 [255]
597 [255]
598
598
599 $ echo '[hooks]' > .hg/hgrc
599 $ echo '[hooks]' > .hg/hgrc
600 $ echo 'post-pull.badmodule = python:nomodule.nowhere' >> .hg/hgrc
600 $ echo 'post-pull.badmodule = python:nomodule.nowhere' >> .hg/hgrc
601 $ hg pull ../a
601 $ hg pull ../a
602 pulling from ../a
602 pulling from ../a
603 searching for changes
603 searching for changes
604 no changes found
604 no changes found
605 abort: post-pull.badmodule hook is invalid: import of "nomodule" failed
605 abort: post-pull.badmodule hook is invalid: import of "nomodule" failed
606 (run with --traceback for stack trace)
606 (run with --traceback for stack trace)
607 [255]
607 [255]
608
608
609 $ echo '[hooks]' > .hg/hgrc
609 $ echo '[hooks]' > .hg/hgrc
610 $ echo 'post-pull.nohook = python:hooktests.nohook' >> .hg/hgrc
610 $ echo 'post-pull.nohook = python:hooktests.nohook' >> .hg/hgrc
611 $ hg pull ../a
611 $ hg pull ../a
612 pulling from ../a
612 pulling from ../a
613 searching for changes
613 searching for changes
614 no changes found
614 no changes found
615 abort: post-pull.nohook hook is invalid: "hooktests.nohook" is not defined
615 abort: post-pull.nohook hook is invalid: "hooktests.nohook" is not defined
616 [255]
616 [255]
617
617
618 make sure --traceback works
618 make sure --traceback works
619
619
620 $ echo '[hooks]' > .hg/hgrc
620 $ echo '[hooks]' > .hg/hgrc
621 $ echo 'commit.abort = python:hooktests.aborthook' >> .hg/hgrc
621 $ echo 'commit.abort = python:hooktests.aborthook' >> .hg/hgrc
622
622
623 $ echo aa > a
623 $ echo aa > a
624 $ hg --traceback commit -d '0 0' -ma 2>&1 | grep '^Traceback'
624 $ hg --traceback commit -d '0 0' -ma 2>&1 | grep '^Traceback'
625 Traceback (most recent call last):
625 Traceback (most recent call last):
626
626
627 $ cd ..
627 $ cd ..
628 $ hg init c
628 $ hg init c
629 $ cd c
629 $ cd c
630
630
631 $ cat > hookext.py <<EOF
631 $ cat > hookext.py <<EOF
632 > def autohook(ui, **args):
632 > def autohook(ui, **args):
633 > ui.write('Automatically installed hook\n')
633 > ui.write('Automatically installed hook\n')
634 >
634 >
635 > def reposetup(ui, repo):
635 > def reposetup(ui, repo):
636 > repo.ui.setconfig("hooks", "commit.auto", autohook)
636 > repo.ui.setconfig("hooks", "commit.auto", autohook)
637 > EOF
637 > EOF
638 $ echo '[extensions]' >> .hg/hgrc
638 $ echo '[extensions]' >> .hg/hgrc
639 $ echo 'hookext = hookext.py' >> .hg/hgrc
639 $ echo 'hookext = hookext.py' >> .hg/hgrc
640
640
641 $ touch foo
641 $ touch foo
642 $ hg add foo
642 $ hg add foo
643 $ hg ci -d '0 0' -m 'add foo'
643 $ hg ci -d '0 0' -m 'add foo'
644 Automatically installed hook
644 Automatically installed hook
645 $ echo >> foo
645 $ echo >> foo
646 $ hg ci --debug -d '0 0' -m 'change foo'
646 $ hg ci --debug -d '0 0' -m 'change foo'
647 committing files:
647 committing files:
648 foo
648 foo
649 committing manifest
649 committing manifest
650 committing changelog
650 committing changelog
651 updating the branch cache
651 updating the branch cache
652 committed changeset 1:52998019f6252a2b893452765fcb0a47351a5708
652 committed changeset 1:52998019f6252a2b893452765fcb0a47351a5708
653 calling hook commit.auto: hgext_hookext.autohook
653 calling hook commit.auto: hgext_hookext.autohook
654 Automatically installed hook
654 Automatically installed hook
655
655
656 $ hg showconfig hooks
656 $ hg showconfig hooks
657 hooks.commit.auto=<function autohook at *> (glob)
657 hooks.commit.auto=<function autohook at *> (glob)
658
658
659 test python hook configured with python:[file]:[hook] syntax
659 test python hook configured with python:[file]:[hook] syntax
660
660
661 $ cd ..
661 $ cd ..
662 $ mkdir d
662 $ mkdir d
663 $ cd d
663 $ cd d
664 $ hg init repo
664 $ hg init repo
665 $ mkdir hooks
665 $ mkdir hooks
666
666
667 $ cd hooks
667 $ cd hooks
668 $ cat > testhooks.py <<EOF
668 $ cat > testhooks.py <<EOF
669 > def testhook(ui, **args):
669 > def testhook(ui, **args):
670 > ui.write('hook works\n')
670 > ui.write('hook works\n')
671 > EOF
671 > EOF
672 $ echo '[hooks]' > ../repo/.hg/hgrc
672 $ echo '[hooks]' > ../repo/.hg/hgrc
673 $ echo "pre-commit.test = python:`pwd`/testhooks.py:testhook" >> ../repo/.hg/hgrc
673 $ echo "pre-commit.test = python:`pwd`/testhooks.py:testhook" >> ../repo/.hg/hgrc
674
674
675 $ cd ../repo
675 $ cd ../repo
676 $ hg commit -d '0 0'
676 $ hg commit -d '0 0'
677 hook works
677 hook works
678 nothing changed
678 nothing changed
679 [1]
679 [1]
680
680
681 $ echo '[hooks]' > .hg/hgrc
681 $ echo '[hooks]' > .hg/hgrc
682 $ echo "update.ne = python:`pwd`/nonexistent.py:testhook" >> .hg/hgrc
682 $ echo "update.ne = python:`pwd`/nonexistent.py:testhook" >> .hg/hgrc
683 $ echo "pre-identify.npmd = python:`pwd`/:no_python_module_dir" >> .hg/hgrc
683 $ echo "pre-identify.npmd = python:`pwd`/:no_python_module_dir" >> .hg/hgrc
684
684
685 $ hg up null
685 $ hg up null
686 loading update.ne hook failed:
686 loading update.ne hook failed:
687 abort: $ENOENT$: $TESTTMP/d/repo/nonexistent.py
687 abort: $ENOENT$: $TESTTMP/d/repo/nonexistent.py
688 [255]
688 [255]
689
689
690 $ hg id
690 $ hg id
691 loading pre-identify.npmd hook failed:
691 loading pre-identify.npmd hook failed:
692 abort: No module named repo!
692 abort: No module named repo!
693 [255]
693 [255]
694
694
695 $ cd ../../b
695 $ cd ../../b
696
696
697 make sure --traceback works on hook import failure
697 make sure --traceback works on hook import failure
698
698
699 $ cat > importfail.py <<EOF
699 $ cat > importfail.py <<EOF
700 > import somebogusmodule
700 > import somebogusmodule
701 > # dereference something in the module to force demandimport to load it
701 > # dereference something in the module to force demandimport to load it
702 > somebogusmodule.whatever
702 > somebogusmodule.whatever
703 > EOF
703 > EOF
704
704
705 $ echo '[hooks]' > .hg/hgrc
705 $ echo '[hooks]' > .hg/hgrc
706 $ echo 'precommit.importfail = python:importfail.whatever' >> .hg/hgrc
706 $ echo 'precommit.importfail = python:importfail.whatever' >> .hg/hgrc
707
707
708 $ echo a >> a
708 $ echo a >> a
709 $ hg --traceback commit -ma 2>&1 | egrep -v '^( +File| [a-zA-Z(])'
709 $ hg --traceback commit -ma 2>&1 | egrep -v '^( +File| [a-zA-Z(])'
710 exception from first failed import attempt:
710 exception from first failed import attempt:
711 Traceback (most recent call last):
711 Traceback (most recent call last):
712 ImportError: No module named somebogusmodule
712 ImportError: No module named somebogusmodule
713 exception from second failed import attempt:
713 exception from second failed import attempt:
714 Traceback (most recent call last):
714 Traceback (most recent call last):
715 ImportError: No module named hgext_importfail
715 ImportError: No module named hgext_importfail
716 Traceback (most recent call last):
716 Traceback (most recent call last):
717 HookLoadError: precommit.importfail hook is invalid: import of "importfail" failed
717 HookLoadError: precommit.importfail hook is invalid: import of "importfail" failed
718 abort: precommit.importfail hook is invalid: import of "importfail" failed
718 abort: precommit.importfail hook is invalid: import of "importfail" failed
719
719
720 Issue1827: Hooks Update & Commit not completely post operation
720 Issue1827: Hooks Update & Commit not completely post operation
721
721
722 commit and update hooks should run after command completion. The largefiles
722 commit and update hooks should run after command completion. The largefiles
723 use demonstrates a recursive wlock, showing the hook doesn't run until the
723 use demonstrates a recursive wlock, showing the hook doesn't run until the
724 final release (and dirstate flush).
724 final release (and dirstate flush).
725
725
726 $ echo '[hooks]' > .hg/hgrc
726 $ echo '[hooks]' > .hg/hgrc
727 $ echo 'commit = hg id' >> .hg/hgrc
727 $ echo 'commit = hg id' >> .hg/hgrc
728 $ echo 'update = hg id' >> .hg/hgrc
728 $ echo 'update = hg id' >> .hg/hgrc
729 $ echo bb > a
729 $ echo bb > a
730 $ hg ci -ma
730 $ hg ci -ma
731 223eafe2750c tip
731 223eafe2750c tip
732 $ hg up 0 --config extensions.largefiles=
732 $ hg up 0 --config extensions.largefiles=
733 The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
733 The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
734 cb9a9f314b8b
734 cb9a9f314b8b
735 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
735 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
736
736
737 make sure --verbose (and --quiet/--debug etc.) are propagated to the local ui
737 make sure --verbose (and --quiet/--debug etc.) are propagated to the local ui
738 that is passed to pre/post hooks
738 that is passed to pre/post hooks
739
739
740 $ echo '[hooks]' > .hg/hgrc
740 $ echo '[hooks]' > .hg/hgrc
741 $ echo 'pre-identify = python:hooktests.verbosehook' >> .hg/hgrc
741 $ echo 'pre-identify = python:hooktests.verbosehook' >> .hg/hgrc
742 $ hg id
742 $ hg id
743 cb9a9f314b8b
743 cb9a9f314b8b
744 $ hg id --verbose
744 $ hg id --verbose
745 calling hook pre-identify: hooktests.verbosehook
745 calling hook pre-identify: hooktests.verbosehook
746 verbose output from hook
746 verbose output from hook
747 cb9a9f314b8b
747 cb9a9f314b8b
748
748
749 Ensure hooks can be prioritized
749 Ensure hooks can be prioritized
750
750
751 $ echo '[hooks]' > .hg/hgrc
751 $ echo '[hooks]' > .hg/hgrc
752 $ echo 'pre-identify.a = python:hooktests.verbosehook' >> .hg/hgrc
752 $ echo 'pre-identify.a = python:hooktests.verbosehook' >> .hg/hgrc
753 $ echo 'pre-identify.b = python:hooktests.verbosehook' >> .hg/hgrc
753 $ echo 'pre-identify.b = python:hooktests.verbosehook' >> .hg/hgrc
754 $ echo 'priority.pre-identify.b = 1' >> .hg/hgrc
754 $ echo 'priority.pre-identify.b = 1' >> .hg/hgrc
755 $ echo 'pre-identify.c = python:hooktests.verbosehook' >> .hg/hgrc
755 $ echo 'pre-identify.c = python:hooktests.verbosehook' >> .hg/hgrc
756 $ hg id --verbose
756 $ hg id --verbose
757 calling hook pre-identify.b: hooktests.verbosehook
757 calling hook pre-identify.b: hooktests.verbosehook
758 verbose output from hook
758 verbose output from hook
759 calling hook pre-identify.a: hooktests.verbosehook
759 calling hook pre-identify.a: hooktests.verbosehook
760 verbose output from hook
760 verbose output from hook
761 calling hook pre-identify.c: hooktests.verbosehook
761 calling hook pre-identify.c: hooktests.verbosehook
762 verbose output from hook
762 verbose output from hook
763 cb9a9f314b8b
763 cb9a9f314b8b
764
764
765 new tags must be visible in pretxncommit (issue3210)
765 new tags must be visible in pretxncommit (issue3210)
766
766
767 $ echo 'pretxncommit.printtags = python:hooktests.printtags' >> .hg/hgrc
767 $ echo 'pretxncommit.printtags = python:hooktests.printtags' >> .hg/hgrc
768 $ hg tag -f foo
768 $ hg tag -f foo
769 ['a', 'foo', 'tip']
769 ['a', 'foo', 'tip']
770
770
771 post-init hooks must not crash (issue4983)
771 post-init hooks must not crash (issue4983)
772 This also creates the `to` repo for the next test block.
772 This also creates the `to` repo for the next test block.
773
773
774 $ cd ..
774 $ cd ..
775 $ cat << EOF >> hgrc-with-post-init-hook
775 $ cat << EOF >> hgrc-with-post-init-hook
776 > [hooks]
776 > [hooks]
777 > post-init = sh -c "printenv.py post-init"
777 > post-init = sh -c "printenv.py post-init"
778 > EOF
778 > EOF
779 $ HGRCPATH=hgrc-with-post-init-hook hg init to
779 $ HGRCPATH=hgrc-with-post-init-hook hg init to
780 post-init hook: HG_ARGS=init to HG_HOOKNAME=post-init HG_HOOKTYPE=post-init HG_OPTS={'insecure': None, 'remotecmd': '', 'ssh': ''} HG_PATS=['to'] HG_RESULT=0
780 post-init hook: HG_ARGS=init to HG_HOOKNAME=post-init HG_HOOKTYPE=post-init HG_OPTS={'insecure': None, 'remotecmd': '', 'ssh': ''} HG_PATS=['to'] HG_RESULT=0
781
781
782 new commits must be visible in pretxnchangegroup (issue3428)
782 new commits must be visible in pretxnchangegroup (issue3428)
783
783
784 $ echo '[hooks]' >> to/.hg/hgrc
784 $ echo '[hooks]' >> to/.hg/hgrc
785 $ echo 'prechangegroup = hg --traceback tip' >> to/.hg/hgrc
785 $ echo 'prechangegroup = hg --traceback tip' >> to/.hg/hgrc
786 $ echo 'pretxnchangegroup = hg --traceback tip' >> to/.hg/hgrc
786 $ echo 'pretxnchangegroup = hg --traceback tip' >> to/.hg/hgrc
787 $ echo a >> to/a
787 $ echo a >> to/a
788 $ hg --cwd to ci -Ama
788 $ hg --cwd to ci -Ama
789 adding a
789 adding a
790 $ hg clone to from
790 $ hg clone to from
791 updating to branch default
791 updating to branch default
792 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
792 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
793 $ echo aa >> from/a
793 $ echo aa >> from/a
794 $ hg --cwd from ci -mb
794 $ hg --cwd from ci -mb
795 $ hg --cwd from push
795 $ hg --cwd from push
796 pushing to $TESTTMP/to
796 pushing to $TESTTMP/to
797 searching for changes
797 searching for changes
798 changeset: 0:cb9a9f314b8b
798 changeset: 0:cb9a9f314b8b
799 tag: tip
799 tag: tip
800 user: test
800 user: test
801 date: Thu Jan 01 00:00:00 1970 +0000
801 date: Thu Jan 01 00:00:00 1970 +0000
802 summary: a
802 summary: a
803
803
804 adding changesets
804 adding changesets
805 adding manifests
805 adding manifests
806 adding file changes
806 adding file changes
807 added 1 changesets with 1 changes to 1 files
807 added 1 changesets with 1 changes to 1 files
808 changeset: 1:9836a07b9b9d
808 changeset: 1:9836a07b9b9d
809 tag: tip
809 tag: tip
810 user: test
810 user: test
811 date: Thu Jan 01 00:00:00 1970 +0000
811 date: Thu Jan 01 00:00:00 1970 +0000
812 summary: b
812 summary: b
813
813
814
814
815 pretxnclose hook failure should abort the transaction
815 pretxnclose hook failure should abort the transaction
816
816
817 $ hg init txnfailure
817 $ hg init txnfailure
818 $ cd txnfailure
818 $ cd txnfailure
819 $ touch a && hg commit -Aqm a
819 $ touch a && hg commit -Aqm a
820 $ cat >> .hg/hgrc <<EOF
820 $ cat >> .hg/hgrc <<EOF
821 > [hooks]
821 > [hooks]
822 > pretxnclose.error = exit 1
822 > pretxnclose.error = exit 1
823 > EOF
823 > EOF
824 $ hg strip -r 0 --config extensions.strip=
824 $ hg strip -r 0 --config extensions.strip=
825 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
825 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
826 saved backup bundle to * (glob)
826 saved backup bundle to * (glob)
827 transaction abort!
827 transaction abort!
828 rollback completed
828 rollback completed
829 strip failed, backup bundle stored in * (glob)
829 strip failed, backup bundle stored in * (glob)
830 abort: pretxnclose.error hook exited with status 1
830 abort: pretxnclose.error hook exited with status 1
831 [255]
831 [255]
832 $ hg recover
832 $ hg recover
833 no interrupted transaction available
833 no interrupted transaction available
834 [1]
834 [1]
835 $ cd ..
835 $ cd ..
836
836
837 check whether HG_PENDING makes pending changes only in related
837 check whether HG_PENDING makes pending changes only in related
838 repositories visible to an external hook.
838 repositories visible to an external hook.
839
839
840 (emulate a transaction running concurrently by copied
840 (emulate a transaction running concurrently by copied
841 .hg/store/00changelog.i.a in subsequent test)
841 .hg/store/00changelog.i.a in subsequent test)
842
842
843 $ cat > $TESTTMP/savepending.sh <<EOF
843 $ cat > $TESTTMP/savepending.sh <<EOF
844 > cp .hg/store/00changelog.i.a .hg/store/00changelog.i.a.saved
844 > cp .hg/store/00changelog.i.a .hg/store/00changelog.i.a.saved
845 > exit 1 # to avoid adding new revision for subsequent tests
845 > exit 1 # to avoid adding new revision for subsequent tests
846 > EOF
846 > EOF
847 $ cd a
847 $ cd a
848 $ hg tip -q
848 $ hg tip -q
849 4:539e4b31b6dc
849 4:539e4b31b6dc
850 $ hg --config hooks.pretxnclose="sh $TESTTMP/savepending.sh" commit -m "invisible"
850 $ hg --config hooks.pretxnclose="sh $TESTTMP/savepending.sh" commit -m "invisible"
851 transaction abort!
851 transaction abort!
852 rollback completed
852 rollback completed
853 abort: pretxnclose hook exited with status 1
853 abort: pretxnclose hook exited with status 1
854 [255]
854 [255]
855 $ cp .hg/store/00changelog.i.a.saved .hg/store/00changelog.i.a
855 $ cp .hg/store/00changelog.i.a.saved .hg/store/00changelog.i.a
856
856
857 (check (in)visibility of new changeset while transaction running in
857 (check (in)visibility of new changeset while transaction running in
858 repo)
858 repo)
859
859
860 $ cat > $TESTTMP/checkpending.sh <<EOF
860 $ cat > $TESTTMP/checkpending.sh <<EOF
861 > echo '@a'
861 > echo '@a'
862 > hg -R "$TESTTMP/a" tip -q
862 > hg -R "$TESTTMP/a" tip -q
863 > echo '@a/nested'
863 > echo '@a/nested'
864 > hg -R "$TESTTMP/a/nested" tip -q
864 > hg -R "$TESTTMP/a/nested" tip -q
865 > exit 1 # to avoid adding new revision for subsequent tests
865 > exit 1 # to avoid adding new revision for subsequent tests
866 > EOF
866 > EOF
867 $ hg init nested
867 $ hg init nested
868 $ cd nested
868 $ cd nested
869 $ echo a > a
869 $ echo a > a
870 $ hg add a
870 $ hg add a
871 $ hg --config hooks.pretxnclose="sh $TESTTMP/checkpending.sh" commit -m '#0'
871 $ hg --config hooks.pretxnclose="sh $TESTTMP/checkpending.sh" commit -m '#0'
872 @a
872 @a
873 4:539e4b31b6dc
873 4:539e4b31b6dc
874 @a/nested
874 @a/nested
875 0:bf5e395ced2c
875 0:bf5e395ced2c
876 transaction abort!
876 transaction abort!
877 rollback completed
877 rollback completed
878 abort: pretxnclose hook exited with status 1
878 abort: pretxnclose hook exited with status 1
879 [255]
879 [255]
880
880
881 Hook from untrusted hgrc are reported as failure
881 Hook from untrusted hgrc are reported as failure
882 ================================================
882 ================================================
883
883
884 $ cat << EOF > $TESTTMP/untrusted.py
884 $ cat << EOF > $TESTTMP/untrusted.py
885 > from mercurial import scmutil, util
885 > from mercurial import scmutil, util
886 > def uisetup(ui):
886 > def uisetup(ui):
887 > class untrustedui(ui.__class__):
887 > class untrustedui(ui.__class__):
888 > def _trusted(self, fp, f):
888 > def _trusted(self, fp, f):
889 > if util.normpath(fp.name).endswith('untrusted/.hg/hgrc'):
889 > if util.normpath(fp.name).endswith('untrusted/.hg/hgrc'):
890 > return False
890 > return False
891 > return super(untrustedui, self)._trusted(fp, f)
891 > return super(untrustedui, self)._trusted(fp, f)
892 > ui.__class__ = untrustedui
892 > ui.__class__ = untrustedui
893 > EOF
893 > EOF
894 $ cat << EOF >> $HGRCPATH
894 $ cat << EOF >> $HGRCPATH
895 > [extensions]
895 > [extensions]
896 > untrusted=$TESTTMP/untrusted.py
896 > untrusted=$TESTTMP/untrusted.py
897 > EOF
897 > EOF
898 $ hg init untrusted
898 $ hg init untrusted
899 $ cd untrusted
899 $ cd untrusted
900
900
901 Non-blocking hook
901 Non-blocking hook
902 -----------------
902 -----------------
903
903
904 $ cat << EOF >> .hg/hgrc
904 $ cat << EOF >> .hg/hgrc
905 > [hooks]
905 > [hooks]
906 > txnclose.testing=echo txnclose hook called
906 > txnclose.testing=echo txnclose hook called
907 > EOF
907 > EOF
908 $ touch a && hg commit -Aqm a
908 $ touch a && hg commit -Aqm a
909 warning: untrusted hook txnclose.testing not executed
909 warning: untrusted hook txnclose.testing not executed
910 $ hg log
910 $ hg log
911 changeset: 0:3903775176ed
911 changeset: 0:3903775176ed
912 tag: tip
912 tag: tip
913 user: test
913 user: test
914 date: Thu Jan 01 00:00:00 1970 +0000
914 date: Thu Jan 01 00:00:00 1970 +0000
915 summary: a
915 summary: a
916
916
917
917
918 Non-blocking hook
918 Non-blocking hook
919 -----------------
919 -----------------
920
920
921 $ cat << EOF >> .hg/hgrc
921 $ cat << EOF >> .hg/hgrc
922 > [hooks]
922 > [hooks]
923 > pretxnclose.testing=echo pre-txnclose hook called
923 > pretxnclose.testing=echo pre-txnclose hook called
924 > EOF
924 > EOF
925 $ touch b && hg commit -Aqm a
925 $ touch b && hg commit -Aqm a
926 transaction abort!
926 transaction abort!
927 rollback completed
927 rollback completed
928 abort: untrusted hook pretxnclose.testing not executed
928 abort: untrusted hook pretxnclose.testing not executed
929 (see 'hg help config.trusted')
929 (see 'hg help config.trusted')
930 [255]
930 [255]
931 $ hg log
931 $ hg log
932 changeset: 0:3903775176ed
932 changeset: 0:3903775176ed
933 tag: tip
933 tag: tip
934 user: test
934 user: test
935 date: Thu Jan 01 00:00:00 1970 +0000
935 date: Thu Jan 01 00:00:00 1970 +0000
936 summary: a
936 summary: a
937
937
General Comments 0
You need to be logged in to leave comments. Login now