##// END OF EJS Templates
bundle2-wireproto: properly propagate the server output on error (issue4594)...
Pierre-Yves David -
r24797:0c4d5e01 default
parent child Browse files
Show More
@@ -1,878 +1,880 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 import urllib, tempfile, os, sys
8 import urllib, tempfile, os, sys
9 from i18n import _
9 from i18n import _
10 from node import bin, hex
10 from node import bin, hex
11 import changegroup as changegroupmod, bundle2, pushkey as pushkeymod
11 import changegroup as changegroupmod, bundle2, pushkey as pushkeymod
12 import peer, error, encoding, util, store, exchange
12 import peer, error, encoding, util, store, exchange
13
13
14
14
15 class abstractserverproto(object):
15 class abstractserverproto(object):
16 """abstract class that summarizes the protocol API
16 """abstract class that summarizes the protocol API
17
17
18 Used as reference and documentation.
18 Used as reference and documentation.
19 """
19 """
20
20
21 def getargs(self, args):
21 def getargs(self, args):
22 """return the value for arguments in <args>
22 """return the value for arguments in <args>
23
23
24 returns a list of values (same order as <args>)"""
24 returns a list of values (same order as <args>)"""
25 raise NotImplementedError()
25 raise NotImplementedError()
26
26
27 def getfile(self, fp):
27 def getfile(self, fp):
28 """write the whole content of a file into a file like object
28 """write the whole content of a file into a file like object
29
29
30 The file is in the form::
30 The file is in the form::
31
31
32 (<chunk-size>\n<chunk>)+0\n
32 (<chunk-size>\n<chunk>)+0\n
33
33
34 chunk size is the ascii version of the int.
34 chunk size is the ascii version of the int.
35 """
35 """
36 raise NotImplementedError()
36 raise NotImplementedError()
37
37
38 def redirect(self):
38 def redirect(self):
39 """may setup interception for stdout and stderr
39 """may setup interception for stdout and stderr
40
40
41 See also the `restore` method."""
41 See also the `restore` method."""
42 raise NotImplementedError()
42 raise NotImplementedError()
43
43
44 # If the `redirect` function does install interception, the `restore`
44 # If the `redirect` function does install interception, the `restore`
45 # function MUST be defined. If interception is not used, this function
45 # function MUST be defined. If interception is not used, this function
46 # MUST NOT be defined.
46 # MUST NOT be defined.
47 #
47 #
48 # left commented here on purpose
48 # left commented here on purpose
49 #
49 #
50 #def restore(self):
50 #def restore(self):
51 # """reinstall previous stdout and stderr and return intercepted stdout
51 # """reinstall previous stdout and stderr and return intercepted stdout
52 # """
52 # """
53 # raise NotImplementedError()
53 # raise NotImplementedError()
54
54
55 def groupchunks(self, cg):
55 def groupchunks(self, cg):
56 """return 4096 chunks from a changegroup object
56 """return 4096 chunks from a changegroup object
57
57
58 Some protocols may have compressed the contents."""
58 Some protocols may have compressed the contents."""
59 raise NotImplementedError()
59 raise NotImplementedError()
60
60
61 # abstract batching support
61 # abstract batching support
62
62
63 class future(object):
63 class future(object):
64 '''placeholder for a value to be set later'''
64 '''placeholder for a value to be set later'''
65 def set(self, value):
65 def set(self, value):
66 if util.safehasattr(self, 'value'):
66 if util.safehasattr(self, 'value'):
67 raise error.RepoError("future is already set")
67 raise error.RepoError("future is already set")
68 self.value = value
68 self.value = value
69
69
70 class batcher(object):
70 class batcher(object):
71 '''base class for batches of commands submittable in a single request
71 '''base class for batches of commands submittable in a single request
72
72
73 All methods invoked on instances of this class are simply queued and
73 All methods invoked on instances of this class are simply queued and
74 return a a future for the result. Once you call submit(), all the queued
74 return a a future for the result. Once you call submit(), all the queued
75 calls are performed and the results set in their respective futures.
75 calls are performed and the results set in their respective futures.
76 '''
76 '''
77 def __init__(self):
77 def __init__(self):
78 self.calls = []
78 self.calls = []
79 def __getattr__(self, name):
79 def __getattr__(self, name):
80 def call(*args, **opts):
80 def call(*args, **opts):
81 resref = future()
81 resref = future()
82 self.calls.append((name, args, opts, resref,))
82 self.calls.append((name, args, opts, resref,))
83 return resref
83 return resref
84 return call
84 return call
85 def submit(self):
85 def submit(self):
86 pass
86 pass
87
87
88 class localbatch(batcher):
88 class localbatch(batcher):
89 '''performs the queued calls directly'''
89 '''performs the queued calls directly'''
90 def __init__(self, local):
90 def __init__(self, local):
91 batcher.__init__(self)
91 batcher.__init__(self)
92 self.local = local
92 self.local = local
93 def submit(self):
93 def submit(self):
94 for name, args, opts, resref in self.calls:
94 for name, args, opts, resref in self.calls:
95 resref.set(getattr(self.local, name)(*args, **opts))
95 resref.set(getattr(self.local, name)(*args, **opts))
96
96
97 class remotebatch(batcher):
97 class remotebatch(batcher):
98 '''batches the queued calls; uses as few roundtrips as possible'''
98 '''batches the queued calls; uses as few roundtrips as possible'''
99 def __init__(self, remote):
99 def __init__(self, remote):
100 '''remote must support _submitbatch(encbatch) and
100 '''remote must support _submitbatch(encbatch) and
101 _submitone(op, encargs)'''
101 _submitone(op, encargs)'''
102 batcher.__init__(self)
102 batcher.__init__(self)
103 self.remote = remote
103 self.remote = remote
104 def submit(self):
104 def submit(self):
105 req, rsp = [], []
105 req, rsp = [], []
106 for name, args, opts, resref in self.calls:
106 for name, args, opts, resref in self.calls:
107 mtd = getattr(self.remote, name)
107 mtd = getattr(self.remote, name)
108 batchablefn = getattr(mtd, 'batchable', None)
108 batchablefn = getattr(mtd, 'batchable', None)
109 if batchablefn is not None:
109 if batchablefn is not None:
110 batchable = batchablefn(mtd.im_self, *args, **opts)
110 batchable = batchablefn(mtd.im_self, *args, **opts)
111 encargsorres, encresref = batchable.next()
111 encargsorres, encresref = batchable.next()
112 if encresref:
112 if encresref:
113 req.append((name, encargsorres,))
113 req.append((name, encargsorres,))
114 rsp.append((batchable, encresref, resref,))
114 rsp.append((batchable, encresref, resref,))
115 else:
115 else:
116 resref.set(encargsorres)
116 resref.set(encargsorres)
117 else:
117 else:
118 if req:
118 if req:
119 self._submitreq(req, rsp)
119 self._submitreq(req, rsp)
120 req, rsp = [], []
120 req, rsp = [], []
121 resref.set(mtd(*args, **opts))
121 resref.set(mtd(*args, **opts))
122 if req:
122 if req:
123 self._submitreq(req, rsp)
123 self._submitreq(req, rsp)
124 def _submitreq(self, req, rsp):
124 def _submitreq(self, req, rsp):
125 encresults = self.remote._submitbatch(req)
125 encresults = self.remote._submitbatch(req)
126 for encres, r in zip(encresults, rsp):
126 for encres, r in zip(encresults, rsp):
127 batchable, encresref, resref = r
127 batchable, encresref, resref = r
128 encresref.set(encres)
128 encresref.set(encres)
129 resref.set(batchable.next())
129 resref.set(batchable.next())
130
130
131 def batchable(f):
131 def batchable(f):
132 '''annotation for batchable methods
132 '''annotation for batchable methods
133
133
134 Such methods must implement a coroutine as follows:
134 Such methods must implement a coroutine as follows:
135
135
136 @batchable
136 @batchable
137 def sample(self, one, two=None):
137 def sample(self, one, two=None):
138 # Handle locally computable results first:
138 # Handle locally computable results first:
139 if not one:
139 if not one:
140 yield "a local result", None
140 yield "a local result", None
141 # Build list of encoded arguments suitable for your wire protocol:
141 # Build list of encoded arguments suitable for your wire protocol:
142 encargs = [('one', encode(one),), ('two', encode(two),)]
142 encargs = [('one', encode(one),), ('two', encode(two),)]
143 # Create future for injection of encoded result:
143 # Create future for injection of encoded result:
144 encresref = future()
144 encresref = future()
145 # Return encoded arguments and future:
145 # Return encoded arguments and future:
146 yield encargs, encresref
146 yield encargs, encresref
147 # Assuming the future to be filled with the result from the batched
147 # Assuming the future to be filled with the result from the batched
148 # request now. Decode it:
148 # request now. Decode it:
149 yield decode(encresref.value)
149 yield decode(encresref.value)
150
150
151 The decorator returns a function which wraps this coroutine as a plain
151 The decorator returns a function which wraps this coroutine as a plain
152 method, but adds the original method as an attribute called "batchable",
152 method, but adds the original method as an attribute called "batchable",
153 which is used by remotebatch to split the call into separate encoding and
153 which is used by remotebatch to split the call into separate encoding and
154 decoding phases.
154 decoding phases.
155 '''
155 '''
156 def plain(*args, **opts):
156 def plain(*args, **opts):
157 batchable = f(*args, **opts)
157 batchable = f(*args, **opts)
158 encargsorres, encresref = batchable.next()
158 encargsorres, encresref = batchable.next()
159 if not encresref:
159 if not encresref:
160 return encargsorres # a local result in this case
160 return encargsorres # a local result in this case
161 self = args[0]
161 self = args[0]
162 encresref.set(self._submitone(f.func_name, encargsorres))
162 encresref.set(self._submitone(f.func_name, encargsorres))
163 return batchable.next()
163 return batchable.next()
164 setattr(plain, 'batchable', f)
164 setattr(plain, 'batchable', f)
165 return plain
165 return plain
166
166
167 # list of nodes encoding / decoding
167 # list of nodes encoding / decoding
168
168
169 def decodelist(l, sep=' '):
169 def decodelist(l, sep=' '):
170 if l:
170 if l:
171 return map(bin, l.split(sep))
171 return map(bin, l.split(sep))
172 return []
172 return []
173
173
174 def encodelist(l, sep=' '):
174 def encodelist(l, sep=' '):
175 try:
175 try:
176 return sep.join(map(hex, l))
176 return sep.join(map(hex, l))
177 except TypeError:
177 except TypeError:
178 print l
178 print l
179 raise
179 raise
180
180
181 # batched call argument encoding
181 # batched call argument encoding
182
182
183 def escapearg(plain):
183 def escapearg(plain):
184 return (plain
184 return (plain
185 .replace(':', '::')
185 .replace(':', '::')
186 .replace(',', ':,')
186 .replace(',', ':,')
187 .replace(';', ':;')
187 .replace(';', ':;')
188 .replace('=', ':='))
188 .replace('=', ':='))
189
189
190 def unescapearg(escaped):
190 def unescapearg(escaped):
191 return (escaped
191 return (escaped
192 .replace(':=', '=')
192 .replace(':=', '=')
193 .replace(':;', ';')
193 .replace(':;', ';')
194 .replace(':,', ',')
194 .replace(':,', ',')
195 .replace('::', ':'))
195 .replace('::', ':'))
196
196
197 # mapping of options accepted by getbundle and their types
197 # mapping of options accepted by getbundle and their types
198 #
198 #
199 # Meant to be extended by extensions. It is extensions responsibility to ensure
199 # Meant to be extended by extensions. It is extensions responsibility to ensure
200 # such options are properly processed in exchange.getbundle.
200 # such options are properly processed in exchange.getbundle.
201 #
201 #
202 # supported types are:
202 # supported types are:
203 #
203 #
204 # :nodes: list of binary nodes
204 # :nodes: list of binary nodes
205 # :csv: list of comma-separated values
205 # :csv: list of comma-separated values
206 # :plain: string with no transformation needed.
206 # :plain: string with no transformation needed.
207 gboptsmap = {'heads': 'nodes',
207 gboptsmap = {'heads': 'nodes',
208 'common': 'nodes',
208 'common': 'nodes',
209 'obsmarkers': 'boolean',
209 'obsmarkers': 'boolean',
210 'bundlecaps': 'csv',
210 'bundlecaps': 'csv',
211 'listkeys': 'csv',
211 'listkeys': 'csv',
212 'cg': 'boolean'}
212 'cg': 'boolean'}
213
213
214 # client side
214 # client side
215
215
216 class wirepeer(peer.peerrepository):
216 class wirepeer(peer.peerrepository):
217
217
218 def batch(self):
218 def batch(self):
219 return remotebatch(self)
219 return remotebatch(self)
220 def _submitbatch(self, req):
220 def _submitbatch(self, req):
221 cmds = []
221 cmds = []
222 for op, argsdict in req:
222 for op, argsdict in req:
223 args = ','.join('%s=%s' % p for p in argsdict.iteritems())
223 args = ','.join('%s=%s' % p for p in argsdict.iteritems())
224 cmds.append('%s %s' % (op, args))
224 cmds.append('%s %s' % (op, args))
225 rsp = self._call("batch", cmds=';'.join(cmds))
225 rsp = self._call("batch", cmds=';'.join(cmds))
226 return rsp.split(';')
226 return rsp.split(';')
227 def _submitone(self, op, args):
227 def _submitone(self, op, args):
228 return self._call(op, **args)
228 return self._call(op, **args)
229
229
230 @batchable
230 @batchable
231 def lookup(self, key):
231 def lookup(self, key):
232 self.requirecap('lookup', _('look up remote revision'))
232 self.requirecap('lookup', _('look up remote revision'))
233 f = future()
233 f = future()
234 yield {'key': encoding.fromlocal(key)}, f
234 yield {'key': encoding.fromlocal(key)}, f
235 d = f.value
235 d = f.value
236 success, data = d[:-1].split(" ", 1)
236 success, data = d[:-1].split(" ", 1)
237 if int(success):
237 if int(success):
238 yield bin(data)
238 yield bin(data)
239 self._abort(error.RepoError(data))
239 self._abort(error.RepoError(data))
240
240
241 @batchable
241 @batchable
242 def heads(self):
242 def heads(self):
243 f = future()
243 f = future()
244 yield {}, f
244 yield {}, f
245 d = f.value
245 d = f.value
246 try:
246 try:
247 yield decodelist(d[:-1])
247 yield decodelist(d[:-1])
248 except ValueError:
248 except ValueError:
249 self._abort(error.ResponseError(_("unexpected response:"), d))
249 self._abort(error.ResponseError(_("unexpected response:"), d))
250
250
251 @batchable
251 @batchable
252 def known(self, nodes):
252 def known(self, nodes):
253 f = future()
253 f = future()
254 yield {'nodes': encodelist(nodes)}, f
254 yield {'nodes': encodelist(nodes)}, f
255 d = f.value
255 d = f.value
256 try:
256 try:
257 yield [bool(int(b)) for b in d]
257 yield [bool(int(b)) for b in d]
258 except ValueError:
258 except ValueError:
259 self._abort(error.ResponseError(_("unexpected response:"), d))
259 self._abort(error.ResponseError(_("unexpected response:"), d))
260
260
261 @batchable
261 @batchable
262 def branchmap(self):
262 def branchmap(self):
263 f = future()
263 f = future()
264 yield {}, f
264 yield {}, f
265 d = f.value
265 d = f.value
266 try:
266 try:
267 branchmap = {}
267 branchmap = {}
268 for branchpart in d.splitlines():
268 for branchpart in d.splitlines():
269 branchname, branchheads = branchpart.split(' ', 1)
269 branchname, branchheads = branchpart.split(' ', 1)
270 branchname = encoding.tolocal(urllib.unquote(branchname))
270 branchname = encoding.tolocal(urllib.unquote(branchname))
271 branchheads = decodelist(branchheads)
271 branchheads = decodelist(branchheads)
272 branchmap[branchname] = branchheads
272 branchmap[branchname] = branchheads
273 yield branchmap
273 yield branchmap
274 except TypeError:
274 except TypeError:
275 self._abort(error.ResponseError(_("unexpected response:"), d))
275 self._abort(error.ResponseError(_("unexpected response:"), d))
276
276
277 def branches(self, nodes):
277 def branches(self, nodes):
278 n = encodelist(nodes)
278 n = encodelist(nodes)
279 d = self._call("branches", nodes=n)
279 d = self._call("branches", nodes=n)
280 try:
280 try:
281 br = [tuple(decodelist(b)) for b in d.splitlines()]
281 br = [tuple(decodelist(b)) for b in d.splitlines()]
282 return br
282 return br
283 except ValueError:
283 except ValueError:
284 self._abort(error.ResponseError(_("unexpected response:"), d))
284 self._abort(error.ResponseError(_("unexpected response:"), d))
285
285
286 def between(self, pairs):
286 def between(self, pairs):
287 batch = 8 # avoid giant requests
287 batch = 8 # avoid giant requests
288 r = []
288 r = []
289 for i in xrange(0, len(pairs), batch):
289 for i in xrange(0, len(pairs), batch):
290 n = " ".join([encodelist(p, '-') for p in pairs[i:i + batch]])
290 n = " ".join([encodelist(p, '-') for p in pairs[i:i + batch]])
291 d = self._call("between", pairs=n)
291 d = self._call("between", pairs=n)
292 try:
292 try:
293 r.extend(l and decodelist(l) or [] for l in d.splitlines())
293 r.extend(l and decodelist(l) or [] for l in d.splitlines())
294 except ValueError:
294 except ValueError:
295 self._abort(error.ResponseError(_("unexpected response:"), d))
295 self._abort(error.ResponseError(_("unexpected response:"), d))
296 return r
296 return r
297
297
298 @batchable
298 @batchable
299 def pushkey(self, namespace, key, old, new):
299 def pushkey(self, namespace, key, old, new):
300 if not self.capable('pushkey'):
300 if not self.capable('pushkey'):
301 yield False, None
301 yield False, None
302 f = future()
302 f = future()
303 self.ui.debug('preparing pushkey for "%s:%s"\n' % (namespace, key))
303 self.ui.debug('preparing pushkey for "%s:%s"\n' % (namespace, key))
304 yield {'namespace': encoding.fromlocal(namespace),
304 yield {'namespace': encoding.fromlocal(namespace),
305 'key': encoding.fromlocal(key),
305 'key': encoding.fromlocal(key),
306 'old': encoding.fromlocal(old),
306 'old': encoding.fromlocal(old),
307 'new': encoding.fromlocal(new)}, f
307 'new': encoding.fromlocal(new)}, f
308 d = f.value
308 d = f.value
309 d, output = d.split('\n', 1)
309 d, output = d.split('\n', 1)
310 try:
310 try:
311 d = bool(int(d))
311 d = bool(int(d))
312 except ValueError:
312 except ValueError:
313 raise error.ResponseError(
313 raise error.ResponseError(
314 _('push failed (unexpected response):'), d)
314 _('push failed (unexpected response):'), d)
315 for l in output.splitlines(True):
315 for l in output.splitlines(True):
316 self.ui.status(_('remote: '), l)
316 self.ui.status(_('remote: '), l)
317 yield d
317 yield d
318
318
319 @batchable
319 @batchable
320 def listkeys(self, namespace):
320 def listkeys(self, namespace):
321 if not self.capable('pushkey'):
321 if not self.capable('pushkey'):
322 yield {}, None
322 yield {}, None
323 f = future()
323 f = future()
324 self.ui.debug('preparing listkeys for "%s"\n' % namespace)
324 self.ui.debug('preparing listkeys for "%s"\n' % namespace)
325 yield {'namespace': encoding.fromlocal(namespace)}, f
325 yield {'namespace': encoding.fromlocal(namespace)}, f
326 d = f.value
326 d = f.value
327 yield pushkeymod.decodekeys(d)
327 yield pushkeymod.decodekeys(d)
328
328
329 def stream_out(self):
329 def stream_out(self):
330 return self._callstream('stream_out')
330 return self._callstream('stream_out')
331
331
332 def changegroup(self, nodes, kind):
332 def changegroup(self, nodes, kind):
333 n = encodelist(nodes)
333 n = encodelist(nodes)
334 f = self._callcompressable("changegroup", roots=n)
334 f = self._callcompressable("changegroup", roots=n)
335 return changegroupmod.cg1unpacker(f, 'UN')
335 return changegroupmod.cg1unpacker(f, 'UN')
336
336
337 def changegroupsubset(self, bases, heads, kind):
337 def changegroupsubset(self, bases, heads, kind):
338 self.requirecap('changegroupsubset', _('look up remote changes'))
338 self.requirecap('changegroupsubset', _('look up remote changes'))
339 bases = encodelist(bases)
339 bases = encodelist(bases)
340 heads = encodelist(heads)
340 heads = encodelist(heads)
341 f = self._callcompressable("changegroupsubset",
341 f = self._callcompressable("changegroupsubset",
342 bases=bases, heads=heads)
342 bases=bases, heads=heads)
343 return changegroupmod.cg1unpacker(f, 'UN')
343 return changegroupmod.cg1unpacker(f, 'UN')
344
344
345 def getbundle(self, source, **kwargs):
345 def getbundle(self, source, **kwargs):
346 self.requirecap('getbundle', _('look up remote changes'))
346 self.requirecap('getbundle', _('look up remote changes'))
347 opts = {}
347 opts = {}
348 for key, value in kwargs.iteritems():
348 for key, value in kwargs.iteritems():
349 if value is None:
349 if value is None:
350 continue
350 continue
351 keytype = gboptsmap.get(key)
351 keytype = gboptsmap.get(key)
352 if keytype is None:
352 if keytype is None:
353 assert False, 'unexpected'
353 assert False, 'unexpected'
354 elif keytype == 'nodes':
354 elif keytype == 'nodes':
355 value = encodelist(value)
355 value = encodelist(value)
356 elif keytype == 'csv':
356 elif keytype == 'csv':
357 value = ','.join(value)
357 value = ','.join(value)
358 elif keytype == 'boolean':
358 elif keytype == 'boolean':
359 value = '%i' % bool(value)
359 value = '%i' % bool(value)
360 elif keytype != 'plain':
360 elif keytype != 'plain':
361 raise KeyError('unknown getbundle option type %s'
361 raise KeyError('unknown getbundle option type %s'
362 % keytype)
362 % keytype)
363 opts[key] = value
363 opts[key] = value
364 f = self._callcompressable("getbundle", **opts)
364 f = self._callcompressable("getbundle", **opts)
365 bundlecaps = kwargs.get('bundlecaps')
365 bundlecaps = kwargs.get('bundlecaps')
366 if bundlecaps is None:
366 if bundlecaps is None:
367 bundlecaps = () # kwargs could have it to None
367 bundlecaps = () # kwargs could have it to None
368 if util.any((cap.startswith('HG2') for cap in bundlecaps)):
368 if util.any((cap.startswith('HG2') for cap in bundlecaps)):
369 return bundle2.getunbundler(self.ui, f)
369 return bundle2.getunbundler(self.ui, f)
370 else:
370 else:
371 return changegroupmod.cg1unpacker(f, 'UN')
371 return changegroupmod.cg1unpacker(f, 'UN')
372
372
373 def unbundle(self, cg, heads, source):
373 def unbundle(self, cg, heads, source):
374 '''Send cg (a readable file-like object representing the
374 '''Send cg (a readable file-like object representing the
375 changegroup to push, typically a chunkbuffer object) to the
375 changegroup to push, typically a chunkbuffer object) to the
376 remote server as a bundle.
376 remote server as a bundle.
377
377
378 When pushing a bundle10 stream, return an integer indicating the
378 When pushing a bundle10 stream, return an integer indicating the
379 result of the push (see localrepository.addchangegroup()).
379 result of the push (see localrepository.addchangegroup()).
380
380
381 When pushing a bundle20 stream, return a bundle20 stream.'''
381 When pushing a bundle20 stream, return a bundle20 stream.'''
382
382
383 if heads != ['force'] and self.capable('unbundlehash'):
383 if heads != ['force'] and self.capable('unbundlehash'):
384 heads = encodelist(['hashed',
384 heads = encodelist(['hashed',
385 util.sha1(''.join(sorted(heads))).digest()])
385 util.sha1(''.join(sorted(heads))).digest()])
386 else:
386 else:
387 heads = encodelist(heads)
387 heads = encodelist(heads)
388
388
389 if util.safehasattr(cg, 'deltaheader'):
389 if util.safehasattr(cg, 'deltaheader'):
390 # this a bundle10, do the old style call sequence
390 # this a bundle10, do the old style call sequence
391 ret, output = self._callpush("unbundle", cg, heads=heads)
391 ret, output = self._callpush("unbundle", cg, heads=heads)
392 if ret == "":
392 if ret == "":
393 raise error.ResponseError(
393 raise error.ResponseError(
394 _('push failed:'), output)
394 _('push failed:'), output)
395 try:
395 try:
396 ret = int(ret)
396 ret = int(ret)
397 except ValueError:
397 except ValueError:
398 raise error.ResponseError(
398 raise error.ResponseError(
399 _('push failed (unexpected response):'), ret)
399 _('push failed (unexpected response):'), ret)
400
400
401 for l in output.splitlines(True):
401 for l in output.splitlines(True):
402 self.ui.status(_('remote: '), l)
402 self.ui.status(_('remote: '), l)
403 else:
403 else:
404 # bundle2 push. Send a stream, fetch a stream.
404 # bundle2 push. Send a stream, fetch a stream.
405 stream = self._calltwowaystream('unbundle', cg, heads=heads)
405 stream = self._calltwowaystream('unbundle', cg, heads=heads)
406 ret = bundle2.getunbundler(self.ui, stream)
406 ret = bundle2.getunbundler(self.ui, stream)
407 return ret
407 return ret
408
408
409 def debugwireargs(self, one, two, three=None, four=None, five=None):
409 def debugwireargs(self, one, two, three=None, four=None, five=None):
410 # don't pass optional arguments left at their default value
410 # don't pass optional arguments left at their default value
411 opts = {}
411 opts = {}
412 if three is not None:
412 if three is not None:
413 opts['three'] = three
413 opts['three'] = three
414 if four is not None:
414 if four is not None:
415 opts['four'] = four
415 opts['four'] = four
416 return self._call('debugwireargs', one=one, two=two, **opts)
416 return self._call('debugwireargs', one=one, two=two, **opts)
417
417
418 def _call(self, cmd, **args):
418 def _call(self, cmd, **args):
419 """execute <cmd> on the server
419 """execute <cmd> on the server
420
420
421 The command is expected to return a simple string.
421 The command is expected to return a simple string.
422
422
423 returns the server reply as a string."""
423 returns the server reply as a string."""
424 raise NotImplementedError()
424 raise NotImplementedError()
425
425
426 def _callstream(self, cmd, **args):
426 def _callstream(self, cmd, **args):
427 """execute <cmd> on the server
427 """execute <cmd> on the server
428
428
429 The command is expected to return a stream.
429 The command is expected to return a stream.
430
430
431 returns the server reply as a file like object."""
431 returns the server reply as a file like object."""
432 raise NotImplementedError()
432 raise NotImplementedError()
433
433
434 def _callcompressable(self, cmd, **args):
434 def _callcompressable(self, cmd, **args):
435 """execute <cmd> on the server
435 """execute <cmd> on the server
436
436
437 The command is expected to return a stream.
437 The command is expected to return a stream.
438
438
439 The stream may have been compressed in some implementations. This
439 The stream may have been compressed in some implementations. This
440 function takes care of the decompression. This is the only difference
440 function takes care of the decompression. This is the only difference
441 with _callstream.
441 with _callstream.
442
442
443 returns the server reply as a file like object.
443 returns the server reply as a file like object.
444 """
444 """
445 raise NotImplementedError()
445 raise NotImplementedError()
446
446
447 def _callpush(self, cmd, fp, **args):
447 def _callpush(self, cmd, fp, **args):
448 """execute a <cmd> on server
448 """execute a <cmd> on server
449
449
450 The command is expected to be related to a push. Push has a special
450 The command is expected to be related to a push. Push has a special
451 return method.
451 return method.
452
452
453 returns the server reply as a (ret, output) tuple. ret is either
453 returns the server reply as a (ret, output) tuple. ret is either
454 empty (error) or a stringified int.
454 empty (error) or a stringified int.
455 """
455 """
456 raise NotImplementedError()
456 raise NotImplementedError()
457
457
458 def _calltwowaystream(self, cmd, fp, **args):
458 def _calltwowaystream(self, cmd, fp, **args):
459 """execute <cmd> on server
459 """execute <cmd> on server
460
460
461 The command will send a stream to the server and get a stream in reply.
461 The command will send a stream to the server and get a stream in reply.
462 """
462 """
463 raise NotImplementedError()
463 raise NotImplementedError()
464
464
465 def _abort(self, exception):
465 def _abort(self, exception):
466 """clearly abort the wire protocol connection and raise the exception
466 """clearly abort the wire protocol connection and raise the exception
467 """
467 """
468 raise NotImplementedError()
468 raise NotImplementedError()
469
469
470 # server side
470 # server side
471
471
472 # wire protocol command can either return a string or one of these classes.
472 # wire protocol command can either return a string or one of these classes.
473 class streamres(object):
473 class streamres(object):
474 """wireproto reply: binary stream
474 """wireproto reply: binary stream
475
475
476 The call was successful and the result is a stream.
476 The call was successful and the result is a stream.
477 Iterate on the `self.gen` attribute to retrieve chunks.
477 Iterate on the `self.gen` attribute to retrieve chunks.
478 """
478 """
479 def __init__(self, gen):
479 def __init__(self, gen):
480 self.gen = gen
480 self.gen = gen
481
481
482 class pushres(object):
482 class pushres(object):
483 """wireproto reply: success with simple integer return
483 """wireproto reply: success with simple integer return
484
484
485 The call was successful and returned an integer contained in `self.res`.
485 The call was successful and returned an integer contained in `self.res`.
486 """
486 """
487 def __init__(self, res):
487 def __init__(self, res):
488 self.res = res
488 self.res = res
489
489
490 class pusherr(object):
490 class pusherr(object):
491 """wireproto reply: failure
491 """wireproto reply: failure
492
492
493 The call failed. The `self.res` attribute contains the error message.
493 The call failed. The `self.res` attribute contains the error message.
494 """
494 """
495 def __init__(self, res):
495 def __init__(self, res):
496 self.res = res
496 self.res = res
497
497
498 class ooberror(object):
498 class ooberror(object):
499 """wireproto reply: failure of a batch of operation
499 """wireproto reply: failure of a batch of operation
500
500
501 Something failed during a batch call. The error message is stored in
501 Something failed during a batch call. The error message is stored in
502 `self.message`.
502 `self.message`.
503 """
503 """
504 def __init__(self, message):
504 def __init__(self, message):
505 self.message = message
505 self.message = message
506
506
507 def dispatch(repo, proto, command):
507 def dispatch(repo, proto, command):
508 repo = repo.filtered("served")
508 repo = repo.filtered("served")
509 func, spec = commands[command]
509 func, spec = commands[command]
510 args = proto.getargs(spec)
510 args = proto.getargs(spec)
511 return func(repo, proto, *args)
511 return func(repo, proto, *args)
512
512
513 def options(cmd, keys, others):
513 def options(cmd, keys, others):
514 opts = {}
514 opts = {}
515 for k in keys:
515 for k in keys:
516 if k in others:
516 if k in others:
517 opts[k] = others[k]
517 opts[k] = others[k]
518 del others[k]
518 del others[k]
519 if others:
519 if others:
520 sys.stderr.write("warning: %s ignored unexpected arguments %s\n"
520 sys.stderr.write("warning: %s ignored unexpected arguments %s\n"
521 % (cmd, ",".join(others)))
521 % (cmd, ",".join(others)))
522 return opts
522 return opts
523
523
524 # list of commands
524 # list of commands
525 commands = {}
525 commands = {}
526
526
527 def wireprotocommand(name, args=''):
527 def wireprotocommand(name, args=''):
528 """decorator for wire protocol command"""
528 """decorator for wire protocol command"""
529 def register(func):
529 def register(func):
530 commands[name] = (func, args)
530 commands[name] = (func, args)
531 return func
531 return func
532 return register
532 return register
533
533
534 @wireprotocommand('batch', 'cmds *')
534 @wireprotocommand('batch', 'cmds *')
535 def batch(repo, proto, cmds, others):
535 def batch(repo, proto, cmds, others):
536 repo = repo.filtered("served")
536 repo = repo.filtered("served")
537 res = []
537 res = []
538 for pair in cmds.split(';'):
538 for pair in cmds.split(';'):
539 op, args = pair.split(' ', 1)
539 op, args = pair.split(' ', 1)
540 vals = {}
540 vals = {}
541 for a in args.split(','):
541 for a in args.split(','):
542 if a:
542 if a:
543 n, v = a.split('=')
543 n, v = a.split('=')
544 vals[n] = unescapearg(v)
544 vals[n] = unescapearg(v)
545 func, spec = commands[op]
545 func, spec = commands[op]
546 if spec:
546 if spec:
547 keys = spec.split()
547 keys = spec.split()
548 data = {}
548 data = {}
549 for k in keys:
549 for k in keys:
550 if k == '*':
550 if k == '*':
551 star = {}
551 star = {}
552 for key in vals.keys():
552 for key in vals.keys():
553 if key not in keys:
553 if key not in keys:
554 star[key] = vals[key]
554 star[key] = vals[key]
555 data['*'] = star
555 data['*'] = star
556 else:
556 else:
557 data[k] = vals[k]
557 data[k] = vals[k]
558 result = func(repo, proto, *[data[k] for k in keys])
558 result = func(repo, proto, *[data[k] for k in keys])
559 else:
559 else:
560 result = func(repo, proto)
560 result = func(repo, proto)
561 if isinstance(result, ooberror):
561 if isinstance(result, ooberror):
562 return result
562 return result
563 res.append(escapearg(result))
563 res.append(escapearg(result))
564 return ';'.join(res)
564 return ';'.join(res)
565
565
566 @wireprotocommand('between', 'pairs')
566 @wireprotocommand('between', 'pairs')
567 def between(repo, proto, pairs):
567 def between(repo, proto, pairs):
568 pairs = [decodelist(p, '-') for p in pairs.split(" ")]
568 pairs = [decodelist(p, '-') for p in pairs.split(" ")]
569 r = []
569 r = []
570 for b in repo.between(pairs):
570 for b in repo.between(pairs):
571 r.append(encodelist(b) + "\n")
571 r.append(encodelist(b) + "\n")
572 return "".join(r)
572 return "".join(r)
573
573
574 @wireprotocommand('branchmap')
574 @wireprotocommand('branchmap')
575 def branchmap(repo, proto):
575 def branchmap(repo, proto):
576 branchmap = repo.branchmap()
576 branchmap = repo.branchmap()
577 heads = []
577 heads = []
578 for branch, nodes in branchmap.iteritems():
578 for branch, nodes in branchmap.iteritems():
579 branchname = urllib.quote(encoding.fromlocal(branch))
579 branchname = urllib.quote(encoding.fromlocal(branch))
580 branchnodes = encodelist(nodes)
580 branchnodes = encodelist(nodes)
581 heads.append('%s %s' % (branchname, branchnodes))
581 heads.append('%s %s' % (branchname, branchnodes))
582 return '\n'.join(heads)
582 return '\n'.join(heads)
583
583
584 @wireprotocommand('branches', 'nodes')
584 @wireprotocommand('branches', 'nodes')
585 def branches(repo, proto, nodes):
585 def branches(repo, proto, nodes):
586 nodes = decodelist(nodes)
586 nodes = decodelist(nodes)
587 r = []
587 r = []
588 for b in repo.branches(nodes):
588 for b in repo.branches(nodes):
589 r.append(encodelist(b) + "\n")
589 r.append(encodelist(b) + "\n")
590 return "".join(r)
590 return "".join(r)
591
591
592
592
593 wireprotocaps = ['lookup', 'changegroupsubset', 'branchmap', 'pushkey',
593 wireprotocaps = ['lookup', 'changegroupsubset', 'branchmap', 'pushkey',
594 'known', 'getbundle', 'unbundlehash', 'batch']
594 'known', 'getbundle', 'unbundlehash', 'batch']
595
595
596 def _capabilities(repo, proto):
596 def _capabilities(repo, proto):
597 """return a list of capabilities for a repo
597 """return a list of capabilities for a repo
598
598
599 This function exists to allow extensions to easily wrap capabilities
599 This function exists to allow extensions to easily wrap capabilities
600 computation
600 computation
601
601
602 - returns a lists: easy to alter
602 - returns a lists: easy to alter
603 - change done here will be propagated to both `capabilities` and `hello`
603 - change done here will be propagated to both `capabilities` and `hello`
604 command without any other action needed.
604 command without any other action needed.
605 """
605 """
606 # copy to prevent modification of the global list
606 # copy to prevent modification of the global list
607 caps = list(wireprotocaps)
607 caps = list(wireprotocaps)
608 if _allowstream(repo.ui):
608 if _allowstream(repo.ui):
609 if repo.ui.configbool('server', 'preferuncompressed', False):
609 if repo.ui.configbool('server', 'preferuncompressed', False):
610 caps.append('stream-preferred')
610 caps.append('stream-preferred')
611 requiredformats = repo.requirements & repo.supportedformats
611 requiredformats = repo.requirements & repo.supportedformats
612 # if our local revlogs are just revlogv1, add 'stream' cap
612 # if our local revlogs are just revlogv1, add 'stream' cap
613 if not requiredformats - set(('revlogv1',)):
613 if not requiredformats - set(('revlogv1',)):
614 caps.append('stream')
614 caps.append('stream')
615 # otherwise, add 'streamreqs' detailing our local revlog format
615 # otherwise, add 'streamreqs' detailing our local revlog format
616 else:
616 else:
617 caps.append('streamreqs=%s' % ','.join(requiredformats))
617 caps.append('streamreqs=%s' % ','.join(requiredformats))
618 if repo.ui.configbool('experimental', 'bundle2-advertise', True):
618 if repo.ui.configbool('experimental', 'bundle2-advertise', True):
619 capsblob = bundle2.encodecaps(bundle2.getrepocaps(repo))
619 capsblob = bundle2.encodecaps(bundle2.getrepocaps(repo))
620 caps.append('bundle2=' + urllib.quote(capsblob))
620 caps.append('bundle2=' + urllib.quote(capsblob))
621 caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority))
621 caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority))
622 caps.append('httpheader=1024')
622 caps.append('httpheader=1024')
623 return caps
623 return caps
624
624
625 # If you are writing an extension and consider wrapping this function. Wrap
625 # If you are writing an extension and consider wrapping this function. Wrap
626 # `_capabilities` instead.
626 # `_capabilities` instead.
627 @wireprotocommand('capabilities')
627 @wireprotocommand('capabilities')
628 def capabilities(repo, proto):
628 def capabilities(repo, proto):
629 return ' '.join(_capabilities(repo, proto))
629 return ' '.join(_capabilities(repo, proto))
630
630
631 @wireprotocommand('changegroup', 'roots')
631 @wireprotocommand('changegroup', 'roots')
632 def changegroup(repo, proto, roots):
632 def changegroup(repo, proto, roots):
633 nodes = decodelist(roots)
633 nodes = decodelist(roots)
634 cg = changegroupmod.changegroup(repo, nodes, 'serve')
634 cg = changegroupmod.changegroup(repo, nodes, 'serve')
635 return streamres(proto.groupchunks(cg))
635 return streamres(proto.groupchunks(cg))
636
636
637 @wireprotocommand('changegroupsubset', 'bases heads')
637 @wireprotocommand('changegroupsubset', 'bases heads')
638 def changegroupsubset(repo, proto, bases, heads):
638 def changegroupsubset(repo, proto, bases, heads):
639 bases = decodelist(bases)
639 bases = decodelist(bases)
640 heads = decodelist(heads)
640 heads = decodelist(heads)
641 cg = changegroupmod.changegroupsubset(repo, bases, heads, 'serve')
641 cg = changegroupmod.changegroupsubset(repo, bases, heads, 'serve')
642 return streamres(proto.groupchunks(cg))
642 return streamres(proto.groupchunks(cg))
643
643
644 @wireprotocommand('debugwireargs', 'one two *')
644 @wireprotocommand('debugwireargs', 'one two *')
645 def debugwireargs(repo, proto, one, two, others):
645 def debugwireargs(repo, proto, one, two, others):
646 # only accept optional args from the known set
646 # only accept optional args from the known set
647 opts = options('debugwireargs', ['three', 'four'], others)
647 opts = options('debugwireargs', ['three', 'four'], others)
648 return repo.debugwireargs(one, two, **opts)
648 return repo.debugwireargs(one, two, **opts)
649
649
650 # List of options accepted by getbundle.
650 # List of options accepted by getbundle.
651 #
651 #
652 # Meant to be extended by extensions. It is the extension's responsibility to
652 # Meant to be extended by extensions. It is the extension's responsibility to
653 # ensure such options are properly processed in exchange.getbundle.
653 # ensure such options are properly processed in exchange.getbundle.
654 gboptslist = ['heads', 'common', 'bundlecaps']
654 gboptslist = ['heads', 'common', 'bundlecaps']
655
655
656 @wireprotocommand('getbundle', '*')
656 @wireprotocommand('getbundle', '*')
657 def getbundle(repo, proto, others):
657 def getbundle(repo, proto, others):
658 opts = options('getbundle', gboptsmap.keys(), others)
658 opts = options('getbundle', gboptsmap.keys(), others)
659 for k, v in opts.iteritems():
659 for k, v in opts.iteritems():
660 keytype = gboptsmap[k]
660 keytype = gboptsmap[k]
661 if keytype == 'nodes':
661 if keytype == 'nodes':
662 opts[k] = decodelist(v)
662 opts[k] = decodelist(v)
663 elif keytype == 'csv':
663 elif keytype == 'csv':
664 opts[k] = set(v.split(','))
664 opts[k] = set(v.split(','))
665 elif keytype == 'boolean':
665 elif keytype == 'boolean':
666 opts[k] = bool(v)
666 opts[k] = bool(v)
667 elif keytype != 'plain':
667 elif keytype != 'plain':
668 raise KeyError('unknown getbundle option type %s'
668 raise KeyError('unknown getbundle option type %s'
669 % keytype)
669 % keytype)
670 cg = exchange.getbundle(repo, 'serve', **opts)
670 cg = exchange.getbundle(repo, 'serve', **opts)
671 return streamres(proto.groupchunks(cg))
671 return streamres(proto.groupchunks(cg))
672
672
673 @wireprotocommand('heads')
673 @wireprotocommand('heads')
674 def heads(repo, proto):
674 def heads(repo, proto):
675 h = repo.heads()
675 h = repo.heads()
676 return encodelist(h) + "\n"
676 return encodelist(h) + "\n"
677
677
678 @wireprotocommand('hello')
678 @wireprotocommand('hello')
679 def hello(repo, proto):
679 def hello(repo, proto):
680 '''the hello command returns a set of lines describing various
680 '''the hello command returns a set of lines describing various
681 interesting things about the server, in an RFC822-like format.
681 interesting things about the server, in an RFC822-like format.
682 Currently the only one defined is "capabilities", which
682 Currently the only one defined is "capabilities", which
683 consists of a line in the form:
683 consists of a line in the form:
684
684
685 capabilities: space separated list of tokens
685 capabilities: space separated list of tokens
686 '''
686 '''
687 return "capabilities: %s\n" % (capabilities(repo, proto))
687 return "capabilities: %s\n" % (capabilities(repo, proto))
688
688
689 @wireprotocommand('listkeys', 'namespace')
689 @wireprotocommand('listkeys', 'namespace')
690 def listkeys(repo, proto, namespace):
690 def listkeys(repo, proto, namespace):
691 d = repo.listkeys(encoding.tolocal(namespace)).items()
691 d = repo.listkeys(encoding.tolocal(namespace)).items()
692 return pushkeymod.encodekeys(d)
692 return pushkeymod.encodekeys(d)
693
693
694 @wireprotocommand('lookup', 'key')
694 @wireprotocommand('lookup', 'key')
695 def lookup(repo, proto, key):
695 def lookup(repo, proto, key):
696 try:
696 try:
697 k = encoding.tolocal(key)
697 k = encoding.tolocal(key)
698 c = repo[k]
698 c = repo[k]
699 r = c.hex()
699 r = c.hex()
700 success = 1
700 success = 1
701 except Exception, inst:
701 except Exception, inst:
702 r = str(inst)
702 r = str(inst)
703 success = 0
703 success = 0
704 return "%s %s\n" % (success, r)
704 return "%s %s\n" % (success, r)
705
705
706 @wireprotocommand('known', 'nodes *')
706 @wireprotocommand('known', 'nodes *')
707 def known(repo, proto, nodes, others):
707 def known(repo, proto, nodes, others):
708 return ''.join(b and "1" or "0" for b in repo.known(decodelist(nodes)))
708 return ''.join(b and "1" or "0" for b in repo.known(decodelist(nodes)))
709
709
710 @wireprotocommand('pushkey', 'namespace key old new')
710 @wireprotocommand('pushkey', 'namespace key old new')
711 def pushkey(repo, proto, namespace, key, old, new):
711 def pushkey(repo, proto, namespace, key, old, new):
712 # compatibility with pre-1.8 clients which were accidentally
712 # compatibility with pre-1.8 clients which were accidentally
713 # sending raw binary nodes rather than utf-8-encoded hex
713 # sending raw binary nodes rather than utf-8-encoded hex
714 if len(new) == 20 and new.encode('string-escape') != new:
714 if len(new) == 20 and new.encode('string-escape') != new:
715 # looks like it could be a binary node
715 # looks like it could be a binary node
716 try:
716 try:
717 new.decode('utf-8')
717 new.decode('utf-8')
718 new = encoding.tolocal(new) # but cleanly decodes as UTF-8
718 new = encoding.tolocal(new) # but cleanly decodes as UTF-8
719 except UnicodeDecodeError:
719 except UnicodeDecodeError:
720 pass # binary, leave unmodified
720 pass # binary, leave unmodified
721 else:
721 else:
722 new = encoding.tolocal(new) # normal path
722 new = encoding.tolocal(new) # normal path
723
723
724 if util.safehasattr(proto, 'restore'):
724 if util.safehasattr(proto, 'restore'):
725
725
726 proto.redirect()
726 proto.redirect()
727
727
728 try:
728 try:
729 r = repo.pushkey(encoding.tolocal(namespace), encoding.tolocal(key),
729 r = repo.pushkey(encoding.tolocal(namespace), encoding.tolocal(key),
730 encoding.tolocal(old), new) or False
730 encoding.tolocal(old), new) or False
731 except util.Abort:
731 except util.Abort:
732 r = False
732 r = False
733
733
734 output = proto.restore()
734 output = proto.restore()
735
735
736 return '%s\n%s' % (int(r), output)
736 return '%s\n%s' % (int(r), output)
737
737
738 r = repo.pushkey(encoding.tolocal(namespace), encoding.tolocal(key),
738 r = repo.pushkey(encoding.tolocal(namespace), encoding.tolocal(key),
739 encoding.tolocal(old), new)
739 encoding.tolocal(old), new)
740 return '%s\n' % int(r)
740 return '%s\n' % int(r)
741
741
742 def _allowstream(ui):
742 def _allowstream(ui):
743 return ui.configbool('server', 'uncompressed', True, untrusted=True)
743 return ui.configbool('server', 'uncompressed', True, untrusted=True)
744
744
745 def _walkstreamfiles(repo):
745 def _walkstreamfiles(repo):
746 # this is it's own function so extensions can override it
746 # this is it's own function so extensions can override it
747 return repo.store.walk()
747 return repo.store.walk()
748
748
749 @wireprotocommand('stream_out')
749 @wireprotocommand('stream_out')
750 def stream(repo, proto):
750 def stream(repo, proto):
751 '''If the server supports streaming clone, it advertises the "stream"
751 '''If the server supports streaming clone, it advertises the "stream"
752 capability with a value representing the version and flags of the repo
752 capability with a value representing the version and flags of the repo
753 it is serving. Client checks to see if it understands the format.
753 it is serving. Client checks to see if it understands the format.
754
754
755 The format is simple: the server writes out a line with the amount
755 The format is simple: the server writes out a line with the amount
756 of files, then the total amount of bytes to be transferred (separated
756 of files, then the total amount of bytes to be transferred (separated
757 by a space). Then, for each file, the server first writes the filename
757 by a space). Then, for each file, the server first writes the filename
758 and file size (separated by the null character), then the file contents.
758 and file size (separated by the null character), then the file contents.
759 '''
759 '''
760
760
761 if not _allowstream(repo.ui):
761 if not _allowstream(repo.ui):
762 return '1\n'
762 return '1\n'
763
763
764 entries = []
764 entries = []
765 total_bytes = 0
765 total_bytes = 0
766 try:
766 try:
767 # get consistent snapshot of repo, lock during scan
767 # get consistent snapshot of repo, lock during scan
768 lock = repo.lock()
768 lock = repo.lock()
769 try:
769 try:
770 repo.ui.debug('scanning\n')
770 repo.ui.debug('scanning\n')
771 for name, ename, size in _walkstreamfiles(repo):
771 for name, ename, size in _walkstreamfiles(repo):
772 if size:
772 if size:
773 entries.append((name, size))
773 entries.append((name, size))
774 total_bytes += size
774 total_bytes += size
775 finally:
775 finally:
776 lock.release()
776 lock.release()
777 except error.LockError:
777 except error.LockError:
778 return '2\n' # error: 2
778 return '2\n' # error: 2
779
779
780 def streamer(repo, entries, total):
780 def streamer(repo, entries, total):
781 '''stream out all metadata files in repository.'''
781 '''stream out all metadata files in repository.'''
782 yield '0\n' # success
782 yield '0\n' # success
783 repo.ui.debug('%d files, %d bytes to transfer\n' %
783 repo.ui.debug('%d files, %d bytes to transfer\n' %
784 (len(entries), total_bytes))
784 (len(entries), total_bytes))
785 yield '%d %d\n' % (len(entries), total_bytes)
785 yield '%d %d\n' % (len(entries), total_bytes)
786
786
787 sopener = repo.svfs
787 sopener = repo.svfs
788 oldaudit = sopener.mustaudit
788 oldaudit = sopener.mustaudit
789 debugflag = repo.ui.debugflag
789 debugflag = repo.ui.debugflag
790 sopener.mustaudit = False
790 sopener.mustaudit = False
791
791
792 try:
792 try:
793 for name, size in entries:
793 for name, size in entries:
794 if debugflag:
794 if debugflag:
795 repo.ui.debug('sending %s (%d bytes)\n' % (name, size))
795 repo.ui.debug('sending %s (%d bytes)\n' % (name, size))
796 # partially encode name over the wire for backwards compat
796 # partially encode name over the wire for backwards compat
797 yield '%s\0%d\n' % (store.encodedir(name), size)
797 yield '%s\0%d\n' % (store.encodedir(name), size)
798 if size <= 65536:
798 if size <= 65536:
799 fp = sopener(name)
799 fp = sopener(name)
800 try:
800 try:
801 data = fp.read(size)
801 data = fp.read(size)
802 finally:
802 finally:
803 fp.close()
803 fp.close()
804 yield data
804 yield data
805 else:
805 else:
806 for chunk in util.filechunkiter(sopener(name), limit=size):
806 for chunk in util.filechunkiter(sopener(name), limit=size):
807 yield chunk
807 yield chunk
808 # replace with "finally:" when support for python 2.4 has been dropped
808 # replace with "finally:" when support for python 2.4 has been dropped
809 except Exception:
809 except Exception:
810 sopener.mustaudit = oldaudit
810 sopener.mustaudit = oldaudit
811 raise
811 raise
812 sopener.mustaudit = oldaudit
812 sopener.mustaudit = oldaudit
813
813
814 return streamres(streamer(repo, entries, total_bytes))
814 return streamres(streamer(repo, entries, total_bytes))
815
815
816 @wireprotocommand('unbundle', 'heads')
816 @wireprotocommand('unbundle', 'heads')
817 def unbundle(repo, proto, heads):
817 def unbundle(repo, proto, heads):
818 their_heads = decodelist(heads)
818 their_heads = decodelist(heads)
819
819
820 try:
820 try:
821 proto.redirect()
821 proto.redirect()
822
822
823 exchange.check_heads(repo, their_heads, 'preparing changes')
823 exchange.check_heads(repo, their_heads, 'preparing changes')
824
824
825 # write bundle data to temporary file because it can be big
825 # write bundle data to temporary file because it can be big
826 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
826 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
827 fp = os.fdopen(fd, 'wb+')
827 fp = os.fdopen(fd, 'wb+')
828 r = 0
828 r = 0
829 try:
829 try:
830 proto.getfile(fp)
830 proto.getfile(fp)
831 fp.seek(0)
831 fp.seek(0)
832 gen = exchange.readbundle(repo.ui, fp, None)
832 gen = exchange.readbundle(repo.ui, fp, None)
833 r = exchange.unbundle(repo, gen, their_heads, 'serve',
833 r = exchange.unbundle(repo, gen, their_heads, 'serve',
834 proto._client())
834 proto._client())
835 if util.safehasattr(r, 'addpart'):
835 if util.safehasattr(r, 'addpart'):
836 # The return looks streamable, we are in the bundle2 case and
836 # The return looks streamable, we are in the bundle2 case and
837 # should return a stream.
837 # should return a stream.
838 return streamres(r.getchunks())
838 return streamres(r.getchunks())
839 return pushres(r)
839 return pushres(r)
840
840
841 finally:
841 finally:
842 fp.close()
842 fp.close()
843 os.unlink(tempname)
843 os.unlink(tempname)
844
844
845 except (error.BundleValueError, util.Abort, error.PushRaced), exc:
845 except (error.BundleValueError, util.Abort, error.PushRaced), exc:
846 # handle non-bundle2 case first
846 # handle non-bundle2 case first
847 if not getattr(exc, 'duringunbundle2', False):
847 if not getattr(exc, 'duringunbundle2', False):
848 try:
848 try:
849 raise
849 raise
850 except util.Abort:
850 except util.Abort:
851 # The old code we moved used sys.stderr directly.
851 # The old code we moved used sys.stderr directly.
852 # We did not change it to minimise code change.
852 # We did not change it to minimise code change.
853 # This need to be moved to something proper.
853 # This need to be moved to something proper.
854 # Feel free to do it.
854 # Feel free to do it.
855 sys.stderr.write("abort: %s\n" % exc)
855 sys.stderr.write("abort: %s\n" % exc)
856 return pushres(0)
856 return pushres(0)
857 except error.PushRaced:
857 except error.PushRaced:
858 return pusherr(str(exc))
858 return pusherr(str(exc))
859
859
860 bundler = bundle2.bundle20(repo.ui)
860 bundler = bundle2.bundle20(repo.ui)
861 for out in getattr(exc, '_bundle2salvagedoutput', ()):
862 bundler.addpart(out)
861 try:
863 try:
862 raise
864 raise
863 except error.BundleValueError, exc:
865 except error.BundleValueError, exc:
864 errpart = bundler.newpart('error:unsupportedcontent')
866 errpart = bundler.newpart('error:unsupportedcontent')
865 if exc.parttype is not None:
867 if exc.parttype is not None:
866 errpart.addparam('parttype', exc.parttype)
868 errpart.addparam('parttype', exc.parttype)
867 if exc.params:
869 if exc.params:
868 errpart.addparam('params', '\0'.join(exc.params))
870 errpart.addparam('params', '\0'.join(exc.params))
869 except util.Abort, exc:
871 except util.Abort, exc:
870 manargs = [('message', str(exc))]
872 manargs = [('message', str(exc))]
871 advargs = []
873 advargs = []
872 if exc.hint is not None:
874 if exc.hint is not None:
873 advargs.append(('hint', exc.hint))
875 advargs.append(('hint', exc.hint))
874 bundler.addpart(bundle2.bundlepart('error:abort',
876 bundler.addpart(bundle2.bundlepart('error:abort',
875 manargs, advargs))
877 manargs, advargs))
876 except error.PushRaced, exc:
878 except error.PushRaced, exc:
877 bundler.newpart('error:pushraced', [('message', str(exc))])
879 bundler.newpart('error:pushraced', [('message', str(exc))])
878 return streamres(bundler.getchunks())
880 return streamres(bundler.getchunks())
@@ -1,590 +1,598 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 $ cat > $TESTTMP/bundle2-pushkey-hook.sh << EOF
10 $ cat > $TESTTMP/bundle2-pushkey-hook.sh << EOF
11 > echo pushkey: lock state after \"\$HG_NAMESPACE\"
11 > echo pushkey: lock state after \"\$HG_NAMESPACE\"
12 > hg debuglock
12 > hg debuglock
13 > EOF
13 > EOF
14
14
15 $ cat >> $HGRCPATH << EOF
15 $ cat >> $HGRCPATH << EOF
16 > [experimental]
16 > [experimental]
17 > evolution=createmarkers,exchange
17 > evolution=createmarkers,exchange
18 > bundle2-exp=True
18 > bundle2-exp=True
19 > [ui]
19 > [ui]
20 > ssh=python "$TESTDIR/dummyssh"
20 > ssh=python "$TESTDIR/dummyssh"
21 > logtemplate={rev}:{node|short} {phase} {author} {bookmarks} {desc|firstline}
21 > logtemplate={rev}:{node|short} {phase} {author} {bookmarks} {desc|firstline}
22 > [web]
22 > [web]
23 > push_ssl = false
23 > push_ssl = false
24 > allow_push = *
24 > allow_push = *
25 > [phases]
25 > [phases]
26 > publish=False
26 > publish=False
27 > [hooks]
27 > [hooks]
28 > pretxnclose.tip = hg log -r tip -T "pre-close-tip:{node|short} {phase} {bookmarks}\n"
28 > pretxnclose.tip = hg log -r tip -T "pre-close-tip:{node|short} {phase} {bookmarks}\n"
29 > txnclose.tip = hg log -r tip -T "postclose-tip:{node|short} {phase} {bookmarks}\n"
29 > txnclose.tip = hg log -r tip -T "postclose-tip:{node|short} {phase} {bookmarks}\n"
30 > txnclose.env = sh -c "HG_LOCAL= python \"$TESTDIR/printenv.py\" txnclose"
30 > txnclose.env = sh -c "HG_LOCAL= python \"$TESTDIR/printenv.py\" txnclose"
31 > pushkey= sh "$TESTTMP/bundle2-pushkey-hook.sh"
31 > pushkey= sh "$TESTTMP/bundle2-pushkey-hook.sh"
32 > EOF
32 > EOF
33
33
34 The extension requires a repo (currently unused)
34 The extension requires a repo (currently unused)
35
35
36 $ hg init main
36 $ hg init main
37 $ cd main
37 $ cd main
38 $ touch a
38 $ touch a
39 $ hg add a
39 $ hg add a
40 $ hg commit -m 'a'
40 $ hg commit -m 'a'
41 pre-close-tip:3903775176ed draft
41 pre-close-tip:3903775176ed draft
42 postclose-tip:3903775176ed draft
42 postclose-tip:3903775176ed draft
43 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
43 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
44
44
45 $ hg unbundle $TESTDIR/bundles/rebase.hg
45 $ hg unbundle $TESTDIR/bundles/rebase.hg
46 adding changesets
46 adding changesets
47 adding manifests
47 adding manifests
48 adding file changes
48 adding file changes
49 added 8 changesets with 7 changes to 7 files (+3 heads)
49 added 8 changesets with 7 changes to 7 files (+3 heads)
50 pre-close-tip:02de42196ebe draft
50 pre-close-tip:02de42196ebe draft
51 postclose-tip:02de42196ebe draft
51 postclose-tip:02de42196ebe draft
52 txnclose hook: HG_NODE=cd010b8cd998f3981a5a8115f94f8da4ab506089 HG_PHASES_MOVED=1 HG_SOURCE=unbundle HG_TXNID=TXN:* HG_TXNNAME=unbundle (glob)
52 txnclose hook: HG_NODE=cd010b8cd998f3981a5a8115f94f8da4ab506089 HG_PHASES_MOVED=1 HG_SOURCE=unbundle HG_TXNID=TXN:* HG_TXNNAME=unbundle (glob)
53 bundle:*/tests/bundles/rebase.hg HG_URL=bundle:*/tests/bundles/rebase.hg (glob)
53 bundle:*/tests/bundles/rebase.hg HG_URL=bundle:*/tests/bundles/rebase.hg (glob)
54 (run 'hg heads' to see heads, 'hg merge' to merge)
54 (run 'hg heads' to see heads, 'hg merge' to merge)
55
55
56 $ cd ..
56 $ cd ..
57
57
58 Real world exchange
58 Real world exchange
59 =====================
59 =====================
60
60
61 Add more obsolescence information
61 Add more obsolescence information
62
62
63 $ hg -R main debugobsolete -d '0 0' 1111111111111111111111111111111111111111 `getmainid 9520eea781bc`
63 $ hg -R main debugobsolete -d '0 0' 1111111111111111111111111111111111111111 `getmainid 9520eea781bc`
64 pre-close-tip:02de42196ebe draft
64 pre-close-tip:02de42196ebe draft
65 postclose-tip:02de42196ebe draft
65 postclose-tip:02de42196ebe draft
66 txnclose hook: HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
66 txnclose hook: HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
67 $ hg -R main debugobsolete -d '0 0' 2222222222222222222222222222222222222222 `getmainid 24b6387c8c8c`
67 $ hg -R main debugobsolete -d '0 0' 2222222222222222222222222222222222222222 `getmainid 24b6387c8c8c`
68 pre-close-tip:02de42196ebe draft
68 pre-close-tip:02de42196ebe draft
69 postclose-tip:02de42196ebe draft
69 postclose-tip:02de42196ebe draft
70 txnclose hook: HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
70 txnclose hook: HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
71
71
72 clone --pull
72 clone --pull
73
73
74 $ hg -R main phase --public cd010b8cd998
74 $ hg -R main phase --public cd010b8cd998
75 pre-close-tip:000000000000 public
75 pre-close-tip:000000000000 public
76 postclose-tip:02de42196ebe draft
76 postclose-tip:02de42196ebe draft
77 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=phase (glob)
77 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=phase (glob)
78 $ hg clone main other --pull --rev 9520eea781bc
78 $ hg clone main other --pull --rev 9520eea781bc
79 adding changesets
79 adding changesets
80 adding manifests
80 adding manifests
81 adding file changes
81 adding file changes
82 added 2 changesets with 2 changes to 2 files
82 added 2 changesets with 2 changes to 2 files
83 1 new obsolescence markers
83 1 new obsolescence markers
84 pre-close-tip:9520eea781bc draft
84 pre-close-tip:9520eea781bc draft
85 postclose-tip:9520eea781bc draft
85 postclose-tip:9520eea781bc draft
86 txnclose hook: HG_NEW_OBSMARKERS=1 HG_NODE=cd010b8cd998f3981a5a8115f94f8da4ab506089 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
86 txnclose hook: HG_NEW_OBSMARKERS=1 HG_NODE=cd010b8cd998f3981a5a8115f94f8da4ab506089 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
87 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
87 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
88 updating to branch default
88 updating to branch default
89 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
89 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
90 $ hg -R other log -G
90 $ hg -R other log -G
91 @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
91 @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
92 |
92 |
93 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
93 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
94
94
95 $ hg -R other debugobsolete
95 $ hg -R other debugobsolete
96 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
96 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
97
97
98 pull
98 pull
99
99
100 $ hg -R main phase --public 9520eea781bc
100 $ hg -R main phase --public 9520eea781bc
101 pre-close-tip:000000000000 public
101 pre-close-tip:000000000000 public
102 postclose-tip:02de42196ebe draft
102 postclose-tip:02de42196ebe draft
103 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=phase (glob)
103 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=phase (glob)
104 $ hg -R other pull -r 24b6387c8c8c
104 $ hg -R other pull -r 24b6387c8c8c
105 pulling from $TESTTMP/main (glob)
105 pulling from $TESTTMP/main (glob)
106 searching for changes
106 searching for changes
107 adding changesets
107 adding changesets
108 adding manifests
108 adding manifests
109 adding file changes
109 adding file changes
110 added 1 changesets with 1 changes to 1 files (+1 heads)
110 added 1 changesets with 1 changes to 1 files (+1 heads)
111 1 new obsolescence markers
111 1 new obsolescence markers
112 pre-close-tip:24b6387c8c8c draft
112 pre-close-tip:24b6387c8c8c draft
113 postclose-tip:24b6387c8c8c draft
113 postclose-tip:24b6387c8c8c draft
114 txnclose hook: HG_NEW_OBSMARKERS=1 HG_NODE=24b6387c8c8cae37178880f3fa95ded3cb1cf785 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
114 txnclose hook: HG_NEW_OBSMARKERS=1 HG_NODE=24b6387c8c8cae37178880f3fa95ded3cb1cf785 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
115 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
115 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
116 (run 'hg heads' to see heads, 'hg merge' to merge)
116 (run 'hg heads' to see heads, 'hg merge' to merge)
117 $ hg -R other log -G
117 $ hg -R other log -G
118 o 2:24b6387c8c8c draft Nicolas Dumazet <nicdumz.commits@gmail.com> F
118 o 2:24b6387c8c8c draft Nicolas Dumazet <nicdumz.commits@gmail.com> F
119 |
119 |
120 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
120 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
121 |/
121 |/
122 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
122 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
123
123
124 $ hg -R other debugobsolete
124 $ hg -R other debugobsolete
125 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
125 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
126 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
126 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
127
127
128 pull empty (with phase movement)
128 pull empty (with phase movement)
129
129
130 $ hg -R main phase --public 24b6387c8c8c
130 $ hg -R main phase --public 24b6387c8c8c
131 pre-close-tip:000000000000 public
131 pre-close-tip:000000000000 public
132 postclose-tip:02de42196ebe draft
132 postclose-tip:02de42196ebe draft
133 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=phase (glob)
133 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=phase (glob)
134 $ hg -R other pull -r 24b6387c8c8c
134 $ hg -R other pull -r 24b6387c8c8c
135 pulling from $TESTTMP/main (glob)
135 pulling from $TESTTMP/main (glob)
136 no changes found
136 no changes found
137 pre-close-tip:000000000000 public
137 pre-close-tip:000000000000 public
138 postclose-tip:24b6387c8c8c public
138 postclose-tip:24b6387c8c8c public
139 txnclose hook: HG_NEW_OBSMARKERS=0 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
139 txnclose hook: HG_NEW_OBSMARKERS=0 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
140 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
140 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
141 $ hg -R other log -G
141 $ hg -R other log -G
142 o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
142 o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
143 |
143 |
144 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
144 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
145 |/
145 |/
146 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
146 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
147
147
148 $ hg -R other debugobsolete
148 $ hg -R other debugobsolete
149 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
149 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
150 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
150 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
151
151
152 pull empty
152 pull empty
153
153
154 $ hg -R other pull -r 24b6387c8c8c
154 $ hg -R other pull -r 24b6387c8c8c
155 pulling from $TESTTMP/main (glob)
155 pulling from $TESTTMP/main (glob)
156 no changes found
156 no changes found
157 pre-close-tip:24b6387c8c8c public
157 pre-close-tip:24b6387c8c8c public
158 postclose-tip:24b6387c8c8c public
158 postclose-tip:24b6387c8c8c public
159 txnclose hook: HG_NEW_OBSMARKERS=0 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
159 txnclose hook: HG_NEW_OBSMARKERS=0 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
160 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
160 file:/*/$TESTTMP/main HG_URL=file:$TESTTMP/main (glob)
161 $ hg -R other log -G
161 $ hg -R other log -G
162 o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
162 o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
163 |
163 |
164 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
164 | @ 1:9520eea781bc draft Nicolas Dumazet <nicdumz.commits@gmail.com> E
165 |/
165 |/
166 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
166 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
167
167
168 $ hg -R other debugobsolete
168 $ hg -R other debugobsolete
169 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
169 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
170 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
170 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
171
171
172 add extra data to test their exchange during push
172 add extra data to test their exchange during push
173
173
174 $ hg -R main bookmark --rev eea13746799a book_eea1
174 $ hg -R main bookmark --rev eea13746799a book_eea1
175 $ hg -R main debugobsolete -d '0 0' 3333333333333333333333333333333333333333 `getmainid eea13746799a`
175 $ hg -R main debugobsolete -d '0 0' 3333333333333333333333333333333333333333 `getmainid eea13746799a`
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_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
178 txnclose hook: HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
179 $ hg -R main bookmark --rev 02de42196ebe book_02de
179 $ hg -R main bookmark --rev 02de42196ebe book_02de
180 $ hg -R main debugobsolete -d '0 0' 4444444444444444444444444444444444444444 `getmainid 02de42196ebe`
180 $ hg -R main debugobsolete -d '0 0' 4444444444444444444444444444444444444444 `getmainid 02de42196ebe`
181 pre-close-tip:02de42196ebe draft book_02de
181 pre-close-tip:02de42196ebe draft book_02de
182 postclose-tip:02de42196ebe draft book_02de
182 postclose-tip:02de42196ebe draft book_02de
183 txnclose hook: HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
183 txnclose hook: HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
184 $ hg -R main bookmark --rev 42ccdea3bb16 book_42cc
184 $ hg -R main bookmark --rev 42ccdea3bb16 book_42cc
185 $ hg -R main debugobsolete -d '0 0' 5555555555555555555555555555555555555555 `getmainid 42ccdea3bb16`
185 $ hg -R main debugobsolete -d '0 0' 5555555555555555555555555555555555555555 `getmainid 42ccdea3bb16`
186 pre-close-tip:02de42196ebe draft book_02de
186 pre-close-tip:02de42196ebe draft book_02de
187 postclose-tip:02de42196ebe draft book_02de
187 postclose-tip:02de42196ebe draft book_02de
188 txnclose hook: HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
188 txnclose hook: HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
189 $ hg -R main bookmark --rev 5fddd98957c8 book_5fdd
189 $ hg -R main bookmark --rev 5fddd98957c8 book_5fdd
190 $ hg -R main debugobsolete -d '0 0' 6666666666666666666666666666666666666666 `getmainid 5fddd98957c8`
190 $ hg -R main debugobsolete -d '0 0' 6666666666666666666666666666666666666666 `getmainid 5fddd98957c8`
191 pre-close-tip:02de42196ebe draft book_02de
191 pre-close-tip:02de42196ebe draft book_02de
192 postclose-tip:02de42196ebe draft book_02de
192 postclose-tip:02de42196ebe draft book_02de
193 txnclose hook: HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
193 txnclose hook: HG_NEW_OBSMARKERS=1 HG_TXNID=TXN:* HG_TXNNAME=debugobsolete (glob)
194 $ hg -R main bookmark --rev 32af7686d403 book_32af
194 $ hg -R main bookmark --rev 32af7686d403 book_32af
195 $ hg -R main debugobsolete -d '0 0' 7777777777777777777777777777777777777777 `getmainid 32af7686d403`
195 $ hg -R main debugobsolete -d '0 0' 7777777777777777777777777777777777777777 `getmainid 32af7686d403`
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
199
200 $ hg -R other bookmark --rev cd010b8cd998 book_eea1
200 $ hg -R other bookmark --rev cd010b8cd998 book_eea1
201 $ hg -R other bookmark --rev cd010b8cd998 book_02de
201 $ hg -R other bookmark --rev cd010b8cd998 book_02de
202 $ hg -R other bookmark --rev cd010b8cd998 book_42cc
202 $ hg -R other bookmark --rev cd010b8cd998 book_42cc
203 $ hg -R other bookmark --rev cd010b8cd998 book_5fdd
203 $ hg -R other bookmark --rev cd010b8cd998 book_5fdd
204 $ hg -R other bookmark --rev cd010b8cd998 book_32af
204 $ hg -R other bookmark --rev cd010b8cd998 book_32af
205
205
206 $ hg -R main phase --public eea13746799a
206 $ hg -R main phase --public eea13746799a
207 pre-close-tip:000000000000 public
207 pre-close-tip:000000000000 public
208 postclose-tip:02de42196ebe draft book_02de
208 postclose-tip:02de42196ebe draft book_02de
209 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=phase (glob)
209 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=phase (glob)
210
210
211 push
211 push
212 $ hg -R main push other --rev eea13746799a --bookmark book_eea1
212 $ hg -R main push other --rev eea13746799a --bookmark book_eea1
213 pushing to other
213 pushing to other
214 searching for changes
214 searching for changes
215 pre-close-tip:eea13746799a public book_eea1
215 pre-close-tip:eea13746799a public book_eea1
216 pushkey: lock state after "phases"
216 pushkey: lock state after "phases"
217 lock: free
217 lock: free
218 wlock: free
218 wlock: free
219 pushkey: lock state after "bookmarks"
219 pushkey: lock state after "bookmarks"
220 lock: free
220 lock: free
221 wlock: free
221 wlock: free
222 postclose-tip:eea13746799a public book_eea1
222 postclose-tip:eea13746799a public book_eea1
223 txnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_NEW_OBSMARKERS=1 HG_NODE=eea13746799a9e0bfd88f29d3c2e9dc9389f524f HG_PHASES_MOVED=1 HG_SOURCE=push HG_TXNID=TXN:* HG_TXNNAME=push HG_URL=push (glob)
223 txnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_NEW_OBSMARKERS=1 HG_NODE=eea13746799a9e0bfd88f29d3c2e9dc9389f524f HG_PHASES_MOVED=1 HG_SOURCE=push HG_TXNID=TXN:* HG_TXNNAME=push HG_URL=push (glob)
224 remote: adding changesets
224 remote: adding changesets
225 remote: adding manifests
225 remote: adding manifests
226 remote: adding file changes
226 remote: adding file changes
227 remote: added 1 changesets with 0 changes to 0 files (-1 heads)
227 remote: added 1 changesets with 0 changes to 0 files (-1 heads)
228 remote: 1 new obsolescence markers
228 remote: 1 new obsolescence markers
229 updating bookmark book_eea1
229 updating bookmark book_eea1
230 pre-close-tip:02de42196ebe draft book_02de
230 pre-close-tip:02de42196ebe draft book_02de
231 postclose-tip:02de42196ebe draft book_02de
231 postclose-tip:02de42196ebe draft book_02de
232 txnclose hook: HG_SOURCE=push-response HG_TXNID=TXN:* HG_TXNNAME=push-response (glob)
232 txnclose hook: HG_SOURCE=push-response HG_TXNID=TXN:* HG_TXNNAME=push-response (glob)
233 file:/*/$TESTTMP/other HG_URL=file:$TESTTMP/other (glob)
233 file:/*/$TESTTMP/other HG_URL=file:$TESTTMP/other (glob)
234 $ hg -R other log -G
234 $ hg -R other log -G
235 o 3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> book_eea1 G
235 o 3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> book_eea1 G
236 |\
236 |\
237 | o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
237 | o 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
238 | |
238 | |
239 @ | 1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
239 @ | 1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
240 |/
240 |/
241 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_02de book_32af book_42cc book_5fdd A
241 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_02de book_32af book_42cc book_5fdd A
242
242
243 $ hg -R other debugobsolete
243 $ hg -R other debugobsolete
244 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
244 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
245 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
245 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
246 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
246 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
247
247
248 pull over ssh
248 pull over ssh
249
249
250 $ hg -R other pull ssh://user@dummy/main -r 02de42196ebe --bookmark book_02de
250 $ hg -R other pull ssh://user@dummy/main -r 02de42196ebe --bookmark book_02de
251 pulling from ssh://user@dummy/main
251 pulling from ssh://user@dummy/main
252 searching for changes
252 searching for changes
253 adding changesets
253 adding changesets
254 adding manifests
254 adding manifests
255 adding file changes
255 adding file changes
256 added 1 changesets with 1 changes to 1 files (+1 heads)
256 added 1 changesets with 1 changes to 1 files (+1 heads)
257 1 new obsolescence markers
257 1 new obsolescence markers
258 updating bookmark book_02de
258 updating bookmark book_02de
259 pre-close-tip:02de42196ebe draft book_02de
259 pre-close-tip:02de42196ebe draft book_02de
260 postclose-tip:02de42196ebe draft book_02de
260 postclose-tip:02de42196ebe draft book_02de
261 txnclose hook: HG_BOOKMARK_MOVED=1 HG_NEW_OBSMARKERS=1 HG_NODE=02de42196ebee42ef284b6780a87cdc96e8eaab6 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
261 txnclose hook: HG_BOOKMARK_MOVED=1 HG_NEW_OBSMARKERS=1 HG_NODE=02de42196ebee42ef284b6780a87cdc96e8eaab6 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
262 ssh://user@dummy/main HG_URL=ssh://user@dummy/main
262 ssh://user@dummy/main HG_URL=ssh://user@dummy/main
263 (run 'hg heads' to see heads, 'hg merge' to merge)
263 (run 'hg heads' to see heads, 'hg merge' to merge)
264 $ hg -R other debugobsolete
264 $ hg -R other debugobsolete
265 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
265 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
266 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
266 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
267 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
267 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
268 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
268 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
269
269
270 pull over http
270 pull over http
271
271
272 $ hg -R main serve -p $HGPORT -d --pid-file=main.pid -E main-error.log
272 $ hg -R main serve -p $HGPORT -d --pid-file=main.pid -E main-error.log
273 $ cat main.pid >> $DAEMON_PIDS
273 $ cat main.pid >> $DAEMON_PIDS
274
274
275 $ hg -R other pull http://localhost:$HGPORT/ -r 42ccdea3bb16 --bookmark book_42cc
275 $ hg -R other pull http://localhost:$HGPORT/ -r 42ccdea3bb16 --bookmark book_42cc
276 pulling from http://localhost:$HGPORT/
276 pulling from http://localhost:$HGPORT/
277 searching for changes
277 searching for changes
278 adding changesets
278 adding changesets
279 adding manifests
279 adding manifests
280 adding file changes
280 adding file changes
281 added 1 changesets with 1 changes to 1 files (+1 heads)
281 added 1 changesets with 1 changes to 1 files (+1 heads)
282 1 new obsolescence markers
282 1 new obsolescence markers
283 updating bookmark book_42cc
283 updating bookmark book_42cc
284 pre-close-tip:42ccdea3bb16 draft book_42cc
284 pre-close-tip:42ccdea3bb16 draft book_42cc
285 postclose-tip:42ccdea3bb16 draft book_42cc
285 postclose-tip:42ccdea3bb16 draft book_42cc
286 txnclose hook: HG_BOOKMARK_MOVED=1 HG_NEW_OBSMARKERS=1 HG_NODE=42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
286 txnclose hook: HG_BOOKMARK_MOVED=1 HG_NEW_OBSMARKERS=1 HG_NODE=42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 HG_PHASES_MOVED=1 HG_SOURCE=pull HG_TXNID=TXN:* HG_TXNNAME=pull (glob)
287 http://localhost:$HGPORT/ HG_URL=http://localhost:$HGPORT/
287 http://localhost:$HGPORT/ HG_URL=http://localhost:$HGPORT/
288 (run 'hg heads .' to see heads, 'hg merge' to merge)
288 (run 'hg heads .' to see heads, 'hg merge' to merge)
289 $ cat main-error.log
289 $ cat main-error.log
290 $ hg -R other debugobsolete
290 $ hg -R other debugobsolete
291 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
291 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
292 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
292 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
293 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
293 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
294 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
294 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
295 5555555555555555555555555555555555555555 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
295 5555555555555555555555555555555555555555 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
296
296
297 push over ssh
297 push over ssh
298
298
299 $ hg -R main push ssh://user@dummy/other -r 5fddd98957c8 --bookmark book_5fdd
299 $ hg -R main push ssh://user@dummy/other -r 5fddd98957c8 --bookmark book_5fdd
300 pushing to ssh://user@dummy/other
300 pushing to ssh://user@dummy/other
301 searching for changes
301 searching for changes
302 remote: adding changesets
302 remote: adding changesets
303 remote: adding manifests
303 remote: adding manifests
304 remote: adding file changes
304 remote: adding file changes
305 remote: added 1 changesets with 1 changes to 1 files
305 remote: added 1 changesets with 1 changes to 1 files
306 remote: 1 new obsolescence markers
306 remote: 1 new obsolescence markers
307 updating bookmark book_5fdd
307 updating bookmark book_5fdd
308 remote: pre-close-tip:5fddd98957c8 draft book_5fdd
308 remote: pre-close-tip:5fddd98957c8 draft book_5fdd
309 remote: pushkey: lock state after "bookmarks"
309 remote: pushkey: lock state after "bookmarks"
310 remote: lock: free
310 remote: lock: free
311 remote: wlock: free
311 remote: wlock: free
312 remote: postclose-tip:5fddd98957c8 draft book_5fdd
312 remote: postclose-tip:5fddd98957c8 draft book_5fdd
313 remote: txnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_NEW_OBSMARKERS=1 HG_NODE=5fddd98957c8a54a4d436dfe1da9d87f21a1b97b HG_SOURCE=serve HG_TXNID=TXN:* HG_TXNNAME=serve HG_URL=remote:ssh:127.0.0.1 (glob)
313 remote: txnclose hook: HG_BOOKMARK_MOVED=1 HG_BUNDLE2=1 HG_NEW_OBSMARKERS=1 HG_NODE=5fddd98957c8a54a4d436dfe1da9d87f21a1b97b HG_SOURCE=serve HG_TXNID=TXN:* HG_TXNNAME=serve HG_URL=remote:ssh:127.0.0.1 (glob)
314 pre-close-tip:02de42196ebe draft book_02de
314 pre-close-tip:02de42196ebe draft book_02de
315 postclose-tip:02de42196ebe draft book_02de
315 postclose-tip:02de42196ebe draft book_02de
316 txnclose hook: HG_SOURCE=push-response HG_TXNID=TXN:* HG_TXNNAME=push-response (glob)
316 txnclose hook: HG_SOURCE=push-response HG_TXNID=TXN:* HG_TXNNAME=push-response (glob)
317 ssh://user@dummy/other HG_URL=ssh://user@dummy/other
317 ssh://user@dummy/other HG_URL=ssh://user@dummy/other
318 $ hg -R other log -G
318 $ hg -R other log -G
319 o 6:5fddd98957c8 draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_5fdd C
319 o 6:5fddd98957c8 draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_5fdd C
320 |
320 |
321 o 5:42ccdea3bb16 draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_42cc B
321 o 5:42ccdea3bb16 draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_42cc B
322 |
322 |
323 | o 4:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_02de H
323 | o 4:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_02de H
324 | |
324 | |
325 | | o 3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> book_eea1 G
325 | | o 3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> book_eea1 G
326 | |/|
326 | |/|
327 | o | 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
327 | o | 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
328 |/ /
328 |/ /
329 | @ 1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
329 | @ 1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
330 |/
330 |/
331 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_32af A
331 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_32af A
332
332
333 $ hg -R other debugobsolete
333 $ hg -R other debugobsolete
334 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
334 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
335 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
335 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
336 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
336 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
337 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
337 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
338 5555555555555555555555555555555555555555 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
338 5555555555555555555555555555555555555555 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
339 6666666666666666666666666666666666666666 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
339 6666666666666666666666666666666666666666 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
340
340
341 push over http
341 push over http
342
342
343 $ hg -R other serve -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
343 $ hg -R other serve -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
344 $ cat other.pid >> $DAEMON_PIDS
344 $ cat other.pid >> $DAEMON_PIDS
345
345
346 $ hg -R main phase --public 32af7686d403
346 $ hg -R main phase --public 32af7686d403
347 pre-close-tip:000000000000 public
347 pre-close-tip:000000000000 public
348 postclose-tip:02de42196ebe draft book_02de
348 postclose-tip:02de42196ebe draft book_02de
349 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=phase (glob)
349 txnclose hook: HG_PHASES_MOVED=1 HG_TXNID=TXN:* HG_TXNNAME=phase (glob)
350 $ hg -R main push http://localhost:$HGPORT2/ -r 32af7686d403 --bookmark book_32af
350 $ hg -R main push http://localhost:$HGPORT2/ -r 32af7686d403 --bookmark book_32af
351 pushing to http://localhost:$HGPORT2/
351 pushing to http://localhost:$HGPORT2/
352 searching for changes
352 searching for changes
353 remote: adding changesets
353 remote: adding changesets
354 remote: adding manifests
354 remote: adding manifests
355 remote: adding file changes
355 remote: adding file changes
356 remote: added 1 changesets with 1 changes to 1 files
356 remote: added 1 changesets with 1 changes to 1 files
357 remote: 1 new obsolescence markers
357 remote: 1 new obsolescence markers
358 updating bookmark book_32af
358 updating bookmark book_32af
359 pre-close-tip:02de42196ebe draft book_02de
359 pre-close-tip:02de42196ebe draft book_02de
360 postclose-tip:02de42196ebe draft book_02de
360 postclose-tip:02de42196ebe draft book_02de
361 txnclose hook: HG_SOURCE=push-response HG_TXNID=TXN:* HG_TXNNAME=push-response (glob)
361 txnclose hook: HG_SOURCE=push-response HG_TXNID=TXN:* HG_TXNNAME=push-response (glob)
362 http://localhost:$HGPORT2/ HG_URL=http://localhost:$HGPORT2/
362 http://localhost:$HGPORT2/ HG_URL=http://localhost:$HGPORT2/
363 $ cat other-error.log
363 $ cat other-error.log
364
364
365 Check final content.
365 Check final content.
366
366
367 $ hg -R other log -G
367 $ hg -R other log -G
368 o 7:32af7686d403 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_32af D
368 o 7:32af7686d403 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_32af D
369 |
369 |
370 o 6:5fddd98957c8 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_5fdd C
370 o 6:5fddd98957c8 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_5fdd C
371 |
371 |
372 o 5:42ccdea3bb16 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_42cc B
372 o 5:42ccdea3bb16 public Nicolas Dumazet <nicdumz.commits@gmail.com> book_42cc B
373 |
373 |
374 | o 4:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_02de H
374 | o 4:02de42196ebe draft Nicolas Dumazet <nicdumz.commits@gmail.com> book_02de H
375 | |
375 | |
376 | | o 3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> book_eea1 G
376 | | o 3:eea13746799a public Nicolas Dumazet <nicdumz.commits@gmail.com> book_eea1 G
377 | |/|
377 | |/|
378 | o | 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
378 | o | 2:24b6387c8c8c public Nicolas Dumazet <nicdumz.commits@gmail.com> F
379 |/ /
379 |/ /
380 | @ 1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
380 | @ 1:9520eea781bc public Nicolas Dumazet <nicdumz.commits@gmail.com> E
381 |/
381 |/
382 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
382 o 0:cd010b8cd998 public Nicolas Dumazet <nicdumz.commits@gmail.com> A
383
383
384 $ hg -R other debugobsolete
384 $ hg -R other debugobsolete
385 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
385 1111111111111111111111111111111111111111 9520eea781bcca16c1e15acc0ba14335a0e8e5ba 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
386 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
386 2222222222222222222222222222222222222222 24b6387c8c8cae37178880f3fa95ded3cb1cf785 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
387 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
387 3333333333333333333333333333333333333333 eea13746799a9e0bfd88f29d3c2e9dc9389f524f 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
388 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
388 4444444444444444444444444444444444444444 02de42196ebee42ef284b6780a87cdc96e8eaab6 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
389 5555555555555555555555555555555555555555 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
389 5555555555555555555555555555555555555555 42ccdea3bb16d28e1848c95fe2e44c000f3f21b1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
390 6666666666666666666666666666666666666666 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
390 6666666666666666666666666666666666666666 5fddd98957c8a54a4d436dfe1da9d87f21a1b97b 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
391 7777777777777777777777777777777777777777 32af7686d403cf45b5d95f2d70cebea587ac806a 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
391 7777777777777777777777777777777777777777 32af7686d403cf45b5d95f2d70cebea587ac806a 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
392
392
393 (check that no 'pending' files remain)
393 (check that no 'pending' files remain)
394
394
395 $ ls -1 other/.hg/bookmarks*
395 $ ls -1 other/.hg/bookmarks*
396 other/.hg/bookmarks
396 other/.hg/bookmarks
397 $ ls -1 other/.hg/store/phaseroots*
397 $ ls -1 other/.hg/store/phaseroots*
398 other/.hg/store/phaseroots
398 other/.hg/store/phaseroots
399 $ ls -1 other/.hg/store/00changelog.i*
399 $ ls -1 other/.hg/store/00changelog.i*
400 other/.hg/store/00changelog.i
400 other/.hg/store/00changelog.i
401
401
402 Error Handling
402 Error Handling
403 ==============
403 ==============
404
404
405 Check that errors are properly returned to the client during push.
405 Check that errors are properly returned to the client during push.
406
406
407 Setting up
407 Setting up
408
408
409 $ cat > failpush.py << EOF
409 $ cat > failpush.py << EOF
410 > """A small extension that makes push fails when using bundle2
410 > """A small extension that makes push fails when using bundle2
411 >
411 >
412 > used to test error handling in bundle2
412 > used to test error handling in bundle2
413 > """
413 > """
414 >
414 >
415 > from mercurial import util
415 > from mercurial import util
416 > from mercurial import bundle2
416 > from mercurial import bundle2
417 > from mercurial import exchange
417 > from mercurial import exchange
418 > from mercurial import extensions
418 > from mercurial import extensions
419 >
419 >
420 > def _pushbundle2failpart(pushop, bundler):
420 > def _pushbundle2failpart(pushop, bundler):
421 > reason = pushop.ui.config('failpush', 'reason', None)
421 > reason = pushop.ui.config('failpush', 'reason', None)
422 > part = None
422 > part = None
423 > if reason == 'abort':
423 > if reason == 'abort':
424 > bundler.newpart('test:abort')
424 > bundler.newpart('test:abort')
425 > if reason == 'unknown':
425 > if reason == 'unknown':
426 > bundler.newpart('test:unknown')
426 > bundler.newpart('test:unknown')
427 > if reason == 'race':
427 > if reason == 'race':
428 > # 20 Bytes of crap
428 > # 20 Bytes of crap
429 > bundler.newpart('check:heads', data='01234567890123456789')
429 > bundler.newpart('check:heads', data='01234567890123456789')
430 >
430 >
431 > @bundle2.parthandler("test:abort")
431 > @bundle2.parthandler("test:abort")
432 > def handleabort(op, part):
432 > def handleabort(op, part):
433 > raise util.Abort('Abandon ship!', hint="don't panic")
433 > raise util.Abort('Abandon ship!', hint="don't panic")
434 >
434 >
435 > def uisetup(ui):
435 > def uisetup(ui):
436 > exchange.b2partsgenmapping['failpart'] = _pushbundle2failpart
436 > exchange.b2partsgenmapping['failpart'] = _pushbundle2failpart
437 > exchange.b2partsgenorder.insert(0, 'failpart')
437 > exchange.b2partsgenorder.insert(0, 'failpart')
438 >
438 >
439 > EOF
439 > EOF
440
440
441 $ cd main
441 $ cd main
442 $ hg up tip
442 $ hg up tip
443 3 files updated, 0 files merged, 1 files removed, 0 files unresolved
443 3 files updated, 0 files merged, 1 files removed, 0 files unresolved
444 $ echo 'I' > I
444 $ echo 'I' > I
445 $ hg add I
445 $ hg add I
446 $ hg ci -m 'I'
446 $ hg ci -m 'I'
447 pre-close-tip:e7ec4e813ba6 draft
447 pre-close-tip:e7ec4e813ba6 draft
448 postclose-tip:e7ec4e813ba6 draft
448 postclose-tip:e7ec4e813ba6 draft
449 txnclose hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
449 txnclose hook: HG_TXNID=TXN:* HG_TXNNAME=commit (glob)
450 $ hg id
450 $ hg id
451 e7ec4e813ba6 tip
451 e7ec4e813ba6 tip
452 $ cd ..
452 $ cd ..
453
453
454 $ cat << EOF >> $HGRCPATH
454 $ cat << EOF >> $HGRCPATH
455 > [extensions]
455 > [extensions]
456 > failpush=$TESTTMP/failpush.py
456 > failpush=$TESTTMP/failpush.py
457 > EOF
457 > EOF
458
458
459 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
459 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
460 $ hg -R other serve -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
460 $ hg -R other serve -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
461 $ cat other.pid >> $DAEMON_PIDS
461 $ cat other.pid >> $DAEMON_PIDS
462
462
463 Doing the actual push: Abort error
463 Doing the actual push: Abort error
464
464
465 $ cat << EOF >> $HGRCPATH
465 $ cat << EOF >> $HGRCPATH
466 > [failpush]
466 > [failpush]
467 > reason = abort
467 > reason = abort
468 > EOF
468 > EOF
469
469
470 $ hg -R main push other -r e7ec4e813ba6
470 $ hg -R main push other -r e7ec4e813ba6
471 pushing to other
471 pushing to other
472 searching for changes
472 searching for changes
473 abort: Abandon ship!
473 abort: Abandon ship!
474 (don't panic)
474 (don't panic)
475 [255]
475 [255]
476
476
477 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
477 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
478 pushing to ssh://user@dummy/other
478 pushing to ssh://user@dummy/other
479 searching for changes
479 searching for changes
480 abort: Abandon ship!
480 abort: Abandon ship!
481 (don't panic)
481 (don't panic)
482 [255]
482 [255]
483
483
484 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
484 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
485 pushing to http://localhost:$HGPORT2/
485 pushing to http://localhost:$HGPORT2/
486 searching for changes
486 searching for changes
487 abort: Abandon ship!
487 abort: Abandon ship!
488 (don't panic)
488 (don't panic)
489 [255]
489 [255]
490
490
491
491
492 Doing the actual push: unknown mandatory parts
492 Doing the actual push: unknown mandatory parts
493
493
494 $ cat << EOF >> $HGRCPATH
494 $ cat << EOF >> $HGRCPATH
495 > [failpush]
495 > [failpush]
496 > reason = unknown
496 > reason = unknown
497 > EOF
497 > EOF
498
498
499 $ hg -R main push other -r e7ec4e813ba6
499 $ hg -R main push other -r e7ec4e813ba6
500 pushing to other
500 pushing to other
501 searching for changes
501 searching for changes
502 abort: missing support for test:unknown
502 abort: missing support for test:unknown
503 [255]
503 [255]
504
504
505 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
505 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
506 pushing to ssh://user@dummy/other
506 pushing to ssh://user@dummy/other
507 searching for changes
507 searching for changes
508 abort: missing support for test:unknown
508 abort: missing support for test:unknown
509 [255]
509 [255]
510
510
511 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
511 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
512 pushing to http://localhost:$HGPORT2/
512 pushing to http://localhost:$HGPORT2/
513 searching for changes
513 searching for changes
514 abort: missing support for test:unknown
514 abort: missing support for test:unknown
515 [255]
515 [255]
516
516
517 Doing the actual push: race
517 Doing the actual push: race
518
518
519 $ cat << EOF >> $HGRCPATH
519 $ cat << EOF >> $HGRCPATH
520 > [failpush]
520 > [failpush]
521 > reason = race
521 > reason = race
522 > EOF
522 > EOF
523
523
524 $ hg -R main push other -r e7ec4e813ba6
524 $ hg -R main push other -r e7ec4e813ba6
525 pushing to other
525 pushing to other
526 searching for changes
526 searching for changes
527 abort: push failed:
527 abort: push failed:
528 'repository changed while pushing - please try again'
528 'repository changed while pushing - please try again'
529 [255]
529 [255]
530
530
531 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
531 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
532 pushing to ssh://user@dummy/other
532 pushing to ssh://user@dummy/other
533 searching for changes
533 searching for changes
534 abort: push failed:
534 abort: push failed:
535 'repository changed while pushing - please try again'
535 'repository changed while pushing - please try again'
536 [255]
536 [255]
537
537
538 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
538 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
539 pushing to http://localhost:$HGPORT2/
539 pushing to http://localhost:$HGPORT2/
540 searching for changes
540 searching for changes
541 abort: push failed:
541 abort: push failed:
542 'repository changed while pushing - please try again'
542 'repository changed while pushing - please try again'
543 [255]
543 [255]
544
544
545 Doing the actual push: hook abort
545 Doing the actual push: hook abort
546
546
547 $ cat << EOF >> $HGRCPATH
547 $ cat << EOF >> $HGRCPATH
548 > [failpush]
548 > [failpush]
549 > reason =
549 > reason =
550 > [hooks]
550 > [hooks]
551 > pretxnclose.failpush = false
551 > pretxnclose.failpush = false
552 > EOF
552 > EOF
553
553
554 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
554 $ "$TESTDIR/killdaemons.py" $DAEMON_PIDS
555 $ hg -R other serve -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
555 $ hg -R other serve -p $HGPORT2 -d --pid-file=other.pid -E other-error.log
556 $ cat other.pid >> $DAEMON_PIDS
556 $ cat other.pid >> $DAEMON_PIDS
557
557
558 $ hg -R main push other -r e7ec4e813ba6
558 $ hg -R main push other -r e7ec4e813ba6
559 pushing to other
559 pushing to other
560 searching for changes
560 searching for changes
561 pre-close-tip:e7ec4e813ba6 draft
561 pre-close-tip:e7ec4e813ba6 draft
562 transaction abort!
562 transaction abort!
563 rollback completed
563 rollback completed
564 abort: pretxnclose.failpush hook exited with status 1
564 abort: pretxnclose.failpush hook exited with status 1
565 [255]
565 [255]
566
566
567 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
567 $ hg -R main push ssh://user@dummy/other -r e7ec4e813ba6
568 pushing to ssh://user@dummy/other
568 pushing to ssh://user@dummy/other
569 searching for changes
569 searching for changes
570 remote: adding changesets
571 remote: adding manifests
572 remote: adding file changes
573 remote: added 1 changesets with 1 changes to 1 files
570 abort: pretxnclose.failpush hook exited with status 1
574 abort: pretxnclose.failpush hook exited with status 1
571 remote: pre-close-tip:e7ec4e813ba6 draft
575 remote: pre-close-tip:e7ec4e813ba6 draft
572 remote: transaction abort!
576 remote: transaction abort!
573 remote: rollback completed
577 remote: rollback completed
574 [255]
578 [255]
575
579
576 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
580 $ hg -R main push http://localhost:$HGPORT2/ -r e7ec4e813ba6
577 pushing to http://localhost:$HGPORT2/
581 pushing to http://localhost:$HGPORT2/
578 searching for changes
582 searching for changes
583 remote: adding changesets
584 remote: adding manifests
585 remote: adding file changes
586 remote: added 1 changesets with 1 changes to 1 files
579 abort: pretxnclose.failpush hook exited with status 1
587 abort: pretxnclose.failpush hook exited with status 1
580 [255]
588 [255]
581
589
582 (check that no 'pending' files remain)
590 (check that no 'pending' files remain)
583
591
584 $ ls -1 other/.hg/bookmarks*
592 $ ls -1 other/.hg/bookmarks*
585 other/.hg/bookmarks
593 other/.hg/bookmarks
586 $ ls -1 other/.hg/store/phaseroots*
594 $ ls -1 other/.hg/store/phaseroots*
587 other/.hg/store/phaseroots
595 other/.hg/store/phaseroots
588 $ ls -1 other/.hg/store/00changelog.i*
596 $ ls -1 other/.hg/store/00changelog.i*
589 other/.hg/store/00changelog.i
597 other/.hg/store/00changelog.i
590
598
General Comments 0
You need to be logged in to leave comments. Login now