##// END OF EJS Templates
Make filectx.__init__ use LookupError
Brendan Cully -
r3227:618a7f2c default
parent child Browse files
Show More
@@ -1,292 +1,295
1 # context.py - changeset and file context objects for mercurial
1 # context.py - changeset and file context objects for mercurial
2 #
2 #
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from node import *
8 from node import *
9 from i18n import gettext as _
9 from i18n import gettext as _
10 from demandload import demandload
10 from demandload import demandload
11 demandload(globals(), "ancestor bdiff repo util")
11 demandload(globals(), "ancestor bdiff repo revlog util")
12
12
13 class changectx(object):
13 class changectx(object):
14 """A changecontext object makes access to data related to a particular
14 """A changecontext object makes access to data related to a particular
15 changeset convenient."""
15 changeset convenient."""
16 def __init__(self, repo, changeid=None):
16 def __init__(self, repo, changeid=None):
17 """changeid is a revision number, node, or tag"""
17 """changeid is a revision number, node, or tag"""
18 self._repo = repo
18 self._repo = repo
19
19
20 if not changeid and changeid != 0:
20 if not changeid and changeid != 0:
21 p1, p2 = self._repo.dirstate.parents()
21 p1, p2 = self._repo.dirstate.parents()
22 self._rev = self._repo.changelog.rev(p1)
22 self._rev = self._repo.changelog.rev(p1)
23 if self._rev == -1:
23 if self._rev == -1:
24 changeid = 'tip'
24 changeid = 'tip'
25 else:
25 else:
26 self._node = p1
26 self._node = p1
27 return
27 return
28
28
29 self._node = self._repo.lookup(changeid)
29 self._node = self._repo.lookup(changeid)
30 self._rev = self._repo.changelog.rev(self._node)
30 self._rev = self._repo.changelog.rev(self._node)
31
31
32 def __str__(self):
32 def __str__(self):
33 return short(self.node())
33 return short(self.node())
34
34
35 def __repr__(self):
35 def __repr__(self):
36 return "<changectx %s>" % short(self.node())
36 return "<changectx %s>" % short(self.node())
37
37
38 def __eq__(self, other):
38 def __eq__(self, other):
39 return self._rev == other._rev
39 return self._rev == other._rev
40
40
41 def __nonzero__(self):
41 def __nonzero__(self):
42 return self._rev != -1
42 return self._rev != -1
43
43
44 def changeset(self):
44 def changeset(self):
45 try:
45 try:
46 return self._changeset
46 return self._changeset
47 except AttributeError:
47 except AttributeError:
48 self._changeset = self._repo.changelog.read(self.node())
48 self._changeset = self._repo.changelog.read(self.node())
49 return self._changeset
49 return self._changeset
50
50
51 def manifest(self):
51 def manifest(self):
52 try:
52 try:
53 return self._manifest
53 return self._manifest
54 except AttributeError:
54 except AttributeError:
55 self._manifest = self._repo.manifest.read(self.changeset()[0])
55 self._manifest = self._repo.manifest.read(self.changeset()[0])
56 return self._manifest
56 return self._manifest
57
57
58 def rev(self): return self._rev
58 def rev(self): return self._rev
59 def node(self): return self._node
59 def node(self): return self._node
60 def user(self): return self.changeset()[1]
60 def user(self): return self.changeset()[1]
61 def date(self): return self.changeset()[2]
61 def date(self): return self.changeset()[2]
62 def files(self): return self.changeset()[3]
62 def files(self): return self.changeset()[3]
63 def description(self): return self.changeset()[4]
63 def description(self): return self.changeset()[4]
64
64
65 def parents(self):
65 def parents(self):
66 """return contexts for each parent changeset"""
66 """return contexts for each parent changeset"""
67 p = self._repo.changelog.parents(self._node)
67 p = self._repo.changelog.parents(self._node)
68 return [ changectx(self._repo, x) for x in p ]
68 return [ changectx(self._repo, x) for x in p ]
69
69
70 def children(self):
70 def children(self):
71 """return contexts for each child changeset"""
71 """return contexts for each child changeset"""
72 c = self._repo.changelog.children(self._node)
72 c = self._repo.changelog.children(self._node)
73 return [ changectx(self._repo, x) for x in c ]
73 return [ changectx(self._repo, x) for x in c ]
74
74
75 def filenode(self, path):
75 def filenode(self, path):
76 node, flag = self._repo.manifest.find(self.changeset()[0], path)
76 node, flag = self._repo.manifest.find(self.changeset()[0], path)
77 return node
77 return node
78
78
79 def filectx(self, path, fileid=None):
79 def filectx(self, path, fileid=None):
80 """get a file context from this changeset"""
80 """get a file context from this changeset"""
81 if fileid is None:
81 if fileid is None:
82 fileid = self.filenode(path)
82 fileid = self.filenode(path)
83 if not fileid:
83 if not fileid:
84 raise repo.LookupError(_("'%s' does not exist in changeset %s") %
84 raise repo.LookupError(_("'%s' does not exist in changeset %s") %
85 (path, hex(self.node())))
85 (path, hex(self.node())))
86 return filectx(self._repo, path, fileid=fileid)
86 return filectx(self._repo, path, fileid=fileid)
87
87
88 def filectxs(self):
88 def filectxs(self):
89 """generate a file context for each file in this changeset's
89 """generate a file context for each file in this changeset's
90 manifest"""
90 manifest"""
91 mf = self.manifest()
91 mf = self.manifest()
92 m = mf.keys()
92 m = mf.keys()
93 m.sort()
93 m.sort()
94 for f in m:
94 for f in m:
95 yield self.filectx(f, fileid=mf[f])
95 yield self.filectx(f, fileid=mf[f])
96
96
97 def ancestor(self, c2):
97 def ancestor(self, c2):
98 """
98 """
99 return the ancestor context of self and c2
99 return the ancestor context of self and c2
100 """
100 """
101 n = self._repo.changelog.ancestor(self._node, c2._node)
101 n = self._repo.changelog.ancestor(self._node, c2._node)
102 return changectx(self._repo, n)
102 return changectx(self._repo, n)
103
103
104 class filectx(object):
104 class filectx(object):
105 """A filecontext object makes access to data related to a particular
105 """A filecontext object makes access to data related to a particular
106 filerevision convenient."""
106 filerevision convenient."""
107 def __init__(self, repo, path, changeid=None, fileid=None, filelog=None):
107 def __init__(self, repo_, path, changeid=None, fileid=None, filelog=None):
108 """changeid can be a changeset revision, node, or tag.
108 """changeid can be a changeset revision, node, or tag.
109 fileid can be a file revision or node."""
109 fileid can be a file revision or node."""
110 self._repo = repo
110 self._repo = repo_
111 self._path = path
111 self._path = path
112
112
113 assert changeid is not None or fileid is not None
113 assert changeid is not None or fileid is not None
114
114
115 if filelog:
115 if filelog:
116 self._filelog = filelog
116 self._filelog = filelog
117 else:
117 else:
118 self._filelog = self._repo.file(self._path)
118 self._filelog = self._repo.file(self._path)
119
119
120 if fileid is None:
120 if fileid is None:
121 self._changeid = changeid
121 self._changeid = changeid
122 else:
122 else:
123 self._filenode = self._filelog.lookup(fileid)
123 try:
124 self._filenode = self._filelog.lookup(fileid)
125 except revlog.RevlogError, inst:
126 raise repo.LookupError(str(inst))
124 self._changeid = self._filelog.linkrev(self._filenode)
127 self._changeid = self._filelog.linkrev(self._filenode)
125
128
126 def __getattr__(self, name):
129 def __getattr__(self, name):
127 if name == '_changectx':
130 if name == '_changectx':
128 self._changectx = changectx(self._repo, self._changeid)
131 self._changectx = changectx(self._repo, self._changeid)
129 return self._changectx
132 return self._changectx
130 elif name == '_filenode':
133 elif name == '_filenode':
131 self._filenode = self._changectx.filenode(self._path)
134 self._filenode = self._changectx.filenode(self._path)
132 return self._filenode
135 return self._filenode
133 elif name == '_filerev':
136 elif name == '_filerev':
134 self._filerev = self._filelog.rev(self._filenode)
137 self._filerev = self._filelog.rev(self._filenode)
135 return self._filerev
138 return self._filerev
136 else:
139 else:
137 raise AttributeError, name
140 raise AttributeError, name
138
141
139 def __nonzero__(self):
142 def __nonzero__(self):
140 return self._filerev != nullid
143 return self._filerev != nullid
141
144
142 def __str__(self):
145 def __str__(self):
143 return "%s@%s" % (self.path(), short(self.node()))
146 return "%s@%s" % (self.path(), short(self.node()))
144
147
145 def __repr__(self):
148 def __repr__(self):
146 return "<filectx %s@%s>" % (self.path(), short(self.node()))
149 return "<filectx %s@%s>" % (self.path(), short(self.node()))
147
150
148 def __eq__(self, other):
151 def __eq__(self, other):
149 return self._path == other._path and self._changeid == other._changeid
152 return self._path == other._path and self._changeid == other._changeid
150
153
151 def filectx(self, fileid):
154 def filectx(self, fileid):
152 '''opens an arbitrary revision of the file without
155 '''opens an arbitrary revision of the file without
153 opening a new filelog'''
156 opening a new filelog'''
154 return filectx(self._repo, self._path, fileid=fileid,
157 return filectx(self._repo, self._path, fileid=fileid,
155 filelog=self._filelog)
158 filelog=self._filelog)
156
159
157 def filerev(self): return self._filerev
160 def filerev(self): return self._filerev
158 def filenode(self): return self._filenode
161 def filenode(self): return self._filenode
159 def filelog(self): return self._filelog
162 def filelog(self): return self._filelog
160
163
161 def rev(self):
164 def rev(self):
162 if hasattr(self, "_changectx"):
165 if hasattr(self, "_changectx"):
163 return self._changectx.rev()
166 return self._changectx.rev()
164 return self._filelog.linkrev(self._filenode)
167 return self._filelog.linkrev(self._filenode)
165
168
166 def node(self): return self._changectx.node()
169 def node(self): return self._changectx.node()
167 def user(self): return self._changectx.user()
170 def user(self): return self._changectx.user()
168 def date(self): return self._changectx.date()
171 def date(self): return self._changectx.date()
169 def files(self): return self._changectx.files()
172 def files(self): return self._changectx.files()
170 def description(self): return self._changectx.description()
173 def description(self): return self._changectx.description()
171 def manifest(self): return self._changectx.manifest()
174 def manifest(self): return self._changectx.manifest()
172 def changectx(self): return self._changectx
175 def changectx(self): return self._changectx
173
176
174 def data(self): return self._filelog.read(self._filenode)
177 def data(self): return self._filelog.read(self._filenode)
175 def renamed(self): return self._filelog.renamed(self._filenode)
178 def renamed(self): return self._filelog.renamed(self._filenode)
176 def path(self): return self._path
179 def path(self): return self._path
177
180
178 def parents(self):
181 def parents(self):
179 p = self._path
182 p = self._path
180 fl = self._filelog
183 fl = self._filelog
181 pl = [ (p, n, fl) for n in self._filelog.parents(self._filenode) ]
184 pl = [ (p, n, fl) for n in self._filelog.parents(self._filenode) ]
182
185
183 r = self.renamed()
186 r = self.renamed()
184 if r:
187 if r:
185 pl[0] = (r[0], r[1], None)
188 pl[0] = (r[0], r[1], None)
186
189
187 return [ filectx(self._repo, p, fileid=n, filelog=l)
190 return [ filectx(self._repo, p, fileid=n, filelog=l)
188 for p,n,l in pl if n != nullid ]
191 for p,n,l in pl if n != nullid ]
189
192
190 def children(self):
193 def children(self):
191 # hard for renames
194 # hard for renames
192 c = self._filelog.children(self._filenode)
195 c = self._filelog.children(self._filenode)
193 return [ filectx(self._repo, self._path, fileid=x,
196 return [ filectx(self._repo, self._path, fileid=x,
194 filelog=self._filelog) for x in c ]
197 filelog=self._filelog) for x in c ]
195
198
196 def annotate(self, follow=False):
199 def annotate(self, follow=False):
197 '''returns a list of tuples of (ctx, line) for each line
200 '''returns a list of tuples of (ctx, line) for each line
198 in the file, where ctx is the filectx of the node where
201 in the file, where ctx is the filectx of the node where
199 that line was last changed'''
202 that line was last changed'''
200
203
201 def decorate(text, rev):
204 def decorate(text, rev):
202 return ([rev] * len(text.splitlines()), text)
205 return ([rev] * len(text.splitlines()), text)
203
206
204 def pair(parent, child):
207 def pair(parent, child):
205 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
208 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
206 child[0][b1:b2] = parent[0][a1:a2]
209 child[0][b1:b2] = parent[0][a1:a2]
207 return child
210 return child
208
211
209 getlog = util.cachefunc(lambda x: self._repo.file(x))
212 getlog = util.cachefunc(lambda x: self._repo.file(x))
210 def getctx(path, fileid):
213 def getctx(path, fileid):
211 log = path == self._path and self._filelog or getlog(path)
214 log = path == self._path and self._filelog or getlog(path)
212 return filectx(self._repo, path, fileid=fileid, filelog=log)
215 return filectx(self._repo, path, fileid=fileid, filelog=log)
213 getctx = util.cachefunc(getctx)
216 getctx = util.cachefunc(getctx)
214
217
215 def parents(f):
218 def parents(f):
216 # we want to reuse filectx objects as much as possible
219 # we want to reuse filectx objects as much as possible
217 p = f._path
220 p = f._path
218 pl = [ (p, f._filelog.rev(n)) for n in f._filelog.parents(f._filenode) ]
221 pl = [ (p, f._filelog.rev(n)) for n in f._filelog.parents(f._filenode) ]
219
222
220 if follow:
223 if follow:
221 r = f.renamed()
224 r = f.renamed()
222 if r:
225 if r:
223 pl[0] = (r[0], getlog(r[0]).rev(r[1]))
226 pl[0] = (r[0], getlog(r[0]).rev(r[1]))
224
227
225 return [ getctx(p, n) for p, n in pl if n != -1 ]
228 return [ getctx(p, n) for p, n in pl if n != -1 ]
226
229
227 # find all ancestors
230 # find all ancestors
228 needed = {self: 1}
231 needed = {self: 1}
229 visit = [self]
232 visit = [self]
230 files = [self._path]
233 files = [self._path]
231 while visit:
234 while visit:
232 f = visit.pop(0)
235 f = visit.pop(0)
233 for p in parents(f):
236 for p in parents(f):
234 if p not in needed:
237 if p not in needed:
235 needed[p] = 1
238 needed[p] = 1
236 visit.append(p)
239 visit.append(p)
237 if p._path not in files:
240 if p._path not in files:
238 files.append(p._path)
241 files.append(p._path)
239 else:
242 else:
240 # count how many times we'll use this
243 # count how many times we'll use this
241 needed[p] += 1
244 needed[p] += 1
242
245
243 # sort by revision (per file) which is a topological order
246 # sort by revision (per file) which is a topological order
244 visit = []
247 visit = []
245 files.reverse()
248 files.reverse()
246 for f in files:
249 for f in files:
247 fn = [(n._filerev, n) for n in needed.keys() if n._path == f]
250 fn = [(n._filerev, n) for n in needed.keys() if n._path == f]
248 fn.sort()
251 fn.sort()
249 visit.extend(fn)
252 visit.extend(fn)
250 hist = {}
253 hist = {}
251
254
252 for r, f in visit:
255 for r, f in visit:
253 curr = decorate(f.data(), f)
256 curr = decorate(f.data(), f)
254 for p in parents(f):
257 for p in parents(f):
255 if p != nullid:
258 if p != nullid:
256 curr = pair(hist[p], curr)
259 curr = pair(hist[p], curr)
257 # trim the history of unneeded revs
260 # trim the history of unneeded revs
258 needed[p] -= 1
261 needed[p] -= 1
259 if not needed[p]:
262 if not needed[p]:
260 del hist[p]
263 del hist[p]
261 hist[f] = curr
264 hist[f] = curr
262
265
263 return zip(hist[f][0], hist[f][1].splitlines(1))
266 return zip(hist[f][0], hist[f][1].splitlines(1))
264
267
265 def ancestor(self, fc2):
268 def ancestor(self, fc2):
266 """
269 """
267 find the common ancestor file context, if any, of self, and fc2
270 find the common ancestor file context, if any, of self, and fc2
268 """
271 """
269
272
270 acache = {}
273 acache = {}
271 flcache = {self._path:self._filelog, fc2._path:fc2._filelog}
274 flcache = {self._path:self._filelog, fc2._path:fc2._filelog}
272 def parents(vertex):
275 def parents(vertex):
273 if vertex in acache:
276 if vertex in acache:
274 return acache[vertex]
277 return acache[vertex]
275 f, n = vertex
278 f, n = vertex
276 if f not in flcache:
279 if f not in flcache:
277 flcache[f] = self._repo.file(f)
280 flcache[f] = self._repo.file(f)
278 fl = flcache[f]
281 fl = flcache[f]
279 pl = [ (f,p) for p in fl.parents(n) if p != nullid ]
282 pl = [ (f,p) for p in fl.parents(n) if p != nullid ]
280 re = fl.renamed(n)
283 re = fl.renamed(n)
281 if re:
284 if re:
282 pl.append(re)
285 pl.append(re)
283 acache[vertex]=pl
286 acache[vertex]=pl
284 return pl
287 return pl
285
288
286 a, b = (self._path, self._filenode), (fc2._path, fc2._filenode)
289 a, b = (self._path, self._filenode), (fc2._path, fc2._filenode)
287 v = ancestor.ancestor(a, b, parents)
290 v = ancestor.ancestor(a, b, parents)
288 if v:
291 if v:
289 f,n = v
292 f,n = v
290 return filectx(self._repo, f, fileid=n, filelog=flcache[f])
293 return filectx(self._repo, f, fileid=n, filelog=flcache[f])
291
294
292 return None
295 return None
General Comments 0
You need to be logged in to leave comments. Login now