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