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