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