##// END OF EJS Templates
bundlerepo: indent some code to prepare next patch...
Pierre-Yves David -
r26543:a018cbab default
parent child Browse files
Show More
@@ -1,503 +1,505
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 self._tempparent = None
240 self._tempparent = None
241 try:
241 try:
242 localrepo.localrepository.__init__(self, ui, path)
242 localrepo.localrepository.__init__(self, ui, path)
243 except error.RepoError:
243 except error.RepoError:
244 self._tempparent = tempfile.mkdtemp()
244 self._tempparent = tempfile.mkdtemp()
245 localrepo.instance(ui, self._tempparent, 1)
245 localrepo.instance(ui, self._tempparent, 1)
246 localrepo.localrepository.__init__(self, ui, self._tempparent)
246 localrepo.localrepository.__init__(self, ui, self._tempparent)
247 self.ui.setconfig('phases', 'publish', False, 'bundlerepo')
247 self.ui.setconfig('phases', 'publish', False, 'bundlerepo')
248
248
249 if path:
249 if path:
250 self._url = 'bundle:' + util.expandpath(path) + '+' + bundlename
250 self._url = 'bundle:' + util.expandpath(path) + '+' + bundlename
251 else:
251 else:
252 self._url = 'bundle:' + bundlename
252 self._url = 'bundle:' + bundlename
253
253
254 self.tempfile = None
254 self.tempfile = None
255 f = util.posixfile(bundlename, "rb")
255 f = util.posixfile(bundlename, "rb")
256 self.bundlefile = self.bundle = exchange.readbundle(ui, f, bundlename)
256 self.bundlefile = self.bundle = exchange.readbundle(ui, f, bundlename)
257 if self.bundle.compressed():
257 if self.bundle.compressed():
258 fdtemp, temp = self.vfs.mkstemp(prefix="hg-bundle-",
258 fdtemp, temp = self.vfs.mkstemp(prefix="hg-bundle-",
259 suffix=".hg10un")
259 suffix=".hg10un")
260 self.tempfile = temp
260 self.tempfile = temp
261 fptemp = os.fdopen(fdtemp, 'wb')
261 fptemp = os.fdopen(fdtemp, 'wb')
262
262
263 try:
263 try:
264 fptemp.write("HG10UN")
264 fptemp.write("HG10UN")
265 while True:
265 while True:
266 chunk = self.bundle.read(2**18)
266 chunk = self.bundle.read(2**18)
267 if not chunk:
267 if not chunk:
268 break
268 break
269 fptemp.write(chunk)
269 fptemp.write(chunk)
270 finally:
270 finally:
271 fptemp.close()
271 fptemp.close()
272
272
273 f = self.vfs.open(self.tempfile, mode="rb")
273 f = self.vfs.open(self.tempfile, mode="rb")
274 self.bundlefile = self.bundle = exchange.readbundle(ui, f,
274 self.bundlefile = self.bundle = exchange.readbundle(ui, f,
275 bundlename,
275 bundlename,
276 self.vfs)
276 self.vfs)
277
277
278 if isinstance(self.bundle, bundle2.unbundle20):
278 if isinstance(self.bundle, bundle2.unbundle20):
279 cgparts = [part for part in self.bundle.iterparts()
279 cgparts = [part for part in self.bundle.iterparts()
280 if (part.type == 'changegroup')
280 if (part.type == 'changegroup')
281 and (part.params.get('version', '01')
281 and (part.params.get('version', '01')
282 in changegroup.packermap)]
282 in changegroup.packermap)]
283
283
284 if not cgparts:
284 if not cgparts:
285 raise util.Abort('No changegroups found')
285 raise util.Abort('No changegroups found')
286 version = cgparts[0].params.get('version', '01')
286 version = cgparts[0].params.get('version', '01')
287 cgparts = [p for p in cgparts
287 cgparts = [p for p in cgparts
288 if p.params.get('version', '01') == version]
288 if p.params.get('version', '01') == version]
289 if len(cgparts) > 1:
289 if len(cgparts) > 1:
290 raise NotImplementedError("Can't process multiple changegroups")
290 raise NotImplementedError("Can't process multiple changegroups")
291 part = cgparts[0]
291 part = cgparts[0]
292
292
293 part.seek(0)
293 part.seek(0)
294 self.bundle = changegroup.packermap[version][1](part, 'UN')
294 self.bundle = changegroup.packermap[version][1](part, 'UN')
295
295
296 # dict with the mapping 'filename' -> position in the bundle
296 # dict with the mapping 'filename' -> position in the bundle
297 self.bundlefilespos = {}
297 self.bundlefilespos = {}
298
298
299 self.firstnewrev = self.changelog.repotiprev + 1
299 self.firstnewrev = self.changelog.repotiprev + 1
300 phases.retractboundary(self, None, phases.draft,
300 phases.retractboundary(self, None, phases.draft,
301 [ctx.node() for ctx in self[self.firstnewrev:]])
301 [ctx.node() for ctx in self[self.firstnewrev:]])
302
302
303 @localrepo.unfilteredpropertycache
303 @localrepo.unfilteredpropertycache
304 def _phasecache(self):
304 def _phasecache(self):
305 return bundlephasecache(self, self._phasedefaults)
305 return bundlephasecache(self, self._phasedefaults)
306
306
307 @localrepo.unfilteredpropertycache
307 @localrepo.unfilteredpropertycache
308 def changelog(self):
308 def changelog(self):
309 # consume the header if it exists
309 # consume the header if it exists
310 self.bundle.changelogheader()
310 self.bundle.changelogheader()
311 c = bundlechangelog(self.svfs, self.bundle)
311 c = bundlechangelog(self.svfs, self.bundle)
312 self.manstart = self.bundle.tell()
312 self.manstart = self.bundle.tell()
313 return c
313 return c
314
314
315 @localrepo.unfilteredpropertycache
315 @localrepo.unfilteredpropertycache
316 def manifest(self):
316 def manifest(self):
317 self.bundle.seek(self.manstart)
317 self.bundle.seek(self.manstart)
318 # consume the header if it exists
318 # consume the header if it exists
319 self.bundle.manifestheader()
319 self.bundle.manifestheader()
320 m = bundlemanifest(self.svfs, self.bundle, self.changelog.rev)
320 m = bundlemanifest(self.svfs, self.bundle, self.changelog.rev)
321 self.filestart = self.bundle.tell()
321 self.filestart = self.bundle.tell()
322 return m
322 return m
323
323
324 @localrepo.unfilteredpropertycache
324 @localrepo.unfilteredpropertycache
325 def manstart(self):
325 def manstart(self):
326 self.changelog
326 self.changelog
327 return self.manstart
327 return self.manstart
328
328
329 @localrepo.unfilteredpropertycache
329 @localrepo.unfilteredpropertycache
330 def filestart(self):
330 def filestart(self):
331 self.manifest
331 self.manifest
332 return self.filestart
332 return self.filestart
333
333
334 def url(self):
334 def url(self):
335 return self._url
335 return self._url
336
336
337 def file(self, f):
337 def file(self, f):
338 if not self.bundlefilespos:
338 if not self.bundlefilespos:
339 self.bundle.seek(self.filestart)
339 self.bundle.seek(self.filestart)
340 while True:
340 while True:
341 chunkdata = self.bundle.filelogheader()
341 chunkdata = self.bundle.filelogheader()
342 if not chunkdata:
342 if not chunkdata:
343 break
343 break
344 fname = chunkdata['filename']
344 fname = chunkdata['filename']
345 self.bundlefilespos[fname] = self.bundle.tell()
345 self.bundlefilespos[fname] = self.bundle.tell()
346 while True:
346 while True:
347 c = self.bundle.deltachunk(None)
347 c = self.bundle.deltachunk(None)
348 if not c:
348 if not c:
349 break
349 break
350
350
351 if f in self.bundlefilespos:
351 if f in self.bundlefilespos:
352 self.bundle.seek(self.bundlefilespos[f])
352 self.bundle.seek(self.bundlefilespos[f])
353 return bundlefilelog(self.svfs, f, self.bundle, self.changelog.rev)
353 return bundlefilelog(self.svfs, f, self.bundle, self.changelog.rev)
354 else:
354 else:
355 return filelog.filelog(self.svfs, f)
355 return filelog.filelog(self.svfs, f)
356
356
357 def close(self):
357 def close(self):
358 """Close assigned bundle file immediately."""
358 """Close assigned bundle file immediately."""
359 self.bundlefile.close()
359 self.bundlefile.close()
360 if self.tempfile is not None:
360 if self.tempfile is not None:
361 self.vfs.unlink(self.tempfile)
361 self.vfs.unlink(self.tempfile)
362 if self._tempparent:
362 if self._tempparent:
363 shutil.rmtree(self._tempparent, True)
363 shutil.rmtree(self._tempparent, True)
364
364
365 def cancopy(self):
365 def cancopy(self):
366 return False
366 return False
367
367
368 def peer(self):
368 def peer(self):
369 return bundlepeer(self)
369 return bundlepeer(self)
370
370
371 def getcwd(self):
371 def getcwd(self):
372 return os.getcwd() # always outside the repo
372 return os.getcwd() # always outside the repo
373
373
374
374
375 def instance(ui, path, create):
375 def instance(ui, path, create):
376 if create:
376 if create:
377 raise util.Abort(_('cannot create new bundle repository'))
377 raise util.Abort(_('cannot create new bundle repository'))
378 # internal config: bundle.mainreporoot
378 # internal config: bundle.mainreporoot
379 parentpath = ui.config("bundle", "mainreporoot", "")
379 parentpath = ui.config("bundle", "mainreporoot", "")
380 if not parentpath:
380 if not parentpath:
381 # try to find the correct path to the working directory repo
381 # try to find the correct path to the working directory repo
382 parentpath = cmdutil.findrepo(os.getcwd())
382 parentpath = cmdutil.findrepo(os.getcwd())
383 if parentpath is None:
383 if parentpath is None:
384 parentpath = ''
384 parentpath = ''
385 if parentpath:
385 if parentpath:
386 # Try to make the full path relative so we get a nice, short URL.
386 # Try to make the full path relative so we get a nice, short URL.
387 # In particular, we don't want temp dir names in test outputs.
387 # In particular, we don't want temp dir names in test outputs.
388 cwd = os.getcwd()
388 cwd = os.getcwd()
389 if parentpath == cwd:
389 if parentpath == cwd:
390 parentpath = ''
390 parentpath = ''
391 else:
391 else:
392 cwd = pathutil.normasprefix(cwd)
392 cwd = pathutil.normasprefix(cwd)
393 if parentpath.startswith(cwd):
393 if parentpath.startswith(cwd):
394 parentpath = parentpath[len(cwd):]
394 parentpath = parentpath[len(cwd):]
395 u = util.url(path)
395 u = util.url(path)
396 path = u.localpath()
396 path = u.localpath()
397 if u.scheme == 'bundle':
397 if u.scheme == 'bundle':
398 s = path.split("+", 1)
398 s = path.split("+", 1)
399 if len(s) == 1:
399 if len(s) == 1:
400 repopath, bundlename = parentpath, s[0]
400 repopath, bundlename = parentpath, s[0]
401 else:
401 else:
402 repopath, bundlename = s
402 repopath, bundlename = s
403 else:
403 else:
404 repopath, bundlename = parentpath, path
404 repopath, bundlename = parentpath, path
405 return bundlerepository(ui, repopath, bundlename)
405 return bundlerepository(ui, repopath, bundlename)
406
406
407 class bundletransactionmanager(object):
407 class bundletransactionmanager(object):
408 def transaction(self):
408 def transaction(self):
409 return None
409 return None
410
410
411 def close(self):
411 def close(self):
412 raise NotImplementedError
412 raise NotImplementedError
413
413
414 def release(self):
414 def release(self):
415 raise NotImplementedError
415 raise NotImplementedError
416
416
417 def getremotechanges(ui, repo, other, onlyheads=None, bundlename=None,
417 def getremotechanges(ui, repo, other, onlyheads=None, bundlename=None,
418 force=False):
418 force=False):
419 '''obtains a bundle of changes incoming from other
419 '''obtains a bundle of changes incoming from other
420
420
421 "onlyheads" restricts the returned changes to those reachable from the
421 "onlyheads" restricts the returned changes to those reachable from the
422 specified heads.
422 specified heads.
423 "bundlename", if given, stores the bundle to this file path permanently;
423 "bundlename", if given, stores the bundle to this file path permanently;
424 otherwise it's stored to a temp file and gets deleted again when you call
424 otherwise it's stored to a temp file and gets deleted again when you call
425 the returned "cleanupfn".
425 the returned "cleanupfn".
426 "force" indicates whether to proceed on unrelated repos.
426 "force" indicates whether to proceed on unrelated repos.
427
427
428 Returns a tuple (local, csets, cleanupfn):
428 Returns a tuple (local, csets, cleanupfn):
429
429
430 "local" is a local repo from which to obtain the actual incoming
430 "local" is a local repo from which to obtain the actual incoming
431 changesets; it is a bundlerepo for the obtained bundle when the
431 changesets; it is a bundlerepo for the obtained bundle when the
432 original "other" is remote.
432 original "other" is remote.
433 "csets" lists the incoming changeset node ids.
433 "csets" lists the incoming changeset node ids.
434 "cleanupfn" must be called without arguments when you're done processing
434 "cleanupfn" must be called without arguments when you're done processing
435 the changes; it closes both the original "other" and the one returned
435 the changes; it closes both the original "other" and the one returned
436 here.
436 here.
437 '''
437 '''
438 tmp = discovery.findcommonincoming(repo, other, heads=onlyheads,
438 tmp = discovery.findcommonincoming(repo, other, heads=onlyheads,
439 force=force)
439 force=force)
440 common, incoming, rheads = tmp
440 common, incoming, rheads = tmp
441 if not incoming:
441 if not incoming:
442 try:
442 try:
443 if bundlename:
443 if bundlename:
444 os.unlink(bundlename)
444 os.unlink(bundlename)
445 except OSError:
445 except OSError:
446 pass
446 pass
447 return repo, [], other.close
447 return repo, [], other.close
448
448
449 commonset = set(common)
449 commonset = set(common)
450 rheads = [x for x in rheads if x not in commonset]
450 rheads = [x for x in rheads if x not in commonset]
451
451
452 bundle = None
452 bundle = None
453 bundlerepo = None
453 bundlerepo = None
454 localrepo = other.local()
454 localrepo = other.local()
455 if bundlename or not localrepo:
455 if bundlename or not localrepo:
456 # create a bundle (uncompressed if other repo is not local)
456 # create a bundle (uncompressed if other repo is not local)
457
457
458 if other.capable('getbundle'):
458 if True:
459 cg = other.getbundle('incoming', common=common, heads=rheads)
459 if other.capable('getbundle'):
460 elif onlyheads is None and not other.capable('changegroupsubset'):
460 cg = other.getbundle('incoming', common=common, heads=rheads)
461 # compat with older servers when pulling all remote heads
461 elif onlyheads is None and not other.capable('changegroupsubset'):
462 cg = other.changegroup(incoming, "incoming")
462 # compat with older servers when pulling all remote heads
463 rheads = None
463 cg = other.changegroup(incoming, "incoming")
464 else:
464 rheads = None
465 cg = other.changegroupsubset(incoming, rheads, 'incoming')
465 else:
466 if localrepo:
466 cg = other.changegroupsubset(incoming, rheads, 'incoming')
467 bundletype = "HG10BZ"
467 if localrepo:
468 else:
468 bundletype = "HG10BZ"
469 bundletype = "HG10UN"
469 else:
470 fname = bundle = changegroup.writebundle(ui, cg, bundlename, bundletype)
470 bundletype = "HG10UN"
471 fname = bundle = changegroup.writebundle(ui, cg, bundlename,
472 bundletype)
471 # keep written bundle?
473 # keep written bundle?
472 if bundlename:
474 if bundlename:
473 bundle = None
475 bundle = None
474 if not localrepo:
476 if not localrepo:
475 # use the created uncompressed bundlerepo
477 # use the created uncompressed bundlerepo
476 localrepo = bundlerepo = bundlerepository(repo.baseui, repo.root,
478 localrepo = bundlerepo = bundlerepository(repo.baseui, repo.root,
477 fname)
479 fname)
478 # this repo contains local and other now, so filter out local again
480 # this repo contains local and other now, so filter out local again
479 common = repo.heads()
481 common = repo.heads()
480 if localrepo:
482 if localrepo:
481 # Part of common may be remotely filtered
483 # Part of common may be remotely filtered
482 # So use an unfiltered version
484 # So use an unfiltered version
483 # The discovery process probably need cleanup to avoid that
485 # The discovery process probably need cleanup to avoid that
484 localrepo = localrepo.unfiltered()
486 localrepo = localrepo.unfiltered()
485
487
486 csets = localrepo.changelog.findmissing(common, rheads)
488 csets = localrepo.changelog.findmissing(common, rheads)
487
489
488 if bundlerepo:
490 if bundlerepo:
489 reponodes = [ctx.node() for ctx in bundlerepo[bundlerepo.firstnewrev:]]
491 reponodes = [ctx.node() for ctx in bundlerepo[bundlerepo.firstnewrev:]]
490 remotephases = other.listkeys('phases')
492 remotephases = other.listkeys('phases')
491
493
492 pullop = exchange.pulloperation(bundlerepo, other, heads=reponodes)
494 pullop = exchange.pulloperation(bundlerepo, other, heads=reponodes)
493 pullop.trmanager = bundletransactionmanager()
495 pullop.trmanager = bundletransactionmanager()
494 exchange._pullapplyphases(pullop, remotephases)
496 exchange._pullapplyphases(pullop, remotephases)
495
497
496 def cleanup():
498 def cleanup():
497 if bundlerepo:
499 if bundlerepo:
498 bundlerepo.close()
500 bundlerepo.close()
499 if bundle:
501 if bundle:
500 os.unlink(bundle)
502 os.unlink(bundle)
501 other.close()
503 other.close()
502
504
503 return (localrepo, csets, cleanup)
505 return (localrepo, csets, cleanup)
General Comments 0
You need to be logged in to leave comments. Login now