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