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