##// END OF EJS Templates
manifest: make one use of _mancache avoid manifestctxs...
Durham Goode -
r29925:1619efcd default
parent child Browse files
Show More
@@ -1,554 +1,554 b''
1 # bundlerepo.py - repository class for viewing uncompressed bundles
1 # bundlerepo.py - repository class for viewing uncompressed bundles
2 #
2 #
3 # Copyright 2006, 2007 Benoit Boissinot <bboissin@gmail.com>
3 # Copyright 2006, 2007 Benoit Boissinot <bboissin@gmail.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 """Repository class for viewing uncompressed bundles.
8 """Repository class for viewing uncompressed bundles.
9
9
10 This provides a read-only repository interface to bundles as if they
10 This provides a read-only repository interface to bundles as if they
11 were part of the actual repository.
11 were part of the actual repository.
12 """
12 """
13
13
14 from __future__ import absolute_import
14 from __future__ import absolute_import
15
15
16 import os
16 import os
17 import shutil
17 import shutil
18 import tempfile
18 import tempfile
19
19
20 from .i18n import _
20 from .i18n import _
21 from .node import nullid
21 from .node import nullid
22
22
23 from . import (
23 from . import (
24 bundle2,
24 bundle2,
25 changegroup,
25 changegroup,
26 changelog,
26 changelog,
27 cmdutil,
27 cmdutil,
28 discovery,
28 discovery,
29 error,
29 error,
30 exchange,
30 exchange,
31 filelog,
31 filelog,
32 localrepo,
32 localrepo,
33 manifest,
33 manifest,
34 mdiff,
34 mdiff,
35 node as nodemod,
35 node as nodemod,
36 pathutil,
36 pathutil,
37 phases,
37 phases,
38 revlog,
38 revlog,
39 scmutil,
39 scmutil,
40 util,
40 util,
41 )
41 )
42
42
43 class bundlerevlog(revlog.revlog):
43 class bundlerevlog(revlog.revlog):
44 def __init__(self, opener, indexfile, bundle, linkmapper):
44 def __init__(self, opener, indexfile, bundle, linkmapper):
45 # How it works:
45 # How it works:
46 # To retrieve a revision, we need to know the offset of the revision in
46 # To retrieve a revision, we need to know the offset of the revision in
47 # the bundle (an unbundle object). We store this offset in the index
47 # the bundle (an unbundle object). We store this offset in the index
48 # (start). The base of the delta is stored in the base field.
48 # (start). The base of the delta is stored in the base field.
49 #
49 #
50 # To differentiate a rev in the bundle from a rev in the revlog, we
50 # To differentiate a rev in the bundle from a rev in the revlog, we
51 # check revision against repotiprev.
51 # check revision against repotiprev.
52 opener = scmutil.readonlyvfs(opener)
52 opener = scmutil.readonlyvfs(opener)
53 revlog.revlog.__init__(self, opener, indexfile)
53 revlog.revlog.__init__(self, opener, indexfile)
54 self.bundle = bundle
54 self.bundle = bundle
55 n = len(self)
55 n = len(self)
56 self.repotiprev = n - 1
56 self.repotiprev = n - 1
57 chain = None
57 chain = None
58 self.bundlerevs = set() # used by 'bundle()' revset expression
58 self.bundlerevs = set() # used by 'bundle()' revset expression
59 getchunk = lambda: bundle.deltachunk(chain)
59 getchunk = lambda: bundle.deltachunk(chain)
60 for chunkdata in iter(getchunk, {}):
60 for chunkdata in iter(getchunk, {}):
61 node = chunkdata['node']
61 node = chunkdata['node']
62 p1 = chunkdata['p1']
62 p1 = chunkdata['p1']
63 p2 = chunkdata['p2']
63 p2 = chunkdata['p2']
64 cs = chunkdata['cs']
64 cs = chunkdata['cs']
65 deltabase = chunkdata['deltabase']
65 deltabase = chunkdata['deltabase']
66 delta = chunkdata['delta']
66 delta = chunkdata['delta']
67
67
68 size = len(delta)
68 size = len(delta)
69 start = bundle.tell() - size
69 start = bundle.tell() - size
70
70
71 link = linkmapper(cs)
71 link = linkmapper(cs)
72 if node in self.nodemap:
72 if node in self.nodemap:
73 # this can happen if two branches make the same change
73 # this can happen if two branches make the same change
74 chain = node
74 chain = node
75 self.bundlerevs.add(self.nodemap[node])
75 self.bundlerevs.add(self.nodemap[node])
76 continue
76 continue
77
77
78 for p in (p1, p2):
78 for p in (p1, p2):
79 if p not in self.nodemap:
79 if p not in self.nodemap:
80 raise error.LookupError(p, self.indexfile,
80 raise error.LookupError(p, self.indexfile,
81 _("unknown parent"))
81 _("unknown parent"))
82
82
83 if deltabase not in self.nodemap:
83 if deltabase not in self.nodemap:
84 raise LookupError(deltabase, self.indexfile,
84 raise LookupError(deltabase, self.indexfile,
85 _('unknown delta base'))
85 _('unknown delta base'))
86
86
87 baserev = self.rev(deltabase)
87 baserev = self.rev(deltabase)
88 # start, size, full unc. size, base (unused), link, p1, p2, node
88 # start, size, full unc. size, base (unused), link, p1, p2, node
89 e = (revlog.offset_type(start, 0), size, -1, baserev, link,
89 e = (revlog.offset_type(start, 0), size, -1, baserev, link,
90 self.rev(p1), self.rev(p2), node)
90 self.rev(p1), self.rev(p2), node)
91 self.index.insert(-1, e)
91 self.index.insert(-1, e)
92 self.nodemap[node] = n
92 self.nodemap[node] = n
93 self.bundlerevs.add(n)
93 self.bundlerevs.add(n)
94 chain = node
94 chain = node
95 n += 1
95 n += 1
96
96
97 def _chunk(self, rev):
97 def _chunk(self, rev):
98 # Warning: in case of bundle, the diff is against what we stored as
98 # Warning: in case of bundle, the diff is against what we stored as
99 # delta base, not against rev - 1
99 # delta base, not against rev - 1
100 # XXX: could use some caching
100 # XXX: could use some caching
101 if rev <= self.repotiprev:
101 if rev <= self.repotiprev:
102 return revlog.revlog._chunk(self, rev)
102 return revlog.revlog._chunk(self, rev)
103 self.bundle.seek(self.start(rev))
103 self.bundle.seek(self.start(rev))
104 return self.bundle.read(self.length(rev))
104 return self.bundle.read(self.length(rev))
105
105
106 def revdiff(self, rev1, rev2):
106 def revdiff(self, rev1, rev2):
107 """return or calculate a delta between two revisions"""
107 """return or calculate a delta between two revisions"""
108 if rev1 > self.repotiprev and rev2 > self.repotiprev:
108 if rev1 > self.repotiprev and rev2 > self.repotiprev:
109 # hot path for bundle
109 # hot path for bundle
110 revb = self.index[rev2][3]
110 revb = self.index[rev2][3]
111 if revb == rev1:
111 if revb == rev1:
112 return self._chunk(rev2)
112 return self._chunk(rev2)
113 elif rev1 <= self.repotiprev and rev2 <= self.repotiprev:
113 elif rev1 <= self.repotiprev and rev2 <= self.repotiprev:
114 return revlog.revlog.revdiff(self, rev1, rev2)
114 return revlog.revlog.revdiff(self, rev1, rev2)
115
115
116 return mdiff.textdiff(self.revision(self.node(rev1)),
116 return mdiff.textdiff(self.revision(self.node(rev1)),
117 self.revision(self.node(rev2)))
117 self.revision(self.node(rev2)))
118
118
119 def revision(self, nodeorrev):
119 def revision(self, nodeorrev):
120 """return an uncompressed revision of a given node or revision
120 """return an uncompressed revision of a given node or revision
121 number.
121 number.
122 """
122 """
123 if isinstance(nodeorrev, int):
123 if isinstance(nodeorrev, int):
124 rev = nodeorrev
124 rev = nodeorrev
125 node = self.node(rev)
125 node = self.node(rev)
126 else:
126 else:
127 node = nodeorrev
127 node = nodeorrev
128 rev = self.rev(node)
128 rev = self.rev(node)
129
129
130 if node == nullid:
130 if node == nullid:
131 return ""
131 return ""
132
132
133 text = None
133 text = None
134 chain = []
134 chain = []
135 iterrev = rev
135 iterrev = rev
136 # reconstruct the revision if it is from a changegroup
136 # reconstruct the revision if it is from a changegroup
137 while iterrev > self.repotiprev:
137 while iterrev > self.repotiprev:
138 if self._cache and self._cache[1] == iterrev:
138 if self._cache and self._cache[1] == iterrev:
139 text = self._cache[2]
139 text = self._cache[2]
140 break
140 break
141 chain.append(iterrev)
141 chain.append(iterrev)
142 iterrev = self.index[iterrev][3]
142 iterrev = self.index[iterrev][3]
143 if text is None:
143 if text is None:
144 text = self.baserevision(iterrev)
144 text = self.baserevision(iterrev)
145
145
146 while chain:
146 while chain:
147 delta = self._chunk(chain.pop())
147 delta = self._chunk(chain.pop())
148 text = mdiff.patches(text, [delta])
148 text = mdiff.patches(text, [delta])
149
149
150 self._checkhash(text, node, rev)
150 self._checkhash(text, node, rev)
151 self._cache = (node, rev, text)
151 self._cache = (node, rev, text)
152 return text
152 return text
153
153
154 def baserevision(self, nodeorrev):
154 def baserevision(self, nodeorrev):
155 # Revlog subclasses may override 'revision' method to modify format of
155 # Revlog subclasses may override 'revision' method to modify format of
156 # content retrieved from revlog. To use bundlerevlog with such class one
156 # content retrieved from revlog. To use bundlerevlog with such class one
157 # needs to override 'baserevision' and make more specific call here.
157 # needs to override 'baserevision' and make more specific call here.
158 return revlog.revlog.revision(self, nodeorrev)
158 return revlog.revlog.revision(self, nodeorrev)
159
159
160 def addrevision(self, text, transaction, link, p1=None, p2=None, d=None):
160 def addrevision(self, text, transaction, link, p1=None, p2=None, d=None):
161 raise NotImplementedError
161 raise NotImplementedError
162 def addgroup(self, revs, linkmapper, transaction):
162 def addgroup(self, revs, linkmapper, transaction):
163 raise NotImplementedError
163 raise NotImplementedError
164 def strip(self, rev, minlink):
164 def strip(self, rev, minlink):
165 raise NotImplementedError
165 raise NotImplementedError
166 def checksize(self):
166 def checksize(self):
167 raise NotImplementedError
167 raise NotImplementedError
168
168
169 class bundlechangelog(bundlerevlog, changelog.changelog):
169 class bundlechangelog(bundlerevlog, changelog.changelog):
170 def __init__(self, opener, bundle):
170 def __init__(self, opener, bundle):
171 changelog.changelog.__init__(self, opener)
171 changelog.changelog.__init__(self, opener)
172 linkmapper = lambda x: x
172 linkmapper = lambda x: x
173 bundlerevlog.__init__(self, opener, self.indexfile, bundle,
173 bundlerevlog.__init__(self, opener, self.indexfile, bundle,
174 linkmapper)
174 linkmapper)
175
175
176 def baserevision(self, nodeorrev):
176 def baserevision(self, nodeorrev):
177 # Although changelog doesn't override 'revision' method, some extensions
177 # Although changelog doesn't override 'revision' method, some extensions
178 # may replace this class with another that does. Same story with
178 # may replace this class with another that does. Same story with
179 # manifest and filelog classes.
179 # manifest and filelog classes.
180
180
181 # This bypasses filtering on changelog.node() and rev() because we need
181 # This bypasses filtering on changelog.node() and rev() because we need
182 # revision text of the bundle base even if it is hidden.
182 # revision text of the bundle base even if it is hidden.
183 oldfilter = self.filteredrevs
183 oldfilter = self.filteredrevs
184 try:
184 try:
185 self.filteredrevs = ()
185 self.filteredrevs = ()
186 return changelog.changelog.revision(self, nodeorrev)
186 return changelog.changelog.revision(self, nodeorrev)
187 finally:
187 finally:
188 self.filteredrevs = oldfilter
188 self.filteredrevs = oldfilter
189
189
190 class bundlemanifest(bundlerevlog, manifest.manifest):
190 class bundlemanifest(bundlerevlog, manifest.manifest):
191 def __init__(self, opener, bundle, linkmapper, dirlogstarts=None, dir=''):
191 def __init__(self, opener, bundle, linkmapper, dirlogstarts=None, dir=''):
192 manifest.manifest.__init__(self, opener, dir=dir)
192 manifest.manifest.__init__(self, opener, dir=dir)
193 bundlerevlog.__init__(self, opener, self.indexfile, bundle,
193 bundlerevlog.__init__(self, opener, self.indexfile, bundle,
194 linkmapper)
194 linkmapper)
195 if dirlogstarts is None:
195 if dirlogstarts is None:
196 dirlogstarts = {}
196 dirlogstarts = {}
197 if self.bundle.version == "03":
197 if self.bundle.version == "03":
198 dirlogstarts = _getfilestarts(self.bundle)
198 dirlogstarts = _getfilestarts(self.bundle)
199 self._dirlogstarts = dirlogstarts
199 self._dirlogstarts = dirlogstarts
200 self._linkmapper = linkmapper
200 self._linkmapper = linkmapper
201
201
202 def baserevision(self, nodeorrev):
202 def baserevision(self, nodeorrev):
203 node = nodeorrev
203 node = nodeorrev
204 if isinstance(node, int):
204 if isinstance(node, int):
205 node = self.node(node)
205 node = self.node(node)
206
206
207 if node in self._mancache:
207 if node in self.fulltextcache:
208 result = self._mancache[node].text()
208 result = self.fulltextcache[node].tostring()
209 else:
209 else:
210 result = manifest.manifest.revision(self, nodeorrev)
210 result = manifest.manifest.revision(self, nodeorrev)
211 return result
211 return result
212
212
213 def dirlog(self, d):
213 def dirlog(self, d):
214 if d in self._dirlogstarts:
214 if d in self._dirlogstarts:
215 self.bundle.seek(self._dirlogstarts[d])
215 self.bundle.seek(self._dirlogstarts[d])
216 return bundlemanifest(
216 return bundlemanifest(
217 self.opener, self.bundle, self._linkmapper,
217 self.opener, self.bundle, self._linkmapper,
218 self._dirlogstarts, dir=d)
218 self._dirlogstarts, dir=d)
219 return super(bundlemanifest, self).dirlog(d)
219 return super(bundlemanifest, self).dirlog(d)
220
220
221 class bundlefilelog(bundlerevlog, filelog.filelog):
221 class bundlefilelog(bundlerevlog, filelog.filelog):
222 def __init__(self, opener, path, bundle, linkmapper):
222 def __init__(self, opener, path, bundle, linkmapper):
223 filelog.filelog.__init__(self, opener, path)
223 filelog.filelog.__init__(self, opener, path)
224 bundlerevlog.__init__(self, opener, self.indexfile, bundle,
224 bundlerevlog.__init__(self, opener, self.indexfile, bundle,
225 linkmapper)
225 linkmapper)
226
226
227 def baserevision(self, nodeorrev):
227 def baserevision(self, nodeorrev):
228 return filelog.filelog.revision(self, nodeorrev)
228 return filelog.filelog.revision(self, nodeorrev)
229
229
230 class bundlepeer(localrepo.localpeer):
230 class bundlepeer(localrepo.localpeer):
231 def canpush(self):
231 def canpush(self):
232 return False
232 return False
233
233
234 class bundlephasecache(phases.phasecache):
234 class bundlephasecache(phases.phasecache):
235 def __init__(self, *args, **kwargs):
235 def __init__(self, *args, **kwargs):
236 super(bundlephasecache, self).__init__(*args, **kwargs)
236 super(bundlephasecache, self).__init__(*args, **kwargs)
237 if util.safehasattr(self, 'opener'):
237 if util.safehasattr(self, 'opener'):
238 self.opener = scmutil.readonlyvfs(self.opener)
238 self.opener = scmutil.readonlyvfs(self.opener)
239
239
240 def write(self):
240 def write(self):
241 raise NotImplementedError
241 raise NotImplementedError
242
242
243 def _write(self, fp):
243 def _write(self, fp):
244 raise NotImplementedError
244 raise NotImplementedError
245
245
246 def _updateroots(self, phase, newroots, tr):
246 def _updateroots(self, phase, newroots, tr):
247 self.phaseroots[phase] = newroots
247 self.phaseroots[phase] = newroots
248 self.invalidate()
248 self.invalidate()
249 self.dirty = True
249 self.dirty = True
250
250
251 def _getfilestarts(bundle):
251 def _getfilestarts(bundle):
252 bundlefilespos = {}
252 bundlefilespos = {}
253 for chunkdata in iter(bundle.filelogheader, {}):
253 for chunkdata in iter(bundle.filelogheader, {}):
254 fname = chunkdata['filename']
254 fname = chunkdata['filename']
255 bundlefilespos[fname] = bundle.tell()
255 bundlefilespos[fname] = bundle.tell()
256 for chunk in iter(lambda: bundle.deltachunk(None), {}):
256 for chunk in iter(lambda: bundle.deltachunk(None), {}):
257 pass
257 pass
258 return bundlefilespos
258 return bundlefilespos
259
259
260 class bundlerepository(localrepo.localrepository):
260 class bundlerepository(localrepo.localrepository):
261 def __init__(self, ui, path, bundlename):
261 def __init__(self, ui, path, bundlename):
262 def _writetempbundle(read, suffix, header=''):
262 def _writetempbundle(read, suffix, header=''):
263 """Write a temporary file to disk
263 """Write a temporary file to disk
264
264
265 This is closure because we need to make sure this tracked by
265 This is closure because we need to make sure this tracked by
266 self.tempfile for cleanup purposes."""
266 self.tempfile for cleanup purposes."""
267 fdtemp, temp = self.vfs.mkstemp(prefix="hg-bundle-",
267 fdtemp, temp = self.vfs.mkstemp(prefix="hg-bundle-",
268 suffix=".hg10un")
268 suffix=".hg10un")
269 self.tempfile = temp
269 self.tempfile = temp
270
270
271 with os.fdopen(fdtemp, 'wb') as fptemp:
271 with os.fdopen(fdtemp, 'wb') as fptemp:
272 fptemp.write(header)
272 fptemp.write(header)
273 while True:
273 while True:
274 chunk = read(2**18)
274 chunk = read(2**18)
275 if not chunk:
275 if not chunk:
276 break
276 break
277 fptemp.write(chunk)
277 fptemp.write(chunk)
278
278
279 return self.vfs.open(self.tempfile, mode="rb")
279 return self.vfs.open(self.tempfile, mode="rb")
280 self._tempparent = None
280 self._tempparent = None
281 try:
281 try:
282 localrepo.localrepository.__init__(self, ui, path)
282 localrepo.localrepository.__init__(self, ui, path)
283 except error.RepoError:
283 except error.RepoError:
284 self._tempparent = tempfile.mkdtemp()
284 self._tempparent = tempfile.mkdtemp()
285 localrepo.instance(ui, self._tempparent, 1)
285 localrepo.instance(ui, self._tempparent, 1)
286 localrepo.localrepository.__init__(self, ui, self._tempparent)
286 localrepo.localrepository.__init__(self, ui, self._tempparent)
287 self.ui.setconfig('phases', 'publish', False, 'bundlerepo')
287 self.ui.setconfig('phases', 'publish', False, 'bundlerepo')
288
288
289 if path:
289 if path:
290 self._url = 'bundle:' + util.expandpath(path) + '+' + bundlename
290 self._url = 'bundle:' + util.expandpath(path) + '+' + bundlename
291 else:
291 else:
292 self._url = 'bundle:' + bundlename
292 self._url = 'bundle:' + bundlename
293
293
294 self.tempfile = None
294 self.tempfile = None
295 f = util.posixfile(bundlename, "rb")
295 f = util.posixfile(bundlename, "rb")
296 self.bundlefile = self.bundle = exchange.readbundle(ui, f, bundlename)
296 self.bundlefile = self.bundle = exchange.readbundle(ui, f, bundlename)
297
297
298 if isinstance(self.bundle, bundle2.unbundle20):
298 if isinstance(self.bundle, bundle2.unbundle20):
299 cgstream = None
299 cgstream = None
300 for part in self.bundle.iterparts():
300 for part in self.bundle.iterparts():
301 if part.type == 'changegroup':
301 if part.type == 'changegroup':
302 if cgstream is not None:
302 if cgstream is not None:
303 raise NotImplementedError("can't process "
303 raise NotImplementedError("can't process "
304 "multiple changegroups")
304 "multiple changegroups")
305 cgstream = part
305 cgstream = part
306 version = part.params.get('version', '01')
306 version = part.params.get('version', '01')
307 legalcgvers = changegroup.supportedincomingversions(self)
307 legalcgvers = changegroup.supportedincomingversions(self)
308 if version not in legalcgvers:
308 if version not in legalcgvers:
309 msg = _('Unsupported changegroup version: %s')
309 msg = _('Unsupported changegroup version: %s')
310 raise error.Abort(msg % version)
310 raise error.Abort(msg % version)
311 if self.bundle.compressed():
311 if self.bundle.compressed():
312 cgstream = _writetempbundle(part.read,
312 cgstream = _writetempbundle(part.read,
313 ".cg%sun" % version)
313 ".cg%sun" % version)
314
314
315 if cgstream is None:
315 if cgstream is None:
316 raise error.Abort(_('No changegroups found'))
316 raise error.Abort(_('No changegroups found'))
317 cgstream.seek(0)
317 cgstream.seek(0)
318
318
319 self.bundle = changegroup.getunbundler(version, cgstream, 'UN')
319 self.bundle = changegroup.getunbundler(version, cgstream, 'UN')
320
320
321 elif self.bundle.compressed():
321 elif self.bundle.compressed():
322 f = _writetempbundle(self.bundle.read, '.hg10un', header='HG10UN')
322 f = _writetempbundle(self.bundle.read, '.hg10un', header='HG10UN')
323 self.bundlefile = self.bundle = exchange.readbundle(ui, f,
323 self.bundlefile = self.bundle = exchange.readbundle(ui, f,
324 bundlename,
324 bundlename,
325 self.vfs)
325 self.vfs)
326
326
327 # dict with the mapping 'filename' -> position in the bundle
327 # dict with the mapping 'filename' -> position in the bundle
328 self.bundlefilespos = {}
328 self.bundlefilespos = {}
329
329
330 self.firstnewrev = self.changelog.repotiprev + 1
330 self.firstnewrev = self.changelog.repotiprev + 1
331 phases.retractboundary(self, None, phases.draft,
331 phases.retractboundary(self, None, phases.draft,
332 [ctx.node() for ctx in self[self.firstnewrev:]])
332 [ctx.node() for ctx in self[self.firstnewrev:]])
333
333
334 @localrepo.unfilteredpropertycache
334 @localrepo.unfilteredpropertycache
335 def _phasecache(self):
335 def _phasecache(self):
336 return bundlephasecache(self, self._phasedefaults)
336 return bundlephasecache(self, self._phasedefaults)
337
337
338 @localrepo.unfilteredpropertycache
338 @localrepo.unfilteredpropertycache
339 def changelog(self):
339 def changelog(self):
340 # consume the header if it exists
340 # consume the header if it exists
341 self.bundle.changelogheader()
341 self.bundle.changelogheader()
342 c = bundlechangelog(self.svfs, self.bundle)
342 c = bundlechangelog(self.svfs, self.bundle)
343 self.manstart = self.bundle.tell()
343 self.manstart = self.bundle.tell()
344 return c
344 return c
345
345
346 @localrepo.unfilteredpropertycache
346 @localrepo.unfilteredpropertycache
347 def manifest(self):
347 def manifest(self):
348 self.bundle.seek(self.manstart)
348 self.bundle.seek(self.manstart)
349 # consume the header if it exists
349 # consume the header if it exists
350 self.bundle.manifestheader()
350 self.bundle.manifestheader()
351 linkmapper = self.unfiltered().changelog.rev
351 linkmapper = self.unfiltered().changelog.rev
352 m = bundlemanifest(self.svfs, self.bundle, linkmapper)
352 m = bundlemanifest(self.svfs, self.bundle, linkmapper)
353 self.filestart = self.bundle.tell()
353 self.filestart = self.bundle.tell()
354 return m
354 return m
355
355
356 @localrepo.unfilteredpropertycache
356 @localrepo.unfilteredpropertycache
357 def manstart(self):
357 def manstart(self):
358 self.changelog
358 self.changelog
359 return self.manstart
359 return self.manstart
360
360
361 @localrepo.unfilteredpropertycache
361 @localrepo.unfilteredpropertycache
362 def filestart(self):
362 def filestart(self):
363 self.manifest
363 self.manifest
364 return self.filestart
364 return self.filestart
365
365
366 def url(self):
366 def url(self):
367 return self._url
367 return self._url
368
368
369 def file(self, f):
369 def file(self, f):
370 if not self.bundlefilespos:
370 if not self.bundlefilespos:
371 self.bundle.seek(self.filestart)
371 self.bundle.seek(self.filestart)
372 self.bundlefilespos = _getfilestarts(self.bundle)
372 self.bundlefilespos = _getfilestarts(self.bundle)
373
373
374 if f in self.bundlefilespos:
374 if f in self.bundlefilespos:
375 self.bundle.seek(self.bundlefilespos[f])
375 self.bundle.seek(self.bundlefilespos[f])
376 linkmapper = self.unfiltered().changelog.rev
376 linkmapper = self.unfiltered().changelog.rev
377 return bundlefilelog(self.svfs, f, self.bundle, linkmapper)
377 return bundlefilelog(self.svfs, f, self.bundle, linkmapper)
378 else:
378 else:
379 return filelog.filelog(self.svfs, f)
379 return filelog.filelog(self.svfs, f)
380
380
381 def close(self):
381 def close(self):
382 """Close assigned bundle file immediately."""
382 """Close assigned bundle file immediately."""
383 self.bundlefile.close()
383 self.bundlefile.close()
384 if self.tempfile is not None:
384 if self.tempfile is not None:
385 self.vfs.unlink(self.tempfile)
385 self.vfs.unlink(self.tempfile)
386 if self._tempparent:
386 if self._tempparent:
387 shutil.rmtree(self._tempparent, True)
387 shutil.rmtree(self._tempparent, True)
388
388
389 def cancopy(self):
389 def cancopy(self):
390 return False
390 return False
391
391
392 def peer(self):
392 def peer(self):
393 return bundlepeer(self)
393 return bundlepeer(self)
394
394
395 def getcwd(self):
395 def getcwd(self):
396 return os.getcwd() # always outside the repo
396 return os.getcwd() # always outside the repo
397
397
398 # Check if parents exist in localrepo before setting
398 # Check if parents exist in localrepo before setting
399 def setparents(self, p1, p2=nullid):
399 def setparents(self, p1, p2=nullid):
400 p1rev = self.changelog.rev(p1)
400 p1rev = self.changelog.rev(p1)
401 p2rev = self.changelog.rev(p2)
401 p2rev = self.changelog.rev(p2)
402 msg = _("setting parent to node %s that only exists in the bundle\n")
402 msg = _("setting parent to node %s that only exists in the bundle\n")
403 if self.changelog.repotiprev < p1rev:
403 if self.changelog.repotiprev < p1rev:
404 self.ui.warn(msg % nodemod.hex(p1))
404 self.ui.warn(msg % nodemod.hex(p1))
405 if self.changelog.repotiprev < p2rev:
405 if self.changelog.repotiprev < p2rev:
406 self.ui.warn(msg % nodemod.hex(p2))
406 self.ui.warn(msg % nodemod.hex(p2))
407 return super(bundlerepository, self).setparents(p1, p2)
407 return super(bundlerepository, self).setparents(p1, p2)
408
408
409 def instance(ui, path, create):
409 def instance(ui, path, create):
410 if create:
410 if create:
411 raise error.Abort(_('cannot create new bundle repository'))
411 raise error.Abort(_('cannot create new bundle repository'))
412 # internal config: bundle.mainreporoot
412 # internal config: bundle.mainreporoot
413 parentpath = ui.config("bundle", "mainreporoot", "")
413 parentpath = ui.config("bundle", "mainreporoot", "")
414 if not parentpath:
414 if not parentpath:
415 # try to find the correct path to the working directory repo
415 # try to find the correct path to the working directory repo
416 parentpath = cmdutil.findrepo(os.getcwd())
416 parentpath = cmdutil.findrepo(os.getcwd())
417 if parentpath is None:
417 if parentpath is None:
418 parentpath = ''
418 parentpath = ''
419 if parentpath:
419 if parentpath:
420 # Try to make the full path relative so we get a nice, short URL.
420 # Try to make the full path relative so we get a nice, short URL.
421 # In particular, we don't want temp dir names in test outputs.
421 # In particular, we don't want temp dir names in test outputs.
422 cwd = os.getcwd()
422 cwd = os.getcwd()
423 if parentpath == cwd:
423 if parentpath == cwd:
424 parentpath = ''
424 parentpath = ''
425 else:
425 else:
426 cwd = pathutil.normasprefix(cwd)
426 cwd = pathutil.normasprefix(cwd)
427 if parentpath.startswith(cwd):
427 if parentpath.startswith(cwd):
428 parentpath = parentpath[len(cwd):]
428 parentpath = parentpath[len(cwd):]
429 u = util.url(path)
429 u = util.url(path)
430 path = u.localpath()
430 path = u.localpath()
431 if u.scheme == 'bundle':
431 if u.scheme == 'bundle':
432 s = path.split("+", 1)
432 s = path.split("+", 1)
433 if len(s) == 1:
433 if len(s) == 1:
434 repopath, bundlename = parentpath, s[0]
434 repopath, bundlename = parentpath, s[0]
435 else:
435 else:
436 repopath, bundlename = s
436 repopath, bundlename = s
437 else:
437 else:
438 repopath, bundlename = parentpath, path
438 repopath, bundlename = parentpath, path
439 return bundlerepository(ui, repopath, bundlename)
439 return bundlerepository(ui, repopath, bundlename)
440
440
441 class bundletransactionmanager(object):
441 class bundletransactionmanager(object):
442 def transaction(self):
442 def transaction(self):
443 return None
443 return None
444
444
445 def close(self):
445 def close(self):
446 raise NotImplementedError
446 raise NotImplementedError
447
447
448 def release(self):
448 def release(self):
449 raise NotImplementedError
449 raise NotImplementedError
450
450
451 def getremotechanges(ui, repo, other, onlyheads=None, bundlename=None,
451 def getremotechanges(ui, repo, other, onlyheads=None, bundlename=None,
452 force=False):
452 force=False):
453 '''obtains a bundle of changes incoming from other
453 '''obtains a bundle of changes incoming from other
454
454
455 "onlyheads" restricts the returned changes to those reachable from the
455 "onlyheads" restricts the returned changes to those reachable from the
456 specified heads.
456 specified heads.
457 "bundlename", if given, stores the bundle to this file path permanently;
457 "bundlename", if given, stores the bundle to this file path permanently;
458 otherwise it's stored to a temp file and gets deleted again when you call
458 otherwise it's stored to a temp file and gets deleted again when you call
459 the returned "cleanupfn".
459 the returned "cleanupfn".
460 "force" indicates whether to proceed on unrelated repos.
460 "force" indicates whether to proceed on unrelated repos.
461
461
462 Returns a tuple (local, csets, cleanupfn):
462 Returns a tuple (local, csets, cleanupfn):
463
463
464 "local" is a local repo from which to obtain the actual incoming
464 "local" is a local repo from which to obtain the actual incoming
465 changesets; it is a bundlerepo for the obtained bundle when the
465 changesets; it is a bundlerepo for the obtained bundle when the
466 original "other" is remote.
466 original "other" is remote.
467 "csets" lists the incoming changeset node ids.
467 "csets" lists the incoming changeset node ids.
468 "cleanupfn" must be called without arguments when you're done processing
468 "cleanupfn" must be called without arguments when you're done processing
469 the changes; it closes both the original "other" and the one returned
469 the changes; it closes both the original "other" and the one returned
470 here.
470 here.
471 '''
471 '''
472 tmp = discovery.findcommonincoming(repo, other, heads=onlyheads,
472 tmp = discovery.findcommonincoming(repo, other, heads=onlyheads,
473 force=force)
473 force=force)
474 common, incoming, rheads = tmp
474 common, incoming, rheads = tmp
475 if not incoming:
475 if not incoming:
476 try:
476 try:
477 if bundlename:
477 if bundlename:
478 os.unlink(bundlename)
478 os.unlink(bundlename)
479 except OSError:
479 except OSError:
480 pass
480 pass
481 return repo, [], other.close
481 return repo, [], other.close
482
482
483 commonset = set(common)
483 commonset = set(common)
484 rheads = [x for x in rheads if x not in commonset]
484 rheads = [x for x in rheads if x not in commonset]
485
485
486 bundle = None
486 bundle = None
487 bundlerepo = None
487 bundlerepo = None
488 localrepo = other.local()
488 localrepo = other.local()
489 if bundlename or not localrepo:
489 if bundlename or not localrepo:
490 # create a bundle (uncompressed if other repo is not local)
490 # create a bundle (uncompressed if other repo is not local)
491
491
492 # developer config: devel.legacy.exchange
492 # developer config: devel.legacy.exchange
493 legexc = ui.configlist('devel', 'legacy.exchange')
493 legexc = ui.configlist('devel', 'legacy.exchange')
494 forcebundle1 = 'bundle2' not in legexc and 'bundle1' in legexc
494 forcebundle1 = 'bundle2' not in legexc and 'bundle1' in legexc
495 canbundle2 = (not forcebundle1
495 canbundle2 = (not forcebundle1
496 and other.capable('getbundle')
496 and other.capable('getbundle')
497 and other.capable('bundle2'))
497 and other.capable('bundle2'))
498 if canbundle2:
498 if canbundle2:
499 kwargs = {}
499 kwargs = {}
500 kwargs['common'] = common
500 kwargs['common'] = common
501 kwargs['heads'] = rheads
501 kwargs['heads'] = rheads
502 kwargs['bundlecaps'] = exchange.caps20to10(repo)
502 kwargs['bundlecaps'] = exchange.caps20to10(repo)
503 kwargs['cg'] = True
503 kwargs['cg'] = True
504 b2 = other.getbundle('incoming', **kwargs)
504 b2 = other.getbundle('incoming', **kwargs)
505 fname = bundle = changegroup.writechunks(ui, b2._forwardchunks(),
505 fname = bundle = changegroup.writechunks(ui, b2._forwardchunks(),
506 bundlename)
506 bundlename)
507 else:
507 else:
508 if other.capable('getbundle'):
508 if other.capable('getbundle'):
509 cg = other.getbundle('incoming', common=common, heads=rheads)
509 cg = other.getbundle('incoming', common=common, heads=rheads)
510 elif onlyheads is None and not other.capable('changegroupsubset'):
510 elif onlyheads is None and not other.capable('changegroupsubset'):
511 # compat with older servers when pulling all remote heads
511 # compat with older servers when pulling all remote heads
512 cg = other.changegroup(incoming, "incoming")
512 cg = other.changegroup(incoming, "incoming")
513 rheads = None
513 rheads = None
514 else:
514 else:
515 cg = other.changegroupsubset(incoming, rheads, 'incoming')
515 cg = other.changegroupsubset(incoming, rheads, 'incoming')
516 if localrepo:
516 if localrepo:
517 bundletype = "HG10BZ"
517 bundletype = "HG10BZ"
518 else:
518 else:
519 bundletype = "HG10UN"
519 bundletype = "HG10UN"
520 fname = bundle = bundle2.writebundle(ui, cg, bundlename,
520 fname = bundle = bundle2.writebundle(ui, cg, bundlename,
521 bundletype)
521 bundletype)
522 # keep written bundle?
522 # keep written bundle?
523 if bundlename:
523 if bundlename:
524 bundle = None
524 bundle = None
525 if not localrepo:
525 if not localrepo:
526 # use the created uncompressed bundlerepo
526 # use the created uncompressed bundlerepo
527 localrepo = bundlerepo = bundlerepository(repo.baseui, repo.root,
527 localrepo = bundlerepo = bundlerepository(repo.baseui, repo.root,
528 fname)
528 fname)
529 # this repo contains local and other now, so filter out local again
529 # this repo contains local and other now, so filter out local again
530 common = repo.heads()
530 common = repo.heads()
531 if localrepo:
531 if localrepo:
532 # Part of common may be remotely filtered
532 # Part of common may be remotely filtered
533 # So use an unfiltered version
533 # So use an unfiltered version
534 # The discovery process probably need cleanup to avoid that
534 # The discovery process probably need cleanup to avoid that
535 localrepo = localrepo.unfiltered()
535 localrepo = localrepo.unfiltered()
536
536
537 csets = localrepo.changelog.findmissing(common, rheads)
537 csets = localrepo.changelog.findmissing(common, rheads)
538
538
539 if bundlerepo:
539 if bundlerepo:
540 reponodes = [ctx.node() for ctx in bundlerepo[bundlerepo.firstnewrev:]]
540 reponodes = [ctx.node() for ctx in bundlerepo[bundlerepo.firstnewrev:]]
541 remotephases = other.listkeys('phases')
541 remotephases = other.listkeys('phases')
542
542
543 pullop = exchange.pulloperation(bundlerepo, other, heads=reponodes)
543 pullop = exchange.pulloperation(bundlerepo, other, heads=reponodes)
544 pullop.trmanager = bundletransactionmanager()
544 pullop.trmanager = bundletransactionmanager()
545 exchange._pullapplyphases(pullop, remotephases)
545 exchange._pullapplyphases(pullop, remotephases)
546
546
547 def cleanup():
547 def cleanup():
548 if bundlerepo:
548 if bundlerepo:
549 bundlerepo.close()
549 bundlerepo.close()
550 if bundle:
550 if bundle:
551 os.unlink(bundle)
551 os.unlink(bundle)
552 other.close()
552 other.close()
553
553
554 return (localrepo, csets, cleanup)
554 return (localrepo, csets, cleanup)
General Comments 0
You need to be logged in to leave comments. Login now