##// END OF EJS Templates
protocol: do not translate error messages on the remote side
Dirkjan Ochtman -
r11620:86b49e00 default
parent child Browse files
Show More
@@ -1,285 +1,285 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
8 import urllib, tempfile, os
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 streamclone, repo, error, encoding, util
12 import streamclone, repo, error, encoding, util
13 import pushkey as pushkey_
13 import pushkey as pushkey_
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 return map(bin, l.split(sep))
18 return map(bin, l.split(sep))
19
19
20 def encodelist(l, sep=' '):
20 def encodelist(l, sep=' '):
21 return sep.join(map(hex, l))
21 return sep.join(map(hex, l))
22
22
23 # client side
23 # client side
24
24
25 class wirerepository(repo.repository):
25 class wirerepository(repo.repository):
26 def lookup(self, key):
26 def lookup(self, key):
27 self.requirecap('lookup', _('look up remote revision'))
27 self.requirecap('lookup', _('look up remote revision'))
28 d = self._call("lookup", key=key)
28 d = self._call("lookup", key=key)
29 success, data = d[:-1].split(" ", 1)
29 success, data = d[:-1].split(" ", 1)
30 if int(success):
30 if int(success):
31 return bin(data)
31 return bin(data)
32 self._abort(error.RepoError(data))
32 self._abort(error.RepoError(data))
33
33
34 def heads(self):
34 def heads(self):
35 d = self._call("heads")
35 d = self._call("heads")
36 try:
36 try:
37 return decodelist(d[:-1])
37 return decodelist(d[:-1])
38 except:
38 except:
39 self.abort(error.ResponseError(_("unexpected response:"), d))
39 self.abort(error.ResponseError(_("unexpected response:"), d))
40
40
41 def branchmap(self):
41 def branchmap(self):
42 d = self._call("branchmap")
42 d = self._call("branchmap")
43 try:
43 try:
44 branchmap = {}
44 branchmap = {}
45 for branchpart in d.splitlines():
45 for branchpart in d.splitlines():
46 branchname, branchheads = branchpart.split(' ', 1)
46 branchname, branchheads = branchpart.split(' ', 1)
47 branchname = urllib.unquote(branchname)
47 branchname = urllib.unquote(branchname)
48 # Earlier servers (1.3.x) send branch names in (their) local
48 # Earlier servers (1.3.x) send branch names in (their) local
49 # charset. The best we can do is assume it's identical to our
49 # charset. The best we can do is assume it's identical to our
50 # own local charset, in case it's not utf-8.
50 # own local charset, in case it's not utf-8.
51 try:
51 try:
52 branchname.decode('utf-8')
52 branchname.decode('utf-8')
53 except UnicodeDecodeError:
53 except UnicodeDecodeError:
54 branchname = encoding.fromlocal(branchname)
54 branchname = encoding.fromlocal(branchname)
55 branchheads = decodelist(branchheads)
55 branchheads = decodelist(branchheads)
56 branchmap[branchname] = branchheads
56 branchmap[branchname] = branchheads
57 return branchmap
57 return branchmap
58 except TypeError:
58 except TypeError:
59 self._abort(error.ResponseError(_("unexpected response:"), d))
59 self._abort(error.ResponseError(_("unexpected response:"), d))
60
60
61 def branches(self, nodes):
61 def branches(self, nodes):
62 n = encodelist(nodes)
62 n = encodelist(nodes)
63 d = self._call("branches", nodes=n)
63 d = self._call("branches", nodes=n)
64 try:
64 try:
65 br = [tuple(decodelist(b)) for b in d.splitlines()]
65 br = [tuple(decodelist(b)) for b in d.splitlines()]
66 return br
66 return br
67 except:
67 except:
68 self._abort(error.ResponseError(_("unexpected response:"), d))
68 self._abort(error.ResponseError(_("unexpected response:"), d))
69
69
70 def between(self, pairs):
70 def between(self, pairs):
71 batch = 8 # avoid giant requests
71 batch = 8 # avoid giant requests
72 r = []
72 r = []
73 for i in xrange(0, len(pairs), batch):
73 for i in xrange(0, len(pairs), batch):
74 n = " ".join([encodelist(p, '-') for p in pairs[i:i + batch]])
74 n = " ".join([encodelist(p, '-') for p in pairs[i:i + batch]])
75 d = self._call("between", pairs=n)
75 d = self._call("between", pairs=n)
76 try:
76 try:
77 r.extend(l and decodelist(l) or [] for l in d.splitlines())
77 r.extend(l and decodelist(l) or [] for l in d.splitlines())
78 except:
78 except:
79 self._abort(error.ResponseError(_("unexpected response:"), d))
79 self._abort(error.ResponseError(_("unexpected response:"), d))
80 return r
80 return r
81
81
82 def pushkey(self, namespace, key, old, new):
82 def pushkey(self, namespace, key, old, new):
83 if not self.capable('pushkey'):
83 if not self.capable('pushkey'):
84 return False
84 return False
85 d = self._call("pushkey",
85 d = self._call("pushkey",
86 namespace=namespace, key=key, old=old, new=new)
86 namespace=namespace, key=key, old=old, new=new)
87 return bool(int(d))
87 return bool(int(d))
88
88
89 def listkeys(self, namespace):
89 def listkeys(self, namespace):
90 if not self.capable('pushkey'):
90 if not self.capable('pushkey'):
91 return {}
91 return {}
92 d = self._call("listkeys", namespace=namespace)
92 d = self._call("listkeys", namespace=namespace)
93 r = {}
93 r = {}
94 for l in d.splitlines():
94 for l in d.splitlines():
95 k, v = l.split('\t')
95 k, v = l.split('\t')
96 r[k.decode('string-escape')] = v.decode('string-escape')
96 r[k.decode('string-escape')] = v.decode('string-escape')
97 return r
97 return r
98
98
99 def stream_out(self):
99 def stream_out(self):
100 return self._callstream('stream_out')
100 return self._callstream('stream_out')
101
101
102 def changegroup(self, nodes, kind):
102 def changegroup(self, nodes, kind):
103 n = encodelist(nodes)
103 n = encodelist(nodes)
104 f = self._callstream("changegroup", roots=n)
104 f = self._callstream("changegroup", roots=n)
105 return self._decompress(f)
105 return self._decompress(f)
106
106
107 def changegroupsubset(self, bases, heads, kind):
107 def changegroupsubset(self, bases, heads, kind):
108 self.requirecap('changegroupsubset', _('look up remote changes'))
108 self.requirecap('changegroupsubset', _('look up remote changes'))
109 bases = encodelist(bases)
109 bases = encodelist(bases)
110 heads = encodelist(heads)
110 heads = encodelist(heads)
111 return self._decompress(self._callstream("changegroupsubset",
111 return self._decompress(self._callstream("changegroupsubset",
112 bases=bases, heads=heads))
112 bases=bases, heads=heads))
113
113
114 def unbundle(self, cg, heads, source):
114 def unbundle(self, cg, heads, source):
115 '''Send cg (a readable file-like object representing the
115 '''Send cg (a readable file-like object representing the
116 changegroup to push, typically a chunkbuffer object) to the
116 changegroup to push, typically a chunkbuffer object) to the
117 remote server as a bundle. Return an integer indicating the
117 remote server as a bundle. Return an integer indicating the
118 result of the push (see localrepository.addchangegroup()).'''
118 result of the push (see localrepository.addchangegroup()).'''
119
119
120 ret, output = self._callpush("unbundle", cg, heads=encodelist(heads))
120 ret, output = self._callpush("unbundle", cg, heads=encodelist(heads))
121 if ret == "":
121 if ret == "":
122 raise error.ResponseError(
122 raise error.ResponseError(
123 _('push failed:'), output)
123 _('push failed:'), output)
124 try:
124 try:
125 ret = int(ret)
125 ret = int(ret)
126 except ValueError, err:
126 except ValueError, err:
127 raise error.ResponseError(
127 raise error.ResponseError(
128 _('push failed (unexpected response):'), ret)
128 _('push failed (unexpected response):'), ret)
129
129
130 for l in output.splitlines(True):
130 for l in output.splitlines(True):
131 self.ui.status(_('remote: '), l)
131 self.ui.status(_('remote: '), l)
132 return ret
132 return ret
133
133
134 # server side
134 # server side
135
135
136 def dispatch(repo, proto, command):
136 def dispatch(repo, proto, command):
137 func, spec = commands[command]
137 func, spec = commands[command]
138 args = proto.getargs(spec)
138 args = proto.getargs(spec)
139 r = func(repo, proto, *args)
139 r = func(repo, proto, *args)
140 if r != None:
140 if r != None:
141 proto.respond(r)
141 proto.respond(r)
142
142
143 def between(repo, proto, pairs):
143 def between(repo, proto, pairs):
144 pairs = [decodelist(p, '-') for p in pairs.split(" ")]
144 pairs = [decodelist(p, '-') for p in pairs.split(" ")]
145 r = []
145 r = []
146 for b in repo.between(pairs):
146 for b in repo.between(pairs):
147 r.append(encodelist(b) + "\n")
147 r.append(encodelist(b) + "\n")
148 return "".join(r)
148 return "".join(r)
149
149
150 def branchmap(repo, proto):
150 def branchmap(repo, proto):
151 branchmap = repo.branchmap()
151 branchmap = repo.branchmap()
152 heads = []
152 heads = []
153 for branch, nodes in branchmap.iteritems():
153 for branch, nodes in branchmap.iteritems():
154 branchname = urllib.quote(branch)
154 branchname = urllib.quote(branch)
155 branchnodes = encodelist(nodes)
155 branchnodes = encodelist(nodes)
156 heads.append('%s %s' % (branchname, branchnodes))
156 heads.append('%s %s' % (branchname, branchnodes))
157 return '\n'.join(heads)
157 return '\n'.join(heads)
158
158
159 def branches(repo, proto, nodes):
159 def branches(repo, proto, nodes):
160 nodes = decodelist(nodes)
160 nodes = decodelist(nodes)
161 r = []
161 r = []
162 for b in repo.branches(nodes):
162 for b in repo.branches(nodes):
163 r.append(encodelist(b) + "\n")
163 r.append(encodelist(b) + "\n")
164 return "".join(r)
164 return "".join(r)
165
165
166 def capabilities(repo, proto):
166 def capabilities(repo, proto):
167 caps = 'lookup changegroupsubset branchmap pushkey'.split()
167 caps = 'lookup changegroupsubset branchmap pushkey'.split()
168 if streamclone.allowed(repo.ui):
168 if streamclone.allowed(repo.ui):
169 caps.append('stream=%d' % repo.changelog.version)
169 caps.append('stream=%d' % repo.changelog.version)
170 caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority))
170 caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority))
171 return ' '.join(caps)
171 return ' '.join(caps)
172
172
173 def changegroup(repo, proto, roots):
173 def changegroup(repo, proto, roots):
174 nodes = decodelist(roots)
174 nodes = decodelist(roots)
175 cg = repo.changegroup(nodes, 'serve')
175 cg = repo.changegroup(nodes, 'serve')
176 proto.sendchangegroup(cg)
176 proto.sendchangegroup(cg)
177
177
178 def changegroupsubset(repo, proto, bases, heads):
178 def changegroupsubset(repo, proto, bases, heads):
179 bases = decodelist(bases)
179 bases = decodelist(bases)
180 heads = decodelist(heads)
180 heads = decodelist(heads)
181 cg = repo.changegroupsubset(bases, heads, 'serve')
181 cg = repo.changegroupsubset(bases, heads, 'serve')
182 proto.sendchangegroup(cg)
182 proto.sendchangegroup(cg)
183
183
184 def heads(repo, proto):
184 def heads(repo, proto):
185 h = repo.heads()
185 h = repo.heads()
186 return encodelist(h) + "\n"
186 return encodelist(h) + "\n"
187
187
188 def hello(repo, proto):
188 def hello(repo, proto):
189 '''the hello command returns a set of lines describing various
189 '''the hello command returns a set of lines describing various
190 interesting things about the server, in an RFC822-like format.
190 interesting things about the server, in an RFC822-like format.
191 Currently the only one defined is "capabilities", which
191 Currently the only one defined is "capabilities", which
192 consists of a line in the form:
192 consists of a line in the form:
193
193
194 capabilities: space separated list of tokens
194 capabilities: space separated list of tokens
195 '''
195 '''
196 return "capabilities: %s\n" % (capabilities(repo, proto))
196 return "capabilities: %s\n" % (capabilities(repo, proto))
197
197
198 def listkeys(repo, proto, namespace):
198 def listkeys(repo, proto, namespace):
199 d = pushkey_.list(repo, namespace).items()
199 d = pushkey_.list(repo, namespace).items()
200 t = '\n'.join(['%s\t%s' % (k.encode('string-escape'),
200 t = '\n'.join(['%s\t%s' % (k.encode('string-escape'),
201 v.encode('string-escape')) for k, v in d])
201 v.encode('string-escape')) for k, v in d])
202 return t
202 return t
203
203
204 def lookup(repo, proto, key):
204 def lookup(repo, proto, key):
205 try:
205 try:
206 r = hex(repo.lookup(key))
206 r = hex(repo.lookup(key))
207 success = 1
207 success = 1
208 except Exception, inst:
208 except Exception, inst:
209 r = str(inst)
209 r = str(inst)
210 success = 0
210 success = 0
211 return "%s %s\n" % (success, r)
211 return "%s %s\n" % (success, r)
212
212
213 def pushkey(repo, proto, namespace, key, old, new):
213 def pushkey(repo, proto, namespace, key, old, new):
214 r = pushkey_.push(repo, namespace, key, old, new)
214 r = pushkey_.push(repo, namespace, key, old, new)
215 return '%s\n' % int(r)
215 return '%s\n' % int(r)
216
216
217 def stream(repo, proto):
217 def stream(repo, proto):
218 try:
218 try:
219 proto.sendstream(streamclone.stream_out(repo))
219 proto.sendstream(streamclone.stream_out(repo))
220 except streamclone.StreamException, inst:
220 except streamclone.StreamException, inst:
221 return str(inst)
221 return str(inst)
222
222
223 def unbundle(repo, proto, heads):
223 def unbundle(repo, proto, heads):
224 their_heads = decodelist(heads)
224 their_heads = decodelist(heads)
225
225
226 def check_heads():
226 def check_heads():
227 heads = repo.heads()
227 heads = repo.heads()
228 return their_heads == ['force'] or their_heads == heads
228 return their_heads == ['force'] or their_heads == heads
229
229
230 # fail early if possible
230 # fail early if possible
231 if not check_heads():
231 if not check_heads():
232 return _('unsynced changes')
232 return 'unsynced changes'
233
233
234 # write bundle data to temporary file because it can be big
234 # write bundle data to temporary file because it can be big
235 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
235 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
236 fp = os.fdopen(fd, 'wb+')
236 fp = os.fdopen(fd, 'wb+')
237 r = 0
237 r = 0
238 proto.redirect()
238 proto.redirect()
239 try:
239 try:
240 proto.getfile(fp)
240 proto.getfile(fp)
241 lock = repo.lock()
241 lock = repo.lock()
242 try:
242 try:
243 if not check_heads():
243 if not check_heads():
244 # someone else committed/pushed/unbundled while we
244 # someone else committed/pushed/unbundled while we
245 # were transferring data
245 # were transferring data
246 return _('unsynced changes')
246 return 'unsynced changes'
247
247
248 # push can proceed
248 # push can proceed
249 fp.seek(0)
249 fp.seek(0)
250 header = fp.read(6)
250 header = fp.read(6)
251 if header.startswith('HG'):
251 if header.startswith('HG'):
252 if not header.startswith('HG10'):
252 if not header.startswith('HG10'):
253 raise ValueError('unknown bundle version')
253 raise ValueError('unknown bundle version')
254 elif header not in changegroupmod.bundletypes:
254 elif header not in changegroupmod.bundletypes:
255 raise ValueError('unknown bundle compression type')
255 raise ValueError('unknown bundle compression type')
256 gen = changegroupmod.unbundle(header, fp)
256 gen = changegroupmod.unbundle(header, fp)
257
257
258 try:
258 try:
259 r = repo.addchangegroup(gen, 'serve', proto._client(),
259 r = repo.addchangegroup(gen, 'serve', proto._client(),
260 lock=lock)
260 lock=lock)
261 except util.Abort, inst:
261 except util.Abort, inst:
262 sys.stderr.write("abort: %s\n" % inst)
262 sys.stderr.write("abort: %s\n" % inst)
263 finally:
263 finally:
264 lock.release()
264 lock.release()
265 proto.respondpush(r)
265 proto.respondpush(r)
266
266
267 finally:
267 finally:
268 fp.close()
268 fp.close()
269 os.unlink(tempname)
269 os.unlink(tempname)
270
270
271 commands = {
271 commands = {
272 'between': (between, 'pairs'),
272 'between': (between, 'pairs'),
273 'branchmap': (branchmap, ''),
273 'branchmap': (branchmap, ''),
274 'branches': (branches, 'nodes'),
274 'branches': (branches, 'nodes'),
275 'capabilities': (capabilities, ''),
275 'capabilities': (capabilities, ''),
276 'changegroup': (changegroup, 'roots'),
276 'changegroup': (changegroup, 'roots'),
277 'changegroupsubset': (changegroupsubset, 'bases heads'),
277 'changegroupsubset': (changegroupsubset, 'bases heads'),
278 'heads': (heads, ''),
278 'heads': (heads, ''),
279 'hello': (hello, ''),
279 'hello': (hello, ''),
280 'listkeys': (listkeys, 'namespace'),
280 'listkeys': (listkeys, 'namespace'),
281 'lookup': (lookup, 'key'),
281 'lookup': (lookup, 'key'),
282 'pushkey': (pushkey, 'namespace key old new'),
282 'pushkey': (pushkey, 'namespace key old new'),
283 'stream_out': (stream, ''),
283 'stream_out': (stream, ''),
284 'unbundle': (unbundle, 'heads'),
284 'unbundle': (unbundle, 'heads'),
285 }
285 }
General Comments 0
You need to be logged in to leave comments. Login now