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 from node import nullrev |
|
10 import mdiff, util |
10 import struct, os, bz2, zlib, tempfile |
11 import struct, os, bz2, zlib, tempfile |
|
12 |
|
13 _BUNDLE10_DELTA_HEADER = "20s20s20s20s" |
11 |
14 |
12 def readexactly(stream, n): |
15 def readexactly(stream, n): |
13 '''read n bytes from stream.read and abort if less was available''' |
16 '''read n bytes from stream.read and abort if less was available''' |
14 s = stream.read(n) |
17 s = stream.read(n) |
15 if len(s) < n: |
18 if len(s) < n: |
41 return x |
44 return x |
42 def flush(self): |
45 def flush(self): |
43 return "" |
46 return "" |
44 |
47 |
45 bundletypes = { |
48 bundletypes = { |
46 "": ("", nocompress), |
49 "": ("", nocompress), # only when using unbundle on ssh and old http servers |
|
50 # since the unification ssh accepts a header but there |
|
51 # is no capability signaling it. |
47 "HG10UN": ("HG10UN", nocompress), |
52 "HG10UN": ("HG10UN", nocompress), |
48 "HG10BZ": ("HG10", lambda: bz2.BZ2Compressor()), |
53 "HG10BZ": ("HG10", lambda: bz2.BZ2Compressor()), |
49 "HG10GZ": ("HG10GZ", lambda: zlib.compressobj()), |
54 "HG10GZ": ("HG10GZ", lambda: zlib.compressobj()), |
50 } |
55 } |
51 |
|
52 def collector(cl, mmfs, files): |
|
53 # Gather information about changeset nodes going out in a bundle. |
|
54 # We want to gather manifests needed and filelogs affected. |
|
55 def collect(node): |
|
56 c = cl.read(node) |
|
57 files.update(c[3]) |
|
58 mmfs.setdefault(c[0], node) |
|
59 return collect |
|
60 |
56 |
61 # hgweb uses this list to communicate its preferred type |
57 # hgweb uses this list to communicate its preferred type |
62 bundlepriority = ['HG10GZ', 'HG10BZ', 'HG10UN'] |
58 bundlepriority = ['HG10GZ', 'HG10BZ', 'HG10UN'] |
63 |
59 |
64 def writebundle(cg, filename, bundletype): |
60 def writebundle(cg, filename, bundletype): |
133 else: |
129 else: |
134 raise util.Abort("unknown bundle compression '%s'" % alg) |
130 raise util.Abort("unknown bundle compression '%s'" % alg) |
135 return util.chunkbuffer(generator(fh)) |
131 return util.chunkbuffer(generator(fh)) |
136 |
132 |
137 class unbundle10(object): |
133 class unbundle10(object): |
|
134 deltaheader = _BUNDLE10_DELTA_HEADER |
|
135 deltaheadersize = struct.calcsize(deltaheader) |
138 def __init__(self, fh, alg): |
136 def __init__(self, fh, alg): |
139 self._stream = decompressor(fh, alg) |
137 self._stream = decompressor(fh, alg) |
140 self._type = alg |
138 self._type = alg |
141 self.callback = None |
139 self.callback = None |
142 def compressed(self): |
140 def compressed(self): |
159 return 0 |
157 return 0 |
160 if self.callback: |
158 if self.callback: |
161 self.callback() |
159 self.callback() |
162 return l - 4 |
160 return l - 4 |
163 |
161 |
164 def chunk(self): |
162 def changelogheader(self): |
165 """return the next chunk from changegroup 'source' as a string""" |
163 """v10 does not have a changelog header chunk""" |
166 l = self.chunklength() |
164 return {} |
167 return readexactly(self._stream, l) |
165 |
168 |
166 def manifestheader(self): |
169 def parsechunk(self): |
167 """v10 does not have a manifest header chunk""" |
|
168 return {} |
|
169 |
|
170 def filelogheader(self): |
|
171 """return the header of the filelogs chunk, v10 only has the filename""" |
170 l = self.chunklength() |
172 l = self.chunklength() |
171 if not l: |
173 if not l: |
172 return {} |
174 return {} |
173 h = readexactly(self._stream, 80) |
175 fname = readexactly(self._stream, l) |
174 node, p1, p2, cs = struct.unpack("20s20s20s20s", h) |
176 return dict(filename=fname) |
175 data = readexactly(self._stream, l - 80) |
177 |
176 return dict(node=node, p1=p1, p2=p2, cs=cs, data=data) |
178 def _deltaheader(self, headertuple, prevnode): |
|
179 node, p1, p2, cs = headertuple |
|
180 if prevnode is None: |
|
181 deltabase = p1 |
|
182 else: |
|
183 deltabase = prevnode |
|
184 return node, p1, p2, deltabase, cs |
|
185 |
|
186 def deltachunk(self, prevnode): |
|
187 l = self.chunklength() |
|
188 if not l: |
|
189 return {} |
|
190 headerdata = readexactly(self._stream, self.deltaheadersize) |
|
191 header = struct.unpack(self.deltaheader, headerdata) |
|
192 delta = readexactly(self._stream, l - self.deltaheadersize) |
|
193 node, p1, p2, deltabase, cs = self._deltaheader(header, prevnode) |
|
194 return dict(node=node, p1=p1, p2=p2, cs=cs, |
|
195 deltabase=deltabase, delta=delta) |
177 |
196 |
178 class headerlessfixup(object): |
197 class headerlessfixup(object): |
179 def __init__(self, fh, h): |
198 def __init__(self, fh, h): |
180 self._h = h |
199 self._h = h |
181 self._fh = fh |
200 self._fh = fh |
201 if magic != 'HG': |
220 if magic != 'HG': |
202 raise util.Abort(_('%s: not a Mercurial bundle') % fname) |
221 raise util.Abort(_('%s: not a Mercurial bundle') % fname) |
203 if version != '10': |
222 if version != '10': |
204 raise util.Abort(_('%s: unknown bundle version %s') % (fname, version)) |
223 raise util.Abort(_('%s: unknown bundle version %s') % (fname, version)) |
205 return unbundle10(fh, alg) |
224 return unbundle10(fh, alg) |
|
225 |
|
226 class bundle10(object): |
|
227 deltaheader = _BUNDLE10_DELTA_HEADER |
|
228 def __init__(self, lookup): |
|
229 self._lookup = lookup |
|
230 def close(self): |
|
231 return closechunk() |
|
232 def fileheader(self, fname): |
|
233 return chunkheader(len(fname)) + fname |
|
234 def revchunk(self, revlog, rev, prev): |
|
235 node = revlog.node(rev) |
|
236 p1, p2 = revlog.parentrevs(rev) |
|
237 base = prev |
|
238 |
|
239 prefix = '' |
|
240 if base == nullrev: |
|
241 delta = revlog.revision(node) |
|
242 prefix = mdiff.trivialdiffheader(len(delta)) |
|
243 else: |
|
244 delta = revlog.revdiff(base, rev) |
|
245 linknode = self._lookup(revlog, node) |
|
246 p1n, p2n = revlog.parents(node) |
|
247 basenode = revlog.node(base) |
|
248 meta = self.builddeltaheader(node, p1n, p2n, basenode, linknode) |
|
249 meta += prefix |
|
250 l = len(meta) + len(delta) |
|
251 yield chunkheader(l) |
|
252 yield meta |
|
253 yield delta |
|
254 def builddeltaheader(self, node, p1n, p2n, basenode, linknode): |
|
255 # do nothing with basenode, it is implicitly the previous one in HG10 |
|
256 return struct.pack(self.deltaheader, node, p1n, p2n, linknode) |