##// END OF EJS Templates
bundlerepo: it was meant to be revdiff() instead of chunk()
Benoit Boissinot -
r4028:540d1059 default
parent child Browse files
Show More
@@ -1,256 +1,256
1 """
1 """
2 bundlerepo.py - repository class for viewing uncompressed bundles
2 bundlerepo.py - repository class for viewing uncompressed bundles
3
3
4 This provides a read-only repository interface to bundles as if
4 This provides a read-only repository interface to bundles as if
5 they were part of the actual repository.
5 they were part of the actual repository.
6
6
7 Copyright 2006 Benoit Boissinot <benoit.boissinot@ens-lyon.org>
7 Copyright 2006 Benoit Boissinot <benoit.boissinot@ens-lyon.org>
8
8
9 This software may be used and distributed according to the terms
9 This software may be used and distributed according to the terms
10 of the GNU General Public License, incorporated herein by reference.
10 of the GNU General Public License, incorporated herein by reference.
11 """
11 """
12
12
13 from node import *
13 from node import *
14 from i18n import gettext as _
14 from i18n import gettext as _
15 from demandload import demandload
15 from demandload import demandload
16 demandload(globals(), "changegroup util os struct bz2 tempfile")
16 demandload(globals(), "changegroup util os struct bz2 tempfile")
17
17
18 import localrepo, changelog, manifest, filelog, revlog
18 import localrepo, changelog, manifest, filelog, revlog
19
19
20 class bundlerevlog(revlog.revlog):
20 class bundlerevlog(revlog.revlog):
21 def __init__(self, opener, indexfile, datafile, bundlefile,
21 def __init__(self, opener, indexfile, datafile, bundlefile,
22 linkmapper=None):
22 linkmapper=None):
23 # How it works:
23 # How it works:
24 # to retrieve a revision, we need to know the offset of
24 # to retrieve a revision, we need to know the offset of
25 # the revision in the bundlefile (an opened file).
25 # the revision in the bundlefile (an opened file).
26 #
26 #
27 # We store this offset in the index (start), to differentiate a
27 # We store this offset in the index (start), to differentiate a
28 # rev in the bundle and from a rev in the revlog, we check
28 # rev in the bundle and from a rev in the revlog, we check
29 # len(index[r]). If the tuple is bigger than 7, it is a bundle
29 # len(index[r]). If the tuple is bigger than 7, it is a bundle
30 # (it is bigger since we store the node to which the delta is)
30 # (it is bigger since we store the node to which the delta is)
31 #
31 #
32 revlog.revlog.__init__(self, opener, indexfile, datafile)
32 revlog.revlog.__init__(self, opener, indexfile, datafile)
33 self.bundlefile = bundlefile
33 self.bundlefile = bundlefile
34 self.basemap = {}
34 self.basemap = {}
35 def chunkpositer():
35 def chunkpositer():
36 for chunk in changegroup.chunkiter(bundlefile):
36 for chunk in changegroup.chunkiter(bundlefile):
37 pos = bundlefile.tell()
37 pos = bundlefile.tell()
38 yield chunk, pos - len(chunk)
38 yield chunk, pos - len(chunk)
39 n = self.count()
39 n = self.count()
40 prev = None
40 prev = None
41 for chunk, start in chunkpositer():
41 for chunk, start in chunkpositer():
42 size = len(chunk)
42 size = len(chunk)
43 if size < 80:
43 if size < 80:
44 raise util.Abort("invalid changegroup")
44 raise util.Abort("invalid changegroup")
45 start += 80
45 start += 80
46 size -= 80
46 size -= 80
47 node, p1, p2, cs = struct.unpack("20s20s20s20s", chunk[:80])
47 node, p1, p2, cs = struct.unpack("20s20s20s20s", chunk[:80])
48 if node in self.nodemap:
48 if node in self.nodemap:
49 prev = node
49 prev = node
50 continue
50 continue
51 for p in (p1, p2):
51 for p in (p1, p2):
52 if not p in self.nodemap:
52 if not p in self.nodemap:
53 raise revlog.RevlogError(_("unknown parent %s") % short(p1))
53 raise revlog.RevlogError(_("unknown parent %s") % short(p1))
54 if linkmapper is None:
54 if linkmapper is None:
55 link = n
55 link = n
56 else:
56 else:
57 link = linkmapper(cs)
57 link = linkmapper(cs)
58
58
59 if not prev:
59 if not prev:
60 prev = p1
60 prev = p1
61 # start, size, base is not used, link, p1, p2, delta ref
61 # start, size, base is not used, link, p1, p2, delta ref
62 if self.version == revlog.REVLOGV0:
62 if self.version == revlog.REVLOGV0:
63 e = (start, size, None, link, p1, p2, node)
63 e = (start, size, None, link, p1, p2, node)
64 else:
64 else:
65 e = (self.offset_type(start, 0), size, -1, None, link,
65 e = (self.offset_type(start, 0), size, -1, None, link,
66 self.rev(p1), self.rev(p2), node)
66 self.rev(p1), self.rev(p2), node)
67 self.basemap[n] = prev
67 self.basemap[n] = prev
68 self.index.append(e)
68 self.index.append(e)
69 self.nodemap[node] = n
69 self.nodemap[node] = n
70 prev = node
70 prev = node
71 n += 1
71 n += 1
72
72
73 def bundle(self, rev):
73 def bundle(self, rev):
74 """is rev from the bundle"""
74 """is rev from the bundle"""
75 if rev < 0:
75 if rev < 0:
76 return False
76 return False
77 return rev in self.basemap
77 return rev in self.basemap
78 def bundlebase(self, rev): return self.basemap[rev]
78 def bundlebase(self, rev): return self.basemap[rev]
79 def chunk(self, rev, df=None, cachelen=4096):
79 def chunk(self, rev, df=None, cachelen=4096):
80 # Warning: in case of bundle, the diff is against bundlebase,
80 # Warning: in case of bundle, the diff is against bundlebase,
81 # not against rev - 1
81 # not against rev - 1
82 # XXX: could use some caching
82 # XXX: could use some caching
83 if not self.bundle(rev):
83 if not self.bundle(rev):
84 return revlog.revlog.chunk(self, rev, df, cachelen)
84 return revlog.revlog.chunk(self, rev, df, cachelen)
85 self.bundlefile.seek(self.start(rev))
85 self.bundlefile.seek(self.start(rev))
86 return self.bundlefile.read(self.length(rev))
86 return self.bundlefile.read(self.length(rev))
87
87
88 def revdiff(self, rev1, rev2):
88 def revdiff(self, rev1, rev2):
89 """return or calculate a delta between two revisions"""
89 """return or calculate a delta between two revisions"""
90 if self.bundle(rev1) and self.bundle(rev2):
90 if self.bundle(rev1) and self.bundle(rev2):
91 # hot path for bundle
91 # hot path for bundle
92 revb = self.rev(self.bundlebase(rev2))
92 revb = self.rev(self.bundlebase(rev2))
93 if revb == rev1:
93 if revb == rev1:
94 return self.chunk(rev2)
94 return self.chunk(rev2)
95 elif not self.bundle(rev1) and not self.bundle(rev2):
95 elif not self.bundle(rev1) and not self.bundle(rev2):
96 return revlog.revlog.chunk(self, rev1, rev2)
96 return revlog.revlog.revdiff(self, rev1, rev2)
97
97
98 return self.diff(self.revision(self.node(rev1)),
98 return self.diff(self.revision(self.node(rev1)),
99 self.revision(self.node(rev2)))
99 self.revision(self.node(rev2)))
100
100
101 def revision(self, node):
101 def revision(self, node):
102 """return an uncompressed revision of a given"""
102 """return an uncompressed revision of a given"""
103 if node == nullid: return ""
103 if node == nullid: return ""
104
104
105 text = None
105 text = None
106 chain = []
106 chain = []
107 iter_node = node
107 iter_node = node
108 rev = self.rev(iter_node)
108 rev = self.rev(iter_node)
109 # reconstruct the revision if it is from a changegroup
109 # reconstruct the revision if it is from a changegroup
110 while self.bundle(rev):
110 while self.bundle(rev):
111 if self.cache and self.cache[0] == iter_node:
111 if self.cache and self.cache[0] == iter_node:
112 text = self.cache[2]
112 text = self.cache[2]
113 break
113 break
114 chain.append(rev)
114 chain.append(rev)
115 iter_node = self.bundlebase(rev)
115 iter_node = self.bundlebase(rev)
116 rev = self.rev(iter_node)
116 rev = self.rev(iter_node)
117 if text is None:
117 if text is None:
118 text = revlog.revlog.revision(self, iter_node)
118 text = revlog.revlog.revision(self, iter_node)
119
119
120 while chain:
120 while chain:
121 delta = self.chunk(chain.pop())
121 delta = self.chunk(chain.pop())
122 text = self.patches(text, [delta])
122 text = self.patches(text, [delta])
123
123
124 p1, p2 = self.parents(node)
124 p1, p2 = self.parents(node)
125 if node != revlog.hash(text, p1, p2):
125 if node != revlog.hash(text, p1, p2):
126 raise revlog.RevlogError(_("integrity check failed on %s:%d")
126 raise revlog.RevlogError(_("integrity check failed on %s:%d")
127 % (self.datafile, self.rev(node)))
127 % (self.datafile, self.rev(node)))
128
128
129 self.cache = (node, self.rev(node), text)
129 self.cache = (node, self.rev(node), text)
130 return text
130 return text
131
131
132 def addrevision(self, text, transaction, link, p1=None, p2=None, d=None):
132 def addrevision(self, text, transaction, link, p1=None, p2=None, d=None):
133 raise NotImplementedError
133 raise NotImplementedError
134 def addgroup(self, revs, linkmapper, transaction, unique=0):
134 def addgroup(self, revs, linkmapper, transaction, unique=0):
135 raise NotImplementedError
135 raise NotImplementedError
136 def strip(self, rev, minlink):
136 def strip(self, rev, minlink):
137 raise NotImplementedError
137 raise NotImplementedError
138 def checksize(self):
138 def checksize(self):
139 raise NotImplementedError
139 raise NotImplementedError
140
140
141 class bundlechangelog(bundlerevlog, changelog.changelog):
141 class bundlechangelog(bundlerevlog, changelog.changelog):
142 def __init__(self, opener, bundlefile):
142 def __init__(self, opener, bundlefile):
143 changelog.changelog.__init__(self, opener)
143 changelog.changelog.__init__(self, opener)
144 bundlerevlog.__init__(self, opener, self.indexfile, self.datafile,
144 bundlerevlog.__init__(self, opener, self.indexfile, self.datafile,
145 bundlefile)
145 bundlefile)
146
146
147 class bundlemanifest(bundlerevlog, manifest.manifest):
147 class bundlemanifest(bundlerevlog, manifest.manifest):
148 def __init__(self, opener, bundlefile, linkmapper):
148 def __init__(self, opener, bundlefile, linkmapper):
149 manifest.manifest.__init__(self, opener)
149 manifest.manifest.__init__(self, opener)
150 bundlerevlog.__init__(self, opener, self.indexfile, self.datafile,
150 bundlerevlog.__init__(self, opener, self.indexfile, self.datafile,
151 bundlefile, linkmapper)
151 bundlefile, linkmapper)
152
152
153 class bundlefilelog(bundlerevlog, filelog.filelog):
153 class bundlefilelog(bundlerevlog, filelog.filelog):
154 def __init__(self, opener, path, bundlefile, linkmapper):
154 def __init__(self, opener, path, bundlefile, linkmapper):
155 filelog.filelog.__init__(self, opener, path)
155 filelog.filelog.__init__(self, opener, path)
156 bundlerevlog.__init__(self, opener, self.indexfile, self.datafile,
156 bundlerevlog.__init__(self, opener, self.indexfile, self.datafile,
157 bundlefile, linkmapper)
157 bundlefile, linkmapper)
158
158
159 class bundlerepository(localrepo.localrepository):
159 class bundlerepository(localrepo.localrepository):
160 def __init__(self, ui, path, bundlename):
160 def __init__(self, ui, path, bundlename):
161 localrepo.localrepository.__init__(self, ui, path)
161 localrepo.localrepository.__init__(self, ui, path)
162
162
163 self._url = 'bundle:' + bundlename
163 self._url = 'bundle:' + bundlename
164 if path: self._url += '+' + path
164 if path: self._url += '+' + path
165
165
166 self.tempfile = None
166 self.tempfile = None
167 self.bundlefile = open(bundlename, "rb")
167 self.bundlefile = open(bundlename, "rb")
168 header = self.bundlefile.read(6)
168 header = self.bundlefile.read(6)
169 if not header.startswith("HG"):
169 if not header.startswith("HG"):
170 raise util.Abort(_("%s: not a Mercurial bundle file") % bundlename)
170 raise util.Abort(_("%s: not a Mercurial bundle file") % bundlename)
171 elif not header.startswith("HG10"):
171 elif not header.startswith("HG10"):
172 raise util.Abort(_("%s: unknown bundle version") % bundlename)
172 raise util.Abort(_("%s: unknown bundle version") % bundlename)
173 elif header == "HG10BZ":
173 elif header == "HG10BZ":
174 fdtemp, temp = tempfile.mkstemp(prefix="hg-bundle-",
174 fdtemp, temp = tempfile.mkstemp(prefix="hg-bundle-",
175 suffix=".hg10un", dir=self.path)
175 suffix=".hg10un", dir=self.path)
176 self.tempfile = temp
176 self.tempfile = temp
177 fptemp = os.fdopen(fdtemp, 'wb')
177 fptemp = os.fdopen(fdtemp, 'wb')
178 def generator(f):
178 def generator(f):
179 zd = bz2.BZ2Decompressor()
179 zd = bz2.BZ2Decompressor()
180 zd.decompress("BZ")
180 zd.decompress("BZ")
181 for chunk in f:
181 for chunk in f:
182 yield zd.decompress(chunk)
182 yield zd.decompress(chunk)
183 gen = generator(util.filechunkiter(self.bundlefile, 4096))
183 gen = generator(util.filechunkiter(self.bundlefile, 4096))
184
184
185 try:
185 try:
186 fptemp.write("HG10UN")
186 fptemp.write("HG10UN")
187 for chunk in gen:
187 for chunk in gen:
188 fptemp.write(chunk)
188 fptemp.write(chunk)
189 finally:
189 finally:
190 fptemp.close()
190 fptemp.close()
191 self.bundlefile.close()
191 self.bundlefile.close()
192
192
193 self.bundlefile = open(self.tempfile, "rb")
193 self.bundlefile = open(self.tempfile, "rb")
194 # seek right after the header
194 # seek right after the header
195 self.bundlefile.seek(6)
195 self.bundlefile.seek(6)
196 elif header == "HG10UN":
196 elif header == "HG10UN":
197 # nothing to do
197 # nothing to do
198 pass
198 pass
199 else:
199 else:
200 raise util.Abort(_("%s: unknown bundle compression type")
200 raise util.Abort(_("%s: unknown bundle compression type")
201 % bundlename)
201 % bundlename)
202 self.changelog = bundlechangelog(self.sopener, self.bundlefile)
202 self.changelog = bundlechangelog(self.sopener, self.bundlefile)
203 self.manifest = bundlemanifest(self.sopener, self.bundlefile,
203 self.manifest = bundlemanifest(self.sopener, self.bundlefile,
204 self.changelog.rev)
204 self.changelog.rev)
205 # dict with the mapping 'filename' -> position in the bundle
205 # dict with the mapping 'filename' -> position in the bundle
206 self.bundlefilespos = {}
206 self.bundlefilespos = {}
207 while 1:
207 while 1:
208 f = changegroup.getchunk(self.bundlefile)
208 f = changegroup.getchunk(self.bundlefile)
209 if not f:
209 if not f:
210 break
210 break
211 self.bundlefilespos[f] = self.bundlefile.tell()
211 self.bundlefilespos[f] = self.bundlefile.tell()
212 for c in changegroup.chunkiter(self.bundlefile):
212 for c in changegroup.chunkiter(self.bundlefile):
213 pass
213 pass
214
214
215 def url(self):
215 def url(self):
216 return self._url
216 return self._url
217
217
218 def dev(self):
218 def dev(self):
219 return -1
219 return -1
220
220
221 def file(self, f):
221 def file(self, f):
222 if f[0] == '/':
222 if f[0] == '/':
223 f = f[1:]
223 f = f[1:]
224 if f in self.bundlefilespos:
224 if f in self.bundlefilespos:
225 self.bundlefile.seek(self.bundlefilespos[f])
225 self.bundlefile.seek(self.bundlefilespos[f])
226 return bundlefilelog(self.sopener, f, self.bundlefile,
226 return bundlefilelog(self.sopener, f, self.bundlefile,
227 self.changelog.rev)
227 self.changelog.rev)
228 else:
228 else:
229 return filelog.filelog(self.sopener, f)
229 return filelog.filelog(self.sopener, f)
230
230
231 def close(self):
231 def close(self):
232 """Close assigned bundle file immediately."""
232 """Close assigned bundle file immediately."""
233 self.bundlefile.close()
233 self.bundlefile.close()
234
234
235 def __del__(self):
235 def __del__(self):
236 bundlefile = getattr(self, 'bundlefile', None)
236 bundlefile = getattr(self, 'bundlefile', None)
237 if bundlefile and not bundlefile.closed:
237 if bundlefile and not bundlefile.closed:
238 bundlefile.close()
238 bundlefile.close()
239 tempfile = getattr(self, 'tempfile', None)
239 tempfile = getattr(self, 'tempfile', None)
240 if tempfile is not None:
240 if tempfile is not None:
241 os.unlink(tempfile)
241 os.unlink(tempfile)
242
242
243 def instance(ui, path, create):
243 def instance(ui, path, create):
244 if create:
244 if create:
245 raise util.Abort(_('cannot create new bundle repository'))
245 raise util.Abort(_('cannot create new bundle repository'))
246 path = util.drop_scheme('file', path)
246 path = util.drop_scheme('file', path)
247 if path.startswith('bundle:'):
247 if path.startswith('bundle:'):
248 path = util.drop_scheme('bundle', path)
248 path = util.drop_scheme('bundle', path)
249 s = path.split("+", 1)
249 s = path.split("+", 1)
250 if len(s) == 1:
250 if len(s) == 1:
251 repopath, bundlename = "", s[0]
251 repopath, bundlename = "", s[0]
252 else:
252 else:
253 repopath, bundlename = s
253 repopath, bundlename = s
254 else:
254 else:
255 repopath, bundlename = '', path
255 repopath, bundlename = '', path
256 return bundlerepository(ui, repopath, bundlename)
256 return bundlerepository(ui, repopath, bundlename)
General Comments 0
You need to be logged in to leave comments. Login now