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