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