##// END OF EJS Templates
context: add copies method with caching
Matt Mackall -
r16602:80aef0bc default
parent child Browse files
Show More
@@ -1,1266 +1,1275 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 of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import nullid, nullrev, short, hex, bin
8 from node import nullid, nullrev, short, hex, bin
9 from i18n import _
9 from i18n import _
10 import ancestor, mdiff, error, util, scmutil, subrepo, patch, encoding, phases
10 import ancestor, mdiff, error, util, scmutil, subrepo, patch, encoding, phases
11 import copies
11 import match as matchmod
12 import match as matchmod
12 import os, errno, stat
13 import os, errno, stat
13
14
14 propertycache = util.propertycache
15 propertycache = util.propertycache
15
16
16 class changectx(object):
17 class changectx(object):
17 """A changecontext object makes access to data related to a particular
18 """A changecontext object makes access to data related to a particular
18 changeset convenient."""
19 changeset convenient."""
19 def __init__(self, repo, changeid=''):
20 def __init__(self, repo, changeid=''):
20 """changeid is a revision number, node, or tag"""
21 """changeid is a revision number, node, or tag"""
21 if changeid == '':
22 if changeid == '':
22 changeid = '.'
23 changeid = '.'
23 self._repo = repo
24 self._repo = repo
24
25
25 if isinstance(changeid, int):
26 if isinstance(changeid, int):
26 self._rev = changeid
27 self._rev = changeid
27 self._node = repo.changelog.node(changeid)
28 self._node = repo.changelog.node(changeid)
28 return
29 return
29 if changeid == '.':
30 if changeid == '.':
30 self._node = repo.dirstate.p1()
31 self._node = repo.dirstate.p1()
31 self._rev = repo.changelog.rev(self._node)
32 self._rev = repo.changelog.rev(self._node)
32 return
33 return
33 if changeid == 'null':
34 if changeid == 'null':
34 self._node = nullid
35 self._node = nullid
35 self._rev = nullrev
36 self._rev = nullrev
36 return
37 return
37 if changeid == 'tip':
38 if changeid == 'tip':
38 self._rev = len(repo.changelog) - 1
39 self._rev = len(repo.changelog) - 1
39 self._node = repo.changelog.node(self._rev)
40 self._node = repo.changelog.node(self._rev)
40 return
41 return
41 if len(changeid) == 20:
42 if len(changeid) == 20:
42 try:
43 try:
43 self._node = changeid
44 self._node = changeid
44 self._rev = repo.changelog.rev(changeid)
45 self._rev = repo.changelog.rev(changeid)
45 return
46 return
46 except LookupError:
47 except LookupError:
47 pass
48 pass
48
49
49 try:
50 try:
50 r = int(changeid)
51 r = int(changeid)
51 if str(r) != changeid:
52 if str(r) != changeid:
52 raise ValueError
53 raise ValueError
53 l = len(repo.changelog)
54 l = len(repo.changelog)
54 if r < 0:
55 if r < 0:
55 r += l
56 r += l
56 if r < 0 or r >= l:
57 if r < 0 or r >= l:
57 raise ValueError
58 raise ValueError
58 self._rev = r
59 self._rev = r
59 self._node = repo.changelog.node(r)
60 self._node = repo.changelog.node(r)
60 return
61 return
61 except (ValueError, OverflowError):
62 except (ValueError, OverflowError):
62 pass
63 pass
63
64
64 if len(changeid) == 40:
65 if len(changeid) == 40:
65 try:
66 try:
66 self._node = bin(changeid)
67 self._node = bin(changeid)
67 self._rev = repo.changelog.rev(self._node)
68 self._rev = repo.changelog.rev(self._node)
68 return
69 return
69 except (TypeError, LookupError):
70 except (TypeError, LookupError):
70 pass
71 pass
71
72
72 if changeid in repo._bookmarks:
73 if changeid in repo._bookmarks:
73 self._node = repo._bookmarks[changeid]
74 self._node = repo._bookmarks[changeid]
74 self._rev = repo.changelog.rev(self._node)
75 self._rev = repo.changelog.rev(self._node)
75 return
76 return
76 if changeid in repo._tagscache.tags:
77 if changeid in repo._tagscache.tags:
77 self._node = repo._tagscache.tags[changeid]
78 self._node = repo._tagscache.tags[changeid]
78 self._rev = repo.changelog.rev(self._node)
79 self._rev = repo.changelog.rev(self._node)
79 return
80 return
80 if changeid in repo.branchtags():
81 if changeid in repo.branchtags():
81 self._node = repo.branchtags()[changeid]
82 self._node = repo.branchtags()[changeid]
82 self._rev = repo.changelog.rev(self._node)
83 self._rev = repo.changelog.rev(self._node)
83 return
84 return
84
85
85 self._node = repo.changelog._partialmatch(changeid)
86 self._node = repo.changelog._partialmatch(changeid)
86 if self._node is not None:
87 if self._node is not None:
87 self._rev = repo.changelog.rev(self._node)
88 self._rev = repo.changelog.rev(self._node)
88 return
89 return
89
90
90 # lookup failed
91 # lookup failed
91 # check if it might have come from damaged dirstate
92 # check if it might have come from damaged dirstate
92 if changeid in repo.dirstate.parents():
93 if changeid in repo.dirstate.parents():
93 raise error.Abort(_("working directory has unknown parent '%s'!")
94 raise error.Abort(_("working directory has unknown parent '%s'!")
94 % short(changeid))
95 % short(changeid))
95 try:
96 try:
96 if len(changeid) == 20:
97 if len(changeid) == 20:
97 changeid = hex(changeid)
98 changeid = hex(changeid)
98 except TypeError:
99 except TypeError:
99 pass
100 pass
100 raise error.RepoLookupError(
101 raise error.RepoLookupError(
101 _("unknown revision '%s'") % changeid)
102 _("unknown revision '%s'") % changeid)
102
103
103 def __str__(self):
104 def __str__(self):
104 return short(self.node())
105 return short(self.node())
105
106
106 def __int__(self):
107 def __int__(self):
107 return self.rev()
108 return self.rev()
108
109
109 def __repr__(self):
110 def __repr__(self):
110 return "<changectx %s>" % str(self)
111 return "<changectx %s>" % str(self)
111
112
112 def __hash__(self):
113 def __hash__(self):
113 try:
114 try:
114 return hash(self._rev)
115 return hash(self._rev)
115 except AttributeError:
116 except AttributeError:
116 return id(self)
117 return id(self)
117
118
118 def __eq__(self, other):
119 def __eq__(self, other):
119 try:
120 try:
120 return self._rev == other._rev
121 return self._rev == other._rev
121 except AttributeError:
122 except AttributeError:
122 return False
123 return False
123
124
124 def __ne__(self, other):
125 def __ne__(self, other):
125 return not (self == other)
126 return not (self == other)
126
127
127 def __nonzero__(self):
128 def __nonzero__(self):
128 return self._rev != nullrev
129 return self._rev != nullrev
129
130
130 @propertycache
131 @propertycache
131 def _changeset(self):
132 def _changeset(self):
132 return self._repo.changelog.read(self.rev())
133 return self._repo.changelog.read(self.rev())
133
134
134 @propertycache
135 @propertycache
135 def _manifest(self):
136 def _manifest(self):
136 return self._repo.manifest.read(self._changeset[0])
137 return self._repo.manifest.read(self._changeset[0])
137
138
138 @propertycache
139 @propertycache
139 def _manifestdelta(self):
140 def _manifestdelta(self):
140 return self._repo.manifest.readdelta(self._changeset[0])
141 return self._repo.manifest.readdelta(self._changeset[0])
141
142
142 @propertycache
143 @propertycache
143 def _parents(self):
144 def _parents(self):
144 p = self._repo.changelog.parentrevs(self._rev)
145 p = self._repo.changelog.parentrevs(self._rev)
145 if p[1] == nullrev:
146 if p[1] == nullrev:
146 p = p[:-1]
147 p = p[:-1]
147 return [changectx(self._repo, x) for x in p]
148 return [changectx(self._repo, x) for x in p]
148
149
149 @propertycache
150 @propertycache
150 def substate(self):
151 def substate(self):
151 return subrepo.state(self, self._repo.ui)
152 return subrepo.state(self, self._repo.ui)
152
153
153 def __contains__(self, key):
154 def __contains__(self, key):
154 return key in self._manifest
155 return key in self._manifest
155
156
156 def __getitem__(self, key):
157 def __getitem__(self, key):
157 return self.filectx(key)
158 return self.filectx(key)
158
159
159 def __iter__(self):
160 def __iter__(self):
160 for f in sorted(self._manifest):
161 for f in sorted(self._manifest):
161 yield f
162 yield f
162
163
163 def changeset(self):
164 def changeset(self):
164 return self._changeset
165 return self._changeset
165 def manifest(self):
166 def manifest(self):
166 return self._manifest
167 return self._manifest
167 def manifestnode(self):
168 def manifestnode(self):
168 return self._changeset[0]
169 return self._changeset[0]
169
170
170 def rev(self):
171 def rev(self):
171 return self._rev
172 return self._rev
172 def node(self):
173 def node(self):
173 return self._node
174 return self._node
174 def hex(self):
175 def hex(self):
175 return hex(self._node)
176 return hex(self._node)
176 def user(self):
177 def user(self):
177 return self._changeset[1]
178 return self._changeset[1]
178 def date(self):
179 def date(self):
179 return self._changeset[2]
180 return self._changeset[2]
180 def files(self):
181 def files(self):
181 return self._changeset[3]
182 return self._changeset[3]
182 def description(self):
183 def description(self):
183 return self._changeset[4]
184 return self._changeset[4]
184 def branch(self):
185 def branch(self):
185 return encoding.tolocal(self._changeset[5].get("branch"))
186 return encoding.tolocal(self._changeset[5].get("branch"))
186 def extra(self):
187 def extra(self):
187 return self._changeset[5]
188 return self._changeset[5]
188 def tags(self):
189 def tags(self):
189 return self._repo.nodetags(self._node)
190 return self._repo.nodetags(self._node)
190 def bookmarks(self):
191 def bookmarks(self):
191 return self._repo.nodebookmarks(self._node)
192 return self._repo.nodebookmarks(self._node)
192 def phase(self):
193 def phase(self):
193 if self._rev == -1:
194 if self._rev == -1:
194 return phases.public
195 return phases.public
195 if self._rev >= len(self._repo._phaserev):
196 if self._rev >= len(self._repo._phaserev):
196 # outdated cache
197 # outdated cache
197 del self._repo._phaserev
198 del self._repo._phaserev
198 return self._repo._phaserev[self._rev]
199 return self._repo._phaserev[self._rev]
199 def phasestr(self):
200 def phasestr(self):
200 return phases.phasenames[self.phase()]
201 return phases.phasenames[self.phase()]
201 def mutable(self):
202 def mutable(self):
202 return self.phase() > phases.public
203 return self.phase() > phases.public
203 def hidden(self):
204 def hidden(self):
204 return self._rev in self._repo.changelog.hiddenrevs
205 return self._rev in self._repo.changelog.hiddenrevs
205
206
206 def parents(self):
207 def parents(self):
207 """return contexts for each parent changeset"""
208 """return contexts for each parent changeset"""
208 return self._parents
209 return self._parents
209
210
210 def p1(self):
211 def p1(self):
211 return self._parents[0]
212 return self._parents[0]
212
213
213 def p2(self):
214 def p2(self):
214 if len(self._parents) == 2:
215 if len(self._parents) == 2:
215 return self._parents[1]
216 return self._parents[1]
216 return changectx(self._repo, -1)
217 return changectx(self._repo, -1)
217
218
218 def children(self):
219 def children(self):
219 """return contexts for each child changeset"""
220 """return contexts for each child changeset"""
220 c = self._repo.changelog.children(self._node)
221 c = self._repo.changelog.children(self._node)
221 return [changectx(self._repo, x) for x in c]
222 return [changectx(self._repo, x) for x in c]
222
223
223 def ancestors(self):
224 def ancestors(self):
224 for a in self._repo.changelog.ancestors(self._rev):
225 for a in self._repo.changelog.ancestors(self._rev):
225 yield changectx(self._repo, a)
226 yield changectx(self._repo, a)
226
227
227 def descendants(self):
228 def descendants(self):
228 for d in self._repo.changelog.descendants(self._rev):
229 for d in self._repo.changelog.descendants(self._rev):
229 yield changectx(self._repo, d)
230 yield changectx(self._repo, d)
230
231
231 def _fileinfo(self, path):
232 def _fileinfo(self, path):
232 if '_manifest' in self.__dict__:
233 if '_manifest' in self.__dict__:
233 try:
234 try:
234 return self._manifest[path], self._manifest.flags(path)
235 return self._manifest[path], self._manifest.flags(path)
235 except KeyError:
236 except KeyError:
236 raise error.LookupError(self._node, path,
237 raise error.LookupError(self._node, path,
237 _('not found in manifest'))
238 _('not found in manifest'))
238 if '_manifestdelta' in self.__dict__ or path in self.files():
239 if '_manifestdelta' in self.__dict__ or path in self.files():
239 if path in self._manifestdelta:
240 if path in self._manifestdelta:
240 return self._manifestdelta[path], self._manifestdelta.flags(path)
241 return self._manifestdelta[path], self._manifestdelta.flags(path)
241 node, flag = self._repo.manifest.find(self._changeset[0], path)
242 node, flag = self._repo.manifest.find(self._changeset[0], path)
242 if not node:
243 if not node:
243 raise error.LookupError(self._node, path,
244 raise error.LookupError(self._node, path,
244 _('not found in manifest'))
245 _('not found in manifest'))
245
246
246 return node, flag
247 return node, flag
247
248
248 def filenode(self, path):
249 def filenode(self, path):
249 return self._fileinfo(path)[0]
250 return self._fileinfo(path)[0]
250
251
251 def flags(self, path):
252 def flags(self, path):
252 try:
253 try:
253 return self._fileinfo(path)[1]
254 return self._fileinfo(path)[1]
254 except error.LookupError:
255 except error.LookupError:
255 return ''
256 return ''
256
257
257 def filectx(self, path, fileid=None, filelog=None):
258 def filectx(self, path, fileid=None, filelog=None):
258 """get a file context from this changeset"""
259 """get a file context from this changeset"""
259 if fileid is None:
260 if fileid is None:
260 fileid = self.filenode(path)
261 fileid = self.filenode(path)
261 return filectx(self._repo, path, fileid=fileid,
262 return filectx(self._repo, path, fileid=fileid,
262 changectx=self, filelog=filelog)
263 changectx=self, filelog=filelog)
263
264
264 def ancestor(self, c2):
265 def ancestor(self, c2):
265 """
266 """
266 return the ancestor context of self and c2
267 return the ancestor context of self and c2
267 """
268 """
268 # deal with workingctxs
269 # deal with workingctxs
269 n2 = c2._node
270 n2 = c2._node
270 if n2 is None:
271 if n2 is None:
271 n2 = c2._parents[0]._node
272 n2 = c2._parents[0]._node
272 n = self._repo.changelog.ancestor(self._node, n2)
273 n = self._repo.changelog.ancestor(self._node, n2)
273 return changectx(self._repo, n)
274 return changectx(self._repo, n)
274
275
275 def walk(self, match):
276 def walk(self, match):
276 fset = set(match.files())
277 fset = set(match.files())
277 # for dirstate.walk, files=['.'] means "walk the whole tree".
278 # for dirstate.walk, files=['.'] means "walk the whole tree".
278 # follow that here, too
279 # follow that here, too
279 fset.discard('.')
280 fset.discard('.')
280 for fn in self:
281 for fn in self:
281 if fn in fset:
282 if fn in fset:
282 # specified pattern is the exact name
283 # specified pattern is the exact name
283 fset.remove(fn)
284 fset.remove(fn)
284 if match(fn):
285 if match(fn):
285 yield fn
286 yield fn
286 for fn in sorted(fset):
287 for fn in sorted(fset):
287 if fn in self._dirs:
288 if fn in self._dirs:
288 # specified pattern is a directory
289 # specified pattern is a directory
289 continue
290 continue
290 if match.bad(fn, _('no such file in rev %s') % self) and match(fn):
291 if match.bad(fn, _('no such file in rev %s') % self) and match(fn):
291 yield fn
292 yield fn
292
293
293 def sub(self, path):
294 def sub(self, path):
294 return subrepo.subrepo(self, path)
295 return subrepo.subrepo(self, path)
295
296
296 def match(self, pats=[], include=None, exclude=None, default='glob'):
297 def match(self, pats=[], include=None, exclude=None, default='glob'):
297 r = self._repo
298 r = self._repo
298 return matchmod.match(r.root, r.getcwd(), pats,
299 return matchmod.match(r.root, r.getcwd(), pats,
299 include, exclude, default,
300 include, exclude, default,
300 auditor=r.auditor, ctx=self)
301 auditor=r.auditor, ctx=self)
301
302
302 def diff(self, ctx2=None, match=None, **opts):
303 def diff(self, ctx2=None, match=None, **opts):
303 """Returns a diff generator for the given contexts and matcher"""
304 """Returns a diff generator for the given contexts and matcher"""
304 if ctx2 is None:
305 if ctx2 is None:
305 ctx2 = self.p1()
306 ctx2 = self.p1()
306 if ctx2 is not None and not isinstance(ctx2, changectx):
307 if ctx2 is not None and not isinstance(ctx2, changectx):
307 ctx2 = self._repo[ctx2]
308 ctx2 = self._repo[ctx2]
308 diffopts = patch.diffopts(self._repo.ui, opts)
309 diffopts = patch.diffopts(self._repo.ui, opts)
309 return patch.diff(self._repo, ctx2.node(), self.node(),
310 return patch.diff(self._repo, ctx2.node(), self.node(),
310 match=match, opts=diffopts)
311 match=match, opts=diffopts)
311
312
312 @propertycache
313 @propertycache
313 def _dirs(self):
314 def _dirs(self):
314 dirs = set()
315 dirs = set()
315 for f in self._manifest:
316 for f in self._manifest:
316 pos = f.rfind('/')
317 pos = f.rfind('/')
317 while pos != -1:
318 while pos != -1:
318 f = f[:pos]
319 f = f[:pos]
319 if f in dirs:
320 if f in dirs:
320 break # dirs already contains this and above
321 break # dirs already contains this and above
321 dirs.add(f)
322 dirs.add(f)
322 pos = f.rfind('/')
323 pos = f.rfind('/')
323 return dirs
324 return dirs
324
325
325 def dirs(self):
326 def dirs(self):
326 return self._dirs
327 return self._dirs
327
328
328 class filectx(object):
329 class filectx(object):
329 """A filecontext object makes access to data related to a particular
330 """A filecontext object makes access to data related to a particular
330 filerevision convenient."""
331 filerevision convenient."""
331 def __init__(self, repo, path, changeid=None, fileid=None,
332 def __init__(self, repo, path, changeid=None, fileid=None,
332 filelog=None, changectx=None):
333 filelog=None, changectx=None):
333 """changeid can be a changeset revision, node, or tag.
334 """changeid can be a changeset revision, node, or tag.
334 fileid can be a file revision or node."""
335 fileid can be a file revision or node."""
335 self._repo = repo
336 self._repo = repo
336 self._path = path
337 self._path = path
337
338
338 assert (changeid is not None
339 assert (changeid is not None
339 or fileid is not None
340 or fileid is not None
340 or changectx is not None), \
341 or changectx is not None), \
341 ("bad args: changeid=%r, fileid=%r, changectx=%r"
342 ("bad args: changeid=%r, fileid=%r, changectx=%r"
342 % (changeid, fileid, changectx))
343 % (changeid, fileid, changectx))
343
344
344 if filelog:
345 if filelog:
345 self._filelog = filelog
346 self._filelog = filelog
346
347
347 if changeid is not None:
348 if changeid is not None:
348 self._changeid = changeid
349 self._changeid = changeid
349 if changectx is not None:
350 if changectx is not None:
350 self._changectx = changectx
351 self._changectx = changectx
351 if fileid is not None:
352 if fileid is not None:
352 self._fileid = fileid
353 self._fileid = fileid
353
354
354 @propertycache
355 @propertycache
355 def _changectx(self):
356 def _changectx(self):
356 return changectx(self._repo, self._changeid)
357 return changectx(self._repo, self._changeid)
357
358
358 @propertycache
359 @propertycache
359 def _filelog(self):
360 def _filelog(self):
360 return self._repo.file(self._path)
361 return self._repo.file(self._path)
361
362
362 @propertycache
363 @propertycache
363 def _changeid(self):
364 def _changeid(self):
364 if '_changectx' in self.__dict__:
365 if '_changectx' in self.__dict__:
365 return self._changectx.rev()
366 return self._changectx.rev()
366 else:
367 else:
367 return self._filelog.linkrev(self._filerev)
368 return self._filelog.linkrev(self._filerev)
368
369
369 @propertycache
370 @propertycache
370 def _filenode(self):
371 def _filenode(self):
371 if '_fileid' in self.__dict__:
372 if '_fileid' in self.__dict__:
372 return self._filelog.lookup(self._fileid)
373 return self._filelog.lookup(self._fileid)
373 else:
374 else:
374 return self._changectx.filenode(self._path)
375 return self._changectx.filenode(self._path)
375
376
376 @propertycache
377 @propertycache
377 def _filerev(self):
378 def _filerev(self):
378 return self._filelog.rev(self._filenode)
379 return self._filelog.rev(self._filenode)
379
380
380 @propertycache
381 @propertycache
381 def _repopath(self):
382 def _repopath(self):
382 return self._path
383 return self._path
383
384
384 def __nonzero__(self):
385 def __nonzero__(self):
385 try:
386 try:
386 self._filenode
387 self._filenode
387 return True
388 return True
388 except error.LookupError:
389 except error.LookupError:
389 # file is missing
390 # file is missing
390 return False
391 return False
391
392
392 def __str__(self):
393 def __str__(self):
393 return "%s@%s" % (self.path(), short(self.node()))
394 return "%s@%s" % (self.path(), short(self.node()))
394
395
395 def __repr__(self):
396 def __repr__(self):
396 return "<filectx %s>" % str(self)
397 return "<filectx %s>" % str(self)
397
398
398 def __hash__(self):
399 def __hash__(self):
399 try:
400 try:
400 return hash((self._path, self._filenode))
401 return hash((self._path, self._filenode))
401 except AttributeError:
402 except AttributeError:
402 return id(self)
403 return id(self)
403
404
404 def __eq__(self, other):
405 def __eq__(self, other):
405 try:
406 try:
406 return (self._path == other._path
407 return (self._path == other._path
407 and self._filenode == other._filenode)
408 and self._filenode == other._filenode)
408 except AttributeError:
409 except AttributeError:
409 return False
410 return False
410
411
411 def __ne__(self, other):
412 def __ne__(self, other):
412 return not (self == other)
413 return not (self == other)
413
414
414 def filectx(self, fileid):
415 def filectx(self, fileid):
415 '''opens an arbitrary revision of the file without
416 '''opens an arbitrary revision of the file without
416 opening a new filelog'''
417 opening a new filelog'''
417 return filectx(self._repo, self._path, fileid=fileid,
418 return filectx(self._repo, self._path, fileid=fileid,
418 filelog=self._filelog)
419 filelog=self._filelog)
419
420
420 def filerev(self):
421 def filerev(self):
421 return self._filerev
422 return self._filerev
422 def filenode(self):
423 def filenode(self):
423 return self._filenode
424 return self._filenode
424 def flags(self):
425 def flags(self):
425 return self._changectx.flags(self._path)
426 return self._changectx.flags(self._path)
426 def filelog(self):
427 def filelog(self):
427 return self._filelog
428 return self._filelog
428
429
429 def rev(self):
430 def rev(self):
430 if '_changectx' in self.__dict__:
431 if '_changectx' in self.__dict__:
431 return self._changectx.rev()
432 return self._changectx.rev()
432 if '_changeid' in self.__dict__:
433 if '_changeid' in self.__dict__:
433 return self._changectx.rev()
434 return self._changectx.rev()
434 return self._filelog.linkrev(self._filerev)
435 return self._filelog.linkrev(self._filerev)
435
436
436 def linkrev(self):
437 def linkrev(self):
437 return self._filelog.linkrev(self._filerev)
438 return self._filelog.linkrev(self._filerev)
438 def node(self):
439 def node(self):
439 return self._changectx.node()
440 return self._changectx.node()
440 def hex(self):
441 def hex(self):
441 return hex(self.node())
442 return hex(self.node())
442 def user(self):
443 def user(self):
443 return self._changectx.user()
444 return self._changectx.user()
444 def date(self):
445 def date(self):
445 return self._changectx.date()
446 return self._changectx.date()
446 def files(self):
447 def files(self):
447 return self._changectx.files()
448 return self._changectx.files()
448 def description(self):
449 def description(self):
449 return self._changectx.description()
450 return self._changectx.description()
450 def branch(self):
451 def branch(self):
451 return self._changectx.branch()
452 return self._changectx.branch()
452 def extra(self):
453 def extra(self):
453 return self._changectx.extra()
454 return self._changectx.extra()
454 def manifest(self):
455 def manifest(self):
455 return self._changectx.manifest()
456 return self._changectx.manifest()
456 def changectx(self):
457 def changectx(self):
457 return self._changectx
458 return self._changectx
458
459
459 def data(self):
460 def data(self):
460 return self._filelog.read(self._filenode)
461 return self._filelog.read(self._filenode)
461 def path(self):
462 def path(self):
462 return self._path
463 return self._path
463 def size(self):
464 def size(self):
464 return self._filelog.size(self._filerev)
465 return self._filelog.size(self._filerev)
465
466
466 def isbinary(self):
467 def isbinary(self):
467 try:
468 try:
468 return util.binary(self.data())
469 return util.binary(self.data())
469 except IOError:
470 except IOError:
470 return False
471 return False
471
472
472 def cmp(self, fctx):
473 def cmp(self, fctx):
473 """compare with other file context
474 """compare with other file context
474
475
475 returns True if different than fctx.
476 returns True if different than fctx.
476 """
477 """
477 if (fctx._filerev is None
478 if (fctx._filerev is None
478 and (self._repo._encodefilterpats
479 and (self._repo._encodefilterpats
479 # if file data starts with '\1\n', empty metadata block is
480 # if file data starts with '\1\n', empty metadata block is
480 # prepended, which adds 4 bytes to filelog.size().
481 # prepended, which adds 4 bytes to filelog.size().
481 or self.size() - 4 == fctx.size())
482 or self.size() - 4 == fctx.size())
482 or self.size() == fctx.size()):
483 or self.size() == fctx.size()):
483 return self._filelog.cmp(self._filenode, fctx.data())
484 return self._filelog.cmp(self._filenode, fctx.data())
484
485
485 return True
486 return True
486
487
487 def renamed(self):
488 def renamed(self):
488 """check if file was actually renamed in this changeset revision
489 """check if file was actually renamed in this changeset revision
489
490
490 If rename logged in file revision, we report copy for changeset only
491 If rename logged in file revision, we report copy for changeset only
491 if file revisions linkrev points back to the changeset in question
492 if file revisions linkrev points back to the changeset in question
492 or both changeset parents contain different file revisions.
493 or both changeset parents contain different file revisions.
493 """
494 """
494
495
495 renamed = self._filelog.renamed(self._filenode)
496 renamed = self._filelog.renamed(self._filenode)
496 if not renamed:
497 if not renamed:
497 return renamed
498 return renamed
498
499
499 if self.rev() == self.linkrev():
500 if self.rev() == self.linkrev():
500 return renamed
501 return renamed
501
502
502 name = self.path()
503 name = self.path()
503 fnode = self._filenode
504 fnode = self._filenode
504 for p in self._changectx.parents():
505 for p in self._changectx.parents():
505 try:
506 try:
506 if fnode == p.filenode(name):
507 if fnode == p.filenode(name):
507 return None
508 return None
508 except error.LookupError:
509 except error.LookupError:
509 pass
510 pass
510 return renamed
511 return renamed
511
512
512 def parents(self):
513 def parents(self):
513 p = self._path
514 p = self._path
514 fl = self._filelog
515 fl = self._filelog
515 pl = [(p, n, fl) for n in self._filelog.parents(self._filenode)]
516 pl = [(p, n, fl) for n in self._filelog.parents(self._filenode)]
516
517
517 r = self._filelog.renamed(self._filenode)
518 r = self._filelog.renamed(self._filenode)
518 if r:
519 if r:
519 pl[0] = (r[0], r[1], None)
520 pl[0] = (r[0], r[1], None)
520
521
521 return [filectx(self._repo, p, fileid=n, filelog=l)
522 return [filectx(self._repo, p, fileid=n, filelog=l)
522 for p, n, l in pl if n != nullid]
523 for p, n, l in pl if n != nullid]
523
524
524 def p1(self):
525 def p1(self):
525 return self.parents()[0]
526 return self.parents()[0]
526
527
527 def p2(self):
528 def p2(self):
528 p = self.parents()
529 p = self.parents()
529 if len(p) == 2:
530 if len(p) == 2:
530 return p[1]
531 return p[1]
531 return filectx(self._repo, self._path, fileid=-1, filelog=self._filelog)
532 return filectx(self._repo, self._path, fileid=-1, filelog=self._filelog)
532
533
533 def children(self):
534 def children(self):
534 # hard for renames
535 # hard for renames
535 c = self._filelog.children(self._filenode)
536 c = self._filelog.children(self._filenode)
536 return [filectx(self._repo, self._path, fileid=x,
537 return [filectx(self._repo, self._path, fileid=x,
537 filelog=self._filelog) for x in c]
538 filelog=self._filelog) for x in c]
538
539
539 def annotate(self, follow=False, linenumber=None, diffopts=None):
540 def annotate(self, follow=False, linenumber=None, diffopts=None):
540 '''returns a list of tuples of (ctx, line) for each line
541 '''returns a list of tuples of (ctx, line) for each line
541 in the file, where ctx is the filectx of the node where
542 in the file, where ctx is the filectx of the node where
542 that line was last changed.
543 that line was last changed.
543 This returns tuples of ((ctx, linenumber), line) for each line,
544 This returns tuples of ((ctx, linenumber), line) for each line,
544 if "linenumber" parameter is NOT "None".
545 if "linenumber" parameter is NOT "None".
545 In such tuples, linenumber means one at the first appearance
546 In such tuples, linenumber means one at the first appearance
546 in the managed file.
547 in the managed file.
547 To reduce annotation cost,
548 To reduce annotation cost,
548 this returns fixed value(False is used) as linenumber,
549 this returns fixed value(False is used) as linenumber,
549 if "linenumber" parameter is "False".'''
550 if "linenumber" parameter is "False".'''
550
551
551 def decorate_compat(text, rev):
552 def decorate_compat(text, rev):
552 return ([rev] * len(text.splitlines()), text)
553 return ([rev] * len(text.splitlines()), text)
553
554
554 def without_linenumber(text, rev):
555 def without_linenumber(text, rev):
555 return ([(rev, False)] * len(text.splitlines()), text)
556 return ([(rev, False)] * len(text.splitlines()), text)
556
557
557 def with_linenumber(text, rev):
558 def with_linenumber(text, rev):
558 size = len(text.splitlines())
559 size = len(text.splitlines())
559 return ([(rev, i) for i in xrange(1, size + 1)], text)
560 return ([(rev, i) for i in xrange(1, size + 1)], text)
560
561
561 decorate = (((linenumber is None) and decorate_compat) or
562 decorate = (((linenumber is None) and decorate_compat) or
562 (linenumber and with_linenumber) or
563 (linenumber and with_linenumber) or
563 without_linenumber)
564 without_linenumber)
564
565
565 def pair(parent, child):
566 def pair(parent, child):
566 blocks = mdiff.allblocks(parent[1], child[1], opts=diffopts,
567 blocks = mdiff.allblocks(parent[1], child[1], opts=diffopts,
567 refine=True)
568 refine=True)
568 for (a1, a2, b1, b2), t in blocks:
569 for (a1, a2, b1, b2), t in blocks:
569 # Changed blocks ('!') or blocks made only of blank lines ('~')
570 # Changed blocks ('!') or blocks made only of blank lines ('~')
570 # belong to the child.
571 # belong to the child.
571 if t == '=':
572 if t == '=':
572 child[0][b1:b2] = parent[0][a1:a2]
573 child[0][b1:b2] = parent[0][a1:a2]
573 return child
574 return child
574
575
575 getlog = util.lrucachefunc(lambda x: self._repo.file(x))
576 getlog = util.lrucachefunc(lambda x: self._repo.file(x))
576 def getctx(path, fileid):
577 def getctx(path, fileid):
577 log = path == self._path and self._filelog or getlog(path)
578 log = path == self._path and self._filelog or getlog(path)
578 return filectx(self._repo, path, fileid=fileid, filelog=log)
579 return filectx(self._repo, path, fileid=fileid, filelog=log)
579 getctx = util.lrucachefunc(getctx)
580 getctx = util.lrucachefunc(getctx)
580
581
581 def parents(f):
582 def parents(f):
582 # we want to reuse filectx objects as much as possible
583 # we want to reuse filectx objects as much as possible
583 p = f._path
584 p = f._path
584 if f._filerev is None: # working dir
585 if f._filerev is None: # working dir
585 pl = [(n.path(), n.filerev()) for n in f.parents()]
586 pl = [(n.path(), n.filerev()) for n in f.parents()]
586 else:
587 else:
587 pl = [(p, n) for n in f._filelog.parentrevs(f._filerev)]
588 pl = [(p, n) for n in f._filelog.parentrevs(f._filerev)]
588
589
589 if follow:
590 if follow:
590 r = f.renamed()
591 r = f.renamed()
591 if r:
592 if r:
592 pl[0] = (r[0], getlog(r[0]).rev(r[1]))
593 pl[0] = (r[0], getlog(r[0]).rev(r[1]))
593
594
594 return [getctx(p, n) for p, n in pl if n != nullrev]
595 return [getctx(p, n) for p, n in pl if n != nullrev]
595
596
596 # use linkrev to find the first changeset where self appeared
597 # use linkrev to find the first changeset where self appeared
597 if self.rev() != self.linkrev():
598 if self.rev() != self.linkrev():
598 base = self.filectx(self.filerev())
599 base = self.filectx(self.filerev())
599 else:
600 else:
600 base = self
601 base = self
601
602
602 # This algorithm would prefer to be recursive, but Python is a
603 # This algorithm would prefer to be recursive, but Python is a
603 # bit recursion-hostile. Instead we do an iterative
604 # bit recursion-hostile. Instead we do an iterative
604 # depth-first search.
605 # depth-first search.
605
606
606 visit = [base]
607 visit = [base]
607 hist = {}
608 hist = {}
608 pcache = {}
609 pcache = {}
609 needed = {base: 1}
610 needed = {base: 1}
610 while visit:
611 while visit:
611 f = visit[-1]
612 f = visit[-1]
612 if f not in pcache:
613 if f not in pcache:
613 pcache[f] = parents(f)
614 pcache[f] = parents(f)
614
615
615 ready = True
616 ready = True
616 pl = pcache[f]
617 pl = pcache[f]
617 for p in pl:
618 for p in pl:
618 if p not in hist:
619 if p not in hist:
619 ready = False
620 ready = False
620 visit.append(p)
621 visit.append(p)
621 needed[p] = needed.get(p, 0) + 1
622 needed[p] = needed.get(p, 0) + 1
622 if ready:
623 if ready:
623 visit.pop()
624 visit.pop()
624 curr = decorate(f.data(), f)
625 curr = decorate(f.data(), f)
625 for p in pl:
626 for p in pl:
626 curr = pair(hist[p], curr)
627 curr = pair(hist[p], curr)
627 if needed[p] == 1:
628 if needed[p] == 1:
628 del hist[p]
629 del hist[p]
629 else:
630 else:
630 needed[p] -= 1
631 needed[p] -= 1
631
632
632 hist[f] = curr
633 hist[f] = curr
633 pcache[f] = []
634 pcache[f] = []
634
635
635 return zip(hist[base][0], hist[base][1].splitlines(True))
636 return zip(hist[base][0], hist[base][1].splitlines(True))
636
637
637 def ancestor(self, fc2, actx):
638 def ancestor(self, fc2, actx):
638 """
639 """
639 find the common ancestor file context, if any, of self, and fc2
640 find the common ancestor file context, if any, of self, and fc2
640
641
641 actx must be the changectx of the common ancestor
642 actx must be the changectx of the common ancestor
642 of self's and fc2's respective changesets.
643 of self's and fc2's respective changesets.
643 """
644 """
644
645
645 # the easy case: no (relevant) renames
646 # the easy case: no (relevant) renames
646 if fc2.path() == self.path() and self.path() in actx:
647 if fc2.path() == self.path() and self.path() in actx:
647 return actx[self.path()]
648 return actx[self.path()]
648
649
649 # the next easiest cases: unambiguous predecessor (name trumps
650 # the next easiest cases: unambiguous predecessor (name trumps
650 # history)
651 # history)
651 if self.path() in actx and fc2.path() not in actx:
652 if self.path() in actx and fc2.path() not in actx:
652 return actx[self.path()]
653 return actx[self.path()]
653 if fc2.path() in actx and self.path() not in actx:
654 if fc2.path() in actx and self.path() not in actx:
654 return actx[fc2.path()]
655 return actx[fc2.path()]
655
656
656 # prime the ancestor cache for the working directory
657 # prime the ancestor cache for the working directory
657 acache = {}
658 acache = {}
658 for c in (self, fc2):
659 for c in (self, fc2):
659 if c._filerev is None:
660 if c._filerev is None:
660 pl = [(n.path(), n.filenode()) for n in c.parents()]
661 pl = [(n.path(), n.filenode()) for n in c.parents()]
661 acache[(c._path, None)] = pl
662 acache[(c._path, None)] = pl
662
663
663 flcache = {self._repopath:self._filelog, fc2._repopath:fc2._filelog}
664 flcache = {self._repopath:self._filelog, fc2._repopath:fc2._filelog}
664 def parents(vertex):
665 def parents(vertex):
665 if vertex in acache:
666 if vertex in acache:
666 return acache[vertex]
667 return acache[vertex]
667 f, n = vertex
668 f, n = vertex
668 if f not in flcache:
669 if f not in flcache:
669 flcache[f] = self._repo.file(f)
670 flcache[f] = self._repo.file(f)
670 fl = flcache[f]
671 fl = flcache[f]
671 pl = [(f, p) for p in fl.parents(n) if p != nullid]
672 pl = [(f, p) for p in fl.parents(n) if p != nullid]
672 re = fl.renamed(n)
673 re = fl.renamed(n)
673 if re:
674 if re:
674 pl.append(re)
675 pl.append(re)
675 acache[vertex] = pl
676 acache[vertex] = pl
676 return pl
677 return pl
677
678
678 a, b = (self._path, self._filenode), (fc2._path, fc2._filenode)
679 a, b = (self._path, self._filenode), (fc2._path, fc2._filenode)
679 v = ancestor.ancestor(a, b, parents)
680 v = ancestor.ancestor(a, b, parents)
680 if v:
681 if v:
681 f, n = v
682 f, n = v
682 return filectx(self._repo, f, fileid=n, filelog=flcache[f])
683 return filectx(self._repo, f, fileid=n, filelog=flcache[f])
683
684
684 return None
685 return None
685
686
686 def ancestors(self, followfirst=False):
687 def ancestors(self, followfirst=False):
687 visit = {}
688 visit = {}
688 c = self
689 c = self
689 cut = followfirst and 1 or None
690 cut = followfirst and 1 or None
690 while True:
691 while True:
691 for parent in c.parents()[:cut]:
692 for parent in c.parents()[:cut]:
692 visit[(parent.rev(), parent.node())] = parent
693 visit[(parent.rev(), parent.node())] = parent
693 if not visit:
694 if not visit:
694 break
695 break
695 c = visit.pop(max(visit))
696 c = visit.pop(max(visit))
696 yield c
697 yield c
697
698
699 def copies(self, c2):
700 if not util.hasattr(self, "_copycache"):
701 self._copycache = {}
702 sc2 = str(c2)
703 if sc2 not in self._copycache:
704 self._copycache[sc2] = copies.pathcopies(c2)
705 return self._copycache[sc2]
706
698 class workingctx(changectx):
707 class workingctx(changectx):
699 """A workingctx object makes access to data related to
708 """A workingctx object makes access to data related to
700 the current working directory convenient.
709 the current working directory convenient.
701 date - any valid date string or (unixtime, offset), or None.
710 date - any valid date string or (unixtime, offset), or None.
702 user - username string, or None.
711 user - username string, or None.
703 extra - a dictionary of extra values, or None.
712 extra - a dictionary of extra values, or None.
704 changes - a list of file lists as returned by localrepo.status()
713 changes - a list of file lists as returned by localrepo.status()
705 or None to use the repository status.
714 or None to use the repository status.
706 """
715 """
707 def __init__(self, repo, text="", user=None, date=None, extra=None,
716 def __init__(self, repo, text="", user=None, date=None, extra=None,
708 changes=None):
717 changes=None):
709 self._repo = repo
718 self._repo = repo
710 self._rev = None
719 self._rev = None
711 self._node = None
720 self._node = None
712 self._text = text
721 self._text = text
713 if date:
722 if date:
714 self._date = util.parsedate(date)
723 self._date = util.parsedate(date)
715 if user:
724 if user:
716 self._user = user
725 self._user = user
717 if changes:
726 if changes:
718 self._status = list(changes[:4])
727 self._status = list(changes[:4])
719 self._unknown = changes[4]
728 self._unknown = changes[4]
720 self._ignored = changes[5]
729 self._ignored = changes[5]
721 self._clean = changes[6]
730 self._clean = changes[6]
722 else:
731 else:
723 self._unknown = None
732 self._unknown = None
724 self._ignored = None
733 self._ignored = None
725 self._clean = None
734 self._clean = None
726
735
727 self._extra = {}
736 self._extra = {}
728 if extra:
737 if extra:
729 self._extra = extra.copy()
738 self._extra = extra.copy()
730 if 'branch' not in self._extra:
739 if 'branch' not in self._extra:
731 try:
740 try:
732 branch = encoding.fromlocal(self._repo.dirstate.branch())
741 branch = encoding.fromlocal(self._repo.dirstate.branch())
733 except UnicodeDecodeError:
742 except UnicodeDecodeError:
734 raise util.Abort(_('branch name not in UTF-8!'))
743 raise util.Abort(_('branch name not in UTF-8!'))
735 self._extra['branch'] = branch
744 self._extra['branch'] = branch
736 if self._extra['branch'] == '':
745 if self._extra['branch'] == '':
737 self._extra['branch'] = 'default'
746 self._extra['branch'] = 'default'
738
747
739 def __str__(self):
748 def __str__(self):
740 return str(self._parents[0]) + "+"
749 return str(self._parents[0]) + "+"
741
750
742 def __repr__(self):
751 def __repr__(self):
743 return "<workingctx %s>" % str(self)
752 return "<workingctx %s>" % str(self)
744
753
745 def __nonzero__(self):
754 def __nonzero__(self):
746 return True
755 return True
747
756
748 def __contains__(self, key):
757 def __contains__(self, key):
749 return self._repo.dirstate[key] not in "?r"
758 return self._repo.dirstate[key] not in "?r"
750
759
751 def _buildflagfunc(self):
760 def _buildflagfunc(self):
752 # Create a fallback function for getting file flags when the
761 # Create a fallback function for getting file flags when the
753 # filesystem doesn't support them
762 # filesystem doesn't support them
754
763
755 copiesget = self._repo.dirstate.copies().get
764 copiesget = self._repo.dirstate.copies().get
756
765
757 if len(self._parents) < 2:
766 if len(self._parents) < 2:
758 # when we have one parent, it's easy: copy from parent
767 # when we have one parent, it's easy: copy from parent
759 man = self._parents[0].manifest()
768 man = self._parents[0].manifest()
760 def func(f):
769 def func(f):
761 f = copiesget(f, f)
770 f = copiesget(f, f)
762 return man.flags(f)
771 return man.flags(f)
763 else:
772 else:
764 # merges are tricky: we try to reconstruct the unstored
773 # merges are tricky: we try to reconstruct the unstored
765 # result from the merge (issue1802)
774 # result from the merge (issue1802)
766 p1, p2 = self._parents
775 p1, p2 = self._parents
767 pa = p1.ancestor(p2)
776 pa = p1.ancestor(p2)
768 m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest()
777 m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest()
769
778
770 def func(f):
779 def func(f):
771 f = copiesget(f, f) # may be wrong for merges with copies
780 f = copiesget(f, f) # may be wrong for merges with copies
772 fl1, fl2, fla = m1.flags(f), m2.flags(f), ma.flags(f)
781 fl1, fl2, fla = m1.flags(f), m2.flags(f), ma.flags(f)
773 if fl1 == fl2:
782 if fl1 == fl2:
774 return fl1
783 return fl1
775 if fl1 == fla:
784 if fl1 == fla:
776 return fl2
785 return fl2
777 if fl2 == fla:
786 if fl2 == fla:
778 return fl1
787 return fl1
779 return '' # punt for conflicts
788 return '' # punt for conflicts
780
789
781 return func
790 return func
782
791
783 @propertycache
792 @propertycache
784 def _flagfunc(self):
793 def _flagfunc(self):
785 return self._repo.dirstate.flagfunc(self._buildflagfunc)
794 return self._repo.dirstate.flagfunc(self._buildflagfunc)
786
795
787 @propertycache
796 @propertycache
788 def _manifest(self):
797 def _manifest(self):
789 """generate a manifest corresponding to the working directory"""
798 """generate a manifest corresponding to the working directory"""
790
799
791 man = self._parents[0].manifest().copy()
800 man = self._parents[0].manifest().copy()
792 if len(self._parents) > 1:
801 if len(self._parents) > 1:
793 man2 = self.p2().manifest()
802 man2 = self.p2().manifest()
794 def getman(f):
803 def getman(f):
795 if f in man:
804 if f in man:
796 return man
805 return man
797 return man2
806 return man2
798 else:
807 else:
799 getman = lambda f: man
808 getman = lambda f: man
800
809
801 copied = self._repo.dirstate.copies()
810 copied = self._repo.dirstate.copies()
802 ff = self._flagfunc
811 ff = self._flagfunc
803 modified, added, removed, deleted = self._status
812 modified, added, removed, deleted = self._status
804 for i, l in (("a", added), ("m", modified)):
813 for i, l in (("a", added), ("m", modified)):
805 for f in l:
814 for f in l:
806 orig = copied.get(f, f)
815 orig = copied.get(f, f)
807 man[f] = getman(orig).get(orig, nullid) + i
816 man[f] = getman(orig).get(orig, nullid) + i
808 try:
817 try:
809 man.set(f, ff(f))
818 man.set(f, ff(f))
810 except OSError:
819 except OSError:
811 pass
820 pass
812
821
813 for f in deleted + removed:
822 for f in deleted + removed:
814 if f in man:
823 if f in man:
815 del man[f]
824 del man[f]
816
825
817 return man
826 return man
818
827
819 def __iter__(self):
828 def __iter__(self):
820 d = self._repo.dirstate
829 d = self._repo.dirstate
821 for f in d:
830 for f in d:
822 if d[f] != 'r':
831 if d[f] != 'r':
823 yield f
832 yield f
824
833
825 @propertycache
834 @propertycache
826 def _status(self):
835 def _status(self):
827 return self._repo.status()[:4]
836 return self._repo.status()[:4]
828
837
829 @propertycache
838 @propertycache
830 def _user(self):
839 def _user(self):
831 return self._repo.ui.username()
840 return self._repo.ui.username()
832
841
833 @propertycache
842 @propertycache
834 def _date(self):
843 def _date(self):
835 return util.makedate()
844 return util.makedate()
836
845
837 @propertycache
846 @propertycache
838 def _parents(self):
847 def _parents(self):
839 p = self._repo.dirstate.parents()
848 p = self._repo.dirstate.parents()
840 if p[1] == nullid:
849 if p[1] == nullid:
841 p = p[:-1]
850 p = p[:-1]
842 self._parents = [changectx(self._repo, x) for x in p]
851 self._parents = [changectx(self._repo, x) for x in p]
843 return self._parents
852 return self._parents
844
853
845 def status(self, ignored=False, clean=False, unknown=False):
854 def status(self, ignored=False, clean=False, unknown=False):
846 """Explicit status query
855 """Explicit status query
847 Unless this method is used to query the working copy status, the
856 Unless this method is used to query the working copy status, the
848 _status property will implicitly read the status using its default
857 _status property will implicitly read the status using its default
849 arguments."""
858 arguments."""
850 stat = self._repo.status(ignored=ignored, clean=clean, unknown=unknown)
859 stat = self._repo.status(ignored=ignored, clean=clean, unknown=unknown)
851 self._unknown = self._ignored = self._clean = None
860 self._unknown = self._ignored = self._clean = None
852 if unknown:
861 if unknown:
853 self._unknown = stat[4]
862 self._unknown = stat[4]
854 if ignored:
863 if ignored:
855 self._ignored = stat[5]
864 self._ignored = stat[5]
856 if clean:
865 if clean:
857 self._clean = stat[6]
866 self._clean = stat[6]
858 self._status = stat[:4]
867 self._status = stat[:4]
859 return stat
868 return stat
860
869
861 def manifest(self):
870 def manifest(self):
862 return self._manifest
871 return self._manifest
863 def user(self):
872 def user(self):
864 return self._user or self._repo.ui.username()
873 return self._user or self._repo.ui.username()
865 def date(self):
874 def date(self):
866 return self._date
875 return self._date
867 def description(self):
876 def description(self):
868 return self._text
877 return self._text
869 def files(self):
878 def files(self):
870 return sorted(self._status[0] + self._status[1] + self._status[2])
879 return sorted(self._status[0] + self._status[1] + self._status[2])
871
880
872 def modified(self):
881 def modified(self):
873 return self._status[0]
882 return self._status[0]
874 def added(self):
883 def added(self):
875 return self._status[1]
884 return self._status[1]
876 def removed(self):
885 def removed(self):
877 return self._status[2]
886 return self._status[2]
878 def deleted(self):
887 def deleted(self):
879 return self._status[3]
888 return self._status[3]
880 def unknown(self):
889 def unknown(self):
881 assert self._unknown is not None # must call status first
890 assert self._unknown is not None # must call status first
882 return self._unknown
891 return self._unknown
883 def ignored(self):
892 def ignored(self):
884 assert self._ignored is not None # must call status first
893 assert self._ignored is not None # must call status first
885 return self._ignored
894 return self._ignored
886 def clean(self):
895 def clean(self):
887 assert self._clean is not None # must call status first
896 assert self._clean is not None # must call status first
888 return self._clean
897 return self._clean
889 def branch(self):
898 def branch(self):
890 return encoding.tolocal(self._extra['branch'])
899 return encoding.tolocal(self._extra['branch'])
891 def extra(self):
900 def extra(self):
892 return self._extra
901 return self._extra
893
902
894 def tags(self):
903 def tags(self):
895 t = []
904 t = []
896 for p in self.parents():
905 for p in self.parents():
897 t.extend(p.tags())
906 t.extend(p.tags())
898 return t
907 return t
899
908
900 def bookmarks(self):
909 def bookmarks(self):
901 b = []
910 b = []
902 for p in self.parents():
911 for p in self.parents():
903 b.extend(p.bookmarks())
912 b.extend(p.bookmarks())
904 return b
913 return b
905
914
906 def phase(self):
915 def phase(self):
907 phase = phases.draft # default phase to draft
916 phase = phases.draft # default phase to draft
908 for p in self.parents():
917 for p in self.parents():
909 phase = max(phase, p.phase())
918 phase = max(phase, p.phase())
910 return phase
919 return phase
911
920
912 def hidden(self):
921 def hidden(self):
913 return False
922 return False
914
923
915 def children(self):
924 def children(self):
916 return []
925 return []
917
926
918 def flags(self, path):
927 def flags(self, path):
919 if '_manifest' in self.__dict__:
928 if '_manifest' in self.__dict__:
920 try:
929 try:
921 return self._manifest.flags(path)
930 return self._manifest.flags(path)
922 except KeyError:
931 except KeyError:
923 return ''
932 return ''
924
933
925 try:
934 try:
926 return self._flagfunc(path)
935 return self._flagfunc(path)
927 except OSError:
936 except OSError:
928 return ''
937 return ''
929
938
930 def filectx(self, path, filelog=None):
939 def filectx(self, path, filelog=None):
931 """get a file context from the working directory"""
940 """get a file context from the working directory"""
932 return workingfilectx(self._repo, path, workingctx=self,
941 return workingfilectx(self._repo, path, workingctx=self,
933 filelog=filelog)
942 filelog=filelog)
934
943
935 def ancestor(self, c2):
944 def ancestor(self, c2):
936 """return the ancestor context of self and c2"""
945 """return the ancestor context of self and c2"""
937 return self._parents[0].ancestor(c2) # punt on two parents for now
946 return self._parents[0].ancestor(c2) # punt on two parents for now
938
947
939 def walk(self, match):
948 def walk(self, match):
940 return sorted(self._repo.dirstate.walk(match, self.substate.keys(),
949 return sorted(self._repo.dirstate.walk(match, self.substate.keys(),
941 True, False))
950 True, False))
942
951
943 def dirty(self, missing=False, merge=True, branch=True):
952 def dirty(self, missing=False, merge=True, branch=True):
944 "check whether a working directory is modified"
953 "check whether a working directory is modified"
945 # check subrepos first
954 # check subrepos first
946 for s in self.substate:
955 for s in self.substate:
947 if self.sub(s).dirty():
956 if self.sub(s).dirty():
948 return True
957 return True
949 # check current working dir
958 # check current working dir
950 return ((merge and self.p2()) or
959 return ((merge and self.p2()) or
951 (branch and self.branch() != self.p1().branch()) or
960 (branch and self.branch() != self.p1().branch()) or
952 self.modified() or self.added() or self.removed() or
961 self.modified() or self.added() or self.removed() or
953 (missing and self.deleted()))
962 (missing and self.deleted()))
954
963
955 def add(self, list, prefix=""):
964 def add(self, list, prefix=""):
956 join = lambda f: os.path.join(prefix, f)
965 join = lambda f: os.path.join(prefix, f)
957 wlock = self._repo.wlock()
966 wlock = self._repo.wlock()
958 ui, ds = self._repo.ui, self._repo.dirstate
967 ui, ds = self._repo.ui, self._repo.dirstate
959 try:
968 try:
960 rejected = []
969 rejected = []
961 for f in list:
970 for f in list:
962 scmutil.checkportable(ui, join(f))
971 scmutil.checkportable(ui, join(f))
963 p = self._repo.wjoin(f)
972 p = self._repo.wjoin(f)
964 try:
973 try:
965 st = os.lstat(p)
974 st = os.lstat(p)
966 except OSError:
975 except OSError:
967 ui.warn(_("%s does not exist!\n") % join(f))
976 ui.warn(_("%s does not exist!\n") % join(f))
968 rejected.append(f)
977 rejected.append(f)
969 continue
978 continue
970 if st.st_size > 10000000:
979 if st.st_size > 10000000:
971 ui.warn(_("%s: up to %d MB of RAM may be required "
980 ui.warn(_("%s: up to %d MB of RAM may be required "
972 "to manage this file\n"
981 "to manage this file\n"
973 "(use 'hg revert %s' to cancel the "
982 "(use 'hg revert %s' to cancel the "
974 "pending addition)\n")
983 "pending addition)\n")
975 % (f, 3 * st.st_size // 1000000, join(f)))
984 % (f, 3 * st.st_size // 1000000, join(f)))
976 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
985 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
977 ui.warn(_("%s not added: only files and symlinks "
986 ui.warn(_("%s not added: only files and symlinks "
978 "supported currently\n") % join(f))
987 "supported currently\n") % join(f))
979 rejected.append(p)
988 rejected.append(p)
980 elif ds[f] in 'amn':
989 elif ds[f] in 'amn':
981 ui.warn(_("%s already tracked!\n") % join(f))
990 ui.warn(_("%s already tracked!\n") % join(f))
982 elif ds[f] == 'r':
991 elif ds[f] == 'r':
983 ds.normallookup(f)
992 ds.normallookup(f)
984 else:
993 else:
985 ds.add(f)
994 ds.add(f)
986 return rejected
995 return rejected
987 finally:
996 finally:
988 wlock.release()
997 wlock.release()
989
998
990 def forget(self, files, prefix=""):
999 def forget(self, files, prefix=""):
991 join = lambda f: os.path.join(prefix, f)
1000 join = lambda f: os.path.join(prefix, f)
992 wlock = self._repo.wlock()
1001 wlock = self._repo.wlock()
993 try:
1002 try:
994 rejected = []
1003 rejected = []
995 for f in files:
1004 for f in files:
996 if f not in self._repo.dirstate:
1005 if f not in self._repo.dirstate:
997 self._repo.ui.warn(_("%s not tracked!\n") % join(f))
1006 self._repo.ui.warn(_("%s not tracked!\n") % join(f))
998 rejected.append(f)
1007 rejected.append(f)
999 elif self._repo.dirstate[f] != 'a':
1008 elif self._repo.dirstate[f] != 'a':
1000 self._repo.dirstate.remove(f)
1009 self._repo.dirstate.remove(f)
1001 else:
1010 else:
1002 self._repo.dirstate.drop(f)
1011 self._repo.dirstate.drop(f)
1003 return rejected
1012 return rejected
1004 finally:
1013 finally:
1005 wlock.release()
1014 wlock.release()
1006
1015
1007 def ancestors(self):
1016 def ancestors(self):
1008 for a in self._repo.changelog.ancestors(
1017 for a in self._repo.changelog.ancestors(
1009 *[p.rev() for p in self._parents]):
1018 *[p.rev() for p in self._parents]):
1010 yield changectx(self._repo, a)
1019 yield changectx(self._repo, a)
1011
1020
1012 def undelete(self, list):
1021 def undelete(self, list):
1013 pctxs = self.parents()
1022 pctxs = self.parents()
1014 wlock = self._repo.wlock()
1023 wlock = self._repo.wlock()
1015 try:
1024 try:
1016 for f in list:
1025 for f in list:
1017 if self._repo.dirstate[f] != 'r':
1026 if self._repo.dirstate[f] != 'r':
1018 self._repo.ui.warn(_("%s not removed!\n") % f)
1027 self._repo.ui.warn(_("%s not removed!\n") % f)
1019 else:
1028 else:
1020 fctx = f in pctxs[0] and pctxs[0][f] or pctxs[1][f]
1029 fctx = f in pctxs[0] and pctxs[0][f] or pctxs[1][f]
1021 t = fctx.data()
1030 t = fctx.data()
1022 self._repo.wwrite(f, t, fctx.flags())
1031 self._repo.wwrite(f, t, fctx.flags())
1023 self._repo.dirstate.normal(f)
1032 self._repo.dirstate.normal(f)
1024 finally:
1033 finally:
1025 wlock.release()
1034 wlock.release()
1026
1035
1027 def copy(self, source, dest):
1036 def copy(self, source, dest):
1028 p = self._repo.wjoin(dest)
1037 p = self._repo.wjoin(dest)
1029 if not os.path.lexists(p):
1038 if not os.path.lexists(p):
1030 self._repo.ui.warn(_("%s does not exist!\n") % dest)
1039 self._repo.ui.warn(_("%s does not exist!\n") % dest)
1031 elif not (os.path.isfile(p) or os.path.islink(p)):
1040 elif not (os.path.isfile(p) or os.path.islink(p)):
1032 self._repo.ui.warn(_("copy failed: %s is not a file or a "
1041 self._repo.ui.warn(_("copy failed: %s is not a file or a "
1033 "symbolic link\n") % dest)
1042 "symbolic link\n") % dest)
1034 else:
1043 else:
1035 wlock = self._repo.wlock()
1044 wlock = self._repo.wlock()
1036 try:
1045 try:
1037 if self._repo.dirstate[dest] in '?r':
1046 if self._repo.dirstate[dest] in '?r':
1038 self._repo.dirstate.add(dest)
1047 self._repo.dirstate.add(dest)
1039 self._repo.dirstate.copy(source, dest)
1048 self._repo.dirstate.copy(source, dest)
1040 finally:
1049 finally:
1041 wlock.release()
1050 wlock.release()
1042
1051
1043 def dirs(self):
1052 def dirs(self):
1044 return self._repo.dirstate.dirs()
1053 return self._repo.dirstate.dirs()
1045
1054
1046 class workingfilectx(filectx):
1055 class workingfilectx(filectx):
1047 """A workingfilectx object makes access to data related to a particular
1056 """A workingfilectx object makes access to data related to a particular
1048 file in the working directory convenient."""
1057 file in the working directory convenient."""
1049 def __init__(self, repo, path, filelog=None, workingctx=None):
1058 def __init__(self, repo, path, filelog=None, workingctx=None):
1050 """changeid can be a changeset revision, node, or tag.
1059 """changeid can be a changeset revision, node, or tag.
1051 fileid can be a file revision or node."""
1060 fileid can be a file revision or node."""
1052 self._repo = repo
1061 self._repo = repo
1053 self._path = path
1062 self._path = path
1054 self._changeid = None
1063 self._changeid = None
1055 self._filerev = self._filenode = None
1064 self._filerev = self._filenode = None
1056
1065
1057 if filelog:
1066 if filelog:
1058 self._filelog = filelog
1067 self._filelog = filelog
1059 if workingctx:
1068 if workingctx:
1060 self._changectx = workingctx
1069 self._changectx = workingctx
1061
1070
1062 @propertycache
1071 @propertycache
1063 def _changectx(self):
1072 def _changectx(self):
1064 return workingctx(self._repo)
1073 return workingctx(self._repo)
1065
1074
1066 def __nonzero__(self):
1075 def __nonzero__(self):
1067 return True
1076 return True
1068
1077
1069 def __str__(self):
1078 def __str__(self):
1070 return "%s@%s" % (self.path(), self._changectx)
1079 return "%s@%s" % (self.path(), self._changectx)
1071
1080
1072 def __repr__(self):
1081 def __repr__(self):
1073 return "<workingfilectx %s>" % str(self)
1082 return "<workingfilectx %s>" % str(self)
1074
1083
1075 def data(self):
1084 def data(self):
1076 return self._repo.wread(self._path)
1085 return self._repo.wread(self._path)
1077 def renamed(self):
1086 def renamed(self):
1078 rp = self._repo.dirstate.copied(self._path)
1087 rp = self._repo.dirstate.copied(self._path)
1079 if not rp:
1088 if not rp:
1080 return None
1089 return None
1081 return rp, self._changectx._parents[0]._manifest.get(rp, nullid)
1090 return rp, self._changectx._parents[0]._manifest.get(rp, nullid)
1082
1091
1083 def parents(self):
1092 def parents(self):
1084 '''return parent filectxs, following copies if necessary'''
1093 '''return parent filectxs, following copies if necessary'''
1085 def filenode(ctx, path):
1094 def filenode(ctx, path):
1086 return ctx._manifest.get(path, nullid)
1095 return ctx._manifest.get(path, nullid)
1087
1096
1088 path = self._path
1097 path = self._path
1089 fl = self._filelog
1098 fl = self._filelog
1090 pcl = self._changectx._parents
1099 pcl = self._changectx._parents
1091 renamed = self.renamed()
1100 renamed = self.renamed()
1092
1101
1093 if renamed:
1102 if renamed:
1094 pl = [renamed + (None,)]
1103 pl = [renamed + (None,)]
1095 else:
1104 else:
1096 pl = [(path, filenode(pcl[0], path), fl)]
1105 pl = [(path, filenode(pcl[0], path), fl)]
1097
1106
1098 for pc in pcl[1:]:
1107 for pc in pcl[1:]:
1099 pl.append((path, filenode(pc, path), fl))
1108 pl.append((path, filenode(pc, path), fl))
1100
1109
1101 return [filectx(self._repo, p, fileid=n, filelog=l)
1110 return [filectx(self._repo, p, fileid=n, filelog=l)
1102 for p, n, l in pl if n != nullid]
1111 for p, n, l in pl if n != nullid]
1103
1112
1104 def children(self):
1113 def children(self):
1105 return []
1114 return []
1106
1115
1107 def size(self):
1116 def size(self):
1108 return os.lstat(self._repo.wjoin(self._path)).st_size
1117 return os.lstat(self._repo.wjoin(self._path)).st_size
1109 def date(self):
1118 def date(self):
1110 t, tz = self._changectx.date()
1119 t, tz = self._changectx.date()
1111 try:
1120 try:
1112 return (int(os.lstat(self._repo.wjoin(self._path)).st_mtime), tz)
1121 return (int(os.lstat(self._repo.wjoin(self._path)).st_mtime), tz)
1113 except OSError, err:
1122 except OSError, err:
1114 if err.errno != errno.ENOENT:
1123 if err.errno != errno.ENOENT:
1115 raise
1124 raise
1116 return (t, tz)
1125 return (t, tz)
1117
1126
1118 def cmp(self, fctx):
1127 def cmp(self, fctx):
1119 """compare with other file context
1128 """compare with other file context
1120
1129
1121 returns True if different than fctx.
1130 returns True if different than fctx.
1122 """
1131 """
1123 # fctx should be a filectx (not a wfctx)
1132 # fctx should be a filectx (not a wfctx)
1124 # invert comparison to reuse the same code path
1133 # invert comparison to reuse the same code path
1125 return fctx.cmp(self)
1134 return fctx.cmp(self)
1126
1135
1127 class memctx(object):
1136 class memctx(object):
1128 """Use memctx to perform in-memory commits via localrepo.commitctx().
1137 """Use memctx to perform in-memory commits via localrepo.commitctx().
1129
1138
1130 Revision information is supplied at initialization time while
1139 Revision information is supplied at initialization time while
1131 related files data and is made available through a callback
1140 related files data and is made available through a callback
1132 mechanism. 'repo' is the current localrepo, 'parents' is a
1141 mechanism. 'repo' is the current localrepo, 'parents' is a
1133 sequence of two parent revisions identifiers (pass None for every
1142 sequence of two parent revisions identifiers (pass None for every
1134 missing parent), 'text' is the commit message and 'files' lists
1143 missing parent), 'text' is the commit message and 'files' lists
1135 names of files touched by the revision (normalized and relative to
1144 names of files touched by the revision (normalized and relative to
1136 repository root).
1145 repository root).
1137
1146
1138 filectxfn(repo, memctx, path) is a callable receiving the
1147 filectxfn(repo, memctx, path) is a callable receiving the
1139 repository, the current memctx object and the normalized path of
1148 repository, the current memctx object and the normalized path of
1140 requested file, relative to repository root. It is fired by the
1149 requested file, relative to repository root. It is fired by the
1141 commit function for every file in 'files', but calls order is
1150 commit function for every file in 'files', but calls order is
1142 undefined. If the file is available in the revision being
1151 undefined. If the file is available in the revision being
1143 committed (updated or added), filectxfn returns a memfilectx
1152 committed (updated or added), filectxfn returns a memfilectx
1144 object. If the file was removed, filectxfn raises an
1153 object. If the file was removed, filectxfn raises an
1145 IOError. Moved files are represented by marking the source file
1154 IOError. Moved files are represented by marking the source file
1146 removed and the new file added with copy information (see
1155 removed and the new file added with copy information (see
1147 memfilectx).
1156 memfilectx).
1148
1157
1149 user receives the committer name and defaults to current
1158 user receives the committer name and defaults to current
1150 repository username, date is the commit date in any format
1159 repository username, date is the commit date in any format
1151 supported by util.parsedate() and defaults to current date, extra
1160 supported by util.parsedate() and defaults to current date, extra
1152 is a dictionary of metadata or is left empty.
1161 is a dictionary of metadata or is left empty.
1153 """
1162 """
1154 def __init__(self, repo, parents, text, files, filectxfn, user=None,
1163 def __init__(self, repo, parents, text, files, filectxfn, user=None,
1155 date=None, extra=None):
1164 date=None, extra=None):
1156 self._repo = repo
1165 self._repo = repo
1157 self._rev = None
1166 self._rev = None
1158 self._node = None
1167 self._node = None
1159 self._text = text
1168 self._text = text
1160 self._date = date and util.parsedate(date) or util.makedate()
1169 self._date = date and util.parsedate(date) or util.makedate()
1161 self._user = user
1170 self._user = user
1162 parents = [(p or nullid) for p in parents]
1171 parents = [(p or nullid) for p in parents]
1163 p1, p2 = parents
1172 p1, p2 = parents
1164 self._parents = [changectx(self._repo, p) for p in (p1, p2)]
1173 self._parents = [changectx(self._repo, p) for p in (p1, p2)]
1165 files = sorted(set(files))
1174 files = sorted(set(files))
1166 self._status = [files, [], [], [], []]
1175 self._status = [files, [], [], [], []]
1167 self._filectxfn = filectxfn
1176 self._filectxfn = filectxfn
1168
1177
1169 self._extra = extra and extra.copy() or {}
1178 self._extra = extra and extra.copy() or {}
1170 if self._extra.get('branch', '') == '':
1179 if self._extra.get('branch', '') == '':
1171 self._extra['branch'] = 'default'
1180 self._extra['branch'] = 'default'
1172
1181
1173 def __str__(self):
1182 def __str__(self):
1174 return str(self._parents[0]) + "+"
1183 return str(self._parents[0]) + "+"
1175
1184
1176 def __int__(self):
1185 def __int__(self):
1177 return self._rev
1186 return self._rev
1178
1187
1179 def __nonzero__(self):
1188 def __nonzero__(self):
1180 return True
1189 return True
1181
1190
1182 def __getitem__(self, key):
1191 def __getitem__(self, key):
1183 return self.filectx(key)
1192 return self.filectx(key)
1184
1193
1185 def p1(self):
1194 def p1(self):
1186 return self._parents[0]
1195 return self._parents[0]
1187 def p2(self):
1196 def p2(self):
1188 return self._parents[1]
1197 return self._parents[1]
1189
1198
1190 def user(self):
1199 def user(self):
1191 return self._user or self._repo.ui.username()
1200 return self._user or self._repo.ui.username()
1192 def date(self):
1201 def date(self):
1193 return self._date
1202 return self._date
1194 def description(self):
1203 def description(self):
1195 return self._text
1204 return self._text
1196 def files(self):
1205 def files(self):
1197 return self.modified()
1206 return self.modified()
1198 def modified(self):
1207 def modified(self):
1199 return self._status[0]
1208 return self._status[0]
1200 def added(self):
1209 def added(self):
1201 return self._status[1]
1210 return self._status[1]
1202 def removed(self):
1211 def removed(self):
1203 return self._status[2]
1212 return self._status[2]
1204 def deleted(self):
1213 def deleted(self):
1205 return self._status[3]
1214 return self._status[3]
1206 def unknown(self):
1215 def unknown(self):
1207 return self._status[4]
1216 return self._status[4]
1208 def ignored(self):
1217 def ignored(self):
1209 return self._status[5]
1218 return self._status[5]
1210 def clean(self):
1219 def clean(self):
1211 return self._status[6]
1220 return self._status[6]
1212 def branch(self):
1221 def branch(self):
1213 return encoding.tolocal(self._extra['branch'])
1222 return encoding.tolocal(self._extra['branch'])
1214 def extra(self):
1223 def extra(self):
1215 return self._extra
1224 return self._extra
1216 def flags(self, f):
1225 def flags(self, f):
1217 return self[f].flags()
1226 return self[f].flags()
1218
1227
1219 def parents(self):
1228 def parents(self):
1220 """return contexts for each parent changeset"""
1229 """return contexts for each parent changeset"""
1221 return self._parents
1230 return self._parents
1222
1231
1223 def filectx(self, path, filelog=None):
1232 def filectx(self, path, filelog=None):
1224 """get a file context from the working directory"""
1233 """get a file context from the working directory"""
1225 return self._filectxfn(self._repo, self, path)
1234 return self._filectxfn(self._repo, self, path)
1226
1235
1227 def commit(self):
1236 def commit(self):
1228 """commit context to the repo"""
1237 """commit context to the repo"""
1229 return self._repo.commitctx(self)
1238 return self._repo.commitctx(self)
1230
1239
1231 class memfilectx(object):
1240 class memfilectx(object):
1232 """memfilectx represents an in-memory file to commit.
1241 """memfilectx represents an in-memory file to commit.
1233
1242
1234 See memctx for more details.
1243 See memctx for more details.
1235 """
1244 """
1236 def __init__(self, path, data, islink=False, isexec=False, copied=None):
1245 def __init__(self, path, data, islink=False, isexec=False, copied=None):
1237 """
1246 """
1238 path is the normalized file path relative to repository root.
1247 path is the normalized file path relative to repository root.
1239 data is the file content as a string.
1248 data is the file content as a string.
1240 islink is True if the file is a symbolic link.
1249 islink is True if the file is a symbolic link.
1241 isexec is True if the file is executable.
1250 isexec is True if the file is executable.
1242 copied is the source file path if current file was copied in the
1251 copied is the source file path if current file was copied in the
1243 revision being committed, or None."""
1252 revision being committed, or None."""
1244 self._path = path
1253 self._path = path
1245 self._data = data
1254 self._data = data
1246 self._flags = (islink and 'l' or '') + (isexec and 'x' or '')
1255 self._flags = (islink and 'l' or '') + (isexec and 'x' or '')
1247 self._copied = None
1256 self._copied = None
1248 if copied:
1257 if copied:
1249 self._copied = (copied, nullid)
1258 self._copied = (copied, nullid)
1250
1259
1251 def __nonzero__(self):
1260 def __nonzero__(self):
1252 return True
1261 return True
1253 def __str__(self):
1262 def __str__(self):
1254 return "%s@%s" % (self.path(), self._changectx)
1263 return "%s@%s" % (self.path(), self._changectx)
1255 def path(self):
1264 def path(self):
1256 return self._path
1265 return self._path
1257 def data(self):
1266 def data(self):
1258 return self._data
1267 return self._data
1259 def flags(self):
1268 def flags(self):
1260 return self._flags
1269 return self._flags
1261 def isexec(self):
1270 def isexec(self):
1262 return 'x' in self._flags
1271 return 'x' in self._flags
1263 def islink(self):
1272 def islink(self):
1264 return 'l' in self._flags
1273 return 'l' in self._flags
1265 def renamed(self):
1274 def renamed(self):
1266 return self._copied
1275 return self._copied
General Comments 0
You need to be logged in to leave comments. Login now