##// END OF EJS Templates
unionrepo: properly handle hidden linkrev in revlog (issue5070)...
Pierre-Yves David -
r28222:b966e35a stable
parent child Browse files
Show More
@@ -1,262 +1,262 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 import os
17 17
18 18 from .i18n import _
19 19 from .node import nullid
20 20
21 21 from . import (
22 22 changelog,
23 23 cmdutil,
24 24 error,
25 25 filelog,
26 26 localrepo,
27 27 manifest,
28 28 mdiff,
29 29 pathutil,
30 30 revlog,
31 31 scmutil,
32 32 util,
33 33 )
34 34
35 35 class unionrevlog(revlog.revlog):
36 36 def __init__(self, opener, indexfile, revlog2, linkmapper):
37 37 # How it works:
38 38 # To retrieve a revision, we just need to know the node id so we can
39 39 # look it up in revlog2.
40 40 #
41 41 # To differentiate a rev in the second revlog from a rev in the revlog,
42 42 # we check revision against repotiprev.
43 43 opener = scmutil.readonlyvfs(opener)
44 44 revlog.revlog.__init__(self, opener, indexfile)
45 45 self.revlog2 = revlog2
46 46
47 47 n = len(self)
48 48 self.repotiprev = n - 1
49 49 self.bundlerevs = set() # used by 'bundle()' revset expression
50 50 for rev2 in self.revlog2:
51 51 rev = self.revlog2.index[rev2]
52 52 # rev numbers - in revlog2, very different from self.rev
53 53 _start, _csize, _rsize, base, linkrev, p1rev, p2rev, node = rev
54 54 flags = _start & 0xFFFF
55 55
56 56 if linkmapper is None: # link is to same revlog
57 57 assert linkrev == rev2 # we never link back
58 58 link = n
59 59 else: # rev must be mapped from repo2 cl to unified cl by linkmapper
60 60 link = linkmapper(linkrev)
61 61
62 62 if linkmapper is not None: # link is to same revlog
63 63 base = linkmapper(base)
64 64
65 65 if node in self.nodemap:
66 66 # this happens for the common revlog revisions
67 67 self.bundlerevs.add(self.nodemap[node])
68 68 continue
69 69
70 70 p1node = self.revlog2.node(p1rev)
71 71 p2node = self.revlog2.node(p2rev)
72 72
73 73 e = (flags, None, None, base,
74 74 link, self.rev(p1node), self.rev(p2node), node)
75 75 self.index.insert(-1, e)
76 76 self.nodemap[node] = n
77 77 self.bundlerevs.add(n)
78 78 n += 1
79 79
80 80 def _chunk(self, rev):
81 81 if rev <= self.repotiprev:
82 82 return revlog.revlog._chunk(self, rev)
83 83 return self.revlog2._chunk(self.node(rev))
84 84
85 85 def revdiff(self, rev1, rev2):
86 86 """return or calculate a delta between two revisions"""
87 87 if rev1 > self.repotiprev and rev2 > self.repotiprev:
88 88 return self.revlog2.revdiff(
89 89 self.revlog2.rev(self.node(rev1)),
90 90 self.revlog2.rev(self.node(rev2)))
91 91 elif rev1 <= self.repotiprev and rev2 <= self.repotiprev:
92 92 return self.baserevdiff(rev1, rev2)
93 93
94 94 return mdiff.textdiff(self.revision(self.node(rev1)),
95 95 self.revision(self.node(rev2)))
96 96
97 97 def revision(self, nodeorrev):
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._cache = (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 131 def addgroup(self, revs, linkmapper, transaction):
132 132 raise NotImplementedError
133 133 def strip(self, rev, minlink):
134 134 raise NotImplementedError
135 135 def checksize(self):
136 136 raise NotImplementedError
137 137
138 138 class unionchangelog(unionrevlog, changelog.changelog):
139 139 def __init__(self, opener, opener2):
140 140 changelog.changelog.__init__(self, opener)
141 141 linkmapper = None
142 142 changelog2 = changelog.changelog(opener2)
143 143 unionrevlog.__init__(self, opener, self.indexfile, changelog2,
144 144 linkmapper)
145 145
146 146 def baserevision(self, nodeorrev):
147 147 # Although changelog doesn't override 'revision' method, some extensions
148 148 # may replace this class with another that does. Same story with
149 149 # manifest and filelog classes.
150 150 return changelog.changelog.revision(self, nodeorrev)
151 151
152 152 def baserevdiff(self, rev1, rev2):
153 153 return changelog.changelog.revdiff(self, rev1, rev2)
154 154
155 155 class unionmanifest(unionrevlog, manifest.manifest):
156 156 def __init__(self, opener, opener2, linkmapper):
157 157 manifest.manifest.__init__(self, opener)
158 158 manifest2 = manifest.manifest(opener2)
159 159 unionrevlog.__init__(self, opener, self.indexfile, manifest2,
160 160 linkmapper)
161 161
162 162 def baserevision(self, nodeorrev):
163 163 return manifest.manifest.revision(self, nodeorrev)
164 164
165 165 def baserevdiff(self, rev1, rev2):
166 166 return manifest.manifest.revdiff(self, rev1, rev2)
167 167
168 168 class unionfilelog(unionrevlog, filelog.filelog):
169 169 def __init__(self, opener, path, opener2, linkmapper, repo):
170 170 filelog.filelog.__init__(self, opener, path)
171 171 filelog2 = filelog.filelog(opener2, path)
172 172 unionrevlog.__init__(self, opener, self.indexfile, filelog2,
173 173 linkmapper)
174 174 self._repo = repo
175 175
176 176 def baserevision(self, nodeorrev):
177 177 return filelog.filelog.revision(self, nodeorrev)
178 178
179 179 def baserevdiff(self, rev1, rev2):
180 180 return filelog.filelog.revdiff(self, rev1, rev2)
181 181
182 182 def iscensored(self, rev):
183 183 """Check if a revision is censored."""
184 184 if rev <= self.repotiprev:
185 185 return filelog.filelog.iscensored(self, rev)
186 186 node = self.node(rev)
187 187 return self.revlog2.iscensored(self.revlog2.rev(node))
188 188
189 189 class unionpeer(localrepo.localpeer):
190 190 def canpush(self):
191 191 return False
192 192
193 193 class unionrepository(localrepo.localrepository):
194 194 def __init__(self, ui, path, path2):
195 195 localrepo.localrepository.__init__(self, ui, path)
196 196 self.ui.setconfig('phases', 'publish', False, 'unionrepo')
197 197
198 198 self._url = 'union:%s+%s' % (util.expandpath(path),
199 199 util.expandpath(path2))
200 200 self.repo2 = localrepo.localrepository(ui, path2)
201 201
202 202 @localrepo.unfilteredpropertycache
203 203 def changelog(self):
204 204 return unionchangelog(self.svfs, self.repo2.svfs)
205 205
206 206 def _clrev(self, rev2):
207 207 """map from repo2 changelog rev to temporary rev in self.changelog"""
208 208 node = self.repo2.changelog.node(rev2)
209 209 return self.changelog.rev(node)
210 210
211 211 @localrepo.unfilteredpropertycache
212 212 def manifest(self):
213 213 return unionmanifest(self.svfs, self.repo2.svfs,
214 self._clrev)
214 self.unfiltered()._clrev)
215 215
216 216 def url(self):
217 217 return self._url
218 218
219 219 def file(self, f):
220 220 return unionfilelog(self.svfs, f, self.repo2.svfs,
221 self._clrev, self)
221 self.unfiltered()._clrev, self)
222 222
223 223 def close(self):
224 224 self.repo2.close()
225 225
226 226 def cancopy(self):
227 227 return False
228 228
229 229 def peer(self):
230 230 return unionpeer(self)
231 231
232 232 def getcwd(self):
233 233 return os.getcwd() # always outside the repo
234 234
235 235 def instance(ui, path, create):
236 236 if create:
237 237 raise error.Abort(_('cannot create new union repository'))
238 238 parentpath = ui.config("bundle", "mainreporoot", "")
239 239 if not parentpath:
240 240 # try to find the correct path to the working directory repo
241 241 parentpath = cmdutil.findrepo(os.getcwd())
242 242 if parentpath is None:
243 243 parentpath = ''
244 244 if parentpath:
245 245 # Try to make the full path relative so we get a nice, short URL.
246 246 # In particular, we don't want temp dir names in test outputs.
247 247 cwd = os.getcwd()
248 248 if parentpath == cwd:
249 249 parentpath = ''
250 250 else:
251 251 cwd = pathutil.normasprefix(cwd)
252 252 if parentpath.startswith(cwd):
253 253 parentpath = parentpath[len(cwd):]
254 254 if path.startswith('union:'):
255 255 s = path.split(":", 1)[1].split("+", 1)
256 256 if len(s) == 1:
257 257 repopath, repopath2 = parentpath, s[0]
258 258 else:
259 259 repopath, repopath2 = s
260 260 else:
261 261 repopath, repopath2 = parentpath, path
262 262 return unionrepository(ui, repopath, repopath2)
General Comments 0
You need to be logged in to leave comments. Login now