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