##// END OF EJS Templates
context: add "descendant()" to changectx for efficient descendant examination...
FUJIWARA Katsunori -
r17626:3a524b64 default
parent child Browse files
Show More
@@ -1,1291 +1,1295 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, bin
8 from node import nullid, nullrev, short, hex, bin
9 from i18n import _
9 from i18n import _
10 import ancestor, mdiff, error, util, scmutil, subrepo, patch, encoding, phases
10 import ancestor, mdiff, error, util, scmutil, subrepo, patch, encoding, phases
11 import copies
11 import copies
12 import match as matchmod
12 import match as matchmod
13 import os, errno, stat
13 import os, errno, stat
14 import obsolete as obsmod
14 import obsolete as obsmod
15
15
16 propertycache = util.propertycache
16 propertycache = util.propertycache
17
17
18 class changectx(object):
18 class changectx(object):
19 """A changecontext object makes access to data related to a particular
19 """A changecontext object makes access to data related to a particular
20 changeset convenient."""
20 changeset convenient."""
21 def __init__(self, repo, changeid=''):
21 def __init__(self, repo, changeid=''):
22 """changeid is a revision number, node, or tag"""
22 """changeid is a revision number, node, or tag"""
23 if changeid == '':
23 if changeid == '':
24 changeid = '.'
24 changeid = '.'
25 self._repo = repo
25 self._repo = repo
26
26
27 if isinstance(changeid, int):
27 if isinstance(changeid, int):
28 self._rev = changeid
28 self._rev = changeid
29 self._node = repo.changelog.node(changeid)
29 self._node = repo.changelog.node(changeid)
30 return
30 return
31 if isinstance(changeid, long):
31 if isinstance(changeid, long):
32 changeid = str(changeid)
32 changeid = str(changeid)
33 if changeid == '.':
33 if changeid == '.':
34 self._node = repo.dirstate.p1()
34 self._node = repo.dirstate.p1()
35 self._rev = repo.changelog.rev(self._node)
35 self._rev = repo.changelog.rev(self._node)
36 return
36 return
37 if changeid == 'null':
37 if changeid == 'null':
38 self._node = nullid
38 self._node = nullid
39 self._rev = nullrev
39 self._rev = nullrev
40 return
40 return
41 if changeid == 'tip':
41 if changeid == 'tip':
42 self._rev = len(repo.changelog) - 1
42 self._rev = len(repo.changelog) - 1
43 self._node = repo.changelog.node(self._rev)
43 self._node = repo.changelog.node(self._rev)
44 return
44 return
45 if len(changeid) == 20:
45 if len(changeid) == 20:
46 try:
46 try:
47 self._node = changeid
47 self._node = changeid
48 self._rev = repo.changelog.rev(changeid)
48 self._rev = repo.changelog.rev(changeid)
49 return
49 return
50 except LookupError:
50 except LookupError:
51 pass
51 pass
52
52
53 try:
53 try:
54 r = int(changeid)
54 r = int(changeid)
55 if str(r) != changeid:
55 if str(r) != changeid:
56 raise ValueError
56 raise ValueError
57 l = len(repo.changelog)
57 l = len(repo.changelog)
58 if r < 0:
58 if r < 0:
59 r += l
59 r += l
60 if r < 0 or r >= l:
60 if r < 0 or r >= l:
61 raise ValueError
61 raise ValueError
62 self._rev = r
62 self._rev = r
63 self._node = repo.changelog.node(r)
63 self._node = repo.changelog.node(r)
64 return
64 return
65 except (ValueError, OverflowError):
65 except (ValueError, OverflowError):
66 pass
66 pass
67
67
68 if len(changeid) == 40:
68 if len(changeid) == 40:
69 try:
69 try:
70 self._node = bin(changeid)
70 self._node = bin(changeid)
71 self._rev = repo.changelog.rev(self._node)
71 self._rev = repo.changelog.rev(self._node)
72 return
72 return
73 except (TypeError, LookupError):
73 except (TypeError, LookupError):
74 pass
74 pass
75
75
76 if changeid in repo._bookmarks:
76 if changeid in repo._bookmarks:
77 self._node = repo._bookmarks[changeid]
77 self._node = repo._bookmarks[changeid]
78 self._rev = repo.changelog.rev(self._node)
78 self._rev = repo.changelog.rev(self._node)
79 return
79 return
80 if changeid in repo._tagscache.tags:
80 if changeid in repo._tagscache.tags:
81 self._node = repo._tagscache.tags[changeid]
81 self._node = repo._tagscache.tags[changeid]
82 self._rev = repo.changelog.rev(self._node)
82 self._rev = repo.changelog.rev(self._node)
83 return
83 return
84 try:
84 try:
85 self._node = repo.branchtip(changeid)
85 self._node = repo.branchtip(changeid)
86 self._rev = repo.changelog.rev(self._node)
86 self._rev = repo.changelog.rev(self._node)
87 return
87 return
88 except error.RepoLookupError:
88 except error.RepoLookupError:
89 pass
89 pass
90
90
91 self._node = repo.changelog._partialmatch(changeid)
91 self._node = repo.changelog._partialmatch(changeid)
92 if self._node is not None:
92 if self._node is not None:
93 self._rev = repo.changelog.rev(self._node)
93 self._rev = repo.changelog.rev(self._node)
94 return
94 return
95
95
96 # lookup failed
96 # lookup failed
97 # check if it might have come from damaged dirstate
97 # check if it might have come from damaged dirstate
98 if changeid in repo.dirstate.parents():
98 if changeid in repo.dirstate.parents():
99 raise error.Abort(_("working directory has unknown parent '%s'!")
99 raise error.Abort(_("working directory has unknown parent '%s'!")
100 % short(changeid))
100 % short(changeid))
101 try:
101 try:
102 if len(changeid) == 20:
102 if len(changeid) == 20:
103 changeid = hex(changeid)
103 changeid = hex(changeid)
104 except TypeError:
104 except TypeError:
105 pass
105 pass
106 raise error.RepoLookupError(
106 raise error.RepoLookupError(
107 _("unknown revision '%s'") % changeid)
107 _("unknown revision '%s'") % changeid)
108
108
109 def __str__(self):
109 def __str__(self):
110 return short(self.node())
110 return short(self.node())
111
111
112 def __int__(self):
112 def __int__(self):
113 return self.rev()
113 return self.rev()
114
114
115 def __repr__(self):
115 def __repr__(self):
116 return "<changectx %s>" % str(self)
116 return "<changectx %s>" % str(self)
117
117
118 def __hash__(self):
118 def __hash__(self):
119 try:
119 try:
120 return hash(self._rev)
120 return hash(self._rev)
121 except AttributeError:
121 except AttributeError:
122 return id(self)
122 return id(self)
123
123
124 def __eq__(self, other):
124 def __eq__(self, other):
125 try:
125 try:
126 return self._rev == other._rev
126 return self._rev == other._rev
127 except AttributeError:
127 except AttributeError:
128 return False
128 return False
129
129
130 def __ne__(self, other):
130 def __ne__(self, other):
131 return not (self == other)
131 return not (self == other)
132
132
133 def __nonzero__(self):
133 def __nonzero__(self):
134 return self._rev != nullrev
134 return self._rev != nullrev
135
135
136 @propertycache
136 @propertycache
137 def _changeset(self):
137 def _changeset(self):
138 return self._repo.changelog.read(self.rev())
138 return self._repo.changelog.read(self.rev())
139
139
140 @propertycache
140 @propertycache
141 def _manifest(self):
141 def _manifest(self):
142 return self._repo.manifest.read(self._changeset[0])
142 return self._repo.manifest.read(self._changeset[0])
143
143
144 @propertycache
144 @propertycache
145 def _manifestdelta(self):
145 def _manifestdelta(self):
146 return self._repo.manifest.readdelta(self._changeset[0])
146 return self._repo.manifest.readdelta(self._changeset[0])
147
147
148 @propertycache
148 @propertycache
149 def _parents(self):
149 def _parents(self):
150 p = self._repo.changelog.parentrevs(self._rev)
150 p = self._repo.changelog.parentrevs(self._rev)
151 if p[1] == nullrev:
151 if p[1] == nullrev:
152 p = p[:-1]
152 p = p[:-1]
153 return [changectx(self._repo, x) for x in p]
153 return [changectx(self._repo, x) for x in p]
154
154
155 @propertycache
155 @propertycache
156 def substate(self):
156 def substate(self):
157 return subrepo.state(self, self._repo.ui)
157 return subrepo.state(self, self._repo.ui)
158
158
159 def __contains__(self, key):
159 def __contains__(self, key):
160 return key in self._manifest
160 return key in self._manifest
161
161
162 def __getitem__(self, key):
162 def __getitem__(self, key):
163 return self.filectx(key)
163 return self.filectx(key)
164
164
165 def __iter__(self):
165 def __iter__(self):
166 for f in sorted(self._manifest):
166 for f in sorted(self._manifest):
167 yield f
167 yield f
168
168
169 def changeset(self):
169 def changeset(self):
170 return self._changeset
170 return self._changeset
171 def manifest(self):
171 def manifest(self):
172 return self._manifest
172 return self._manifest
173 def manifestnode(self):
173 def manifestnode(self):
174 return self._changeset[0]
174 return self._changeset[0]
175
175
176 def rev(self):
176 def rev(self):
177 return self._rev
177 return self._rev
178 def node(self):
178 def node(self):
179 return self._node
179 return self._node
180 def hex(self):
180 def hex(self):
181 return hex(self._node)
181 return hex(self._node)
182 def user(self):
182 def user(self):
183 return self._changeset[1]
183 return self._changeset[1]
184 def date(self):
184 def date(self):
185 return self._changeset[2]
185 return self._changeset[2]
186 def files(self):
186 def files(self):
187 return self._changeset[3]
187 return self._changeset[3]
188 def description(self):
188 def description(self):
189 return self._changeset[4]
189 return self._changeset[4]
190 def branch(self):
190 def branch(self):
191 return encoding.tolocal(self._changeset[5].get("branch"))
191 return encoding.tolocal(self._changeset[5].get("branch"))
192 def closesbranch(self):
192 def closesbranch(self):
193 return 'close' in self._changeset[5]
193 return 'close' in self._changeset[5]
194 def extra(self):
194 def extra(self):
195 return self._changeset[5]
195 return self._changeset[5]
196 def tags(self):
196 def tags(self):
197 return self._repo.nodetags(self._node)
197 return self._repo.nodetags(self._node)
198 def bookmarks(self):
198 def bookmarks(self):
199 return self._repo.nodebookmarks(self._node)
199 return self._repo.nodebookmarks(self._node)
200 def phase(self):
200 def phase(self):
201 return self._repo._phasecache.phase(self._repo, self._rev)
201 return self._repo._phasecache.phase(self._repo, self._rev)
202 def phasestr(self):
202 def phasestr(self):
203 return phases.phasenames[self.phase()]
203 return phases.phasenames[self.phase()]
204 def mutable(self):
204 def mutable(self):
205 return self.phase() > phases.public
205 return self.phase() > phases.public
206 def hidden(self):
206 def hidden(self):
207 return self._rev in self._repo.hiddenrevs
207 return self._rev in self._repo.hiddenrevs
208
208
209 def parents(self):
209 def parents(self):
210 """return contexts for each parent changeset"""
210 """return contexts for each parent changeset"""
211 return self._parents
211 return self._parents
212
212
213 def p1(self):
213 def p1(self):
214 return self._parents[0]
214 return self._parents[0]
215
215
216 def p2(self):
216 def p2(self):
217 if len(self._parents) == 2:
217 if len(self._parents) == 2:
218 return self._parents[1]
218 return self._parents[1]
219 return changectx(self._repo, -1)
219 return changectx(self._repo, -1)
220
220
221 def children(self):
221 def children(self):
222 """return contexts for each child changeset"""
222 """return contexts for each child changeset"""
223 c = self._repo.changelog.children(self._node)
223 c = self._repo.changelog.children(self._node)
224 return [changectx(self._repo, x) for x in c]
224 return [changectx(self._repo, x) for x in c]
225
225
226 def ancestors(self):
226 def ancestors(self):
227 for a in self._repo.changelog.ancestors([self._rev]):
227 for a in self._repo.changelog.ancestors([self._rev]):
228 yield changectx(self._repo, a)
228 yield changectx(self._repo, a)
229
229
230 def descendants(self):
230 def descendants(self):
231 for d in self._repo.changelog.descendants([self._rev]):
231 for d in self._repo.changelog.descendants([self._rev]):
232 yield changectx(self._repo, d)
232 yield changectx(self._repo, d)
233
233
234 def obsolete(self):
234 def obsolete(self):
235 """True if the changeset is obsolete"""
235 """True if the changeset is obsolete"""
236 return self.rev() in obsmod.getobscache(self._repo, 'obsolete')
236 return self.rev() in obsmod.getobscache(self._repo, 'obsolete')
237
237
238 def extinct(self):
238 def extinct(self):
239 """True if the changeset is extinct"""
239 """True if the changeset is extinct"""
240 return self.rev() in obsmod.getobscache(self._repo, 'extinct')
240 return self.rev() in obsmod.getobscache(self._repo, 'extinct')
241
241
242 def unstable(self):
242 def unstable(self):
243 """True if the changeset is not obsolete but it's ancestor are"""
243 """True if the changeset is not obsolete but it's ancestor are"""
244 return self.rev() in obsmod.getobscache(self._repo, 'unstable')
244 return self.rev() in obsmod.getobscache(self._repo, 'unstable')
245
245
246 def _fileinfo(self, path):
246 def _fileinfo(self, path):
247 if '_manifest' in self.__dict__:
247 if '_manifest' in self.__dict__:
248 try:
248 try:
249 return self._manifest[path], self._manifest.flags(path)
249 return self._manifest[path], self._manifest.flags(path)
250 except KeyError:
250 except KeyError:
251 raise error.LookupError(self._node, path,
251 raise error.LookupError(self._node, path,
252 _('not found in manifest'))
252 _('not found in manifest'))
253 if '_manifestdelta' in self.__dict__ or path in self.files():
253 if '_manifestdelta' in self.__dict__ or path in self.files():
254 if path in self._manifestdelta:
254 if path in self._manifestdelta:
255 return (self._manifestdelta[path],
255 return (self._manifestdelta[path],
256 self._manifestdelta.flags(path))
256 self._manifestdelta.flags(path))
257 node, flag = self._repo.manifest.find(self._changeset[0], path)
257 node, flag = self._repo.manifest.find(self._changeset[0], path)
258 if not node:
258 if not node:
259 raise error.LookupError(self._node, path,
259 raise error.LookupError(self._node, path,
260 _('not found in manifest'))
260 _('not found in manifest'))
261
261
262 return node, flag
262 return node, flag
263
263
264 def filenode(self, path):
264 def filenode(self, path):
265 return self._fileinfo(path)[0]
265 return self._fileinfo(path)[0]
266
266
267 def flags(self, path):
267 def flags(self, path):
268 try:
268 try:
269 return self._fileinfo(path)[1]
269 return self._fileinfo(path)[1]
270 except error.LookupError:
270 except error.LookupError:
271 return ''
271 return ''
272
272
273 def filectx(self, path, fileid=None, filelog=None):
273 def filectx(self, path, fileid=None, filelog=None):
274 """get a file context from this changeset"""
274 """get a file context from this changeset"""
275 if fileid is None:
275 if fileid is None:
276 fileid = self.filenode(path)
276 fileid = self.filenode(path)
277 return filectx(self._repo, path, fileid=fileid,
277 return filectx(self._repo, path, fileid=fileid,
278 changectx=self, filelog=filelog)
278 changectx=self, filelog=filelog)
279
279
280 def ancestor(self, c2):
280 def ancestor(self, c2):
281 """
281 """
282 return the ancestor context of self and c2
282 return the ancestor context of self and c2
283 """
283 """
284 # deal with workingctxs
284 # deal with workingctxs
285 n2 = c2._node
285 n2 = c2._node
286 if n2 is None:
286 if n2 is None:
287 n2 = c2._parents[0]._node
287 n2 = c2._parents[0]._node
288 n = self._repo.changelog.ancestor(self._node, n2)
288 n = self._repo.changelog.ancestor(self._node, n2)
289 return changectx(self._repo, n)
289 return changectx(self._repo, n)
290
290
291 def descendant(self, other):
292 """True if other is descendant of this changeset"""
293 return self._repo.changelog.descendant(self._rev, other._rev)
294
291 def walk(self, match):
295 def walk(self, match):
292 fset = set(match.files())
296 fset = set(match.files())
293 # for dirstate.walk, files=['.'] means "walk the whole tree".
297 # for dirstate.walk, files=['.'] means "walk the whole tree".
294 # follow that here, too
298 # follow that here, too
295 fset.discard('.')
299 fset.discard('.')
296 for fn in self:
300 for fn in self:
297 if fn in fset:
301 if fn in fset:
298 # specified pattern is the exact name
302 # specified pattern is the exact name
299 fset.remove(fn)
303 fset.remove(fn)
300 if match(fn):
304 if match(fn):
301 yield fn
305 yield fn
302 for fn in sorted(fset):
306 for fn in sorted(fset):
303 if fn in self._dirs:
307 if fn in self._dirs:
304 # specified pattern is a directory
308 # specified pattern is a directory
305 continue
309 continue
306 if match.bad(fn, _('no such file in rev %s') % self) and match(fn):
310 if match.bad(fn, _('no such file in rev %s') % self) and match(fn):
307 yield fn
311 yield fn
308
312
309 def sub(self, path):
313 def sub(self, path):
310 return subrepo.subrepo(self, path)
314 return subrepo.subrepo(self, path)
311
315
312 def match(self, pats=[], include=None, exclude=None, default='glob'):
316 def match(self, pats=[], include=None, exclude=None, default='glob'):
313 r = self._repo
317 r = self._repo
314 return matchmod.match(r.root, r.getcwd(), pats,
318 return matchmod.match(r.root, r.getcwd(), pats,
315 include, exclude, default,
319 include, exclude, default,
316 auditor=r.auditor, ctx=self)
320 auditor=r.auditor, ctx=self)
317
321
318 def diff(self, ctx2=None, match=None, **opts):
322 def diff(self, ctx2=None, match=None, **opts):
319 """Returns a diff generator for the given contexts and matcher"""
323 """Returns a diff generator for the given contexts and matcher"""
320 if ctx2 is None:
324 if ctx2 is None:
321 ctx2 = self.p1()
325 ctx2 = self.p1()
322 if ctx2 is not None and not isinstance(ctx2, changectx):
326 if ctx2 is not None and not isinstance(ctx2, changectx):
323 ctx2 = self._repo[ctx2]
327 ctx2 = self._repo[ctx2]
324 diffopts = patch.diffopts(self._repo.ui, opts)
328 diffopts = patch.diffopts(self._repo.ui, opts)
325 return patch.diff(self._repo, ctx2.node(), self.node(),
329 return patch.diff(self._repo, ctx2.node(), self.node(),
326 match=match, opts=diffopts)
330 match=match, opts=diffopts)
327
331
328 @propertycache
332 @propertycache
329 def _dirs(self):
333 def _dirs(self):
330 dirs = set()
334 dirs = set()
331 for f in self._manifest:
335 for f in self._manifest:
332 pos = f.rfind('/')
336 pos = f.rfind('/')
333 while pos != -1:
337 while pos != -1:
334 f = f[:pos]
338 f = f[:pos]
335 if f in dirs:
339 if f in dirs:
336 break # dirs already contains this and above
340 break # dirs already contains this and above
337 dirs.add(f)
341 dirs.add(f)
338 pos = f.rfind('/')
342 pos = f.rfind('/')
339 return dirs
343 return dirs
340
344
341 def dirs(self):
345 def dirs(self):
342 return self._dirs
346 return self._dirs
343
347
344 class filectx(object):
348 class filectx(object):
345 """A filecontext object makes access to data related to a particular
349 """A filecontext object makes access to data related to a particular
346 filerevision convenient."""
350 filerevision convenient."""
347 def __init__(self, repo, path, changeid=None, fileid=None,
351 def __init__(self, repo, path, changeid=None, fileid=None,
348 filelog=None, changectx=None):
352 filelog=None, changectx=None):
349 """changeid can be a changeset revision, node, or tag.
353 """changeid can be a changeset revision, node, or tag.
350 fileid can be a file revision or node."""
354 fileid can be a file revision or node."""
351 self._repo = repo
355 self._repo = repo
352 self._path = path
356 self._path = path
353
357
354 assert (changeid is not None
358 assert (changeid is not None
355 or fileid is not None
359 or fileid is not None
356 or changectx is not None), \
360 or changectx is not None), \
357 ("bad args: changeid=%r, fileid=%r, changectx=%r"
361 ("bad args: changeid=%r, fileid=%r, changectx=%r"
358 % (changeid, fileid, changectx))
362 % (changeid, fileid, changectx))
359
363
360 if filelog:
364 if filelog:
361 self._filelog = filelog
365 self._filelog = filelog
362
366
363 if changeid is not None:
367 if changeid is not None:
364 self._changeid = changeid
368 self._changeid = changeid
365 if changectx is not None:
369 if changectx is not None:
366 self._changectx = changectx
370 self._changectx = changectx
367 if fileid is not None:
371 if fileid is not None:
368 self._fileid = fileid
372 self._fileid = fileid
369
373
370 @propertycache
374 @propertycache
371 def _changectx(self):
375 def _changectx(self):
372 return changectx(self._repo, self._changeid)
376 return changectx(self._repo, self._changeid)
373
377
374 @propertycache
378 @propertycache
375 def _filelog(self):
379 def _filelog(self):
376 return self._repo.file(self._path)
380 return self._repo.file(self._path)
377
381
378 @propertycache
382 @propertycache
379 def _changeid(self):
383 def _changeid(self):
380 if '_changectx' in self.__dict__:
384 if '_changectx' in self.__dict__:
381 return self._changectx.rev()
385 return self._changectx.rev()
382 else:
386 else:
383 return self._filelog.linkrev(self._filerev)
387 return self._filelog.linkrev(self._filerev)
384
388
385 @propertycache
389 @propertycache
386 def _filenode(self):
390 def _filenode(self):
387 if '_fileid' in self.__dict__:
391 if '_fileid' in self.__dict__:
388 return self._filelog.lookup(self._fileid)
392 return self._filelog.lookup(self._fileid)
389 else:
393 else:
390 return self._changectx.filenode(self._path)
394 return self._changectx.filenode(self._path)
391
395
392 @propertycache
396 @propertycache
393 def _filerev(self):
397 def _filerev(self):
394 return self._filelog.rev(self._filenode)
398 return self._filelog.rev(self._filenode)
395
399
396 @propertycache
400 @propertycache
397 def _repopath(self):
401 def _repopath(self):
398 return self._path
402 return self._path
399
403
400 def __nonzero__(self):
404 def __nonzero__(self):
401 try:
405 try:
402 self._filenode
406 self._filenode
403 return True
407 return True
404 except error.LookupError:
408 except error.LookupError:
405 # file is missing
409 # file is missing
406 return False
410 return False
407
411
408 def __str__(self):
412 def __str__(self):
409 return "%s@%s" % (self.path(), short(self.node()))
413 return "%s@%s" % (self.path(), short(self.node()))
410
414
411 def __repr__(self):
415 def __repr__(self):
412 return "<filectx %s>" % str(self)
416 return "<filectx %s>" % str(self)
413
417
414 def __hash__(self):
418 def __hash__(self):
415 try:
419 try:
416 return hash((self._path, self._filenode))
420 return hash((self._path, self._filenode))
417 except AttributeError:
421 except AttributeError:
418 return id(self)
422 return id(self)
419
423
420 def __eq__(self, other):
424 def __eq__(self, other):
421 try:
425 try:
422 return (self._path == other._path
426 return (self._path == other._path
423 and self._filenode == other._filenode)
427 and self._filenode == other._filenode)
424 except AttributeError:
428 except AttributeError:
425 return False
429 return False
426
430
427 def __ne__(self, other):
431 def __ne__(self, other):
428 return not (self == other)
432 return not (self == other)
429
433
430 def filectx(self, fileid):
434 def filectx(self, fileid):
431 '''opens an arbitrary revision of the file without
435 '''opens an arbitrary revision of the file without
432 opening a new filelog'''
436 opening a new filelog'''
433 return filectx(self._repo, self._path, fileid=fileid,
437 return filectx(self._repo, self._path, fileid=fileid,
434 filelog=self._filelog)
438 filelog=self._filelog)
435
439
436 def filerev(self):
440 def filerev(self):
437 return self._filerev
441 return self._filerev
438 def filenode(self):
442 def filenode(self):
439 return self._filenode
443 return self._filenode
440 def flags(self):
444 def flags(self):
441 return self._changectx.flags(self._path)
445 return self._changectx.flags(self._path)
442 def filelog(self):
446 def filelog(self):
443 return self._filelog
447 return self._filelog
444
448
445 def rev(self):
449 def rev(self):
446 if '_changectx' in self.__dict__:
450 if '_changectx' in self.__dict__:
447 return self._changectx.rev()
451 return self._changectx.rev()
448 if '_changeid' in self.__dict__:
452 if '_changeid' in self.__dict__:
449 return self._changectx.rev()
453 return self._changectx.rev()
450 return self._filelog.linkrev(self._filerev)
454 return self._filelog.linkrev(self._filerev)
451
455
452 def linkrev(self):
456 def linkrev(self):
453 return self._filelog.linkrev(self._filerev)
457 return self._filelog.linkrev(self._filerev)
454 def node(self):
458 def node(self):
455 return self._changectx.node()
459 return self._changectx.node()
456 def hex(self):
460 def hex(self):
457 return hex(self.node())
461 return hex(self.node())
458 def user(self):
462 def user(self):
459 return self._changectx.user()
463 return self._changectx.user()
460 def date(self):
464 def date(self):
461 return self._changectx.date()
465 return self._changectx.date()
462 def files(self):
466 def files(self):
463 return self._changectx.files()
467 return self._changectx.files()
464 def description(self):
468 def description(self):
465 return self._changectx.description()
469 return self._changectx.description()
466 def branch(self):
470 def branch(self):
467 return self._changectx.branch()
471 return self._changectx.branch()
468 def extra(self):
472 def extra(self):
469 return self._changectx.extra()
473 return self._changectx.extra()
470 def manifest(self):
474 def manifest(self):
471 return self._changectx.manifest()
475 return self._changectx.manifest()
472 def changectx(self):
476 def changectx(self):
473 return self._changectx
477 return self._changectx
474
478
475 def data(self):
479 def data(self):
476 return self._filelog.read(self._filenode)
480 return self._filelog.read(self._filenode)
477 def path(self):
481 def path(self):
478 return self._path
482 return self._path
479 def size(self):
483 def size(self):
480 return self._filelog.size(self._filerev)
484 return self._filelog.size(self._filerev)
481
485
482 def isbinary(self):
486 def isbinary(self):
483 try:
487 try:
484 return util.binary(self.data())
488 return util.binary(self.data())
485 except IOError:
489 except IOError:
486 return False
490 return False
487
491
488 def cmp(self, fctx):
492 def cmp(self, fctx):
489 """compare with other file context
493 """compare with other file context
490
494
491 returns True if different than fctx.
495 returns True if different than fctx.
492 """
496 """
493 if (fctx._filerev is None
497 if (fctx._filerev is None
494 and (self._repo._encodefilterpats
498 and (self._repo._encodefilterpats
495 # if file data starts with '\1\n', empty metadata block is
499 # if file data starts with '\1\n', empty metadata block is
496 # prepended, which adds 4 bytes to filelog.size().
500 # prepended, which adds 4 bytes to filelog.size().
497 or self.size() - 4 == fctx.size())
501 or self.size() - 4 == fctx.size())
498 or self.size() == fctx.size()):
502 or self.size() == fctx.size()):
499 return self._filelog.cmp(self._filenode, fctx.data())
503 return self._filelog.cmp(self._filenode, fctx.data())
500
504
501 return True
505 return True
502
506
503 def renamed(self):
507 def renamed(self):
504 """check if file was actually renamed in this changeset revision
508 """check if file was actually renamed in this changeset revision
505
509
506 If rename logged in file revision, we report copy for changeset only
510 If rename logged in file revision, we report copy for changeset only
507 if file revisions linkrev points back to the changeset in question
511 if file revisions linkrev points back to the changeset in question
508 or both changeset parents contain different file revisions.
512 or both changeset parents contain different file revisions.
509 """
513 """
510
514
511 renamed = self._filelog.renamed(self._filenode)
515 renamed = self._filelog.renamed(self._filenode)
512 if not renamed:
516 if not renamed:
513 return renamed
517 return renamed
514
518
515 if self.rev() == self.linkrev():
519 if self.rev() == self.linkrev():
516 return renamed
520 return renamed
517
521
518 name = self.path()
522 name = self.path()
519 fnode = self._filenode
523 fnode = self._filenode
520 for p in self._changectx.parents():
524 for p in self._changectx.parents():
521 try:
525 try:
522 if fnode == p.filenode(name):
526 if fnode == p.filenode(name):
523 return None
527 return None
524 except error.LookupError:
528 except error.LookupError:
525 pass
529 pass
526 return renamed
530 return renamed
527
531
528 def parents(self):
532 def parents(self):
529 p = self._path
533 p = self._path
530 fl = self._filelog
534 fl = self._filelog
531 pl = [(p, n, fl) for n in self._filelog.parents(self._filenode)]
535 pl = [(p, n, fl) for n in self._filelog.parents(self._filenode)]
532
536
533 r = self._filelog.renamed(self._filenode)
537 r = self._filelog.renamed(self._filenode)
534 if r:
538 if r:
535 pl[0] = (r[0], r[1], None)
539 pl[0] = (r[0], r[1], None)
536
540
537 return [filectx(self._repo, p, fileid=n, filelog=l)
541 return [filectx(self._repo, p, fileid=n, filelog=l)
538 for p, n, l in pl if n != nullid]
542 for p, n, l in pl if n != nullid]
539
543
540 def p1(self):
544 def p1(self):
541 return self.parents()[0]
545 return self.parents()[0]
542
546
543 def p2(self):
547 def p2(self):
544 p = self.parents()
548 p = self.parents()
545 if len(p) == 2:
549 if len(p) == 2:
546 return p[1]
550 return p[1]
547 return filectx(self._repo, self._path, fileid=-1, filelog=self._filelog)
551 return filectx(self._repo, self._path, fileid=-1, filelog=self._filelog)
548
552
549 def children(self):
553 def children(self):
550 # hard for renames
554 # hard for renames
551 c = self._filelog.children(self._filenode)
555 c = self._filelog.children(self._filenode)
552 return [filectx(self._repo, self._path, fileid=x,
556 return [filectx(self._repo, self._path, fileid=x,
553 filelog=self._filelog) for x in c]
557 filelog=self._filelog) for x in c]
554
558
555 def annotate(self, follow=False, linenumber=None, diffopts=None):
559 def annotate(self, follow=False, linenumber=None, diffopts=None):
556 '''returns a list of tuples of (ctx, line) for each line
560 '''returns a list of tuples of (ctx, line) for each line
557 in the file, where ctx is the filectx of the node where
561 in the file, where ctx is the filectx of the node where
558 that line was last changed.
562 that line was last changed.
559 This returns tuples of ((ctx, linenumber), line) for each line,
563 This returns tuples of ((ctx, linenumber), line) for each line,
560 if "linenumber" parameter is NOT "None".
564 if "linenumber" parameter is NOT "None".
561 In such tuples, linenumber means one at the first appearance
565 In such tuples, linenumber means one at the first appearance
562 in the managed file.
566 in the managed file.
563 To reduce annotation cost,
567 To reduce annotation cost,
564 this returns fixed value(False is used) as linenumber,
568 this returns fixed value(False is used) as linenumber,
565 if "linenumber" parameter is "False".'''
569 if "linenumber" parameter is "False".'''
566
570
567 def decorate_compat(text, rev):
571 def decorate_compat(text, rev):
568 return ([rev] * len(text.splitlines()), text)
572 return ([rev] * len(text.splitlines()), text)
569
573
570 def without_linenumber(text, rev):
574 def without_linenumber(text, rev):
571 return ([(rev, False)] * len(text.splitlines()), text)
575 return ([(rev, False)] * len(text.splitlines()), text)
572
576
573 def with_linenumber(text, rev):
577 def with_linenumber(text, rev):
574 size = len(text.splitlines())
578 size = len(text.splitlines())
575 return ([(rev, i) for i in xrange(1, size + 1)], text)
579 return ([(rev, i) for i in xrange(1, size + 1)], text)
576
580
577 decorate = (((linenumber is None) and decorate_compat) or
581 decorate = (((linenumber is None) and decorate_compat) or
578 (linenumber and with_linenumber) or
582 (linenumber and with_linenumber) or
579 without_linenumber)
583 without_linenumber)
580
584
581 def pair(parent, child):
585 def pair(parent, child):
582 blocks = mdiff.allblocks(parent[1], child[1], opts=diffopts,
586 blocks = mdiff.allblocks(parent[1], child[1], opts=diffopts,
583 refine=True)
587 refine=True)
584 for (a1, a2, b1, b2), t in blocks:
588 for (a1, a2, b1, b2), t in blocks:
585 # Changed blocks ('!') or blocks made only of blank lines ('~')
589 # Changed blocks ('!') or blocks made only of blank lines ('~')
586 # belong to the child.
590 # belong to the child.
587 if t == '=':
591 if t == '=':
588 child[0][b1:b2] = parent[0][a1:a2]
592 child[0][b1:b2] = parent[0][a1:a2]
589 return child
593 return child
590
594
591 getlog = util.lrucachefunc(lambda x: self._repo.file(x))
595 getlog = util.lrucachefunc(lambda x: self._repo.file(x))
592 def getctx(path, fileid):
596 def getctx(path, fileid):
593 log = path == self._path and self._filelog or getlog(path)
597 log = path == self._path and self._filelog or getlog(path)
594 return filectx(self._repo, path, fileid=fileid, filelog=log)
598 return filectx(self._repo, path, fileid=fileid, filelog=log)
595 getctx = util.lrucachefunc(getctx)
599 getctx = util.lrucachefunc(getctx)
596
600
597 def parents(f):
601 def parents(f):
598 # we want to reuse filectx objects as much as possible
602 # we want to reuse filectx objects as much as possible
599 p = f._path
603 p = f._path
600 if f._filerev is None: # working dir
604 if f._filerev is None: # working dir
601 pl = [(n.path(), n.filerev()) for n in f.parents()]
605 pl = [(n.path(), n.filerev()) for n in f.parents()]
602 else:
606 else:
603 pl = [(p, n) for n in f._filelog.parentrevs(f._filerev)]
607 pl = [(p, n) for n in f._filelog.parentrevs(f._filerev)]
604
608
605 if follow:
609 if follow:
606 r = f.renamed()
610 r = f.renamed()
607 if r:
611 if r:
608 pl[0] = (r[0], getlog(r[0]).rev(r[1]))
612 pl[0] = (r[0], getlog(r[0]).rev(r[1]))
609
613
610 return [getctx(p, n) for p, n in pl if n != nullrev]
614 return [getctx(p, n) for p, n in pl if n != nullrev]
611
615
612 # use linkrev to find the first changeset where self appeared
616 # use linkrev to find the first changeset where self appeared
613 if self.rev() != self.linkrev():
617 if self.rev() != self.linkrev():
614 base = self.filectx(self.filerev())
618 base = self.filectx(self.filerev())
615 else:
619 else:
616 base = self
620 base = self
617
621
618 # This algorithm would prefer to be recursive, but Python is a
622 # This algorithm would prefer to be recursive, but Python is a
619 # bit recursion-hostile. Instead we do an iterative
623 # bit recursion-hostile. Instead we do an iterative
620 # depth-first search.
624 # depth-first search.
621
625
622 visit = [base]
626 visit = [base]
623 hist = {}
627 hist = {}
624 pcache = {}
628 pcache = {}
625 needed = {base: 1}
629 needed = {base: 1}
626 while visit:
630 while visit:
627 f = visit[-1]
631 f = visit[-1]
628 if f not in pcache:
632 if f not in pcache:
629 pcache[f] = parents(f)
633 pcache[f] = parents(f)
630
634
631 ready = True
635 ready = True
632 pl = pcache[f]
636 pl = pcache[f]
633 for p in pl:
637 for p in pl:
634 if p not in hist:
638 if p not in hist:
635 ready = False
639 ready = False
636 visit.append(p)
640 visit.append(p)
637 needed[p] = needed.get(p, 0) + 1
641 needed[p] = needed.get(p, 0) + 1
638 if ready:
642 if ready:
639 visit.pop()
643 visit.pop()
640 curr = decorate(f.data(), f)
644 curr = decorate(f.data(), f)
641 for p in pl:
645 for p in pl:
642 curr = pair(hist[p], curr)
646 curr = pair(hist[p], curr)
643 if needed[p] == 1:
647 if needed[p] == 1:
644 del hist[p]
648 del hist[p]
645 else:
649 else:
646 needed[p] -= 1
650 needed[p] -= 1
647
651
648 hist[f] = curr
652 hist[f] = curr
649 pcache[f] = []
653 pcache[f] = []
650
654
651 return zip(hist[base][0], hist[base][1].splitlines(True))
655 return zip(hist[base][0], hist[base][1].splitlines(True))
652
656
653 def ancestor(self, fc2, actx):
657 def ancestor(self, fc2, actx):
654 """
658 """
655 find the common ancestor file context, if any, of self, and fc2
659 find the common ancestor file context, if any, of self, and fc2
656
660
657 actx must be the changectx of the common ancestor
661 actx must be the changectx of the common ancestor
658 of self's and fc2's respective changesets.
662 of self's and fc2's respective changesets.
659 """
663 """
660
664
661 # the easy case: no (relevant) renames
665 # the easy case: no (relevant) renames
662 if fc2.path() == self.path() and self.path() in actx:
666 if fc2.path() == self.path() and self.path() in actx:
663 return actx[self.path()]
667 return actx[self.path()]
664
668
665 # the next easiest cases: unambiguous predecessor (name trumps
669 # the next easiest cases: unambiguous predecessor (name trumps
666 # history)
670 # history)
667 if self.path() in actx and fc2.path() not in actx:
671 if self.path() in actx and fc2.path() not in actx:
668 return actx[self.path()]
672 return actx[self.path()]
669 if fc2.path() in actx and self.path() not in actx:
673 if fc2.path() in actx and self.path() not in actx:
670 return actx[fc2.path()]
674 return actx[fc2.path()]
671
675
672 # prime the ancestor cache for the working directory
676 # prime the ancestor cache for the working directory
673 acache = {}
677 acache = {}
674 for c in (self, fc2):
678 for c in (self, fc2):
675 if c._filerev is None:
679 if c._filerev is None:
676 pl = [(n.path(), n.filenode()) for n in c.parents()]
680 pl = [(n.path(), n.filenode()) for n in c.parents()]
677 acache[(c._path, None)] = pl
681 acache[(c._path, None)] = pl
678
682
679 flcache = {self._repopath:self._filelog, fc2._repopath:fc2._filelog}
683 flcache = {self._repopath:self._filelog, fc2._repopath:fc2._filelog}
680 def parents(vertex):
684 def parents(vertex):
681 if vertex in acache:
685 if vertex in acache:
682 return acache[vertex]
686 return acache[vertex]
683 f, n = vertex
687 f, n = vertex
684 if f not in flcache:
688 if f not in flcache:
685 flcache[f] = self._repo.file(f)
689 flcache[f] = self._repo.file(f)
686 fl = flcache[f]
690 fl = flcache[f]
687 pl = [(f, p) for p in fl.parents(n) if p != nullid]
691 pl = [(f, p) for p in fl.parents(n) if p != nullid]
688 re = fl.renamed(n)
692 re = fl.renamed(n)
689 if re:
693 if re:
690 pl.append(re)
694 pl.append(re)
691 acache[vertex] = pl
695 acache[vertex] = pl
692 return pl
696 return pl
693
697
694 a, b = (self._path, self._filenode), (fc2._path, fc2._filenode)
698 a, b = (self._path, self._filenode), (fc2._path, fc2._filenode)
695 v = ancestor.ancestor(a, b, parents)
699 v = ancestor.ancestor(a, b, parents)
696 if v:
700 if v:
697 f, n = v
701 f, n = v
698 return filectx(self._repo, f, fileid=n, filelog=flcache[f])
702 return filectx(self._repo, f, fileid=n, filelog=flcache[f])
699
703
700 return None
704 return None
701
705
702 def ancestors(self, followfirst=False):
706 def ancestors(self, followfirst=False):
703 visit = {}
707 visit = {}
704 c = self
708 c = self
705 cut = followfirst and 1 or None
709 cut = followfirst and 1 or None
706 while True:
710 while True:
707 for parent in c.parents()[:cut]:
711 for parent in c.parents()[:cut]:
708 visit[(parent.rev(), parent.node())] = parent
712 visit[(parent.rev(), parent.node())] = parent
709 if not visit:
713 if not visit:
710 break
714 break
711 c = visit.pop(max(visit))
715 c = visit.pop(max(visit))
712 yield c
716 yield c
713
717
714 def copies(self, c2):
718 def copies(self, c2):
715 if not util.safehasattr(self, "_copycache"):
719 if not util.safehasattr(self, "_copycache"):
716 self._copycache = {}
720 self._copycache = {}
717 sc2 = str(c2)
721 sc2 = str(c2)
718 if sc2 not in self._copycache:
722 if sc2 not in self._copycache:
719 self._copycache[sc2] = copies.pathcopies(c2)
723 self._copycache[sc2] = copies.pathcopies(c2)
720 return self._copycache[sc2]
724 return self._copycache[sc2]
721
725
722 class workingctx(changectx):
726 class workingctx(changectx):
723 """A workingctx object makes access to data related to
727 """A workingctx object makes access to data related to
724 the current working directory convenient.
728 the current working directory convenient.
725 date - any valid date string or (unixtime, offset), or None.
729 date - any valid date string or (unixtime, offset), or None.
726 user - username string, or None.
730 user - username string, or None.
727 extra - a dictionary of extra values, or None.
731 extra - a dictionary of extra values, or None.
728 changes - a list of file lists as returned by localrepo.status()
732 changes - a list of file lists as returned by localrepo.status()
729 or None to use the repository status.
733 or None to use the repository status.
730 """
734 """
731 def __init__(self, repo, text="", user=None, date=None, extra=None,
735 def __init__(self, repo, text="", user=None, date=None, extra=None,
732 changes=None):
736 changes=None):
733 self._repo = repo
737 self._repo = repo
734 self._rev = None
738 self._rev = None
735 self._node = None
739 self._node = None
736 self._text = text
740 self._text = text
737 if date:
741 if date:
738 self._date = util.parsedate(date)
742 self._date = util.parsedate(date)
739 if user:
743 if user:
740 self._user = user
744 self._user = user
741 if changes:
745 if changes:
742 self._status = list(changes[:4])
746 self._status = list(changes[:4])
743 self._unknown = changes[4]
747 self._unknown = changes[4]
744 self._ignored = changes[5]
748 self._ignored = changes[5]
745 self._clean = changes[6]
749 self._clean = changes[6]
746 else:
750 else:
747 self._unknown = None
751 self._unknown = None
748 self._ignored = None
752 self._ignored = None
749 self._clean = None
753 self._clean = None
750
754
751 self._extra = {}
755 self._extra = {}
752 if extra:
756 if extra:
753 self._extra = extra.copy()
757 self._extra = extra.copy()
754 if 'branch' not in self._extra:
758 if 'branch' not in self._extra:
755 try:
759 try:
756 branch = encoding.fromlocal(self._repo.dirstate.branch())
760 branch = encoding.fromlocal(self._repo.dirstate.branch())
757 except UnicodeDecodeError:
761 except UnicodeDecodeError:
758 raise util.Abort(_('branch name not in UTF-8!'))
762 raise util.Abort(_('branch name not in UTF-8!'))
759 self._extra['branch'] = branch
763 self._extra['branch'] = branch
760 if self._extra['branch'] == '':
764 if self._extra['branch'] == '':
761 self._extra['branch'] = 'default'
765 self._extra['branch'] = 'default'
762
766
763 def __str__(self):
767 def __str__(self):
764 return str(self._parents[0]) + "+"
768 return str(self._parents[0]) + "+"
765
769
766 def __repr__(self):
770 def __repr__(self):
767 return "<workingctx %s>" % str(self)
771 return "<workingctx %s>" % str(self)
768
772
769 def __nonzero__(self):
773 def __nonzero__(self):
770 return True
774 return True
771
775
772 def __contains__(self, key):
776 def __contains__(self, key):
773 return self._repo.dirstate[key] not in "?r"
777 return self._repo.dirstate[key] not in "?r"
774
778
775 def _buildflagfunc(self):
779 def _buildflagfunc(self):
776 # Create a fallback function for getting file flags when the
780 # Create a fallback function for getting file flags when the
777 # filesystem doesn't support them
781 # filesystem doesn't support them
778
782
779 copiesget = self._repo.dirstate.copies().get
783 copiesget = self._repo.dirstate.copies().get
780
784
781 if len(self._parents) < 2:
785 if len(self._parents) < 2:
782 # when we have one parent, it's easy: copy from parent
786 # when we have one parent, it's easy: copy from parent
783 man = self._parents[0].manifest()
787 man = self._parents[0].manifest()
784 def func(f):
788 def func(f):
785 f = copiesget(f, f)
789 f = copiesget(f, f)
786 return man.flags(f)
790 return man.flags(f)
787 else:
791 else:
788 # merges are tricky: we try to reconstruct the unstored
792 # merges are tricky: we try to reconstruct the unstored
789 # result from the merge (issue1802)
793 # result from the merge (issue1802)
790 p1, p2 = self._parents
794 p1, p2 = self._parents
791 pa = p1.ancestor(p2)
795 pa = p1.ancestor(p2)
792 m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest()
796 m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest()
793
797
794 def func(f):
798 def func(f):
795 f = copiesget(f, f) # may be wrong for merges with copies
799 f = copiesget(f, f) # may be wrong for merges with copies
796 fl1, fl2, fla = m1.flags(f), m2.flags(f), ma.flags(f)
800 fl1, fl2, fla = m1.flags(f), m2.flags(f), ma.flags(f)
797 if fl1 == fl2:
801 if fl1 == fl2:
798 return fl1
802 return fl1
799 if fl1 == fla:
803 if fl1 == fla:
800 return fl2
804 return fl2
801 if fl2 == fla:
805 if fl2 == fla:
802 return fl1
806 return fl1
803 return '' # punt for conflicts
807 return '' # punt for conflicts
804
808
805 return func
809 return func
806
810
807 @propertycache
811 @propertycache
808 def _flagfunc(self):
812 def _flagfunc(self):
809 return self._repo.dirstate.flagfunc(self._buildflagfunc)
813 return self._repo.dirstate.flagfunc(self._buildflagfunc)
810
814
811 @propertycache
815 @propertycache
812 def _manifest(self):
816 def _manifest(self):
813 """generate a manifest corresponding to the working directory"""
817 """generate a manifest corresponding to the working directory"""
814
818
815 man = self._parents[0].manifest().copy()
819 man = self._parents[0].manifest().copy()
816 if len(self._parents) > 1:
820 if len(self._parents) > 1:
817 man2 = self.p2().manifest()
821 man2 = self.p2().manifest()
818 def getman(f):
822 def getman(f):
819 if f in man:
823 if f in man:
820 return man
824 return man
821 return man2
825 return man2
822 else:
826 else:
823 getman = lambda f: man
827 getman = lambda f: man
824
828
825 copied = self._repo.dirstate.copies()
829 copied = self._repo.dirstate.copies()
826 ff = self._flagfunc
830 ff = self._flagfunc
827 modified, added, removed, deleted = self._status
831 modified, added, removed, deleted = self._status
828 for i, l in (("a", added), ("m", modified)):
832 for i, l in (("a", added), ("m", modified)):
829 for f in l:
833 for f in l:
830 orig = copied.get(f, f)
834 orig = copied.get(f, f)
831 man[f] = getman(orig).get(orig, nullid) + i
835 man[f] = getman(orig).get(orig, nullid) + i
832 try:
836 try:
833 man.set(f, ff(f))
837 man.set(f, ff(f))
834 except OSError:
838 except OSError:
835 pass
839 pass
836
840
837 for f in deleted + removed:
841 for f in deleted + removed:
838 if f in man:
842 if f in man:
839 del man[f]
843 del man[f]
840
844
841 return man
845 return man
842
846
843 def __iter__(self):
847 def __iter__(self):
844 d = self._repo.dirstate
848 d = self._repo.dirstate
845 for f in d:
849 for f in d:
846 if d[f] != 'r':
850 if d[f] != 'r':
847 yield f
851 yield f
848
852
849 @propertycache
853 @propertycache
850 def _status(self):
854 def _status(self):
851 return self._repo.status()[:4]
855 return self._repo.status()[:4]
852
856
853 @propertycache
857 @propertycache
854 def _user(self):
858 def _user(self):
855 return self._repo.ui.username()
859 return self._repo.ui.username()
856
860
857 @propertycache
861 @propertycache
858 def _date(self):
862 def _date(self):
859 return util.makedate()
863 return util.makedate()
860
864
861 @propertycache
865 @propertycache
862 def _parents(self):
866 def _parents(self):
863 p = self._repo.dirstate.parents()
867 p = self._repo.dirstate.parents()
864 if p[1] == nullid:
868 if p[1] == nullid:
865 p = p[:-1]
869 p = p[:-1]
866 return [changectx(self._repo, x) for x in p]
870 return [changectx(self._repo, x) for x in p]
867
871
868 def status(self, ignored=False, clean=False, unknown=False):
872 def status(self, ignored=False, clean=False, unknown=False):
869 """Explicit status query
873 """Explicit status query
870 Unless this method is used to query the working copy status, the
874 Unless this method is used to query the working copy status, the
871 _status property will implicitly read the status using its default
875 _status property will implicitly read the status using its default
872 arguments."""
876 arguments."""
873 stat = self._repo.status(ignored=ignored, clean=clean, unknown=unknown)
877 stat = self._repo.status(ignored=ignored, clean=clean, unknown=unknown)
874 self._unknown = self._ignored = self._clean = None
878 self._unknown = self._ignored = self._clean = None
875 if unknown:
879 if unknown:
876 self._unknown = stat[4]
880 self._unknown = stat[4]
877 if ignored:
881 if ignored:
878 self._ignored = stat[5]
882 self._ignored = stat[5]
879 if clean:
883 if clean:
880 self._clean = stat[6]
884 self._clean = stat[6]
881 self._status = stat[:4]
885 self._status = stat[:4]
882 return stat
886 return stat
883
887
884 def manifest(self):
888 def manifest(self):
885 return self._manifest
889 return self._manifest
886 def user(self):
890 def user(self):
887 return self._user or self._repo.ui.username()
891 return self._user or self._repo.ui.username()
888 def date(self):
892 def date(self):
889 return self._date
893 return self._date
890 def description(self):
894 def description(self):
891 return self._text
895 return self._text
892 def files(self):
896 def files(self):
893 return sorted(self._status[0] + self._status[1] + self._status[2])
897 return sorted(self._status[0] + self._status[1] + self._status[2])
894
898
895 def modified(self):
899 def modified(self):
896 return self._status[0]
900 return self._status[0]
897 def added(self):
901 def added(self):
898 return self._status[1]
902 return self._status[1]
899 def removed(self):
903 def removed(self):
900 return self._status[2]
904 return self._status[2]
901 def deleted(self):
905 def deleted(self):
902 return self._status[3]
906 return self._status[3]
903 def unknown(self):
907 def unknown(self):
904 assert self._unknown is not None # must call status first
908 assert self._unknown is not None # must call status first
905 return self._unknown
909 return self._unknown
906 def ignored(self):
910 def ignored(self):
907 assert self._ignored is not None # must call status first
911 assert self._ignored is not None # must call status first
908 return self._ignored
912 return self._ignored
909 def clean(self):
913 def clean(self):
910 assert self._clean is not None # must call status first
914 assert self._clean is not None # must call status first
911 return self._clean
915 return self._clean
912 def branch(self):
916 def branch(self):
913 return encoding.tolocal(self._extra['branch'])
917 return encoding.tolocal(self._extra['branch'])
914 def closesbranch(self):
918 def closesbranch(self):
915 return 'close' in self._extra
919 return 'close' in self._extra
916 def extra(self):
920 def extra(self):
917 return self._extra
921 return self._extra
918
922
919 def tags(self):
923 def tags(self):
920 t = []
924 t = []
921 for p in self.parents():
925 for p in self.parents():
922 t.extend(p.tags())
926 t.extend(p.tags())
923 return t
927 return t
924
928
925 def bookmarks(self):
929 def bookmarks(self):
926 b = []
930 b = []
927 for p in self.parents():
931 for p in self.parents():
928 b.extend(p.bookmarks())
932 b.extend(p.bookmarks())
929 return b
933 return b
930
934
931 def phase(self):
935 def phase(self):
932 phase = phases.draft # default phase to draft
936 phase = phases.draft # default phase to draft
933 for p in self.parents():
937 for p in self.parents():
934 phase = max(phase, p.phase())
938 phase = max(phase, p.phase())
935 return phase
939 return phase
936
940
937 def hidden(self):
941 def hidden(self):
938 return False
942 return False
939
943
940 def children(self):
944 def children(self):
941 return []
945 return []
942
946
943 def flags(self, path):
947 def flags(self, path):
944 if '_manifest' in self.__dict__:
948 if '_manifest' in self.__dict__:
945 try:
949 try:
946 return self._manifest.flags(path)
950 return self._manifest.flags(path)
947 except KeyError:
951 except KeyError:
948 return ''
952 return ''
949
953
950 try:
954 try:
951 return self._flagfunc(path)
955 return self._flagfunc(path)
952 except OSError:
956 except OSError:
953 return ''
957 return ''
954
958
955 def filectx(self, path, filelog=None):
959 def filectx(self, path, filelog=None):
956 """get a file context from the working directory"""
960 """get a file context from the working directory"""
957 return workingfilectx(self._repo, path, workingctx=self,
961 return workingfilectx(self._repo, path, workingctx=self,
958 filelog=filelog)
962 filelog=filelog)
959
963
960 def ancestor(self, c2):
964 def ancestor(self, c2):
961 """return the ancestor context of self and c2"""
965 """return the ancestor context of self and c2"""
962 return self._parents[0].ancestor(c2) # punt on two parents for now
966 return self._parents[0].ancestor(c2) # punt on two parents for now
963
967
964 def walk(self, match):
968 def walk(self, match):
965 return sorted(self._repo.dirstate.walk(match, self.substate.keys(),
969 return sorted(self._repo.dirstate.walk(match, self.substate.keys(),
966 True, False))
970 True, False))
967
971
968 def dirty(self, missing=False, merge=True, branch=True):
972 def dirty(self, missing=False, merge=True, branch=True):
969 "check whether a working directory is modified"
973 "check whether a working directory is modified"
970 # check subrepos first
974 # check subrepos first
971 for s in self.substate:
975 for s in self.substate:
972 if self.sub(s).dirty():
976 if self.sub(s).dirty():
973 return True
977 return True
974 # check current working dir
978 # check current working dir
975 return ((merge and self.p2()) or
979 return ((merge and self.p2()) or
976 (branch and self.branch() != self.p1().branch()) or
980 (branch and self.branch() != self.p1().branch()) or
977 self.modified() or self.added() or self.removed() or
981 self.modified() or self.added() or self.removed() or
978 (missing and self.deleted()))
982 (missing and self.deleted()))
979
983
980 def add(self, list, prefix=""):
984 def add(self, list, prefix=""):
981 join = lambda f: os.path.join(prefix, f)
985 join = lambda f: os.path.join(prefix, f)
982 wlock = self._repo.wlock()
986 wlock = self._repo.wlock()
983 ui, ds = self._repo.ui, self._repo.dirstate
987 ui, ds = self._repo.ui, self._repo.dirstate
984 try:
988 try:
985 rejected = []
989 rejected = []
986 for f in list:
990 for f in list:
987 scmutil.checkportable(ui, join(f))
991 scmutil.checkportable(ui, join(f))
988 p = self._repo.wjoin(f)
992 p = self._repo.wjoin(f)
989 try:
993 try:
990 st = os.lstat(p)
994 st = os.lstat(p)
991 except OSError:
995 except OSError:
992 ui.warn(_("%s does not exist!\n") % join(f))
996 ui.warn(_("%s does not exist!\n") % join(f))
993 rejected.append(f)
997 rejected.append(f)
994 continue
998 continue
995 if st.st_size > 10000000:
999 if st.st_size > 10000000:
996 ui.warn(_("%s: up to %d MB of RAM may be required "
1000 ui.warn(_("%s: up to %d MB of RAM may be required "
997 "to manage this file\n"
1001 "to manage this file\n"
998 "(use 'hg revert %s' to cancel the "
1002 "(use 'hg revert %s' to cancel the "
999 "pending addition)\n")
1003 "pending addition)\n")
1000 % (f, 3 * st.st_size // 1000000, join(f)))
1004 % (f, 3 * st.st_size // 1000000, join(f)))
1001 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
1005 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
1002 ui.warn(_("%s not added: only files and symlinks "
1006 ui.warn(_("%s not added: only files and symlinks "
1003 "supported currently\n") % join(f))
1007 "supported currently\n") % join(f))
1004 rejected.append(p)
1008 rejected.append(p)
1005 elif ds[f] in 'amn':
1009 elif ds[f] in 'amn':
1006 ui.warn(_("%s already tracked!\n") % join(f))
1010 ui.warn(_("%s already tracked!\n") % join(f))
1007 elif ds[f] == 'r':
1011 elif ds[f] == 'r':
1008 ds.normallookup(f)
1012 ds.normallookup(f)
1009 else:
1013 else:
1010 ds.add(f)
1014 ds.add(f)
1011 return rejected
1015 return rejected
1012 finally:
1016 finally:
1013 wlock.release()
1017 wlock.release()
1014
1018
1015 def forget(self, files, prefix=""):
1019 def forget(self, files, prefix=""):
1016 join = lambda f: os.path.join(prefix, f)
1020 join = lambda f: os.path.join(prefix, f)
1017 wlock = self._repo.wlock()
1021 wlock = self._repo.wlock()
1018 try:
1022 try:
1019 rejected = []
1023 rejected = []
1020 for f in files:
1024 for f in files:
1021 if f not in self._repo.dirstate:
1025 if f not in self._repo.dirstate:
1022 self._repo.ui.warn(_("%s not tracked!\n") % join(f))
1026 self._repo.ui.warn(_("%s not tracked!\n") % join(f))
1023 rejected.append(f)
1027 rejected.append(f)
1024 elif self._repo.dirstate[f] != 'a':
1028 elif self._repo.dirstate[f] != 'a':
1025 self._repo.dirstate.remove(f)
1029 self._repo.dirstate.remove(f)
1026 else:
1030 else:
1027 self._repo.dirstate.drop(f)
1031 self._repo.dirstate.drop(f)
1028 return rejected
1032 return rejected
1029 finally:
1033 finally:
1030 wlock.release()
1034 wlock.release()
1031
1035
1032 def ancestors(self):
1036 def ancestors(self):
1033 for a in self._repo.changelog.ancestors(
1037 for a in self._repo.changelog.ancestors(
1034 [p.rev() for p in self._parents]):
1038 [p.rev() for p in self._parents]):
1035 yield changectx(self._repo, a)
1039 yield changectx(self._repo, a)
1036
1040
1037 def undelete(self, list):
1041 def undelete(self, list):
1038 pctxs = self.parents()
1042 pctxs = self.parents()
1039 wlock = self._repo.wlock()
1043 wlock = self._repo.wlock()
1040 try:
1044 try:
1041 for f in list:
1045 for f in list:
1042 if self._repo.dirstate[f] != 'r':
1046 if self._repo.dirstate[f] != 'r':
1043 self._repo.ui.warn(_("%s not removed!\n") % f)
1047 self._repo.ui.warn(_("%s not removed!\n") % f)
1044 else:
1048 else:
1045 fctx = f in pctxs[0] and pctxs[0][f] or pctxs[1][f]
1049 fctx = f in pctxs[0] and pctxs[0][f] or pctxs[1][f]
1046 t = fctx.data()
1050 t = fctx.data()
1047 self._repo.wwrite(f, t, fctx.flags())
1051 self._repo.wwrite(f, t, fctx.flags())
1048 self._repo.dirstate.normal(f)
1052 self._repo.dirstate.normal(f)
1049 finally:
1053 finally:
1050 wlock.release()
1054 wlock.release()
1051
1055
1052 def copy(self, source, dest):
1056 def copy(self, source, dest):
1053 p = self._repo.wjoin(dest)
1057 p = self._repo.wjoin(dest)
1054 if not os.path.lexists(p):
1058 if not os.path.lexists(p):
1055 self._repo.ui.warn(_("%s does not exist!\n") % dest)
1059 self._repo.ui.warn(_("%s does not exist!\n") % dest)
1056 elif not (os.path.isfile(p) or os.path.islink(p)):
1060 elif not (os.path.isfile(p) or os.path.islink(p)):
1057 self._repo.ui.warn(_("copy failed: %s is not a file or a "
1061 self._repo.ui.warn(_("copy failed: %s is not a file or a "
1058 "symbolic link\n") % dest)
1062 "symbolic link\n") % dest)
1059 else:
1063 else:
1060 wlock = self._repo.wlock()
1064 wlock = self._repo.wlock()
1061 try:
1065 try:
1062 if self._repo.dirstate[dest] in '?r':
1066 if self._repo.dirstate[dest] in '?r':
1063 self._repo.dirstate.add(dest)
1067 self._repo.dirstate.add(dest)
1064 self._repo.dirstate.copy(source, dest)
1068 self._repo.dirstate.copy(source, dest)
1065 finally:
1069 finally:
1066 wlock.release()
1070 wlock.release()
1067
1071
1068 def dirs(self):
1072 def dirs(self):
1069 return set(self._repo.dirstate.dirs())
1073 return set(self._repo.dirstate.dirs())
1070
1074
1071 class workingfilectx(filectx):
1075 class workingfilectx(filectx):
1072 """A workingfilectx object makes access to data related to a particular
1076 """A workingfilectx object makes access to data related to a particular
1073 file in the working directory convenient."""
1077 file in the working directory convenient."""
1074 def __init__(self, repo, path, filelog=None, workingctx=None):
1078 def __init__(self, repo, path, filelog=None, workingctx=None):
1075 """changeid can be a changeset revision, node, or tag.
1079 """changeid can be a changeset revision, node, or tag.
1076 fileid can be a file revision or node."""
1080 fileid can be a file revision or node."""
1077 self._repo = repo
1081 self._repo = repo
1078 self._path = path
1082 self._path = path
1079 self._changeid = None
1083 self._changeid = None
1080 self._filerev = self._filenode = None
1084 self._filerev = self._filenode = None
1081
1085
1082 if filelog:
1086 if filelog:
1083 self._filelog = filelog
1087 self._filelog = filelog
1084 if workingctx:
1088 if workingctx:
1085 self._changectx = workingctx
1089 self._changectx = workingctx
1086
1090
1087 @propertycache
1091 @propertycache
1088 def _changectx(self):
1092 def _changectx(self):
1089 return workingctx(self._repo)
1093 return workingctx(self._repo)
1090
1094
1091 def __nonzero__(self):
1095 def __nonzero__(self):
1092 return True
1096 return True
1093
1097
1094 def __str__(self):
1098 def __str__(self):
1095 return "%s@%s" % (self.path(), self._changectx)
1099 return "%s@%s" % (self.path(), self._changectx)
1096
1100
1097 def __repr__(self):
1101 def __repr__(self):
1098 return "<workingfilectx %s>" % str(self)
1102 return "<workingfilectx %s>" % str(self)
1099
1103
1100 def data(self):
1104 def data(self):
1101 return self._repo.wread(self._path)
1105 return self._repo.wread(self._path)
1102 def renamed(self):
1106 def renamed(self):
1103 rp = self._repo.dirstate.copied(self._path)
1107 rp = self._repo.dirstate.copied(self._path)
1104 if not rp:
1108 if not rp:
1105 return None
1109 return None
1106 return rp, self._changectx._parents[0]._manifest.get(rp, nullid)
1110 return rp, self._changectx._parents[0]._manifest.get(rp, nullid)
1107
1111
1108 def parents(self):
1112 def parents(self):
1109 '''return parent filectxs, following copies if necessary'''
1113 '''return parent filectxs, following copies if necessary'''
1110 def filenode(ctx, path):
1114 def filenode(ctx, path):
1111 return ctx._manifest.get(path, nullid)
1115 return ctx._manifest.get(path, nullid)
1112
1116
1113 path = self._path
1117 path = self._path
1114 fl = self._filelog
1118 fl = self._filelog
1115 pcl = self._changectx._parents
1119 pcl = self._changectx._parents
1116 renamed = self.renamed()
1120 renamed = self.renamed()
1117
1121
1118 if renamed:
1122 if renamed:
1119 pl = [renamed + (None,)]
1123 pl = [renamed + (None,)]
1120 else:
1124 else:
1121 pl = [(path, filenode(pcl[0], path), fl)]
1125 pl = [(path, filenode(pcl[0], path), fl)]
1122
1126
1123 for pc in pcl[1:]:
1127 for pc in pcl[1:]:
1124 pl.append((path, filenode(pc, path), fl))
1128 pl.append((path, filenode(pc, path), fl))
1125
1129
1126 return [filectx(self._repo, p, fileid=n, filelog=l)
1130 return [filectx(self._repo, p, fileid=n, filelog=l)
1127 for p, n, l in pl if n != nullid]
1131 for p, n, l in pl if n != nullid]
1128
1132
1129 def children(self):
1133 def children(self):
1130 return []
1134 return []
1131
1135
1132 def size(self):
1136 def size(self):
1133 return os.lstat(self._repo.wjoin(self._path)).st_size
1137 return os.lstat(self._repo.wjoin(self._path)).st_size
1134 def date(self):
1138 def date(self):
1135 t, tz = self._changectx.date()
1139 t, tz = self._changectx.date()
1136 try:
1140 try:
1137 return (int(os.lstat(self._repo.wjoin(self._path)).st_mtime), tz)
1141 return (int(os.lstat(self._repo.wjoin(self._path)).st_mtime), tz)
1138 except OSError, err:
1142 except OSError, err:
1139 if err.errno != errno.ENOENT:
1143 if err.errno != errno.ENOENT:
1140 raise
1144 raise
1141 return (t, tz)
1145 return (t, tz)
1142
1146
1143 def cmp(self, fctx):
1147 def cmp(self, fctx):
1144 """compare with other file context
1148 """compare with other file context
1145
1149
1146 returns True if different than fctx.
1150 returns True if different than fctx.
1147 """
1151 """
1148 # fctx should be a filectx (not a workingfilectx)
1152 # fctx should be a filectx (not a workingfilectx)
1149 # invert comparison to reuse the same code path
1153 # invert comparison to reuse the same code path
1150 return fctx.cmp(self)
1154 return fctx.cmp(self)
1151
1155
1152 class memctx(object):
1156 class memctx(object):
1153 """Use memctx to perform in-memory commits via localrepo.commitctx().
1157 """Use memctx to perform in-memory commits via localrepo.commitctx().
1154
1158
1155 Revision information is supplied at initialization time while
1159 Revision information is supplied at initialization time while
1156 related files data and is made available through a callback
1160 related files data and is made available through a callback
1157 mechanism. 'repo' is the current localrepo, 'parents' is a
1161 mechanism. 'repo' is the current localrepo, 'parents' is a
1158 sequence of two parent revisions identifiers (pass None for every
1162 sequence of two parent revisions identifiers (pass None for every
1159 missing parent), 'text' is the commit message and 'files' lists
1163 missing parent), 'text' is the commit message and 'files' lists
1160 names of files touched by the revision (normalized and relative to
1164 names of files touched by the revision (normalized and relative to
1161 repository root).
1165 repository root).
1162
1166
1163 filectxfn(repo, memctx, path) is a callable receiving the
1167 filectxfn(repo, memctx, path) is a callable receiving the
1164 repository, the current memctx object and the normalized path of
1168 repository, the current memctx object and the normalized path of
1165 requested file, relative to repository root. It is fired by the
1169 requested file, relative to repository root. It is fired by the
1166 commit function for every file in 'files', but calls order is
1170 commit function for every file in 'files', but calls order is
1167 undefined. If the file is available in the revision being
1171 undefined. If the file is available in the revision being
1168 committed (updated or added), filectxfn returns a memfilectx
1172 committed (updated or added), filectxfn returns a memfilectx
1169 object. If the file was removed, filectxfn raises an
1173 object. If the file was removed, filectxfn raises an
1170 IOError. Moved files are represented by marking the source file
1174 IOError. Moved files are represented by marking the source file
1171 removed and the new file added with copy information (see
1175 removed and the new file added with copy information (see
1172 memfilectx).
1176 memfilectx).
1173
1177
1174 user receives the committer name and defaults to current
1178 user receives the committer name and defaults to current
1175 repository username, date is the commit date in any format
1179 repository username, date is the commit date in any format
1176 supported by util.parsedate() and defaults to current date, extra
1180 supported by util.parsedate() and defaults to current date, extra
1177 is a dictionary of metadata or is left empty.
1181 is a dictionary of metadata or is left empty.
1178 """
1182 """
1179 def __init__(self, repo, parents, text, files, filectxfn, user=None,
1183 def __init__(self, repo, parents, text, files, filectxfn, user=None,
1180 date=None, extra=None):
1184 date=None, extra=None):
1181 self._repo = repo
1185 self._repo = repo
1182 self._rev = None
1186 self._rev = None
1183 self._node = None
1187 self._node = None
1184 self._text = text
1188 self._text = text
1185 self._date = date and util.parsedate(date) or util.makedate()
1189 self._date = date and util.parsedate(date) or util.makedate()
1186 self._user = user
1190 self._user = user
1187 parents = [(p or nullid) for p in parents]
1191 parents = [(p or nullid) for p in parents]
1188 p1, p2 = parents
1192 p1, p2 = parents
1189 self._parents = [changectx(self._repo, p) for p in (p1, p2)]
1193 self._parents = [changectx(self._repo, p) for p in (p1, p2)]
1190 files = sorted(set(files))
1194 files = sorted(set(files))
1191 self._status = [files, [], [], [], []]
1195 self._status = [files, [], [], [], []]
1192 self._filectxfn = filectxfn
1196 self._filectxfn = filectxfn
1193
1197
1194 self._extra = extra and extra.copy() or {}
1198 self._extra = extra and extra.copy() or {}
1195 if self._extra.get('branch', '') == '':
1199 if self._extra.get('branch', '') == '':
1196 self._extra['branch'] = 'default'
1200 self._extra['branch'] = 'default'
1197
1201
1198 def __str__(self):
1202 def __str__(self):
1199 return str(self._parents[0]) + "+"
1203 return str(self._parents[0]) + "+"
1200
1204
1201 def __int__(self):
1205 def __int__(self):
1202 return self._rev
1206 return self._rev
1203
1207
1204 def __nonzero__(self):
1208 def __nonzero__(self):
1205 return True
1209 return True
1206
1210
1207 def __getitem__(self, key):
1211 def __getitem__(self, key):
1208 return self.filectx(key)
1212 return self.filectx(key)
1209
1213
1210 def p1(self):
1214 def p1(self):
1211 return self._parents[0]
1215 return self._parents[0]
1212 def p2(self):
1216 def p2(self):
1213 return self._parents[1]
1217 return self._parents[1]
1214
1218
1215 def user(self):
1219 def user(self):
1216 return self._user or self._repo.ui.username()
1220 return self._user or self._repo.ui.username()
1217 def date(self):
1221 def date(self):
1218 return self._date
1222 return self._date
1219 def description(self):
1223 def description(self):
1220 return self._text
1224 return self._text
1221 def files(self):
1225 def files(self):
1222 return self.modified()
1226 return self.modified()
1223 def modified(self):
1227 def modified(self):
1224 return self._status[0]
1228 return self._status[0]
1225 def added(self):
1229 def added(self):
1226 return self._status[1]
1230 return self._status[1]
1227 def removed(self):
1231 def removed(self):
1228 return self._status[2]
1232 return self._status[2]
1229 def deleted(self):
1233 def deleted(self):
1230 return self._status[3]
1234 return self._status[3]
1231 def unknown(self):
1235 def unknown(self):
1232 return self._status[4]
1236 return self._status[4]
1233 def ignored(self):
1237 def ignored(self):
1234 return self._status[5]
1238 return self._status[5]
1235 def clean(self):
1239 def clean(self):
1236 return self._status[6]
1240 return self._status[6]
1237 def branch(self):
1241 def branch(self):
1238 return encoding.tolocal(self._extra['branch'])
1242 return encoding.tolocal(self._extra['branch'])
1239 def extra(self):
1243 def extra(self):
1240 return self._extra
1244 return self._extra
1241 def flags(self, f):
1245 def flags(self, f):
1242 return self[f].flags()
1246 return self[f].flags()
1243
1247
1244 def parents(self):
1248 def parents(self):
1245 """return contexts for each parent changeset"""
1249 """return contexts for each parent changeset"""
1246 return self._parents
1250 return self._parents
1247
1251
1248 def filectx(self, path, filelog=None):
1252 def filectx(self, path, filelog=None):
1249 """get a file context from the working directory"""
1253 """get a file context from the working directory"""
1250 return self._filectxfn(self._repo, self, path)
1254 return self._filectxfn(self._repo, self, path)
1251
1255
1252 def commit(self):
1256 def commit(self):
1253 """commit context to the repo"""
1257 """commit context to the repo"""
1254 return self._repo.commitctx(self)
1258 return self._repo.commitctx(self)
1255
1259
1256 class memfilectx(object):
1260 class memfilectx(object):
1257 """memfilectx represents an in-memory file to commit.
1261 """memfilectx represents an in-memory file to commit.
1258
1262
1259 See memctx for more details.
1263 See memctx for more details.
1260 """
1264 """
1261 def __init__(self, path, data, islink=False, isexec=False, copied=None):
1265 def __init__(self, path, data, islink=False, isexec=False, copied=None):
1262 """
1266 """
1263 path is the normalized file path relative to repository root.
1267 path is the normalized file path relative to repository root.
1264 data is the file content as a string.
1268 data is the file content as a string.
1265 islink is True if the file is a symbolic link.
1269 islink is True if the file is a symbolic link.
1266 isexec is True if the file is executable.
1270 isexec is True if the file is executable.
1267 copied is the source file path if current file was copied in the
1271 copied is the source file path if current file was copied in the
1268 revision being committed, or None."""
1272 revision being committed, or None."""
1269 self._path = path
1273 self._path = path
1270 self._data = data
1274 self._data = data
1271 self._flags = (islink and 'l' or '') + (isexec and 'x' or '')
1275 self._flags = (islink and 'l' or '') + (isexec and 'x' or '')
1272 self._copied = None
1276 self._copied = None
1273 if copied:
1277 if copied:
1274 self._copied = (copied, nullid)
1278 self._copied = (copied, nullid)
1275
1279
1276 def __nonzero__(self):
1280 def __nonzero__(self):
1277 return True
1281 return True
1278 def __str__(self):
1282 def __str__(self):
1279 return "%s@%s" % (self.path(), self._changectx)
1283 return "%s@%s" % (self.path(), self._changectx)
1280 def path(self):
1284 def path(self):
1281 return self._path
1285 return self._path
1282 def data(self):
1286 def data(self):
1283 return self._data
1287 return self._data
1284 def flags(self):
1288 def flags(self):
1285 return self._flags
1289 return self._flags
1286 def isexec(self):
1290 def isexec(self):
1287 return 'x' in self._flags
1291 return 'x' in self._flags
1288 def islink(self):
1292 def islink(self):
1289 return 'l' in self._flags
1293 return 'l' in self._flags
1290 def renamed(self):
1294 def renamed(self):
1291 return self._copied
1295 return self._copied
General Comments 0
You need to be logged in to leave comments. Login now