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