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