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