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