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