##// END OF EJS Templates
Backed out changeset b9d6ab187523 (doesn't work on Python 2.3/2.4)
Dirkjan Ochtman -
r6794:8ff321a3 default
parent child Browse files
Show More
@@ -1,209 +1,208 b''
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
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 import cStringIO, zlib, tempfile, errno, os, sys
8 import cStringIO, zlib, tempfile, errno, os, sys
9 from mercurial import util, streamclone
9 from mercurial import util, streamclone
10 from mercurial.node import bin, hex
10 from mercurial.node import bin, hex
11 from mercurial import changegroup as changegroupmod
11 from mercurial import changegroup as changegroupmod
12 from common import HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
12 from common import HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
13
13
14 # __all__ is populated with the allowed commands. Be sure to add to it if
14 # __all__ is populated with the allowed commands. Be sure to add to it if
15 # you're adding a new command, or the new command won't work.
15 # you're adding a new command, or the new command won't work.
16
16
17 __all__ = [
17 __all__ = [
18 'lookup', 'heads', 'branches', 'between', 'changegroup',
18 'lookup', 'heads', 'branches', 'between', 'changegroup',
19 'changegroupsubset', 'capabilities', 'unbundle', 'stream_out',
19 'changegroupsubset', 'capabilities', 'unbundle', 'stream_out',
20 ]
20 ]
21
21
22 HGTYPE = 'application/mercurial-0.1'
22 HGTYPE = 'application/mercurial-0.1'
23
23
24 def lookup(repo, req):
24 def lookup(repo, req):
25 try:
25 try:
26 r = hex(repo.lookup(req.form['key'][0]))
26 r = hex(repo.lookup(req.form['key'][0]))
27 success = 1
27 success = 1
28 except Exception,inst:
28 except Exception,inst:
29 r = str(inst)
29 r = str(inst)
30 success = 0
30 success = 0
31 resp = "%s %s\n" % (success, r)
31 resp = "%s %s\n" % (success, r)
32 req.respond(HTTP_OK, HGTYPE, length=len(resp))
32 req.respond(HTTP_OK, HGTYPE, length=len(resp))
33 req.write(resp)
33 req.write(resp)
34
34
35 def heads(repo, req):
35 def heads(repo, req):
36 resp = " ".join(map(hex, repo.heads())) + "\n"
36 resp = " ".join(map(hex, repo.heads())) + "\n"
37 req.respond(HTTP_OK, HGTYPE, length=len(resp))
37 req.respond(HTTP_OK, HGTYPE, length=len(resp))
38 req.write(resp)
38 req.write(resp)
39
39
40 def branches(repo, req):
40 def branches(repo, req):
41 nodes = []
41 nodes = []
42 if 'nodes' in req.form:
42 if 'nodes' in req.form:
43 nodes = map(bin, req.form['nodes'][0].split(" "))
43 nodes = map(bin, req.form['nodes'][0].split(" "))
44 resp = cStringIO.StringIO()
44 resp = cStringIO.StringIO()
45 for b in repo.branches(nodes):
45 for b in repo.branches(nodes):
46 resp.write(" ".join(map(hex, b)) + "\n")
46 resp.write(" ".join(map(hex, b)) + "\n")
47 resp = resp.getvalue()
47 resp = resp.getvalue()
48 req.respond(HTTP_OK, HGTYPE, length=len(resp))
48 req.respond(HTTP_OK, HGTYPE, length=len(resp))
49 req.write(resp)
49 req.write(resp)
50
50
51 def between(repo, req):
51 def between(repo, req):
52 if 'pairs' in req.form:
52 if 'pairs' in req.form:
53 pairs = [map(bin, p.split("-"))
53 pairs = [map(bin, p.split("-"))
54 for p in req.form['pairs'][0].split(" ")]
54 for p in req.form['pairs'][0].split(" ")]
55 resp = cStringIO.StringIO()
55 resp = cStringIO.StringIO()
56 for b in repo.between(pairs):
56 for b in repo.between(pairs):
57 resp.write(" ".join(map(hex, b)) + "\n")
57 resp.write(" ".join(map(hex, b)) + "\n")
58 resp = resp.getvalue()
58 resp = resp.getvalue()
59 req.respond(HTTP_OK, HGTYPE, length=len(resp))
59 req.respond(HTTP_OK, HGTYPE, length=len(resp))
60 req.write(resp)
60 req.write(resp)
61
61
62 def changegroup(repo, req):
62 def changegroup(repo, req):
63 req.respond(HTTP_OK, HGTYPE)
63 req.respond(HTTP_OK, HGTYPE)
64 nodes = []
64 nodes = []
65
65
66 if 'roots' in req.form:
66 if 'roots' in req.form:
67 nodes = map(bin, req.form['roots'][0].split(" "))
67 nodes = map(bin, req.form['roots'][0].split(" "))
68
68
69 z = zlib.compressobj()
69 z = zlib.compressobj()
70 f = repo.changegroup(nodes, 'serve')
70 f = repo.changegroup(nodes, 'serve')
71 while 1:
71 while 1:
72 chunk = f.read(4096)
72 chunk = f.read(4096)
73 if not chunk:
73 if not chunk:
74 break
74 break
75 req.write(z.compress(chunk))
75 req.write(z.compress(chunk))
76
76
77 req.write(z.flush())
77 req.write(z.flush())
78
78
79 def changegroupsubset(repo, req):
79 def changegroupsubset(repo, req):
80 req.respond(HTTP_OK, HGTYPE)
80 req.respond(HTTP_OK, HGTYPE)
81 bases = []
81 bases = []
82 heads = []
82 heads = []
83
83
84 if 'bases' in req.form:
84 if 'bases' in req.form:
85 bases = [bin(x) for x in req.form['bases'][0].split(' ')]
85 bases = [bin(x) for x in req.form['bases'][0].split(' ')]
86 if 'heads' in req.form:
86 if 'heads' in req.form:
87 heads = [bin(x) for x in req.form['heads'][0].split(' ')]
87 heads = [bin(x) for x in req.form['heads'][0].split(' ')]
88
88
89 z = zlib.compressobj()
89 z = zlib.compressobj()
90 f = repo.changegroupsubset(bases, heads, 'serve')
90 f = repo.changegroupsubset(bases, heads, 'serve')
91 while 1:
91 while 1:
92 chunk = f.read(4096)
92 chunk = f.read(4096)
93 if not chunk:
93 if not chunk:
94 break
94 break
95 req.write(z.compress(chunk))
95 req.write(z.compress(chunk))
96
96
97 req.write(z.flush())
97 req.write(z.flush())
98
98
99 def capabilities(repo, req):
99 def capabilities(repo, req):
100 caps = ['lookup', 'changegroupsubset']
100 caps = ['lookup', 'changegroupsubset']
101 if repo.ui.configbool('server', 'uncompressed', untrusted=True):
101 if repo.ui.configbool('server', 'uncompressed', untrusted=True):
102 caps.append('stream=%d' % repo.changelog.version)
102 caps.append('stream=%d' % repo.changelog.version)
103 if changegroupmod.bundlepriority:
103 if changegroupmod.bundlepriority:
104 caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority))
104 caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority))
105 rsp = ' '.join(caps)
105 rsp = ' '.join(caps)
106 req.respond(HTTP_OK, HGTYPE, length=len(rsp))
106 req.respond(HTTP_OK, HGTYPE, length=len(rsp))
107 req.write(rsp)
107 req.write(rsp)
108
108
109 def unbundle(repo, req):
109 def unbundle(repo, req):
110
110
111 def bail(response, headers={}):
111 def bail(response, headers={}):
112 length = int(req.env.get('CONTENT_LENGTH', 0))
112 length = int(req.env.get('CONTENT_LENGTH', 0))
113 for s in util.filechunkiter(req, limit=length):
113 for s in util.filechunkiter(req, limit=length):
114 # drain incoming bundle, else client will not see
114 # drain incoming bundle, else client will not see
115 # response when run outside cgi script
115 # response when run outside cgi script
116 pass
116 pass
117
117
118 status = headers.pop('status', HTTP_OK)
118 status = headers.pop('status', HTTP_OK)
119 req.header(headers.items())
119 req.header(headers.items())
120 req.respond(status, HGTYPE)
120 req.respond(status, HGTYPE)
121 req.write('0\n')
121 req.write('0\n')
122 req.write(response)
122 req.write(response)
123
123
124 proto = req.env.get('wsgi.url_scheme') or 'http'
124 proto = req.env.get('wsgi.url_scheme') or 'http'
125 their_heads = req.form['heads'][0].split(' ')
125 their_heads = req.form['heads'][0].split(' ')
126
126
127 def check_heads():
127 def check_heads():
128 heads = map(hex, repo.heads())
128 heads = map(hex, repo.heads())
129 return their_heads == [hex('force')] or their_heads == heads
129 return their_heads == [hex('force')] or their_heads == heads
130
130
131 # fail early if possible
131 # fail early if possible
132 if not check_heads():
132 if not check_heads():
133 bail('unsynced changes\n')
133 bail('unsynced changes\n')
134 return
134 return
135
135
136 req.respond(HTTP_OK, HGTYPE)
136 req.respond(HTTP_OK, HGTYPE)
137
137
138 # do not lock repo until all changegroup data is
138 # do not lock repo until all changegroup data is
139 # streamed. save to temporary file.
139 # streamed. save to temporary file.
140
140
141 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
141 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
142 fp = os.fdopen(fd, 'wb+')
142 fp = os.fdopen(fd, 'wb+')
143 try:
143 try:
144 length = int(req.env['CONTENT_LENGTH'])
144 length = int(req.env['CONTENT_LENGTH'])
145 for s in util.filechunkiter(req, limit=length):
145 for s in util.filechunkiter(req, limit=length):
146 fp.write(s)
146 fp.write(s)
147
147
148 try:
148 try:
149 lock = repo.lock()
149 lock = repo.lock()
150 try:
150 try:
151 if not check_heads():
151 if not check_heads():
152 req.write('0\n')
152 req.write('0\n')
153 req.write('unsynced changes\n')
153 req.write('unsynced changes\n')
154 return
154 return
155
155
156 fp.seek(0)
156 fp.seek(0)
157 header = fp.read(6)
157 header = fp.read(6)
158 if header.startswith('HG') and not header.startswith('HG10'):
158 if header.startswith('HG') and not header.startswith('HG10'):
159 raise ValueError('unknown bundle version')
159 raise ValueError('unknown bundle version')
160 elif header not in changegroupmod.bundletypes:
160 elif header not in changegroupmod.bundletypes:
161 raise ValueError('unknown bundle compression type')
161 raise ValueError('unknown bundle compression type')
162 gen = changegroupmod.unbundle(header, fp)
162 gen = changegroupmod.unbundle(header, fp)
163
163
164 # send addchangegroup output to client
164 # send addchangegroup output to client
165
165
166 oldio = sys.stdout, sys.stderr
166 oldio = sys.stdout, sys.stderr
167 sys.stderr = sys.stdout = cStringIO.StringIO()
167 sys.stderr = sys.stdout = cStringIO.StringIO()
168
168
169 try:
169 try:
170 url = 'remote:%s:%s' % (proto,
170 url = 'remote:%s:%s' % (proto,
171 req.env.get('REMOTE_HOST', ''))
171 req.env.get('REMOTE_HOST', ''))
172 try:
172 try:
173 ret = repo.addchangegroup(gen, 'serve', url)
173 ret = repo.addchangegroup(gen, 'serve', url)
174 except util.Abort, inst:
174 except util.Abort, inst:
175 sys.stdout.write("abort: %s\n" % inst)
175 sys.stdout.write("abort: %s\n" % inst)
176 ret = 0
176 ret = 0
177 finally:
177 finally:
178 val = sys.stdout.getvalue()
178 val = sys.stdout.getvalue()
179 sys.stdout, sys.stderr = oldio
179 sys.stdout, sys.stderr = oldio
180 req.write('%d\n' % ret)
180 req.write('%d\n' % ret)
181 req.write(val)
181 req.write(val)
182 finally:
182 finally:
183 del lock
183 del lock
184 except ValueError, inst:
184 except ValueError, inst:
185 req.write('0\n')
185 req.write('0\n')
186 req.write(str(inst) + '\n')
186 req.write(str(inst) + '\n')
187 except (OSError, IOError), inst:
187 except (OSError, IOError), inst:
188 req.write('0\n')
188 req.write('0\n')
189 filename = getattr(inst, 'filename', '')
189 filename = getattr(inst, 'filename', '')
190 # Don't send our filesystem layout to the client
190 # Don't send our filesystem layout to the client
191 if filename.startswith(repo.root):
191 if filename.startswith(repo.root):
192 filename = filename[len(repo.root)+1:]
192 filename = filename[len(repo.root)+1:]
193 else:
193 else:
194 filename = ''
194 filename = ''
195 error = getattr(inst, 'strerror', 'Unknown error')
195 error = getattr(inst, 'strerror', 'Unknown error')
196 if inst.errno == errno.ENOENT:
196 if inst.errno == errno.ENOENT:
197 code = HTTP_NOT_FOUND
197 code = HTTP_NOT_FOUND
198 else:
198 else:
199 code = HTTP_SERVER_ERROR
199 code = HTTP_SERVER_ERROR
200 req.respond(code)
200 req.respond(code)
201 req.write('%s: %s\n' % (error, filename))
201 req.write('%s: %s\n' % (error, filename))
202 finally:
202 finally:
203 fp.close()
203 fp.close()
204 os.unlink(tempname)
204 os.unlink(tempname)
205
205
206 def stream_out(repo, req):
206 def stream_out(repo, req):
207 req.respond(HTTP_OK, HGTYPE)
207 req.respond(HTTP_OK, HGTYPE)
208 for chunk in streamclone.stream_out(repo, untrusted=True):
208 streamclone.stream_out(repo, req, untrusted=True)
209 req.write(chunk)
@@ -1,209 +1,207 b''
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
6 # This software may be used and distributed according to the terms
7 # of the GNU General Public License, incorporated herein by reference.
7 # of the GNU General Public License, incorporated herein by reference.
8
8
9 from i18n import _
9 from i18n import _
10 from node import bin, hex
10 from node import bin, hex
11 import os, streamclone, sys, tempfile, util, hook
11 import os, streamclone, sys, tempfile, util, hook
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 getarg(self):
28 def getarg(self):
29 argline = self.fin.readline()[:-1]
29 argline = self.fin.readline()[:-1]
30 arg, l = argline.split()
30 arg, l = argline.split()
31 val = self.fin.read(int(l))
31 val = self.fin.read(int(l))
32 return arg, val
32 return arg, val
33
33
34 def respond(self, v):
34 def respond(self, v):
35 self.fout.write("%d\n" % len(v))
35 self.fout.write("%d\n" % len(v))
36 self.fout.write(v)
36 self.fout.write(v)
37 self.fout.flush()
37 self.fout.flush()
38
38
39 def serve_forever(self):
39 def serve_forever(self):
40 while self.serve_one(): pass
40 while self.serve_one(): pass
41 sys.exit(0)
41 sys.exit(0)
42
42
43 def serve_one(self):
43 def serve_one(self):
44 cmd = self.fin.readline()[:-1]
44 cmd = self.fin.readline()[:-1]
45 if cmd:
45 if cmd:
46 impl = getattr(self, 'do_' + cmd, None)
46 impl = getattr(self, 'do_' + cmd, None)
47 if impl: impl()
47 if impl: impl()
48 else: self.respond("")
48 else: self.respond("")
49 return cmd != ''
49 return cmd != ''
50
50
51 def do_lookup(self):
51 def do_lookup(self):
52 arg, key = self.getarg()
52 arg, key = self.getarg()
53 assert arg == 'key'
53 assert arg == 'key'
54 try:
54 try:
55 r = hex(self.repo.lookup(key))
55 r = hex(self.repo.lookup(key))
56 success = 1
56 success = 1
57 except Exception,inst:
57 except Exception,inst:
58 r = str(inst)
58 r = str(inst)
59 success = 0
59 success = 0
60 self.respond("%s %s\n" % (success, r))
60 self.respond("%s %s\n" % (success, r))
61
61
62 def do_heads(self):
62 def do_heads(self):
63 h = self.repo.heads()
63 h = self.repo.heads()
64 self.respond(" ".join(map(hex, h)) + "\n")
64 self.respond(" ".join(map(hex, h)) + "\n")
65
65
66 def do_hello(self):
66 def do_hello(self):
67 '''the hello command returns a set of lines describing various
67 '''the hello command returns a set of lines describing various
68 interesting things about the server, in an RFC822-like format.
68 interesting things about the server, in an RFC822-like format.
69 Currently the only one defined is "capabilities", which
69 Currently the only one defined is "capabilities", which
70 consists of a line in the form:
70 consists of a line in the form:
71
71
72 capabilities: space separated list of tokens
72 capabilities: space separated list of tokens
73 '''
73 '''
74
74
75 caps = ['unbundle', 'lookup', 'changegroupsubset']
75 caps = ['unbundle', 'lookup', 'changegroupsubset']
76 if self.ui.configbool('server', 'uncompressed'):
76 if self.ui.configbool('server', 'uncompressed'):
77 caps.append('stream=%d' % self.repo.changelog.version)
77 caps.append('stream=%d' % self.repo.changelog.version)
78 self.respond("capabilities: %s\n" % (' '.join(caps),))
78 self.respond("capabilities: %s\n" % (' '.join(caps),))
79
79
80 def do_lock(self):
80 def do_lock(self):
81 '''DEPRECATED - allowing remote client to lock repo is not safe'''
81 '''DEPRECATED - allowing remote client to lock repo is not safe'''
82
82
83 self.lock = self.repo.lock()
83 self.lock = self.repo.lock()
84 self.respond("")
84 self.respond("")
85
85
86 def do_unlock(self):
86 def do_unlock(self):
87 '''DEPRECATED'''
87 '''DEPRECATED'''
88
88
89 if self.lock:
89 if self.lock:
90 self.lock.release()
90 self.lock.release()
91 self.lock = None
91 self.lock = None
92 self.respond("")
92 self.respond("")
93
93
94 def do_branches(self):
94 def do_branches(self):
95 arg, nodes = self.getarg()
95 arg, nodes = self.getarg()
96 nodes = map(bin, nodes.split(" "))
96 nodes = map(bin, nodes.split(" "))
97 r = []
97 r = []
98 for b in self.repo.branches(nodes):
98 for b in self.repo.branches(nodes):
99 r.append(" ".join(map(hex, b)) + "\n")
99 r.append(" ".join(map(hex, b)) + "\n")
100 self.respond("".join(r))
100 self.respond("".join(r))
101
101
102 def do_between(self):
102 def do_between(self):
103 arg, pairs = self.getarg()
103 arg, pairs = self.getarg()
104 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
104 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
105 r = []
105 r = []
106 for b in self.repo.between(pairs):
106 for b in self.repo.between(pairs):
107 r.append(" ".join(map(hex, b)) + "\n")
107 r.append(" ".join(map(hex, b)) + "\n")
108 self.respond("".join(r))
108 self.respond("".join(r))
109
109
110 def do_changegroup(self):
110 def do_changegroup(self):
111 nodes = []
111 nodes = []
112 arg, roots = self.getarg()
112 arg, roots = self.getarg()
113 nodes = map(bin, roots.split(" "))
113 nodes = map(bin, roots.split(" "))
114
114
115 cg = self.repo.changegroup(nodes, 'serve')
115 cg = self.repo.changegroup(nodes, 'serve')
116 while True:
116 while True:
117 d = cg.read(4096)
117 d = cg.read(4096)
118 if not d:
118 if not d:
119 break
119 break
120 self.fout.write(d)
120 self.fout.write(d)
121
121
122 self.fout.flush()
122 self.fout.flush()
123
123
124 def do_changegroupsubset(self):
124 def do_changegroupsubset(self):
125 bases = []
125 bases = []
126 heads = []
126 heads = []
127 argmap = dict([self.getarg(), self.getarg()])
127 argmap = dict([self.getarg(), self.getarg()])
128 bases = [bin(n) for n in argmap['bases'].split(' ')]
128 bases = [bin(n) for n in argmap['bases'].split(' ')]
129 heads = [bin(n) for n in argmap['heads'].split(' ')]
129 heads = [bin(n) for n in argmap['heads'].split(' ')]
130
130
131 cg = self.repo.changegroupsubset(bases, heads, 'serve')
131 cg = self.repo.changegroupsubset(bases, heads, 'serve')
132 while True:
132 while True:
133 d = cg.read(4096)
133 d = cg.read(4096)
134 if not d:
134 if not d:
135 break
135 break
136 self.fout.write(d)
136 self.fout.write(d)
137
137
138 self.fout.flush()
138 self.fout.flush()
139
139
140 def do_addchangegroup(self):
140 def do_addchangegroup(self):
141 '''DEPRECATED'''
141 '''DEPRECATED'''
142
142
143 if not self.lock:
143 if not self.lock:
144 self.respond("not locked")
144 self.respond("not locked")
145 return
145 return
146
146
147 self.respond("")
147 self.respond("")
148 r = self.repo.addchangegroup(self.fin, 'serve', self.client_url())
148 r = self.repo.addchangegroup(self.fin, 'serve', self.client_url())
149 self.respond(str(r))
149 self.respond(str(r))
150
150
151 def client_url(self):
151 def client_url(self):
152 client = os.environ.get('SSH_CLIENT', '').split(' ', 1)[0]
152 client = os.environ.get('SSH_CLIENT', '').split(' ', 1)[0]
153 return 'remote:ssh:' + client
153 return 'remote:ssh:' + client
154
154
155 def do_unbundle(self):
155 def do_unbundle(self):
156 their_heads = self.getarg()[1].split()
156 their_heads = self.getarg()[1].split()
157
157
158 def check_heads():
158 def check_heads():
159 heads = map(hex, self.repo.heads())
159 heads = map(hex, self.repo.heads())
160 return their_heads == [hex('force')] or their_heads == heads
160 return their_heads == [hex('force')] or their_heads == heads
161
161
162 # fail early if possible
162 # fail early if possible
163 if not check_heads():
163 if not check_heads():
164 self.respond(_('unsynced changes'))
164 self.respond(_('unsynced changes'))
165 return
165 return
166
166
167 self.respond('')
167 self.respond('')
168
168
169 # write bundle data to temporary file because it can be big
169 # write bundle data to temporary file because it can be big
170 tempname = fp = None
170 tempname = fp = None
171 try:
171 try:
172 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
172 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
173 fp = os.fdopen(fd, 'wb+')
173 fp = os.fdopen(fd, 'wb+')
174
174
175 count = int(self.fin.readline())
175 count = int(self.fin.readline())
176 while count:
176 while count:
177 fp.write(self.fin.read(count))
177 fp.write(self.fin.read(count))
178 count = int(self.fin.readline())
178 count = int(self.fin.readline())
179
179
180 was_locked = self.lock is not None
180 was_locked = self.lock is not None
181 if not was_locked:
181 if not was_locked:
182 self.lock = self.repo.lock()
182 self.lock = self.repo.lock()
183 try:
183 try:
184 if not check_heads():
184 if not check_heads():
185 # someone else committed/pushed/unbundled while we
185 # someone else committed/pushed/unbundled while we
186 # were transferring data
186 # were transferring data
187 self.respond(_('unsynced changes'))
187 self.respond(_('unsynced changes'))
188 return
188 return
189 self.respond('')
189 self.respond('')
190
190
191 # push can proceed
191 # push can proceed
192
192
193 fp.seek(0)
193 fp.seek(0)
194 r = self.repo.addchangegroup(fp, 'serve', self.client_url())
194 r = self.repo.addchangegroup(fp, 'serve', self.client_url())
195 self.respond(str(r))
195 self.respond(str(r))
196 finally:
196 finally:
197 if not was_locked:
197 if not was_locked:
198 self.lock.release()
198 self.lock.release()
199 self.lock = None
199 self.lock = None
200 finally:
200 finally:
201 if fp is not None:
201 if fp is not None:
202 fp.close()
202 fp.close()
203 if tempname is not None:
203 if tempname is not None:
204 os.unlink(tempname)
204 os.unlink(tempname)
205
205
206 def do_stream_out(self):
206 def do_stream_out(self):
207 for chunk in streamclone.stream_out(self.repo):
207 streamclone.stream_out(self.repo, self.fout)
208 self.fout.write(chunk)
209 self.fout.flush()
@@ -1,91 +1,93 b''
1 # streamclone.py - streaming clone server support for mercurial
1 # streamclone.py - streaming clone server support for mercurial
2 #
2 #
3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 import os, osutil, stat, util, lock
8 import os, osutil, stat, util, lock
9
9
10 # if server supports streaming clone, it advertises "stream"
10 # if server supports streaming clone, it advertises "stream"
11 # capability with value that is version+flags of repo it is serving.
11 # capability with value that is version+flags of repo it is serving.
12 # client only streams if it can read that repo format.
12 # client only streams if it can read that repo format.
13
13
14 def walkrepo(root):
14 def walkrepo(root):
15 '''iterate over metadata files in repository.
15 '''iterate over metadata files in repository.
16 walk in natural (sorted) order.
16 walk in natural (sorted) order.
17 yields 2-tuples: name of .d or .i file, size of file.'''
17 yields 2-tuples: name of .d or .i file, size of file.'''
18
18
19 strip_count = len(root) + len(os.sep)
19 strip_count = len(root) + len(os.sep)
20 def walk(path, recurse):
20 def walk(path, recurse):
21 for e, kind, st in osutil.listdir(path, stat=True):
21 for e, kind, st in osutil.listdir(path, stat=True):
22 pe = os.path.join(path, e)
22 pe = os.path.join(path, e)
23 if kind == stat.S_IFDIR:
23 if kind == stat.S_IFDIR:
24 if recurse:
24 if recurse:
25 for x in walk(pe, True):
25 for x in walk(pe, True):
26 yield x
26 yield x
27 else:
27 else:
28 if kind != stat.S_IFREG or len(e) < 2:
28 if kind != stat.S_IFREG or len(e) < 2:
29 continue
29 continue
30 sfx = e[-2:]
30 sfx = e[-2:]
31 if sfx in ('.d', '.i'):
31 if sfx in ('.d', '.i'):
32 yield pe[strip_count:], st.st_size
32 yield pe[strip_count:], st.st_size
33 # write file data first
33 # write file data first
34 for x in walk(os.path.join(root, 'data'), True):
34 for x in walk(os.path.join(root, 'data'), True):
35 yield x
35 yield x
36 # write manifest before changelog
36 # write manifest before changelog
37 meta = util.sort(walk(root, False))
37 meta = util.sort(walk(root, False))
38 meta.reverse()
38 meta.reverse()
39 for x in meta:
39 for x in meta:
40 yield x
40 yield x
41
41
42 # stream file format is simple.
42 # stream file format is simple.
43 #
43 #
44 # server writes out line that says how many files, how many total
44 # server writes out line that says how many files, how many total
45 # bytes. separator is ascii space, byte counts are strings.
45 # bytes. separator is ascii space, byte counts are strings.
46 #
46 #
47 # then for each file:
47 # then for each file:
48 #
48 #
49 # server writes out line that says file name, how many bytes in
49 # server writes out line that says file name, how many bytes in
50 # file. separator is ascii nul, byte count is string.
50 # file. separator is ascii nul, byte count is string.
51 #
51 #
52 # server writes out raw file data.
52 # server writes out raw file data.
53
53
54 def stream_out(repo, untrusted=False):
54 def stream_out(repo, fileobj, untrusted=False):
55 '''stream out all metadata files in repository.
55 '''stream out all metadata files in repository.
56 writes to file-like object, must support write() and optional flush().'''
56 writes to file-like object, must support write() and optional flush().'''
57
57
58 if not repo.ui.configbool('server', 'uncompressed', untrusted=untrusted):
58 if not repo.ui.configbool('server', 'uncompressed', untrusted=untrusted):
59 yield '1\n'
59 fileobj.write('1\n')
60 return
60 return
61
61
62 # get consistent snapshot of repo. lock during scan so lock not
62 # get consistent snapshot of repo. lock during scan so lock not
63 # needed while we stream, and commits can happen.
63 # needed while we stream, and commits can happen.
64 repolock = None
64 repolock = None
65 try:
65 try:
66 try:
66 try:
67 repolock = repo.lock()
67 repolock = repo.lock()
68 except (lock.LockHeld, lock.LockUnavailable), inst:
68 except (lock.LockHeld, lock.LockUnavailable), inst:
69 repo.ui.warn('locking the repository failed: %s\n' % (inst,))
69 repo.ui.warn('locking the repository failed: %s\n' % (inst,))
70 yield '2\n'
70 fileobj.write('2\n')
71 return
71 return
72
72
73 yield '0\n'
73 fileobj.write('0\n')
74 repo.ui.debug('scanning\n')
74 repo.ui.debug('scanning\n')
75 entries = []
75 entries = []
76 total_bytes = 0
76 total_bytes = 0
77 for name, size in walkrepo(repo.spath):
77 for name, size in walkrepo(repo.spath):
78 name = repo.decodefn(util.pconvert(name))
78 name = repo.decodefn(util.pconvert(name))
79 entries.append((name, size))
79 entries.append((name, size))
80 total_bytes += size
80 total_bytes += size
81 finally:
81 finally:
82 del repolock
82 del repolock
83
83
84 repo.ui.debug('%d files, %d bytes to transfer\n' %
84 repo.ui.debug('%d files, %d bytes to transfer\n' %
85 (len(entries), total_bytes))
85 (len(entries), total_bytes))
86 yield '%d %d\n' % (len(entries), total_bytes)
86 fileobj.write('%d %d\n' % (len(entries), total_bytes))
87 for name, size in entries:
87 for name, size in entries:
88 repo.ui.debug('sending %s (%d bytes)\n' % (name, size))
88 repo.ui.debug('sending %s (%d bytes)\n' % (name, size))
89 yield '%s\0%d\n' % (name, size)
89 fileobj.write('%s\0%d\n' % (name, size))
90 for chunk in util.filechunkiter(repo.sopener(name), limit=size):
90 for chunk in util.filechunkiter(repo.sopener(name), limit=size):
91 yield chunk
91 fileobj.write(chunk)
92 flush = getattr(fileobj, 'flush', None)
93 if flush: flush()
General Comments 0
You need to be logged in to leave comments. Login now