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