##// END OF EJS Templates
unionrepo: fix wrong rev being checked in iscensored (issue5024)
Sean Farley -
r27723:bf86e3e8 stable
parent child Browse files
Show More
@@ -1,260 +1,261 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
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 e = (None, None, None, base,
73 73 link, self.rev(p1node), self.rev(p2node), node)
74 74 self.index.insert(-1, e)
75 75 self.nodemap[node] = n
76 76 self.bundlerevs.add(n)
77 77 n += 1
78 78
79 79 def _chunk(self, rev):
80 80 if rev <= self.repotiprev:
81 81 return revlog.revlog._chunk(self, rev)
82 82 return self.revlog2._chunk(self.node(rev))
83 83
84 84 def revdiff(self, rev1, rev2):
85 85 """return or calculate a delta between two revisions"""
86 86 if rev1 > self.repotiprev and rev2 > self.repotiprev:
87 87 return self.revlog2.revdiff(
88 88 self.revlog2.rev(self.node(rev1)),
89 89 self.revlog2.rev(self.node(rev2)))
90 90 elif rev1 <= self.repotiprev and rev2 <= self.repotiprev:
91 91 return self.baserevdiff(rev1, rev2)
92 92
93 93 return mdiff.textdiff(self.revision(self.node(rev1)),
94 94 self.revision(self.node(rev2)))
95 95
96 96 def revision(self, nodeorrev):
97 97 """return an uncompressed revision of a given node or revision
98 98 number.
99 99 """
100 100 if isinstance(nodeorrev, int):
101 101 rev = nodeorrev
102 102 node = self.node(rev)
103 103 else:
104 104 node = nodeorrev
105 105 rev = self.rev(node)
106 106
107 107 if node == nullid:
108 108 return ""
109 109
110 110 if rev > self.repotiprev:
111 111 text = self.revlog2.revision(node)
112 112 self._cache = (node, rev, text)
113 113 else:
114 114 text = self.baserevision(rev)
115 115 # already cached
116 116 return text
117 117
118 118 def baserevision(self, nodeorrev):
119 119 # Revlog subclasses may override 'revision' method to modify format of
120 120 # content retrieved from revlog. To use unionrevlog with such class one
121 121 # needs to override 'baserevision' and make more specific call here.
122 122 return revlog.revlog.revision(self, nodeorrev)
123 123
124 124 def baserevdiff(self, rev1, rev2):
125 125 # Exists for the same purpose as baserevision.
126 126 return revlog.revlog.revdiff(self, rev1, rev2)
127 127
128 128 def addrevision(self, text, transaction, link, p1=None, p2=None, d=None):
129 129 raise NotImplementedError
130 130 def addgroup(self, revs, linkmapper, transaction):
131 131 raise NotImplementedError
132 132 def strip(self, rev, minlink):
133 133 raise NotImplementedError
134 134 def checksize(self):
135 135 raise NotImplementedError
136 136
137 137 class unionchangelog(unionrevlog, changelog.changelog):
138 138 def __init__(self, opener, opener2):
139 139 changelog.changelog.__init__(self, opener)
140 140 linkmapper = None
141 141 changelog2 = changelog.changelog(opener2)
142 142 unionrevlog.__init__(self, opener, self.indexfile, changelog2,
143 143 linkmapper)
144 144
145 145 def baserevision(self, nodeorrev):
146 146 # Although changelog doesn't override 'revision' method, some extensions
147 147 # may replace this class with another that does. Same story with
148 148 # manifest and filelog classes.
149 149 return changelog.changelog.revision(self, nodeorrev)
150 150
151 151 def baserevdiff(self, rev1, rev2):
152 152 return changelog.changelog.revdiff(self, rev1, rev2)
153 153
154 154 class unionmanifest(unionrevlog, manifest.manifest):
155 155 def __init__(self, opener, opener2, linkmapper):
156 156 manifest.manifest.__init__(self, opener)
157 157 manifest2 = manifest.manifest(opener2)
158 158 unionrevlog.__init__(self, opener, self.indexfile, manifest2,
159 159 linkmapper)
160 160
161 161 def baserevision(self, nodeorrev):
162 162 return manifest.manifest.revision(self, nodeorrev)
163 163
164 164 def baserevdiff(self, rev1, rev2):
165 165 return manifest.manifest.revdiff(self, rev1, rev2)
166 166
167 167 class unionfilelog(unionrevlog, filelog.filelog):
168 168 def __init__(self, opener, path, opener2, linkmapper, repo):
169 169 filelog.filelog.__init__(self, opener, path)
170 170 filelog2 = filelog.filelog(opener2, path)
171 171 unionrevlog.__init__(self, opener, self.indexfile, filelog2,
172 172 linkmapper)
173 173 self._repo = repo
174 174
175 175 def baserevision(self, nodeorrev):
176 176 return filelog.filelog.revision(self, nodeorrev)
177 177
178 178 def baserevdiff(self, rev1, rev2):
179 179 return filelog.filelog.revdiff(self, rev1, rev2)
180 180
181 181 def iscensored(self, rev):
182 182 """Check if a revision is censored."""
183 183 if rev <= self.repotiprev:
184 184 return filelog.filelog.iscensored(self, rev)
185 return self.revlog2.iscensored(rev)
185 node = self.node(rev)
186 return self.revlog2.iscensored(self.revlog2.rev(node))
186 187
187 188 class unionpeer(localrepo.localpeer):
188 189 def canpush(self):
189 190 return False
190 191
191 192 class unionrepository(localrepo.localrepository):
192 193 def __init__(self, ui, path, path2):
193 194 localrepo.localrepository.__init__(self, ui, path)
194 195 self.ui.setconfig('phases', 'publish', False, 'unionrepo')
195 196
196 197 self._url = 'union:%s+%s' % (util.expandpath(path),
197 198 util.expandpath(path2))
198 199 self.repo2 = localrepo.localrepository(ui, path2)
199 200
200 201 @localrepo.unfilteredpropertycache
201 202 def changelog(self):
202 203 return unionchangelog(self.svfs, self.repo2.svfs)
203 204
204 205 def _clrev(self, rev2):
205 206 """map from repo2 changelog rev to temporary rev in self.changelog"""
206 207 node = self.repo2.changelog.node(rev2)
207 208 return self.changelog.rev(node)
208 209
209 210 @localrepo.unfilteredpropertycache
210 211 def manifest(self):
211 212 return unionmanifest(self.svfs, self.repo2.svfs,
212 213 self._clrev)
213 214
214 215 def url(self):
215 216 return self._url
216 217
217 218 def file(self, f):
218 219 return unionfilelog(self.svfs, f, self.repo2.svfs,
219 220 self._clrev, self)
220 221
221 222 def close(self):
222 223 self.repo2.close()
223 224
224 225 def cancopy(self):
225 226 return False
226 227
227 228 def peer(self):
228 229 return unionpeer(self)
229 230
230 231 def getcwd(self):
231 232 return os.getcwd() # always outside the repo
232 233
233 234 def instance(ui, path, create):
234 235 if create:
235 236 raise error.Abort(_('cannot create new union repository'))
236 237 parentpath = ui.config("bundle", "mainreporoot", "")
237 238 if not parentpath:
238 239 # try to find the correct path to the working directory repo
239 240 parentpath = cmdutil.findrepo(os.getcwd())
240 241 if parentpath is None:
241 242 parentpath = ''
242 243 if parentpath:
243 244 # Try to make the full path relative so we get a nice, short URL.
244 245 # In particular, we don't want temp dir names in test outputs.
245 246 cwd = os.getcwd()
246 247 if parentpath == cwd:
247 248 parentpath = ''
248 249 else:
249 250 cwd = pathutil.normasprefix(cwd)
250 251 if parentpath.startswith(cwd):
251 252 parentpath = parentpath[len(cwd):]
252 253 if path.startswith('union:'):
253 254 s = path.split(":", 1)[1].split("+", 1)
254 255 if len(s) == 1:
255 256 repopath, repopath2 = parentpath, s[0]
256 257 else:
257 258 repopath, repopath2 = s
258 259 else:
259 260 repopath, repopath2 = parentpath, path
260 261 return unionrepository(ui, repopath, repopath2)
@@ -1,150 +1,169 b''
1 1 Test unionrepo functionality
2 2
3 3 Create one repository
4 4
5 5 $ hg init repo1
6 6 $ cd repo1
7 7 $ touch repo1-0
8 8 $ echo repo1-0 > f
9 9 $ hg ci -Aqmrepo1-0
10 10 $ touch repo1-1
11 11 $ echo repo1-1 >> f
12 12 $ hg ci -Aqmrepo1-1
13 13 $ touch repo1-2
14 14 $ echo repo1-2 >> f
15 15 $ hg ci -Aqmrepo1-2
16 16 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
17 17 2:68c0685446a3 repo1-2
18 18 1:8a58db72e69d repo1-1
19 19 0:f093fec0529b repo1-0
20 20 $ tip1=`hg id -q`
21 21 $ cd ..
22 22
23 23 - and a clone with a not-completely-trivial history
24 24
25 25 $ hg clone -q repo1 --rev 0 repo2
26 26 $ cd repo2
27 27 $ touch repo2-1
28 28 $ sed '1i\
29 29 > repo2-1 at top
30 30 > ' f > f.tmp
31 31 $ mv f.tmp f
32 32 $ hg ci -Aqmrepo2-1
33 33 $ touch repo2-2
34 34 $ hg pull -q ../repo1 -r 1
35 35 $ hg merge -q
36 36 $ hg ci -Aqmrepo2-2-merge
37 37 $ touch repo2-3
38 38 $ echo repo2-3 >> f
39 39 $ hg ci -mrepo2-3
40 40 $ hg log --template '{rev}:{node|short} {desc|firstline}\n'
41 41 4:2f0d178c469c repo2-3
42 42 3:9e6fb3e0b9da repo2-2-merge
43 43 2:8a58db72e69d repo1-1
44 44 1:c337dba826e7 repo2-1
45 45 0:f093fec0529b repo1-0
46 46 $ cd ..
47 47
48 48 revisions from repo2 appear as appended / pulled to repo1
49 49
50 50 $ hg -R union:repo1+repo2 log --template '{rev}:{node|short} {desc|firstline}\n'
51 51 5:2f0d178c469c repo2-3
52 52 4:9e6fb3e0b9da repo2-2-merge
53 53 3:c337dba826e7 repo2-1
54 54 2:68c0685446a3 repo1-2
55 55 1:8a58db72e69d repo1-1
56 56 0:f093fec0529b repo1-0
57 57
58 58 manifest can be retrieved for revisions in both repos
59 59
60 60 $ hg -R union:repo1+repo2 mani -r $tip1
61 61 f
62 62 repo1-0
63 63 repo1-1
64 64 repo1-2
65 65 $ hg -R union:repo1+repo2 mani -r 4
66 66 f
67 67 repo1-0
68 68 repo1-1
69 69 repo2-1
70 70 repo2-2
71 71
72 72 files can be retrieved form both repos
73 73
74 74 $ hg -R repo1 cat repo1/f -r2
75 75 repo1-0
76 76 repo1-1
77 77 repo1-2
78 78
79 79 $ hg -R union:repo1+repo2 cat -r$tip1 repo1/f
80 80 repo1-0
81 81 repo1-1
82 82 repo1-2
83 83
84 84 $ hg -R union:repo1+repo2 cat -r4 $TESTTMP/repo1/f
85 85 repo2-1 at top
86 86 repo1-0
87 87 repo1-1
88 88
89 89 files can be compared across repos
90 90
91 91 $ hg -R union:repo1+repo2 diff -r$tip1 -rtip
92 92 diff -r 68c0685446a3 -r 2f0d178c469c f
93 93 --- a/f Thu Jan 01 00:00:00 1970 +0000
94 94 +++ b/f Thu Jan 01 00:00:00 1970 +0000
95 95 @@ -1,3 +1,4 @@
96 96 +repo2-1 at top
97 97 repo1-0
98 98 repo1-1
99 99 -repo1-2
100 100 +repo2-3
101 101
102 102 heads from both repos are found correctly
103 103
104 104 $ hg -R union:repo1+repo2 heads --template '{rev}:{node|short} {desc|firstline}\n'
105 105 5:2f0d178c469c repo2-3
106 106 2:68c0685446a3 repo1-2
107 107
108 108 revsets works across repos
109 109
110 110 $ hg -R union:repo1+repo2 id -r "ancestor($tip1, 5)"
111 111 8a58db72e69d
112 112
113 113 annotate works - an indication that linkrevs works
114 114
115 115 $ hg --cwd repo1 -Runion:../repo2 annotate $TESTTMP/repo1/f -r tip
116 116 3: repo2-1 at top
117 117 0: repo1-0
118 118 1: repo1-1
119 119 5: repo2-3
120 120
121 121 union repos can be cloned ... and clones works correctly
122 122
123 123 $ hg clone -U union:repo1+repo2 repo3
124 124 requesting all changes
125 125 adding changesets
126 126 adding manifests
127 127 adding file changes
128 128 added 6 changesets with 11 changes to 6 files (+1 heads)
129 129
130 130 $ hg -R repo3 paths
131 131 default = union:repo1+repo2
132 132
133 133 $ hg -R repo3 verify
134 134 checking changesets
135 135 checking manifests
136 136 crosschecking files in changesets and manifests
137 137 checking files
138 138 6 files, 6 changesets, 11 total revisions
139 139
140 140 $ hg -R repo3 heads --template '{rev}:{node|short} {desc|firstline}\n'
141 141 5:2f0d178c469c repo2-3
142 142 2:68c0685446a3 repo1-2
143 143
144 144 $ hg -R repo3 log --template '{rev}:{node|short} {desc|firstline}\n'
145 145 5:2f0d178c469c repo2-3
146 146 4:9e6fb3e0b9da repo2-2-merge
147 147 3:c337dba826e7 repo2-1
148 148 2:68c0685446a3 repo1-2
149 149 1:8a58db72e69d repo1-1
150 150 0:f093fec0529b repo1-0
151
152 union repos should use the correct rev number (issue5024)
153
154 $ hg init a
155 $ cd a
156 $ echo a0 >> f
157 $ hg ci -Aqm a0
158 $ cd ..
159 $ hg init b
160 $ cd b
161 $ echo b0 >> f
162 $ hg ci -Aqm b0
163 $ echo b1 >> f
164 $ hg ci -qm b1
165 $ cd ..
166
167 "hg files -v" to call fctx.size() -> fctx.iscensored()
168 $ hg files -R union:b+a -r2 -v
169 3 b/f (glob)
General Comments 0
You need to be logged in to leave comments. Login now