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