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