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