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