##// END OF EJS Templates
workingctx: correctly compute the flag for noexec filesystems+merge...
Benoit Boissinot -
r10921:fb89cd21 stable
parent child Browse files
Show More
@@ -1,912 +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 cf = lambda x: man.flags(copied.get(x, x))
592 if len(self._parents) > 1:
593 man2 = self.p2().manifest()
594 def getman(f):
595 if f in man:
596 return man
597 return man2
598 else:
599 getman = lambda f: man
600 def cf(f):
601 f = copied.get(f, f)
602 return getman(f).flags(f)
593 ff = self._repo.dirstate.flagfunc(cf)
603 ff = self._repo.dirstate.flagfunc(cf)
594 modified, added, removed, deleted, unknown = self._status[:5]
604 modified, added, removed, deleted, unknown = self._status[:5]
595 for i, l in (("a", added), ("m", modified), ("u", unknown)):
605 for i, l in (("a", added), ("m", modified), ("u", unknown)):
596 for f in l:
606 for f in l:
597 man[f] = man.get(copied.get(f, f), nullid) + i
607 orig = copied.get(f, f)
608 man[f] = getman(orig).get(orig, nullid) + i
598 try:
609 try:
599 man.set(f, ff(f))
610 man.set(f, ff(f))
600 except OSError:
611 except OSError:
601 pass
612 pass
602
613
603 for f in deleted + removed:
614 for f in deleted + removed:
604 if f in man:
615 if f in man:
605 del man[f]
616 del man[f]
606
617
607 return man
618 return man
608
619
609 @propertycache
620 @propertycache
610 def _status(self):
621 def _status(self):
611 return self._repo.status(unknown=True)
622 return self._repo.status(unknown=True)
612
623
613 @propertycache
624 @propertycache
614 def _user(self):
625 def _user(self):
615 return self._repo.ui.username()
626 return self._repo.ui.username()
616
627
617 @propertycache
628 @propertycache
618 def _date(self):
629 def _date(self):
619 return util.makedate()
630 return util.makedate()
620
631
621 @propertycache
632 @propertycache
622 def _parents(self):
633 def _parents(self):
623 p = self._repo.dirstate.parents()
634 p = self._repo.dirstate.parents()
624 if p[1] == nullid:
635 if p[1] == nullid:
625 p = p[:-1]
636 p = p[:-1]
626 self._parents = [changectx(self._repo, x) for x in p]
637 self._parents = [changectx(self._repo, x) for x in p]
627 return self._parents
638 return self._parents
628
639
629 def manifest(self):
640 def manifest(self):
630 return self._manifest
641 return self._manifest
631 def user(self):
642 def user(self):
632 return self._user or self._repo.ui.username()
643 return self._user or self._repo.ui.username()
633 def date(self):
644 def date(self):
634 return self._date
645 return self._date
635 def description(self):
646 def description(self):
636 return self._text
647 return self._text
637 def files(self):
648 def files(self):
638 return sorted(self._status[0] + self._status[1] + self._status[2])
649 return sorted(self._status[0] + self._status[1] + self._status[2])
639
650
640 def modified(self):
651 def modified(self):
641 return self._status[0]
652 return self._status[0]
642 def added(self):
653 def added(self):
643 return self._status[1]
654 return self._status[1]
644 def removed(self):
655 def removed(self):
645 return self._status[2]
656 return self._status[2]
646 def deleted(self):
657 def deleted(self):
647 return self._status[3]
658 return self._status[3]
648 def unknown(self):
659 def unknown(self):
649 return self._status[4]
660 return self._status[4]
650 def clean(self):
661 def clean(self):
651 return self._status[5]
662 return self._status[5]
652 def branch(self):
663 def branch(self):
653 return self._extra['branch']
664 return self._extra['branch']
654 def extra(self):
665 def extra(self):
655 return self._extra
666 return self._extra
656
667
657 def tags(self):
668 def tags(self):
658 t = []
669 t = []
659 [t.extend(p.tags()) for p in self.parents()]
670 [t.extend(p.tags()) for p in self.parents()]
660 return t
671 return t
661
672
662 def children(self):
673 def children(self):
663 return []
674 return []
664
675
665 def flags(self, path):
676 def flags(self, path):
666 if '_manifest' in self.__dict__:
677 if '_manifest' in self.__dict__:
667 try:
678 try:
668 return self._manifest.flags(path)
679 return self._manifest.flags(path)
669 except KeyError:
680 except KeyError:
670 return ''
681 return ''
671
682
672 pnode = self._parents[0].changeset()[0]
673 orig = self._repo.dirstate.copies().get(path, path)
683 orig = self._repo.dirstate.copies().get(path, path)
674 node, flag = self._repo.manifest.find(pnode, orig)
675 try:
676 ff = self._repo.dirstate.flagfunc(lambda x: flag or '')
677 return ff(path)
678 except OSError:
679 pass
680
684
681 if not node or path in self.deleted() or path in self.removed():
685 def findflag(ctx):
686 mnode = ctx.changeset()[0]
687 node, flag = self._repo.manifest.find(mnode, orig)
688 ff = self._repo.dirstate.flagfunc(lambda x: flag or None)
689 try:
690 return ff(orig)
691 except OSError:
692 pass
693
694 flag = findflag(self._parents[0])
695 if flag is None and len(self.parents()) > 1:
696 flag = findflag(self._parents[1])
697 if flag is None or self._repo.dirstate[path] == 'r':
682 return ''
698 return ''
683 return flag
699 return flag
684
700
685 def filectx(self, path, filelog=None):
701 def filectx(self, path, filelog=None):
686 """get a file context from the working directory"""
702 """get a file context from the working directory"""
687 return workingfilectx(self._repo, path, workingctx=self,
703 return workingfilectx(self._repo, path, workingctx=self,
688 filelog=filelog)
704 filelog=filelog)
689
705
690 def ancestor(self, c2):
706 def ancestor(self, c2):
691 """return the ancestor context of self and c2"""
707 """return the ancestor context of self and c2"""
692 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
693
709
694 def walk(self, match):
710 def walk(self, match):
695 return sorted(self._repo.dirstate.walk(match, self.substate.keys(),
711 return sorted(self._repo.dirstate.walk(match, self.substate.keys(),
696 True, False))
712 True, False))
697
713
698 def dirty(self, missing=False):
714 def dirty(self, missing=False):
699 "check whether a working directory is modified"
715 "check whether a working directory is modified"
700
716
701 return (self.p2() or self.branch() != self.p1().branch() or
717 return (self.p2() or self.branch() != self.p1().branch() or
702 self.modified() or self.added() or self.removed() or
718 self.modified() or self.added() or self.removed() or
703 (missing and self.deleted()))
719 (missing and self.deleted()))
704
720
705 class workingfilectx(filectx):
721 class workingfilectx(filectx):
706 """A workingfilectx object makes access to data related to a particular
722 """A workingfilectx object makes access to data related to a particular
707 file in the working directory convenient."""
723 file in the working directory convenient."""
708 def __init__(self, repo, path, filelog=None, workingctx=None):
724 def __init__(self, repo, path, filelog=None, workingctx=None):
709 """changeid can be a changeset revision, node, or tag.
725 """changeid can be a changeset revision, node, or tag.
710 fileid can be a file revision or node."""
726 fileid can be a file revision or node."""
711 self._repo = repo
727 self._repo = repo
712 self._path = path
728 self._path = path
713 self._changeid = None
729 self._changeid = None
714 self._filerev = self._filenode = None
730 self._filerev = self._filenode = None
715
731
716 if filelog:
732 if filelog:
717 self._filelog = filelog
733 self._filelog = filelog
718 if workingctx:
734 if workingctx:
719 self._changectx = workingctx
735 self._changectx = workingctx
720
736
721 @propertycache
737 @propertycache
722 def _changectx(self):
738 def _changectx(self):
723 return workingctx(self._repo)
739 return workingctx(self._repo)
724
740
725 def __nonzero__(self):
741 def __nonzero__(self):
726 return True
742 return True
727
743
728 def __str__(self):
744 def __str__(self):
729 return "%s@%s" % (self.path(), self._changectx)
745 return "%s@%s" % (self.path(), self._changectx)
730
746
731 def data(self):
747 def data(self):
732 return self._repo.wread(self._path)
748 return self._repo.wread(self._path)
733 def renamed(self):
749 def renamed(self):
734 rp = self._repo.dirstate.copied(self._path)
750 rp = self._repo.dirstate.copied(self._path)
735 if not rp:
751 if not rp:
736 return None
752 return None
737 return rp, self._changectx._parents[0]._manifest.get(rp, nullid)
753 return rp, self._changectx._parents[0]._manifest.get(rp, nullid)
738
754
739 def parents(self):
755 def parents(self):
740 '''return parent filectxs, following copies if necessary'''
756 '''return parent filectxs, following copies if necessary'''
741 def filenode(ctx, path):
757 def filenode(ctx, path):
742 return ctx._manifest.get(path, nullid)
758 return ctx._manifest.get(path, nullid)
743
759
744 path = self._path
760 path = self._path
745 fl = self._filelog
761 fl = self._filelog
746 pcl = self._changectx._parents
762 pcl = self._changectx._parents
747 renamed = self.renamed()
763 renamed = self.renamed()
748
764
749 if renamed:
765 if renamed:
750 pl = [renamed + (None,)]
766 pl = [renamed + (None,)]
751 else:
767 else:
752 pl = [(path, filenode(pcl[0], path), fl)]
768 pl = [(path, filenode(pcl[0], path), fl)]
753
769
754 for pc in pcl[1:]:
770 for pc in pcl[1:]:
755 pl.append((path, filenode(pc, path), fl))
771 pl.append((path, filenode(pc, path), fl))
756
772
757 return [filectx(self._repo, p, fileid=n, filelog=l)
773 return [filectx(self._repo, p, fileid=n, filelog=l)
758 for p, n, l in pl if n != nullid]
774 for p, n, l in pl if n != nullid]
759
775
760 def children(self):
776 def children(self):
761 return []
777 return []
762
778
763 def size(self):
779 def size(self):
764 return os.stat(self._repo.wjoin(self._path)).st_size
780 return os.stat(self._repo.wjoin(self._path)).st_size
765 def date(self):
781 def date(self):
766 t, tz = self._changectx.date()
782 t, tz = self._changectx.date()
767 try:
783 try:
768 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)
769 except OSError, err:
785 except OSError, err:
770 if err.errno != errno.ENOENT:
786 if err.errno != errno.ENOENT:
771 raise
787 raise
772 return (t, tz)
788 return (t, tz)
773
789
774 def cmp(self, text):
790 def cmp(self, text):
775 return self._repo.wread(self._path) == text
791 return self._repo.wread(self._path) == text
776
792
777 class memctx(object):
793 class memctx(object):
778 """Use memctx to perform in-memory commits via localrepo.commitctx().
794 """Use memctx to perform in-memory commits via localrepo.commitctx().
779
795
780 Revision information is supplied at initialization time while
796 Revision information is supplied at initialization time while
781 related files data and is made available through a callback
797 related files data and is made available through a callback
782 mechanism. 'repo' is the current localrepo, 'parents' is a
798 mechanism. 'repo' is the current localrepo, 'parents' is a
783 sequence of two parent revisions identifiers (pass None for every
799 sequence of two parent revisions identifiers (pass None for every
784 missing parent), 'text' is the commit message and 'files' lists
800 missing parent), 'text' is the commit message and 'files' lists
785 names of files touched by the revision (normalized and relative to
801 names of files touched by the revision (normalized and relative to
786 repository root).
802 repository root).
787
803
788 filectxfn(repo, memctx, path) is a callable receiving the
804 filectxfn(repo, memctx, path) is a callable receiving the
789 repository, the current memctx object and the normalized path of
805 repository, the current memctx object and the normalized path of
790 requested file, relative to repository root. It is fired by the
806 requested file, relative to repository root. It is fired by the
791 commit function for every file in 'files', but calls order is
807 commit function for every file in 'files', but calls order is
792 undefined. If the file is available in the revision being
808 undefined. If the file is available in the revision being
793 committed (updated or added), filectxfn returns a memfilectx
809 committed (updated or added), filectxfn returns a memfilectx
794 object. If the file was removed, filectxfn raises an
810 object. If the file was removed, filectxfn raises an
795 IOError. Moved files are represented by marking the source file
811 IOError. Moved files are represented by marking the source file
796 removed and the new file added with copy information (see
812 removed and the new file added with copy information (see
797 memfilectx).
813 memfilectx).
798
814
799 user receives the committer name and defaults to current
815 user receives the committer name and defaults to current
800 repository username, date is the commit date in any format
816 repository username, date is the commit date in any format
801 supported by util.parsedate() and defaults to current date, extra
817 supported by util.parsedate() and defaults to current date, extra
802 is a dictionary of metadata or is left empty.
818 is a dictionary of metadata or is left empty.
803 """
819 """
804 def __init__(self, repo, parents, text, files, filectxfn, user=None,
820 def __init__(self, repo, parents, text, files, filectxfn, user=None,
805 date=None, extra=None):
821 date=None, extra=None):
806 self._repo = repo
822 self._repo = repo
807 self._rev = None
823 self._rev = None
808 self._node = None
824 self._node = None
809 self._text = text
825 self._text = text
810 self._date = date and util.parsedate(date) or util.makedate()
826 self._date = date and util.parsedate(date) or util.makedate()
811 self._user = user
827 self._user = user
812 parents = [(p or nullid) for p in parents]
828 parents = [(p or nullid) for p in parents]
813 p1, p2 = parents
829 p1, p2 = parents
814 self._parents = [changectx(self._repo, p) for p in (p1, p2)]
830 self._parents = [changectx(self._repo, p) for p in (p1, p2)]
815 files = sorted(set(files))
831 files = sorted(set(files))
816 self._status = [files, [], [], [], []]
832 self._status = [files, [], [], [], []]
817 self._filectxfn = filectxfn
833 self._filectxfn = filectxfn
818
834
819 self._extra = extra and extra.copy() or {}
835 self._extra = extra and extra.copy() or {}
820 if 'branch' not in self._extra:
836 if 'branch' not in self._extra:
821 self._extra['branch'] = 'default'
837 self._extra['branch'] = 'default'
822 elif self._extra.get('branch') == '':
838 elif self._extra.get('branch') == '':
823 self._extra['branch'] = 'default'
839 self._extra['branch'] = 'default'
824
840
825 def __str__(self):
841 def __str__(self):
826 return str(self._parents[0]) + "+"
842 return str(self._parents[0]) + "+"
827
843
828 def __int__(self):
844 def __int__(self):
829 return self._rev
845 return self._rev
830
846
831 def __nonzero__(self):
847 def __nonzero__(self):
832 return True
848 return True
833
849
834 def __getitem__(self, key):
850 def __getitem__(self, key):
835 return self.filectx(key)
851 return self.filectx(key)
836
852
837 def p1(self):
853 def p1(self):
838 return self._parents[0]
854 return self._parents[0]
839 def p2(self):
855 def p2(self):
840 return self._parents[1]
856 return self._parents[1]
841
857
842 def user(self):
858 def user(self):
843 return self._user or self._repo.ui.username()
859 return self._user or self._repo.ui.username()
844 def date(self):
860 def date(self):
845 return self._date
861 return self._date
846 def description(self):
862 def description(self):
847 return self._text
863 return self._text
848 def files(self):
864 def files(self):
849 return self.modified()
865 return self.modified()
850 def modified(self):
866 def modified(self):
851 return self._status[0]
867 return self._status[0]
852 def added(self):
868 def added(self):
853 return self._status[1]
869 return self._status[1]
854 def removed(self):
870 def removed(self):
855 return self._status[2]
871 return self._status[2]
856 def deleted(self):
872 def deleted(self):
857 return self._status[3]
873 return self._status[3]
858 def unknown(self):
874 def unknown(self):
859 return self._status[4]
875 return self._status[4]
860 def clean(self):
876 def clean(self):
861 return self._status[5]
877 return self._status[5]
862 def branch(self):
878 def branch(self):
863 return self._extra['branch']
879 return self._extra['branch']
864 def extra(self):
880 def extra(self):
865 return self._extra
881 return self._extra
866 def flags(self, f):
882 def flags(self, f):
867 return self[f].flags()
883 return self[f].flags()
868
884
869 def parents(self):
885 def parents(self):
870 """return contexts for each parent changeset"""
886 """return contexts for each parent changeset"""
871 return self._parents
887 return self._parents
872
888
873 def filectx(self, path, filelog=None):
889 def filectx(self, path, filelog=None):
874 """get a file context from the working directory"""
890 """get a file context from the working directory"""
875 return self._filectxfn(self._repo, self, path)
891 return self._filectxfn(self._repo, self, path)
876
892
877 class memfilectx(object):
893 class memfilectx(object):
878 """memfilectx represents an in-memory file to commit.
894 """memfilectx represents an in-memory file to commit.
879
895
880 See memctx for more details.
896 See memctx for more details.
881 """
897 """
882 def __init__(self, path, data, islink, isexec, copied):
898 def __init__(self, path, data, islink, isexec, copied):
883 """
899 """
884 path is the normalized file path relative to repository root.
900 path is the normalized file path relative to repository root.
885 data is the file content as a string.
901 data is the file content as a string.
886 islink is True if the file is a symbolic link.
902 islink is True if the file is a symbolic link.
887 isexec is True if the file is executable.
903 isexec is True if the file is executable.
888 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
889 revision being committed, or None."""
905 revision being committed, or None."""
890 self._path = path
906 self._path = path
891 self._data = data
907 self._data = data
892 self._flags = (islink and 'l' or '') + (isexec and 'x' or '')
908 self._flags = (islink and 'l' or '') + (isexec and 'x' or '')
893 self._copied = None
909 self._copied = None
894 if copied:
910 if copied:
895 self._copied = (copied, nullid)
911 self._copied = (copied, nullid)
896
912
897 def __nonzero__(self):
913 def __nonzero__(self):
898 return True
914 return True
899 def __str__(self):
915 def __str__(self):
900 return "%s@%s" % (self.path(), self._changectx)
916 return "%s@%s" % (self.path(), self._changectx)
901 def path(self):
917 def path(self):
902 return self._path
918 return self._path
903 def data(self):
919 def data(self):
904 return self._data
920 return self._data
905 def flags(self):
921 def flags(self):
906 return self._flags
922 return self._flags
907 def isexec(self):
923 def isexec(self):
908 return 'x' in self._flags
924 return 'x' in self._flags
909 def islink(self):
925 def islink(self):
910 return 'l' in self._flags
926 return 'l' in self._flags
911 def renamed(self):
927 def renamed(self):
912 return self._copied
928 return self._copied
General Comments 0
You need to be logged in to leave comments. Login now