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