##// END OF EJS Templates
changegroup: fix typo introduced in 9f2c407caf34
Jim Hague -
r13459:acbe171c stable
parent child Browse files
Show More
@@ -1,205 +1,205 b''
1 # changegroup.py - Mercurial changegroup manipulation functions
1 # changegroup.py - Mercurial changegroup manipulation functions
2 #
2 #
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006 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 from i18n import _
8 from i18n import _
9 import util
9 import util
10 import struct, os, bz2, zlib, tempfile
10 import struct, os, bz2, zlib, tempfile
11
11
12 def readexactly(stream, n):
12 def readexactly(stream, n):
13 '''read n bytes from stream.read and abort if less was available'''
13 '''read n bytes from stream.read and abort if less was available'''
14 s = stream.read(n)
14 s = stream.read(n)
15 if len(s) < n:
15 if len(s) < n:
16 raise util.Abort(_("stream ended unexpectedly"
16 raise util.Abort(_("stream ended unexpectedly"
17 " (got %d bytes, expected %d)")
17 " (got %d bytes, expected %d)")
18 % (len(s), n))
18 % (len(s), n))
19 return s
19 return s
20
20
21 def getchunk(stream):
21 def getchunk(stream):
22 """return the next chunk from stream as a string"""
22 """return the next chunk from stream as a string"""
23 d = readexactly(stream, 4)
23 d = readexactly(stream, 4)
24 l = struct.unpack(">l", d)[0]
24 l = struct.unpack(">l", d)[0]
25 if l <= 4:
25 if l <= 4:
26 if l:
26 if l:
27 raise util.Abort(_("invalid chunk length %d") % l)
27 raise util.Abort(_("invalid chunk length %d") % l)
28 return ""
28 return ""
29 return readexactly(stream, l - 4)
29 return readexactly(stream, l - 4)
30
30
31 def chunkheader(length):
31 def chunkheader(length):
32 """return a changegroup chunk header (string)"""
32 """return a changegroup chunk header (string)"""
33 return struct.pack(">l", length + 4)
33 return struct.pack(">l", length + 4)
34
34
35 def closechunk():
35 def closechunk():
36 """return a changegroup chunk header (string) for a zero-length chunk"""
36 """return a changegroup chunk header (string) for a zero-length chunk"""
37 return struct.pack(">l", 0)
37 return struct.pack(">l", 0)
38
38
39 class nocompress(object):
39 class nocompress(object):
40 def compress(self, x):
40 def compress(self, x):
41 return x
41 return x
42 def flush(self):
42 def flush(self):
43 return ""
43 return ""
44
44
45 bundletypes = {
45 bundletypes = {
46 "": ("", nocompress),
46 "": ("", nocompress),
47 "HG10UN": ("HG10UN", nocompress),
47 "HG10UN": ("HG10UN", nocompress),
48 "HG10BZ": ("HG10", lambda: bz2.BZ2Compressor()),
48 "HG10BZ": ("HG10", lambda: bz2.BZ2Compressor()),
49 "HG10GZ": ("HG10GZ", lambda: zlib.compressobj()),
49 "HG10GZ": ("HG10GZ", lambda: zlib.compressobj()),
50 }
50 }
51
51
52 def collector(cl, mmfs, files):
52 def collector(cl, mmfs, files):
53 # Gather information about changeset nodes going out in a bundle.
53 # Gather information about changeset nodes going out in a bundle.
54 # We want to gather manifests needed and filelogs affected.
54 # We want to gather manifests needed and filelogs affected.
55 def collect(node):
55 def collect(node):
56 c = cl.read(node)
56 c = cl.read(node)
57 files.update(c[3])
57 files.update(c[3])
58 mmfs.setdefault(c[0], node)
58 mmfs.setdefault(c[0], node)
59 return collect
59 return collect
60
60
61 # hgweb uses this list to communicate its preferred type
61 # hgweb uses this list to communicate its preferred type
62 bundlepriority = ['HG10GZ', 'HG10BZ', 'HG10UN']
62 bundlepriority = ['HG10GZ', 'HG10BZ', 'HG10UN']
63
63
64 def writebundle(cg, filename, bundletype):
64 def writebundle(cg, filename, bundletype):
65 """Write a bundle file and return its filename.
65 """Write a bundle file and return its filename.
66
66
67 Existing files will not be overwritten.
67 Existing files will not be overwritten.
68 If no filename is specified, a temporary file is created.
68 If no filename is specified, a temporary file is created.
69 bz2 compression can be turned off.
69 bz2 compression can be turned off.
70 The bundle file will be deleted in case of errors.
70 The bundle file will be deleted in case of errors.
71 """
71 """
72
72
73 fh = None
73 fh = None
74 cleanup = None
74 cleanup = None
75 try:
75 try:
76 if filename:
76 if filename:
77 fh = open(filename, "wb")
77 fh = open(filename, "wb")
78 else:
78 else:
79 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
79 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
80 fh = os.fdopen(fd, "wb")
80 fh = os.fdopen(fd, "wb")
81 cleanup = filename
81 cleanup = filename
82
82
83 header, compressor = bundletypes[bundletype]
83 header, compressor = bundletypes[bundletype]
84 fh.write(header)
84 fh.write(header)
85 z = compressor()
85 z = compressor()
86
86
87 # parse the changegroup data, otherwise we will block
87 # parse the changegroup data, otherwise we will block
88 # in case of sshrepo because we don't know the end of the stream
88 # in case of sshrepo because we don't know the end of the stream
89
89
90 # an empty chunkgroup is the end of the changegroup
90 # an empty chunkgroup is the end of the changegroup
91 # a changegroup has at least 2 chunkgroups (changelog and manifest).
91 # a changegroup has at least 2 chunkgroups (changelog and manifest).
92 # after that, an empty chunkgroup is the end of the changegroup
92 # after that, an empty chunkgroup is the end of the changegroup
93 empty = False
93 empty = False
94 count = 0
94 count = 0
95 while not empty or count <= 2:
95 while not empty or count <= 2:
96 empty = True
96 empty = True
97 count += 1
97 count += 1
98 while 1:
98 while 1:
99 chunk = getchunk(cg)
99 chunk = getchunk(cg)
100 if not chunk:
100 if not chunk:
101 break
101 break
102 empty = False
102 empty = False
103 fh.write(z.compress(chunkheader(len(chunk))))
103 fh.write(z.compress(chunkheader(len(chunk))))
104 pos = 0
104 pos = 0
105 while pos < len(chunk):
105 while pos < len(chunk):
106 next = pos + 2**20
106 next = pos + 2**20
107 fh.write(z.compress(chunk[pos:next]))
107 fh.write(z.compress(chunk[pos:next]))
108 pos = next
108 pos = next
109 fh.write(z.compress(closechunk()))
109 fh.write(z.compress(closechunk()))
110 fh.write(z.flush())
110 fh.write(z.flush())
111 cleanup = None
111 cleanup = None
112 return filename
112 return filename
113 finally:
113 finally:
114 if fh is not None:
114 if fh is not None:
115 fh.close()
115 fh.close()
116 if cleanup is not None:
116 if cleanup is not None:
117 os.unlink(cleanup)
117 os.unlink(cleanup)
118
118
119 def decompressor(fh, alg):
119 def decompressor(fh, alg):
120 if alg == 'UN':
120 if alg == 'UN':
121 return fh
121 return fh
122 elif alg == 'GZ':
122 elif alg == 'GZ':
123 def generator(f):
123 def generator(f):
124 zd = zlib.decompressobj()
124 zd = zlib.decompressobj()
125 for chunk in f:
125 for chunk in f:
126 yield zd.decompress(chunk)
126 yield zd.decompress(chunk)
127 elif alg == 'BZ':
127 elif alg == 'BZ':
128 def generator(f):
128 def generator(f):
129 zd = bz2.BZ2Decompressor()
129 zd = bz2.BZ2Decompressor()
130 zd.decompress("BZ")
130 zd.decompress("BZ")
131 for chunk in util.filechunkiter(f, 4096):
131 for chunk in util.filechunkiter(f, 4096):
132 yield zd.decompress(chunk)
132 yield zd.decompress(chunk)
133 else:
133 else:
134 raise util.Abort("unknown bundle compression '%s'" % alg)
134 raise util.Abort("unknown bundle compression '%s'" % alg)
135 return util.chunkbuffer(generator(fh))
135 return util.chunkbuffer(generator(fh))
136
136
137 class unbundle10(object):
137 class unbundle10(object):
138 def __init__(self, fh, alg):
138 def __init__(self, fh, alg):
139 self._stream = decompressor(fh, alg)
139 self._stream = decompressor(fh, alg)
140 self._type = alg
140 self._type = alg
141 self.callback = None
141 self.callback = None
142 def compressed(self):
142 def compressed(self):
143 return self._type != 'UN'
143 return self._type != 'UN'
144 def read(self, l):
144 def read(self, l):
145 return self._stream.read(l)
145 return self._stream.read(l)
146 def seek(self, pos):
146 def seek(self, pos):
147 return self._stream.seek(pos)
147 return self._stream.seek(pos)
148 def tell(self):
148 def tell(self):
149 return self._stream.tell()
149 return self._stream.tell()
150 def close(self):
150 def close(self):
151 return self._stream.close()
151 return self._stream.close()
152
152
153 def chunklength(self):
153 def chunklength(self):
154 d = readexactly(stream, 4)
154 d = readexactly(self._stream, 4)
155 l = struct.unpack(">l", d)[0]
155 l = struct.unpack(">l", d)[0]
156 if l <= 4:
156 if l <= 4:
157 if l:
157 if l:
158 raise util.Abort(_("invalid chunk length %d") % l)
158 raise util.Abort(_("invalid chunk length %d") % l)
159 return 0
159 return 0
160 if self.callback:
160 if self.callback:
161 self.callback()
161 self.callback()
162 return l - 4
162 return l - 4
163
163
164 def chunk(self):
164 def chunk(self):
165 """return the next chunk from changegroup 'source' as a string"""
165 """return the next chunk from changegroup 'source' as a string"""
166 l = self.chunklength()
166 l = self.chunklength()
167 return readexactly(self._stream, l)
167 return readexactly(self._stream, l)
168
168
169 def parsechunk(self):
169 def parsechunk(self):
170 l = self.chunklength()
170 l = self.chunklength()
171 if not l:
171 if not l:
172 return {}
172 return {}
173 h = readexactly(self._stream, 80)
173 h = readexactly(self._stream, 80)
174 node, p1, p2, cs = struct.unpack("20s20s20s20s", h)
174 node, p1, p2, cs = struct.unpack("20s20s20s20s", h)
175 data = readexactly(self._stream, l - 80)
175 data = readexactly(self._stream, l - 80)
176 return dict(node=node, p1=p1, p2=p2, cs=cs, data=data)
176 return dict(node=node, p1=p1, p2=p2, cs=cs, data=data)
177
177
178 class headerlessfixup(object):
178 class headerlessfixup(object):
179 def __init__(self, fh, h):
179 def __init__(self, fh, h):
180 self._h = h
180 self._h = h
181 self._fh = fh
181 self._fh = fh
182 def read(self, n):
182 def read(self, n):
183 if self._h:
183 if self._h:
184 d, self._h = self._h[:n], self._h[n:]
184 d, self._h = self._h[:n], self._h[n:]
185 if len(d) < n:
185 if len(d) < n:
186 d += readexactly(self._fh, n - len(d))
186 d += readexactly(self._fh, n - len(d))
187 return d
187 return d
188 return readexactly(self._fh, n)
188 return readexactly(self._fh, n)
189
189
190 def readbundle(fh, fname):
190 def readbundle(fh, fname):
191 header = readexactly(fh, 6)
191 header = readexactly(fh, 6)
192
192
193 if not fname:
193 if not fname:
194 fname = "stream"
194 fname = "stream"
195 if not header.startswith('HG') and header.startswith('\0'):
195 if not header.startswith('HG') and header.startswith('\0'):
196 fh = headerlessfixup(fh, header)
196 fh = headerlessfixup(fh, header)
197 header = "HG10UN"
197 header = "HG10UN"
198
198
199 magic, version, alg = header[0:2], header[2:4], header[4:6]
199 magic, version, alg = header[0:2], header[2:4], header[4:6]
200
200
201 if magic != 'HG':
201 if magic != 'HG':
202 raise util.Abort(_('%s: not a Mercurial bundle') % fname)
202 raise util.Abort(_('%s: not a Mercurial bundle') % fname)
203 if version != '10':
203 if version != '10':
204 raise util.Abort(_('%s: unknown bundle version %s') % (fname, version))
204 raise util.Abort(_('%s: unknown bundle version %s') % (fname, version))
205 return unbundle10(fh, alg)
205 return unbundle10(fh, alg)
General Comments 0
You need to be logged in to leave comments. Login now