##// END OF EJS Templates
context: parents() already filters nullid
Benoit Boissinot -
r9974:da5209af default
parent child Browse files
Show More
@@ -1,831 +1,830
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 of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 from node import nullid, nullrev, short, hex
8 from node import nullid, nullrev, short, hex
9 from i18n import _
9 from i18n import _
10 import ancestor, bdiff, error, util, subrepo
10 import ancestor, bdiff, error, util, subrepo
11 import os, errno
11 import os, errno
12
12
13 propertycache = util.propertycache
13 propertycache = util.propertycache
14
14
15 class changectx(object):
15 class changectx(object):
16 """A changecontext object makes access to data related to a particular
16 """A changecontext object makes access to data related to a particular
17 changeset convenient."""
17 changeset convenient."""
18 def __init__(self, repo, changeid=''):
18 def __init__(self, repo, changeid=''):
19 """changeid is a revision number, node, or tag"""
19 """changeid is a revision number, node, or tag"""
20 if changeid == '':
20 if changeid == '':
21 changeid = '.'
21 changeid = '.'
22 self._repo = repo
22 self._repo = repo
23 if isinstance(changeid, (long, int)):
23 if isinstance(changeid, (long, int)):
24 self._rev = changeid
24 self._rev = changeid
25 self._node = self._repo.changelog.node(changeid)
25 self._node = self._repo.changelog.node(changeid)
26 else:
26 else:
27 self._node = self._repo.lookup(changeid)
27 self._node = self._repo.lookup(changeid)
28 self._rev = self._repo.changelog.rev(self._node)
28 self._rev = self._repo.changelog.rev(self._node)
29
29
30 def __str__(self):
30 def __str__(self):
31 return short(self.node())
31 return short(self.node())
32
32
33 def __int__(self):
33 def __int__(self):
34 return self.rev()
34 return self.rev()
35
35
36 def __repr__(self):
36 def __repr__(self):
37 return "<changectx %s>" % str(self)
37 return "<changectx %s>" % str(self)
38
38
39 def __hash__(self):
39 def __hash__(self):
40 try:
40 try:
41 return hash(self._rev)
41 return hash(self._rev)
42 except AttributeError:
42 except AttributeError:
43 return id(self)
43 return id(self)
44
44
45 def __eq__(self, other):
45 def __eq__(self, other):
46 try:
46 try:
47 return self._rev == other._rev
47 return self._rev == other._rev
48 except AttributeError:
48 except AttributeError:
49 return False
49 return False
50
50
51 def __ne__(self, other):
51 def __ne__(self, other):
52 return not (self == other)
52 return not (self == other)
53
53
54 def __nonzero__(self):
54 def __nonzero__(self):
55 return self._rev != nullrev
55 return self._rev != nullrev
56
56
57 @propertycache
57 @propertycache
58 def _changeset(self):
58 def _changeset(self):
59 return self._repo.changelog.read(self.node())
59 return self._repo.changelog.read(self.node())
60
60
61 @propertycache
61 @propertycache
62 def _manifest(self):
62 def _manifest(self):
63 return self._repo.manifest.read(self._changeset[0])
63 return self._repo.manifest.read(self._changeset[0])
64
64
65 @propertycache
65 @propertycache
66 def _manifestdelta(self):
66 def _manifestdelta(self):
67 return self._repo.manifest.readdelta(self._changeset[0])
67 return self._repo.manifest.readdelta(self._changeset[0])
68
68
69 @propertycache
69 @propertycache
70 def _parents(self):
70 def _parents(self):
71 p = self._repo.changelog.parentrevs(self._rev)
71 p = self._repo.changelog.parentrevs(self._rev)
72 if p[1] == nullrev:
72 if p[1] == nullrev:
73 p = p[:-1]
73 p = p[:-1]
74 return [changectx(self._repo, x) for x in p]
74 return [changectx(self._repo, x) for x in p]
75
75
76 @propertycache
76 @propertycache
77 def substate(self):
77 def substate(self):
78 return subrepo.state(self)
78 return subrepo.state(self)
79
79
80 def __contains__(self, key):
80 def __contains__(self, key):
81 return key in self._manifest
81 return key in self._manifest
82
82
83 def __getitem__(self, key):
83 def __getitem__(self, key):
84 return self.filectx(key)
84 return self.filectx(key)
85
85
86 def __iter__(self):
86 def __iter__(self):
87 for f in sorted(self._manifest):
87 for f in sorted(self._manifest):
88 yield f
88 yield f
89
89
90 def changeset(self): return self._changeset
90 def changeset(self): return self._changeset
91 def manifest(self): return self._manifest
91 def manifest(self): return self._manifest
92 def manifestnode(self): return self._changeset[0]
92 def manifestnode(self): return self._changeset[0]
93
93
94 def rev(self): return self._rev
94 def rev(self): return self._rev
95 def node(self): return self._node
95 def node(self): return self._node
96 def hex(self): return hex(self._node)
96 def hex(self): return hex(self._node)
97 def user(self): return self._changeset[1]
97 def user(self): return self._changeset[1]
98 def date(self): return self._changeset[2]
98 def date(self): return self._changeset[2]
99 def files(self): return self._changeset[3]
99 def files(self): return self._changeset[3]
100 def description(self): return self._changeset[4]
100 def description(self): return self._changeset[4]
101 def branch(self): return self._changeset[5].get("branch")
101 def branch(self): return self._changeset[5].get("branch")
102 def extra(self): return self._changeset[5]
102 def extra(self): return self._changeset[5]
103 def tags(self): return self._repo.nodetags(self._node)
103 def tags(self): return self._repo.nodetags(self._node)
104
104
105 def parents(self):
105 def parents(self):
106 """return contexts for each parent changeset"""
106 """return contexts for each parent changeset"""
107 return self._parents
107 return self._parents
108
108
109 def p1(self):
109 def p1(self):
110 return self._parents[0]
110 return self._parents[0]
111
111
112 def p2(self):
112 def p2(self):
113 if len(self._parents) == 2:
113 if len(self._parents) == 2:
114 return self._parents[1]
114 return self._parents[1]
115 return changectx(self._repo, -1)
115 return changectx(self._repo, -1)
116
116
117 def children(self):
117 def children(self):
118 """return contexts for each child changeset"""
118 """return contexts for each child changeset"""
119 c = self._repo.changelog.children(self._node)
119 c = self._repo.changelog.children(self._node)
120 return [changectx(self._repo, x) for x in c]
120 return [changectx(self._repo, x) for x in c]
121
121
122 def ancestors(self):
122 def ancestors(self):
123 for a in self._repo.changelog.ancestors(self._rev):
123 for a in self._repo.changelog.ancestors(self._rev):
124 yield changectx(self._repo, a)
124 yield changectx(self._repo, a)
125
125
126 def descendants(self):
126 def descendants(self):
127 for d in self._repo.changelog.descendants(self._rev):
127 for d in self._repo.changelog.descendants(self._rev):
128 yield changectx(self._repo, d)
128 yield changectx(self._repo, d)
129
129
130 def _fileinfo(self, path):
130 def _fileinfo(self, path):
131 if '_manifest' in self.__dict__:
131 if '_manifest' in self.__dict__:
132 try:
132 try:
133 return self._manifest[path], self._manifest.flags(path)
133 return self._manifest[path], self._manifest.flags(path)
134 except KeyError:
134 except KeyError:
135 raise error.LookupError(self._node, path,
135 raise error.LookupError(self._node, path,
136 _('not found in manifest'))
136 _('not found in manifest'))
137 if '_manifestdelta' in self.__dict__ or path in self.files():
137 if '_manifestdelta' in self.__dict__ or path in self.files():
138 if path in self._manifestdelta:
138 if path in self._manifestdelta:
139 return self._manifestdelta[path], self._manifestdelta.flags(path)
139 return self._manifestdelta[path], self._manifestdelta.flags(path)
140 node, flag = self._repo.manifest.find(self._changeset[0], path)
140 node, flag = self._repo.manifest.find(self._changeset[0], path)
141 if not node:
141 if not node:
142 raise error.LookupError(self._node, path,
142 raise error.LookupError(self._node, path,
143 _('not found in manifest'))
143 _('not found in manifest'))
144
144
145 return node, flag
145 return node, flag
146
146
147 def filenode(self, path):
147 def filenode(self, path):
148 return self._fileinfo(path)[0]
148 return self._fileinfo(path)[0]
149
149
150 def flags(self, path):
150 def flags(self, path):
151 try:
151 try:
152 return self._fileinfo(path)[1]
152 return self._fileinfo(path)[1]
153 except error.LookupError:
153 except error.LookupError:
154 return ''
154 return ''
155
155
156 def filectx(self, path, fileid=None, filelog=None):
156 def filectx(self, path, fileid=None, filelog=None):
157 """get a file context from this changeset"""
157 """get a file context from this changeset"""
158 if fileid is None:
158 if fileid is None:
159 fileid = self.filenode(path)
159 fileid = self.filenode(path)
160 return filectx(self._repo, path, fileid=fileid,
160 return filectx(self._repo, path, fileid=fileid,
161 changectx=self, filelog=filelog)
161 changectx=self, filelog=filelog)
162
162
163 def ancestor(self, c2):
163 def ancestor(self, c2):
164 """
164 """
165 return the ancestor context of self and c2
165 return the ancestor context of self and c2
166 """
166 """
167 # deal with workingctxs
167 # deal with workingctxs
168 n2 = c2._node
168 n2 = c2._node
169 if n2 == None:
169 if n2 == None:
170 n2 = c2._parents[0]._node
170 n2 = c2._parents[0]._node
171 n = self._repo.changelog.ancestor(self._node, n2)
171 n = self._repo.changelog.ancestor(self._node, n2)
172 return changectx(self._repo, n)
172 return changectx(self._repo, n)
173
173
174 def walk(self, match):
174 def walk(self, match):
175 fset = set(match.files())
175 fset = set(match.files())
176 # for dirstate.walk, files=['.'] means "walk the whole tree".
176 # for dirstate.walk, files=['.'] means "walk the whole tree".
177 # follow that here, too
177 # follow that here, too
178 fset.discard('.')
178 fset.discard('.')
179 for fn in self:
179 for fn in self:
180 for ffn in fset:
180 for ffn in fset:
181 # match if the file is the exact name or a directory
181 # match if the file is the exact name or a directory
182 if ffn == fn or fn.startswith("%s/" % ffn):
182 if ffn == fn or fn.startswith("%s/" % ffn):
183 fset.remove(ffn)
183 fset.remove(ffn)
184 break
184 break
185 if match(fn):
185 if match(fn):
186 yield fn
186 yield fn
187 for fn in sorted(fset):
187 for fn in sorted(fset):
188 if match.bad(fn, 'No such file in rev ' + str(self)) and match(fn):
188 if match.bad(fn, 'No such file in rev ' + str(self)) and match(fn):
189 yield fn
189 yield fn
190
190
191 def sub(self, path):
191 def sub(self, path):
192 return subrepo.subrepo(self, path)
192 return subrepo.subrepo(self, path)
193
193
194 class filectx(object):
194 class filectx(object):
195 """A filecontext object makes access to data related to a particular
195 """A filecontext object makes access to data related to a particular
196 filerevision convenient."""
196 filerevision convenient."""
197 def __init__(self, repo, path, changeid=None, fileid=None,
197 def __init__(self, repo, path, changeid=None, fileid=None,
198 filelog=None, changectx=None):
198 filelog=None, changectx=None):
199 """changeid can be a changeset revision, node, or tag.
199 """changeid can be a changeset revision, node, or tag.
200 fileid can be a file revision or node."""
200 fileid can be a file revision or node."""
201 self._repo = repo
201 self._repo = repo
202 self._path = path
202 self._path = path
203
203
204 assert (changeid is not None
204 assert (changeid is not None
205 or fileid is not None
205 or fileid is not None
206 or changectx is not None), \
206 or changectx is not None), \
207 ("bad args: changeid=%r, fileid=%r, changectx=%r"
207 ("bad args: changeid=%r, fileid=%r, changectx=%r"
208 % (changeid, fileid, changectx))
208 % (changeid, fileid, changectx))
209
209
210 if filelog:
210 if filelog:
211 self._filelog = filelog
211 self._filelog = filelog
212
212
213 if changeid is not None:
213 if changeid is not None:
214 self._changeid = changeid
214 self._changeid = changeid
215 if changectx is not None:
215 if changectx is not None:
216 self._changectx = changectx
216 self._changectx = changectx
217 if fileid is not None:
217 if fileid is not None:
218 self._fileid = fileid
218 self._fileid = fileid
219
219
220 @propertycache
220 @propertycache
221 def _changectx(self):
221 def _changectx(self):
222 return changectx(self._repo, self._changeid)
222 return changectx(self._repo, self._changeid)
223
223
224 @propertycache
224 @propertycache
225 def _filelog(self):
225 def _filelog(self):
226 return self._repo.file(self._path)
226 return self._repo.file(self._path)
227
227
228 @propertycache
228 @propertycache
229 def _changeid(self):
229 def _changeid(self):
230 if '_changectx' in self.__dict__:
230 if '_changectx' in self.__dict__:
231 return self._changectx.rev()
231 return self._changectx.rev()
232 else:
232 else:
233 return self._filelog.linkrev(self._filerev)
233 return self._filelog.linkrev(self._filerev)
234
234
235 @propertycache
235 @propertycache
236 def _filenode(self):
236 def _filenode(self):
237 if '_fileid' in self.__dict__:
237 if '_fileid' in self.__dict__:
238 return self._filelog.lookup(self._fileid)
238 return self._filelog.lookup(self._fileid)
239 else:
239 else:
240 return self._changectx.filenode(self._path)
240 return self._changectx.filenode(self._path)
241
241
242 @propertycache
242 @propertycache
243 def _filerev(self):
243 def _filerev(self):
244 return self._filelog.rev(self._filenode)
244 return self._filelog.rev(self._filenode)
245
245
246 @propertycache
246 @propertycache
247 def _repopath(self):
247 def _repopath(self):
248 return self._path
248 return self._path
249
249
250 def __nonzero__(self):
250 def __nonzero__(self):
251 try:
251 try:
252 self._filenode
252 self._filenode
253 return True
253 return True
254 except error.LookupError:
254 except error.LookupError:
255 # file is missing
255 # file is missing
256 return False
256 return False
257
257
258 def __str__(self):
258 def __str__(self):
259 return "%s@%s" % (self.path(), short(self.node()))
259 return "%s@%s" % (self.path(), short(self.node()))
260
260
261 def __repr__(self):
261 def __repr__(self):
262 return "<filectx %s>" % str(self)
262 return "<filectx %s>" % str(self)
263
263
264 def __hash__(self):
264 def __hash__(self):
265 try:
265 try:
266 return hash((self._path, self._fileid))
266 return hash((self._path, self._fileid))
267 except AttributeError:
267 except AttributeError:
268 return id(self)
268 return id(self)
269
269
270 def __eq__(self, other):
270 def __eq__(self, other):
271 try:
271 try:
272 return (self._path == other._path
272 return (self._path == other._path
273 and self._fileid == other._fileid)
273 and self._fileid == other._fileid)
274 except AttributeError:
274 except AttributeError:
275 return False
275 return False
276
276
277 def __ne__(self, other):
277 def __ne__(self, other):
278 return not (self == other)
278 return not (self == other)
279
279
280 def filectx(self, fileid):
280 def filectx(self, fileid):
281 '''opens an arbitrary revision of the file without
281 '''opens an arbitrary revision of the file without
282 opening a new filelog'''
282 opening a new filelog'''
283 return filectx(self._repo, self._path, fileid=fileid,
283 return filectx(self._repo, self._path, fileid=fileid,
284 filelog=self._filelog)
284 filelog=self._filelog)
285
285
286 def filerev(self): return self._filerev
286 def filerev(self): return self._filerev
287 def filenode(self): return self._filenode
287 def filenode(self): return self._filenode
288 def flags(self): return self._changectx.flags(self._path)
288 def flags(self): return self._changectx.flags(self._path)
289 def filelog(self): return self._filelog
289 def filelog(self): return self._filelog
290
290
291 def rev(self):
291 def rev(self):
292 if '_changectx' in self.__dict__:
292 if '_changectx' in self.__dict__:
293 return self._changectx.rev()
293 return self._changectx.rev()
294 if '_changeid' in self.__dict__:
294 if '_changeid' in self.__dict__:
295 return self._changectx.rev()
295 return self._changectx.rev()
296 return self._filelog.linkrev(self._filerev)
296 return self._filelog.linkrev(self._filerev)
297
297
298 def linkrev(self): return self._filelog.linkrev(self._filerev)
298 def linkrev(self): return self._filelog.linkrev(self._filerev)
299 def node(self): return self._changectx.node()
299 def node(self): return self._changectx.node()
300 def hex(self): return hex(self.node())
300 def hex(self): return hex(self.node())
301 def user(self): return self._changectx.user()
301 def user(self): return self._changectx.user()
302 def date(self): return self._changectx.date()
302 def date(self): return self._changectx.date()
303 def files(self): return self._changectx.files()
303 def files(self): return self._changectx.files()
304 def description(self): return self._changectx.description()
304 def description(self): return self._changectx.description()
305 def branch(self): return self._changectx.branch()
305 def branch(self): return self._changectx.branch()
306 def extra(self): return self._changectx.extra()
306 def extra(self): return self._changectx.extra()
307 def manifest(self): return self._changectx.manifest()
307 def manifest(self): return self._changectx.manifest()
308 def changectx(self): return self._changectx
308 def changectx(self): return self._changectx
309
309
310 def data(self): return self._filelog.read(self._filenode)
310 def data(self): return self._filelog.read(self._filenode)
311 def path(self): return self._path
311 def path(self): return self._path
312 def size(self): return self._filelog.size(self._filerev)
312 def size(self): return self._filelog.size(self._filerev)
313
313
314 def cmp(self, text): return self._filelog.cmp(self._filenode, text)
314 def cmp(self, text): return self._filelog.cmp(self._filenode, text)
315
315
316 def renamed(self):
316 def renamed(self):
317 """check if file was actually renamed in this changeset revision
317 """check if file was actually renamed in this changeset revision
318
318
319 If rename logged in file revision, we report copy for changeset only
319 If rename logged in file revision, we report copy for changeset only
320 if file revisions linkrev points back to the changeset in question
320 if file revisions linkrev points back to the changeset in question
321 or both changeset parents contain different file revisions.
321 or both changeset parents contain different file revisions.
322 """
322 """
323
323
324 renamed = self._filelog.renamed(self._filenode)
324 renamed = self._filelog.renamed(self._filenode)
325 if not renamed:
325 if not renamed:
326 return renamed
326 return renamed
327
327
328 if self.rev() == self.linkrev():
328 if self.rev() == self.linkrev():
329 return renamed
329 return renamed
330
330
331 name = self.path()
331 name = self.path()
332 fnode = self._filenode
332 fnode = self._filenode
333 for p in self._changectx.parents():
333 for p in self._changectx.parents():
334 try:
334 try:
335 if fnode == p.filenode(name):
335 if fnode == p.filenode(name):
336 return None
336 return None
337 except error.LookupError:
337 except error.LookupError:
338 pass
338 pass
339 return renamed
339 return renamed
340
340
341 def parents(self):
341 def parents(self):
342 p = self._path
342 p = self._path
343 fl = self._filelog
343 fl = self._filelog
344 pl = [(p, n, fl) for n in self._filelog.parents(self._filenode)]
344 pl = [(p, n, fl) for n in self._filelog.parents(self._filenode)]
345
345
346 r = self._filelog.renamed(self._filenode)
346 r = self._filelog.renamed(self._filenode)
347 if r:
347 if r:
348 pl[0] = (r[0], r[1], None)
348 pl[0] = (r[0], r[1], None)
349
349
350 return [filectx(self._repo, p, fileid=n, filelog=l)
350 return [filectx(self._repo, p, fileid=n, filelog=l)
351 for p,n,l in pl if n != nullid]
351 for p,n,l in pl if n != nullid]
352
352
353 def children(self):
353 def children(self):
354 # hard for renames
354 # hard for renames
355 c = self._filelog.children(self._filenode)
355 c = self._filelog.children(self._filenode)
356 return [filectx(self._repo, self._path, fileid=x,
356 return [filectx(self._repo, self._path, fileid=x,
357 filelog=self._filelog) for x in c]
357 filelog=self._filelog) for x in c]
358
358
359 def annotate(self, follow=False, linenumber=None):
359 def annotate(self, follow=False, linenumber=None):
360 '''returns a list of tuples of (ctx, line) for each line
360 '''returns a list of tuples of (ctx, line) for each line
361 in the file, where ctx is the filectx of the node where
361 in the file, where ctx is the filectx of the node where
362 that line was last changed.
362 that line was last changed.
363 This returns tuples of ((ctx, linenumber), line) for each line,
363 This returns tuples of ((ctx, linenumber), line) for each line,
364 if "linenumber" parameter is NOT "None".
364 if "linenumber" parameter is NOT "None".
365 In such tuples, linenumber means one at the first appearance
365 In such tuples, linenumber means one at the first appearance
366 in the managed file.
366 in the managed file.
367 To reduce annotation cost,
367 To reduce annotation cost,
368 this returns fixed value(False is used) as linenumber,
368 this returns fixed value(False is used) as linenumber,
369 if "linenumber" parameter is "False".'''
369 if "linenumber" parameter is "False".'''
370
370
371 def decorate_compat(text, rev):
371 def decorate_compat(text, rev):
372 return ([rev] * len(text.splitlines()), text)
372 return ([rev] * len(text.splitlines()), text)
373
373
374 def without_linenumber(text, rev):
374 def without_linenumber(text, rev):
375 return ([(rev, False)] * len(text.splitlines()), text)
375 return ([(rev, False)] * len(text.splitlines()), text)
376
376
377 def with_linenumber(text, rev):
377 def with_linenumber(text, rev):
378 size = len(text.splitlines())
378 size = len(text.splitlines())
379 return ([(rev, i) for i in xrange(1, size + 1)], text)
379 return ([(rev, i) for i in xrange(1, size + 1)], text)
380
380
381 decorate = (((linenumber is None) and decorate_compat) or
381 decorate = (((linenumber is None) and decorate_compat) or
382 (linenumber and with_linenumber) or
382 (linenumber and with_linenumber) or
383 without_linenumber)
383 without_linenumber)
384
384
385 def pair(parent, child):
385 def pair(parent, child):
386 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
386 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
387 child[0][b1:b2] = parent[0][a1:a2]
387 child[0][b1:b2] = parent[0][a1:a2]
388 return child
388 return child
389
389
390 getlog = util.lrucachefunc(lambda x: self._repo.file(x))
390 getlog = util.lrucachefunc(lambda x: self._repo.file(x))
391 def getctx(path, fileid):
391 def getctx(path, fileid):
392 log = path == self._path and self._filelog or getlog(path)
392 log = path == self._path and self._filelog or getlog(path)
393 return filectx(self._repo, path, fileid=fileid, filelog=log)
393 return filectx(self._repo, path, fileid=fileid, filelog=log)
394 getctx = util.lrucachefunc(getctx)
394 getctx = util.lrucachefunc(getctx)
395
395
396 def parents(f):
396 def parents(f):
397 # we want to reuse filectx objects as much as possible
397 # we want to reuse filectx objects as much as possible
398 p = f._path
398 p = f._path
399 if f._filerev is None: # working dir
399 if f._filerev is None: # working dir
400 pl = [(n.path(), n.filerev()) for n in f.parents()]
400 pl = [(n.path(), n.filerev()) for n in f.parents()]
401 else:
401 else:
402 pl = [(p, n) for n in f._filelog.parentrevs(f._filerev)]
402 pl = [(p, n) for n in f._filelog.parentrevs(f._filerev)]
403
403
404 if follow:
404 if follow:
405 r = f.renamed()
405 r = f.renamed()
406 if r:
406 if r:
407 pl[0] = (r[0], getlog(r[0]).rev(r[1]))
407 pl[0] = (r[0], getlog(r[0]).rev(r[1]))
408
408
409 return [getctx(p, n) for p, n in pl if n != nullrev]
409 return [getctx(p, n) for p, n in pl if n != nullrev]
410
410
411 # use linkrev to find the first changeset where self appeared
411 # use linkrev to find the first changeset where self appeared
412 if self.rev() != self.linkrev():
412 if self.rev() != self.linkrev():
413 base = self.filectx(self.filerev())
413 base = self.filectx(self.filerev())
414 else:
414 else:
415 base = self
415 base = self
416
416
417 # find all ancestors
417 # find all ancestors
418 needed = {base: 1}
418 needed = {base: 1}
419 visit = [base]
419 visit = [base]
420 files = [base._path]
420 files = [base._path]
421 while visit:
421 while visit:
422 f = visit.pop(0)
422 f = visit.pop(0)
423 for p in parents(f):
423 for p in parents(f):
424 if p not in needed:
424 if p not in needed:
425 needed[p] = 1
425 needed[p] = 1
426 visit.append(p)
426 visit.append(p)
427 if p._path not in files:
427 if p._path not in files:
428 files.append(p._path)
428 files.append(p._path)
429 else:
429 else:
430 # count how many times we'll use this
430 # count how many times we'll use this
431 needed[p] += 1
431 needed[p] += 1
432
432
433 # sort by revision (per file) which is a topological order
433 # sort by revision (per file) which is a topological order
434 visit = []
434 visit = []
435 for f in files:
435 for f in files:
436 visit.extend(n for n in needed if n._path == f)
436 visit.extend(n for n in needed if n._path == f)
437
437
438 hist = {}
438 hist = {}
439 for f in sorted(visit, key=lambda x: x.rev()):
439 for f in sorted(visit, key=lambda x: x.rev()):
440 curr = decorate(f.data(), f)
440 curr = decorate(f.data(), f)
441 for p in parents(f):
441 for p in parents(f):
442 if p != nullid:
443 curr = pair(hist[p], curr)
442 curr = pair(hist[p], curr)
444 # trim the history of unneeded revs
443 # trim the history of unneeded revs
445 needed[p] -= 1
444 needed[p] -= 1
446 if not needed[p]:
445 if not needed[p]:
447 del hist[p]
446 del hist[p]
448 hist[f] = curr
447 hist[f] = curr
449
448
450 return zip(hist[f][0], hist[f][1].splitlines(True))
449 return zip(hist[f][0], hist[f][1].splitlines(True))
451
450
452 def ancestor(self, fc2):
451 def ancestor(self, fc2):
453 """
452 """
454 find the common ancestor file context, if any, of self, and fc2
453 find the common ancestor file context, if any, of self, and fc2
455 """
454 """
456
455
457 actx = self.changectx().ancestor(fc2.changectx())
456 actx = self.changectx().ancestor(fc2.changectx())
458
457
459 # the trivial case: changesets are unrelated, files must be too
458 # the trivial case: changesets are unrelated, files must be too
460 if not actx:
459 if not actx:
461 return None
460 return None
462
461
463 # the easy case: no (relevant) renames
462 # the easy case: no (relevant) renames
464 if fc2.path() == self.path() and self.path() in actx:
463 if fc2.path() == self.path() and self.path() in actx:
465 return actx[self.path()]
464 return actx[self.path()]
466 acache = {}
465 acache = {}
467
466
468 # prime the ancestor cache for the working directory
467 # prime the ancestor cache for the working directory
469 for c in (self, fc2):
468 for c in (self, fc2):
470 if c._filerev is None:
469 if c._filerev is None:
471 pl = [(n.path(), n.filenode()) for n in c.parents()]
470 pl = [(n.path(), n.filenode()) for n in c.parents()]
472 acache[(c._path, None)] = pl
471 acache[(c._path, None)] = pl
473
472
474 flcache = {self._repopath:self._filelog, fc2._repopath:fc2._filelog}
473 flcache = {self._repopath:self._filelog, fc2._repopath:fc2._filelog}
475 def parents(vertex):
474 def parents(vertex):
476 if vertex in acache:
475 if vertex in acache:
477 return acache[vertex]
476 return acache[vertex]
478 f, n = vertex
477 f, n = vertex
479 if f not in flcache:
478 if f not in flcache:
480 flcache[f] = self._repo.file(f)
479 flcache[f] = self._repo.file(f)
481 fl = flcache[f]
480 fl = flcache[f]
482 pl = [(f, p) for p in fl.parents(n) if p != nullid]
481 pl = [(f, p) for p in fl.parents(n) if p != nullid]
483 re = fl.renamed(n)
482 re = fl.renamed(n)
484 if re:
483 if re:
485 pl.append(re)
484 pl.append(re)
486 acache[vertex] = pl
485 acache[vertex] = pl
487 return pl
486 return pl
488
487
489 a, b = (self._path, self._filenode), (fc2._path, fc2._filenode)
488 a, b = (self._path, self._filenode), (fc2._path, fc2._filenode)
490 v = ancestor.ancestor(a, b, parents)
489 v = ancestor.ancestor(a, b, parents)
491 if v:
490 if v:
492 f, n = v
491 f, n = v
493 return filectx(self._repo, f, fileid=n, filelog=flcache[f])
492 return filectx(self._repo, f, fileid=n, filelog=flcache[f])
494
493
495 return None
494 return None
496
495
497 class workingctx(changectx):
496 class workingctx(changectx):
498 """A workingctx object makes access to data related to
497 """A workingctx object makes access to data related to
499 the current working directory convenient.
498 the current working directory convenient.
500 parents - a pair of parent nodeids, or None to use the dirstate.
499 parents - a pair of parent nodeids, or None to use the dirstate.
501 date - any valid date string or (unixtime, offset), or None.
500 date - any valid date string or (unixtime, offset), or None.
502 user - username string, or None.
501 user - username string, or None.
503 extra - a dictionary of extra values, or None.
502 extra - a dictionary of extra values, or None.
504 changes - a list of file lists as returned by localrepo.status()
503 changes - a list of file lists as returned by localrepo.status()
505 or None to use the repository status.
504 or None to use the repository status.
506 """
505 """
507 def __init__(self, repo, parents=None, text="", user=None, date=None,
506 def __init__(self, repo, parents=None, text="", user=None, date=None,
508 extra=None, changes=None):
507 extra=None, changes=None):
509 self._repo = repo
508 self._repo = repo
510 self._rev = None
509 self._rev = None
511 self._node = None
510 self._node = None
512 self._text = text
511 self._text = text
513 if date:
512 if date:
514 self._date = util.parsedate(date)
513 self._date = util.parsedate(date)
515 if user:
514 if user:
516 self._user = user
515 self._user = user
517 if parents:
516 if parents:
518 self._parents = [changectx(self._repo, p) for p in parents]
517 self._parents = [changectx(self._repo, p) for p in parents]
519 if changes:
518 if changes:
520 self._status = list(changes)
519 self._status = list(changes)
521
520
522 self._extra = {}
521 self._extra = {}
523 if extra:
522 if extra:
524 self._extra = extra.copy()
523 self._extra = extra.copy()
525 if 'branch' not in self._extra:
524 if 'branch' not in self._extra:
526 branch = self._repo.dirstate.branch()
525 branch = self._repo.dirstate.branch()
527 try:
526 try:
528 branch = branch.decode('UTF-8').encode('UTF-8')
527 branch = branch.decode('UTF-8').encode('UTF-8')
529 except UnicodeDecodeError:
528 except UnicodeDecodeError:
530 raise util.Abort(_('branch name not in UTF-8!'))
529 raise util.Abort(_('branch name not in UTF-8!'))
531 self._extra['branch'] = branch
530 self._extra['branch'] = branch
532 if self._extra['branch'] == '':
531 if self._extra['branch'] == '':
533 self._extra['branch'] = 'default'
532 self._extra['branch'] = 'default'
534
533
535 def __str__(self):
534 def __str__(self):
536 return str(self._parents[0]) + "+"
535 return str(self._parents[0]) + "+"
537
536
538 def __nonzero__(self):
537 def __nonzero__(self):
539 return True
538 return True
540
539
541 def __contains__(self, key):
540 def __contains__(self, key):
542 return self._repo.dirstate[key] not in "?r"
541 return self._repo.dirstate[key] not in "?r"
543
542
544 @propertycache
543 @propertycache
545 def _manifest(self):
544 def _manifest(self):
546 """generate a manifest corresponding to the working directory"""
545 """generate a manifest corresponding to the working directory"""
547
546
548 man = self._parents[0].manifest().copy()
547 man = self._parents[0].manifest().copy()
549 copied = self._repo.dirstate.copies()
548 copied = self._repo.dirstate.copies()
550 cf = lambda x: man.flags(copied.get(x, x))
549 cf = lambda x: man.flags(copied.get(x, x))
551 ff = self._repo.dirstate.flagfunc(cf)
550 ff = self._repo.dirstate.flagfunc(cf)
552 modified, added, removed, deleted, unknown = self._status[:5]
551 modified, added, removed, deleted, unknown = self._status[:5]
553 for i, l in (("a", added), ("m", modified), ("u", unknown)):
552 for i, l in (("a", added), ("m", modified), ("u", unknown)):
554 for f in l:
553 for f in l:
555 man[f] = man.get(copied.get(f, f), nullid) + i
554 man[f] = man.get(copied.get(f, f), nullid) + i
556 try:
555 try:
557 man.set(f, ff(f))
556 man.set(f, ff(f))
558 except OSError:
557 except OSError:
559 pass
558 pass
560
559
561 for f in deleted + removed:
560 for f in deleted + removed:
562 if f in man:
561 if f in man:
563 del man[f]
562 del man[f]
564
563
565 return man
564 return man
566
565
567 @propertycache
566 @propertycache
568 def _status(self):
567 def _status(self):
569 return self._repo.status(unknown=True)
568 return self._repo.status(unknown=True)
570
569
571 @propertycache
570 @propertycache
572 def _user(self):
571 def _user(self):
573 return self._repo.ui.username()
572 return self._repo.ui.username()
574
573
575 @propertycache
574 @propertycache
576 def _date(self):
575 def _date(self):
577 return util.makedate()
576 return util.makedate()
578
577
579 @propertycache
578 @propertycache
580 def _parents(self):
579 def _parents(self):
581 p = self._repo.dirstate.parents()
580 p = self._repo.dirstate.parents()
582 if p[1] == nullid:
581 if p[1] == nullid:
583 p = p[:-1]
582 p = p[:-1]
584 self._parents = [changectx(self._repo, x) for x in p]
583 self._parents = [changectx(self._repo, x) for x in p]
585 return self._parents
584 return self._parents
586
585
587 def manifest(self): return self._manifest
586 def manifest(self): return self._manifest
588
587
589 def user(self): return self._user or self._repo.ui.username()
588 def user(self): return self._user or self._repo.ui.username()
590 def date(self): return self._date
589 def date(self): return self._date
591 def description(self): return self._text
590 def description(self): return self._text
592 def files(self):
591 def files(self):
593 return sorted(self._status[0] + self._status[1] + self._status[2])
592 return sorted(self._status[0] + self._status[1] + self._status[2])
594
593
595 def modified(self): return self._status[0]
594 def modified(self): return self._status[0]
596 def added(self): return self._status[1]
595 def added(self): return self._status[1]
597 def removed(self): return self._status[2]
596 def removed(self): return self._status[2]
598 def deleted(self): return self._status[3]
597 def deleted(self): return self._status[3]
599 def unknown(self): return self._status[4]
598 def unknown(self): return self._status[4]
600 def clean(self): return self._status[5]
599 def clean(self): return self._status[5]
601 def branch(self): return self._extra['branch']
600 def branch(self): return self._extra['branch']
602 def extra(self): return self._extra
601 def extra(self): return self._extra
603
602
604 def tags(self):
603 def tags(self):
605 t = []
604 t = []
606 [t.extend(p.tags()) for p in self.parents()]
605 [t.extend(p.tags()) for p in self.parents()]
607 return t
606 return t
608
607
609 def children(self):
608 def children(self):
610 return []
609 return []
611
610
612 def flags(self, path):
611 def flags(self, path):
613 if '_manifest' in self.__dict__:
612 if '_manifest' in self.__dict__:
614 try:
613 try:
615 return self._manifest.flags(path)
614 return self._manifest.flags(path)
616 except KeyError:
615 except KeyError:
617 return ''
616 return ''
618
617
619 pnode = self._parents[0].changeset()[0]
618 pnode = self._parents[0].changeset()[0]
620 orig = self._repo.dirstate.copies().get(path, path)
619 orig = self._repo.dirstate.copies().get(path, path)
621 node, flag = self._repo.manifest.find(pnode, orig)
620 node, flag = self._repo.manifest.find(pnode, orig)
622 try:
621 try:
623 ff = self._repo.dirstate.flagfunc(lambda x: flag or '')
622 ff = self._repo.dirstate.flagfunc(lambda x: flag or '')
624 return ff(path)
623 return ff(path)
625 except OSError:
624 except OSError:
626 pass
625 pass
627
626
628 if not node or path in self.deleted() or path in self.removed():
627 if not node or path in self.deleted() or path in self.removed():
629 return ''
628 return ''
630 return flag
629 return flag
631
630
632 def filectx(self, path, filelog=None):
631 def filectx(self, path, filelog=None):
633 """get a file context from the working directory"""
632 """get a file context from the working directory"""
634 return workingfilectx(self._repo, path, workingctx=self,
633 return workingfilectx(self._repo, path, workingctx=self,
635 filelog=filelog)
634 filelog=filelog)
636
635
637 def ancestor(self, c2):
636 def ancestor(self, c2):
638 """return the ancestor context of self and c2"""
637 """return the ancestor context of self and c2"""
639 return self._parents[0].ancestor(c2) # punt on two parents for now
638 return self._parents[0].ancestor(c2) # punt on two parents for now
640
639
641 def walk(self, match):
640 def walk(self, match):
642 return sorted(self._repo.dirstate.walk(match, True, False))
641 return sorted(self._repo.dirstate.walk(match, True, False))
643
642
644 def dirty(self, missing=False):
643 def dirty(self, missing=False):
645 "check whether a working directory is modified"
644 "check whether a working directory is modified"
646
645
647 return (self.p2() or self.branch() != self.p1().branch() or
646 return (self.p2() or self.branch() != self.p1().branch() or
648 self.modified() or self.added() or self.removed() or
647 self.modified() or self.added() or self.removed() or
649 (missing and self.deleted()))
648 (missing and self.deleted()))
650
649
651 class workingfilectx(filectx):
650 class workingfilectx(filectx):
652 """A workingfilectx object makes access to data related to a particular
651 """A workingfilectx object makes access to data related to a particular
653 file in the working directory convenient."""
652 file in the working directory convenient."""
654 def __init__(self, repo, path, filelog=None, workingctx=None):
653 def __init__(self, repo, path, filelog=None, workingctx=None):
655 """changeid can be a changeset revision, node, or tag.
654 """changeid can be a changeset revision, node, or tag.
656 fileid can be a file revision or node."""
655 fileid can be a file revision or node."""
657 self._repo = repo
656 self._repo = repo
658 self._path = path
657 self._path = path
659 self._changeid = None
658 self._changeid = None
660 self._filerev = self._filenode = None
659 self._filerev = self._filenode = None
661
660
662 if filelog:
661 if filelog:
663 self._filelog = filelog
662 self._filelog = filelog
664 if workingctx:
663 if workingctx:
665 self._changectx = workingctx
664 self._changectx = workingctx
666
665
667 @propertycache
666 @propertycache
668 def _changectx(self):
667 def _changectx(self):
669 return workingctx(self._repo)
668 return workingctx(self._repo)
670
669
671 def __nonzero__(self):
670 def __nonzero__(self):
672 return True
671 return True
673
672
674 def __str__(self):
673 def __str__(self):
675 return "%s@%s" % (self.path(), self._changectx)
674 return "%s@%s" % (self.path(), self._changectx)
676
675
677 def data(self): return self._repo.wread(self._path)
676 def data(self): return self._repo.wread(self._path)
678 def renamed(self):
677 def renamed(self):
679 rp = self._repo.dirstate.copied(self._path)
678 rp = self._repo.dirstate.copied(self._path)
680 if not rp:
679 if not rp:
681 return None
680 return None
682 return rp, self._changectx._parents[0]._manifest.get(rp, nullid)
681 return rp, self._changectx._parents[0]._manifest.get(rp, nullid)
683
682
684 def parents(self):
683 def parents(self):
685 '''return parent filectxs, following copies if necessary'''
684 '''return parent filectxs, following copies if necessary'''
686 def filenode(ctx, path):
685 def filenode(ctx, path):
687 return ctx._manifest.get(path, nullid)
686 return ctx._manifest.get(path, nullid)
688
687
689 path = self._path
688 path = self._path
690 fl = self._filelog
689 fl = self._filelog
691 pcl = self._changectx._parents
690 pcl = self._changectx._parents
692 renamed = self.renamed()
691 renamed = self.renamed()
693
692
694 if renamed:
693 if renamed:
695 pl = [renamed + (None,)]
694 pl = [renamed + (None,)]
696 else:
695 else:
697 pl = [(path, filenode(pcl[0], path), fl)]
696 pl = [(path, filenode(pcl[0], path), fl)]
698
697
699 for pc in pcl[1:]:
698 for pc in pcl[1:]:
700 pl.append((path, filenode(pc, path), fl))
699 pl.append((path, filenode(pc, path), fl))
701
700
702 return [filectx(self._repo, p, fileid=n, filelog=l)
701 return [filectx(self._repo, p, fileid=n, filelog=l)
703 for p,n,l in pl if n != nullid]
702 for p,n,l in pl if n != nullid]
704
703
705 def children(self):
704 def children(self):
706 return []
705 return []
707
706
708 def size(self): return os.stat(self._repo.wjoin(self._path)).st_size
707 def size(self): return os.stat(self._repo.wjoin(self._path)).st_size
709 def date(self):
708 def date(self):
710 t, tz = self._changectx.date()
709 t, tz = self._changectx.date()
711 try:
710 try:
712 return (int(os.lstat(self._repo.wjoin(self._path)).st_mtime), tz)
711 return (int(os.lstat(self._repo.wjoin(self._path)).st_mtime), tz)
713 except OSError, err:
712 except OSError, err:
714 if err.errno != errno.ENOENT: raise
713 if err.errno != errno.ENOENT: raise
715 return (t, tz)
714 return (t, tz)
716
715
717 def cmp(self, text): return self._repo.wread(self._path) == text
716 def cmp(self, text): return self._repo.wread(self._path) == text
718
717
719 class memctx(object):
718 class memctx(object):
720 """Use memctx to perform in-memory commits via localrepo.commitctx().
719 """Use memctx to perform in-memory commits via localrepo.commitctx().
721
720
722 Revision information is supplied at initialization time while
721 Revision information is supplied at initialization time while
723 related files data and is made available through a callback
722 related files data and is made available through a callback
724 mechanism. 'repo' is the current localrepo, 'parents' is a
723 mechanism. 'repo' is the current localrepo, 'parents' is a
725 sequence of two parent revisions identifiers (pass None for every
724 sequence of two parent revisions identifiers (pass None for every
726 missing parent), 'text' is the commit message and 'files' lists
725 missing parent), 'text' is the commit message and 'files' lists
727 names of files touched by the revision (normalized and relative to
726 names of files touched by the revision (normalized and relative to
728 repository root).
727 repository root).
729
728
730 filectxfn(repo, memctx, path) is a callable receiving the
729 filectxfn(repo, memctx, path) is a callable receiving the
731 repository, the current memctx object and the normalized path of
730 repository, the current memctx object and the normalized path of
732 requested file, relative to repository root. It is fired by the
731 requested file, relative to repository root. It is fired by the
733 commit function for every file in 'files', but calls order is
732 commit function for every file in 'files', but calls order is
734 undefined. If the file is available in the revision being
733 undefined. If the file is available in the revision being
735 committed (updated or added), filectxfn returns a memfilectx
734 committed (updated or added), filectxfn returns a memfilectx
736 object. If the file was removed, filectxfn raises an
735 object. If the file was removed, filectxfn raises an
737 IOError. Moved files are represented by marking the source file
736 IOError. Moved files are represented by marking the source file
738 removed and the new file added with copy information (see
737 removed and the new file added with copy information (see
739 memfilectx).
738 memfilectx).
740
739
741 user receives the committer name and defaults to current
740 user receives the committer name and defaults to current
742 repository username, date is the commit date in any format
741 repository username, date is the commit date in any format
743 supported by util.parsedate() and defaults to current date, extra
742 supported by util.parsedate() and defaults to current date, extra
744 is a dictionary of metadata or is left empty.
743 is a dictionary of metadata or is left empty.
745 """
744 """
746 def __init__(self, repo, parents, text, files, filectxfn, user=None,
745 def __init__(self, repo, parents, text, files, filectxfn, user=None,
747 date=None, extra=None):
746 date=None, extra=None):
748 self._repo = repo
747 self._repo = repo
749 self._rev = None
748 self._rev = None
750 self._node = None
749 self._node = None
751 self._text = text
750 self._text = text
752 self._date = date and util.parsedate(date) or util.makedate()
751 self._date = date and util.parsedate(date) or util.makedate()
753 self._user = user
752 self._user = user
754 parents = [(p or nullid) for p in parents]
753 parents = [(p or nullid) for p in parents]
755 p1, p2 = parents
754 p1, p2 = parents
756 self._parents = [changectx(self._repo, p) for p in (p1, p2)]
755 self._parents = [changectx(self._repo, p) for p in (p1, p2)]
757 files = sorted(set(files))
756 files = sorted(set(files))
758 self._status = [files, [], [], [], []]
757 self._status = [files, [], [], [], []]
759 self._filectxfn = filectxfn
758 self._filectxfn = filectxfn
760
759
761 self._extra = extra and extra.copy() or {}
760 self._extra = extra and extra.copy() or {}
762 if 'branch' not in self._extra:
761 if 'branch' not in self._extra:
763 self._extra['branch'] = 'default'
762 self._extra['branch'] = 'default'
764 elif self._extra.get('branch') == '':
763 elif self._extra.get('branch') == '':
765 self._extra['branch'] = 'default'
764 self._extra['branch'] = 'default'
766
765
767 def __str__(self):
766 def __str__(self):
768 return str(self._parents[0]) + "+"
767 return str(self._parents[0]) + "+"
769
768
770 def __int__(self):
769 def __int__(self):
771 return self._rev
770 return self._rev
772
771
773 def __nonzero__(self):
772 def __nonzero__(self):
774 return True
773 return True
775
774
776 def __getitem__(self, key):
775 def __getitem__(self, key):
777 return self.filectx(key)
776 return self.filectx(key)
778
777
779 def p1(self): return self._parents[0]
778 def p1(self): return self._parents[0]
780 def p2(self): return self._parents[1]
779 def p2(self): return self._parents[1]
781
780
782 def user(self): return self._user or self._repo.ui.username()
781 def user(self): return self._user or self._repo.ui.username()
783 def date(self): return self._date
782 def date(self): return self._date
784 def description(self): return self._text
783 def description(self): return self._text
785 def files(self): return self.modified()
784 def files(self): return self.modified()
786 def modified(self): return self._status[0]
785 def modified(self): return self._status[0]
787 def added(self): return self._status[1]
786 def added(self): return self._status[1]
788 def removed(self): return self._status[2]
787 def removed(self): return self._status[2]
789 def deleted(self): return self._status[3]
788 def deleted(self): return self._status[3]
790 def unknown(self): return self._status[4]
789 def unknown(self): return self._status[4]
791 def clean(self): return self._status[5]
790 def clean(self): return self._status[5]
792 def branch(self): return self._extra['branch']
791 def branch(self): return self._extra['branch']
793 def extra(self): return self._extra
792 def extra(self): return self._extra
794 def flags(self, f): return self[f].flags()
793 def flags(self, f): return self[f].flags()
795
794
796 def parents(self):
795 def parents(self):
797 """return contexts for each parent changeset"""
796 """return contexts for each parent changeset"""
798 return self._parents
797 return self._parents
799
798
800 def filectx(self, path, filelog=None):
799 def filectx(self, path, filelog=None):
801 """get a file context from the working directory"""
800 """get a file context from the working directory"""
802 return self._filectxfn(self._repo, self, path)
801 return self._filectxfn(self._repo, self, path)
803
802
804 class memfilectx(object):
803 class memfilectx(object):
805 """memfilectx represents an in-memory file to commit.
804 """memfilectx represents an in-memory file to commit.
806
805
807 See memctx for more details.
806 See memctx for more details.
808 """
807 """
809 def __init__(self, path, data, islink, isexec, copied):
808 def __init__(self, path, data, islink, isexec, copied):
810 """
809 """
811 path is the normalized file path relative to repository root.
810 path is the normalized file path relative to repository root.
812 data is the file content as a string.
811 data is the file content as a string.
813 islink is True if the file is a symbolic link.
812 islink is True if the file is a symbolic link.
814 isexec is True if the file is executable.
813 isexec is True if the file is executable.
815 copied is the source file path if current file was copied in the
814 copied is the source file path if current file was copied in the
816 revision being committed, or None."""
815 revision being committed, or None."""
817 self._path = path
816 self._path = path
818 self._data = data
817 self._data = data
819 self._flags = (islink and 'l' or '') + (isexec and 'x' or '')
818 self._flags = (islink and 'l' or '') + (isexec and 'x' or '')
820 self._copied = None
819 self._copied = None
821 if copied:
820 if copied:
822 self._copied = (copied, nullid)
821 self._copied = (copied, nullid)
823
822
824 def __nonzero__(self): return True
823 def __nonzero__(self): return True
825 def __str__(self): return "%s@%s" % (self.path(), self._changectx)
824 def __str__(self): return "%s@%s" % (self.path(), self._changectx)
826 def path(self): return self._path
825 def path(self): return self._path
827 def data(self): return self._data
826 def data(self): return self._data
828 def flags(self): return self._flags
827 def flags(self): return self._flags
829 def isexec(self): return 'x' in self._flags
828 def isexec(self): return 'x' in self._flags
830 def islink(self): return 'l' in self._flags
829 def islink(self): return 'l' in self._flags
831 def renamed(self): return self._copied
830 def renamed(self): return self._copied
General Comments 0
You need to be logged in to leave comments. Login now