##// END OF EJS Templates
unionrepo: sync with repository API...
Joerg Sonnenberger -
r42382:94e2f843 default
parent child Browse files
Show More
@@ -1,287 +1,288 b''
1 # unionrepo.py - repository class for viewing union of repository changesets
1 # unionrepo.py - repository class for viewing union of repository changesets
2 #
2 #
3 # Derived from bundlerepo.py
3 # Derived from bundlerepo.py
4 # Copyright 2006, 2007 Benoit Boissinot <bboissin@gmail.com>
4 # Copyright 2006, 2007 Benoit Boissinot <bboissin@gmail.com>
5 # Copyright 2013 Unity Technologies, Mads Kiilerich <madski@unity3d.com>
5 # Copyright 2013 Unity Technologies, Mads Kiilerich <madski@unity3d.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 """Repository class for "in-memory pull" of one local repository to another,
10 """Repository class for "in-memory pull" of one local repository to another,
11 allowing operations like diff and log with revsets.
11 allowing operations like diff and log with revsets.
12 """
12 """
13
13
14 from __future__ import absolute_import
14 from __future__ import absolute_import
15
15
16 from .i18n import _
16 from .i18n import _
17 from .node import nullid
17 from .node import nullid
18
18
19 from . import (
19 from . import (
20 changelog,
20 changelog,
21 cmdutil,
21 cmdutil,
22 encoding,
22 encoding,
23 error,
23 error,
24 filelog,
24 filelog,
25 localrepo,
25 localrepo,
26 manifest,
26 manifest,
27 mdiff,
27 mdiff,
28 pathutil,
28 pathutil,
29 revlog,
29 revlog,
30 util,
30 util,
31 vfs as vfsmod,
31 vfs as vfsmod,
32 )
32 )
33
33
34 class unionrevlog(revlog.revlog):
34 class unionrevlog(revlog.revlog):
35 def __init__(self, opener, indexfile, revlog2, linkmapper):
35 def __init__(self, opener, indexfile, revlog2, linkmapper):
36 # How it works:
36 # How it works:
37 # To retrieve a revision, we just need to know the node id so we can
37 # To retrieve a revision, we just need to know the node id so we can
38 # look it up in revlog2.
38 # look it up in revlog2.
39 #
39 #
40 # To differentiate a rev in the second revlog from a rev in the revlog,
40 # To differentiate a rev in the second revlog from a rev in the revlog,
41 # we check revision against repotiprev.
41 # we check revision against repotiprev.
42 opener = vfsmod.readonlyvfs(opener)
42 opener = vfsmod.readonlyvfs(opener)
43 revlog.revlog.__init__(self, opener, indexfile)
43 revlog.revlog.__init__(self, opener, indexfile)
44 self.revlog2 = revlog2
44 self.revlog2 = revlog2
45
45
46 n = len(self)
46 n = len(self)
47 self.repotiprev = n - 1
47 self.repotiprev = n - 1
48 self.bundlerevs = set() # used by 'bundle()' revset expression
48 self.bundlerevs = set() # used by 'bundle()' revset expression
49 for rev2 in self.revlog2:
49 for rev2 in self.revlog2:
50 rev = self.revlog2.index[rev2]
50 rev = self.revlog2.index[rev2]
51 # rev numbers - in revlog2, very different from self.rev
51 # rev numbers - in revlog2, very different from self.rev
52 _start, _csize, rsize, base, linkrev, p1rev, p2rev, node = rev
52 _start, _csize, rsize, base, linkrev, p1rev, p2rev, node = rev
53 flags = _start & 0xFFFF
53 flags = _start & 0xFFFF
54
54
55 if linkmapper is None: # link is to same revlog
55 if linkmapper is None: # link is to same revlog
56 assert linkrev == rev2 # we never link back
56 assert linkrev == rev2 # we never link back
57 link = n
57 link = n
58 else: # rev must be mapped from repo2 cl to unified cl by linkmapper
58 else: # rev must be mapped from repo2 cl to unified cl by linkmapper
59 link = linkmapper(linkrev)
59 link = linkmapper(linkrev)
60
60
61 if linkmapper is not None: # link is to same revlog
61 if linkmapper is not None: # link is to same revlog
62 base = linkmapper(base)
62 base = linkmapper(base)
63
63
64 if node in self.nodemap:
64 if node in self.nodemap:
65 # this happens for the common revlog revisions
65 # this happens for the common revlog revisions
66 self.bundlerevs.add(self.nodemap[node])
66 self.bundlerevs.add(self.nodemap[node])
67 continue
67 continue
68
68
69 p1node = self.revlog2.node(p1rev)
69 p1node = self.revlog2.node(p1rev)
70 p2node = self.revlog2.node(p2rev)
70 p2node = self.revlog2.node(p2rev)
71
71
72 # TODO: it's probably wrong to set compressed length to None, but
72 # TODO: it's probably wrong to set compressed length to None, but
73 # I have no idea if csize is valid in the base revlog context.
73 # I have no idea if csize is valid in the base revlog context.
74 e = (flags, None, rsize, base,
74 e = (flags, None, rsize, base,
75 link, self.rev(p1node), self.rev(p2node), node)
75 link, self.rev(p1node), self.rev(p2node), node)
76 self.index.append(e)
76 self.index.append(e)
77 self.nodemap[node] = n
77 self.nodemap[node] = n
78 self.bundlerevs.add(n)
78 self.bundlerevs.add(n)
79 n += 1
79 n += 1
80
80
81 def _chunk(self, rev):
81 def _chunk(self, rev):
82 if rev <= self.repotiprev:
82 if rev <= self.repotiprev:
83 return revlog.revlog._chunk(self, rev)
83 return revlog.revlog._chunk(self, rev)
84 return self.revlog2._chunk(self.node(rev))
84 return self.revlog2._chunk(self.node(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 return self.revlog2.revdiff(
89 return self.revlog2.revdiff(
90 self.revlog2.rev(self.node(rev1)),
90 self.revlog2.rev(self.node(rev1)),
91 self.revlog2.rev(self.node(rev2)))
91 self.revlog2.rev(self.node(rev2)))
92 elif rev1 <= self.repotiprev and rev2 <= self.repotiprev:
92 elif rev1 <= self.repotiprev and rev2 <= self.repotiprev:
93 return self.baserevdiff(rev1, rev2)
93 return self.baserevdiff(rev1, rev2)
94
94
95 return mdiff.textdiff(self.revision(rev1), self.revision(rev2))
95 return mdiff.textdiff(self.revision(rev1), self.revision(rev2))
96
96
97 def revision(self, nodeorrev, _df=None, raw=False):
97 def revision(self, nodeorrev, _df=None, raw=False):
98 """return an uncompressed revision of a given node or revision
98 """return an uncompressed revision of a given node or revision
99 number.
99 number.
100 """
100 """
101 if isinstance(nodeorrev, int):
101 if isinstance(nodeorrev, int):
102 rev = nodeorrev
102 rev = nodeorrev
103 node = self.node(rev)
103 node = self.node(rev)
104 else:
104 else:
105 node = nodeorrev
105 node = nodeorrev
106 rev = self.rev(node)
106 rev = self.rev(node)
107
107
108 if node == nullid:
108 if node == nullid:
109 return ""
109 return ""
110
110
111 if rev > self.repotiprev:
111 if rev > self.repotiprev:
112 text = self.revlog2.revision(node)
112 text = self.revlog2.revision(node)
113 self._revisioncache = (node, rev, text)
113 self._revisioncache = (node, rev, text)
114 else:
114 else:
115 text = self.baserevision(rev)
115 text = self.baserevision(rev)
116 # already cached
116 # already cached
117 return text
117 return text
118
118
119 def baserevision(self, nodeorrev):
119 def baserevision(self, nodeorrev):
120 # Revlog subclasses may override 'revision' method to modify format of
120 # Revlog subclasses may override 'revision' method to modify format of
121 # content retrieved from revlog. To use unionrevlog with such class one
121 # content retrieved from revlog. To use unionrevlog with such class one
122 # needs to override 'baserevision' and make more specific call here.
122 # needs to override 'baserevision' and make more specific call here.
123 return revlog.revlog.revision(self, nodeorrev)
123 return revlog.revlog.revision(self, nodeorrev)
124
124
125 def baserevdiff(self, rev1, rev2):
125 def baserevdiff(self, rev1, rev2):
126 # Exists for the same purpose as baserevision.
126 # Exists for the same purpose as baserevision.
127 return revlog.revlog.revdiff(self, rev1, rev2)
127 return revlog.revlog.revdiff(self, rev1, rev2)
128
128
129 def addrevision(self, text, transaction, link, p1=None, p2=None, d=None):
129 def addrevision(self, text, transaction, link, p1=None, p2=None, d=None):
130 raise NotImplementedError
130 raise NotImplementedError
131 def addgroup(self, deltas, transaction, addrevisioncb=None):
131 def addgroup(self, deltas, linkmapper, transaction, addrevisioncb=None,
132 maybemissingparents=False):
132 raise NotImplementedError
133 raise NotImplementedError
133 def strip(self, rev, minlink):
134 def strip(self, minlink, transaction):
134 raise NotImplementedError
135 raise NotImplementedError
135 def checksize(self):
136 def checksize(self):
136 raise NotImplementedError
137 raise NotImplementedError
137
138
138 class unionchangelog(unionrevlog, changelog.changelog):
139 class unionchangelog(unionrevlog, changelog.changelog):
139 def __init__(self, opener, opener2):
140 def __init__(self, opener, opener2):
140 changelog.changelog.__init__(self, opener)
141 changelog.changelog.__init__(self, opener)
141 linkmapper = None
142 linkmapper = None
142 changelog2 = changelog.changelog(opener2)
143 changelog2 = changelog.changelog(opener2)
143 unionrevlog.__init__(self, opener, self.indexfile, changelog2,
144 unionrevlog.__init__(self, opener, self.indexfile, changelog2,
144 linkmapper)
145 linkmapper)
145
146
146 def baserevision(self, nodeorrev):
147 def baserevision(self, nodeorrev):
147 # Although changelog doesn't override 'revision' method, some extensions
148 # Although changelog doesn't override 'revision' method, some extensions
148 # may replace this class with another that does. Same story with
149 # may replace this class with another that does. Same story with
149 # manifest and filelog classes.
150 # manifest and filelog classes.
150 return changelog.changelog.revision(self, nodeorrev)
151 return changelog.changelog.revision(self, nodeorrev)
151
152
152 def baserevdiff(self, rev1, rev2):
153 def baserevdiff(self, rev1, rev2):
153 return changelog.changelog.revdiff(self, rev1, rev2)
154 return changelog.changelog.revdiff(self, rev1, rev2)
154
155
155 class unionmanifest(unionrevlog, manifest.manifestrevlog):
156 class unionmanifest(unionrevlog, manifest.manifestrevlog):
156 def __init__(self, opener, opener2, linkmapper):
157 def __init__(self, opener, opener2, linkmapper):
157 manifest.manifestrevlog.__init__(self, opener)
158 manifest.manifestrevlog.__init__(self, opener)
158 manifest2 = manifest.manifestrevlog(opener2)
159 manifest2 = manifest.manifestrevlog(opener2)
159 unionrevlog.__init__(self, opener, self.indexfile, manifest2,
160 unionrevlog.__init__(self, opener, self.indexfile, manifest2,
160 linkmapper)
161 linkmapper)
161
162
162 def baserevision(self, nodeorrev):
163 def baserevision(self, nodeorrev):
163 return manifest.manifestrevlog.revision(self, nodeorrev)
164 return manifest.manifestrevlog.revision(self, nodeorrev)
164
165
165 def baserevdiff(self, rev1, rev2):
166 def baserevdiff(self, rev1, rev2):
166 return manifest.manifestrevlog.revdiff(self, rev1, rev2)
167 return manifest.manifestrevlog.revdiff(self, rev1, rev2)
167
168
168 class unionfilelog(filelog.filelog):
169 class unionfilelog(filelog.filelog):
169 def __init__(self, opener, path, opener2, linkmapper, repo):
170 def __init__(self, opener, path, opener2, linkmapper, repo):
170 filelog.filelog.__init__(self, opener, path)
171 filelog.filelog.__init__(self, opener, path)
171 filelog2 = filelog.filelog(opener2, path)
172 filelog2 = filelog.filelog(opener2, path)
172 self._revlog = unionrevlog(opener, self.indexfile,
173 self._revlog = unionrevlog(opener, self.indexfile,
173 filelog2._revlog, linkmapper)
174 filelog2._revlog, linkmapper)
174 self._repo = repo
175 self._repo = repo
175 self.repotiprev = self._revlog.repotiprev
176 self.repotiprev = self._revlog.repotiprev
176 self.revlog2 = self._revlog.revlog2
177 self.revlog2 = self._revlog.revlog2
177
178
178 def baserevision(self, nodeorrev):
179 def baserevision(self, nodeorrev):
179 return filelog.filelog.revision(self, nodeorrev)
180 return filelog.filelog.revision(self, nodeorrev)
180
181
181 def baserevdiff(self, rev1, rev2):
182 def baserevdiff(self, rev1, rev2):
182 return filelog.filelog.revdiff(self, rev1, rev2)
183 return filelog.filelog.revdiff(self, rev1, rev2)
183
184
184 def iscensored(self, rev):
185 def iscensored(self, rev):
185 """Check if a revision is censored."""
186 """Check if a revision is censored."""
186 if rev <= self.repotiprev:
187 if rev <= self.repotiprev:
187 return filelog.filelog.iscensored(self, rev)
188 return filelog.filelog.iscensored(self, rev)
188 node = self.node(rev)
189 node = self.node(rev)
189 return self.revlog2.iscensored(self.revlog2.rev(node))
190 return self.revlog2.iscensored(self.revlog2.rev(node))
190
191
191 class unionpeer(localrepo.localpeer):
192 class unionpeer(localrepo.localpeer):
192 def canpush(self):
193 def canpush(self):
193 return False
194 return False
194
195
195 class unionrepository(object):
196 class unionrepository(object):
196 """Represents the union of data in 2 repositories.
197 """Represents the union of data in 2 repositories.
197
198
198 Instances are not usable if constructed directly. Use ``instance()``
199 Instances are not usable if constructed directly. Use ``instance()``
199 or ``makeunionrepository()`` to create a usable instance.
200 or ``makeunionrepository()`` to create a usable instance.
200 """
201 """
201 def __init__(self, repo2, url):
202 def __init__(self, repo2, url):
202 self.repo2 = repo2
203 self.repo2 = repo2
203 self._url = url
204 self._url = url
204
205
205 self.ui.setconfig('phases', 'publish', False, 'unionrepo')
206 self.ui.setconfig('phases', 'publish', False, 'unionrepo')
206
207
207 @localrepo.unfilteredpropertycache
208 @localrepo.unfilteredpropertycache
208 def changelog(self):
209 def changelog(self):
209 return unionchangelog(self.svfs, self.repo2.svfs)
210 return unionchangelog(self.svfs, self.repo2.svfs)
210
211
211 @localrepo.unfilteredpropertycache
212 @localrepo.unfilteredpropertycache
212 def manifestlog(self):
213 def manifestlog(self):
213 rootstore = unionmanifest(self.svfs, self.repo2.svfs,
214 rootstore = unionmanifest(self.svfs, self.repo2.svfs,
214 self.unfiltered()._clrev)
215 self.unfiltered()._clrev)
215 return manifest.manifestlog(self.svfs, self, rootstore,
216 return manifest.manifestlog(self.svfs, self, rootstore,
216 self.narrowmatch())
217 self.narrowmatch())
217
218
218 def _clrev(self, rev2):
219 def _clrev(self, rev2):
219 """map from repo2 changelog rev to temporary rev in self.changelog"""
220 """map from repo2 changelog rev to temporary rev in self.changelog"""
220 node = self.repo2.changelog.node(rev2)
221 node = self.repo2.changelog.node(rev2)
221 return self.changelog.rev(node)
222 return self.changelog.rev(node)
222
223
223 def url(self):
224 def url(self):
224 return self._url
225 return self._url
225
226
226 def file(self, f):
227 def file(self, f):
227 return unionfilelog(self.svfs, f, self.repo2.svfs,
228 return unionfilelog(self.svfs, f, self.repo2.svfs,
228 self.unfiltered()._clrev, self)
229 self.unfiltered()._clrev, self)
229
230
230 def close(self):
231 def close(self):
231 self.repo2.close()
232 self.repo2.close()
232
233
233 def cancopy(self):
234 def cancopy(self):
234 return False
235 return False
235
236
236 def peer(self):
237 def peer(self):
237 return unionpeer(self)
238 return unionpeer(self)
238
239
239 def getcwd(self):
240 def getcwd(self):
240 return encoding.getcwd() # always outside the repo
241 return encoding.getcwd() # always outside the repo
241
242
242 def instance(ui, path, create, intents=None, createopts=None):
243 def instance(ui, path, create, intents=None, createopts=None):
243 if create:
244 if create:
244 raise error.Abort(_('cannot create new union repository'))
245 raise error.Abort(_('cannot create new union repository'))
245 parentpath = ui.config("bundle", "mainreporoot")
246 parentpath = ui.config("bundle", "mainreporoot")
246 if not parentpath:
247 if not parentpath:
247 # try to find the correct path to the working directory repo
248 # try to find the correct path to the working directory repo
248 parentpath = cmdutil.findrepo(encoding.getcwd())
249 parentpath = cmdutil.findrepo(encoding.getcwd())
249 if parentpath is None:
250 if parentpath is None:
250 parentpath = ''
251 parentpath = ''
251 if parentpath:
252 if parentpath:
252 # Try to make the full path relative so we get a nice, short URL.
253 # Try to make the full path relative so we get a nice, short URL.
253 # In particular, we don't want temp dir names in test outputs.
254 # In particular, we don't want temp dir names in test outputs.
254 cwd = encoding.getcwd()
255 cwd = encoding.getcwd()
255 if parentpath == cwd:
256 if parentpath == cwd:
256 parentpath = ''
257 parentpath = ''
257 else:
258 else:
258 cwd = pathutil.normasprefix(cwd)
259 cwd = pathutil.normasprefix(cwd)
259 if parentpath.startswith(cwd):
260 if parentpath.startswith(cwd):
260 parentpath = parentpath[len(cwd):]
261 parentpath = parentpath[len(cwd):]
261 if path.startswith('union:'):
262 if path.startswith('union:'):
262 s = path.split(":", 1)[1].split("+", 1)
263 s = path.split(":", 1)[1].split("+", 1)
263 if len(s) == 1:
264 if len(s) == 1:
264 repopath, repopath2 = parentpath, s[0]
265 repopath, repopath2 = parentpath, s[0]
265 else:
266 else:
266 repopath, repopath2 = s
267 repopath, repopath2 = s
267 else:
268 else:
268 repopath, repopath2 = parentpath, path
269 repopath, repopath2 = parentpath, path
269
270
270 return makeunionrepository(ui, repopath, repopath2)
271 return makeunionrepository(ui, repopath, repopath2)
271
272
272 def makeunionrepository(ui, repopath1, repopath2):
273 def makeunionrepository(ui, repopath1, repopath2):
273 """Make a union repository object from 2 local repo paths."""
274 """Make a union repository object from 2 local repo paths."""
274 repo1 = localrepo.instance(ui, repopath1, create=False)
275 repo1 = localrepo.instance(ui, repopath1, create=False)
275 repo2 = localrepo.instance(ui, repopath2, create=False)
276 repo2 = localrepo.instance(ui, repopath2, create=False)
276
277
277 url = 'union:%s+%s' % (util.expandpath(repopath1),
278 url = 'union:%s+%s' % (util.expandpath(repopath1),
278 util.expandpath(repopath2))
279 util.expandpath(repopath2))
279
280
280 class derivedunionrepository(unionrepository, repo1.__class__):
281 class derivedunionrepository(unionrepository, repo1.__class__):
281 pass
282 pass
282
283
283 repo = repo1
284 repo = repo1
284 repo.__class__ = derivedunionrepository
285 repo.__class__ = derivedunionrepository
285 unionrepository.__init__(repo1, repo2, url)
286 unionrepository.__init__(repo1, repo2, url)
286
287
287 return repo
288 return repo
General Comments 0
You need to be logged in to leave comments. Login now