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