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