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