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