##// END OF EJS Templates
bundlerepository: test self.tempfile field, not tempfile module...
Martin Geisler -
r12961:ad63e5f8 stable
parent child Browse files
Show More
@@ -1,322 +1,322 b''
1 1 # bundlerepo.py - repository class for viewing uncompressed bundles
2 2 #
3 3 # Copyright 2006, 2007 Benoit Boissinot <bboissin@gmail.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 """Repository class for viewing uncompressed bundles.
9 9
10 10 This provides a read-only repository interface to bundles as if they
11 11 were part of the actual repository.
12 12 """
13 13
14 14 from node import nullid
15 15 from i18n import _
16 16 import os, struct, tempfile, shutil
17 17 import changegroup, util, mdiff, discovery
18 18 import localrepo, changelog, manifest, filelog, revlog, error
19 19
20 20 class bundlerevlog(revlog.revlog):
21 21 def __init__(self, opener, indexfile, bundle,
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 bundle (an unbundle object).
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)
33 33 self.bundle = bundle
34 34 self.basemap = {}
35 35 def chunkpositer():
36 36 while 1:
37 37 chunk = bundle.chunk()
38 38 if not chunk:
39 39 break
40 40 pos = bundle.tell()
41 41 yield chunk, pos - len(chunk)
42 42 n = len(self)
43 43 prev = None
44 44 for chunk, start in chunkpositer():
45 45 size = len(chunk)
46 46 if size < 80:
47 47 raise util.Abort(_("invalid changegroup"))
48 48 start += 80
49 49 size -= 80
50 50 node, p1, p2, cs = struct.unpack("20s20s20s20s", chunk[:80])
51 51 if node in self.nodemap:
52 52 prev = node
53 53 continue
54 54 for p in (p1, p2):
55 55 if not p in self.nodemap:
56 56 raise error.LookupError(p, self.indexfile,
57 57 _("unknown parent"))
58 58 if linkmapper is None:
59 59 link = n
60 60 else:
61 61 link = linkmapper(cs)
62 62
63 63 if not prev:
64 64 prev = p1
65 65 # start, size, full unc. size, base (unused), link, p1, p2, node
66 66 e = (revlog.offset_type(start, 0), size, -1, -1, link,
67 67 self.rev(p1), self.rev(p2), node)
68 68 self.basemap[n] = prev
69 69 self.index.insert(-1, e)
70 70 self.nodemap[node] = n
71 71 prev = node
72 72 n += 1
73 73
74 74 def inbundle(self, rev):
75 75 """is rev from the bundle"""
76 76 if rev < 0:
77 77 return False
78 78 return rev in self.basemap
79 79 def bundlebase(self, rev):
80 80 return self.basemap[rev]
81 81 def _chunk(self, rev):
82 82 # Warning: in case of bundle, the diff is against bundlebase,
83 83 # not against rev - 1
84 84 # XXX: could use some caching
85 85 if not self.inbundle(rev):
86 86 return revlog.revlog._chunk(self, rev)
87 87 self.bundle.seek(self.start(rev))
88 88 return self.bundle.read(self.length(rev))
89 89
90 90 def revdiff(self, rev1, rev2):
91 91 """return or calculate a delta between two revisions"""
92 92 if self.inbundle(rev1) and self.inbundle(rev2):
93 93 # hot path for bundle
94 94 revb = self.rev(self.bundlebase(rev2))
95 95 if revb == rev1:
96 96 return self._chunk(rev2)
97 97 elif not self.inbundle(rev1) and not self.inbundle(rev2):
98 98 return revlog.revlog.revdiff(self, rev1, rev2)
99 99
100 100 return mdiff.textdiff(self.revision(self.node(rev1)),
101 101 self.revision(self.node(rev2)))
102 102
103 103 def revision(self, node):
104 104 """return an uncompressed revision of a given"""
105 105 if node == nullid:
106 106 return ""
107 107
108 108 text = None
109 109 chain = []
110 110 iter_node = node
111 111 rev = self.rev(iter_node)
112 112 # reconstruct the revision if it is from a changegroup
113 113 while self.inbundle(rev):
114 114 if self._cache and self._cache[0] == iter_node:
115 115 text = self._cache[2]
116 116 break
117 117 chain.append(rev)
118 118 iter_node = self.bundlebase(rev)
119 119 rev = self.rev(iter_node)
120 120 if text is None:
121 121 text = revlog.revlog.revision(self, iter_node)
122 122
123 123 while chain:
124 124 delta = self._chunk(chain.pop())
125 125 text = mdiff.patches(text, [delta])
126 126
127 127 p1, p2 = self.parents(node)
128 128 if node != revlog.hash(text, p1, p2):
129 129 raise error.RevlogError(_("integrity check failed on %s:%d")
130 130 % (self.datafile, self.rev(node)))
131 131
132 132 self._cache = (node, self.rev(node), text)
133 133 return text
134 134
135 135 def addrevision(self, text, transaction, link, p1=None, p2=None, d=None):
136 136 raise NotImplementedError
137 137 def addgroup(self, revs, linkmapper, transaction):
138 138 raise NotImplementedError
139 139 def strip(self, rev, minlink):
140 140 raise NotImplementedError
141 141 def checksize(self):
142 142 raise NotImplementedError
143 143
144 144 class bundlechangelog(bundlerevlog, changelog.changelog):
145 145 def __init__(self, opener, bundle):
146 146 changelog.changelog.__init__(self, opener)
147 147 bundlerevlog.__init__(self, opener, self.indexfile, bundle)
148 148
149 149 class bundlemanifest(bundlerevlog, manifest.manifest):
150 150 def __init__(self, opener, bundle, linkmapper):
151 151 manifest.manifest.__init__(self, opener)
152 152 bundlerevlog.__init__(self, opener, self.indexfile, bundle,
153 153 linkmapper)
154 154
155 155 class bundlefilelog(bundlerevlog, filelog.filelog):
156 156 def __init__(self, opener, path, bundle, linkmapper):
157 157 filelog.filelog.__init__(self, opener, path)
158 158 bundlerevlog.__init__(self, opener, self.indexfile, bundle,
159 159 linkmapper)
160 160
161 161 class bundlerepository(localrepo.localrepository):
162 162 def __init__(self, ui, path, bundlename):
163 163 self._tempparent = None
164 164 try:
165 165 localrepo.localrepository.__init__(self, ui, path)
166 166 except error.RepoError:
167 167 self._tempparent = tempfile.mkdtemp()
168 168 localrepo.instance(ui, self._tempparent, 1)
169 169 localrepo.localrepository.__init__(self, ui, self._tempparent)
170 170
171 171 if path:
172 172 self._url = 'bundle:' + util.expandpath(path) + '+' + bundlename
173 173 else:
174 174 self._url = 'bundle:' + bundlename
175 175
176 176 self.tempfile = None
177 177 f = open(bundlename, "rb")
178 178 self.bundle = changegroup.readbundle(f, bundlename)
179 179 if self.bundle.compressed():
180 180 fdtemp, temp = tempfile.mkstemp(prefix="hg-bundle-",
181 181 suffix=".hg10un", dir=self.path)
182 182 self.tempfile = temp
183 183 fptemp = os.fdopen(fdtemp, 'wb')
184 184
185 185 try:
186 186 fptemp.write("HG10UN")
187 187 while 1:
188 188 chunk = self.bundle.read(2**18)
189 189 if not chunk:
190 190 break
191 191 fptemp.write(chunk)
192 192 finally:
193 193 fptemp.close()
194 194
195 195 f = open(self.tempfile, "rb")
196 196 self.bundle = changegroup.readbundle(f, bundlename)
197 197
198 198 # dict with the mapping 'filename' -> position in the bundle
199 199 self.bundlefilespos = {}
200 200
201 201 @util.propertycache
202 202 def changelog(self):
203 203 c = bundlechangelog(self.sopener, self.bundle)
204 204 self.manstart = self.bundle.tell()
205 205 return c
206 206
207 207 @util.propertycache
208 208 def manifest(self):
209 209 self.bundle.seek(self.manstart)
210 210 m = bundlemanifest(self.sopener, self.bundle, self.changelog.rev)
211 211 self.filestart = self.bundle.tell()
212 212 return m
213 213
214 214 @util.propertycache
215 215 def manstart(self):
216 216 self.changelog
217 217 return self.manstart
218 218
219 219 @util.propertycache
220 220 def filestart(self):
221 221 self.manifest
222 222 return self.filestart
223 223
224 224 def url(self):
225 225 return self._url
226 226
227 227 def file(self, f):
228 228 if not self.bundlefilespos:
229 229 self.bundle.seek(self.filestart)
230 230 while 1:
231 231 chunk = self.bundle.chunk()
232 232 if not chunk:
233 233 break
234 234 self.bundlefilespos[chunk] = self.bundle.tell()
235 235 while 1:
236 236 c = self.bundle.chunk()
237 237 if not c:
238 238 break
239 239
240 240 if f[0] == '/':
241 241 f = f[1:]
242 242 if f in self.bundlefilespos:
243 243 self.bundle.seek(self.bundlefilespos[f])
244 244 return bundlefilelog(self.sopener, f, self.bundle,
245 245 self.changelog.rev)
246 246 else:
247 247 return filelog.filelog(self.sopener, f)
248 248
249 249 def close(self):
250 250 """Close assigned bundle file immediately."""
251 251 self.bundle.close()
252 252
253 253 def __del__(self):
254 254 del self.bundle
255 if tempfile is not None:
256 os.unlink(tempfile)
255 if self.tempfile is not None:
256 os.unlink(self.tempfile)
257 257 if self._tempparent:
258 258 shutil.rmtree(self._tempparent, True)
259 259
260 260 def cancopy(self):
261 261 return False
262 262
263 263 def getcwd(self):
264 264 return os.getcwd() # always outside the repo
265 265
266 266 def instance(ui, path, create):
267 267 if create:
268 268 raise util.Abort(_('cannot create new bundle repository'))
269 269 parentpath = ui.config("bundle", "mainreporoot", "")
270 270 if parentpath:
271 271 # Try to make the full path relative so we get a nice, short URL.
272 272 # In particular, we don't want temp dir names in test outputs.
273 273 cwd = os.getcwd()
274 274 if parentpath == cwd:
275 275 parentpath = ''
276 276 else:
277 277 cwd = os.path.join(cwd,'')
278 278 if parentpath.startswith(cwd):
279 279 parentpath = parentpath[len(cwd):]
280 280 path = util.drop_scheme('file', path)
281 281 if path.startswith('bundle:'):
282 282 path = util.drop_scheme('bundle', path)
283 283 s = path.split("+", 1)
284 284 if len(s) == 1:
285 285 repopath, bundlename = parentpath, s[0]
286 286 else:
287 287 repopath, bundlename = s
288 288 else:
289 289 repopath, bundlename = parentpath, path
290 290 return bundlerepository(ui, repopath, bundlename)
291 291
292 292 def getremotechanges(ui, repo, other, revs=None, bundlename=None, force=False):
293 293 tmp = discovery.findcommonincoming(repo, other, heads=revs, force=force)
294 294 common, incoming, rheads = tmp
295 295 if not incoming:
296 296 try:
297 297 os.unlink(bundlename)
298 298 except:
299 299 pass
300 300 return other, None, None
301 301
302 302 bundle = None
303 303 if bundlename or not other.local():
304 304 # create a bundle (uncompressed if other repo is not local)
305 305
306 306 if revs is None and other.capable('changegroupsubset'):
307 307 revs = rheads
308 308
309 309 if revs is None:
310 310 cg = other.changegroup(incoming, "incoming")
311 311 else:
312 312 cg = other.changegroupsubset(incoming, revs, 'incoming')
313 313 bundletype = other.local() and "HG10BZ" or "HG10UN"
314 314 fname = bundle = changegroup.writebundle(cg, bundlename, bundletype)
315 315 # keep written bundle?
316 316 if bundlename:
317 317 bundle = None
318 318 if not other.local():
319 319 # use the created uncompressed bundlerepo
320 320 other = bundlerepository(ui, repo.root, fname)
321 321 return (other, incoming, bundle)
322 322
General Comments 0
You need to be logged in to leave comments. Login now