comparison mercurial/bundlerepo.py @ 12332:680fe77ab5b8

bundlerepo: use bundle objects everywhere
author Matt Mackall <mpm@selenic.com>
date Fri, 17 Sep 2010 19:24:29 -0500
parents 40935b59518b
children 44c7dfc2f6a3
comparison
equal deleted inserted replaced
12331:40935b59518b 12332:680fe77ab5b8
16 import os, struct, tempfile, shutil 16 import os, struct, tempfile, shutil
17 import changegroup, util, mdiff 17 import changegroup, util, mdiff
18 import localrepo, changelog, manifest, filelog, revlog, error 18 import localrepo, changelog, manifest, filelog, revlog, error
19 19
20 class bundlerevlog(revlog.revlog): 20 class bundlerevlog(revlog.revlog):
21 def __init__(self, opener, indexfile, bundlefile, 21 def __init__(self, opener, indexfile, bundle,
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 bundle (an unbundle object).
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) 32 revlog.revlog.__init__(self, opener, indexfile)
33 self.bundlefile = bundlefile 33 self.bundle = bundle
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(bundle):
37 pos = bundlefile.tell() 37 pos = bundle.tell()
38 yield chunk, pos - len(chunk) 38 yield chunk, pos - len(chunk)
39 n = len(self) 39 n = len(self)
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)
66 self.index.insert(-1, e) 66 self.index.insert(-1, e)
67 self.nodemap[node] = n 67 self.nodemap[node] = n
68 prev = node 68 prev = node
69 n += 1 69 n += 1
70 70
71 def bundle(self, rev): 71 def inbundle(self, rev):
72 """is rev from the bundle""" 72 """is rev from the bundle"""
73 if rev < 0: 73 if rev < 0:
74 return False 74 return False
75 return rev in self.basemap 75 return rev in self.basemap
76 def bundlebase(self, rev): 76 def bundlebase(self, rev):
77 return self.basemap[rev] 77 return self.basemap[rev]
78 def _chunk(self, rev): 78 def _chunk(self, rev):
79 # Warning: in case of bundle, the diff is against bundlebase, 79 # Warning: in case of bundle, the diff is against bundlebase,
80 # not against rev - 1 80 # not against rev - 1
81 # XXX: could use some caching 81 # XXX: could use some caching
82 if not self.bundle(rev): 82 if not self.inbundle(rev):
83 return revlog.revlog._chunk(self, rev) 83 return revlog.revlog._chunk(self, rev)
84 self.bundlefile.seek(self.start(rev)) 84 self.bundle.seek(self.start(rev))
85 return self.bundlefile.read(self.length(rev)) 85 return self.bundle.read(self.length(rev))
86 86
87 def revdiff(self, rev1, rev2): 87 def revdiff(self, rev1, rev2):
88 """return or calculate a delta between two revisions""" 88 """return or calculate a delta between two revisions"""
89 if self.bundle(rev1) and self.bundle(rev2): 89 if self.inbundle(rev1) and self.inbundle(rev2):
90 # hot path for bundle 90 # hot path for bundle
91 revb = self.rev(self.bundlebase(rev2)) 91 revb = self.rev(self.bundlebase(rev2))
92 if revb == rev1: 92 if revb == rev1:
93 return self._chunk(rev2) 93 return self._chunk(rev2)
94 elif not self.bundle(rev1) and not self.bundle(rev2): 94 elif not self.inbundle(rev1) and not self.inbundle(rev2):
95 return revlog.revlog.revdiff(self, rev1, rev2) 95 return revlog.revlog.revdiff(self, rev1, rev2)
96 96
97 return mdiff.textdiff(self.revision(self.node(rev1)), 97 return mdiff.textdiff(self.revision(self.node(rev1)),
98 self.revision(self.node(rev2))) 98 self.revision(self.node(rev2)))
99 99
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.inbundle(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)
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, bundle):
143 changelog.changelog.__init__(self, opener) 143 changelog.changelog.__init__(self, opener)
144 bundlerevlog.__init__(self, opener, self.indexfile, bundlefile) 144 bundlerevlog.__init__(self, opener, self.indexfile, bundle)
145 145
146 class bundlemanifest(bundlerevlog, manifest.manifest): 146 class bundlemanifest(bundlerevlog, manifest.manifest):
147 def __init__(self, opener, bundlefile, linkmapper): 147 def __init__(self, opener, bundle, linkmapper):
148 manifest.manifest.__init__(self, opener) 148 manifest.manifest.__init__(self, opener)
149 bundlerevlog.__init__(self, opener, self.indexfile, bundlefile, 149 bundlerevlog.__init__(self, opener, self.indexfile, bundle,
150 linkmapper) 150 linkmapper)
151 151
152 class bundlefilelog(bundlerevlog, filelog.filelog): 152 class bundlefilelog(bundlerevlog, filelog.filelog):
153 def __init__(self, opener, path, bundlefile, linkmapper): 153 def __init__(self, opener, path, bundle, linkmapper):
154 filelog.filelog.__init__(self, opener, path) 154 filelog.filelog.__init__(self, opener, path)
155 bundlerevlog.__init__(self, opener, self.indexfile, bundlefile, 155 bundlerevlog.__init__(self, opener, self.indexfile, bundle,
156 linkmapper) 156 linkmapper)
157 157
158 class bundlerepository(localrepo.localrepository): 158 class bundlerepository(localrepo.localrepository):
159 def __init__(self, ui, path, bundlename): 159 def __init__(self, ui, path, bundlename):
160 self._tempparent = None 160 self._tempparent = None
169 self._url = 'bundle:' + util.expandpath(path) + '+' + bundlename 169 self._url = 'bundle:' + util.expandpath(path) + '+' + bundlename
170 else: 170 else:
171 self._url = 'bundle:' + bundlename 171 self._url = 'bundle:' + bundlename
172 172
173 self.tempfile = None 173 self.tempfile = None
174 self.bundlefile = open(bundlename, "rb") 174 f = open(bundlename, "rb")
175 b = changegroup.readbundle(self.bundlefile, bundlename) 175 self.bundle = changegroup.readbundle(f, bundlename)
176 if b.compressed(): 176 if self.bundle.compressed():
177 # we need a seekable, decompressed bundle 177 # we need a seekable, decompressed bundle
178 fdtemp, temp = tempfile.mkstemp(prefix="hg-bundle-", 178 fdtemp, temp = tempfile.mkstemp(prefix="hg-bundle-",
179 suffix=".hg10un", dir=self.path) 179 suffix=".hg10un", dir=self.path)
180 self.tempfile = temp 180 self.tempfile = temp
181 fptemp = os.fdopen(fdtemp, 'wb') 181 fptemp = os.fdopen(fdtemp, 'wb')
182 182
183 try: 183 try:
184 fptemp.write("HG10UN") 184 fptemp.write("HG10UN")
185 while 1: 185 while 1:
186 chunk = b.read(2**18) 186 chunk = self.bundle.read(2**18)
187 if not chunk: 187 if not chunk:
188 break 188 break
189 fptemp.write(chunk) 189 fptemp.write(chunk)
190 finally: 190 finally:
191 fptemp.close() 191 fptemp.close()
192 self.bundlefile.close() 192
193 193 f = open(self.tempfile, "rb")
194 self.bundlefile = open(self.tempfile, "rb") 194 self.bundle = changegroup.readbundle(f, bundlename)
195 self.bundlefile.seek(6)
196 195
197 # dict with the mapping 'filename' -> position in the bundle 196 # dict with the mapping 'filename' -> position in the bundle
198 self.bundlefilespos = {} 197 self.bundlefilespos = {}
199 198
200 @util.propertycache 199 @util.propertycache
201 def changelog(self): 200 def changelog(self):
202 c = bundlechangelog(self.sopener, self.bundlefile) 201 c = bundlechangelog(self.sopener, self.bundle)
203 self.manstart = self.bundlefile.tell() 202 self.manstart = self.bundle.tell()
204 return c 203 return c
205 204
206 @util.propertycache 205 @util.propertycache
207 def manifest(self): 206 def manifest(self):
208 self.bundlefile.seek(self.manstart) 207 self.bundle.seek(self.manstart)
209 m = bundlemanifest(self.sopener, self.bundlefile, self.changelog.rev) 208 m = bundlemanifest(self.sopener, self.bundle, self.changelog.rev)
210 self.filestart = self.bundlefile.tell() 209 self.filestart = self.bundle.tell()
211 return m 210 return m
212 211
213 @util.propertycache 212 @util.propertycache
214 def manstart(self): 213 def manstart(self):
215 self.changelog 214 self.changelog
223 def url(self): 222 def url(self):
224 return self._url 223 return self._url
225 224
226 def file(self, f): 225 def file(self, f):
227 if not self.bundlefilespos: 226 if not self.bundlefilespos:
228 self.bundlefile.seek(self.filestart) 227 self.bundle.seek(self.filestart)
229 while 1: 228 while 1:
230 chunk = changegroup.getchunk(self.bundlefile) 229 chunk = changegroup.getchunk(self.bundle)
231 if not chunk: 230 if not chunk:
232 break 231 break
233 self.bundlefilespos[chunk] = self.bundlefile.tell() 232 self.bundlefilespos[chunk] = self.bundle.tell()
234 for c in changegroup.chunkiter(self.bundlefile): 233 for c in changegroup.chunkiter(self.bundle):
235 pass 234 pass
236 235
237 if f[0] == '/': 236 if f[0] == '/':
238 f = f[1:] 237 f = f[1:]
239 if f in self.bundlefilespos: 238 if f in self.bundlefilespos:
240 self.bundlefile.seek(self.bundlefilespos[f]) 239 self.bundle.seek(self.bundlefilespos[f])
241 return bundlefilelog(self.sopener, f, self.bundlefile, 240 return bundlefilelog(self.sopener, f, self.bundle,
242 self.changelog.rev) 241 self.changelog.rev)
243 else: 242 else:
244 return filelog.filelog(self.sopener, f) 243 return filelog.filelog(self.sopener, f)
245 244
246 def __del__(self): 245 def __del__(self):
247 bundlefile = getattr(self, 'bundlefile', None) 246 del self.bundle
248 if bundlefile and not bundlefile.closed:
249 bundlefile.close()
250 tempfile = getattr(self, 'tempfile', None)
251 if tempfile is not None: 247 if tempfile is not None:
252 os.unlink(tempfile) 248 os.unlink(tempfile)
253 if self._tempparent: 249 if self._tempparent:
254 shutil.rmtree(self._tempparent, True) 250 shutil.rmtree(self._tempparent, True)
255 251