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