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