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