##// END OF EJS Templates
protocol: command must be checked before passing in
Dirkjan Ochtman -
r11618:83070a9c default
parent child Browse files
Show More
@@ -1,71 +1,71
1 #
1 #
2 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
2 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 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 cStringIO, zlib, sys, urllib
8 import cStringIO, zlib, sys, urllib
9 from mercurial import util, wireproto
9 from mercurial import util, wireproto
10 from common import HTTP_OK
10 from common import HTTP_OK
11
11
12 HGTYPE = 'application/mercurial-0.1'
12 HGTYPE = 'application/mercurial-0.1'
13
13
14 class webproto(object):
14 class webproto(object):
15 def __init__(self, req):
15 def __init__(self, req):
16 self.req = req
16 self.req = req
17 self.response = ''
17 self.response = ''
18 def getargs(self, args):
18 def getargs(self, args):
19 data = {}
19 data = {}
20 keys = args.split()
20 keys = args.split()
21 for k in keys:
21 for k in keys:
22 if k == '*':
22 if k == '*':
23 star = {}
23 star = {}
24 for key in self.req.form.keys():
24 for key in self.req.form.keys():
25 if key not in keys:
25 if key not in keys:
26 star[key] = self.req.form[key][0]
26 star[key] = self.req.form[key][0]
27 data['*'] = star
27 data['*'] = star
28 else:
28 else:
29 data[k] = self.req.form[k][0]
29 data[k] = self.req.form[k][0]
30 return [data[k] for k in keys]
30 return [data[k] for k in keys]
31 def sendchangegroup(self, cg):
31 def sendchangegroup(self, cg):
32 self.req.respond(HTTP_OK, HGTYPE)
32 self.req.respond(HTTP_OK, HGTYPE)
33 z = zlib.compressobj()
33 z = zlib.compressobj()
34 while 1:
34 while 1:
35 chunk = cg.read(4096)
35 chunk = cg.read(4096)
36 if not chunk:
36 if not chunk:
37 break
37 break
38 self.req.write(z.compress(chunk))
38 self.req.write(z.compress(chunk))
39 self.req.write(z.flush())
39 self.req.write(z.flush())
40 def sendstream(self, source):
40 def sendstream(self, source):
41 self.req.respond(HTTP_OK, HGTYPE)
41 self.req.respond(HTTP_OK, HGTYPE)
42 for chunk in source:
42 for chunk in source:
43 self.req.write(chunk)
43 self.req.write(chunk)
44 def respond(self, s):
44 def respond(self, s):
45 self.req.respond(HTTP_OK, HGTYPE, length=len(s))
45 self.req.respond(HTTP_OK, HGTYPE, length=len(s))
46 self.response = s
46 self.response = s
47 def getfile(self, fp):
47 def getfile(self, fp):
48 length = int(self.req.env['CONTENT_LENGTH'])
48 length = int(self.req.env['CONTENT_LENGTH'])
49 for s in util.filechunkiter(self.req, limit=length):
49 for s in util.filechunkiter(self.req, limit=length):
50 fp.write(s)
50 fp.write(s)
51 def redirect(self):
51 def redirect(self):
52 self.oldio = sys.stdout, sys.stderr
52 self.oldio = sys.stdout, sys.stderr
53 sys.stderr = sys.stdout = cStringIO.StringIO()
53 sys.stderr = sys.stdout = cStringIO.StringIO()
54 def respondpush(self, ret):
54 def respondpush(self, ret):
55 val = sys.stdout.getvalue()
55 val = sys.stdout.getvalue()
56 sys.stdout, sys.stderr = self.oldio
56 sys.stdout, sys.stderr = self.oldio
57 self.req.respond(HTTP_OK, HGTYPE)
57 self.req.respond(HTTP_OK, HGTYPE)
58 self.response = '%d\n%s' % (ret, val)
58 self.response = '%d\n%s' % (ret, val)
59 def _client(self):
59 def _client(self):
60 return 'remote:%s:%s:%s' % (
60 return 'remote:%s:%s:%s' % (
61 self.req.env.get('wsgi.url_scheme') or 'http',
61 self.req.env.get('wsgi.url_scheme') or 'http',
62 urllib.quote(self.req.env.get('REMOTE_HOST', '')),
62 urllib.quote(self.req.env.get('REMOTE_HOST', '')),
63 urllib.quote(self.req.env.get('REMOTE_USER', '')))
63 urllib.quote(self.req.env.get('REMOTE_USER', '')))
64
64
65 def iscmd(cmd):
65 def iscmd(cmd):
66 return cmd in wireproto.commands
66 return cmd in wireproto.commands
67
67
68 def call(repo, req, cmd):
68 def call(repo, req, cmd):
69 p = webproto(req)
69 p = webproto(req)
70 r = wireproto.dispatch(repo, p, cmd)
70 wireproto.dispatch(repo, p, cmd)
71 yield p.response
71 yield p.response
@@ -1,133 +1,135
1 # sshserver.py - ssh protocol server support for mercurial
1 # sshserver.py - ssh protocol server support for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 from i18n import _
9 from i18n import _
10 import util, hook, wireproto
10 import util, hook, wireproto
11 import os, sys
11 import os, sys
12
12
13 class sshserver(object):
13 class sshserver(object):
14 def __init__(self, ui, repo):
14 def __init__(self, ui, repo):
15 self.ui = ui
15 self.ui = ui
16 self.repo = repo
16 self.repo = repo
17 self.lock = None
17 self.lock = None
18 self.fin = sys.stdin
18 self.fin = sys.stdin
19 self.fout = sys.stdout
19 self.fout = sys.stdout
20
20
21 hook.redirect(True)
21 hook.redirect(True)
22 sys.stdout = sys.stderr
22 sys.stdout = sys.stderr
23
23
24 # Prevent insertion/deletion of CRs
24 # Prevent insertion/deletion of CRs
25 util.set_binary(self.fin)
25 util.set_binary(self.fin)
26 util.set_binary(self.fout)
26 util.set_binary(self.fout)
27
27
28 def getargs(self, args):
28 def getargs(self, args):
29 data = {}
29 data = {}
30 keys = args.split()
30 keys = args.split()
31 count = len(keys)
31 count = len(keys)
32 for n in xrange(len(keys)):
32 for n in xrange(len(keys)):
33 argline = self.fin.readline()[:-1]
33 argline = self.fin.readline()[:-1]
34 arg, l = argline.split()
34 arg, l = argline.split()
35 val = self.fin.read(int(l))
35 val = self.fin.read(int(l))
36 if arg not in keys:
36 if arg not in keys:
37 raise util.Abort("unexpected parameter %r" % arg)
37 raise util.Abort("unexpected parameter %r" % arg)
38 if arg == '*':
38 if arg == '*':
39 star = {}
39 star = {}
40 for n in xrange(int(l)):
40 for n in xrange(int(l)):
41 arg, l = argline.split()
41 arg, l = argline.split()
42 val = self.fin.read(int(l))
42 val = self.fin.read(int(l))
43 star[arg] = val
43 star[arg] = val
44 data['*'] = star
44 data['*'] = star
45 else:
45 else:
46 data[arg] = val
46 data[arg] = val
47 return [data[k] for k in keys]
47 return [data[k] for k in keys]
48
48
49 def getarg(self, name):
49 def getarg(self, name):
50 return self.getargs(name)[0]
50 return self.getargs(name)[0]
51
51
52 def respond(self, v):
52 def respond(self, v):
53 self.fout.write("%d\n" % len(v))
53 self.fout.write("%d\n" % len(v))
54 self.fout.write(v)
54 self.fout.write(v)
55 self.fout.flush()
55 self.fout.flush()
56
56
57 def sendchangegroup(self, changegroup):
57 def sendchangegroup(self, changegroup):
58 while True:
58 while True:
59 d = changegroup.read(4096)
59 d = changegroup.read(4096)
60 if not d:
60 if not d:
61 break
61 break
62 self.fout.write(d)
62 self.fout.write(d)
63
63
64 self.fout.flush()
64 self.fout.flush()
65
65
66 def sendstream(self, source):
66 def sendstream(self, source):
67 for chunk in source:
67 for chunk in source:
68 self.fout.write(chunk)
68 self.fout.write(chunk)
69 self.fout.flush()
69 self.fout.flush()
70
70
71 def getfile(self, fpout):
71 def getfile(self, fpout):
72 self.respond('')
72 self.respond('')
73 count = int(self.fin.readline())
73 count = int(self.fin.readline())
74 while count:
74 while count:
75 fpout.write(self.fin.read(count))
75 fpout.write(self.fin.read(count))
76 count = int(self.fin.readline())
76 count = int(self.fin.readline())
77
77
78 def redirect(self):
78 def redirect(self):
79 pass
79 pass
80
80
81 def respondpush(self, ret):
81 def respondpush(self, ret):
82 self.respond('')
82 self.respond('')
83 self.respond(str(ret))
83 self.respond(str(ret))
84
84
85 def serve_forever(self):
85 def serve_forever(self):
86 try:
86 try:
87 while self.serve_one():
87 while self.serve_one():
88 pass
88 pass
89 finally:
89 finally:
90 if self.lock is not None:
90 if self.lock is not None:
91 self.lock.release()
91 self.lock.release()
92 sys.exit(0)
92 sys.exit(0)
93
93
94 def serve_one(self):
94 def serve_one(self):
95 cmd = self.fin.readline()[:-1]
95 cmd = self.fin.readline()[:-1]
96 if cmd and not wireproto.dispatch(self.repo, self, cmd):
96 if cmd and cmd in wireproto.commands:
97 wireproto.dispatch(self.repo, self, cmd)
98 elif cmd:
97 impl = getattr(self, 'do_' + cmd, None)
99 impl = getattr(self, 'do_' + cmd, None)
98 if impl:
100 if impl:
99 r = impl()
101 r = impl()
100 if r is not None:
102 if r is not None:
101 self.respond(r)
103 self.respond(r)
102 else: self.respond("")
104 else: self.respond("")
103 return cmd != ''
105 return cmd != ''
104
106
105 def do_lock(self):
107 def do_lock(self):
106 '''DEPRECATED - allowing remote client to lock repo is not safe'''
108 '''DEPRECATED - allowing remote client to lock repo is not safe'''
107
109
108 self.lock = self.repo.lock()
110 self.lock = self.repo.lock()
109 return ""
111 return ""
110
112
111 def do_unlock(self):
113 def do_unlock(self):
112 '''DEPRECATED'''
114 '''DEPRECATED'''
113
115
114 if self.lock:
116 if self.lock:
115 self.lock.release()
117 self.lock.release()
116 self.lock = None
118 self.lock = None
117 return ""
119 return ""
118
120
119 def do_addchangegroup(self):
121 def do_addchangegroup(self):
120 '''DEPRECATED'''
122 '''DEPRECATED'''
121
123
122 if not self.lock:
124 if not self.lock:
123 self.respond("not locked")
125 self.respond("not locked")
124 return
126 return
125
127
126 self.respond("")
128 self.respond("")
127 r = self.repo.addchangegroup(self.fin, 'serve', self._client(),
129 r = self.repo.addchangegroup(self.fin, 'serve', self._client(),
128 lock=self.lock)
130 lock=self.lock)
129 return str(r)
131 return str(r)
130
132
131 def _client(self):
133 def _client(self):
132 client = os.environ.get('SSH_CLIENT', '').split(' ', 1)[0]
134 client = os.environ.get('SSH_CLIENT', '').split(' ', 1)[0]
133 return 'remote:ssh:' + client
135 return 'remote:ssh:' + client
@@ -1,290 +1,287
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 if command not in commands:
138 return False
139 func, spec = commands[command]
137 func, spec = commands[command]
140 args = proto.getargs(spec)
138 args = proto.getargs(spec)
141 r = func(repo, proto, *args)
139 r = func(repo, proto, *args)
142 if r != None:
140 if r != None:
143 proto.respond(r)
141 proto.respond(r)
144 return True
145
142
146 def between(repo, proto, pairs):
143 def between(repo, proto, pairs):
147 pairs = [decodelist(p, '-') for p in pairs.split(" ")]
144 pairs = [decodelist(p, '-') for p in pairs.split(" ")]
148 r = []
145 r = []
149 for b in repo.between(pairs):
146 for b in repo.between(pairs):
150 r.append(encodelist(b) + "\n")
147 r.append(encodelist(b) + "\n")
151 return "".join(r)
148 return "".join(r)
152
149
153 def branchmap(repo, proto):
150 def branchmap(repo, proto):
154 branchmap = repo.branchmap()
151 branchmap = repo.branchmap()
155 heads = []
152 heads = []
156 for branch, nodes in branchmap.iteritems():
153 for branch, nodes in branchmap.iteritems():
157 branchname = urllib.quote(branch)
154 branchname = urllib.quote(branch)
158 branchnodes = encodelist(nodes)
155 branchnodes = encodelist(nodes)
159 heads.append('%s %s' % (branchname, branchnodes))
156 heads.append('%s %s' % (branchname, branchnodes))
160 return '\n'.join(heads)
157 return '\n'.join(heads)
161
158
162 def branches(repo, proto, nodes):
159 def branches(repo, proto, nodes):
163 nodes = decodelist(nodes)
160 nodes = decodelist(nodes)
164 r = []
161 r = []
165 for b in repo.branches(nodes):
162 for b in repo.branches(nodes):
166 r.append(encodelist(b) + "\n")
163 r.append(encodelist(b) + "\n")
167 return "".join(r)
164 return "".join(r)
168
165
169 def capabilities(repo, proto):
166 def capabilities(repo, proto):
170 caps = 'lookup changegroupsubset branchmap pushkey'.split()
167 caps = 'lookup changegroupsubset branchmap pushkey'.split()
171 if streamclone.allowed(repo.ui):
168 if streamclone.allowed(repo.ui):
172 caps.append('stream=%d' % repo.changelog.version)
169 caps.append('stream=%d' % repo.changelog.version)
173 caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority))
170 caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority))
174 return ' '.join(caps)
171 return ' '.join(caps)
175
172
176 def changegroup(repo, proto, roots):
173 def changegroup(repo, proto, roots):
177 nodes = decodelist(roots)
174 nodes = decodelist(roots)
178 cg = repo.changegroup(nodes, 'serve')
175 cg = repo.changegroup(nodes, 'serve')
179 proto.sendchangegroup(cg)
176 proto.sendchangegroup(cg)
180
177
181 def changegroupsubset(repo, proto, bases, heads):
178 def changegroupsubset(repo, proto, bases, heads):
182 bases = decodelist(bases)
179 bases = decodelist(bases)
183 heads = decodelist(heads)
180 heads = decodelist(heads)
184 cg = repo.changegroupsubset(bases, heads, 'serve')
181 cg = repo.changegroupsubset(bases, heads, 'serve')
185 proto.sendchangegroup(cg)
182 proto.sendchangegroup(cg)
186
183
187 def heads(repo, proto):
184 def heads(repo, proto):
188 h = repo.heads()
185 h = repo.heads()
189 return encodelist(h) + "\n"
186 return encodelist(h) + "\n"
190
187
191 def hello(repo, proto):
188 def hello(repo, proto):
192 '''the hello command returns a set of lines describing various
189 '''the hello command returns a set of lines describing various
193 interesting things about the server, in an RFC822-like format.
190 interesting things about the server, in an RFC822-like format.
194 Currently the only one defined is "capabilities", which
191 Currently the only one defined is "capabilities", which
195 consists of a line in the form:
192 consists of a line in the form:
196
193
197 capabilities: space separated list of tokens
194 capabilities: space separated list of tokens
198 '''
195 '''
199 return "capabilities: %s\n" % (capabilities(repo, proto))
196 return "capabilities: %s\n" % (capabilities(repo, proto))
200
197
201 def listkeys(repo, proto, namespace):
198 def listkeys(repo, proto, namespace):
202 d = pushkey_.list(repo, namespace).items()
199 d = pushkey_.list(repo, namespace).items()
203 t = '\n'.join(['%s\t%s' % (k.encode('string-escape'),
200 t = '\n'.join(['%s\t%s' % (k.encode('string-escape'),
204 v.encode('string-escape')) for k, v in d])
201 v.encode('string-escape')) for k, v in d])
205 return t
202 return t
206
203
207 def lookup(repo, proto, key):
204 def lookup(repo, proto, key):
208 try:
205 try:
209 r = hex(repo.lookup(key))
206 r = hex(repo.lookup(key))
210 success = 1
207 success = 1
211 except Exception, inst:
208 except Exception, inst:
212 r = str(inst)
209 r = str(inst)
213 success = 0
210 success = 0
214 return "%s %s\n" % (success, r)
211 return "%s %s\n" % (success, r)
215
212
216 def pushkey(repo, proto, namespace, key, old, new):
213 def pushkey(repo, proto, namespace, key, old, new):
217 r = pushkey_.push(repo, namespace, key, old, new)
214 r = pushkey_.push(repo, namespace, key, old, new)
218 return '%s\n' % int(r)
215 return '%s\n' % int(r)
219
216
220 def stream(repo, proto):
217 def stream(repo, proto):
221 try:
218 try:
222 proto.sendstream(streamclone.stream_out(repo))
219 proto.sendstream(streamclone.stream_out(repo))
223 except streamclone.StreamException, inst:
220 except streamclone.StreamException, inst:
224 return str(inst)
221 return str(inst)
225
222
226 def unbundle(repo, proto, heads):
223 def unbundle(repo, proto, heads):
227 their_heads = decodelist(heads)
224 their_heads = decodelist(heads)
228
225
229 def check_heads():
226 def check_heads():
230 heads = repo.heads()
227 heads = repo.heads()
231 return their_heads == ['force'] or their_heads == heads
228 return their_heads == ['force'] or their_heads == heads
232
229
233 # fail early if possible
230 # fail early if possible
234 if not check_heads():
231 if not check_heads():
235 repo.respond(_('unsynced changes'))
232 repo.respond(_('unsynced changes'))
236 return
233 return
237
234
238 # write bundle data to temporary file because it can be big
235 # write bundle data to temporary file because it can be big
239 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
236 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
240 fp = os.fdopen(fd, 'wb+')
237 fp = os.fdopen(fd, 'wb+')
241 r = 0
238 r = 0
242 proto.redirect()
239 proto.redirect()
243 try:
240 try:
244 proto.getfile(fp)
241 proto.getfile(fp)
245 lock = repo.lock()
242 lock = repo.lock()
246 try:
243 try:
247 if not check_heads():
244 if not check_heads():
248 # someone else committed/pushed/unbundled while we
245 # someone else committed/pushed/unbundled while we
249 # were transferring data
246 # were transferring data
250 proto.respond(_('unsynced changes'))
247 proto.respond(_('unsynced changes'))
251 return
248 return
252
249
253 # push can proceed
250 # push can proceed
254 fp.seek(0)
251 fp.seek(0)
255 header = fp.read(6)
252 header = fp.read(6)
256 if header.startswith('HG'):
253 if header.startswith('HG'):
257 if not header.startswith('HG10'):
254 if not header.startswith('HG10'):
258 raise ValueError('unknown bundle version')
255 raise ValueError('unknown bundle version')
259 elif header not in changegroupmod.bundletypes:
256 elif header not in changegroupmod.bundletypes:
260 raise ValueError('unknown bundle compression type')
257 raise ValueError('unknown bundle compression type')
261 gen = changegroupmod.unbundle(header, fp)
258 gen = changegroupmod.unbundle(header, fp)
262
259
263 try:
260 try:
264 r = repo.addchangegroup(gen, 'serve', proto._client(),
261 r = repo.addchangegroup(gen, 'serve', proto._client(),
265 lock=lock)
262 lock=lock)
266 except util.Abort, inst:
263 except util.Abort, inst:
267 sys.stderr.write("abort: %s\n" % inst)
264 sys.stderr.write("abort: %s\n" % inst)
268 finally:
265 finally:
269 lock.release()
266 lock.release()
270 proto.respondpush(r)
267 proto.respondpush(r)
271
268
272 finally:
269 finally:
273 fp.close()
270 fp.close()
274 os.unlink(tempname)
271 os.unlink(tempname)
275
272
276 commands = {
273 commands = {
277 'between': (between, 'pairs'),
274 'between': (between, 'pairs'),
278 'branchmap': (branchmap, ''),
275 'branchmap': (branchmap, ''),
279 'branches': (branches, 'nodes'),
276 'branches': (branches, 'nodes'),
280 'capabilities': (capabilities, ''),
277 'capabilities': (capabilities, ''),
281 'changegroup': (changegroup, 'roots'),
278 'changegroup': (changegroup, 'roots'),
282 'changegroupsubset': (changegroupsubset, 'bases heads'),
279 'changegroupsubset': (changegroupsubset, 'bases heads'),
283 'heads': (heads, ''),
280 'heads': (heads, ''),
284 'hello': (hello, ''),
281 'hello': (hello, ''),
285 'listkeys': (listkeys, 'namespace'),
282 'listkeys': (listkeys, 'namespace'),
286 'lookup': (lookup, 'key'),
283 'lookup': (lookup, 'key'),
287 'pushkey': (pushkey, 'namespace key old new'),
284 'pushkey': (pushkey, 'namespace key old new'),
288 'stream_out': (stream, ''),
285 'stream_out': (stream, ''),
289 'unbundle': (unbundle, 'heads'),
286 'unbundle': (unbundle, 'heads'),
290 }
287 }
General Comments 0
You need to be logged in to leave comments. Login now