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