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 |
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 |