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