##// END OF EJS Templates
bundle1: fix bundle1-denied reporting for push over ssh...
Pierre-Yves David -
r30909:d554e624 stable
parent child Browse files
Show More
@@ -1,1020 +1,1028 b''
1 # wireproto.py - generic wire protocol support functions
1 # wireproto.py - generic wire protocol support functions
2 #
2 #
3 # Copyright 2005-2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2010 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import hashlib
10 import hashlib
11 import itertools
11 import itertools
12 import os
12 import os
13 import tempfile
13 import tempfile
14
14
15 from .i18n import _
15 from .i18n import _
16 from .node import (
16 from .node import (
17 bin,
17 bin,
18 hex,
18 hex,
19 )
19 )
20
20
21 from . import (
21 from . import (
22 bundle2,
22 bundle2,
23 changegroup as changegroupmod,
23 changegroup as changegroupmod,
24 encoding,
24 encoding,
25 error,
25 error,
26 exchange,
26 exchange,
27 peer,
27 peer,
28 pushkey as pushkeymod,
28 pushkey as pushkeymod,
29 streamclone,
29 streamclone,
30 util,
30 util,
31 )
31 )
32
32
33 urlerr = util.urlerr
33 urlerr = util.urlerr
34 urlreq = util.urlreq
34 urlreq = util.urlreq
35
35
36 bundle2required = _(
36 bundle2requiredmain = _('incompatible Mercurial client; bundle2 required')
37 'incompatible Mercurial client; bundle2 required\n'
37 bundle2requiredhint = _('see https://www.mercurial-scm.org/wiki/'
38 '(see https://www.mercurial-scm.org/wiki/IncompatibleClient)\n')
38 'IncompatibleClient')
39 bundle2required = '%s\n(%s)\n' % (bundle2requiredmain, bundle2requiredhint)
39
40
40 class abstractserverproto(object):
41 class abstractserverproto(object):
41 """abstract class that summarizes the protocol API
42 """abstract class that summarizes the protocol API
42
43
43 Used as reference and documentation.
44 Used as reference and documentation.
44 """
45 """
45
46
46 def getargs(self, args):
47 def getargs(self, args):
47 """return the value for arguments in <args>
48 """return the value for arguments in <args>
48
49
49 returns a list of values (same order as <args>)"""
50 returns a list of values (same order as <args>)"""
50 raise NotImplementedError()
51 raise NotImplementedError()
51
52
52 def getfile(self, fp):
53 def getfile(self, fp):
53 """write the whole content of a file into a file like object
54 """write the whole content of a file into a file like object
54
55
55 The file is in the form::
56 The file is in the form::
56
57
57 (<chunk-size>\n<chunk>)+0\n
58 (<chunk-size>\n<chunk>)+0\n
58
59
59 chunk size is the ascii version of the int.
60 chunk size is the ascii version of the int.
60 """
61 """
61 raise NotImplementedError()
62 raise NotImplementedError()
62
63
63 def redirect(self):
64 def redirect(self):
64 """may setup interception for stdout and stderr
65 """may setup interception for stdout and stderr
65
66
66 See also the `restore` method."""
67 See also the `restore` method."""
67 raise NotImplementedError()
68 raise NotImplementedError()
68
69
69 # If the `redirect` function does install interception, the `restore`
70 # If the `redirect` function does install interception, the `restore`
70 # function MUST be defined. If interception is not used, this function
71 # function MUST be defined. If interception is not used, this function
71 # MUST NOT be defined.
72 # MUST NOT be defined.
72 #
73 #
73 # left commented here on purpose
74 # left commented here on purpose
74 #
75 #
75 #def restore(self):
76 #def restore(self):
76 # """reinstall previous stdout and stderr and return intercepted stdout
77 # """reinstall previous stdout and stderr and return intercepted stdout
77 # """
78 # """
78 # raise NotImplementedError()
79 # raise NotImplementedError()
79
80
80 class remotebatch(peer.batcher):
81 class remotebatch(peer.batcher):
81 '''batches the queued calls; uses as few roundtrips as possible'''
82 '''batches the queued calls; uses as few roundtrips as possible'''
82 def __init__(self, remote):
83 def __init__(self, remote):
83 '''remote must support _submitbatch(encbatch) and
84 '''remote must support _submitbatch(encbatch) and
84 _submitone(op, encargs)'''
85 _submitone(op, encargs)'''
85 peer.batcher.__init__(self)
86 peer.batcher.__init__(self)
86 self.remote = remote
87 self.remote = remote
87 def submit(self):
88 def submit(self):
88 req, rsp = [], []
89 req, rsp = [], []
89 for name, args, opts, resref in self.calls:
90 for name, args, opts, resref in self.calls:
90 mtd = getattr(self.remote, name)
91 mtd = getattr(self.remote, name)
91 batchablefn = getattr(mtd, 'batchable', None)
92 batchablefn = getattr(mtd, 'batchable', None)
92 if batchablefn is not None:
93 if batchablefn is not None:
93 batchable = batchablefn(mtd.im_self, *args, **opts)
94 batchable = batchablefn(mtd.im_self, *args, **opts)
94 encargsorres, encresref = next(batchable)
95 encargsorres, encresref = next(batchable)
95 if encresref:
96 if encresref:
96 req.append((name, encargsorres,))
97 req.append((name, encargsorres,))
97 rsp.append((batchable, encresref, resref,))
98 rsp.append((batchable, encresref, resref,))
98 else:
99 else:
99 resref.set(encargsorres)
100 resref.set(encargsorres)
100 else:
101 else:
101 if req:
102 if req:
102 self._submitreq(req, rsp)
103 self._submitreq(req, rsp)
103 req, rsp = [], []
104 req, rsp = [], []
104 resref.set(mtd(*args, **opts))
105 resref.set(mtd(*args, **opts))
105 if req:
106 if req:
106 self._submitreq(req, rsp)
107 self._submitreq(req, rsp)
107 def _submitreq(self, req, rsp):
108 def _submitreq(self, req, rsp):
108 encresults = self.remote._submitbatch(req)
109 encresults = self.remote._submitbatch(req)
109 for encres, r in zip(encresults, rsp):
110 for encres, r in zip(encresults, rsp):
110 batchable, encresref, resref = r
111 batchable, encresref, resref = r
111 encresref.set(encres)
112 encresref.set(encres)
112 resref.set(next(batchable))
113 resref.set(next(batchable))
113
114
114 class remoteiterbatcher(peer.iterbatcher):
115 class remoteiterbatcher(peer.iterbatcher):
115 def __init__(self, remote):
116 def __init__(self, remote):
116 super(remoteiterbatcher, self).__init__()
117 super(remoteiterbatcher, self).__init__()
117 self._remote = remote
118 self._remote = remote
118
119
119 def __getattr__(self, name):
120 def __getattr__(self, name):
120 if not getattr(self._remote, name, False):
121 if not getattr(self._remote, name, False):
121 raise AttributeError(
122 raise AttributeError(
122 'Attempted to iterbatch non-batchable call to %r' % name)
123 'Attempted to iterbatch non-batchable call to %r' % name)
123 return super(remoteiterbatcher, self).__getattr__(name)
124 return super(remoteiterbatcher, self).__getattr__(name)
124
125
125 def submit(self):
126 def submit(self):
126 """Break the batch request into many patch calls and pipeline them.
127 """Break the batch request into many patch calls and pipeline them.
127
128
128 This is mostly valuable over http where request sizes can be
129 This is mostly valuable over http where request sizes can be
129 limited, but can be used in other places as well.
130 limited, but can be used in other places as well.
130 """
131 """
131 req, rsp = [], []
132 req, rsp = [], []
132 for name, args, opts, resref in self.calls:
133 for name, args, opts, resref in self.calls:
133 mtd = getattr(self._remote, name)
134 mtd = getattr(self._remote, name)
134 batchable = mtd.batchable(mtd.im_self, *args, **opts)
135 batchable = mtd.batchable(mtd.im_self, *args, **opts)
135 encargsorres, encresref = next(batchable)
136 encargsorres, encresref = next(batchable)
136 assert encresref
137 assert encresref
137 req.append((name, encargsorres))
138 req.append((name, encargsorres))
138 rsp.append((batchable, encresref))
139 rsp.append((batchable, encresref))
139 if req:
140 if req:
140 self._resultiter = self._remote._submitbatch(req)
141 self._resultiter = self._remote._submitbatch(req)
141 self._rsp = rsp
142 self._rsp = rsp
142
143
143 def results(self):
144 def results(self):
144 for (batchable, encresref), encres in itertools.izip(
145 for (batchable, encresref), encres in itertools.izip(
145 self._rsp, self._resultiter):
146 self._rsp, self._resultiter):
146 encresref.set(encres)
147 encresref.set(encres)
147 yield next(batchable)
148 yield next(batchable)
148
149
149 # Forward a couple of names from peer to make wireproto interactions
150 # Forward a couple of names from peer to make wireproto interactions
150 # slightly more sensible.
151 # slightly more sensible.
151 batchable = peer.batchable
152 batchable = peer.batchable
152 future = peer.future
153 future = peer.future
153
154
154 # list of nodes encoding / decoding
155 # list of nodes encoding / decoding
155
156
156 def decodelist(l, sep=' '):
157 def decodelist(l, sep=' '):
157 if l:
158 if l:
158 return map(bin, l.split(sep))
159 return map(bin, l.split(sep))
159 return []
160 return []
160
161
161 def encodelist(l, sep=' '):
162 def encodelist(l, sep=' '):
162 try:
163 try:
163 return sep.join(map(hex, l))
164 return sep.join(map(hex, l))
164 except TypeError:
165 except TypeError:
165 raise
166 raise
166
167
167 # batched call argument encoding
168 # batched call argument encoding
168
169
169 def escapearg(plain):
170 def escapearg(plain):
170 return (plain
171 return (plain
171 .replace(':', ':c')
172 .replace(':', ':c')
172 .replace(',', ':o')
173 .replace(',', ':o')
173 .replace(';', ':s')
174 .replace(';', ':s')
174 .replace('=', ':e'))
175 .replace('=', ':e'))
175
176
176 def unescapearg(escaped):
177 def unescapearg(escaped):
177 return (escaped
178 return (escaped
178 .replace(':e', '=')
179 .replace(':e', '=')
179 .replace(':s', ';')
180 .replace(':s', ';')
180 .replace(':o', ',')
181 .replace(':o', ',')
181 .replace(':c', ':'))
182 .replace(':c', ':'))
182
183
183 def encodebatchcmds(req):
184 def encodebatchcmds(req):
184 """Return a ``cmds`` argument value for the ``batch`` command."""
185 """Return a ``cmds`` argument value for the ``batch`` command."""
185 cmds = []
186 cmds = []
186 for op, argsdict in req:
187 for op, argsdict in req:
187 # Old servers didn't properly unescape argument names. So prevent
188 # Old servers didn't properly unescape argument names. So prevent
188 # the sending of argument names that may not be decoded properly by
189 # the sending of argument names that may not be decoded properly by
189 # servers.
190 # servers.
190 assert all(escapearg(k) == k for k in argsdict)
191 assert all(escapearg(k) == k for k in argsdict)
191
192
192 args = ','.join('%s=%s' % (escapearg(k), escapearg(v))
193 args = ','.join('%s=%s' % (escapearg(k), escapearg(v))
193 for k, v in argsdict.iteritems())
194 for k, v in argsdict.iteritems())
194 cmds.append('%s %s' % (op, args))
195 cmds.append('%s %s' % (op, args))
195
196
196 return ';'.join(cmds)
197 return ';'.join(cmds)
197
198
198 # mapping of options accepted by getbundle and their types
199 # mapping of options accepted by getbundle and their types
199 #
200 #
200 # Meant to be extended by extensions. It is extensions responsibility to ensure
201 # Meant to be extended by extensions. It is extensions responsibility to ensure
201 # such options are properly processed in exchange.getbundle.
202 # such options are properly processed in exchange.getbundle.
202 #
203 #
203 # supported types are:
204 # supported types are:
204 #
205 #
205 # :nodes: list of binary nodes
206 # :nodes: list of binary nodes
206 # :csv: list of comma-separated values
207 # :csv: list of comma-separated values
207 # :scsv: list of comma-separated values return as set
208 # :scsv: list of comma-separated values return as set
208 # :plain: string with no transformation needed.
209 # :plain: string with no transformation needed.
209 gboptsmap = {'heads': 'nodes',
210 gboptsmap = {'heads': 'nodes',
210 'common': 'nodes',
211 'common': 'nodes',
211 'obsmarkers': 'boolean',
212 'obsmarkers': 'boolean',
212 'bundlecaps': 'scsv',
213 'bundlecaps': 'scsv',
213 'listkeys': 'csv',
214 'listkeys': 'csv',
214 'cg': 'boolean',
215 'cg': 'boolean',
215 'cbattempted': 'boolean'}
216 'cbattempted': 'boolean'}
216
217
217 # client side
218 # client side
218
219
219 class wirepeer(peer.peerrepository):
220 class wirepeer(peer.peerrepository):
220 """Client-side interface for communicating with a peer repository.
221 """Client-side interface for communicating with a peer repository.
221
222
222 Methods commonly call wire protocol commands of the same name.
223 Methods commonly call wire protocol commands of the same name.
223
224
224 See also httppeer.py and sshpeer.py for protocol-specific
225 See also httppeer.py and sshpeer.py for protocol-specific
225 implementations of this interface.
226 implementations of this interface.
226 """
227 """
227 def batch(self):
228 def batch(self):
228 if self.capable('batch'):
229 if self.capable('batch'):
229 return remotebatch(self)
230 return remotebatch(self)
230 else:
231 else:
231 return peer.localbatch(self)
232 return peer.localbatch(self)
232 def _submitbatch(self, req):
233 def _submitbatch(self, req):
233 """run batch request <req> on the server
234 """run batch request <req> on the server
234
235
235 Returns an iterator of the raw responses from the server.
236 Returns an iterator of the raw responses from the server.
236 """
237 """
237 rsp = self._callstream("batch", cmds=encodebatchcmds(req))
238 rsp = self._callstream("batch", cmds=encodebatchcmds(req))
238 chunk = rsp.read(1024)
239 chunk = rsp.read(1024)
239 work = [chunk]
240 work = [chunk]
240 while chunk:
241 while chunk:
241 while ';' not in chunk and chunk:
242 while ';' not in chunk and chunk:
242 chunk = rsp.read(1024)
243 chunk = rsp.read(1024)
243 work.append(chunk)
244 work.append(chunk)
244 merged = ''.join(work)
245 merged = ''.join(work)
245 while ';' in merged:
246 while ';' in merged:
246 one, merged = merged.split(';', 1)
247 one, merged = merged.split(';', 1)
247 yield unescapearg(one)
248 yield unescapearg(one)
248 chunk = rsp.read(1024)
249 chunk = rsp.read(1024)
249 work = [merged, chunk]
250 work = [merged, chunk]
250 yield unescapearg(''.join(work))
251 yield unescapearg(''.join(work))
251
252
252 def _submitone(self, op, args):
253 def _submitone(self, op, args):
253 return self._call(op, **args)
254 return self._call(op, **args)
254
255
255 def iterbatch(self):
256 def iterbatch(self):
256 return remoteiterbatcher(self)
257 return remoteiterbatcher(self)
257
258
258 @batchable
259 @batchable
259 def lookup(self, key):
260 def lookup(self, key):
260 self.requirecap('lookup', _('look up remote revision'))
261 self.requirecap('lookup', _('look up remote revision'))
261 f = future()
262 f = future()
262 yield {'key': encoding.fromlocal(key)}, f
263 yield {'key': encoding.fromlocal(key)}, f
263 d = f.value
264 d = f.value
264 success, data = d[:-1].split(" ", 1)
265 success, data = d[:-1].split(" ", 1)
265 if int(success):
266 if int(success):
266 yield bin(data)
267 yield bin(data)
267 self._abort(error.RepoError(data))
268 self._abort(error.RepoError(data))
268
269
269 @batchable
270 @batchable
270 def heads(self):
271 def heads(self):
271 f = future()
272 f = future()
272 yield {}, f
273 yield {}, f
273 d = f.value
274 d = f.value
274 try:
275 try:
275 yield decodelist(d[:-1])
276 yield decodelist(d[:-1])
276 except ValueError:
277 except ValueError:
277 self._abort(error.ResponseError(_("unexpected response:"), d))
278 self._abort(error.ResponseError(_("unexpected response:"), d))
278
279
279 @batchable
280 @batchable
280 def known(self, nodes):
281 def known(self, nodes):
281 f = future()
282 f = future()
282 yield {'nodes': encodelist(nodes)}, f
283 yield {'nodes': encodelist(nodes)}, f
283 d = f.value
284 d = f.value
284 try:
285 try:
285 yield [bool(int(b)) for b in d]
286 yield [bool(int(b)) for b in d]
286 except ValueError:
287 except ValueError:
287 self._abort(error.ResponseError(_("unexpected response:"), d))
288 self._abort(error.ResponseError(_("unexpected response:"), d))
288
289
289 @batchable
290 @batchable
290 def branchmap(self):
291 def branchmap(self):
291 f = future()
292 f = future()
292 yield {}, f
293 yield {}, f
293 d = f.value
294 d = f.value
294 try:
295 try:
295 branchmap = {}
296 branchmap = {}
296 for branchpart in d.splitlines():
297 for branchpart in d.splitlines():
297 branchname, branchheads = branchpart.split(' ', 1)
298 branchname, branchheads = branchpart.split(' ', 1)
298 branchname = encoding.tolocal(urlreq.unquote(branchname))
299 branchname = encoding.tolocal(urlreq.unquote(branchname))
299 branchheads = decodelist(branchheads)
300 branchheads = decodelist(branchheads)
300 branchmap[branchname] = branchheads
301 branchmap[branchname] = branchheads
301 yield branchmap
302 yield branchmap
302 except TypeError:
303 except TypeError:
303 self._abort(error.ResponseError(_("unexpected response:"), d))
304 self._abort(error.ResponseError(_("unexpected response:"), d))
304
305
305 def branches(self, nodes):
306 def branches(self, nodes):
306 n = encodelist(nodes)
307 n = encodelist(nodes)
307 d = self._call("branches", nodes=n)
308 d = self._call("branches", nodes=n)
308 try:
309 try:
309 br = [tuple(decodelist(b)) for b in d.splitlines()]
310 br = [tuple(decodelist(b)) for b in d.splitlines()]
310 return br
311 return br
311 except ValueError:
312 except ValueError:
312 self._abort(error.ResponseError(_("unexpected response:"), d))
313 self._abort(error.ResponseError(_("unexpected response:"), d))
313
314
314 def between(self, pairs):
315 def between(self, pairs):
315 batch = 8 # avoid giant requests
316 batch = 8 # avoid giant requests
316 r = []
317 r = []
317 for i in xrange(0, len(pairs), batch):
318 for i in xrange(0, len(pairs), batch):
318 n = " ".join([encodelist(p, '-') for p in pairs[i:i + batch]])
319 n = " ".join([encodelist(p, '-') for p in pairs[i:i + batch]])
319 d = self._call("between", pairs=n)
320 d = self._call("between", pairs=n)
320 try:
321 try:
321 r.extend(l and decodelist(l) or [] for l in d.splitlines())
322 r.extend(l and decodelist(l) or [] for l in d.splitlines())
322 except ValueError:
323 except ValueError:
323 self._abort(error.ResponseError(_("unexpected response:"), d))
324 self._abort(error.ResponseError(_("unexpected response:"), d))
324 return r
325 return r
325
326
326 @batchable
327 @batchable
327 def pushkey(self, namespace, key, old, new):
328 def pushkey(self, namespace, key, old, new):
328 if not self.capable('pushkey'):
329 if not self.capable('pushkey'):
329 yield False, None
330 yield False, None
330 f = future()
331 f = future()
331 self.ui.debug('preparing pushkey for "%s:%s"\n' % (namespace, key))
332 self.ui.debug('preparing pushkey for "%s:%s"\n' % (namespace, key))
332 yield {'namespace': encoding.fromlocal(namespace),
333 yield {'namespace': encoding.fromlocal(namespace),
333 'key': encoding.fromlocal(key),
334 'key': encoding.fromlocal(key),
334 'old': encoding.fromlocal(old),
335 'old': encoding.fromlocal(old),
335 'new': encoding.fromlocal(new)}, f
336 'new': encoding.fromlocal(new)}, f
336 d = f.value
337 d = f.value
337 d, output = d.split('\n', 1)
338 d, output = d.split('\n', 1)
338 try:
339 try:
339 d = bool(int(d))
340 d = bool(int(d))
340 except ValueError:
341 except ValueError:
341 raise error.ResponseError(
342 raise error.ResponseError(
342 _('push failed (unexpected response):'), d)
343 _('push failed (unexpected response):'), d)
343 for l in output.splitlines(True):
344 for l in output.splitlines(True):
344 self.ui.status(_('remote: '), l)
345 self.ui.status(_('remote: '), l)
345 yield d
346 yield d
346
347
347 @batchable
348 @batchable
348 def listkeys(self, namespace):
349 def listkeys(self, namespace):
349 if not self.capable('pushkey'):
350 if not self.capable('pushkey'):
350 yield {}, None
351 yield {}, None
351 f = future()
352 f = future()
352 self.ui.debug('preparing listkeys for "%s"\n' % namespace)
353 self.ui.debug('preparing listkeys for "%s"\n' % namespace)
353 yield {'namespace': encoding.fromlocal(namespace)}, f
354 yield {'namespace': encoding.fromlocal(namespace)}, f
354 d = f.value
355 d = f.value
355 self.ui.debug('received listkey for "%s": %i bytes\n'
356 self.ui.debug('received listkey for "%s": %i bytes\n'
356 % (namespace, len(d)))
357 % (namespace, len(d)))
357 yield pushkeymod.decodekeys(d)
358 yield pushkeymod.decodekeys(d)
358
359
359 def stream_out(self):
360 def stream_out(self):
360 return self._callstream('stream_out')
361 return self._callstream('stream_out')
361
362
362 def changegroup(self, nodes, kind):
363 def changegroup(self, nodes, kind):
363 n = encodelist(nodes)
364 n = encodelist(nodes)
364 f = self._callcompressable("changegroup", roots=n)
365 f = self._callcompressable("changegroup", roots=n)
365 return changegroupmod.cg1unpacker(f, 'UN')
366 return changegroupmod.cg1unpacker(f, 'UN')
366
367
367 def changegroupsubset(self, bases, heads, kind):
368 def changegroupsubset(self, bases, heads, kind):
368 self.requirecap('changegroupsubset', _('look up remote changes'))
369 self.requirecap('changegroupsubset', _('look up remote changes'))
369 bases = encodelist(bases)
370 bases = encodelist(bases)
370 heads = encodelist(heads)
371 heads = encodelist(heads)
371 f = self._callcompressable("changegroupsubset",
372 f = self._callcompressable("changegroupsubset",
372 bases=bases, heads=heads)
373 bases=bases, heads=heads)
373 return changegroupmod.cg1unpacker(f, 'UN')
374 return changegroupmod.cg1unpacker(f, 'UN')
374
375
375 def getbundle(self, source, **kwargs):
376 def getbundle(self, source, **kwargs):
376 self.requirecap('getbundle', _('look up remote changes'))
377 self.requirecap('getbundle', _('look up remote changes'))
377 opts = {}
378 opts = {}
378 bundlecaps = kwargs.get('bundlecaps')
379 bundlecaps = kwargs.get('bundlecaps')
379 if bundlecaps is not None:
380 if bundlecaps is not None:
380 kwargs['bundlecaps'] = sorted(bundlecaps)
381 kwargs['bundlecaps'] = sorted(bundlecaps)
381 else:
382 else:
382 bundlecaps = () # kwargs could have it to None
383 bundlecaps = () # kwargs could have it to None
383 for key, value in kwargs.iteritems():
384 for key, value in kwargs.iteritems():
384 if value is None:
385 if value is None:
385 continue
386 continue
386 keytype = gboptsmap.get(key)
387 keytype = gboptsmap.get(key)
387 if keytype is None:
388 if keytype is None:
388 assert False, 'unexpected'
389 assert False, 'unexpected'
389 elif keytype == 'nodes':
390 elif keytype == 'nodes':
390 value = encodelist(value)
391 value = encodelist(value)
391 elif keytype in ('csv', 'scsv'):
392 elif keytype in ('csv', 'scsv'):
392 value = ','.join(value)
393 value = ','.join(value)
393 elif keytype == 'boolean':
394 elif keytype == 'boolean':
394 value = '%i' % bool(value)
395 value = '%i' % bool(value)
395 elif keytype != 'plain':
396 elif keytype != 'plain':
396 raise KeyError('unknown getbundle option type %s'
397 raise KeyError('unknown getbundle option type %s'
397 % keytype)
398 % keytype)
398 opts[key] = value
399 opts[key] = value
399 f = self._callcompressable("getbundle", **opts)
400 f = self._callcompressable("getbundle", **opts)
400 if any((cap.startswith('HG2') for cap in bundlecaps)):
401 if any((cap.startswith('HG2') for cap in bundlecaps)):
401 return bundle2.getunbundler(self.ui, f)
402 return bundle2.getunbundler(self.ui, f)
402 else:
403 else:
403 return changegroupmod.cg1unpacker(f, 'UN')
404 return changegroupmod.cg1unpacker(f, 'UN')
404
405
405 def unbundle(self, cg, heads, url):
406 def unbundle(self, cg, heads, url):
406 '''Send cg (a readable file-like object representing the
407 '''Send cg (a readable file-like object representing the
407 changegroup to push, typically a chunkbuffer object) to the
408 changegroup to push, typically a chunkbuffer object) to the
408 remote server as a bundle.
409 remote server as a bundle.
409
410
410 When pushing a bundle10 stream, return an integer indicating the
411 When pushing a bundle10 stream, return an integer indicating the
411 result of the push (see localrepository.addchangegroup()).
412 result of the push (see localrepository.addchangegroup()).
412
413
413 When pushing a bundle20 stream, return a bundle20 stream.
414 When pushing a bundle20 stream, return a bundle20 stream.
414
415
415 `url` is the url the client thinks it's pushing to, which is
416 `url` is the url the client thinks it's pushing to, which is
416 visible to hooks.
417 visible to hooks.
417 '''
418 '''
418
419
419 if heads != ['force'] and self.capable('unbundlehash'):
420 if heads != ['force'] and self.capable('unbundlehash'):
420 heads = encodelist(['hashed',
421 heads = encodelist(['hashed',
421 hashlib.sha1(''.join(sorted(heads))).digest()])
422 hashlib.sha1(''.join(sorted(heads))).digest()])
422 else:
423 else:
423 heads = encodelist(heads)
424 heads = encodelist(heads)
424
425
425 if util.safehasattr(cg, 'deltaheader'):
426 if util.safehasattr(cg, 'deltaheader'):
426 # this a bundle10, do the old style call sequence
427 # this a bundle10, do the old style call sequence
427 ret, output = self._callpush("unbundle", cg, heads=heads)
428 ret, output = self._callpush("unbundle", cg, heads=heads)
428 if ret == "":
429 if ret == "":
429 raise error.ResponseError(
430 raise error.ResponseError(
430 _('push failed:'), output)
431 _('push failed:'), output)
431 try:
432 try:
432 ret = int(ret)
433 ret = int(ret)
433 except ValueError:
434 except ValueError:
434 raise error.ResponseError(
435 raise error.ResponseError(
435 _('push failed (unexpected response):'), ret)
436 _('push failed (unexpected response):'), ret)
436
437
437 for l in output.splitlines(True):
438 for l in output.splitlines(True):
438 self.ui.status(_('remote: '), l)
439 self.ui.status(_('remote: '), l)
439 else:
440 else:
440 # bundle2 push. Send a stream, fetch a stream.
441 # bundle2 push. Send a stream, fetch a stream.
441 stream = self._calltwowaystream('unbundle', cg, heads=heads)
442 stream = self._calltwowaystream('unbundle', cg, heads=heads)
442 ret = bundle2.getunbundler(self.ui, stream)
443 ret = bundle2.getunbundler(self.ui, stream)
443 return ret
444 return ret
444
445
445 def debugwireargs(self, one, two, three=None, four=None, five=None):
446 def debugwireargs(self, one, two, three=None, four=None, five=None):
446 # don't pass optional arguments left at their default value
447 # don't pass optional arguments left at their default value
447 opts = {}
448 opts = {}
448 if three is not None:
449 if three is not None:
449 opts['three'] = three
450 opts['three'] = three
450 if four is not None:
451 if four is not None:
451 opts['four'] = four
452 opts['four'] = four
452 return self._call('debugwireargs', one=one, two=two, **opts)
453 return self._call('debugwireargs', one=one, two=two, **opts)
453
454
454 def _call(self, cmd, **args):
455 def _call(self, cmd, **args):
455 """execute <cmd> on the server
456 """execute <cmd> on the server
456
457
457 The command is expected to return a simple string.
458 The command is expected to return a simple string.
458
459
459 returns the server reply as a string."""
460 returns the server reply as a string."""
460 raise NotImplementedError()
461 raise NotImplementedError()
461
462
462 def _callstream(self, cmd, **args):
463 def _callstream(self, cmd, **args):
463 """execute <cmd> on the server
464 """execute <cmd> on the server
464
465
465 The command is expected to return a stream. Note that if the
466 The command is expected to return a stream. Note that if the
466 command doesn't return a stream, _callstream behaves
467 command doesn't return a stream, _callstream behaves
467 differently for ssh and http peers.
468 differently for ssh and http peers.
468
469
469 returns the server reply as a file like object.
470 returns the server reply as a file like object.
470 """
471 """
471 raise NotImplementedError()
472 raise NotImplementedError()
472
473
473 def _callcompressable(self, cmd, **args):
474 def _callcompressable(self, cmd, **args):
474 """execute <cmd> on the server
475 """execute <cmd> on the server
475
476
476 The command is expected to return a stream.
477 The command is expected to return a stream.
477
478
478 The stream may have been compressed in some implementations. This
479 The stream may have been compressed in some implementations. This
479 function takes care of the decompression. This is the only difference
480 function takes care of the decompression. This is the only difference
480 with _callstream.
481 with _callstream.
481
482
482 returns the server reply as a file like object.
483 returns the server reply as a file like object.
483 """
484 """
484 raise NotImplementedError()
485 raise NotImplementedError()
485
486
486 def _callpush(self, cmd, fp, **args):
487 def _callpush(self, cmd, fp, **args):
487 """execute a <cmd> on server
488 """execute a <cmd> on server
488
489
489 The command is expected to be related to a push. Push has a special
490 The command is expected to be related to a push. Push has a special
490 return method.
491 return method.
491
492
492 returns the server reply as a (ret, output) tuple. ret is either
493 returns the server reply as a (ret, output) tuple. ret is either
493 empty (error) or a stringified int.
494 empty (error) or a stringified int.
494 """
495 """
495 raise NotImplementedError()
496 raise NotImplementedError()
496
497
497 def _calltwowaystream(self, cmd, fp, **args):
498 def _calltwowaystream(self, cmd, fp, **args):
498 """execute <cmd> on server
499 """execute <cmd> on server
499
500
500 The command will send a stream to the server and get a stream in reply.
501 The command will send a stream to the server and get a stream in reply.
501 """
502 """
502 raise NotImplementedError()
503 raise NotImplementedError()
503
504
504 def _abort(self, exception):
505 def _abort(self, exception):
505 """clearly abort the wire protocol connection and raise the exception
506 """clearly abort the wire protocol connection and raise the exception
506 """
507 """
507 raise NotImplementedError()
508 raise NotImplementedError()
508
509
509 # server side
510 # server side
510
511
511 # wire protocol command can either return a string or one of these classes.
512 # wire protocol command can either return a string or one of these classes.
512 class streamres(object):
513 class streamres(object):
513 """wireproto reply: binary stream
514 """wireproto reply: binary stream
514
515
515 The call was successful and the result is a stream.
516 The call was successful and the result is a stream.
516
517
517 Accepts either a generator or an object with a ``read(size)`` method.
518 Accepts either a generator or an object with a ``read(size)`` method.
518
519
519 ``v1compressible`` indicates whether this data can be compressed to
520 ``v1compressible`` indicates whether this data can be compressed to
520 "version 1" clients (technically: HTTP peers using
521 "version 1" clients (technically: HTTP peers using
521 application/mercurial-0.1 media type). This flag should NOT be used on
522 application/mercurial-0.1 media type). This flag should NOT be used on
522 new commands because new clients should support a more modern compression
523 new commands because new clients should support a more modern compression
523 mechanism.
524 mechanism.
524 """
525 """
525 def __init__(self, gen=None, reader=None, v1compressible=False):
526 def __init__(self, gen=None, reader=None, v1compressible=False):
526 self.gen = gen
527 self.gen = gen
527 self.reader = reader
528 self.reader = reader
528 self.v1compressible = v1compressible
529 self.v1compressible = v1compressible
529
530
530 class pushres(object):
531 class pushres(object):
531 """wireproto reply: success with simple integer return
532 """wireproto reply: success with simple integer return
532
533
533 The call was successful and returned an integer contained in `self.res`.
534 The call was successful and returned an integer contained in `self.res`.
534 """
535 """
535 def __init__(self, res):
536 def __init__(self, res):
536 self.res = res
537 self.res = res
537
538
538 class pusherr(object):
539 class pusherr(object):
539 """wireproto reply: failure
540 """wireproto reply: failure
540
541
541 The call failed. The `self.res` attribute contains the error message.
542 The call failed. The `self.res` attribute contains the error message.
542 """
543 """
543 def __init__(self, res):
544 def __init__(self, res):
544 self.res = res
545 self.res = res
545
546
546 class ooberror(object):
547 class ooberror(object):
547 """wireproto reply: failure of a batch of operation
548 """wireproto reply: failure of a batch of operation
548
549
549 Something failed during a batch call. The error message is stored in
550 Something failed during a batch call. The error message is stored in
550 `self.message`.
551 `self.message`.
551 """
552 """
552 def __init__(self, message):
553 def __init__(self, message):
553 self.message = message
554 self.message = message
554
555
555 def getdispatchrepo(repo, proto, command):
556 def getdispatchrepo(repo, proto, command):
556 """Obtain the repo used for processing wire protocol commands.
557 """Obtain the repo used for processing wire protocol commands.
557
558
558 The intent of this function is to serve as a monkeypatch point for
559 The intent of this function is to serve as a monkeypatch point for
559 extensions that need commands to operate on different repo views under
560 extensions that need commands to operate on different repo views under
560 specialized circumstances.
561 specialized circumstances.
561 """
562 """
562 return repo.filtered('served')
563 return repo.filtered('served')
563
564
564 def dispatch(repo, proto, command):
565 def dispatch(repo, proto, command):
565 repo = getdispatchrepo(repo, proto, command)
566 repo = getdispatchrepo(repo, proto, command)
566 func, spec = commands[command]
567 func, spec = commands[command]
567 args = proto.getargs(spec)
568 args = proto.getargs(spec)
568 return func(repo, proto, *args)
569 return func(repo, proto, *args)
569
570
570 def options(cmd, keys, others):
571 def options(cmd, keys, others):
571 opts = {}
572 opts = {}
572 for k in keys:
573 for k in keys:
573 if k in others:
574 if k in others:
574 opts[k] = others[k]
575 opts[k] = others[k]
575 del others[k]
576 del others[k]
576 if others:
577 if others:
577 util.stderr.write("warning: %s ignored unexpected arguments %s\n"
578 util.stderr.write("warning: %s ignored unexpected arguments %s\n"
578 % (cmd, ",".join(others)))
579 % (cmd, ",".join(others)))
579 return opts
580 return opts
580
581
581 def bundle1allowed(repo, action):
582 def bundle1allowed(repo, action):
582 """Whether a bundle1 operation is allowed from the server.
583 """Whether a bundle1 operation is allowed from the server.
583
584
584 Priority is:
585 Priority is:
585
586
586 1. server.bundle1gd.<action> (if generaldelta active)
587 1. server.bundle1gd.<action> (if generaldelta active)
587 2. server.bundle1.<action>
588 2. server.bundle1.<action>
588 3. server.bundle1gd (if generaldelta active)
589 3. server.bundle1gd (if generaldelta active)
589 4. server.bundle1
590 4. server.bundle1
590 """
591 """
591 ui = repo.ui
592 ui = repo.ui
592 gd = 'generaldelta' in repo.requirements
593 gd = 'generaldelta' in repo.requirements
593
594
594 if gd:
595 if gd:
595 v = ui.configbool('server', 'bundle1gd.%s' % action, None)
596 v = ui.configbool('server', 'bundle1gd.%s' % action, None)
596 if v is not None:
597 if v is not None:
597 return v
598 return v
598
599
599 v = ui.configbool('server', 'bundle1.%s' % action, None)
600 v = ui.configbool('server', 'bundle1.%s' % action, None)
600 if v is not None:
601 if v is not None:
601 return v
602 return v
602
603
603 if gd:
604 if gd:
604 v = ui.configbool('server', 'bundle1gd', None)
605 v = ui.configbool('server', 'bundle1gd', None)
605 if v is not None:
606 if v is not None:
606 return v
607 return v
607
608
608 return ui.configbool('server', 'bundle1', True)
609 return ui.configbool('server', 'bundle1', True)
609
610
610 def supportedcompengines(ui, proto, role):
611 def supportedcompengines(ui, proto, role):
611 """Obtain the list of supported compression engines for a request."""
612 """Obtain the list of supported compression engines for a request."""
612 assert role in (util.CLIENTROLE, util.SERVERROLE)
613 assert role in (util.CLIENTROLE, util.SERVERROLE)
613
614
614 compengines = util.compengines.supportedwireengines(role)
615 compengines = util.compengines.supportedwireengines(role)
615
616
616 # Allow config to override default list and ordering.
617 # Allow config to override default list and ordering.
617 if role == util.SERVERROLE:
618 if role == util.SERVERROLE:
618 configengines = ui.configlist('server', 'compressionengines')
619 configengines = ui.configlist('server', 'compressionengines')
619 config = 'server.compressionengines'
620 config = 'server.compressionengines'
620 else:
621 else:
621 # This is currently implemented mainly to facilitate testing. In most
622 # This is currently implemented mainly to facilitate testing. In most
622 # cases, the server should be in charge of choosing a compression engine
623 # cases, the server should be in charge of choosing a compression engine
623 # because a server has the most to lose from a sub-optimal choice. (e.g.
624 # because a server has the most to lose from a sub-optimal choice. (e.g.
624 # CPU DoS due to an expensive engine or a network DoS due to poor
625 # CPU DoS due to an expensive engine or a network DoS due to poor
625 # compression ratio).
626 # compression ratio).
626 configengines = ui.configlist('experimental',
627 configengines = ui.configlist('experimental',
627 'clientcompressionengines')
628 'clientcompressionengines')
628 config = 'experimental.clientcompressionengines'
629 config = 'experimental.clientcompressionengines'
629
630
630 # No explicit config. Filter out the ones that aren't supposed to be
631 # No explicit config. Filter out the ones that aren't supposed to be
631 # advertised and return default ordering.
632 # advertised and return default ordering.
632 if not configengines:
633 if not configengines:
633 attr = 'serverpriority' if role == util.SERVERROLE else 'clientpriority'
634 attr = 'serverpriority' if role == util.SERVERROLE else 'clientpriority'
634 return [e for e in compengines
635 return [e for e in compengines
635 if getattr(e.wireprotosupport(), attr) > 0]
636 if getattr(e.wireprotosupport(), attr) > 0]
636
637
637 # If compression engines are listed in the config, assume there is a good
638 # If compression engines are listed in the config, assume there is a good
638 # reason for it (like server operators wanting to achieve specific
639 # reason for it (like server operators wanting to achieve specific
639 # performance characteristics). So fail fast if the config references
640 # performance characteristics). So fail fast if the config references
640 # unusable compression engines.
641 # unusable compression engines.
641 validnames = set(e.name() for e in compengines)
642 validnames = set(e.name() for e in compengines)
642 invalidnames = set(e for e in configengines if e not in validnames)
643 invalidnames = set(e for e in configengines if e not in validnames)
643 if invalidnames:
644 if invalidnames:
644 raise error.Abort(_('invalid compression engine defined in %s: %s') %
645 raise error.Abort(_('invalid compression engine defined in %s: %s') %
645 (config, ', '.join(sorted(invalidnames))))
646 (config, ', '.join(sorted(invalidnames))))
646
647
647 compengines = [e for e in compengines if e.name() in configengines]
648 compengines = [e for e in compengines if e.name() in configengines]
648 compengines = sorted(compengines,
649 compengines = sorted(compengines,
649 key=lambda e: configengines.index(e.name()))
650 key=lambda e: configengines.index(e.name()))
650
651
651 if not compengines:
652 if not compengines:
652 raise error.Abort(_('%s config option does not specify any known '
653 raise error.Abort(_('%s config option does not specify any known '
653 'compression engines') % config,
654 'compression engines') % config,
654 hint=_('usable compression engines: %s') %
655 hint=_('usable compression engines: %s') %
655 ', '.sorted(validnames))
656 ', '.sorted(validnames))
656
657
657 return compengines
658 return compengines
658
659
659 # list of commands
660 # list of commands
660 commands = {}
661 commands = {}
661
662
662 def wireprotocommand(name, args=''):
663 def wireprotocommand(name, args=''):
663 """decorator for wire protocol command"""
664 """decorator for wire protocol command"""
664 def register(func):
665 def register(func):
665 commands[name] = (func, args)
666 commands[name] = (func, args)
666 return func
667 return func
667 return register
668 return register
668
669
669 @wireprotocommand('batch', 'cmds *')
670 @wireprotocommand('batch', 'cmds *')
670 def batch(repo, proto, cmds, others):
671 def batch(repo, proto, cmds, others):
671 repo = repo.filtered("served")
672 repo = repo.filtered("served")
672 res = []
673 res = []
673 for pair in cmds.split(';'):
674 for pair in cmds.split(';'):
674 op, args = pair.split(' ', 1)
675 op, args = pair.split(' ', 1)
675 vals = {}
676 vals = {}
676 for a in args.split(','):
677 for a in args.split(','):
677 if a:
678 if a:
678 n, v = a.split('=')
679 n, v = a.split('=')
679 vals[unescapearg(n)] = unescapearg(v)
680 vals[unescapearg(n)] = unescapearg(v)
680 func, spec = commands[op]
681 func, spec = commands[op]
681 if spec:
682 if spec:
682 keys = spec.split()
683 keys = spec.split()
683 data = {}
684 data = {}
684 for k in keys:
685 for k in keys:
685 if k == '*':
686 if k == '*':
686 star = {}
687 star = {}
687 for key in vals.keys():
688 for key in vals.keys():
688 if key not in keys:
689 if key not in keys:
689 star[key] = vals[key]
690 star[key] = vals[key]
690 data['*'] = star
691 data['*'] = star
691 else:
692 else:
692 data[k] = vals[k]
693 data[k] = vals[k]
693 result = func(repo, proto, *[data[k] for k in keys])
694 result = func(repo, proto, *[data[k] for k in keys])
694 else:
695 else:
695 result = func(repo, proto)
696 result = func(repo, proto)
696 if isinstance(result, ooberror):
697 if isinstance(result, ooberror):
697 return result
698 return result
698 res.append(escapearg(result))
699 res.append(escapearg(result))
699 return ';'.join(res)
700 return ';'.join(res)
700
701
701 @wireprotocommand('between', 'pairs')
702 @wireprotocommand('between', 'pairs')
702 def between(repo, proto, pairs):
703 def between(repo, proto, pairs):
703 pairs = [decodelist(p, '-') for p in pairs.split(" ")]
704 pairs = [decodelist(p, '-') for p in pairs.split(" ")]
704 r = []
705 r = []
705 for b in repo.between(pairs):
706 for b in repo.between(pairs):
706 r.append(encodelist(b) + "\n")
707 r.append(encodelist(b) + "\n")
707 return "".join(r)
708 return "".join(r)
708
709
709 @wireprotocommand('branchmap')
710 @wireprotocommand('branchmap')
710 def branchmap(repo, proto):
711 def branchmap(repo, proto):
711 branchmap = repo.branchmap()
712 branchmap = repo.branchmap()
712 heads = []
713 heads = []
713 for branch, nodes in branchmap.iteritems():
714 for branch, nodes in branchmap.iteritems():
714 branchname = urlreq.quote(encoding.fromlocal(branch))
715 branchname = urlreq.quote(encoding.fromlocal(branch))
715 branchnodes = encodelist(nodes)
716 branchnodes = encodelist(nodes)
716 heads.append('%s %s' % (branchname, branchnodes))
717 heads.append('%s %s' % (branchname, branchnodes))
717 return '\n'.join(heads)
718 return '\n'.join(heads)
718
719
719 @wireprotocommand('branches', 'nodes')
720 @wireprotocommand('branches', 'nodes')
720 def branches(repo, proto, nodes):
721 def branches(repo, proto, nodes):
721 nodes = decodelist(nodes)
722 nodes = decodelist(nodes)
722 r = []
723 r = []
723 for b in repo.branches(nodes):
724 for b in repo.branches(nodes):
724 r.append(encodelist(b) + "\n")
725 r.append(encodelist(b) + "\n")
725 return "".join(r)
726 return "".join(r)
726
727
727 @wireprotocommand('clonebundles', '')
728 @wireprotocommand('clonebundles', '')
728 def clonebundles(repo, proto):
729 def clonebundles(repo, proto):
729 """Server command for returning info for available bundles to seed clones.
730 """Server command for returning info for available bundles to seed clones.
730
731
731 Clients will parse this response and determine what bundle to fetch.
732 Clients will parse this response and determine what bundle to fetch.
732
733
733 Extensions may wrap this command to filter or dynamically emit data
734 Extensions may wrap this command to filter or dynamically emit data
734 depending on the request. e.g. you could advertise URLs for the closest
735 depending on the request. e.g. you could advertise URLs for the closest
735 data center given the client's IP address.
736 data center given the client's IP address.
736 """
737 """
737 return repo.opener.tryread('clonebundles.manifest')
738 return repo.opener.tryread('clonebundles.manifest')
738
739
739 wireprotocaps = ['lookup', 'changegroupsubset', 'branchmap', 'pushkey',
740 wireprotocaps = ['lookup', 'changegroupsubset', 'branchmap', 'pushkey',
740 'known', 'getbundle', 'unbundlehash', 'batch']
741 'known', 'getbundle', 'unbundlehash', 'batch']
741
742
742 def _capabilities(repo, proto):
743 def _capabilities(repo, proto):
743 """return a list of capabilities for a repo
744 """return a list of capabilities for a repo
744
745
745 This function exists to allow extensions to easily wrap capabilities
746 This function exists to allow extensions to easily wrap capabilities
746 computation
747 computation
747
748
748 - returns a lists: easy to alter
749 - returns a lists: easy to alter
749 - change done here will be propagated to both `capabilities` and `hello`
750 - change done here will be propagated to both `capabilities` and `hello`
750 command without any other action needed.
751 command without any other action needed.
751 """
752 """
752 # copy to prevent modification of the global list
753 # copy to prevent modification of the global list
753 caps = list(wireprotocaps)
754 caps = list(wireprotocaps)
754 if streamclone.allowservergeneration(repo.ui):
755 if streamclone.allowservergeneration(repo.ui):
755 if repo.ui.configbool('server', 'preferuncompressed', False):
756 if repo.ui.configbool('server', 'preferuncompressed', False):
756 caps.append('stream-preferred')
757 caps.append('stream-preferred')
757 requiredformats = repo.requirements & repo.supportedformats
758 requiredformats = repo.requirements & repo.supportedformats
758 # if our local revlogs are just revlogv1, add 'stream' cap
759 # if our local revlogs are just revlogv1, add 'stream' cap
759 if not requiredformats - set(('revlogv1',)):
760 if not requiredformats - set(('revlogv1',)):
760 caps.append('stream')
761 caps.append('stream')
761 # otherwise, add 'streamreqs' detailing our local revlog format
762 # otherwise, add 'streamreqs' detailing our local revlog format
762 else:
763 else:
763 caps.append('streamreqs=%s' % ','.join(sorted(requiredformats)))
764 caps.append('streamreqs=%s' % ','.join(sorted(requiredformats)))
764 if repo.ui.configbool('experimental', 'bundle2-advertise', True):
765 if repo.ui.configbool('experimental', 'bundle2-advertise', True):
765 capsblob = bundle2.encodecaps(bundle2.getrepocaps(repo))
766 capsblob = bundle2.encodecaps(bundle2.getrepocaps(repo))
766 caps.append('bundle2=' + urlreq.quote(capsblob))
767 caps.append('bundle2=' + urlreq.quote(capsblob))
767 caps.append('unbundle=%s' % ','.join(bundle2.bundlepriority))
768 caps.append('unbundle=%s' % ','.join(bundle2.bundlepriority))
768
769
769 if proto.name == 'http':
770 if proto.name == 'http':
770 caps.append('httpheader=%d' %
771 caps.append('httpheader=%d' %
771 repo.ui.configint('server', 'maxhttpheaderlen', 1024))
772 repo.ui.configint('server', 'maxhttpheaderlen', 1024))
772 if repo.ui.configbool('experimental', 'httppostargs', False):
773 if repo.ui.configbool('experimental', 'httppostargs', False):
773 caps.append('httppostargs')
774 caps.append('httppostargs')
774
775
775 # FUTURE advertise 0.2rx once support is implemented
776 # FUTURE advertise 0.2rx once support is implemented
776 # FUTURE advertise minrx and mintx after consulting config option
777 # FUTURE advertise minrx and mintx after consulting config option
777 caps.append('httpmediatype=0.1rx,0.1tx,0.2tx')
778 caps.append('httpmediatype=0.1rx,0.1tx,0.2tx')
778
779
779 compengines = supportedcompengines(repo.ui, proto, util.SERVERROLE)
780 compengines = supportedcompengines(repo.ui, proto, util.SERVERROLE)
780 if compengines:
781 if compengines:
781 comptypes = ','.join(urlreq.quote(e.wireprotosupport().name)
782 comptypes = ','.join(urlreq.quote(e.wireprotosupport().name)
782 for e in compengines)
783 for e in compengines)
783 caps.append('compression=%s' % comptypes)
784 caps.append('compression=%s' % comptypes)
784
785
785 return caps
786 return caps
786
787
787 # If you are writing an extension and consider wrapping this function. Wrap
788 # If you are writing an extension and consider wrapping this function. Wrap
788 # `_capabilities` instead.
789 # `_capabilities` instead.
789 @wireprotocommand('capabilities')
790 @wireprotocommand('capabilities')
790 def capabilities(repo, proto):
791 def capabilities(repo, proto):
791 return ' '.join(_capabilities(repo, proto))
792 return ' '.join(_capabilities(repo, proto))
792
793
793 @wireprotocommand('changegroup', 'roots')
794 @wireprotocommand('changegroup', 'roots')
794 def changegroup(repo, proto, roots):
795 def changegroup(repo, proto, roots):
795 nodes = decodelist(roots)
796 nodes = decodelist(roots)
796 cg = changegroupmod.changegroup(repo, nodes, 'serve')
797 cg = changegroupmod.changegroup(repo, nodes, 'serve')
797 return streamres(reader=cg, v1compressible=True)
798 return streamres(reader=cg, v1compressible=True)
798
799
799 @wireprotocommand('changegroupsubset', 'bases heads')
800 @wireprotocommand('changegroupsubset', 'bases heads')
800 def changegroupsubset(repo, proto, bases, heads):
801 def changegroupsubset(repo, proto, bases, heads):
801 bases = decodelist(bases)
802 bases = decodelist(bases)
802 heads = decodelist(heads)
803 heads = decodelist(heads)
803 cg = changegroupmod.changegroupsubset(repo, bases, heads, 'serve')
804 cg = changegroupmod.changegroupsubset(repo, bases, heads, 'serve')
804 return streamres(reader=cg, v1compressible=True)
805 return streamres(reader=cg, v1compressible=True)
805
806
806 @wireprotocommand('debugwireargs', 'one two *')
807 @wireprotocommand('debugwireargs', 'one two *')
807 def debugwireargs(repo, proto, one, two, others):
808 def debugwireargs(repo, proto, one, two, others):
808 # only accept optional args from the known set
809 # only accept optional args from the known set
809 opts = options('debugwireargs', ['three', 'four'], others)
810 opts = options('debugwireargs', ['three', 'four'], others)
810 return repo.debugwireargs(one, two, **opts)
811 return repo.debugwireargs(one, two, **opts)
811
812
812 @wireprotocommand('getbundle', '*')
813 @wireprotocommand('getbundle', '*')
813 def getbundle(repo, proto, others):
814 def getbundle(repo, proto, others):
814 opts = options('getbundle', gboptsmap.keys(), others)
815 opts = options('getbundle', gboptsmap.keys(), others)
815 for k, v in opts.iteritems():
816 for k, v in opts.iteritems():
816 keytype = gboptsmap[k]
817 keytype = gboptsmap[k]
817 if keytype == 'nodes':
818 if keytype == 'nodes':
818 opts[k] = decodelist(v)
819 opts[k] = decodelist(v)
819 elif keytype == 'csv':
820 elif keytype == 'csv':
820 opts[k] = list(v.split(','))
821 opts[k] = list(v.split(','))
821 elif keytype == 'scsv':
822 elif keytype == 'scsv':
822 opts[k] = set(v.split(','))
823 opts[k] = set(v.split(','))
823 elif keytype == 'boolean':
824 elif keytype == 'boolean':
824 # Client should serialize False as '0', which is a non-empty string
825 # Client should serialize False as '0', which is a non-empty string
825 # so it evaluates as a True bool.
826 # so it evaluates as a True bool.
826 if v == '0':
827 if v == '0':
827 opts[k] = False
828 opts[k] = False
828 else:
829 else:
829 opts[k] = bool(v)
830 opts[k] = bool(v)
830 elif keytype != 'plain':
831 elif keytype != 'plain':
831 raise KeyError('unknown getbundle option type %s'
832 raise KeyError('unknown getbundle option type %s'
832 % keytype)
833 % keytype)
833
834
834 if not bundle1allowed(repo, 'pull'):
835 if not bundle1allowed(repo, 'pull'):
835 if not exchange.bundle2requested(opts.get('bundlecaps')):
836 if not exchange.bundle2requested(opts.get('bundlecaps')):
836 return ooberror(bundle2required)
837 return ooberror(bundle2required)
837
838
838 chunks = exchange.getbundlechunks(repo, 'serve', **opts)
839 chunks = exchange.getbundlechunks(repo, 'serve', **opts)
839 return streamres(gen=chunks, v1compressible=True)
840 return streamres(gen=chunks, v1compressible=True)
840
841
841 @wireprotocommand('heads')
842 @wireprotocommand('heads')
842 def heads(repo, proto):
843 def heads(repo, proto):
843 h = repo.heads()
844 h = repo.heads()
844 return encodelist(h) + "\n"
845 return encodelist(h) + "\n"
845
846
846 @wireprotocommand('hello')
847 @wireprotocommand('hello')
847 def hello(repo, proto):
848 def hello(repo, proto):
848 '''the hello command returns a set of lines describing various
849 '''the hello command returns a set of lines describing various
849 interesting things about the server, in an RFC822-like format.
850 interesting things about the server, in an RFC822-like format.
850 Currently the only one defined is "capabilities", which
851 Currently the only one defined is "capabilities", which
851 consists of a line in the form:
852 consists of a line in the form:
852
853
853 capabilities: space separated list of tokens
854 capabilities: space separated list of tokens
854 '''
855 '''
855 return "capabilities: %s\n" % (capabilities(repo, proto))
856 return "capabilities: %s\n" % (capabilities(repo, proto))
856
857
857 @wireprotocommand('listkeys', 'namespace')
858 @wireprotocommand('listkeys', 'namespace')
858 def listkeys(repo, proto, namespace):
859 def listkeys(repo, proto, namespace):
859 d = repo.listkeys(encoding.tolocal(namespace)).items()
860 d = repo.listkeys(encoding.tolocal(namespace)).items()
860 return pushkeymod.encodekeys(d)
861 return pushkeymod.encodekeys(d)
861
862
862 @wireprotocommand('lookup', 'key')
863 @wireprotocommand('lookup', 'key')
863 def lookup(repo, proto, key):
864 def lookup(repo, proto, key):
864 try:
865 try:
865 k = encoding.tolocal(key)
866 k = encoding.tolocal(key)
866 c = repo[k]
867 c = repo[k]
867 r = c.hex()
868 r = c.hex()
868 success = 1
869 success = 1
869 except Exception as inst:
870 except Exception as inst:
870 r = str(inst)
871 r = str(inst)
871 success = 0
872 success = 0
872 return "%s %s\n" % (success, r)
873 return "%s %s\n" % (success, r)
873
874
874 @wireprotocommand('known', 'nodes *')
875 @wireprotocommand('known', 'nodes *')
875 def known(repo, proto, nodes, others):
876 def known(repo, proto, nodes, others):
876 return ''.join(b and "1" or "0" for b in repo.known(decodelist(nodes)))
877 return ''.join(b and "1" or "0" for b in repo.known(decodelist(nodes)))
877
878
878 @wireprotocommand('pushkey', 'namespace key old new')
879 @wireprotocommand('pushkey', 'namespace key old new')
879 def pushkey(repo, proto, namespace, key, old, new):
880 def pushkey(repo, proto, namespace, key, old, new):
880 # compatibility with pre-1.8 clients which were accidentally
881 # compatibility with pre-1.8 clients which were accidentally
881 # sending raw binary nodes rather than utf-8-encoded hex
882 # sending raw binary nodes rather than utf-8-encoded hex
882 if len(new) == 20 and new.encode('string-escape') != new:
883 if len(new) == 20 and new.encode('string-escape') != new:
883 # looks like it could be a binary node
884 # looks like it could be a binary node
884 try:
885 try:
885 new.decode('utf-8')
886 new.decode('utf-8')
886 new = encoding.tolocal(new) # but cleanly decodes as UTF-8
887 new = encoding.tolocal(new) # but cleanly decodes as UTF-8
887 except UnicodeDecodeError:
888 except UnicodeDecodeError:
888 pass # binary, leave unmodified
889 pass # binary, leave unmodified
889 else:
890 else:
890 new = encoding.tolocal(new) # normal path
891 new = encoding.tolocal(new) # normal path
891
892
892 if util.safehasattr(proto, 'restore'):
893 if util.safehasattr(proto, 'restore'):
893
894
894 proto.redirect()
895 proto.redirect()
895
896
896 try:
897 try:
897 r = repo.pushkey(encoding.tolocal(namespace), encoding.tolocal(key),
898 r = repo.pushkey(encoding.tolocal(namespace), encoding.tolocal(key),
898 encoding.tolocal(old), new) or False
899 encoding.tolocal(old), new) or False
899 except error.Abort:
900 except error.Abort:
900 r = False
901 r = False
901
902
902 output = proto.restore()
903 output = proto.restore()
903
904
904 return '%s\n%s' % (int(r), output)
905 return '%s\n%s' % (int(r), output)
905
906
906 r = repo.pushkey(encoding.tolocal(namespace), encoding.tolocal(key),
907 r = repo.pushkey(encoding.tolocal(namespace), encoding.tolocal(key),
907 encoding.tolocal(old), new)
908 encoding.tolocal(old), new)
908 return '%s\n' % int(r)
909 return '%s\n' % int(r)
909
910
910 @wireprotocommand('stream_out')
911 @wireprotocommand('stream_out')
911 def stream(repo, proto):
912 def stream(repo, proto):
912 '''If the server supports streaming clone, it advertises the "stream"
913 '''If the server supports streaming clone, it advertises the "stream"
913 capability with a value representing the version and flags of the repo
914 capability with a value representing the version and flags of the repo
914 it is serving. Client checks to see if it understands the format.
915 it is serving. Client checks to see if it understands the format.
915 '''
916 '''
916 if not streamclone.allowservergeneration(repo.ui):
917 if not streamclone.allowservergeneration(repo.ui):
917 return '1\n'
918 return '1\n'
918
919
919 def getstream(it):
920 def getstream(it):
920 yield '0\n'
921 yield '0\n'
921 for chunk in it:
922 for chunk in it:
922 yield chunk
923 yield chunk
923
924
924 try:
925 try:
925 # LockError may be raised before the first result is yielded. Don't
926 # LockError may be raised before the first result is yielded. Don't
926 # emit output until we're sure we got the lock successfully.
927 # emit output until we're sure we got the lock successfully.
927 it = streamclone.generatev1wireproto(repo)
928 it = streamclone.generatev1wireproto(repo)
928 return streamres(gen=getstream(it))
929 return streamres(gen=getstream(it))
929 except error.LockError:
930 except error.LockError:
930 return '2\n'
931 return '2\n'
931
932
932 @wireprotocommand('unbundle', 'heads')
933 @wireprotocommand('unbundle', 'heads')
933 def unbundle(repo, proto, heads):
934 def unbundle(repo, proto, heads):
934 their_heads = decodelist(heads)
935 their_heads = decodelist(heads)
935
936
936 try:
937 try:
937 proto.redirect()
938 proto.redirect()
938
939
939 exchange.check_heads(repo, their_heads, 'preparing changes')
940 exchange.check_heads(repo, their_heads, 'preparing changes')
940
941
941 # write bundle data to temporary file because it can be big
942 # write bundle data to temporary file because it can be big
942 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
943 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
943 fp = os.fdopen(fd, 'wb+')
944 fp = os.fdopen(fd, 'wb+')
944 r = 0
945 r = 0
945 try:
946 try:
946 proto.getfile(fp)
947 proto.getfile(fp)
947 fp.seek(0)
948 fp.seek(0)
948 gen = exchange.readbundle(repo.ui, fp, None)
949 gen = exchange.readbundle(repo.ui, fp, None)
949 if (isinstance(gen, changegroupmod.cg1unpacker)
950 if (isinstance(gen, changegroupmod.cg1unpacker)
950 and not bundle1allowed(repo, 'push')):
951 and not bundle1allowed(repo, 'push')):
951 return ooberror(bundle2required)
952 if proto.name == 'http':
953 # need to special case http because stderr do not get to
954 # the http client on failed push so we need to abuse some
955 # other error type to make sure the message get to the
956 # user.
957 return ooberror(bundle2required)
958 raise error.Abort(bundle2requiredmain,
959 hint=bundle2requiredhint)
952
960
953 r = exchange.unbundle(repo, gen, their_heads, 'serve',
961 r = exchange.unbundle(repo, gen, their_heads, 'serve',
954 proto._client())
962 proto._client())
955 if util.safehasattr(r, 'addpart'):
963 if util.safehasattr(r, 'addpart'):
956 # The return looks streamable, we are in the bundle2 case and
964 # The return looks streamable, we are in the bundle2 case and
957 # should return a stream.
965 # should return a stream.
958 return streamres(gen=r.getchunks())
966 return streamres(gen=r.getchunks())
959 return pushres(r)
967 return pushres(r)
960
968
961 finally:
969 finally:
962 fp.close()
970 fp.close()
963 os.unlink(tempname)
971 os.unlink(tempname)
964
972
965 except (error.BundleValueError, error.Abort, error.PushRaced) as exc:
973 except (error.BundleValueError, error.Abort, error.PushRaced) as exc:
966 # handle non-bundle2 case first
974 # handle non-bundle2 case first
967 if not getattr(exc, 'duringunbundle2', False):
975 if not getattr(exc, 'duringunbundle2', False):
968 try:
976 try:
969 raise
977 raise
970 except error.Abort:
978 except error.Abort:
971 # The old code we moved used util.stderr directly.
979 # The old code we moved used util.stderr directly.
972 # We did not change it to minimise code change.
980 # We did not change it to minimise code change.
973 # This need to be moved to something proper.
981 # This need to be moved to something proper.
974 # Feel free to do it.
982 # Feel free to do it.
975 util.stderr.write("abort: %s\n" % exc)
983 util.stderr.write("abort: %s\n" % exc)
976 return pushres(0)
984 return pushres(0)
977 except error.PushRaced:
985 except error.PushRaced:
978 return pusherr(str(exc))
986 return pusherr(str(exc))
979
987
980 bundler = bundle2.bundle20(repo.ui)
988 bundler = bundle2.bundle20(repo.ui)
981 for out in getattr(exc, '_bundle2salvagedoutput', ()):
989 for out in getattr(exc, '_bundle2salvagedoutput', ()):
982 bundler.addpart(out)
990 bundler.addpart(out)
983 try:
991 try:
984 try:
992 try:
985 raise
993 raise
986 except error.PushkeyFailed as exc:
994 except error.PushkeyFailed as exc:
987 # check client caps
995 # check client caps
988 remotecaps = getattr(exc, '_replycaps', None)
996 remotecaps = getattr(exc, '_replycaps', None)
989 if (remotecaps is not None
997 if (remotecaps is not None
990 and 'pushkey' not in remotecaps.get('error', ())):
998 and 'pushkey' not in remotecaps.get('error', ())):
991 # no support remote side, fallback to Abort handler.
999 # no support remote side, fallback to Abort handler.
992 raise
1000 raise
993 part = bundler.newpart('error:pushkey')
1001 part = bundler.newpart('error:pushkey')
994 part.addparam('in-reply-to', exc.partid)
1002 part.addparam('in-reply-to', exc.partid)
995 if exc.namespace is not None:
1003 if exc.namespace is not None:
996 part.addparam('namespace', exc.namespace, mandatory=False)
1004 part.addparam('namespace', exc.namespace, mandatory=False)
997 if exc.key is not None:
1005 if exc.key is not None:
998 part.addparam('key', exc.key, mandatory=False)
1006 part.addparam('key', exc.key, mandatory=False)
999 if exc.new is not None:
1007 if exc.new is not None:
1000 part.addparam('new', exc.new, mandatory=False)
1008 part.addparam('new', exc.new, mandatory=False)
1001 if exc.old is not None:
1009 if exc.old is not None:
1002 part.addparam('old', exc.old, mandatory=False)
1010 part.addparam('old', exc.old, mandatory=False)
1003 if exc.ret is not None:
1011 if exc.ret is not None:
1004 part.addparam('ret', exc.ret, mandatory=False)
1012 part.addparam('ret', exc.ret, mandatory=False)
1005 except error.BundleValueError as exc:
1013 except error.BundleValueError as exc:
1006 errpart = bundler.newpart('error:unsupportedcontent')
1014 errpart = bundler.newpart('error:unsupportedcontent')
1007 if exc.parttype is not None:
1015 if exc.parttype is not None:
1008 errpart.addparam('parttype', exc.parttype)
1016 errpart.addparam('parttype', exc.parttype)
1009 if exc.params:
1017 if exc.params:
1010 errpart.addparam('params', '\0'.join(exc.params))
1018 errpart.addparam('params', '\0'.join(exc.params))
1011 except error.Abort as exc:
1019 except error.Abort as exc:
1012 manargs = [('message', str(exc))]
1020 manargs = [('message', str(exc))]
1013 advargs = []
1021 advargs = []
1014 if exc.hint is not None:
1022 if exc.hint is not None:
1015 advargs.append(('hint', exc.hint))
1023 advargs.append(('hint', exc.hint))
1016 bundler.addpart(bundle2.bundlepart('error:abort',
1024 bundler.addpart(bundle2.bundlepart('error:abort',
1017 manargs, advargs))
1025 manargs, advargs))
1018 except error.PushRaced as exc:
1026 except error.PushRaced as exc:
1019 bundler.newpart('error:pushraced', [('message', str(exc))])
1027 bundler.newpart('error:pushraced', [('message', str(exc))])
1020 return streamres(gen=bundler.getchunks())
1028 return streamres(gen=bundler.getchunks())
@@ -1,1116 +1,1124 b''
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,exchange
18 > evolution=createmarkers,exchange
19 > bundle2-output-capture=True
19 > bundle2-output-capture=True
20 > [ui]
20 > [ui]
21 > ssh=python "$TESTDIR/dummyssh"
21 > ssh=python "$TESTDIR/dummyssh"
22 > logtemplate={rev}:{node|short} {phase} {author} {bookmarks} {desc|firstline}
22 > logtemplate={rev}:{node|short} {phase} {author} {bookmarks} {desc|firstline}
23 > [web]
23 > [web]
24 > push_ssl = false
24 > push_ssl = false
25 > allow_push = *
25 > allow_push = *
26 > [phases]
26 > [phases]
27 > publish=False
27 > publish=False
28 > [hooks]
28 > [hooks]
29 > pretxnclose.tip = hg log -r tip -T "pre-close-tip:{node|short} {phase} {bookmarks}\n"
29 > pretxnclose.tip = hg log -r tip -T "pre-close-tip:{node|short} {phase} {bookmarks}\n"
30 > txnclose.tip = hg log -r tip -T "postclose-tip:{node|short} {phase} {bookmarks}\n"
30 > txnclose.tip = hg log -r tip -T "postclose-tip:{node|short} {phase} {bookmarks}\n"
31 > txnclose.env = sh -c "HG_LOCAL= printenv.py txnclose"
31 > txnclose.env = sh -c "HG_LOCAL= printenv.py txnclose"
32 > pushkey= sh "$TESTTMP/bundle2-pushkey-hook.sh"
32 > pushkey= sh "$TESTTMP/bundle2-pushkey-hook.sh"
33 > EOF
33 > EOF
34
34
35 The extension requires a repo (currently unused)
35 The extension requires a repo (currently unused)
36
36
37 $ hg init main
37 $ hg init main
38 $ cd main
38 $ cd main
39 $ touch a
39 $ touch a
40 $ hg add a
40 $ hg add a
41 $ hg commit -m 'a'
41 $ hg commit -m 'a'
42 pre-close-tip:3903775176ed draft
42 pre-close-tip:3903775176ed draft
43 postclose-tip:3903775176ed draft
43 postclose-tip:3903775176ed draft
44 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
44 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
45
45
46 $ hg unbundle $TESTDIR/bundles/rebase.hg
46 $ hg unbundle $TESTDIR/bundles/rebase.hg
47 adding changesets
47 adding changesets
48 adding manifests
48 adding manifests
49 adding file changes
49 adding file changes
50 added 8 changesets with 7 changes to 7 files (+3 heads)
50 added 8 changesets with 7 changes to 7 files (+3 heads)
51 pre-close-tip:02de42196ebe draft
51 pre-close-tip:02de42196ebe draft
52 postclose-tip:02de42196ebe draft
52 postclose-tip:02de42196ebe draft
53 txnclose hook: HG_NODE=cd010b8cd998f3981a5a8115f94f8da4ab506089 HG_NODE_LAST=02de42196ebee42ef284b6780a87cdc96e8eaab6 HG_PHASES_MOVED=1 HG_SOURCE=unbundle HG_TXNID=TXN:* HG_TXNNAME=unbundle (glob)
53 txnclose hook: HG_NODE=cd010b8cd998f3981a5a8115f94f8da4ab506089 HG_NODE_LAST=02de42196ebee42ef284b6780a87cdc96e8eaab6 HG_PHASES_MOVED=1 HG_SOURCE=unbundle HG_TXNID=TXN:* HG_TXNNAME=unbundle (glob)
54 bundle:*/tests/bundles/rebase.hg HG_URL=bundle:*/tests/bundles/rebase.hg (glob)
54 bundle:*/tests/bundles/rebase.hg HG_URL=bundle:*/tests/bundles/rebase.hg (glob)
55 (run 'hg heads' to see heads, 'hg merge' to merge)
55 (run 'hg heads' to see heads, 'hg merge' to merge)
56
56
57 $ cd ..
57 $ cd ..
58
58
59 Real world exchange
59 Real world exchange
60 =====================
60 =====================
61
61
62 Add more obsolescence information
62 Add more obsolescence information
63
63
64 $ hg -R main debugobsolete -d '0 0' 1111111111111111111111111111111111111111 `getmainid 9520eea781bc`
64 $ hg -R main debugobsolete -d '0 0' 1111111111111111111111111111111111111111 `getmainid 9520eea781bc`
65 pre-close-tip:02de42196ebe draft
65 pre-close-tip:02de42196ebe draft
66 postclose-tip:02de42196ebe draft
66 postclose-tip:02de42196ebe draft
67 txnclose hook: HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
67 txnclose hook: HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
68 $ hg -R main debugobsolete -d '0 0' 2222222222222222222222222222222222222222 `getmainid 24b6387c8c8c`
68 $ hg -R main debugobsolete -d '0 0' 2222222222222222222222222222222222222222 `getmainid 24b6387c8c8c`
69 pre-close-tip:02de42196ebe draft
69 pre-close-tip:02de42196ebe draft
70 postclose-tip:02de42196ebe draft
70 postclose-tip:02de42196ebe draft
71 txnclose hook: HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
71 txnclose hook: HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
72
72
73 clone --pull
73 clone --pull
74
74
75 $ hg -R main phase --public cd010b8cd998
75 $ hg -R main phase --public cd010b8cd998
76 pre-close-tip:02de42196ebe draft
76 pre-close-tip:02de42196ebe draft
77 postclose-tip:02de42196ebe draft
77 postclose-tip:02de42196ebe draft
78 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=phase (glob)
78 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=phase (glob)
79 $ hg clone main other --pull --rev 9520eea781bc
79 $ hg clone main other --pull --rev 9520eea781bc
80 adding changesets
80 adding changesets
81 adding manifests
81 adding manifests
82 adding file changes
82 adding file changes
83 added 2 changesets with 2 changes to 2 files
83 added 2 changesets with 2 changes to 2 files
84 1 new obsolescence markers
84 1 new obsolescence markers
85 pre-close-tip:9520eea781bc draft
85 pre-close-tip:9520eea781bc draft
86 postclose-tip:9520eea781bc draft
86 postclose-tip:9520eea781bc draft
87 txnclose hook: HG_NEW_OBSMARKERS=1 HG_NODE=cd010b8cd998f3981a5a8115f94f8da4ab506089 HG_NODE_LAST=9520eea781bcca16c1e15acc0ba14335a0e8e5ba HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
87 txnclose hook: HG_NEW_OBSMARKERS=1 HG_NODE=cd010b8cd998f3981a5a8115f94f8da4ab506089 HG_NODE_LAST=9520eea781bcca16c1e15acc0ba14335a0e8e5ba HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
88 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
88 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
89 updating to branch default
89 updating to branch default
90 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
90 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
91 $ hg -R other log -G
91 $ hg -R other log -G
92 @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
92 @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
93 |
93 |
94 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
94 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
95
95
96 $ hg -R other debugobsolete
96 $ hg -R other debugobsolete
97 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
97 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
98
98
99 pull
99 pull
100
100
101 $ hg -R main phase --public 9520eea781bc
101 $ hg -R main phase --public 9520eea781bc
102 pre-close-tip:02de42196ebe draft
102 pre-close-tip:02de42196ebe draft
103 postclose-tip:02de42196ebe draft
103 postclose-tip:02de42196ebe draft
104 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=phase (glob)
104 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=phase (glob)
105 $ hg -R other pull -r 24b6387c8c8c
105 $ hg -R other pull -r 24b6387c8c8c
106 pulling from $TESTTMP/main (glob)
106 pulling from $TESTTMP/main (glob)
107 searching for changes
107 searching for changes
108 adding changesets
108 adding changesets
109 adding manifests
109 adding manifests
110 adding file changes
110 adding file changes
111 added 1 changesets with 1 changes to 1 files (+1 heads)
111 added 1 changesets with 1 changes to 1 files (+1 heads)
112 1 new obsolescence markers
112 1 new obsolescence markers
113 pre-close-tip:24b6387c8c8c draft
113 pre-close-tip:24b6387c8c8c draft
114 postclose-tip:24b6387c8c8c draft
114 postclose-tip:24b6387c8c8c draft
115 txnclose hook: HG_NEW_OBSMARKERS=1 HG_NODE=24b6387c8c8cae37178880f3fa95ded3cb1cf785 HG_NODE_LAST=24b6387c8c8cae37178880f3fa95ded3cb1cf785 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
115 txnclose hook: HG_NEW_OBSMARKERS=1 HG_NODE=24b6387c8c8cae37178880f3fa95ded3cb1cf785 HG_NODE_LAST=24b6387c8c8cae37178880f3fa95ded3cb1cf785 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
116 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
116 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
117 (run 'hg heads' to see heads, 'hg merge' to merge)
117 (run 'hg heads' to see heads, 'hg merge' to merge)
118 $ hg -R other log -G
118 $ hg -R other log -G
119 o 2:24b6387c8c8c draft Nicolas Dumazet <nicdumz.commits@gmail.com> F
119 o 2:24b6387c8c8c draft Nicolas Dumazet <nicdumz.commits@gmail.com> F
120 |
120 |
121 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
121 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
122 |/
122 |/
123 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
123 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
124
124
125 $ hg -R other debugobsolete
125 $ hg -R other debugobsolete
126 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
126 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
127 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
127 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
128
128
129 pull empty (with phase movement)
129 pull empty (with phase movement)
130
130
131 $ hg -R main phase --public 24b6387c8c8c
131 $ hg -R main phase --public 24b6387c8c8c
132 pre-close-tip:02de42196ebe draft
132 pre-close-tip:02de42196ebe draft
133 postclose-tip:02de42196ebe draft
133 postclose-tip:02de42196ebe draft
134 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=phase (glob)
134 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=phase (glob)
135 $ hg -R other pull -r 24b6387c8c8c
135 $ hg -R other pull -r 24b6387c8c8c
136 pulling from $TESTTMP/main (glob)
136 pulling from $TESTTMP/main (glob)
137 no changes found
137 no changes found
138 pre-close-tip:24b6387c8c8c public
138 pre-close-tip:24b6387c8c8c public
139 postclose-tip:24b6387c8c8c public
139 postclose-tip:24b6387c8c8c public
140 txnclose hook: HG_NEW_OBSMARKERS=0 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
140 txnclose hook: HG_NEW_OBSMARKERS=0 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
141 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
141 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
142 $ hg -R other log -G
142 $ hg -R other log -G
143 o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
143 o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
144 |
144 |
145 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
145 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
146 |/
146 |/
147 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
147 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
148
148
149 $ hg -R other debugobsolete
149 $ hg -R other debugobsolete
150 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
150 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
151 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
151 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
152
152
153 pull empty
153 pull empty
154
154
155 $ hg -R other pull -r 24b6387c8c8c
155 $ hg -R other pull -r 24b6387c8c8c
156 pulling from $TESTTMP/main (glob)
156 pulling from $TESTTMP/main (glob)
157 no changes found
157 no changes found
158 pre-close-tip:24b6387c8c8c public
158 pre-close-tip:24b6387c8c8c public
159 postclose-tip:24b6387c8c8c public
159 postclose-tip:24b6387c8c8c public
160 txnclose hook: HG_NEW_OBSMARKERS=0 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
160 txnclose hook: HG_NEW_OBSMARKERS=0 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
161 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
161 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
162 $ hg -R other log -G
162 $ hg -R other log -G
163 o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
163 o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
164 |
164 |
165 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
165 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
166 |/
166 |/
167 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
167 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
168
168
169 $ hg -R other debugobsolete
169 $ hg -R other debugobsolete
170 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
170 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
171 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
171 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
172
172
173 add extra data to test their exchange during push
173 add extra data to test their exchange during push
174
174
175 $ hg -R main bookmark --rev eea13746799a book_eea1
175 $ hg -R main bookmark --rev eea13746799a book_eea1
176 pre-close-tip:02de42196ebe draft
176 pre-close-tip:02de42196ebe draft
177 postclose-tip:02de42196ebe draft
177 postclose-tip:02de42196ebe draft
178 txnclose hook: HG_BOOKMARK_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
178 txnclose hook: HG_BOOKMARK_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
179 $ hg -R main debugobsolete -d '0 0' 3333333333333333333333333333333333333333 `getmainid eea13746799a`
179 $ hg -R main debugobsolete -d '0 0' 3333333333333333333333333333333333333333 `getmainid eea13746799a`
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_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
182 txnclose hook: HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
183 $ hg -R main bookmark --rev 02de42196ebe book_02de
183 $ hg -R main bookmark --rev 02de42196ebe book_02de
184 pre-close-tip:02de42196ebe draft book_02de
184 pre-close-tip:02de42196ebe draft book_02de
185 postclose-tip:02de42196ebe draft book_02de
185 postclose-tip:02de42196ebe draft book_02de
186 txnclose hook: HG_BOOKMARK_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
186 txnclose hook: HG_BOOKMARK_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
187 $ hg -R main debugobsolete -d '0 0' 4444444444444444444444444444444444444444 `getmainid 02de42196ebe`
187 $ hg -R main debugobsolete -d '0 0' 4444444444444444444444444444444444444444 `getmainid 02de42196ebe`
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_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
190 txnclose hook: HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
191 $ hg -R main bookmark --rev 42ccdea3bb16 book_42cc
191 $ hg -R main bookmark --rev 42ccdea3bb16 book_42cc
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_BOOKMARK_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
194 txnclose hook: HG_BOOKMARK_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
195 $ hg -R main debugobsolete -d '0 0' 5555555555555555555555555555555555555555 `getmainid 42ccdea3bb16`
195 $ hg -R main debugobsolete -d '0 0' 5555555555555555555555555555555555555555 `getmainid 42ccdea3bb16`
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_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
198 txnclose hook: HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
199 $ hg -R main bookmark --rev 5fddd98957c8 book_5fdd
199 $ hg -R main bookmark --rev 5fddd98957c8 book_5fdd
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_BOOKMARK_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
202 txnclose hook: HG_BOOKMARK_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
203 $ hg -R main debugobsolete -d '0 0' 6666666666666666666666666666666666666666 `getmainid 5fddd98957c8`
203 $ hg -R main debugobsolete -d '0 0' 6666666666666666666666666666666666666666 `getmainid 5fddd98957c8`
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_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
206 txnclose hook: HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
207 $ hg -R main bookmark --rev 32af7686d403 book_32af
207 $ hg -R main bookmark --rev 32af7686d403 book_32af
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_BOOKMARK_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
210 txnclose hook: HG_BOOKMARK_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
211 $ hg -R main debugobsolete -d '0 0' 7777777777777777777777777777777777777777 `getmainid 32af7686d403`
211 $ hg -R main debugobsolete -d '0 0' 7777777777777777777777777777777777777777 `getmainid 32af7686d403`
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_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
214 txnclose hook: HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
215
215
216 $ hg -R other bookmark --rev cd010b8cd998 book_eea1
216 $ hg -R other bookmark --rev cd010b8cd998 book_eea1
217 pre-close-tip:24b6387c8c8c public
217 pre-close-tip:24b6387c8c8c public
218 postclose-tip:24b6387c8c8c public
218 postclose-tip:24b6387c8c8c public
219 txnclose hook: HG_BOOKMARK_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
219 txnclose hook: HG_BOOKMARK_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
220 $ hg -R other bookmark --rev cd010b8cd998 book_02de
220 $ hg -R other bookmark --rev cd010b8cd998 book_02de
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_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
223 txnclose hook: HG_BOOKMARK_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
224 $ hg -R other bookmark --rev cd010b8cd998 book_42cc
224 $ hg -R other bookmark --rev cd010b8cd998 book_42cc
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_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
227 txnclose hook: HG_BOOKMARK_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
228 $ hg -R other bookmark --rev cd010b8cd998 book_5fdd
228 $ hg -R other bookmark --rev cd010b8cd998 book_5fdd
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_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
231 txnclose hook: HG_BOOKMARK_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
232 $ hg -R other bookmark --rev cd010b8cd998 book_32af
232 $ hg -R other bookmark --rev cd010b8cd998 book_32af
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_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
235 txnclose hook: HG_BOOKMARK_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=bookmark (glob)
236
236
237 $ hg -R main phase --public eea13746799a
237 $ hg -R main phase --public eea13746799a
238 pre-close-tip:02de42196ebe draft book_02de
238 pre-close-tip:02de42196ebe draft book_02de
239 postclose-tip:02de42196ebe draft book_02de
239 postclose-tip:02de42196ebe draft book_02de
240 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=phase (glob)
240 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=phase (glob)
241
241
242 push
242 push
243 $ hg -R main push other --rev eea13746799a --bookmark book_eea1
243 $ hg -R main push other --rev eea13746799a --bookmark book_eea1
244 pushing to other
244 pushing to other
245 searching for changes
245 searching for changes
246 remote: adding changesets
246 remote: adding changesets
247 remote: adding manifests
247 remote: adding manifests
248 remote: adding file changes
248 remote: adding file changes
249 remote: added 1 changesets with 0 changes to 0 files (-1 heads)
249 remote: added 1 changesets with 0 changes to 0 files (-1 heads)
250 remote: 1 new obsolescence markers
250 remote: 1 new obsolescence markers
251 remote: pre-close-tip:eea13746799a public book_eea1
251 remote: pre-close-tip:eea13746799a public book_eea1
252 remote: pushkey: lock state after "phases"
252 remote: pushkey: lock state after "phases"
253 remote: lock: free
253 remote: lock: free
254 remote: wlock: free
254 remote: wlock: free
255 remote: pushkey: lock state after "bookmarks"
255 remote: pushkey: lock state after "bookmarks"
256 remote: lock: free
256 remote: lock: free
257 remote: wlock: free
257 remote: wlock: free
258 remote: postclose-tip:eea13746799a public book_eea1
258 remote: postclose-tip:eea13746799a public book_eea1
259 remote: txnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_NEW_OBSMARKERS=1 HG_NODE=eea13746799a9e0bfd88f29d3c2e9dc9389f524f HG_NODE_LAST=eea13746799a9e0bfd88f29d3c2e9dc9389f524f HG_PHASES_MOVED=1 HG_SOURCE=push HG_TXNID=TXN:* HG_TXNNAME=push HG_URL=file:$TESTTMP/other (glob)
259 remote: txnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_NEW_OBSMARKERS=1 HG_NODE=eea13746799a9e0bfd88f29d3c2e9dc9389f524f HG_NODE_LAST=eea13746799a9e0bfd88f29d3c2e9dc9389f524f HG_PHASES_MOVED=1 HG_SOURCE=push HG_TXNID=TXN:* HG_TXNNAME=push HG_URL=file:$TESTTMP/other (glob)
260 updating bookmark book_eea1
260 updating bookmark book_eea1
261 pre-close-tip:02de42196ebe draft book_02de
261 pre-close-tip:02de42196ebe draft book_02de
262 postclose-tip:02de42196ebe draft book_02de
262 postclose-tip:02de42196ebe draft book_02de
263 txnclose hook: HG_SOURCE=push-response HG_TXNID=TXN:* HG_TXNNAME=push-response (glob)
263 txnclose hook: HG_SOURCE=push-response HG_TXNID=TXN:* HG_TXNNAME=push-response (glob)
264 file:/*/$TESTTMP/other HG_URL=file:$TESTTMP/other (glob)
264 file:/*/$TESTTMP/other HG_URL=file:$TESTTMP/other (glob)
265 $ hg -R other log -G
265 $ hg -R other log -G
266 o 3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> book_eea1 G
266 o 3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> book_eea1 G
267 |\
267 |\
268 | o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
268 | o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
269 | |
269 | |
270 @ | 1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
270 @ | 1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
271 |/
271 |/
272 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_02de book_32af book_42cc book_5fdd A
272 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_02de book_32af book_42cc book_5fdd A
273
273
274 $ hg -R other debugobsolete
274 $ hg -R other debugobsolete
275 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
275 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
276 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
276 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
277 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
277 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
278
278
279 pull over ssh
279 pull over ssh
280
280
281 $ hg -R other pull ssh://user@dummy/main -r 02de42196ebe --bookmark book_02de
281 $ hg -R other pull ssh://user@dummy/main -r 02de42196ebe --bookmark book_02de
282 pulling from ssh://user@dummy/main
282 pulling from ssh://user@dummy/main
283 searching for changes
283 searching for changes
284 adding changesets
284 adding changesets
285 adding manifests
285 adding manifests
286 adding file changes
286 adding file changes
287 added 1 changesets with 1 changes to 1 files (+1 heads)
287 added 1 changesets with 1 changes to 1 files (+1 heads)
288 1 new obsolescence markers
288 1 new obsolescence markers
289 updating bookmark book_02de
289 updating bookmark book_02de
290 pre-close-tip:02de42196ebe draft book_02de
290 pre-close-tip:02de42196ebe draft book_02de
291 postclose-tip:02de42196ebe draft book_02de
291 postclose-tip:02de42196ebe draft book_02de
292 txnclose hook: HG_BOOKMARK_MOVED=1 HG_NEW_OBSMARKERS=1 HG_NODE=02de42196ebee42ef284b6780a87cdc96e8eaab6 HG_NODE_LAST=02de42196ebee42ef284b6780a87cdc96e8eaab6 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
292 txnclose hook: HG_BOOKMARK_MOVED=1 HG_NEW_OBSMARKERS=1 HG_NODE=02de42196ebee42ef284b6780a87cdc96e8eaab6 HG_NODE_LAST=02de42196ebee42ef284b6780a87cdc96e8eaab6 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
293 ssh://user@dummy/main HG_URL=ssh://user@dummy/main
293 ssh://user@dummy/main HG_URL=ssh://user@dummy/main
294 (run 'hg heads' to see heads, 'hg merge' to merge)
294 (run 'hg heads' to see heads, 'hg merge' to merge)
295 $ hg -R other debugobsolete
295 $ hg -R other debugobsolete
296 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
296 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
297 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
297 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
298 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
298 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
299 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
299 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
300
300
301 pull over http
301 pull over http
302
302
303 $ hg serve -R main -p $HGPORT -d --pid-file=main.pid -E main-error.log
303 $ hg serve -R main -p $HGPORT -d --pid-file=main.pid -E main-error.log
304 $ cat main.pid >> $DAEMON_PIDS
304 $ cat main.pid >> $DAEMON_PIDS
305
305
306 $ hg -R other pull http://localhost:$HGPORT/ -r 42ccdea3bb16 --bookmark book_42cc
306 $ hg -R other pull http://localhost:$HGPORT/ -r 42ccdea3bb16 --bookmark book_42cc
307 pulling from http://localhost:$HGPORT/
307 pulling from http://localhost:$HGPORT/
308 searching for changes
308 searching for changes
309 adding changesets
309 adding changesets
310 adding manifests
310 adding manifests
311 adding file changes
311 adding file changes
312 added 1 changesets with 1 changes to 1 files (+1 heads)
312 added 1 changesets with 1 changes to 1 files (+1 heads)
313 1 new obsolescence markers
313 1 new obsolescence markers
314 updating bookmark book_42cc
314 updating bookmark book_42cc
315 pre-close-tip:42ccdea3bb16 draft book_42cc
315 pre-close-tip:42ccdea3bb16 draft book_42cc
316 postclose-tip:42ccdea3bb16 draft book_42cc
316 postclose-tip:42ccdea3bb16 draft book_42cc
317 txnclose hook: HG_BOOKMARK_MOVED=1 HG_NEW_OBSMARKERS=1 HG_NODE=42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 HG_NODE_LAST=42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
317 txnclose hook: HG_BOOKMARK_MOVED=1 HG_NEW_OBSMARKERS=1 HG_NODE=42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 HG_NODE_LAST=42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
318 http://localhost:$HGPORT/ HG_URL=http://localhost:$HGPORT/
318 http://localhost:$HGPORT/ HG_URL=http://localhost:$HGPORT/
319 (run 'hg heads .' to see heads, 'hg merge' to merge)
319 (run 'hg heads .' to see heads, 'hg merge' to merge)
320 $ cat main-error.log
320 $ cat main-error.log
321 $ hg -R other debugobsolete
321 $ hg -R other debugobsolete
322 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
322 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
323 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
323 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
324 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
324 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
325 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
325 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
326 5555555555555555555555555555555555555555 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
326 5555555555555555555555555555555555555555 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
327
327
328 push over ssh
328 push over ssh
329
329
330 $ hg -R main push ssh://user@dummy/other -r 5fddd98957c8 --bookmark book_5fdd
330 $ hg -R main push ssh://user@dummy/other -r 5fddd98957c8 --bookmark book_5fdd
331 pushing to ssh://user@dummy/other
331 pushing to ssh://user@dummy/other
332 searching for changes
332 searching for changes
333 remote: adding changesets
333 remote: adding changesets
334 remote: adding manifests
334 remote: adding manifests
335 remote: adding file changes
335 remote: adding file changes
336 remote: added 1 changesets with 1 changes to 1 files
336 remote: added 1 changesets with 1 changes to 1 files
337 remote: 1 new obsolescence markers
337 remote: 1 new obsolescence markers
338 remote: pre-close-tip:5fddd98957c8 draft book_5fdd
338 remote: pre-close-tip:5fddd98957c8 draft book_5fdd
339 remote: pushkey: lock state after "bookmarks"
339 remote: pushkey: lock state after "bookmarks"
340 remote: lock: free
340 remote: lock: free
341 remote: wlock: free
341 remote: wlock: free
342 remote: postclose-tip:5fddd98957c8 draft book_5fdd
342 remote: postclose-tip:5fddd98957c8 draft book_5fdd
343 remote: txnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_NEW_OBSMARKERS=1 HG_NODE=5fddd98957c8a54a4d436dfe1da9d87f21a1b97b HG_NODE_LAST=5fddd98957c8a54a4d436dfe1da9d87f21a1b97b HG_SOURCE=serve HG_TXNID=TXN:* HG_TXNNAME=serve HG_URL=remote:ssh:127.0.0.1 (glob)
343 remote: txnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_NEW_OBSMARKERS=1 HG_NODE=5fddd98957c8a54a4d436dfe1da9d87f21a1b97b HG_NODE_LAST=5fddd98957c8a54a4d436dfe1da9d87f21a1b97b HG_SOURCE=serve HG_TXNID=TXN:* HG_TXNNAME=serve HG_URL=remote:ssh:127.0.0.1 (glob)
344 updating bookmark book_5fdd
344 updating bookmark book_5fdd
345 pre-close-tip:02de42196ebe draft book_02de
345 pre-close-tip:02de42196ebe draft book_02de
346 postclose-tip:02de42196ebe draft book_02de
346 postclose-tip:02de42196ebe draft book_02de
347 txnclose hook: HG_SOURCE=push-response HG_TXNID=TXN:* HG_TXNNAME=push-response (glob)
347 txnclose hook: HG_SOURCE=push-response HG_TXNID=TXN:* HG_TXNNAME=push-response (glob)
348 ssh://user@dummy/other HG_URL=ssh://user@dummy/other
348 ssh://user@dummy/other HG_URL=ssh://user@dummy/other
349 $ hg -R other log -G
349 $ hg -R other log -G
350 o 6:5fddd98957c8 draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_5fdd C
350 o 6:5fddd98957c8 draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_5fdd C
351 |
351 |
352 o 5:42ccdea3bb16 draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_42cc B
352 o 5:42ccdea3bb16 draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_42cc B
353 |
353 |
354 | o 4:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_02de H
354 | o 4:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_02de H
355 | |
355 | |
356 | | o 3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> book_eea1 G
356 | | o 3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> book_eea1 G
357 | |/|
357 | |/|
358 | o | 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
358 | o | 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
359 |/ /
359 |/ /
360 | @ 1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
360 | @ 1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
361 |/
361 |/
362 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_32af A
362 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_32af A
363
363
364 $ hg -R other debugobsolete
364 $ hg -R other debugobsolete
365 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
365 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
366 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
366 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
367 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
367 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
368 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
368 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
369 5555555555555555555555555555555555555555 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
369 5555555555555555555555555555555555555555 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
370 6666666666666666666666666666666666666666 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
370 6666666666666666666666666666666666666666 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
371
371
372 push over http
372 push over http
373
373
374 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
374 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
375 $ cat other.pid >> $DAEMON_PIDS
375 $ cat other.pid >> $DAEMON_PIDS
376
376
377 $ hg -R main phase --public 32af7686d403
377 $ hg -R main phase --public 32af7686d403
378 pre-close-tip:02de42196ebe draft book_02de
378 pre-close-tip:02de42196ebe draft book_02de
379 postclose-tip:02de42196ebe draft book_02de
379 postclose-tip:02de42196ebe draft book_02de
380 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=phase (glob)
380 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=phase (glob)
381 $ hg -R main push http://localhost:$HGPORT2/ -r 32af7686d403 --bookmark book_32af
381 $ hg -R main push http://localhost:$HGPORT2/ -r 32af7686d403 --bookmark book_32af
382 pushing to http://localhost:$HGPORT2/
382 pushing to http://localhost:$HGPORT2/
383 searching for changes
383 searching for changes
384 remote: adding changesets
384 remote: adding changesets
385 remote: adding manifests
385 remote: adding manifests
386 remote: adding file changes
386 remote: adding file changes
387 remote: added 1 changesets with 1 changes to 1 files
387 remote: added 1 changesets with 1 changes to 1 files
388 remote: 1 new obsolescence markers
388 remote: 1 new obsolescence markers
389 remote: pre-close-tip:32af7686d403 public book_32af
389 remote: pre-close-tip:32af7686d403 public book_32af
390 remote: pushkey: lock state after "phases"
390 remote: pushkey: lock state after "phases"
391 remote: lock: free
391 remote: lock: free
392 remote: wlock: free
392 remote: wlock: free
393 remote: pushkey: lock state after "bookmarks"
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_NEW_OBSMARKERS=1 HG_NODE=32af7686d403cf45b5d95f2d70cebea587ac806a HG_NODE_LAST=32af7686d403cf45b5d95f2d70cebea587ac806a HG_PHASES_MOVED=1 HG_SOURCE=serve HG_TXNID=TXN:* HG_TXNNAME=serve HG_URL=remote:http:127.0.0.1: (glob)
397 remote: txnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_NEW_OBSMARKERS=1 HG_NODE=32af7686d403cf45b5d95f2d70cebea587ac806a HG_NODE_LAST=32af7686d403cf45b5d95f2d70cebea587ac806a HG_PHASES_MOVED=1 HG_SOURCE=serve HG_TXNID=TXN:* HG_TXNNAME=serve HG_URL=remote:http:127.0.0.1: (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_SOURCE=push-response HG_TXNID=TXN:* HG_TXNNAME=push-response (glob)
401 txnclose hook: HG_SOURCE=push-response HG_TXNID=TXN:* HG_TXNNAME=push-response (glob)
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 >
459 >
460 > def _pushbundle2failpart(pushop, bundler):
460 > def _pushbundle2failpart(pushop, bundler):
461 > reason = pushop.ui.config('failpush', 'reason', None)
461 > reason = pushop.ui.config('failpush', 'reason', None)
462 > part = None
462 > part = None
463 > if reason == 'abort':
463 > if reason == 'abort':
464 > bundler.newpart('test:abort')
464 > bundler.newpart('test:abort')
465 > if reason == 'unknown':
465 > if reason == 'unknown':
466 > bundler.newpart('test:unknown')
466 > bundler.newpart('test:unknown')
467 > if reason == 'race':
467 > if reason == 'race':
468 > # 20 Bytes of crap
468 > # 20 Bytes of crap
469 > bundler.newpart('check:heads', data='01234567890123456789')
469 > bundler.newpart('check:heads', data='01234567890123456789')
470 >
470 >
471 > @bundle2.parthandler("test:abort")
471 > @bundle2.parthandler("test:abort")
472 > def handleabort(op, part):
472 > def handleabort(op, part):
473 > raise error.Abort('Abandon ship!', hint="don't panic")
473 > raise error.Abort('Abandon ship!', hint="don't panic")
474 >
474 >
475 > def uisetup(ui):
475 > def uisetup(ui):
476 > exchange.b2partsgenmapping['failpart'] = _pushbundle2failpart
476 > exchange.b2partsgenmapping['failpart'] = _pushbundle2failpart
477 > exchange.b2partsgenorder.insert(0, 'failpart')
477 > exchange.b2partsgenorder.insert(0, 'failpart')
478 >
478 >
479 > EOF
479 > EOF
480
480
481 $ cd main
481 $ cd main
482 $ hg up tip
482 $ hg up tip
483 3 files updated, 0 files merged, 1 files removed, 0 files unresolved
483 3 files updated, 0 files merged, 1 files removed, 0 files unresolved
484 $ echo 'I' > I
484 $ echo 'I' > I
485 $ hg add I
485 $ hg add I
486 $ hg ci -m 'I'
486 $ hg ci -m 'I'
487 pre-close-tip:e7ec4e813ba6 draft
487 pre-close-tip:e7ec4e813ba6 draft
488 postclose-tip:e7ec4e813ba6 draft
488 postclose-tip:e7ec4e813ba6 draft
489 txnclose hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
489 txnclose hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
490 $ hg id
490 $ hg id
491 e7ec4e813ba6 tip
491 e7ec4e813ba6 tip
492 $ cd ..
492 $ cd ..
493
493
494 $ cat << EOF >> $HGRCPATH
494 $ cat << EOF >> $HGRCPATH
495 > [extensions]
495 > [extensions]
496 > failpush=$TESTTMP/failpush.py
496 > failpush=$TESTTMP/failpush.py
497 > EOF
497 > EOF
498
498
499 $ killdaemons.py
499 $ killdaemons.py
500 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
500 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
501 $ cat other.pid >> $DAEMON_PIDS
501 $ cat other.pid >> $DAEMON_PIDS
502
502
503 Doing the actual push: Abort error
503 Doing the actual push: Abort error
504
504
505 $ cat << EOF >> $HGRCPATH
505 $ cat << EOF >> $HGRCPATH
506 > [failpush]
506 > [failpush]
507 > reason = abort
507 > reason = abort
508 > EOF
508 > EOF
509
509
510 $ hg -R main push other -r e7ec4e813ba6
510 $ hg -R main push other -r e7ec4e813ba6
511 pushing to other
511 pushing to other
512 searching for changes
512 searching for changes
513 abort: Abandon ship!
513 abort: Abandon ship!
514 (don't panic)
514 (don't panic)
515 [255]
515 [255]
516
516
517 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
517 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
518 pushing to ssh://user@dummy/other
518 pushing to ssh://user@dummy/other
519 searching for changes
519 searching for changes
520 remote: Abandon ship!
520 remote: Abandon ship!
521 remote: (don't panic)
521 remote: (don't panic)
522 abort: push failed on remote
522 abort: push failed on remote
523 [255]
523 [255]
524
524
525 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
525 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
526 pushing to http://localhost:$HGPORT2/
526 pushing to http://localhost:$HGPORT2/
527 searching for changes
527 searching for changes
528 remote: Abandon ship!
528 remote: Abandon ship!
529 remote: (don't panic)
529 remote: (don't panic)
530 abort: push failed on remote
530 abort: push failed on remote
531 [255]
531 [255]
532
532
533
533
534 Doing the actual push: unknown mandatory parts
534 Doing the actual push: unknown mandatory parts
535
535
536 $ cat << EOF >> $HGRCPATH
536 $ cat << EOF >> $HGRCPATH
537 > [failpush]
537 > [failpush]
538 > reason = unknown
538 > reason = unknown
539 > EOF
539 > EOF
540
540
541 $ hg -R main push other -r e7ec4e813ba6
541 $ hg -R main push other -r e7ec4e813ba6
542 pushing to other
542 pushing to other
543 searching for changes
543 searching for changes
544 abort: missing support for test:unknown
544 abort: missing support for test:unknown
545 [255]
545 [255]
546
546
547 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
547 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
548 pushing to ssh://user@dummy/other
548 pushing to ssh://user@dummy/other
549 searching for changes
549 searching for changes
550 abort: missing support for test:unknown
550 abort: missing support for test:unknown
551 [255]
551 [255]
552
552
553 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
553 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
554 pushing to http://localhost:$HGPORT2/
554 pushing to http://localhost:$HGPORT2/
555 searching for changes
555 searching for changes
556 abort: missing support for test:unknown
556 abort: missing support for test:unknown
557 [255]
557 [255]
558
558
559 Doing the actual push: race
559 Doing the actual push: race
560
560
561 $ cat << EOF >> $HGRCPATH
561 $ cat << EOF >> $HGRCPATH
562 > [failpush]
562 > [failpush]
563 > reason = race
563 > reason = race
564 > EOF
564 > EOF
565
565
566 $ hg -R main push other -r e7ec4e813ba6
566 $ hg -R main push other -r e7ec4e813ba6
567 pushing to other
567 pushing to other
568 searching for changes
568 searching for changes
569 abort: push failed:
569 abort: push failed:
570 'repository changed while pushing - please try again'
570 'repository changed while pushing - please try again'
571 [255]
571 [255]
572
572
573 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
573 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
574 pushing to ssh://user@dummy/other
574 pushing to ssh://user@dummy/other
575 searching for changes
575 searching for changes
576 abort: push failed:
576 abort: push failed:
577 'repository changed while pushing - please try again'
577 'repository changed while pushing - please try again'
578 [255]
578 [255]
579
579
580 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
580 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
581 pushing to http://localhost:$HGPORT2/
581 pushing to http://localhost:$HGPORT2/
582 searching for changes
582 searching for changes
583 abort: push failed:
583 abort: push failed:
584 'repository changed while pushing - please try again'
584 'repository changed while pushing - please try again'
585 [255]
585 [255]
586
586
587 Doing the actual push: hook abort
587 Doing the actual push: hook abort
588
588
589 $ cat << EOF >> $HGRCPATH
589 $ cat << EOF >> $HGRCPATH
590 > [failpush]
590 > [failpush]
591 > reason =
591 > reason =
592 > [hooks]
592 > [hooks]
593 > pretxnclose.failpush = sh -c "echo 'You shall not pass!'; false"
593 > pretxnclose.failpush = sh -c "echo 'You shall not pass!'; false"
594 > txnabort.failpush = sh -c "echo 'Cleaning up the mess...'"
594 > txnabort.failpush = sh -c "echo 'Cleaning up the mess...'"
595 > EOF
595 > EOF
596
596
597 $ killdaemons.py
597 $ killdaemons.py
598 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
598 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
599 $ cat other.pid >> $DAEMON_PIDS
599 $ cat other.pid >> $DAEMON_PIDS
600
600
601 $ hg -R main push other -r e7ec4e813ba6
601 $ hg -R main push other -r e7ec4e813ba6
602 pushing to other
602 pushing to other
603 searching for changes
603 searching for changes
604 remote: adding changesets
604 remote: adding changesets
605 remote: adding manifests
605 remote: adding manifests
606 remote: adding file changes
606 remote: adding file changes
607 remote: added 1 changesets with 1 changes to 1 files
607 remote: added 1 changesets with 1 changes to 1 files
608 remote: pre-close-tip:e7ec4e813ba6 draft
608 remote: pre-close-tip:e7ec4e813ba6 draft
609 remote: You shall not pass!
609 remote: You shall not pass!
610 remote: transaction abort!
610 remote: transaction abort!
611 remote: Cleaning up the mess...
611 remote: Cleaning up the mess...
612 remote: rollback completed
612 remote: rollback completed
613 abort: pretxnclose.failpush hook exited with status 1
613 abort: pretxnclose.failpush hook exited with status 1
614 [255]
614 [255]
615
615
616 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
616 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
617 pushing to ssh://user@dummy/other
617 pushing to ssh://user@dummy/other
618 searching for changes
618 searching for changes
619 remote: adding changesets
619 remote: adding changesets
620 remote: adding manifests
620 remote: adding manifests
621 remote: adding file changes
621 remote: adding file changes
622 remote: added 1 changesets with 1 changes to 1 files
622 remote: added 1 changesets with 1 changes to 1 files
623 remote: pre-close-tip:e7ec4e813ba6 draft
623 remote: pre-close-tip:e7ec4e813ba6 draft
624 remote: You shall not pass!
624 remote: You shall not pass!
625 remote: transaction abort!
625 remote: transaction abort!
626 remote: Cleaning up the mess...
626 remote: Cleaning up the mess...
627 remote: rollback completed
627 remote: rollback completed
628 remote: pretxnclose.failpush hook exited with status 1
628 remote: pretxnclose.failpush hook exited with status 1
629 abort: push failed on remote
629 abort: push failed on remote
630 [255]
630 [255]
631
631
632 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
632 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
633 pushing to http://localhost:$HGPORT2/
633 pushing to http://localhost:$HGPORT2/
634 searching for changes
634 searching for changes
635 remote: adding changesets
635 remote: adding changesets
636 remote: adding manifests
636 remote: adding manifests
637 remote: adding file changes
637 remote: adding file changes
638 remote: added 1 changesets with 1 changes to 1 files
638 remote: added 1 changesets with 1 changes to 1 files
639 remote: pre-close-tip:e7ec4e813ba6 draft
639 remote: pre-close-tip:e7ec4e813ba6 draft
640 remote: You shall not pass!
640 remote: You shall not pass!
641 remote: transaction abort!
641 remote: transaction abort!
642 remote: Cleaning up the mess...
642 remote: Cleaning up the mess...
643 remote: rollback completed
643 remote: rollback completed
644 remote: pretxnclose.failpush hook exited with status 1
644 remote: pretxnclose.failpush hook exited with status 1
645 abort: push failed on remote
645 abort: push failed on remote
646 [255]
646 [255]
647
647
648 (check that no 'pending' files remain)
648 (check that no 'pending' files remain)
649
649
650 $ ls -1 other/.hg/bookmarks*
650 $ ls -1 other/.hg/bookmarks*
651 other/.hg/bookmarks
651 other/.hg/bookmarks
652 $ ls -1 other/.hg/store/phaseroots*
652 $ ls -1 other/.hg/store/phaseroots*
653 other/.hg/store/phaseroots
653 other/.hg/store/phaseroots
654 $ ls -1 other/.hg/store/00changelog.i*
654 $ ls -1 other/.hg/store/00changelog.i*
655 other/.hg/store/00changelog.i
655 other/.hg/store/00changelog.i
656
656
657 Check error from hook during the unbundling process itself
657 Check error from hook during the unbundling process itself
658
658
659 $ cat << EOF >> $HGRCPATH
659 $ cat << EOF >> $HGRCPATH
660 > pretxnchangegroup = sh -c "echo 'Fail early!'; false"
660 > pretxnchangegroup = sh -c "echo 'Fail early!'; false"
661 > EOF
661 > EOF
662 $ killdaemons.py # reload http config
662 $ killdaemons.py # reload http config
663 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
663 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
664 $ cat other.pid >> $DAEMON_PIDS
664 $ cat other.pid >> $DAEMON_PIDS
665
665
666 $ hg -R main push other -r e7ec4e813ba6
666 $ hg -R main push other -r e7ec4e813ba6
667 pushing to other
667 pushing to other
668 searching for changes
668 searching for changes
669 remote: adding changesets
669 remote: adding changesets
670 remote: adding manifests
670 remote: adding manifests
671 remote: adding file changes
671 remote: adding file changes
672 remote: added 1 changesets with 1 changes to 1 files
672 remote: added 1 changesets with 1 changes to 1 files
673 remote: Fail early!
673 remote: Fail early!
674 remote: transaction abort!
674 remote: transaction abort!
675 remote: Cleaning up the mess...
675 remote: Cleaning up the mess...
676 remote: rollback completed
676 remote: rollback completed
677 abort: pretxnchangegroup hook exited with status 1
677 abort: pretxnchangegroup hook exited with status 1
678 [255]
678 [255]
679 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
679 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
680 pushing to ssh://user@dummy/other
680 pushing to ssh://user@dummy/other
681 searching for changes
681 searching for changes
682 remote: adding changesets
682 remote: adding changesets
683 remote: adding manifests
683 remote: adding manifests
684 remote: adding file changes
684 remote: adding file changes
685 remote: added 1 changesets with 1 changes to 1 files
685 remote: added 1 changesets with 1 changes to 1 files
686 remote: Fail early!
686 remote: Fail early!
687 remote: transaction abort!
687 remote: transaction abort!
688 remote: Cleaning up the mess...
688 remote: Cleaning up the mess...
689 remote: rollback completed
689 remote: rollback completed
690 remote: pretxnchangegroup hook exited with status 1
690 remote: pretxnchangegroup hook exited with status 1
691 abort: push failed on remote
691 abort: push failed on remote
692 [255]
692 [255]
693 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
693 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
694 pushing to http://localhost:$HGPORT2/
694 pushing to http://localhost:$HGPORT2/
695 searching for changes
695 searching for changes
696 remote: adding changesets
696 remote: adding changesets
697 remote: adding manifests
697 remote: adding manifests
698 remote: adding file changes
698 remote: adding file changes
699 remote: added 1 changesets with 1 changes to 1 files
699 remote: added 1 changesets with 1 changes to 1 files
700 remote: Fail early!
700 remote: Fail early!
701 remote: transaction abort!
701 remote: transaction abort!
702 remote: Cleaning up the mess...
702 remote: Cleaning up the mess...
703 remote: rollback completed
703 remote: rollback completed
704 remote: pretxnchangegroup hook exited with status 1
704 remote: pretxnchangegroup hook exited with status 1
705 abort: push failed on remote
705 abort: push failed on remote
706 [255]
706 [255]
707
707
708 Check output capture control.
708 Check output capture control.
709
709
710 (should be still forced for http, disabled for local and ssh)
710 (should be still forced for http, disabled for local and ssh)
711
711
712 $ cat >> $HGRCPATH << EOF
712 $ cat >> $HGRCPATH << EOF
713 > [experimental]
713 > [experimental]
714 > bundle2-output-capture=False
714 > bundle2-output-capture=False
715 > EOF
715 > EOF
716
716
717 $ hg -R main push other -r e7ec4e813ba6
717 $ hg -R main push other -r e7ec4e813ba6
718 pushing to other
718 pushing to other
719 searching for changes
719 searching for changes
720 adding changesets
720 adding changesets
721 adding manifests
721 adding manifests
722 adding file changes
722 adding file changes
723 added 1 changesets with 1 changes to 1 files
723 added 1 changesets with 1 changes to 1 files
724 Fail early!
724 Fail early!
725 transaction abort!
725 transaction abort!
726 Cleaning up the mess...
726 Cleaning up the mess...
727 rollback completed
727 rollback completed
728 abort: pretxnchangegroup hook exited with status 1
728 abort: pretxnchangegroup hook exited with status 1
729 [255]
729 [255]
730 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
730 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
731 pushing to ssh://user@dummy/other
731 pushing to ssh://user@dummy/other
732 searching for changes
732 searching for changes
733 remote: adding changesets
733 remote: adding changesets
734 remote: adding manifests
734 remote: adding manifests
735 remote: adding file changes
735 remote: adding file changes
736 remote: added 1 changesets with 1 changes to 1 files
736 remote: added 1 changesets with 1 changes to 1 files
737 remote: Fail early!
737 remote: Fail early!
738 remote: transaction abort!
738 remote: transaction abort!
739 remote: Cleaning up the mess...
739 remote: Cleaning up the mess...
740 remote: rollback completed
740 remote: rollback completed
741 remote: pretxnchangegroup hook exited with status 1
741 remote: pretxnchangegroup hook exited with status 1
742 abort: push failed on remote
742 abort: push failed on remote
743 [255]
743 [255]
744 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
744 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
745 pushing to http://localhost:$HGPORT2/
745 pushing to http://localhost:$HGPORT2/
746 searching for changes
746 searching for changes
747 remote: adding changesets
747 remote: adding changesets
748 remote: adding manifests
748 remote: adding manifests
749 remote: adding file changes
749 remote: adding file changes
750 remote: added 1 changesets with 1 changes to 1 files
750 remote: added 1 changesets with 1 changes to 1 files
751 remote: Fail early!
751 remote: Fail early!
752 remote: transaction abort!
752 remote: transaction abort!
753 remote: Cleaning up the mess...
753 remote: Cleaning up the mess...
754 remote: rollback completed
754 remote: rollback completed
755 remote: pretxnchangegroup hook exited with status 1
755 remote: pretxnchangegroup hook exited with status 1
756 abort: push failed on remote
756 abort: push failed on remote
757 [255]
757 [255]
758
758
759 Check abort from mandatory pushkey
759 Check abort from mandatory pushkey
760
760
761 $ cat > mandatorypart.py << EOF
761 $ cat > mandatorypart.py << EOF
762 > from mercurial import exchange
762 > from mercurial import exchange
763 > from mercurial import pushkey
763 > from mercurial import pushkey
764 > from mercurial import node
764 > from mercurial import node
765 > from mercurial import error
765 > from mercurial import error
766 > @exchange.b2partsgenerator('failingpuskey')
766 > @exchange.b2partsgenerator('failingpuskey')
767 > def addfailingpushey(pushop, bundler):
767 > def addfailingpushey(pushop, bundler):
768 > enc = pushkey.encode
768 > enc = pushkey.encode
769 > part = bundler.newpart('pushkey')
769 > part = bundler.newpart('pushkey')
770 > part.addparam('namespace', enc('phases'))
770 > part.addparam('namespace', enc('phases'))
771 > part.addparam('key', enc(pushop.repo['cd010b8cd998'].hex()))
771 > part.addparam('key', enc(pushop.repo['cd010b8cd998'].hex()))
772 > part.addparam('old', enc(str(0))) # successful update
772 > part.addparam('old', enc(str(0))) # successful update
773 > part.addparam('new', enc(str(0)))
773 > part.addparam('new', enc(str(0)))
774 > def fail(pushop, exc):
774 > def fail(pushop, exc):
775 > raise error.Abort('Correct phase push failed (because hooks)')
775 > raise error.Abort('Correct phase push failed (because hooks)')
776 > pushop.pkfailcb[part.id] = fail
776 > pushop.pkfailcb[part.id] = fail
777 > EOF
777 > EOF
778 $ cat >> $HGRCPATH << EOF
778 $ cat >> $HGRCPATH << EOF
779 > [hooks]
779 > [hooks]
780 > pretxnchangegroup=
780 > pretxnchangegroup=
781 > pretxnclose.failpush=
781 > pretxnclose.failpush=
782 > prepushkey.failpush = sh -c "echo 'do not push the key !'; false"
782 > prepushkey.failpush = sh -c "echo 'do not push the key !'; false"
783 > [extensions]
783 > [extensions]
784 > mandatorypart=$TESTTMP/mandatorypart.py
784 > mandatorypart=$TESTTMP/mandatorypart.py
785 > EOF
785 > EOF
786 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS # reload http config
786 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS # reload http config
787 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
787 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
788 $ cat other.pid >> $DAEMON_PIDS
788 $ cat other.pid >> $DAEMON_PIDS
789
789
790 (Failure from a hook)
790 (Failure from a hook)
791
791
792 $ hg -R main push other -r e7ec4e813ba6
792 $ hg -R main push other -r e7ec4e813ba6
793 pushing to other
793 pushing to other
794 searching for changes
794 searching for changes
795 adding changesets
795 adding changesets
796 adding manifests
796 adding manifests
797 adding file changes
797 adding file changes
798 added 1 changesets with 1 changes to 1 files
798 added 1 changesets with 1 changes to 1 files
799 do not push the key !
799 do not push the key !
800 pushkey-abort: prepushkey.failpush hook exited with status 1
800 pushkey-abort: prepushkey.failpush hook exited with status 1
801 transaction abort!
801 transaction abort!
802 Cleaning up the mess...
802 Cleaning up the mess...
803 rollback completed
803 rollback completed
804 abort: Correct phase push failed (because hooks)
804 abort: Correct phase push failed (because hooks)
805 [255]
805 [255]
806 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
806 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
807 pushing to ssh://user@dummy/other
807 pushing to ssh://user@dummy/other
808 searching for changes
808 searching for changes
809 remote: adding changesets
809 remote: adding changesets
810 remote: adding manifests
810 remote: adding manifests
811 remote: adding file changes
811 remote: adding file changes
812 remote: added 1 changesets with 1 changes to 1 files
812 remote: added 1 changesets with 1 changes to 1 files
813 remote: do not push the key !
813 remote: do not push the key !
814 remote: pushkey-abort: prepushkey.failpush hook exited with status 1
814 remote: pushkey-abort: prepushkey.failpush hook exited with status 1
815 remote: transaction abort!
815 remote: transaction abort!
816 remote: Cleaning up the mess...
816 remote: Cleaning up the mess...
817 remote: rollback completed
817 remote: rollback completed
818 abort: Correct phase push failed (because hooks)
818 abort: Correct phase push failed (because hooks)
819 [255]
819 [255]
820 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
820 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
821 pushing to http://localhost:$HGPORT2/
821 pushing to http://localhost:$HGPORT2/
822 searching for changes
822 searching for changes
823 remote: adding changesets
823 remote: adding changesets
824 remote: adding manifests
824 remote: adding manifests
825 remote: adding file changes
825 remote: adding file changes
826 remote: added 1 changesets with 1 changes to 1 files
826 remote: added 1 changesets with 1 changes to 1 files
827 remote: do not push the key !
827 remote: do not push the key !
828 remote: pushkey-abort: prepushkey.failpush hook exited with status 1
828 remote: pushkey-abort: prepushkey.failpush hook exited with status 1
829 remote: transaction abort!
829 remote: transaction abort!
830 remote: Cleaning up the mess...
830 remote: Cleaning up the mess...
831 remote: rollback completed
831 remote: rollback completed
832 abort: Correct phase push failed (because hooks)
832 abort: Correct phase push failed (because hooks)
833 [255]
833 [255]
834
834
835 (Failure from a the pushkey)
835 (Failure from a the pushkey)
836
836
837 $ cat > mandatorypart.py << EOF
837 $ cat > mandatorypart.py << EOF
838 > from mercurial import exchange
838 > from mercurial import exchange
839 > from mercurial import pushkey
839 > from mercurial import pushkey
840 > from mercurial import node
840 > from mercurial import node
841 > from mercurial import error
841 > from mercurial import error
842 > @exchange.b2partsgenerator('failingpuskey')
842 > @exchange.b2partsgenerator('failingpuskey')
843 > def addfailingpushey(pushop, bundler):
843 > def addfailingpushey(pushop, bundler):
844 > enc = pushkey.encode
844 > enc = pushkey.encode
845 > part = bundler.newpart('pushkey')
845 > part = bundler.newpart('pushkey')
846 > part.addparam('namespace', enc('phases'))
846 > part.addparam('namespace', enc('phases'))
847 > part.addparam('key', enc(pushop.repo['cd010b8cd998'].hex()))
847 > part.addparam('key', enc(pushop.repo['cd010b8cd998'].hex()))
848 > part.addparam('old', enc(str(4))) # will fail
848 > part.addparam('old', enc(str(4))) # will fail
849 > part.addparam('new', enc(str(3)))
849 > part.addparam('new', enc(str(3)))
850 > def fail(pushop, exc):
850 > def fail(pushop, exc):
851 > raise error.Abort('Clown phase push failed')
851 > raise error.Abort('Clown phase push failed')
852 > pushop.pkfailcb[part.id] = fail
852 > pushop.pkfailcb[part.id] = fail
853 > EOF
853 > EOF
854 $ cat >> $HGRCPATH << EOF
854 $ cat >> $HGRCPATH << EOF
855 > [hooks]
855 > [hooks]
856 > prepushkey.failpush =
856 > prepushkey.failpush =
857 > EOF
857 > EOF
858 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS # reload http config
858 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS # reload http config
859 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
859 $ hg serve -R other -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
860 $ cat other.pid >> $DAEMON_PIDS
860 $ cat other.pid >> $DAEMON_PIDS
861
861
862 $ hg -R main push other -r e7ec4e813ba6
862 $ hg -R main push other -r e7ec4e813ba6
863 pushing to other
863 pushing to other
864 searching for changes
864 searching for changes
865 adding changesets
865 adding changesets
866 adding manifests
866 adding manifests
867 adding file changes
867 adding file changes
868 added 1 changesets with 1 changes to 1 files
868 added 1 changesets with 1 changes to 1 files
869 transaction abort!
869 transaction abort!
870 Cleaning up the mess...
870 Cleaning up the mess...
871 rollback completed
871 rollback completed
872 pushkey: lock state after "phases"
872 pushkey: lock state after "phases"
873 lock: free
873 lock: free
874 wlock: free
874 wlock: free
875 abort: Clown phase push failed
875 abort: Clown phase push failed
876 [255]
876 [255]
877 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
877 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
878 pushing to ssh://user@dummy/other
878 pushing to ssh://user@dummy/other
879 searching for changes
879 searching for changes
880 remote: adding changesets
880 remote: adding changesets
881 remote: adding manifests
881 remote: adding manifests
882 remote: adding file changes
882 remote: adding file changes
883 remote: added 1 changesets with 1 changes to 1 files
883 remote: added 1 changesets with 1 changes to 1 files
884 remote: transaction abort!
884 remote: transaction abort!
885 remote: Cleaning up the mess...
885 remote: Cleaning up the mess...
886 remote: rollback completed
886 remote: rollback completed
887 remote: pushkey: lock state after "phases"
887 remote: pushkey: lock state after "phases"
888 remote: lock: free
888 remote: lock: free
889 remote: wlock: free
889 remote: wlock: free
890 abort: Clown phase push failed
890 abort: Clown phase push failed
891 [255]
891 [255]
892 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
892 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
893 pushing to http://localhost:$HGPORT2/
893 pushing to http://localhost:$HGPORT2/
894 searching for changes
894 searching for changes
895 remote: adding changesets
895 remote: adding changesets
896 remote: adding manifests
896 remote: adding manifests
897 remote: adding file changes
897 remote: adding file changes
898 remote: added 1 changesets with 1 changes to 1 files
898 remote: added 1 changesets with 1 changes to 1 files
899 remote: transaction abort!
899 remote: transaction abort!
900 remote: Cleaning up the mess...
900 remote: Cleaning up the mess...
901 remote: rollback completed
901 remote: rollback completed
902 remote: pushkey: lock state after "phases"
902 remote: pushkey: lock state after "phases"
903 remote: lock: free
903 remote: lock: free
904 remote: wlock: free
904 remote: wlock: free
905 abort: Clown phase push failed
905 abort: Clown phase push failed
906 [255]
906 [255]
907
907
908 Test lazily acquiring the lock during unbundle
908 Test lazily acquiring the lock during unbundle
909 $ cp $TESTTMP/hgrc.orig $HGRCPATH
909 $ cp $TESTTMP/hgrc.orig $HGRCPATH
910 $ cat >> $HGRCPATH <<EOF
910 $ cat >> $HGRCPATH <<EOF
911 > [ui]
911 > [ui]
912 > ssh=python "$TESTDIR/dummyssh"
912 > ssh=python "$TESTDIR/dummyssh"
913 > EOF
913 > EOF
914
914
915 $ cat >> $TESTTMP/locktester.py <<EOF
915 $ cat >> $TESTTMP/locktester.py <<EOF
916 > import os
916 > import os
917 > from mercurial import extensions, bundle2, util
917 > from mercurial import extensions, bundle2, util
918 > def checklock(orig, repo, *args, **kwargs):
918 > def checklock(orig, repo, *args, **kwargs):
919 > if repo.svfs.lexists("lock"):
919 > if repo.svfs.lexists("lock"):
920 > raise util.Abort("Lock should not be taken")
920 > raise util.Abort("Lock should not be taken")
921 > return orig(repo, *args, **kwargs)
921 > return orig(repo, *args, **kwargs)
922 > def extsetup(ui):
922 > def extsetup(ui):
923 > extensions.wrapfunction(bundle2, 'processbundle', checklock)
923 > extensions.wrapfunction(bundle2, 'processbundle', checklock)
924 > EOF
924 > EOF
925
925
926 $ hg init lazylock
926 $ hg init lazylock
927 $ cat >> lazylock/.hg/hgrc <<EOF
927 $ cat >> lazylock/.hg/hgrc <<EOF
928 > [extensions]
928 > [extensions]
929 > locktester=$TESTTMP/locktester.py
929 > locktester=$TESTTMP/locktester.py
930 > EOF
930 > EOF
931
931
932 $ hg clone -q ssh://user@dummy/lazylock lazylockclient
932 $ hg clone -q ssh://user@dummy/lazylock lazylockclient
933 $ cd lazylockclient
933 $ cd lazylockclient
934 $ touch a && hg ci -Aqm a
934 $ touch a && hg ci -Aqm a
935 $ hg push
935 $ hg push
936 pushing to ssh://user@dummy/lazylock
936 pushing to ssh://user@dummy/lazylock
937 searching for changes
937 searching for changes
938 remote: Lock should not be taken
938 remote: Lock should not be taken
939 abort: push failed on remote
939 abort: push failed on remote
940 [255]
940 [255]
941
941
942 $ cat >> ../lazylock/.hg/hgrc <<EOF
942 $ cat >> ../lazylock/.hg/hgrc <<EOF
943 > [experimental]
943 > [experimental]
944 > bundle2lazylocking=True
944 > bundle2lazylocking=True
945 > EOF
945 > EOF
946 $ hg push
946 $ hg push
947 pushing to ssh://user@dummy/lazylock
947 pushing to ssh://user@dummy/lazylock
948 searching for changes
948 searching for changes
949 remote: adding changesets
949 remote: adding changesets
950 remote: adding manifests
950 remote: adding manifests
951 remote: adding file changes
951 remote: adding file changes
952 remote: added 1 changesets with 1 changes to 1 files
952 remote: added 1 changesets with 1 changes to 1 files
953
953
954 $ cd ..
954 $ cd ..
955
955
956 Servers can disable bundle1 for clone/pull operations
956 Servers can disable bundle1 for clone/pull operations
957
957
958 $ killdaemons.py
958 $ killdaemons.py
959 $ hg init bundle2onlyserver
959 $ hg init bundle2onlyserver
960 $ cd bundle2onlyserver
960 $ cd bundle2onlyserver
961 $ cat > .hg/hgrc << EOF
961 $ cat > .hg/hgrc << EOF
962 > [server]
962 > [server]
963 > bundle1.pull = false
963 > bundle1.pull = false
964 > EOF
964 > EOF
965
965
966 $ touch foo
966 $ touch foo
967 $ hg -q commit -A -m initial
967 $ hg -q commit -A -m initial
968
968
969 $ hg serve -p $HGPORT -d --pid-file=hg.pid
969 $ hg serve -p $HGPORT -d --pid-file=hg.pid
970 $ cat hg.pid >> $DAEMON_PIDS
970 $ cat hg.pid >> $DAEMON_PIDS
971
971
972 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT/ not-bundle2
972 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT/ not-bundle2
973 requesting all changes
973 requesting all changes
974 abort: remote error:
974 abort: remote error:
975 incompatible Mercurial client; bundle2 required
975 incompatible Mercurial client; bundle2 required
976 (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
976 (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
977 [255]
977 [255]
978 $ killdaemons.py
978 $ killdaemons.py
979 $ cd ..
979 $ cd ..
980
980
981 bundle1 can still pull non-generaldelta repos when generaldelta bundle1 disabled
981 bundle1 can still pull non-generaldelta repos when generaldelta bundle1 disabled
982
982
983 $ hg --config format.usegeneraldelta=false init notgdserver
983 $ hg --config format.usegeneraldelta=false init notgdserver
984 $ cd notgdserver
984 $ cd notgdserver
985 $ cat > .hg/hgrc << EOF
985 $ cat > .hg/hgrc << EOF
986 > [server]
986 > [server]
987 > bundle1gd.pull = false
987 > bundle1gd.pull = false
988 > EOF
988 > EOF
989
989
990 $ touch foo
990 $ touch foo
991 $ hg -q commit -A -m initial
991 $ hg -q commit -A -m initial
992 $ hg serve -p $HGPORT -d --pid-file=hg.pid
992 $ hg serve -p $HGPORT -d --pid-file=hg.pid
993 $ cat hg.pid >> $DAEMON_PIDS
993 $ cat hg.pid >> $DAEMON_PIDS
994
994
995 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT/ not-bundle2-1
995 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT/ not-bundle2-1
996 requesting all changes
996 requesting all changes
997 adding changesets
997 adding changesets
998 adding manifests
998 adding manifests
999 adding file changes
999 adding file changes
1000 added 1 changesets with 1 changes to 1 files
1000 added 1 changesets with 1 changes to 1 files
1001 updating to branch default
1001 updating to branch default
1002 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1002 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1003
1003
1004 $ killdaemons.py
1004 $ killdaemons.py
1005 $ cd ../bundle2onlyserver
1005 $ cd ../bundle2onlyserver
1006
1006
1007 bundle1 pull can be disabled for generaldelta repos only
1007 bundle1 pull can be disabled for generaldelta repos only
1008
1008
1009 $ cat > .hg/hgrc << EOF
1009 $ cat > .hg/hgrc << EOF
1010 > [server]
1010 > [server]
1011 > bundle1gd.pull = false
1011 > bundle1gd.pull = false
1012 > EOF
1012 > EOF
1013
1013
1014 $ hg serve -p $HGPORT -d --pid-file=hg.pid
1014 $ hg serve -p $HGPORT -d --pid-file=hg.pid
1015 $ cat hg.pid >> $DAEMON_PIDS
1015 $ cat hg.pid >> $DAEMON_PIDS
1016 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT/ not-bundle2
1016 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT/ not-bundle2
1017 requesting all changes
1017 requesting all changes
1018 abort: remote error:
1018 abort: remote error:
1019 incompatible Mercurial client; bundle2 required
1019 incompatible Mercurial client; bundle2 required
1020 (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
1020 (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
1021 [255]
1021 [255]
1022
1022
1023 $ killdaemons.py
1023 $ killdaemons.py
1024
1024
1025 Verify the global server.bundle1 option works
1025 Verify the global server.bundle1 option works
1026
1026
1027 $ cat > .hg/hgrc << EOF
1027 $ cat > .hg/hgrc << EOF
1028 > [server]
1028 > [server]
1029 > bundle1 = false
1029 > bundle1 = false
1030 > EOF
1030 > EOF
1031 $ hg serve -p $HGPORT -d --pid-file=hg.pid
1031 $ hg serve -p $HGPORT -d --pid-file=hg.pid
1032 $ cat hg.pid >> $DAEMON_PIDS
1032 $ cat hg.pid >> $DAEMON_PIDS
1033 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT not-bundle2
1033 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT not-bundle2
1034 requesting all changes
1034 requesting all changes
1035 abort: remote error:
1035 abort: remote error:
1036 incompatible Mercurial client; bundle2 required
1036 incompatible Mercurial client; bundle2 required
1037 (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
1037 (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
1038 [255]
1038 [255]
1039 $ killdaemons.py
1039 $ killdaemons.py
1040
1040
1041 $ cat > .hg/hgrc << EOF
1041 $ cat > .hg/hgrc << EOF
1042 > [server]
1042 > [server]
1043 > bundle1gd = false
1043 > bundle1gd = false
1044 > EOF
1044 > EOF
1045 $ hg serve -p $HGPORT -d --pid-file=hg.pid
1045 $ hg serve -p $HGPORT -d --pid-file=hg.pid
1046 $ cat hg.pid >> $DAEMON_PIDS
1046 $ cat hg.pid >> $DAEMON_PIDS
1047
1047
1048 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT/ not-bundle2
1048 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT/ not-bundle2
1049 requesting all changes
1049 requesting all changes
1050 abort: remote error:
1050 abort: remote error:
1051 incompatible Mercurial client; bundle2 required
1051 incompatible Mercurial client; bundle2 required
1052 (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
1052 (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
1053 [255]
1053 [255]
1054
1054
1055 $ killdaemons.py
1055 $ killdaemons.py
1056
1056
1057 $ cd ../notgdserver
1057 $ cd ../notgdserver
1058 $ cat > .hg/hgrc << EOF
1058 $ cat > .hg/hgrc << EOF
1059 > [server]
1059 > [server]
1060 > bundle1gd = false
1060 > bundle1gd = false
1061 > EOF
1061 > EOF
1062 $ hg serve -p $HGPORT -d --pid-file=hg.pid
1062 $ hg serve -p $HGPORT -d --pid-file=hg.pid
1063 $ cat hg.pid >> $DAEMON_PIDS
1063 $ cat hg.pid >> $DAEMON_PIDS
1064
1064
1065 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT/ not-bundle2-2
1065 $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT/ not-bundle2-2
1066 requesting all changes
1066 requesting all changes
1067 adding changesets
1067 adding changesets
1068 adding manifests
1068 adding manifests
1069 adding file changes
1069 adding file changes
1070 added 1 changesets with 1 changes to 1 files
1070 added 1 changesets with 1 changes to 1 files
1071 updating to branch default
1071 updating to branch default
1072 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1072 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1073
1073
1074 $ killdaemons.py
1074 $ killdaemons.py
1075 $ cd ../bundle2onlyserver
1075 $ cd ../bundle2onlyserver
1076
1076
1077 Verify bundle1 pushes can be disabled
1077 Verify bundle1 pushes can be disabled
1078
1078
1079 $ cat > .hg/hgrc << EOF
1079 $ cat > .hg/hgrc << EOF
1080 > [server]
1080 > [server]
1081 > bundle1.push = false
1081 > bundle1.push = false
1082 > [web]
1082 > [web]
1083 > allow_push = *
1083 > allow_push = *
1084 > push_ssl = false
1084 > push_ssl = false
1085 > EOF
1085 > EOF
1086
1086
1087 $ hg serve -p $HGPORT -d --pid-file=hg.pid -E error.log
1087 $ hg serve -p $HGPORT -d --pid-file=hg.pid -E error.log
1088 $ cat hg.pid >> $DAEMON_PIDS
1088 $ cat hg.pid >> $DAEMON_PIDS
1089 $ cd ..
1089 $ cd ..
1090
1090
1091 $ hg clone http://localhost:$HGPORT bundle2-only
1091 $ hg clone http://localhost:$HGPORT bundle2-only
1092 requesting all changes
1092 requesting all changes
1093 adding changesets
1093 adding changesets
1094 adding manifests
1094 adding manifests
1095 adding file changes
1095 adding file changes
1096 added 1 changesets with 1 changes to 1 files
1096 added 1 changesets with 1 changes to 1 files
1097 updating to branch default
1097 updating to branch default
1098 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1098 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1099 $ cd bundle2-only
1099 $ cd bundle2-only
1100 $ echo commit > foo
1100 $ echo commit > foo
1101 $ hg commit -m commit
1101 $ hg commit -m commit
1102 $ hg --config devel.legacy.exchange=bundle1 push
1102 $ hg --config devel.legacy.exchange=bundle1 push
1103 pushing to http://localhost:$HGPORT/
1103 pushing to http://localhost:$HGPORT/
1104 searching for changes
1104 searching for changes
1105 abort: remote error:
1105 abort: remote error:
1106 incompatible Mercurial client; bundle2 required
1106 incompatible Mercurial client; bundle2 required
1107 (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
1107 (see https://www.mercurial-scm.org/wiki/IncompatibleClient)
1108 [255]
1108 [255]
1109
1109
1110 (also check with ssh)
1111
1112 $ hg --config devel.legacy.exchange=bundle1 push ssh://user@dummy/bundle2onlyserver
1113 pushing to ssh://user@dummy/bundle2onlyserver
1114 searching for changes
1115 remote: abort: incompatible Mercurial client; bundle2 required
1116 [1]
1117
1110 $ hg push
1118 $ hg push
1111 pushing to http://localhost:$HGPORT/
1119 pushing to http://localhost:$HGPORT/
1112 searching for changes
1120 searching for changes
1113 remote: adding changesets
1121 remote: adding changesets
1114 remote: adding manifests
1122 remote: adding manifests
1115 remote: adding file changes
1123 remote: adding file changes
1116 remote: added 1 changesets with 1 changes to 1 files
1124 remote: added 1 changesets with 1 changes to 1 files
General Comments 0
You need to be logged in to leave comments. Login now