##// END OF EJS Templates
Allow filectx.annotate to return the line number of first appearance.
FUJIWARA Katsunori -
r4856:e45c5120 default
parent child Browse files
Show More
@@ -1,525 +1,543
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, 2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006, 2007 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 _
9 from i18n import _
10 import ancestor, bdiff, repo, revlog, util, os, errno
10 import ancestor, bdiff, repo, revlog, util, os, errno
11
11
12 class changectx(object):
12 class changectx(object):
13 """A changecontext object makes access to data related to a particular
13 """A changecontext object makes access to data related to a particular
14 changeset convenient."""
14 changeset convenient."""
15 def __init__(self, repo, changeid=None):
15 def __init__(self, repo, changeid=None):
16 """changeid is a revision number, node, or tag"""
16 """changeid is a revision number, node, or tag"""
17 self._repo = repo
17 self._repo = repo
18
18
19 if not changeid and changeid != 0:
19 if not changeid and changeid != 0:
20 p1, p2 = self._repo.dirstate.parents()
20 p1, p2 = self._repo.dirstate.parents()
21 self._rev = self._repo.changelog.rev(p1)
21 self._rev = self._repo.changelog.rev(p1)
22 if self._rev == -1:
22 if self._rev == -1:
23 changeid = 'tip'
23 changeid = 'tip'
24 else:
24 else:
25 self._node = p1
25 self._node = p1
26 return
26 return
27
27
28 self._node = self._repo.lookup(changeid)
28 self._node = self._repo.lookup(changeid)
29 self._rev = self._repo.changelog.rev(self._node)
29 self._rev = self._repo.changelog.rev(self._node)
30
30
31 def __str__(self):
31 def __str__(self):
32 return short(self.node())
32 return short(self.node())
33
33
34 def __repr__(self):
34 def __repr__(self):
35 return "<changectx %s>" % str(self)
35 return "<changectx %s>" % str(self)
36
36
37 def __eq__(self, other):
37 def __eq__(self, other):
38 try:
38 try:
39 return self._rev == other._rev
39 return self._rev == other._rev
40 except AttributeError:
40 except AttributeError:
41 return False
41 return False
42
42
43 def __ne__(self, other):
43 def __ne__(self, other):
44 return not (self == other)
44 return not (self == other)
45
45
46 def __nonzero__(self):
46 def __nonzero__(self):
47 return self._rev != nullrev
47 return self._rev != nullrev
48
48
49 def __getattr__(self, name):
49 def __getattr__(self, name):
50 if name == '_changeset':
50 if name == '_changeset':
51 self._changeset = self._repo.changelog.read(self.node())
51 self._changeset = self._repo.changelog.read(self.node())
52 return self._changeset
52 return self._changeset
53 elif name == '_manifest':
53 elif name == '_manifest':
54 self._manifest = self._repo.manifest.read(self._changeset[0])
54 self._manifest = self._repo.manifest.read(self._changeset[0])
55 return self._manifest
55 return self._manifest
56 elif name == '_manifestdelta':
56 elif name == '_manifestdelta':
57 md = self._repo.manifest.readdelta(self._changeset[0])
57 md = self._repo.manifest.readdelta(self._changeset[0])
58 self._manifestdelta = md
58 self._manifestdelta = md
59 return self._manifestdelta
59 return self._manifestdelta
60 else:
60 else:
61 raise AttributeError, name
61 raise AttributeError, name
62
62
63 def changeset(self): return self._changeset
63 def changeset(self): return self._changeset
64 def manifest(self): return self._manifest
64 def manifest(self): return self._manifest
65
65
66 def rev(self): return self._rev
66 def rev(self): return self._rev
67 def node(self): return self._node
67 def node(self): return self._node
68 def user(self): return self._changeset[1]
68 def user(self): return self._changeset[1]
69 def date(self): return self._changeset[2]
69 def date(self): return self._changeset[2]
70 def files(self): return self._changeset[3]
70 def files(self): return self._changeset[3]
71 def description(self): return self._changeset[4]
71 def description(self): return self._changeset[4]
72 def branch(self): return self._changeset[5].get("branch")
72 def branch(self): return self._changeset[5].get("branch")
73 def tags(self): return self._repo.nodetags(self._node)
73 def tags(self): return self._repo.nodetags(self._node)
74
74
75 def parents(self):
75 def parents(self):
76 """return contexts for each parent changeset"""
76 """return contexts for each parent changeset"""
77 p = self._repo.changelog.parents(self._node)
77 p = self._repo.changelog.parents(self._node)
78 return [changectx(self._repo, x) for x in p]
78 return [changectx(self._repo, x) for x in p]
79
79
80 def children(self):
80 def children(self):
81 """return contexts for each child changeset"""
81 """return contexts for each child changeset"""
82 c = self._repo.changelog.children(self._node)
82 c = self._repo.changelog.children(self._node)
83 return [changectx(self._repo, x) for x in c]
83 return [changectx(self._repo, x) for x in c]
84
84
85 def filenode(self, path):
85 def filenode(self, path):
86 if '_manifest' in self.__dict__:
86 if '_manifest' in self.__dict__:
87 try:
87 try:
88 return self._manifest[path]
88 return self._manifest[path]
89 except KeyError:
89 except KeyError:
90 raise revlog.LookupError(_("'%s' not found in manifest") % path)
90 raise revlog.LookupError(_("'%s' not found in manifest") % path)
91 if '_manifestdelta' in self.__dict__ or path in self.files():
91 if '_manifestdelta' in self.__dict__ or path in self.files():
92 if path in self._manifestdelta:
92 if path in self._manifestdelta:
93 return self._manifestdelta[path]
93 return self._manifestdelta[path]
94 node, flag = self._repo.manifest.find(self._changeset[0], path)
94 node, flag = self._repo.manifest.find(self._changeset[0], path)
95 if not node:
95 if not node:
96 raise revlog.LookupError(_("'%s' not found in manifest") % path)
96 raise revlog.LookupError(_("'%s' not found in manifest") % path)
97
97
98 return node
98 return node
99
99
100 def filectx(self, path, fileid=None, filelog=None):
100 def filectx(self, path, fileid=None, filelog=None):
101 """get a file context from this changeset"""
101 """get a file context from this changeset"""
102 if fileid is None:
102 if fileid is None:
103 fileid = self.filenode(path)
103 fileid = self.filenode(path)
104 return filectx(self._repo, path, fileid=fileid,
104 return filectx(self._repo, path, fileid=fileid,
105 changectx=self, filelog=filelog)
105 changectx=self, filelog=filelog)
106
106
107 def filectxs(self):
107 def filectxs(self):
108 """generate a file context for each file in this changeset's
108 """generate a file context for each file in this changeset's
109 manifest"""
109 manifest"""
110 mf = self.manifest()
110 mf = self.manifest()
111 m = mf.keys()
111 m = mf.keys()
112 m.sort()
112 m.sort()
113 for f in m:
113 for f in m:
114 yield self.filectx(f, fileid=mf[f])
114 yield self.filectx(f, fileid=mf[f])
115
115
116 def ancestor(self, c2):
116 def ancestor(self, c2):
117 """
117 """
118 return the ancestor context of self and c2
118 return the ancestor context of self and c2
119 """
119 """
120 n = self._repo.changelog.ancestor(self._node, c2._node)
120 n = self._repo.changelog.ancestor(self._node, c2._node)
121 return changectx(self._repo, n)
121 return changectx(self._repo, n)
122
122
123 class filectx(object):
123 class filectx(object):
124 """A filecontext object makes access to data related to a particular
124 """A filecontext object makes access to data related to a particular
125 filerevision convenient."""
125 filerevision convenient."""
126 def __init__(self, repo, path, changeid=None, fileid=None,
126 def __init__(self, repo, path, changeid=None, fileid=None,
127 filelog=None, changectx=None):
127 filelog=None, changectx=None):
128 """changeid can be a changeset revision, node, or tag.
128 """changeid can be a changeset revision, node, or tag.
129 fileid can be a file revision or node."""
129 fileid can be a file revision or node."""
130 self._repo = repo
130 self._repo = repo
131 self._path = path
131 self._path = path
132
132
133 assert (changeid is not None
133 assert (changeid is not None
134 or fileid is not None
134 or fileid is not None
135 or changectx is not None)
135 or changectx is not None)
136
136
137 if filelog:
137 if filelog:
138 self._filelog = filelog
138 self._filelog = filelog
139
139
140 if fileid is None:
140 if fileid is None:
141 if changectx is None:
141 if changectx is None:
142 self._changeid = changeid
142 self._changeid = changeid
143 else:
143 else:
144 self._changectx = changectx
144 self._changectx = changectx
145 else:
145 else:
146 self._fileid = fileid
146 self._fileid = fileid
147
147
148 def __getattr__(self, name):
148 def __getattr__(self, name):
149 if name == '_changectx':
149 if name == '_changectx':
150 self._changectx = changectx(self._repo, self._changeid)
150 self._changectx = changectx(self._repo, self._changeid)
151 return self._changectx
151 return self._changectx
152 elif name == '_filelog':
152 elif name == '_filelog':
153 self._filelog = self._repo.file(self._path)
153 self._filelog = self._repo.file(self._path)
154 return self._filelog
154 return self._filelog
155 elif name == '_changeid':
155 elif name == '_changeid':
156 self._changeid = self._filelog.linkrev(self._filenode)
156 self._changeid = self._filelog.linkrev(self._filenode)
157 return self._changeid
157 return self._changeid
158 elif name == '_filenode':
158 elif name == '_filenode':
159 if '_fileid' in self.__dict__:
159 if '_fileid' in self.__dict__:
160 self._filenode = self._filelog.lookup(self._fileid)
160 self._filenode = self._filelog.lookup(self._fileid)
161 else:
161 else:
162 self._filenode = self._changectx.filenode(self._path)
162 self._filenode = self._changectx.filenode(self._path)
163 return self._filenode
163 return self._filenode
164 elif name == '_filerev':
164 elif name == '_filerev':
165 self._filerev = self._filelog.rev(self._filenode)
165 self._filerev = self._filelog.rev(self._filenode)
166 return self._filerev
166 return self._filerev
167 else:
167 else:
168 raise AttributeError, name
168 raise AttributeError, name
169
169
170 def __nonzero__(self):
170 def __nonzero__(self):
171 try:
171 try:
172 n = self._filenode
172 n = self._filenode
173 return True
173 return True
174 except revlog.LookupError:
174 except revlog.LookupError:
175 # file is missing
175 # file is missing
176 return False
176 return False
177
177
178 def __str__(self):
178 def __str__(self):
179 return "%s@%s" % (self.path(), short(self.node()))
179 return "%s@%s" % (self.path(), short(self.node()))
180
180
181 def __repr__(self):
181 def __repr__(self):
182 return "<filectx %s>" % str(self)
182 return "<filectx %s>" % str(self)
183
183
184 def __eq__(self, other):
184 def __eq__(self, other):
185 try:
185 try:
186 return (self._path == other._path
186 return (self._path == other._path
187 and self._changeid == other._changeid)
187 and self._changeid == other._changeid)
188 except AttributeError:
188 except AttributeError:
189 return False
189 return False
190
190
191 def __ne__(self, other):
191 def __ne__(self, other):
192 return not (self == other)
192 return not (self == other)
193
193
194 def filectx(self, fileid):
194 def filectx(self, fileid):
195 '''opens an arbitrary revision of the file without
195 '''opens an arbitrary revision of the file without
196 opening a new filelog'''
196 opening a new filelog'''
197 return filectx(self._repo, self._path, fileid=fileid,
197 return filectx(self._repo, self._path, fileid=fileid,
198 filelog=self._filelog)
198 filelog=self._filelog)
199
199
200 def filerev(self): return self._filerev
200 def filerev(self): return self._filerev
201 def filenode(self): return self._filenode
201 def filenode(self): return self._filenode
202 def filelog(self): return self._filelog
202 def filelog(self): return self._filelog
203
203
204 def rev(self):
204 def rev(self):
205 if '_changectx' in self.__dict__:
205 if '_changectx' in self.__dict__:
206 return self._changectx.rev()
206 return self._changectx.rev()
207 return self._filelog.linkrev(self._filenode)
207 return self._filelog.linkrev(self._filenode)
208
208
209 def node(self): return self._changectx.node()
209 def node(self): return self._changectx.node()
210 def user(self): return self._changectx.user()
210 def user(self): return self._changectx.user()
211 def date(self): return self._changectx.date()
211 def date(self): return self._changectx.date()
212 def files(self): return self._changectx.files()
212 def files(self): return self._changectx.files()
213 def description(self): return self._changectx.description()
213 def description(self): return self._changectx.description()
214 def branch(self): return self._changectx.branch()
214 def branch(self): return self._changectx.branch()
215 def manifest(self): return self._changectx.manifest()
215 def manifest(self): return self._changectx.manifest()
216 def changectx(self): return self._changectx
216 def changectx(self): return self._changectx
217
217
218 def data(self): return self._filelog.read(self._filenode)
218 def data(self): return self._filelog.read(self._filenode)
219 def renamed(self): return self._filelog.renamed(self._filenode)
219 def renamed(self): return self._filelog.renamed(self._filenode)
220 def path(self): return self._path
220 def path(self): return self._path
221 def size(self): return self._filelog.size(self._filerev)
221 def size(self): return self._filelog.size(self._filerev)
222
222
223 def cmp(self, text): return self._filelog.cmp(self._filenode, text)
223 def cmp(self, text): return self._filelog.cmp(self._filenode, text)
224
224
225 def parents(self):
225 def parents(self):
226 p = self._path
226 p = self._path
227 fl = self._filelog
227 fl = self._filelog
228 pl = [(p, n, fl) for n in self._filelog.parents(self._filenode)]
228 pl = [(p, n, fl) for n in self._filelog.parents(self._filenode)]
229
229
230 r = self.renamed()
230 r = self.renamed()
231 if r:
231 if r:
232 pl[0] = (r[0], r[1], None)
232 pl[0] = (r[0], r[1], None)
233
233
234 return [filectx(self._repo, p, fileid=n, filelog=l)
234 return [filectx(self._repo, p, fileid=n, filelog=l)
235 for p,n,l in pl if n != nullid]
235 for p,n,l in pl if n != nullid]
236
236
237 def children(self):
237 def children(self):
238 # hard for renames
238 # hard for renames
239 c = self._filelog.children(self._filenode)
239 c = self._filelog.children(self._filenode)
240 return [filectx(self._repo, self._path, fileid=x,
240 return [filectx(self._repo, self._path, fileid=x,
241 filelog=self._filelog) for x in c]
241 filelog=self._filelog) for x in c]
242
242
243 def annotate(self, follow=False):
243 def annotate(self, follow=False, linenumber=None):
244 '''returns a list of tuples of (ctx, line) for each line
244 '''returns a list of tuples of (ctx, line) for each line
245 in the file, where ctx is the filectx of the node where
245 in the file, where ctx is the filectx of the node where
246 that line was last changed'''
246 that line was last changed.
247 This returns tuples of ((ctx, linenumber), line) for each line,
248 if "linenumber" parameter is NOT "None".
249 In such tuples, linenumber means one at the first appearance
250 in the managed file.
251 To reduce annotation cost,
252 this returns fixed value(False is used) as linenumber,
253 if "linenumber" parameter is "False".'''
247
254
248 def decorate(text, rev):
255 def decorate_compat(text, rev):
249 return ([rev] * len(text.splitlines()), text)
256 return ([rev] * len(text.splitlines()), text)
250
257
258 def without_linenumber(text, rev):
259 return ([(rev, False)] * len(text.splitlines()), text)
260
261 def with_linenumber(text, rev):
262 size = len(text.splitlines())
263 return ([(rev, i) for i in xrange(1, size + 1)], text)
264
265 decorate = (((linenumber is None) and decorate_compat) or
266 (linenumber and with_linenumber) or
267 without_linenumber)
268
251 def pair(parent, child):
269 def pair(parent, child):
252 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
270 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
253 child[0][b1:b2] = parent[0][a1:a2]
271 child[0][b1:b2] = parent[0][a1:a2]
254 return child
272 return child
255
273
256 getlog = util.cachefunc(lambda x: self._repo.file(x))
274 getlog = util.cachefunc(lambda x: self._repo.file(x))
257 def getctx(path, fileid):
275 def getctx(path, fileid):
258 log = path == self._path and self._filelog or getlog(path)
276 log = path == self._path and self._filelog or getlog(path)
259 return filectx(self._repo, path, fileid=fileid, filelog=log)
277 return filectx(self._repo, path, fileid=fileid, filelog=log)
260 getctx = util.cachefunc(getctx)
278 getctx = util.cachefunc(getctx)
261
279
262 def parents(f):
280 def parents(f):
263 # we want to reuse filectx objects as much as possible
281 # we want to reuse filectx objects as much as possible
264 p = f._path
282 p = f._path
265 if f._filerev is None: # working dir
283 if f._filerev is None: # working dir
266 pl = [(n.path(), n.filerev()) for n in f.parents()]
284 pl = [(n.path(), n.filerev()) for n in f.parents()]
267 else:
285 else:
268 pl = [(p, n) for n in f._filelog.parentrevs(f._filerev)]
286 pl = [(p, n) for n in f._filelog.parentrevs(f._filerev)]
269
287
270 if follow:
288 if follow:
271 r = f.renamed()
289 r = f.renamed()
272 if r:
290 if r:
273 pl[0] = (r[0], getlog(r[0]).rev(r[1]))
291 pl[0] = (r[0], getlog(r[0]).rev(r[1]))
274
292
275 return [getctx(p, n) for p, n in pl if n != nullrev]
293 return [getctx(p, n) for p, n in pl if n != nullrev]
276
294
277 # use linkrev to find the first changeset where self appeared
295 # use linkrev to find the first changeset where self appeared
278 if self.rev() != self._filelog.linkrev(self._filenode):
296 if self.rev() != self._filelog.linkrev(self._filenode):
279 base = self.filectx(self.filerev())
297 base = self.filectx(self.filerev())
280 else:
298 else:
281 base = self
299 base = self
282
300
283 # find all ancestors
301 # find all ancestors
284 needed = {base: 1}
302 needed = {base: 1}
285 visit = [base]
303 visit = [base]
286 files = [base._path]
304 files = [base._path]
287 while visit:
305 while visit:
288 f = visit.pop(0)
306 f = visit.pop(0)
289 for p in parents(f):
307 for p in parents(f):
290 if p not in needed:
308 if p not in needed:
291 needed[p] = 1
309 needed[p] = 1
292 visit.append(p)
310 visit.append(p)
293 if p._path not in files:
311 if p._path not in files:
294 files.append(p._path)
312 files.append(p._path)
295 else:
313 else:
296 # count how many times we'll use this
314 # count how many times we'll use this
297 needed[p] += 1
315 needed[p] += 1
298
316
299 # sort by revision (per file) which is a topological order
317 # sort by revision (per file) which is a topological order
300 visit = []
318 visit = []
301 for f in files:
319 for f in files:
302 fn = [(n.rev(), n) for n in needed.keys() if n._path == f]
320 fn = [(n.rev(), n) for n in needed.keys() if n._path == f]
303 visit.extend(fn)
321 visit.extend(fn)
304 visit.sort()
322 visit.sort()
305 hist = {}
323 hist = {}
306
324
307 for r, f in visit:
325 for r, f in visit:
308 curr = decorate(f.data(), f)
326 curr = decorate(f.data(), f)
309 for p in parents(f):
327 for p in parents(f):
310 if p != nullid:
328 if p != nullid:
311 curr = pair(hist[p], curr)
329 curr = pair(hist[p], curr)
312 # trim the history of unneeded revs
330 # trim the history of unneeded revs
313 needed[p] -= 1
331 needed[p] -= 1
314 if not needed[p]:
332 if not needed[p]:
315 del hist[p]
333 del hist[p]
316 hist[f] = curr
334 hist[f] = curr
317
335
318 return zip(hist[f][0], hist[f][1].splitlines(1))
336 return zip(hist[f][0], hist[f][1].splitlines(1))
319
337
320 def ancestor(self, fc2):
338 def ancestor(self, fc2):
321 """
339 """
322 find the common ancestor file context, if any, of self, and fc2
340 find the common ancestor file context, if any, of self, and fc2
323 """
341 """
324
342
325 acache = {}
343 acache = {}
326
344
327 # prime the ancestor cache for the working directory
345 # prime the ancestor cache for the working directory
328 for c in (self, fc2):
346 for c in (self, fc2):
329 if c._filerev == None:
347 if c._filerev == None:
330 pl = [(n.path(), n.filenode()) for n in c.parents()]
348 pl = [(n.path(), n.filenode()) for n in c.parents()]
331 acache[(c._path, None)] = pl
349 acache[(c._path, None)] = pl
332
350
333 flcache = {self._path:self._filelog, fc2._path:fc2._filelog}
351 flcache = {self._path:self._filelog, fc2._path:fc2._filelog}
334 def parents(vertex):
352 def parents(vertex):
335 if vertex in acache:
353 if vertex in acache:
336 return acache[vertex]
354 return acache[vertex]
337 f, n = vertex
355 f, n = vertex
338 if f not in flcache:
356 if f not in flcache:
339 flcache[f] = self._repo.file(f)
357 flcache[f] = self._repo.file(f)
340 fl = flcache[f]
358 fl = flcache[f]
341 pl = [(f, p) for p in fl.parents(n) if p != nullid]
359 pl = [(f, p) for p in fl.parents(n) if p != nullid]
342 re = fl.renamed(n)
360 re = fl.renamed(n)
343 if re:
361 if re:
344 pl.append(re)
362 pl.append(re)
345 acache[vertex] = pl
363 acache[vertex] = pl
346 return pl
364 return pl
347
365
348 a, b = (self._path, self._filenode), (fc2._path, fc2._filenode)
366 a, b = (self._path, self._filenode), (fc2._path, fc2._filenode)
349 v = ancestor.ancestor(a, b, parents)
367 v = ancestor.ancestor(a, b, parents)
350 if v:
368 if v:
351 f, n = v
369 f, n = v
352 return filectx(self._repo, f, fileid=n, filelog=flcache[f])
370 return filectx(self._repo, f, fileid=n, filelog=flcache[f])
353
371
354 return None
372 return None
355
373
356 class workingctx(changectx):
374 class workingctx(changectx):
357 """A workingctx object makes access to data related to
375 """A workingctx object makes access to data related to
358 the current working directory convenient."""
376 the current working directory convenient."""
359 def __init__(self, repo):
377 def __init__(self, repo):
360 self._repo = repo
378 self._repo = repo
361 self._rev = None
379 self._rev = None
362 self._node = None
380 self._node = None
363
381
364 def __str__(self):
382 def __str__(self):
365 return str(self._parents[0]) + "+"
383 return str(self._parents[0]) + "+"
366
384
367 def __nonzero__(self):
385 def __nonzero__(self):
368 return True
386 return True
369
387
370 def __getattr__(self, name):
388 def __getattr__(self, name):
371 if name == '_parents':
389 if name == '_parents':
372 self._parents = self._repo.parents()
390 self._parents = self._repo.parents()
373 return self._parents
391 return self._parents
374 if name == '_status':
392 if name == '_status':
375 self._status = self._repo.status()
393 self._status = self._repo.status()
376 return self._status
394 return self._status
377 if name == '_manifest':
395 if name == '_manifest':
378 self._buildmanifest()
396 self._buildmanifest()
379 return self._manifest
397 return self._manifest
380 else:
398 else:
381 raise AttributeError, name
399 raise AttributeError, name
382
400
383 def _buildmanifest(self):
401 def _buildmanifest(self):
384 """generate a manifest corresponding to the working directory"""
402 """generate a manifest corresponding to the working directory"""
385
403
386 man = self._parents[0].manifest().copy()
404 man = self._parents[0].manifest().copy()
387 is_exec = util.execfunc(self._repo.root, man.execf)
405 is_exec = util.execfunc(self._repo.root, man.execf)
388 is_link = util.linkfunc(self._repo.root, man.linkf)
406 is_link = util.linkfunc(self._repo.root, man.linkf)
389 copied = self._repo.dirstate.copies()
407 copied = self._repo.dirstate.copies()
390 modified, added, removed, deleted, unknown = self._status[:5]
408 modified, added, removed, deleted, unknown = self._status[:5]
391 for i, l in (("a", added), ("m", modified), ("u", unknown)):
409 for i, l in (("a", added), ("m", modified), ("u", unknown)):
392 for f in l:
410 for f in l:
393 man[f] = man.get(copied.get(f, f), nullid) + i
411 man[f] = man.get(copied.get(f, f), nullid) + i
394 try:
412 try:
395 man.set(f, is_exec(f), is_link(f))
413 man.set(f, is_exec(f), is_link(f))
396 except OSError:
414 except OSError:
397 pass
415 pass
398
416
399 for f in deleted + removed:
417 for f in deleted + removed:
400 if f in man:
418 if f in man:
401 del man[f]
419 del man[f]
402
420
403 self._manifest = man
421 self._manifest = man
404
422
405 def manifest(self): return self._manifest
423 def manifest(self): return self._manifest
406
424
407 def user(self): return self._repo.ui.username()
425 def user(self): return self._repo.ui.username()
408 def date(self): return util.makedate()
426 def date(self): return util.makedate()
409 def description(self): return ""
427 def description(self): return ""
410 def files(self):
428 def files(self):
411 f = self.modified() + self.added() + self.removed()
429 f = self.modified() + self.added() + self.removed()
412 f.sort()
430 f.sort()
413 return f
431 return f
414
432
415 def modified(self): return self._status[0]
433 def modified(self): return self._status[0]
416 def added(self): return self._status[1]
434 def added(self): return self._status[1]
417 def removed(self): return self._status[2]
435 def removed(self): return self._status[2]
418 def deleted(self): return self._status[3]
436 def deleted(self): return self._status[3]
419 def unknown(self): return self._status[4]
437 def unknown(self): return self._status[4]
420 def clean(self): return self._status[5]
438 def clean(self): return self._status[5]
421 def branch(self): return self._repo.dirstate.branch()
439 def branch(self): return self._repo.dirstate.branch()
422
440
423 def tags(self):
441 def tags(self):
424 t = []
442 t = []
425 [t.extend(p.tags()) for p in self.parents()]
443 [t.extend(p.tags()) for p in self.parents()]
426 return t
444 return t
427
445
428 def parents(self):
446 def parents(self):
429 """return contexts for each parent changeset"""
447 """return contexts for each parent changeset"""
430 return self._parents
448 return self._parents
431
449
432 def children(self):
450 def children(self):
433 return []
451 return []
434
452
435 def filectx(self, path, filelog=None):
453 def filectx(self, path, filelog=None):
436 """get a file context from the working directory"""
454 """get a file context from the working directory"""
437 return workingfilectx(self._repo, path, workingctx=self,
455 return workingfilectx(self._repo, path, workingctx=self,
438 filelog=filelog)
456 filelog=filelog)
439
457
440 def ancestor(self, c2):
458 def ancestor(self, c2):
441 """return the ancestor context of self and c2"""
459 """return the ancestor context of self and c2"""
442 return self._parents[0].ancestor(c2) # punt on two parents for now
460 return self._parents[0].ancestor(c2) # punt on two parents for now
443
461
444 class workingfilectx(filectx):
462 class workingfilectx(filectx):
445 """A workingfilectx object makes access to data related to a particular
463 """A workingfilectx object makes access to data related to a particular
446 file in the working directory convenient."""
464 file in the working directory convenient."""
447 def __init__(self, repo, path, filelog=None, workingctx=None):
465 def __init__(self, repo, path, filelog=None, workingctx=None):
448 """changeid can be a changeset revision, node, or tag.
466 """changeid can be a changeset revision, node, or tag.
449 fileid can be a file revision or node."""
467 fileid can be a file revision or node."""
450 self._repo = repo
468 self._repo = repo
451 self._path = path
469 self._path = path
452 self._changeid = None
470 self._changeid = None
453 self._filerev = self._filenode = None
471 self._filerev = self._filenode = None
454
472
455 if filelog:
473 if filelog:
456 self._filelog = filelog
474 self._filelog = filelog
457 if workingctx:
475 if workingctx:
458 self._changectx = workingctx
476 self._changectx = workingctx
459
477
460 def __getattr__(self, name):
478 def __getattr__(self, name):
461 if name == '_changectx':
479 if name == '_changectx':
462 self._changectx = workingctx(self._repo)
480 self._changectx = workingctx(self._repo)
463 return self._changectx
481 return self._changectx
464 elif name == '_repopath':
482 elif name == '_repopath':
465 self._repopath = (self._repo.dirstate.copied(self._path)
483 self._repopath = (self._repo.dirstate.copied(self._path)
466 or self._path)
484 or self._path)
467 return self._repopath
485 return self._repopath
468 elif name == '_filelog':
486 elif name == '_filelog':
469 self._filelog = self._repo.file(self._repopath)
487 self._filelog = self._repo.file(self._repopath)
470 return self._filelog
488 return self._filelog
471 else:
489 else:
472 raise AttributeError, name
490 raise AttributeError, name
473
491
474 def __nonzero__(self):
492 def __nonzero__(self):
475 return True
493 return True
476
494
477 def __str__(self):
495 def __str__(self):
478 return "%s@%s" % (self.path(), self._changectx)
496 return "%s@%s" % (self.path(), self._changectx)
479
497
480 def filectx(self, fileid):
498 def filectx(self, fileid):
481 '''opens an arbitrary revision of the file without
499 '''opens an arbitrary revision of the file without
482 opening a new filelog'''
500 opening a new filelog'''
483 return filectx(self._repo, self._repopath, fileid=fileid,
501 return filectx(self._repo, self._repopath, fileid=fileid,
484 filelog=self._filelog)
502 filelog=self._filelog)
485
503
486 def rev(self):
504 def rev(self):
487 if '_changectx' in self.__dict__:
505 if '_changectx' in self.__dict__:
488 return self._changectx.rev()
506 return self._changectx.rev()
489 return self._filelog.linkrev(self._filenode)
507 return self._filelog.linkrev(self._filenode)
490
508
491 def data(self): return self._repo.wread(self._path)
509 def data(self): return self._repo.wread(self._path)
492 def renamed(self):
510 def renamed(self):
493 rp = self._repopath
511 rp = self._repopath
494 if rp == self._path:
512 if rp == self._path:
495 return None
513 return None
496 return rp, self._changectx._parents[0]._manifest.get(rp, nullid)
514 return rp, self._changectx._parents[0]._manifest.get(rp, nullid)
497
515
498 def parents(self):
516 def parents(self):
499 '''return parent filectxs, following copies if necessary'''
517 '''return parent filectxs, following copies if necessary'''
500 p = self._path
518 p = self._path
501 rp = self._repopath
519 rp = self._repopath
502 pcl = self._changectx._parents
520 pcl = self._changectx._parents
503 fl = self._filelog
521 fl = self._filelog
504 pl = [(rp, pcl[0]._manifest.get(rp, nullid), fl)]
522 pl = [(rp, pcl[0]._manifest.get(rp, nullid), fl)]
505 if len(pcl) > 1:
523 if len(pcl) > 1:
506 if rp != p:
524 if rp != p:
507 fl = None
525 fl = None
508 pl.append((p, pcl[1]._manifest.get(p, nullid), fl))
526 pl.append((p, pcl[1]._manifest.get(p, nullid), fl))
509
527
510 return [filectx(self._repo, p, fileid=n, filelog=l)
528 return [filectx(self._repo, p, fileid=n, filelog=l)
511 for p,n,l in pl if n != nullid]
529 for p,n,l in pl if n != nullid]
512
530
513 def children(self):
531 def children(self):
514 return []
532 return []
515
533
516 def size(self): return os.stat(self._repo.wjoin(self._path)).st_size
534 def size(self): return os.stat(self._repo.wjoin(self._path)).st_size
517 def date(self):
535 def date(self):
518 t, tz = self._changectx.date()
536 t, tz = self._changectx.date()
519 try:
537 try:
520 return (int(os.lstat(self._repo.wjoin(self._path)).st_mtime), tz)
538 return (int(os.lstat(self._repo.wjoin(self._path)).st_mtime), tz)
521 except OSError, err:
539 except OSError, err:
522 if err.errno != errno.ENOENT: raise
540 if err.errno != errno.ENOENT: raise
523 return (t, tz)
541 return (t, tz)
524
542
525 def cmp(self, text): return self._repo.wread(self._path) == text
543 def cmp(self, text): return self._repo.wread(self._path) == text
General Comments 0
You need to be logged in to leave comments. Login now