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