##// END OF EJS Templates
wireproto: enable optional args for known() for future extensibility...
Peter Arrenbrecht -
r14436:5adb5252 default
parent child Browse files
Show More
@@ -1,421 +1,421 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
11 import changegroup as changegroupmod
12 import repo, error, encoding, util, store
12 import repo, error, encoding, util, store
13 import pushkey as pushkeymod
13 import pushkey as pushkeymod
14
14
15 # list of nodes encoding / decoding
15 # list of nodes encoding / decoding
16
16
17 def decodelist(l, sep=' '):
17 def decodelist(l, sep=' '):
18 if l:
18 if l:
19 return map(bin, l.split(sep))
19 return map(bin, l.split(sep))
20 return []
20 return []
21
21
22 def encodelist(l, sep=' '):
22 def encodelist(l, sep=' '):
23 return sep.join(map(hex, l))
23 return sep.join(map(hex, l))
24
24
25 # client side
25 # client side
26
26
27 class wirerepository(repo.repository):
27 class wirerepository(repo.repository):
28 def lookup(self, key):
28 def lookup(self, key):
29 self.requirecap('lookup', _('look up remote revision'))
29 self.requirecap('lookup', _('look up remote revision'))
30 d = self._call("lookup", key=encoding.fromlocal(key))
30 d = self._call("lookup", key=encoding.fromlocal(key))
31 success, data = d[:-1].split(" ", 1)
31 success, data = d[:-1].split(" ", 1)
32 if int(success):
32 if int(success):
33 return bin(data)
33 return bin(data)
34 self._abort(error.RepoError(data))
34 self._abort(error.RepoError(data))
35
35
36 def heads(self):
36 def heads(self):
37 d = self._call("heads")
37 d = self._call("heads")
38 try:
38 try:
39 return decodelist(d[:-1])
39 return decodelist(d[:-1])
40 except ValueError:
40 except ValueError:
41 self._abort(error.ResponseError(_("unexpected response:"), d))
41 self._abort(error.ResponseError(_("unexpected response:"), d))
42
42
43 def known(self, nodes):
43 def known(self, nodes):
44 n = encodelist(nodes)
44 n = encodelist(nodes)
45 d = self._call("known", nodes=n)
45 d = self._call("known", nodes=n)
46 try:
46 try:
47 return [bool(int(f)) for f in d]
47 return [bool(int(f)) for f in d]
48 except ValueError:
48 except ValueError:
49 self._abort(error.ResponseError(_("unexpected response:"), d))
49 self._abort(error.ResponseError(_("unexpected response:"), d))
50
50
51 def branchmap(self):
51 def branchmap(self):
52 d = self._call("branchmap")
52 d = self._call("branchmap")
53 try:
53 try:
54 branchmap = {}
54 branchmap = {}
55 for branchpart in d.splitlines():
55 for branchpart in d.splitlines():
56 branchname, branchheads = branchpart.split(' ', 1)
56 branchname, branchheads = branchpart.split(' ', 1)
57 branchname = encoding.tolocal(urllib.unquote(branchname))
57 branchname = encoding.tolocal(urllib.unquote(branchname))
58 branchheads = decodelist(branchheads)
58 branchheads = decodelist(branchheads)
59 branchmap[branchname] = branchheads
59 branchmap[branchname] = branchheads
60 return branchmap
60 return branchmap
61 except TypeError:
61 except TypeError:
62 self._abort(error.ResponseError(_("unexpected response:"), d))
62 self._abort(error.ResponseError(_("unexpected response:"), d))
63
63
64 def branches(self, nodes):
64 def branches(self, nodes):
65 n = encodelist(nodes)
65 n = encodelist(nodes)
66 d = self._call("branches", nodes=n)
66 d = self._call("branches", nodes=n)
67 try:
67 try:
68 br = [tuple(decodelist(b)) for b in d.splitlines()]
68 br = [tuple(decodelist(b)) for b in d.splitlines()]
69 return br
69 return br
70 except ValueError:
70 except ValueError:
71 self._abort(error.ResponseError(_("unexpected response:"), d))
71 self._abort(error.ResponseError(_("unexpected response:"), d))
72
72
73 def between(self, pairs):
73 def between(self, pairs):
74 batch = 8 # avoid giant requests
74 batch = 8 # avoid giant requests
75 r = []
75 r = []
76 for i in xrange(0, len(pairs), batch):
76 for i in xrange(0, len(pairs), batch):
77 n = " ".join([encodelist(p, '-') for p in pairs[i:i + batch]])
77 n = " ".join([encodelist(p, '-') for p in pairs[i:i + batch]])
78 d = self._call("between", pairs=n)
78 d = self._call("between", pairs=n)
79 try:
79 try:
80 r.extend(l and decodelist(l) or [] for l in d.splitlines())
80 r.extend(l and decodelist(l) or [] for l in d.splitlines())
81 except ValueError:
81 except ValueError:
82 self._abort(error.ResponseError(_("unexpected response:"), d))
82 self._abort(error.ResponseError(_("unexpected response:"), d))
83 return r
83 return r
84
84
85 def pushkey(self, namespace, key, old, new):
85 def pushkey(self, namespace, key, old, new):
86 if not self.capable('pushkey'):
86 if not self.capable('pushkey'):
87 return False
87 return False
88 d = self._call("pushkey",
88 d = self._call("pushkey",
89 namespace=encoding.fromlocal(namespace),
89 namespace=encoding.fromlocal(namespace),
90 key=encoding.fromlocal(key),
90 key=encoding.fromlocal(key),
91 old=encoding.fromlocal(old),
91 old=encoding.fromlocal(old),
92 new=encoding.fromlocal(new))
92 new=encoding.fromlocal(new))
93 try:
93 try:
94 d = bool(int(d))
94 d = bool(int(d))
95 except ValueError:
95 except ValueError:
96 raise error.ResponseError(
96 raise error.ResponseError(
97 _('push failed (unexpected response):'), d)
97 _('push failed (unexpected response):'), d)
98 return d
98 return d
99
99
100 def listkeys(self, namespace):
100 def listkeys(self, namespace):
101 if not self.capable('pushkey'):
101 if not self.capable('pushkey'):
102 return {}
102 return {}
103 d = self._call("listkeys", namespace=encoding.fromlocal(namespace))
103 d = self._call("listkeys", namespace=encoding.fromlocal(namespace))
104 r = {}
104 r = {}
105 for l in d.splitlines():
105 for l in d.splitlines():
106 k, v = l.split('\t')
106 k, v = l.split('\t')
107 r[encoding.tolocal(k)] = encoding.tolocal(v)
107 r[encoding.tolocal(k)] = encoding.tolocal(v)
108 return r
108 return r
109
109
110 def stream_out(self):
110 def stream_out(self):
111 return self._callstream('stream_out')
111 return self._callstream('stream_out')
112
112
113 def changegroup(self, nodes, kind):
113 def changegroup(self, nodes, kind):
114 n = encodelist(nodes)
114 n = encodelist(nodes)
115 f = self._callstream("changegroup", roots=n)
115 f = self._callstream("changegroup", roots=n)
116 return changegroupmod.unbundle10(self._decompress(f), 'UN')
116 return changegroupmod.unbundle10(self._decompress(f), 'UN')
117
117
118 def changegroupsubset(self, bases, heads, kind):
118 def changegroupsubset(self, bases, heads, kind):
119 self.requirecap('changegroupsubset', _('look up remote changes'))
119 self.requirecap('changegroupsubset', _('look up remote changes'))
120 bases = encodelist(bases)
120 bases = encodelist(bases)
121 heads = encodelist(heads)
121 heads = encodelist(heads)
122 f = self._callstream("changegroupsubset",
122 f = self._callstream("changegroupsubset",
123 bases=bases, heads=heads)
123 bases=bases, heads=heads)
124 return changegroupmod.unbundle10(self._decompress(f), 'UN')
124 return changegroupmod.unbundle10(self._decompress(f), 'UN')
125
125
126 def getbundle(self, source, heads=None, common=None):
126 def getbundle(self, source, heads=None, common=None):
127 self.requirecap('getbundle', _('look up remote changes'))
127 self.requirecap('getbundle', _('look up remote changes'))
128 opts = {}
128 opts = {}
129 if heads is not None:
129 if heads is not None:
130 opts['heads'] = encodelist(heads)
130 opts['heads'] = encodelist(heads)
131 if common is not None:
131 if common is not None:
132 opts['common'] = encodelist(common)
132 opts['common'] = encodelist(common)
133 f = self._callstream("getbundle", **opts)
133 f = self._callstream("getbundle", **opts)
134 return changegroupmod.unbundle10(self._decompress(f), 'UN')
134 return changegroupmod.unbundle10(self._decompress(f), 'UN')
135
135
136 def unbundle(self, cg, heads, source):
136 def unbundle(self, cg, heads, source):
137 '''Send cg (a readable file-like object representing the
137 '''Send cg (a readable file-like object representing the
138 changegroup to push, typically a chunkbuffer object) to the
138 changegroup to push, typically a chunkbuffer object) to the
139 remote server as a bundle. Return an integer indicating the
139 remote server as a bundle. Return an integer indicating the
140 result of the push (see localrepository.addchangegroup()).'''
140 result of the push (see localrepository.addchangegroup()).'''
141
141
142 if heads != ['force'] and self.capable('unbundlehash'):
142 if heads != ['force'] and self.capable('unbundlehash'):
143 heads = encodelist(['hashed',
143 heads = encodelist(['hashed',
144 util.sha1(''.join(sorted(heads))).digest()])
144 util.sha1(''.join(sorted(heads))).digest()])
145 else:
145 else:
146 heads = encodelist(heads)
146 heads = encodelist(heads)
147
147
148 ret, output = self._callpush("unbundle", cg, heads=heads)
148 ret, output = self._callpush("unbundle", cg, heads=heads)
149 if ret == "":
149 if ret == "":
150 raise error.ResponseError(
150 raise error.ResponseError(
151 _('push failed:'), output)
151 _('push failed:'), output)
152 try:
152 try:
153 ret = int(ret)
153 ret = int(ret)
154 except ValueError:
154 except ValueError:
155 raise error.ResponseError(
155 raise error.ResponseError(
156 _('push failed (unexpected response):'), ret)
156 _('push failed (unexpected response):'), ret)
157
157
158 for l in output.splitlines(True):
158 for l in output.splitlines(True):
159 self.ui.status(_('remote: '), l)
159 self.ui.status(_('remote: '), l)
160 return ret
160 return ret
161
161
162 def debugwireargs(self, one, two, three=None, four=None, five=None):
162 def debugwireargs(self, one, two, three=None, four=None, five=None):
163 # don't pass optional arguments left at their default value
163 # don't pass optional arguments left at their default value
164 opts = {}
164 opts = {}
165 if three is not None:
165 if three is not None:
166 opts['three'] = three
166 opts['three'] = three
167 if four is not None:
167 if four is not None:
168 opts['four'] = four
168 opts['four'] = four
169 return self._call('debugwireargs', one=one, two=two, **opts)
169 return self._call('debugwireargs', one=one, two=two, **opts)
170
170
171 # server side
171 # server side
172
172
173 class streamres(object):
173 class streamres(object):
174 def __init__(self, gen):
174 def __init__(self, gen):
175 self.gen = gen
175 self.gen = gen
176
176
177 class pushres(object):
177 class pushres(object):
178 def __init__(self, res):
178 def __init__(self, res):
179 self.res = res
179 self.res = res
180
180
181 class pusherr(object):
181 class pusherr(object):
182 def __init__(self, res):
182 def __init__(self, res):
183 self.res = res
183 self.res = res
184
184
185 def dispatch(repo, proto, command):
185 def dispatch(repo, proto, command):
186 func, spec = commands[command]
186 func, spec = commands[command]
187 args = proto.getargs(spec)
187 args = proto.getargs(spec)
188 return func(repo, proto, *args)
188 return func(repo, proto, *args)
189
189
190 def options(cmd, keys, others):
190 def options(cmd, keys, others):
191 opts = {}
191 opts = {}
192 for k in keys:
192 for k in keys:
193 if k in others:
193 if k in others:
194 opts[k] = others[k]
194 opts[k] = others[k]
195 del others[k]
195 del others[k]
196 if others:
196 if others:
197 sys.stderr.write("abort: %s got unexpected arguments %s\n"
197 sys.stderr.write("abort: %s got unexpected arguments %s\n"
198 % (cmd, ",".join(others)))
198 % (cmd, ",".join(others)))
199 return opts
199 return opts
200
200
201 def between(repo, proto, pairs):
201 def between(repo, proto, pairs):
202 pairs = [decodelist(p, '-') for p in pairs.split(" ")]
202 pairs = [decodelist(p, '-') for p in pairs.split(" ")]
203 r = []
203 r = []
204 for b in repo.between(pairs):
204 for b in repo.between(pairs):
205 r.append(encodelist(b) + "\n")
205 r.append(encodelist(b) + "\n")
206 return "".join(r)
206 return "".join(r)
207
207
208 def branchmap(repo, proto):
208 def branchmap(repo, proto):
209 branchmap = repo.branchmap()
209 branchmap = repo.branchmap()
210 heads = []
210 heads = []
211 for branch, nodes in branchmap.iteritems():
211 for branch, nodes in branchmap.iteritems():
212 branchname = urllib.quote(encoding.fromlocal(branch))
212 branchname = urllib.quote(encoding.fromlocal(branch))
213 branchnodes = encodelist(nodes)
213 branchnodes = encodelist(nodes)
214 heads.append('%s %s' % (branchname, branchnodes))
214 heads.append('%s %s' % (branchname, branchnodes))
215 return '\n'.join(heads)
215 return '\n'.join(heads)
216
216
217 def branches(repo, proto, nodes):
217 def branches(repo, proto, nodes):
218 nodes = decodelist(nodes)
218 nodes = decodelist(nodes)
219 r = []
219 r = []
220 for b in repo.branches(nodes):
220 for b in repo.branches(nodes):
221 r.append(encodelist(b) + "\n")
221 r.append(encodelist(b) + "\n")
222 return "".join(r)
222 return "".join(r)
223
223
224 def capabilities(repo, proto):
224 def capabilities(repo, proto):
225 caps = ('lookup changegroupsubset branchmap pushkey known getbundle '
225 caps = ('lookup changegroupsubset branchmap pushkey known getbundle '
226 'unbundlehash').split()
226 'unbundlehash').split()
227 if _allowstream(repo.ui):
227 if _allowstream(repo.ui):
228 requiredformats = repo.requirements & repo.supportedformats
228 requiredformats = repo.requirements & repo.supportedformats
229 # if our local revlogs are just revlogv1, add 'stream' cap
229 # if our local revlogs are just revlogv1, add 'stream' cap
230 if not requiredformats - set(('revlogv1',)):
230 if not requiredformats - set(('revlogv1',)):
231 caps.append('stream')
231 caps.append('stream')
232 # otherwise, add 'streamreqs' detailing our local revlog format
232 # otherwise, add 'streamreqs' detailing our local revlog format
233 else:
233 else:
234 caps.append('streamreqs=%s' % ','.join(requiredformats))
234 caps.append('streamreqs=%s' % ','.join(requiredformats))
235 caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority))
235 caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority))
236 caps.append('httpheader=1024')
236 caps.append('httpheader=1024')
237 return ' '.join(caps)
237 return ' '.join(caps)
238
238
239 def changegroup(repo, proto, roots):
239 def changegroup(repo, proto, roots):
240 nodes = decodelist(roots)
240 nodes = decodelist(roots)
241 cg = repo.changegroup(nodes, 'serve')
241 cg = repo.changegroup(nodes, 'serve')
242 return streamres(proto.groupchunks(cg))
242 return streamres(proto.groupchunks(cg))
243
243
244 def changegroupsubset(repo, proto, bases, heads):
244 def changegroupsubset(repo, proto, bases, heads):
245 bases = decodelist(bases)
245 bases = decodelist(bases)
246 heads = decodelist(heads)
246 heads = decodelist(heads)
247 cg = repo.changegroupsubset(bases, heads, 'serve')
247 cg = repo.changegroupsubset(bases, heads, 'serve')
248 return streamres(proto.groupchunks(cg))
248 return streamres(proto.groupchunks(cg))
249
249
250 def debugwireargs(repo, proto, one, two, others):
250 def debugwireargs(repo, proto, one, two, others):
251 # only accept optional args from the known set
251 # only accept optional args from the known set
252 opts = options('debugwireargs', ['three', 'four'], others)
252 opts = options('debugwireargs', ['three', 'four'], others)
253 return repo.debugwireargs(one, two, **opts)
253 return repo.debugwireargs(one, two, **opts)
254
254
255 def getbundle(repo, proto, others):
255 def getbundle(repo, proto, others):
256 opts = options('getbundle', ['heads', 'common'], others)
256 opts = options('getbundle', ['heads', 'common'], others)
257 for k, v in opts.iteritems():
257 for k, v in opts.iteritems():
258 opts[k] = decodelist(v)
258 opts[k] = decodelist(v)
259 cg = repo.getbundle('serve', **opts)
259 cg = repo.getbundle('serve', **opts)
260 return streamres(proto.groupchunks(cg))
260 return streamres(proto.groupchunks(cg))
261
261
262 def heads(repo, proto):
262 def heads(repo, proto):
263 h = repo.heads()
263 h = repo.heads()
264 return encodelist(h) + "\n"
264 return encodelist(h) + "\n"
265
265
266 def hello(repo, proto):
266 def hello(repo, proto):
267 '''the hello command returns a set of lines describing various
267 '''the hello command returns a set of lines describing various
268 interesting things about the server, in an RFC822-like format.
268 interesting things about the server, in an RFC822-like format.
269 Currently the only one defined is "capabilities", which
269 Currently the only one defined is "capabilities", which
270 consists of a line in the form:
270 consists of a line in the form:
271
271
272 capabilities: space separated list of tokens
272 capabilities: space separated list of tokens
273 '''
273 '''
274 return "capabilities: %s\n" % (capabilities(repo, proto))
274 return "capabilities: %s\n" % (capabilities(repo, proto))
275
275
276 def listkeys(repo, proto, namespace):
276 def listkeys(repo, proto, namespace):
277 d = pushkeymod.list(repo, encoding.tolocal(namespace)).items()
277 d = pushkeymod.list(repo, encoding.tolocal(namespace)).items()
278 t = '\n'.join(['%s\t%s' % (encoding.fromlocal(k), encoding.fromlocal(v))
278 t = '\n'.join(['%s\t%s' % (encoding.fromlocal(k), encoding.fromlocal(v))
279 for k, v in d])
279 for k, v in d])
280 return t
280 return t
281
281
282 def lookup(repo, proto, key):
282 def lookup(repo, proto, key):
283 try:
283 try:
284 r = hex(repo.lookup(encoding.tolocal(key)))
284 r = hex(repo.lookup(encoding.tolocal(key)))
285 success = 1
285 success = 1
286 except Exception, inst:
286 except Exception, inst:
287 r = str(inst)
287 r = str(inst)
288 success = 0
288 success = 0
289 return "%s %s\n" % (success, r)
289 return "%s %s\n" % (success, r)
290
290
291 def known(repo, proto, nodes):
291 def known(repo, proto, nodes, others):
292 return ''.join(b and "1" or "0" for b in repo.known(decodelist(nodes)))
292 return ''.join(b and "1" or "0" for b in repo.known(decodelist(nodes)))
293
293
294 def pushkey(repo, proto, namespace, key, old, new):
294 def pushkey(repo, proto, namespace, key, old, new):
295 # compatibility with pre-1.8 clients which were accidentally
295 # compatibility with pre-1.8 clients which were accidentally
296 # sending raw binary nodes rather than utf-8-encoded hex
296 # sending raw binary nodes rather than utf-8-encoded hex
297 if len(new) == 20 and new.encode('string-escape') != new:
297 if len(new) == 20 and new.encode('string-escape') != new:
298 # looks like it could be a binary node
298 # looks like it could be a binary node
299 try:
299 try:
300 new.decode('utf-8')
300 new.decode('utf-8')
301 new = encoding.tolocal(new) # but cleanly decodes as UTF-8
301 new = encoding.tolocal(new) # but cleanly decodes as UTF-8
302 except UnicodeDecodeError:
302 except UnicodeDecodeError:
303 pass # binary, leave unmodified
303 pass # binary, leave unmodified
304 else:
304 else:
305 new = encoding.tolocal(new) # normal path
305 new = encoding.tolocal(new) # normal path
306
306
307 r = pushkeymod.push(repo,
307 r = pushkeymod.push(repo,
308 encoding.tolocal(namespace), encoding.tolocal(key),
308 encoding.tolocal(namespace), encoding.tolocal(key),
309 encoding.tolocal(old), new)
309 encoding.tolocal(old), new)
310 return '%s\n' % int(r)
310 return '%s\n' % int(r)
311
311
312 def _allowstream(ui):
312 def _allowstream(ui):
313 return ui.configbool('server', 'uncompressed', True, untrusted=True)
313 return ui.configbool('server', 'uncompressed', True, untrusted=True)
314
314
315 def stream(repo, proto):
315 def stream(repo, proto):
316 '''If the server supports streaming clone, it advertises the "stream"
316 '''If the server supports streaming clone, it advertises the "stream"
317 capability with a value representing the version and flags of the repo
317 capability with a value representing the version and flags of the repo
318 it is serving. Client checks to see if it understands the format.
318 it is serving. Client checks to see if it understands the format.
319
319
320 The format is simple: the server writes out a line with the amount
320 The format is simple: the server writes out a line with the amount
321 of files, then the total amount of bytes to be transfered (separated
321 of files, then the total amount of bytes to be transfered (separated
322 by a space). Then, for each file, the server first writes the filename
322 by a space). Then, for each file, the server first writes the filename
323 and filesize (separated by the null character), then the file contents.
323 and filesize (separated by the null character), then the file contents.
324 '''
324 '''
325
325
326 if not _allowstream(repo.ui):
326 if not _allowstream(repo.ui):
327 return '1\n'
327 return '1\n'
328
328
329 entries = []
329 entries = []
330 total_bytes = 0
330 total_bytes = 0
331 try:
331 try:
332 # get consistent snapshot of repo, lock during scan
332 # get consistent snapshot of repo, lock during scan
333 lock = repo.lock()
333 lock = repo.lock()
334 try:
334 try:
335 repo.ui.debug('scanning\n')
335 repo.ui.debug('scanning\n')
336 for name, ename, size in repo.store.walk():
336 for name, ename, size in repo.store.walk():
337 entries.append((name, size))
337 entries.append((name, size))
338 total_bytes += size
338 total_bytes += size
339 finally:
339 finally:
340 lock.release()
340 lock.release()
341 except error.LockError:
341 except error.LockError:
342 return '2\n' # error: 2
342 return '2\n' # error: 2
343
343
344 def streamer(repo, entries, total):
344 def streamer(repo, entries, total):
345 '''stream out all metadata files in repository.'''
345 '''stream out all metadata files in repository.'''
346 yield '0\n' # success
346 yield '0\n' # success
347 repo.ui.debug('%d files, %d bytes to transfer\n' %
347 repo.ui.debug('%d files, %d bytes to transfer\n' %
348 (len(entries), total_bytes))
348 (len(entries), total_bytes))
349 yield '%d %d\n' % (len(entries), total_bytes)
349 yield '%d %d\n' % (len(entries), total_bytes)
350 for name, size in entries:
350 for name, size in entries:
351 repo.ui.debug('sending %s (%d bytes)\n' % (name, size))
351 repo.ui.debug('sending %s (%d bytes)\n' % (name, size))
352 # partially encode name over the wire for backwards compat
352 # partially encode name over the wire for backwards compat
353 yield '%s\0%d\n' % (store.encodedir(name), size)
353 yield '%s\0%d\n' % (store.encodedir(name), size)
354 for chunk in util.filechunkiter(repo.sopener(name), limit=size):
354 for chunk in util.filechunkiter(repo.sopener(name), limit=size):
355 yield chunk
355 yield chunk
356
356
357 return streamres(streamer(repo, entries, total_bytes))
357 return streamres(streamer(repo, entries, total_bytes))
358
358
359 def unbundle(repo, proto, heads):
359 def unbundle(repo, proto, heads):
360 their_heads = decodelist(heads)
360 their_heads = decodelist(heads)
361
361
362 def check_heads():
362 def check_heads():
363 heads = repo.heads()
363 heads = repo.heads()
364 heads_hash = util.sha1(''.join(sorted(heads))).digest()
364 heads_hash = util.sha1(''.join(sorted(heads))).digest()
365 return (their_heads == ['force'] or their_heads == heads or
365 return (their_heads == ['force'] or their_heads == heads or
366 their_heads == ['hashed', heads_hash])
366 their_heads == ['hashed', heads_hash])
367
367
368 proto.redirect()
368 proto.redirect()
369
369
370 # fail early if possible
370 # fail early if possible
371 if not check_heads():
371 if not check_heads():
372 return pusherr('unsynced changes')
372 return pusherr('unsynced changes')
373
373
374 # write bundle data to temporary file because it can be big
374 # write bundle data to temporary file because it can be big
375 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
375 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
376 fp = os.fdopen(fd, 'wb+')
376 fp = os.fdopen(fd, 'wb+')
377 r = 0
377 r = 0
378 try:
378 try:
379 proto.getfile(fp)
379 proto.getfile(fp)
380 lock = repo.lock()
380 lock = repo.lock()
381 try:
381 try:
382 if not check_heads():
382 if not check_heads():
383 # someone else committed/pushed/unbundled while we
383 # someone else committed/pushed/unbundled while we
384 # were transferring data
384 # were transferring data
385 return pusherr('unsynced changes')
385 return pusherr('unsynced changes')
386
386
387 # push can proceed
387 # push can proceed
388 fp.seek(0)
388 fp.seek(0)
389 gen = changegroupmod.readbundle(fp, None)
389 gen = changegroupmod.readbundle(fp, None)
390
390
391 try:
391 try:
392 r = repo.addchangegroup(gen, 'serve', proto._client(),
392 r = repo.addchangegroup(gen, 'serve', proto._client(),
393 lock=lock)
393 lock=lock)
394 except util.Abort, inst:
394 except util.Abort, inst:
395 sys.stderr.write("abort: %s\n" % inst)
395 sys.stderr.write("abort: %s\n" % inst)
396 finally:
396 finally:
397 lock.release()
397 lock.release()
398 return pushres(r)
398 return pushres(r)
399
399
400 finally:
400 finally:
401 fp.close()
401 fp.close()
402 os.unlink(tempname)
402 os.unlink(tempname)
403
403
404 commands = {
404 commands = {
405 'between': (between, 'pairs'),
405 'between': (between, 'pairs'),
406 'branchmap': (branchmap, ''),
406 'branchmap': (branchmap, ''),
407 'branches': (branches, 'nodes'),
407 'branches': (branches, 'nodes'),
408 'capabilities': (capabilities, ''),
408 'capabilities': (capabilities, ''),
409 'changegroup': (changegroup, 'roots'),
409 'changegroup': (changegroup, 'roots'),
410 'changegroupsubset': (changegroupsubset, 'bases heads'),
410 'changegroupsubset': (changegroupsubset, 'bases heads'),
411 'debugwireargs': (debugwireargs, 'one two *'),
411 'debugwireargs': (debugwireargs, 'one two *'),
412 'getbundle': (getbundle, '*'),
412 'getbundle': (getbundle, '*'),
413 'heads': (heads, ''),
413 'heads': (heads, ''),
414 'hello': (hello, ''),
414 'hello': (hello, ''),
415 'known': (known, 'nodes'),
415 'known': (known, 'nodes *'),
416 'listkeys': (listkeys, 'namespace'),
416 'listkeys': (listkeys, 'namespace'),
417 'lookup': (lookup, 'key'),
417 'lookup': (lookup, 'key'),
418 'pushkey': (pushkey, 'namespace key old new'),
418 'pushkey': (pushkey, 'namespace key old new'),
419 'stream_out': (stream, ''),
419 'stream_out': (stream, ''),
420 'unbundle': (unbundle, 'heads'),
420 'unbundle': (unbundle, 'heads'),
421 }
421 }
General Comments 0
You need to be logged in to leave comments. Login now