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