##// END OF EJS Templates
workingctx: have status method call super instead of customized code....
Sean Farley -
r21595:aca692aa default
parent child Browse files
Show More
@@ -1,1677 +1,1674 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 mdiff, error, util, scmutil, subrepo, patch, encoding, phases
10 import mdiff, error, util, scmutil, subrepo, patch, encoding, phases
11 import match as matchmod
11 import match as matchmod
12 import os, errno, stat
12 import os, errno, stat
13 import obsolete as obsmod
13 import obsolete as obsmod
14 import repoview
14 import repoview
15 import fileset
15 import fileset
16
16
17 propertycache = util.propertycache
17 propertycache = util.propertycache
18
18
19 class basectx(object):
19 class basectx(object):
20 """A basectx object represents the common logic for its children:
20 """A basectx object represents the common logic for its children:
21 changectx: read-only context that is already present in the repo,
21 changectx: read-only context that is already present in the repo,
22 workingctx: a context that represents the working directory and can
22 workingctx: a context that represents the working directory and can
23 be committed,
23 be committed,
24 memctx: a context that represents changes in-memory and can also
24 memctx: a context that represents changes in-memory and can also
25 be committed."""
25 be committed."""
26 def __new__(cls, repo, changeid='', *args, **kwargs):
26 def __new__(cls, repo, changeid='', *args, **kwargs):
27 if isinstance(changeid, basectx):
27 if isinstance(changeid, basectx):
28 return changeid
28 return changeid
29
29
30 o = super(basectx, cls).__new__(cls)
30 o = super(basectx, cls).__new__(cls)
31
31
32 o._repo = repo
32 o._repo = repo
33 o._rev = nullrev
33 o._rev = nullrev
34 o._node = nullid
34 o._node = nullid
35
35
36 return o
36 return o
37
37
38 def __str__(self):
38 def __str__(self):
39 return short(self.node())
39 return short(self.node())
40
40
41 def __int__(self):
41 def __int__(self):
42 return self.rev()
42 return self.rev()
43
43
44 def __repr__(self):
44 def __repr__(self):
45 return "<%s %s>" % (type(self).__name__, str(self))
45 return "<%s %s>" % (type(self).__name__, str(self))
46
46
47 def __eq__(self, other):
47 def __eq__(self, other):
48 try:
48 try:
49 return type(self) == type(other) and self._rev == other._rev
49 return type(self) == type(other) and self._rev == other._rev
50 except AttributeError:
50 except AttributeError:
51 return False
51 return False
52
52
53 def __ne__(self, other):
53 def __ne__(self, other):
54 return not (self == other)
54 return not (self == other)
55
55
56 def __contains__(self, key):
56 def __contains__(self, key):
57 return key in self._manifest
57 return key in self._manifest
58
58
59 def __getitem__(self, key):
59 def __getitem__(self, key):
60 return self.filectx(key)
60 return self.filectx(key)
61
61
62 def __iter__(self):
62 def __iter__(self):
63 for f in sorted(self._manifest):
63 for f in sorted(self._manifest):
64 yield f
64 yield f
65
65
66 def _manifestmatches(self, match, s):
66 def _manifestmatches(self, match, s):
67 """generate a new manifest filtered by the match argument
67 """generate a new manifest filtered by the match argument
68
68
69 This method is for internal use only and mainly exists to provide an
69 This method is for internal use only and mainly exists to provide an
70 object oriented way for other contexts to customize the manifest
70 object oriented way for other contexts to customize the manifest
71 generation.
71 generation.
72 """
72 """
73 mf = self.manifest().copy()
73 mf = self.manifest().copy()
74 if match.always():
74 if match.always():
75 return mf
75 return mf
76 for fn in mf.keys():
76 for fn in mf.keys():
77 if not match(fn):
77 if not match(fn):
78 del mf[fn]
78 del mf[fn]
79 return mf
79 return mf
80
80
81 def _matchstatus(self, other, s, match, listignored, listclean,
81 def _matchstatus(self, other, s, match, listignored, listclean,
82 listunknown):
82 listunknown):
83 """return match.always if match is none
83 """return match.always if match is none
84
84
85 This internal method provides a way for child objects to override the
85 This internal method provides a way for child objects to override the
86 match operator.
86 match operator.
87 """
87 """
88 return match or matchmod.always(self._repo.root, self._repo.getcwd())
88 return match or matchmod.always(self._repo.root, self._repo.getcwd())
89
89
90 def _prestatus(self, other, s, match, listignored, listclean, listunknown):
90 def _prestatus(self, other, s, match, listignored, listclean, listunknown):
91 """provide a hook to allow child objects to preprocess status results
91 """provide a hook to allow child objects to preprocess status results
92
92
93 For example, this allows other contexts, such as workingctx, to query
93 For example, this allows other contexts, such as workingctx, to query
94 the dirstate before comparing the manifests.
94 the dirstate before comparing the manifests.
95 """
95 """
96 # load earliest manifest first for caching reasons
96 # load earliest manifest first for caching reasons
97 if self.rev() < other.rev():
97 if self.rev() < other.rev():
98 self.manifest()
98 self.manifest()
99 return s
99 return s
100
100
101 def _poststatus(self, other, s, match, listignored, listclean, listunknown):
101 def _poststatus(self, other, s, match, listignored, listclean, listunknown):
102 """provide a hook to allow child objects to postprocess status results
102 """provide a hook to allow child objects to postprocess status results
103
103
104 For example, this allows other contexts, such as workingctx, to filter
104 For example, this allows other contexts, such as workingctx, to filter
105 suspect symlinks in the case of FAT32 and NTFS filesytems.
105 suspect symlinks in the case of FAT32 and NTFS filesytems.
106 """
106 """
107 return s
107 return s
108
108
109 def _buildstatus(self, other, s, match, listignored, listclean,
109 def _buildstatus(self, other, s, match, listignored, listclean,
110 listunknown):
110 listunknown):
111 """build a status with respect to another context"""
111 """build a status with respect to another context"""
112 mf1 = other._manifestmatches(match, s)
112 mf1 = other._manifestmatches(match, s)
113 mf2 = self._manifestmatches(match, s)
113 mf2 = self._manifestmatches(match, s)
114
114
115 modified, added, clean = [], [], []
115 modified, added, clean = [], [], []
116 deleted, unknown, ignored = s[3], [], []
116 deleted, unknown, ignored = s[3], [], []
117 withflags = mf1.withflags() | mf2.withflags()
117 withflags = mf1.withflags() | mf2.withflags()
118 for fn, mf2node in mf2.iteritems():
118 for fn, mf2node in mf2.iteritems():
119 if fn in mf1:
119 if fn in mf1:
120 if (fn not in deleted and
120 if (fn not in deleted and
121 ((fn in withflags and mf1.flags(fn) != mf2.flags(fn)) or
121 ((fn in withflags and mf1.flags(fn) != mf2.flags(fn)) or
122 (mf1[fn] != mf2node and
122 (mf1[fn] != mf2node and
123 (mf2node or self[fn].cmp(other[fn]))))):
123 (mf2node or self[fn].cmp(other[fn]))))):
124 modified.append(fn)
124 modified.append(fn)
125 elif listclean:
125 elif listclean:
126 clean.append(fn)
126 clean.append(fn)
127 del mf1[fn]
127 del mf1[fn]
128 elif fn not in deleted:
128 elif fn not in deleted:
129 added.append(fn)
129 added.append(fn)
130 removed = mf1.keys()
130 removed = mf1.keys()
131
131
132 return [modified, added, removed, deleted, unknown, ignored, clean]
132 return [modified, added, removed, deleted, unknown, ignored, clean]
133
133
134 @propertycache
134 @propertycache
135 def substate(self):
135 def substate(self):
136 return subrepo.state(self, self._repo.ui)
136 return subrepo.state(self, self._repo.ui)
137
137
138 def subrev(self, subpath):
138 def subrev(self, subpath):
139 return self.substate[subpath][1]
139 return self.substate[subpath][1]
140
140
141 def rev(self):
141 def rev(self):
142 return self._rev
142 return self._rev
143 def node(self):
143 def node(self):
144 return self._node
144 return self._node
145 def hex(self):
145 def hex(self):
146 return hex(self.node())
146 return hex(self.node())
147 def manifest(self):
147 def manifest(self):
148 return self._manifest
148 return self._manifest
149 def phasestr(self):
149 def phasestr(self):
150 return phases.phasenames[self.phase()]
150 return phases.phasenames[self.phase()]
151 def mutable(self):
151 def mutable(self):
152 return self.phase() > phases.public
152 return self.phase() > phases.public
153
153
154 def getfileset(self, expr):
154 def getfileset(self, expr):
155 return fileset.getfileset(self, expr)
155 return fileset.getfileset(self, expr)
156
156
157 def obsolete(self):
157 def obsolete(self):
158 """True if the changeset is obsolete"""
158 """True if the changeset is obsolete"""
159 return self.rev() in obsmod.getrevs(self._repo, 'obsolete')
159 return self.rev() in obsmod.getrevs(self._repo, 'obsolete')
160
160
161 def extinct(self):
161 def extinct(self):
162 """True if the changeset is extinct"""
162 """True if the changeset is extinct"""
163 return self.rev() in obsmod.getrevs(self._repo, 'extinct')
163 return self.rev() in obsmod.getrevs(self._repo, 'extinct')
164
164
165 def unstable(self):
165 def unstable(self):
166 """True if the changeset is not obsolete but it's ancestor are"""
166 """True if the changeset is not obsolete but it's ancestor are"""
167 return self.rev() in obsmod.getrevs(self._repo, 'unstable')
167 return self.rev() in obsmod.getrevs(self._repo, 'unstable')
168
168
169 def bumped(self):
169 def bumped(self):
170 """True if the changeset try to be a successor of a public changeset
170 """True if the changeset try to be a successor of a public changeset
171
171
172 Only non-public and non-obsolete changesets may be bumped.
172 Only non-public and non-obsolete changesets may be bumped.
173 """
173 """
174 return self.rev() in obsmod.getrevs(self._repo, 'bumped')
174 return self.rev() in obsmod.getrevs(self._repo, 'bumped')
175
175
176 def divergent(self):
176 def divergent(self):
177 """Is a successors of a changeset with multiple possible successors set
177 """Is a successors of a changeset with multiple possible successors set
178
178
179 Only non-public and non-obsolete changesets may be divergent.
179 Only non-public and non-obsolete changesets may be divergent.
180 """
180 """
181 return self.rev() in obsmod.getrevs(self._repo, 'divergent')
181 return self.rev() in obsmod.getrevs(self._repo, 'divergent')
182
182
183 def troubled(self):
183 def troubled(self):
184 """True if the changeset is either unstable, bumped or divergent"""
184 """True if the changeset is either unstable, bumped or divergent"""
185 return self.unstable() or self.bumped() or self.divergent()
185 return self.unstable() or self.bumped() or self.divergent()
186
186
187 def troubles(self):
187 def troubles(self):
188 """return the list of troubles affecting this changesets.
188 """return the list of troubles affecting this changesets.
189
189
190 Troubles are returned as strings. possible values are:
190 Troubles are returned as strings. possible values are:
191 - unstable,
191 - unstable,
192 - bumped,
192 - bumped,
193 - divergent.
193 - divergent.
194 """
194 """
195 troubles = []
195 troubles = []
196 if self.unstable():
196 if self.unstable():
197 troubles.append('unstable')
197 troubles.append('unstable')
198 if self.bumped():
198 if self.bumped():
199 troubles.append('bumped')
199 troubles.append('bumped')
200 if self.divergent():
200 if self.divergent():
201 troubles.append('divergent')
201 troubles.append('divergent')
202 return troubles
202 return troubles
203
203
204 def parents(self):
204 def parents(self):
205 """return contexts for each parent changeset"""
205 """return contexts for each parent changeset"""
206 return self._parents
206 return self._parents
207
207
208 def p1(self):
208 def p1(self):
209 return self._parents[0]
209 return self._parents[0]
210
210
211 def p2(self):
211 def p2(self):
212 if len(self._parents) == 2:
212 if len(self._parents) == 2:
213 return self._parents[1]
213 return self._parents[1]
214 return changectx(self._repo, -1)
214 return changectx(self._repo, -1)
215
215
216 def _fileinfo(self, path):
216 def _fileinfo(self, path):
217 if '_manifest' in self.__dict__:
217 if '_manifest' in self.__dict__:
218 try:
218 try:
219 return self._manifest[path], self._manifest.flags(path)
219 return self._manifest[path], self._manifest.flags(path)
220 except KeyError:
220 except KeyError:
221 raise error.ManifestLookupError(self._node, path,
221 raise error.ManifestLookupError(self._node, path,
222 _('not found in manifest'))
222 _('not found in manifest'))
223 if '_manifestdelta' in self.__dict__ or path in self.files():
223 if '_manifestdelta' in self.__dict__ or path in self.files():
224 if path in self._manifestdelta:
224 if path in self._manifestdelta:
225 return (self._manifestdelta[path],
225 return (self._manifestdelta[path],
226 self._manifestdelta.flags(path))
226 self._manifestdelta.flags(path))
227 node, flag = self._repo.manifest.find(self._changeset[0], path)
227 node, flag = self._repo.manifest.find(self._changeset[0], path)
228 if not node:
228 if not node:
229 raise error.ManifestLookupError(self._node, path,
229 raise error.ManifestLookupError(self._node, path,
230 _('not found in manifest'))
230 _('not found in manifest'))
231
231
232 return node, flag
232 return node, flag
233
233
234 def filenode(self, path):
234 def filenode(self, path):
235 return self._fileinfo(path)[0]
235 return self._fileinfo(path)[0]
236
236
237 def flags(self, path):
237 def flags(self, path):
238 try:
238 try:
239 return self._fileinfo(path)[1]
239 return self._fileinfo(path)[1]
240 except error.LookupError:
240 except error.LookupError:
241 return ''
241 return ''
242
242
243 def sub(self, path):
243 def sub(self, path):
244 return subrepo.subrepo(self, path)
244 return subrepo.subrepo(self, path)
245
245
246 def match(self, pats=[], include=None, exclude=None, default='glob'):
246 def match(self, pats=[], include=None, exclude=None, default='glob'):
247 r = self._repo
247 r = self._repo
248 return matchmod.match(r.root, r.getcwd(), pats,
248 return matchmod.match(r.root, r.getcwd(), pats,
249 include, exclude, default,
249 include, exclude, default,
250 auditor=r.auditor, ctx=self)
250 auditor=r.auditor, ctx=self)
251
251
252 def diff(self, ctx2=None, match=None, **opts):
252 def diff(self, ctx2=None, match=None, **opts):
253 """Returns a diff generator for the given contexts and matcher"""
253 """Returns a diff generator for the given contexts and matcher"""
254 if ctx2 is None:
254 if ctx2 is None:
255 ctx2 = self.p1()
255 ctx2 = self.p1()
256 if ctx2 is not None:
256 if ctx2 is not None:
257 ctx2 = self._repo[ctx2]
257 ctx2 = self._repo[ctx2]
258 diffopts = patch.diffopts(self._repo.ui, opts)
258 diffopts = patch.diffopts(self._repo.ui, opts)
259 return patch.diff(self._repo, ctx2.node(), self.node(),
259 return patch.diff(self._repo, ctx2.node(), self.node(),
260 match=match, opts=diffopts)
260 match=match, opts=diffopts)
261
261
262 @propertycache
262 @propertycache
263 def _dirs(self):
263 def _dirs(self):
264 return scmutil.dirs(self._manifest)
264 return scmutil.dirs(self._manifest)
265
265
266 def dirs(self):
266 def dirs(self):
267 return self._dirs
267 return self._dirs
268
268
269 def dirty(self):
269 def dirty(self):
270 return False
270 return False
271
271
272 def status(self, other=None, match=None, listignored=False,
272 def status(self, other=None, match=None, listignored=False,
273 listclean=False, listunknown=False, listsubrepos=False):
273 listclean=False, listunknown=False, listsubrepos=False):
274 """return status of files between two nodes or node and working
274 """return status of files between two nodes or node and working
275 directory.
275 directory.
276
276
277 If other is None, compare this node with working directory.
277 If other is None, compare this node with working directory.
278 """
278 """
279
279
280 ctx1 = self
280 ctx1 = self
281 ctx2 = self._repo[other]
281 ctx2 = self._repo[other]
282
282
283 # This next code block is, admittedly, fragile logic that tests for
283 # This next code block is, admittedly, fragile logic that tests for
284 # reversing the contexts and wouldn't need to exist if it weren't for
284 # reversing the contexts and wouldn't need to exist if it weren't for
285 # the fast (and common) code path of comparing the working directory
285 # the fast (and common) code path of comparing the working directory
286 # with its first parent.
286 # with its first parent.
287 #
287 #
288 # What we're aiming for here is the ability to call:
288 # What we're aiming for here is the ability to call:
289 #
289 #
290 # workingctx.status(parentctx)
290 # workingctx.status(parentctx)
291 #
291 #
292 # If we always built the manifest for each context and compared those,
292 # If we always built the manifest for each context and compared those,
293 # then we'd be done. But the special case of the above call means we
293 # then we'd be done. But the special case of the above call means we
294 # just copy the manifest of the parent.
294 # just copy the manifest of the parent.
295 reversed = False
295 reversed = False
296 if (not isinstance(ctx1, changectx)
296 if (not isinstance(ctx1, changectx)
297 and isinstance(ctx2, changectx)):
297 and isinstance(ctx2, changectx)):
298 reversed = True
298 reversed = True
299 ctx1, ctx2 = ctx2, ctx1
299 ctx1, ctx2 = ctx2, ctx1
300
300
301 r = [[], [], [], [], [], [], []]
301 r = [[], [], [], [], [], [], []]
302 match = ctx2._matchstatus(ctx1, r, match, listignored, listclean,
302 match = ctx2._matchstatus(ctx1, r, match, listignored, listclean,
303 listunknown)
303 listunknown)
304 r = ctx2._prestatus(ctx1, r, match, listignored, listclean, listunknown)
304 r = ctx2._prestatus(ctx1, r, match, listignored, listclean, listunknown)
305 r = ctx2._buildstatus(ctx1, r, match, listignored, listclean,
305 r = ctx2._buildstatus(ctx1, r, match, listignored, listclean,
306 listunknown)
306 listunknown)
307 r = ctx2._poststatus(ctx1, r, match, listignored, listclean,
307 r = ctx2._poststatus(ctx1, r, match, listignored, listclean,
308 listunknown)
308 listunknown)
309
309
310 if reversed:
310 if reversed:
311 r[1], r[2], r[3], r[4] = r[2], r[1], r[4], r[3]
311 r[1], r[2], r[3], r[4] = r[2], r[1], r[4], r[3]
312
312
313 if listsubrepos:
313 if listsubrepos:
314 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
314 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
315 rev2 = ctx2.subrev(subpath)
315 rev2 = ctx2.subrev(subpath)
316 try:
316 try:
317 submatch = matchmod.narrowmatcher(subpath, match)
317 submatch = matchmod.narrowmatcher(subpath, match)
318 s = sub.status(rev2, match=submatch, ignored=listignored,
318 s = sub.status(rev2, match=submatch, ignored=listignored,
319 clean=listclean, unknown=listunknown,
319 clean=listclean, unknown=listunknown,
320 listsubrepos=True)
320 listsubrepos=True)
321 for rfiles, sfiles in zip(r, s):
321 for rfiles, sfiles in zip(r, s):
322 rfiles.extend("%s/%s" % (subpath, f) for f in sfiles)
322 rfiles.extend("%s/%s" % (subpath, f) for f in sfiles)
323 except error.LookupError:
323 except error.LookupError:
324 self._repo.ui.status(_("skipping missing "
324 self._repo.ui.status(_("skipping missing "
325 "subrepository: %s\n") % subpath)
325 "subrepository: %s\n") % subpath)
326
326
327 for l in r:
327 for l in r:
328 l.sort()
328 l.sort()
329 return r
329 return r
330
330
331
331
332 def makememctx(repo, parents, text, user, date, branch, files, store,
332 def makememctx(repo, parents, text, user, date, branch, files, store,
333 editor=None):
333 editor=None):
334 def getfilectx(repo, memctx, path):
334 def getfilectx(repo, memctx, path):
335 data, (islink, isexec), copied = store.getfile(path)
335 data, (islink, isexec), copied = store.getfile(path)
336 return memfilectx(path, data, islink=islink, isexec=isexec,
336 return memfilectx(path, data, islink=islink, isexec=isexec,
337 copied=copied)
337 copied=copied)
338 extra = {}
338 extra = {}
339 if branch:
339 if branch:
340 extra['branch'] = encoding.fromlocal(branch)
340 extra['branch'] = encoding.fromlocal(branch)
341 ctx = memctx(repo, parents, text, files, getfilectx, user,
341 ctx = memctx(repo, parents, text, files, getfilectx, user,
342 date, extra, editor)
342 date, extra, editor)
343 return ctx
343 return ctx
344
344
345 class changectx(basectx):
345 class changectx(basectx):
346 """A changecontext object makes access to data related to a particular
346 """A changecontext object makes access to data related to a particular
347 changeset convenient. It represents a read-only context already present in
347 changeset convenient. It represents a read-only context already present in
348 the repo."""
348 the repo."""
349 def __init__(self, repo, changeid=''):
349 def __init__(self, repo, changeid=''):
350 """changeid is a revision number, node, or tag"""
350 """changeid is a revision number, node, or tag"""
351
351
352 # since basectx.__new__ already took care of copying the object, we
352 # since basectx.__new__ already took care of copying the object, we
353 # don't need to do anything in __init__, so we just exit here
353 # don't need to do anything in __init__, so we just exit here
354 if isinstance(changeid, basectx):
354 if isinstance(changeid, basectx):
355 return
355 return
356
356
357 if changeid == '':
357 if changeid == '':
358 changeid = '.'
358 changeid = '.'
359 self._repo = repo
359 self._repo = repo
360
360
361 if isinstance(changeid, int):
361 if isinstance(changeid, int):
362 try:
362 try:
363 self._node = repo.changelog.node(changeid)
363 self._node = repo.changelog.node(changeid)
364 except IndexError:
364 except IndexError:
365 raise error.RepoLookupError(
365 raise error.RepoLookupError(
366 _("unknown revision '%s'") % changeid)
366 _("unknown revision '%s'") % changeid)
367 self._rev = changeid
367 self._rev = changeid
368 return
368 return
369 if isinstance(changeid, long):
369 if isinstance(changeid, long):
370 changeid = str(changeid)
370 changeid = str(changeid)
371 if changeid == '.':
371 if changeid == '.':
372 self._node = repo.dirstate.p1()
372 self._node = repo.dirstate.p1()
373 self._rev = repo.changelog.rev(self._node)
373 self._rev = repo.changelog.rev(self._node)
374 return
374 return
375 if changeid == 'null':
375 if changeid == 'null':
376 self._node = nullid
376 self._node = nullid
377 self._rev = nullrev
377 self._rev = nullrev
378 return
378 return
379 if changeid == 'tip':
379 if changeid == 'tip':
380 self._node = repo.changelog.tip()
380 self._node = repo.changelog.tip()
381 self._rev = repo.changelog.rev(self._node)
381 self._rev = repo.changelog.rev(self._node)
382 return
382 return
383 if len(changeid) == 20:
383 if len(changeid) == 20:
384 try:
384 try:
385 self._node = changeid
385 self._node = changeid
386 self._rev = repo.changelog.rev(changeid)
386 self._rev = repo.changelog.rev(changeid)
387 return
387 return
388 except LookupError:
388 except LookupError:
389 pass
389 pass
390
390
391 try:
391 try:
392 r = int(changeid)
392 r = int(changeid)
393 if str(r) != changeid:
393 if str(r) != changeid:
394 raise ValueError
394 raise ValueError
395 l = len(repo.changelog)
395 l = len(repo.changelog)
396 if r < 0:
396 if r < 0:
397 r += l
397 r += l
398 if r < 0 or r >= l:
398 if r < 0 or r >= l:
399 raise ValueError
399 raise ValueError
400 self._rev = r
400 self._rev = r
401 self._node = repo.changelog.node(r)
401 self._node = repo.changelog.node(r)
402 return
402 return
403 except (ValueError, OverflowError, IndexError):
403 except (ValueError, OverflowError, IndexError):
404 pass
404 pass
405
405
406 if len(changeid) == 40:
406 if len(changeid) == 40:
407 try:
407 try:
408 self._node = bin(changeid)
408 self._node = bin(changeid)
409 self._rev = repo.changelog.rev(self._node)
409 self._rev = repo.changelog.rev(self._node)
410 return
410 return
411 except (TypeError, LookupError):
411 except (TypeError, LookupError):
412 pass
412 pass
413
413
414 if changeid in repo._bookmarks:
414 if changeid in repo._bookmarks:
415 self._node = repo._bookmarks[changeid]
415 self._node = repo._bookmarks[changeid]
416 self._rev = repo.changelog.rev(self._node)
416 self._rev = repo.changelog.rev(self._node)
417 return
417 return
418 if changeid in repo._tagscache.tags:
418 if changeid in repo._tagscache.tags:
419 self._node = repo._tagscache.tags[changeid]
419 self._node = repo._tagscache.tags[changeid]
420 self._rev = repo.changelog.rev(self._node)
420 self._rev = repo.changelog.rev(self._node)
421 return
421 return
422 try:
422 try:
423 self._node = repo.branchtip(changeid)
423 self._node = repo.branchtip(changeid)
424 self._rev = repo.changelog.rev(self._node)
424 self._rev = repo.changelog.rev(self._node)
425 return
425 return
426 except error.RepoLookupError:
426 except error.RepoLookupError:
427 pass
427 pass
428
428
429 self._node = repo.changelog._partialmatch(changeid)
429 self._node = repo.changelog._partialmatch(changeid)
430 if self._node is not None:
430 if self._node is not None:
431 self._rev = repo.changelog.rev(self._node)
431 self._rev = repo.changelog.rev(self._node)
432 return
432 return
433
433
434 # lookup failed
434 # lookup failed
435 # check if it might have come from damaged dirstate
435 # check if it might have come from damaged dirstate
436 #
436 #
437 # XXX we could avoid the unfiltered if we had a recognizable exception
437 # XXX we could avoid the unfiltered if we had a recognizable exception
438 # for filtered changeset access
438 # for filtered changeset access
439 if changeid in repo.unfiltered().dirstate.parents():
439 if changeid in repo.unfiltered().dirstate.parents():
440 raise error.Abort(_("working directory has unknown parent '%s'!")
440 raise error.Abort(_("working directory has unknown parent '%s'!")
441 % short(changeid))
441 % short(changeid))
442 try:
442 try:
443 if len(changeid) == 20:
443 if len(changeid) == 20:
444 changeid = hex(changeid)
444 changeid = hex(changeid)
445 except TypeError:
445 except TypeError:
446 pass
446 pass
447 raise error.RepoLookupError(
447 raise error.RepoLookupError(
448 _("unknown revision '%s'") % changeid)
448 _("unknown revision '%s'") % changeid)
449
449
450 def __hash__(self):
450 def __hash__(self):
451 try:
451 try:
452 return hash(self._rev)
452 return hash(self._rev)
453 except AttributeError:
453 except AttributeError:
454 return id(self)
454 return id(self)
455
455
456 def __nonzero__(self):
456 def __nonzero__(self):
457 return self._rev != nullrev
457 return self._rev != nullrev
458
458
459 @propertycache
459 @propertycache
460 def _changeset(self):
460 def _changeset(self):
461 return self._repo.changelog.read(self.rev())
461 return self._repo.changelog.read(self.rev())
462
462
463 @propertycache
463 @propertycache
464 def _manifest(self):
464 def _manifest(self):
465 return self._repo.manifest.read(self._changeset[0])
465 return self._repo.manifest.read(self._changeset[0])
466
466
467 @propertycache
467 @propertycache
468 def _manifestdelta(self):
468 def _manifestdelta(self):
469 return self._repo.manifest.readdelta(self._changeset[0])
469 return self._repo.manifest.readdelta(self._changeset[0])
470
470
471 @propertycache
471 @propertycache
472 def _parents(self):
472 def _parents(self):
473 p = self._repo.changelog.parentrevs(self._rev)
473 p = self._repo.changelog.parentrevs(self._rev)
474 if p[1] == nullrev:
474 if p[1] == nullrev:
475 p = p[:-1]
475 p = p[:-1]
476 return [changectx(self._repo, x) for x in p]
476 return [changectx(self._repo, x) for x in p]
477
477
478 def changeset(self):
478 def changeset(self):
479 return self._changeset
479 return self._changeset
480 def manifestnode(self):
480 def manifestnode(self):
481 return self._changeset[0]
481 return self._changeset[0]
482
482
483 def user(self):
483 def user(self):
484 return self._changeset[1]
484 return self._changeset[1]
485 def date(self):
485 def date(self):
486 return self._changeset[2]
486 return self._changeset[2]
487 def files(self):
487 def files(self):
488 return self._changeset[3]
488 return self._changeset[3]
489 def description(self):
489 def description(self):
490 return self._changeset[4]
490 return self._changeset[4]
491 def branch(self):
491 def branch(self):
492 return encoding.tolocal(self._changeset[5].get("branch"))
492 return encoding.tolocal(self._changeset[5].get("branch"))
493 def closesbranch(self):
493 def closesbranch(self):
494 return 'close' in self._changeset[5]
494 return 'close' in self._changeset[5]
495 def extra(self):
495 def extra(self):
496 return self._changeset[5]
496 return self._changeset[5]
497 def tags(self):
497 def tags(self):
498 return self._repo.nodetags(self._node)
498 return self._repo.nodetags(self._node)
499 def bookmarks(self):
499 def bookmarks(self):
500 return self._repo.nodebookmarks(self._node)
500 return self._repo.nodebookmarks(self._node)
501 def phase(self):
501 def phase(self):
502 return self._repo._phasecache.phase(self._repo, self._rev)
502 return self._repo._phasecache.phase(self._repo, self._rev)
503 def hidden(self):
503 def hidden(self):
504 return self._rev in repoview.filterrevs(self._repo, 'visible')
504 return self._rev in repoview.filterrevs(self._repo, 'visible')
505
505
506 def children(self):
506 def children(self):
507 """return contexts for each child changeset"""
507 """return contexts for each child changeset"""
508 c = self._repo.changelog.children(self._node)
508 c = self._repo.changelog.children(self._node)
509 return [changectx(self._repo, x) for x in c]
509 return [changectx(self._repo, x) for x in c]
510
510
511 def ancestors(self):
511 def ancestors(self):
512 for a in self._repo.changelog.ancestors([self._rev]):
512 for a in self._repo.changelog.ancestors([self._rev]):
513 yield changectx(self._repo, a)
513 yield changectx(self._repo, a)
514
514
515 def descendants(self):
515 def descendants(self):
516 for d in self._repo.changelog.descendants([self._rev]):
516 for d in self._repo.changelog.descendants([self._rev]):
517 yield changectx(self._repo, d)
517 yield changectx(self._repo, d)
518
518
519 def filectx(self, path, fileid=None, filelog=None):
519 def filectx(self, path, fileid=None, filelog=None):
520 """get a file context from this changeset"""
520 """get a file context from this changeset"""
521 if fileid is None:
521 if fileid is None:
522 fileid = self.filenode(path)
522 fileid = self.filenode(path)
523 return filectx(self._repo, path, fileid=fileid,
523 return filectx(self._repo, path, fileid=fileid,
524 changectx=self, filelog=filelog)
524 changectx=self, filelog=filelog)
525
525
526 def ancestor(self, c2, warn=False):
526 def ancestor(self, c2, warn=False):
527 """
527 """
528 return the "best" ancestor context of self and c2
528 return the "best" ancestor context of self and c2
529 """
529 """
530 # deal with workingctxs
530 # deal with workingctxs
531 n2 = c2._node
531 n2 = c2._node
532 if n2 is None:
532 if n2 is None:
533 n2 = c2._parents[0]._node
533 n2 = c2._parents[0]._node
534 cahs = self._repo.changelog.commonancestorsheads(self._node, n2)
534 cahs = self._repo.changelog.commonancestorsheads(self._node, n2)
535 if not cahs:
535 if not cahs:
536 anc = nullid
536 anc = nullid
537 elif len(cahs) == 1:
537 elif len(cahs) == 1:
538 anc = cahs[0]
538 anc = cahs[0]
539 else:
539 else:
540 for r in self._repo.ui.configlist('merge', 'preferancestor'):
540 for r in self._repo.ui.configlist('merge', 'preferancestor'):
541 ctx = changectx(self._repo, r)
541 ctx = changectx(self._repo, r)
542 anc = ctx.node()
542 anc = ctx.node()
543 if anc in cahs:
543 if anc in cahs:
544 break
544 break
545 else:
545 else:
546 anc = self._repo.changelog.ancestor(self._node, n2)
546 anc = self._repo.changelog.ancestor(self._node, n2)
547 if warn:
547 if warn:
548 self._repo.ui.status(
548 self._repo.ui.status(
549 (_("note: using %s as ancestor of %s and %s\n") %
549 (_("note: using %s as ancestor of %s and %s\n") %
550 (short(anc), short(self._node), short(n2))) +
550 (short(anc), short(self._node), short(n2))) +
551 ''.join(_(" alternatively, use --config "
551 ''.join(_(" alternatively, use --config "
552 "merge.preferancestor=%s\n") %
552 "merge.preferancestor=%s\n") %
553 short(n) for n in sorted(cahs) if n != anc))
553 short(n) for n in sorted(cahs) if n != anc))
554 return changectx(self._repo, anc)
554 return changectx(self._repo, anc)
555
555
556 def descendant(self, other):
556 def descendant(self, other):
557 """True if other is descendant of this changeset"""
557 """True if other is descendant of this changeset"""
558 return self._repo.changelog.descendant(self._rev, other._rev)
558 return self._repo.changelog.descendant(self._rev, other._rev)
559
559
560 def walk(self, match):
560 def walk(self, match):
561 fset = set(match.files())
561 fset = set(match.files())
562 # for dirstate.walk, files=['.'] means "walk the whole tree".
562 # for dirstate.walk, files=['.'] means "walk the whole tree".
563 # follow that here, too
563 # follow that here, too
564 fset.discard('.')
564 fset.discard('.')
565
565
566 # avoid the entire walk if we're only looking for specific files
566 # avoid the entire walk if we're only looking for specific files
567 if fset and not match.anypats():
567 if fset and not match.anypats():
568 if util.all([fn in self for fn in fset]):
568 if util.all([fn in self for fn in fset]):
569 for fn in sorted(fset):
569 for fn in sorted(fset):
570 if match(fn):
570 if match(fn):
571 yield fn
571 yield fn
572 raise StopIteration
572 raise StopIteration
573
573
574 for fn in self:
574 for fn in self:
575 if fn in fset:
575 if fn in fset:
576 # specified pattern is the exact name
576 # specified pattern is the exact name
577 fset.remove(fn)
577 fset.remove(fn)
578 if match(fn):
578 if match(fn):
579 yield fn
579 yield fn
580 for fn in sorted(fset):
580 for fn in sorted(fset):
581 if fn in self._dirs:
581 if fn in self._dirs:
582 # specified pattern is a directory
582 # specified pattern is a directory
583 continue
583 continue
584 match.bad(fn, _('no such file in rev %s') % self)
584 match.bad(fn, _('no such file in rev %s') % self)
585
585
586 class basefilectx(object):
586 class basefilectx(object):
587 """A filecontext object represents the common logic for its children:
587 """A filecontext object represents the common logic for its children:
588 filectx: read-only access to a filerevision that is already present
588 filectx: read-only access to a filerevision that is already present
589 in the repo,
589 in the repo,
590 workingfilectx: a filecontext that represents files from the working
590 workingfilectx: a filecontext that represents files from the working
591 directory,
591 directory,
592 memfilectx: a filecontext that represents files in-memory."""
592 memfilectx: a filecontext that represents files in-memory."""
593 def __new__(cls, repo, path, *args, **kwargs):
593 def __new__(cls, repo, path, *args, **kwargs):
594 return super(basefilectx, cls).__new__(cls)
594 return super(basefilectx, cls).__new__(cls)
595
595
596 @propertycache
596 @propertycache
597 def _filelog(self):
597 def _filelog(self):
598 return self._repo.file(self._path)
598 return self._repo.file(self._path)
599
599
600 @propertycache
600 @propertycache
601 def _changeid(self):
601 def _changeid(self):
602 if '_changeid' in self.__dict__:
602 if '_changeid' in self.__dict__:
603 return self._changeid
603 return self._changeid
604 elif '_changectx' in self.__dict__:
604 elif '_changectx' in self.__dict__:
605 return self._changectx.rev()
605 return self._changectx.rev()
606 else:
606 else:
607 return self._filelog.linkrev(self._filerev)
607 return self._filelog.linkrev(self._filerev)
608
608
609 @propertycache
609 @propertycache
610 def _filenode(self):
610 def _filenode(self):
611 if '_fileid' in self.__dict__:
611 if '_fileid' in self.__dict__:
612 return self._filelog.lookup(self._fileid)
612 return self._filelog.lookup(self._fileid)
613 else:
613 else:
614 return self._changectx.filenode(self._path)
614 return self._changectx.filenode(self._path)
615
615
616 @propertycache
616 @propertycache
617 def _filerev(self):
617 def _filerev(self):
618 return self._filelog.rev(self._filenode)
618 return self._filelog.rev(self._filenode)
619
619
620 @propertycache
620 @propertycache
621 def _repopath(self):
621 def _repopath(self):
622 return self._path
622 return self._path
623
623
624 def __nonzero__(self):
624 def __nonzero__(self):
625 try:
625 try:
626 self._filenode
626 self._filenode
627 return True
627 return True
628 except error.LookupError:
628 except error.LookupError:
629 # file is missing
629 # file is missing
630 return False
630 return False
631
631
632 def __str__(self):
632 def __str__(self):
633 return "%s@%s" % (self.path(), self._changectx)
633 return "%s@%s" % (self.path(), self._changectx)
634
634
635 def __repr__(self):
635 def __repr__(self):
636 return "<%s %s>" % (type(self).__name__, str(self))
636 return "<%s %s>" % (type(self).__name__, str(self))
637
637
638 def __hash__(self):
638 def __hash__(self):
639 try:
639 try:
640 return hash((self._path, self._filenode))
640 return hash((self._path, self._filenode))
641 except AttributeError:
641 except AttributeError:
642 return id(self)
642 return id(self)
643
643
644 def __eq__(self, other):
644 def __eq__(self, other):
645 try:
645 try:
646 return (type(self) == type(other) and self._path == other._path
646 return (type(self) == type(other) and self._path == other._path
647 and self._filenode == other._filenode)
647 and self._filenode == other._filenode)
648 except AttributeError:
648 except AttributeError:
649 return False
649 return False
650
650
651 def __ne__(self, other):
651 def __ne__(self, other):
652 return not (self == other)
652 return not (self == other)
653
653
654 def filerev(self):
654 def filerev(self):
655 return self._filerev
655 return self._filerev
656 def filenode(self):
656 def filenode(self):
657 return self._filenode
657 return self._filenode
658 def flags(self):
658 def flags(self):
659 return self._changectx.flags(self._path)
659 return self._changectx.flags(self._path)
660 def filelog(self):
660 def filelog(self):
661 return self._filelog
661 return self._filelog
662 def rev(self):
662 def rev(self):
663 return self._changeid
663 return self._changeid
664 def linkrev(self):
664 def linkrev(self):
665 return self._filelog.linkrev(self._filerev)
665 return self._filelog.linkrev(self._filerev)
666 def node(self):
666 def node(self):
667 return self._changectx.node()
667 return self._changectx.node()
668 def hex(self):
668 def hex(self):
669 return self._changectx.hex()
669 return self._changectx.hex()
670 def user(self):
670 def user(self):
671 return self._changectx.user()
671 return self._changectx.user()
672 def date(self):
672 def date(self):
673 return self._changectx.date()
673 return self._changectx.date()
674 def files(self):
674 def files(self):
675 return self._changectx.files()
675 return self._changectx.files()
676 def description(self):
676 def description(self):
677 return self._changectx.description()
677 return self._changectx.description()
678 def branch(self):
678 def branch(self):
679 return self._changectx.branch()
679 return self._changectx.branch()
680 def extra(self):
680 def extra(self):
681 return self._changectx.extra()
681 return self._changectx.extra()
682 def phase(self):
682 def phase(self):
683 return self._changectx.phase()
683 return self._changectx.phase()
684 def phasestr(self):
684 def phasestr(self):
685 return self._changectx.phasestr()
685 return self._changectx.phasestr()
686 def manifest(self):
686 def manifest(self):
687 return self._changectx.manifest()
687 return self._changectx.manifest()
688 def changectx(self):
688 def changectx(self):
689 return self._changectx
689 return self._changectx
690
690
691 def path(self):
691 def path(self):
692 return self._path
692 return self._path
693
693
694 def isbinary(self):
694 def isbinary(self):
695 try:
695 try:
696 return util.binary(self.data())
696 return util.binary(self.data())
697 except IOError:
697 except IOError:
698 return False
698 return False
699
699
700 def cmp(self, fctx):
700 def cmp(self, fctx):
701 """compare with other file context
701 """compare with other file context
702
702
703 returns True if different than fctx.
703 returns True if different than fctx.
704 """
704 """
705 if (fctx._filerev is None
705 if (fctx._filerev is None
706 and (self._repo._encodefilterpats
706 and (self._repo._encodefilterpats
707 # if file data starts with '\1\n', empty metadata block is
707 # if file data starts with '\1\n', empty metadata block is
708 # prepended, which adds 4 bytes to filelog.size().
708 # prepended, which adds 4 bytes to filelog.size().
709 or self.size() - 4 == fctx.size())
709 or self.size() - 4 == fctx.size())
710 or self.size() == fctx.size()):
710 or self.size() == fctx.size()):
711 return self._filelog.cmp(self._filenode, fctx.data())
711 return self._filelog.cmp(self._filenode, fctx.data())
712
712
713 return True
713 return True
714
714
715 def parents(self):
715 def parents(self):
716 p = self._path
716 p = self._path
717 fl = self._filelog
717 fl = self._filelog
718 pl = [(p, n, fl) for n in self._filelog.parents(self._filenode)]
718 pl = [(p, n, fl) for n in self._filelog.parents(self._filenode)]
719
719
720 r = self._filelog.renamed(self._filenode)
720 r = self._filelog.renamed(self._filenode)
721 if r:
721 if r:
722 pl[0] = (r[0], r[1], None)
722 pl[0] = (r[0], r[1], None)
723
723
724 return [filectx(self._repo, p, fileid=n, filelog=l)
724 return [filectx(self._repo, p, fileid=n, filelog=l)
725 for p, n, l in pl if n != nullid]
725 for p, n, l in pl if n != nullid]
726
726
727 def p1(self):
727 def p1(self):
728 return self.parents()[0]
728 return self.parents()[0]
729
729
730 def p2(self):
730 def p2(self):
731 p = self.parents()
731 p = self.parents()
732 if len(p) == 2:
732 if len(p) == 2:
733 return p[1]
733 return p[1]
734 return filectx(self._repo, self._path, fileid=-1, filelog=self._filelog)
734 return filectx(self._repo, self._path, fileid=-1, filelog=self._filelog)
735
735
736 def annotate(self, follow=False, linenumber=None, diffopts=None):
736 def annotate(self, follow=False, linenumber=None, diffopts=None):
737 '''returns a list of tuples of (ctx, line) for each line
737 '''returns a list of tuples of (ctx, line) for each line
738 in the file, where ctx is the filectx of the node where
738 in the file, where ctx is the filectx of the node where
739 that line was last changed.
739 that line was last changed.
740 This returns tuples of ((ctx, linenumber), line) for each line,
740 This returns tuples of ((ctx, linenumber), line) for each line,
741 if "linenumber" parameter is NOT "None".
741 if "linenumber" parameter is NOT "None".
742 In such tuples, linenumber means one at the first appearance
742 In such tuples, linenumber means one at the first appearance
743 in the managed file.
743 in the managed file.
744 To reduce annotation cost,
744 To reduce annotation cost,
745 this returns fixed value(False is used) as linenumber,
745 this returns fixed value(False is used) as linenumber,
746 if "linenumber" parameter is "False".'''
746 if "linenumber" parameter is "False".'''
747
747
748 def decorate_compat(text, rev):
748 def decorate_compat(text, rev):
749 return ([rev] * len(text.splitlines()), text)
749 return ([rev] * len(text.splitlines()), text)
750
750
751 def without_linenumber(text, rev):
751 def without_linenumber(text, rev):
752 return ([(rev, False)] * len(text.splitlines()), text)
752 return ([(rev, False)] * len(text.splitlines()), text)
753
753
754 def with_linenumber(text, rev):
754 def with_linenumber(text, rev):
755 size = len(text.splitlines())
755 size = len(text.splitlines())
756 return ([(rev, i) for i in xrange(1, size + 1)], text)
756 return ([(rev, i) for i in xrange(1, size + 1)], text)
757
757
758 decorate = (((linenumber is None) and decorate_compat) or
758 decorate = (((linenumber is None) and decorate_compat) or
759 (linenumber and with_linenumber) or
759 (linenumber and with_linenumber) or
760 without_linenumber)
760 without_linenumber)
761
761
762 def pair(parent, child):
762 def pair(parent, child):
763 blocks = mdiff.allblocks(parent[1], child[1], opts=diffopts,
763 blocks = mdiff.allblocks(parent[1], child[1], opts=diffopts,
764 refine=True)
764 refine=True)
765 for (a1, a2, b1, b2), t in blocks:
765 for (a1, a2, b1, b2), t in blocks:
766 # Changed blocks ('!') or blocks made only of blank lines ('~')
766 # Changed blocks ('!') or blocks made only of blank lines ('~')
767 # belong to the child.
767 # belong to the child.
768 if t == '=':
768 if t == '=':
769 child[0][b1:b2] = parent[0][a1:a2]
769 child[0][b1:b2] = parent[0][a1:a2]
770 return child
770 return child
771
771
772 getlog = util.lrucachefunc(lambda x: self._repo.file(x))
772 getlog = util.lrucachefunc(lambda x: self._repo.file(x))
773
773
774 def parents(f):
774 def parents(f):
775 pl = f.parents()
775 pl = f.parents()
776
776
777 # Don't return renamed parents if we aren't following.
777 # Don't return renamed parents if we aren't following.
778 if not follow:
778 if not follow:
779 pl = [p for p in pl if p.path() == f.path()]
779 pl = [p for p in pl if p.path() == f.path()]
780
780
781 # renamed filectx won't have a filelog yet, so set it
781 # renamed filectx won't have a filelog yet, so set it
782 # from the cache to save time
782 # from the cache to save time
783 for p in pl:
783 for p in pl:
784 if not '_filelog' in p.__dict__:
784 if not '_filelog' in p.__dict__:
785 p._filelog = getlog(p.path())
785 p._filelog = getlog(p.path())
786
786
787 return pl
787 return pl
788
788
789 # use linkrev to find the first changeset where self appeared
789 # use linkrev to find the first changeset where self appeared
790 if self.rev() != self.linkrev():
790 if self.rev() != self.linkrev():
791 base = self.filectx(self.filenode())
791 base = self.filectx(self.filenode())
792 else:
792 else:
793 base = self
793 base = self
794
794
795 # This algorithm would prefer to be recursive, but Python is a
795 # This algorithm would prefer to be recursive, but Python is a
796 # bit recursion-hostile. Instead we do an iterative
796 # bit recursion-hostile. Instead we do an iterative
797 # depth-first search.
797 # depth-first search.
798
798
799 visit = [base]
799 visit = [base]
800 hist = {}
800 hist = {}
801 pcache = {}
801 pcache = {}
802 needed = {base: 1}
802 needed = {base: 1}
803 while visit:
803 while visit:
804 f = visit[-1]
804 f = visit[-1]
805 pcached = f in pcache
805 pcached = f in pcache
806 if not pcached:
806 if not pcached:
807 pcache[f] = parents(f)
807 pcache[f] = parents(f)
808
808
809 ready = True
809 ready = True
810 pl = pcache[f]
810 pl = pcache[f]
811 for p in pl:
811 for p in pl:
812 if p not in hist:
812 if p not in hist:
813 ready = False
813 ready = False
814 visit.append(p)
814 visit.append(p)
815 if not pcached:
815 if not pcached:
816 needed[p] = needed.get(p, 0) + 1
816 needed[p] = needed.get(p, 0) + 1
817 if ready:
817 if ready:
818 visit.pop()
818 visit.pop()
819 reusable = f in hist
819 reusable = f in hist
820 if reusable:
820 if reusable:
821 curr = hist[f]
821 curr = hist[f]
822 else:
822 else:
823 curr = decorate(f.data(), f)
823 curr = decorate(f.data(), f)
824 for p in pl:
824 for p in pl:
825 if not reusable:
825 if not reusable:
826 curr = pair(hist[p], curr)
826 curr = pair(hist[p], curr)
827 if needed[p] == 1:
827 if needed[p] == 1:
828 del hist[p]
828 del hist[p]
829 del needed[p]
829 del needed[p]
830 else:
830 else:
831 needed[p] -= 1
831 needed[p] -= 1
832
832
833 hist[f] = curr
833 hist[f] = curr
834 pcache[f] = []
834 pcache[f] = []
835
835
836 return zip(hist[base][0], hist[base][1].splitlines(True))
836 return zip(hist[base][0], hist[base][1].splitlines(True))
837
837
838 def ancestors(self, followfirst=False):
838 def ancestors(self, followfirst=False):
839 visit = {}
839 visit = {}
840 c = self
840 c = self
841 cut = followfirst and 1 or None
841 cut = followfirst and 1 or None
842 while True:
842 while True:
843 for parent in c.parents()[:cut]:
843 for parent in c.parents()[:cut]:
844 visit[(parent.rev(), parent.node())] = parent
844 visit[(parent.rev(), parent.node())] = parent
845 if not visit:
845 if not visit:
846 break
846 break
847 c = visit.pop(max(visit))
847 c = visit.pop(max(visit))
848 yield c
848 yield c
849
849
850 class filectx(basefilectx):
850 class filectx(basefilectx):
851 """A filecontext object makes access to data related to a particular
851 """A filecontext object makes access to data related to a particular
852 filerevision convenient."""
852 filerevision convenient."""
853 def __init__(self, repo, path, changeid=None, fileid=None,
853 def __init__(self, repo, path, changeid=None, fileid=None,
854 filelog=None, changectx=None):
854 filelog=None, changectx=None):
855 """changeid can be a changeset revision, node, or tag.
855 """changeid can be a changeset revision, node, or tag.
856 fileid can be a file revision or node."""
856 fileid can be a file revision or node."""
857 self._repo = repo
857 self._repo = repo
858 self._path = path
858 self._path = path
859
859
860 assert (changeid is not None
860 assert (changeid is not None
861 or fileid is not None
861 or fileid is not None
862 or changectx is not None), \
862 or changectx is not None), \
863 ("bad args: changeid=%r, fileid=%r, changectx=%r"
863 ("bad args: changeid=%r, fileid=%r, changectx=%r"
864 % (changeid, fileid, changectx))
864 % (changeid, fileid, changectx))
865
865
866 if filelog is not None:
866 if filelog is not None:
867 self._filelog = filelog
867 self._filelog = filelog
868
868
869 if changeid is not None:
869 if changeid is not None:
870 self._changeid = changeid
870 self._changeid = changeid
871 if changectx is not None:
871 if changectx is not None:
872 self._changectx = changectx
872 self._changectx = changectx
873 if fileid is not None:
873 if fileid is not None:
874 self._fileid = fileid
874 self._fileid = fileid
875
875
876 @propertycache
876 @propertycache
877 def _changectx(self):
877 def _changectx(self):
878 try:
878 try:
879 return changectx(self._repo, self._changeid)
879 return changectx(self._repo, self._changeid)
880 except error.RepoLookupError:
880 except error.RepoLookupError:
881 # Linkrev may point to any revision in the repository. When the
881 # Linkrev may point to any revision in the repository. When the
882 # repository is filtered this may lead to `filectx` trying to build
882 # repository is filtered this may lead to `filectx` trying to build
883 # `changectx` for filtered revision. In such case we fallback to
883 # `changectx` for filtered revision. In such case we fallback to
884 # creating `changectx` on the unfiltered version of the reposition.
884 # creating `changectx` on the unfiltered version of the reposition.
885 # This fallback should not be an issue because `changectx` from
885 # This fallback should not be an issue because `changectx` from
886 # `filectx` are not used in complex operations that care about
886 # `filectx` are not used in complex operations that care about
887 # filtering.
887 # filtering.
888 #
888 #
889 # This fallback is a cheap and dirty fix that prevent several
889 # This fallback is a cheap and dirty fix that prevent several
890 # crashes. It does not ensure the behavior is correct. However the
890 # crashes. It does not ensure the behavior is correct. However the
891 # behavior was not correct before filtering either and "incorrect
891 # behavior was not correct before filtering either and "incorrect
892 # behavior" is seen as better as "crash"
892 # behavior" is seen as better as "crash"
893 #
893 #
894 # Linkrevs have several serious troubles with filtering that are
894 # Linkrevs have several serious troubles with filtering that are
895 # complicated to solve. Proper handling of the issue here should be
895 # complicated to solve. Proper handling of the issue here should be
896 # considered when solving linkrev issue are on the table.
896 # considered when solving linkrev issue are on the table.
897 return changectx(self._repo.unfiltered(), self._changeid)
897 return changectx(self._repo.unfiltered(), self._changeid)
898
898
899 def filectx(self, fileid):
899 def filectx(self, fileid):
900 '''opens an arbitrary revision of the file without
900 '''opens an arbitrary revision of the file without
901 opening a new filelog'''
901 opening a new filelog'''
902 return filectx(self._repo, self._path, fileid=fileid,
902 return filectx(self._repo, self._path, fileid=fileid,
903 filelog=self._filelog)
903 filelog=self._filelog)
904
904
905 def data(self):
905 def data(self):
906 return self._filelog.read(self._filenode)
906 return self._filelog.read(self._filenode)
907 def size(self):
907 def size(self):
908 return self._filelog.size(self._filerev)
908 return self._filelog.size(self._filerev)
909
909
910 def renamed(self):
910 def renamed(self):
911 """check if file was actually renamed in this changeset revision
911 """check if file was actually renamed in this changeset revision
912
912
913 If rename logged in file revision, we report copy for changeset only
913 If rename logged in file revision, we report copy for changeset only
914 if file revisions linkrev points back to the changeset in question
914 if file revisions linkrev points back to the changeset in question
915 or both changeset parents contain different file revisions.
915 or both changeset parents contain different file revisions.
916 """
916 """
917
917
918 renamed = self._filelog.renamed(self._filenode)
918 renamed = self._filelog.renamed(self._filenode)
919 if not renamed:
919 if not renamed:
920 return renamed
920 return renamed
921
921
922 if self.rev() == self.linkrev():
922 if self.rev() == self.linkrev():
923 return renamed
923 return renamed
924
924
925 name = self.path()
925 name = self.path()
926 fnode = self._filenode
926 fnode = self._filenode
927 for p in self._changectx.parents():
927 for p in self._changectx.parents():
928 try:
928 try:
929 if fnode == p.filenode(name):
929 if fnode == p.filenode(name):
930 return None
930 return None
931 except error.LookupError:
931 except error.LookupError:
932 pass
932 pass
933 return renamed
933 return renamed
934
934
935 def children(self):
935 def children(self):
936 # hard for renames
936 # hard for renames
937 c = self._filelog.children(self._filenode)
937 c = self._filelog.children(self._filenode)
938 return [filectx(self._repo, self._path, fileid=x,
938 return [filectx(self._repo, self._path, fileid=x,
939 filelog=self._filelog) for x in c]
939 filelog=self._filelog) for x in c]
940
940
941 class committablectx(basectx):
941 class committablectx(basectx):
942 """A committablectx object provides common functionality for a context that
942 """A committablectx object provides common functionality for a context that
943 wants the ability to commit, e.g. workingctx or memctx."""
943 wants the ability to commit, e.g. workingctx or memctx."""
944 def __init__(self, repo, text="", user=None, date=None, extra=None,
944 def __init__(self, repo, text="", user=None, date=None, extra=None,
945 changes=None):
945 changes=None):
946 self._repo = repo
946 self._repo = repo
947 self._rev = None
947 self._rev = None
948 self._node = None
948 self._node = None
949 self._text = text
949 self._text = text
950 if date:
950 if date:
951 self._date = util.parsedate(date)
951 self._date = util.parsedate(date)
952 if user:
952 if user:
953 self._user = user
953 self._user = user
954 if changes:
954 if changes:
955 self._status = changes
955 self._status = changes
956
956
957 self._extra = {}
957 self._extra = {}
958 if extra:
958 if extra:
959 self._extra = extra.copy()
959 self._extra = extra.copy()
960 if 'branch' not in self._extra:
960 if 'branch' not in self._extra:
961 try:
961 try:
962 branch = encoding.fromlocal(self._repo.dirstate.branch())
962 branch = encoding.fromlocal(self._repo.dirstate.branch())
963 except UnicodeDecodeError:
963 except UnicodeDecodeError:
964 raise util.Abort(_('branch name not in UTF-8!'))
964 raise util.Abort(_('branch name not in UTF-8!'))
965 self._extra['branch'] = branch
965 self._extra['branch'] = branch
966 if self._extra['branch'] == '':
966 if self._extra['branch'] == '':
967 self._extra['branch'] = 'default'
967 self._extra['branch'] = 'default'
968
968
969 def __str__(self):
969 def __str__(self):
970 return str(self._parents[0]) + "+"
970 return str(self._parents[0]) + "+"
971
971
972 def __nonzero__(self):
972 def __nonzero__(self):
973 return True
973 return True
974
974
975 def __contains__(self, key):
975 def __contains__(self, key):
976 return self._repo.dirstate[key] not in "?r"
976 return self._repo.dirstate[key] not in "?r"
977
977
978 def _buildflagfunc(self):
978 def _buildflagfunc(self):
979 # Create a fallback function for getting file flags when the
979 # Create a fallback function for getting file flags when the
980 # filesystem doesn't support them
980 # filesystem doesn't support them
981
981
982 copiesget = self._repo.dirstate.copies().get
982 copiesget = self._repo.dirstate.copies().get
983
983
984 if len(self._parents) < 2:
984 if len(self._parents) < 2:
985 # when we have one parent, it's easy: copy from parent
985 # when we have one parent, it's easy: copy from parent
986 man = self._parents[0].manifest()
986 man = self._parents[0].manifest()
987 def func(f):
987 def func(f):
988 f = copiesget(f, f)
988 f = copiesget(f, f)
989 return man.flags(f)
989 return man.flags(f)
990 else:
990 else:
991 # merges are tricky: we try to reconstruct the unstored
991 # merges are tricky: we try to reconstruct the unstored
992 # result from the merge (issue1802)
992 # result from the merge (issue1802)
993 p1, p2 = self._parents
993 p1, p2 = self._parents
994 pa = p1.ancestor(p2)
994 pa = p1.ancestor(p2)
995 m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest()
995 m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest()
996
996
997 def func(f):
997 def func(f):
998 f = copiesget(f, f) # may be wrong for merges with copies
998 f = copiesget(f, f) # may be wrong for merges with copies
999 fl1, fl2, fla = m1.flags(f), m2.flags(f), ma.flags(f)
999 fl1, fl2, fla = m1.flags(f), m2.flags(f), ma.flags(f)
1000 if fl1 == fl2:
1000 if fl1 == fl2:
1001 return fl1
1001 return fl1
1002 if fl1 == fla:
1002 if fl1 == fla:
1003 return fl2
1003 return fl2
1004 if fl2 == fla:
1004 if fl2 == fla:
1005 return fl1
1005 return fl1
1006 return '' # punt for conflicts
1006 return '' # punt for conflicts
1007
1007
1008 return func
1008 return func
1009
1009
1010 @propertycache
1010 @propertycache
1011 def _flagfunc(self):
1011 def _flagfunc(self):
1012 return self._repo.dirstate.flagfunc(self._buildflagfunc)
1012 return self._repo.dirstate.flagfunc(self._buildflagfunc)
1013
1013
1014 @propertycache
1014 @propertycache
1015 def _manifest(self):
1015 def _manifest(self):
1016 """generate a manifest corresponding to the working directory"""
1016 """generate a manifest corresponding to the working directory"""
1017
1017
1018 man = self._parents[0].manifest().copy()
1018 man = self._parents[0].manifest().copy()
1019 if len(self._parents) > 1:
1019 if len(self._parents) > 1:
1020 man2 = self.p2().manifest()
1020 man2 = self.p2().manifest()
1021 def getman(f):
1021 def getman(f):
1022 if f in man:
1022 if f in man:
1023 return man
1023 return man
1024 return man2
1024 return man2
1025 else:
1025 else:
1026 getman = lambda f: man
1026 getman = lambda f: man
1027
1027
1028 copied = self._repo.dirstate.copies()
1028 copied = self._repo.dirstate.copies()
1029 ff = self._flagfunc
1029 ff = self._flagfunc
1030 modified, added, removed, deleted = self._status[:4]
1030 modified, added, removed, deleted = self._status[:4]
1031 for i, l in (("a", added), ("m", modified)):
1031 for i, l in (("a", added), ("m", modified)):
1032 for f in l:
1032 for f in l:
1033 orig = copied.get(f, f)
1033 orig = copied.get(f, f)
1034 man[f] = getman(orig).get(orig, nullid) + i
1034 man[f] = getman(orig).get(orig, nullid) + i
1035 try:
1035 try:
1036 man.set(f, ff(f))
1036 man.set(f, ff(f))
1037 except OSError:
1037 except OSError:
1038 pass
1038 pass
1039
1039
1040 for f in deleted + removed:
1040 for f in deleted + removed:
1041 if f in man:
1041 if f in man:
1042 del man[f]
1042 del man[f]
1043
1043
1044 return man
1044 return man
1045
1045
1046 @propertycache
1046 @propertycache
1047 def _status(self):
1047 def _status(self):
1048 return self._repo.status()
1048 return self._repo.status()
1049
1049
1050 @propertycache
1050 @propertycache
1051 def _user(self):
1051 def _user(self):
1052 return self._repo.ui.username()
1052 return self._repo.ui.username()
1053
1053
1054 @propertycache
1054 @propertycache
1055 def _date(self):
1055 def _date(self):
1056 return util.makedate()
1056 return util.makedate()
1057
1057
1058 def subrev(self, subpath):
1058 def subrev(self, subpath):
1059 return None
1059 return None
1060
1060
1061 def user(self):
1061 def user(self):
1062 return self._user or self._repo.ui.username()
1062 return self._user or self._repo.ui.username()
1063 def date(self):
1063 def date(self):
1064 return self._date
1064 return self._date
1065 def description(self):
1065 def description(self):
1066 return self._text
1066 return self._text
1067 def files(self):
1067 def files(self):
1068 return sorted(self._status[0] + self._status[1] + self._status[2])
1068 return sorted(self._status[0] + self._status[1] + self._status[2])
1069
1069
1070 def modified(self):
1070 def modified(self):
1071 return self._status[0]
1071 return self._status[0]
1072 def added(self):
1072 def added(self):
1073 return self._status[1]
1073 return self._status[1]
1074 def removed(self):
1074 def removed(self):
1075 return self._status[2]
1075 return self._status[2]
1076 def deleted(self):
1076 def deleted(self):
1077 return self._status[3]
1077 return self._status[3]
1078 def unknown(self):
1078 def unknown(self):
1079 return self._status[4]
1079 return self._status[4]
1080 def ignored(self):
1080 def ignored(self):
1081 return self._status[5]
1081 return self._status[5]
1082 def clean(self):
1082 def clean(self):
1083 return self._status[6]
1083 return self._status[6]
1084 def branch(self):
1084 def branch(self):
1085 return encoding.tolocal(self._extra['branch'])
1085 return encoding.tolocal(self._extra['branch'])
1086 def closesbranch(self):
1086 def closesbranch(self):
1087 return 'close' in self._extra
1087 return 'close' in self._extra
1088 def extra(self):
1088 def extra(self):
1089 return self._extra
1089 return self._extra
1090
1090
1091 def tags(self):
1091 def tags(self):
1092 t = []
1092 t = []
1093 for p in self.parents():
1093 for p in self.parents():
1094 t.extend(p.tags())
1094 t.extend(p.tags())
1095 return t
1095 return t
1096
1096
1097 def bookmarks(self):
1097 def bookmarks(self):
1098 b = []
1098 b = []
1099 for p in self.parents():
1099 for p in self.parents():
1100 b.extend(p.bookmarks())
1100 b.extend(p.bookmarks())
1101 return b
1101 return b
1102
1102
1103 def phase(self):
1103 def phase(self):
1104 phase = phases.draft # default phase to draft
1104 phase = phases.draft # default phase to draft
1105 for p in self.parents():
1105 for p in self.parents():
1106 phase = max(phase, p.phase())
1106 phase = max(phase, p.phase())
1107 return phase
1107 return phase
1108
1108
1109 def hidden(self):
1109 def hidden(self):
1110 return False
1110 return False
1111
1111
1112 def children(self):
1112 def children(self):
1113 return []
1113 return []
1114
1114
1115 def flags(self, path):
1115 def flags(self, path):
1116 if '_manifest' in self.__dict__:
1116 if '_manifest' in self.__dict__:
1117 try:
1117 try:
1118 return self._manifest.flags(path)
1118 return self._manifest.flags(path)
1119 except KeyError:
1119 except KeyError:
1120 return ''
1120 return ''
1121
1121
1122 try:
1122 try:
1123 return self._flagfunc(path)
1123 return self._flagfunc(path)
1124 except OSError:
1124 except OSError:
1125 return ''
1125 return ''
1126
1126
1127 def ancestor(self, c2):
1127 def ancestor(self, c2):
1128 """return the ancestor context of self and c2"""
1128 """return the ancestor context of self and c2"""
1129 return self._parents[0].ancestor(c2) # punt on two parents for now
1129 return self._parents[0].ancestor(c2) # punt on two parents for now
1130
1130
1131 def walk(self, match):
1131 def walk(self, match):
1132 return sorted(self._repo.dirstate.walk(match, sorted(self.substate),
1132 return sorted(self._repo.dirstate.walk(match, sorted(self.substate),
1133 True, False))
1133 True, False))
1134
1134
1135 def ancestors(self):
1135 def ancestors(self):
1136 for a in self._repo.changelog.ancestors(
1136 for a in self._repo.changelog.ancestors(
1137 [p.rev() for p in self._parents]):
1137 [p.rev() for p in self._parents]):
1138 yield changectx(self._repo, a)
1138 yield changectx(self._repo, a)
1139
1139
1140 def markcommitted(self, node):
1140 def markcommitted(self, node):
1141 """Perform post-commit cleanup necessary after committing this ctx
1141 """Perform post-commit cleanup necessary after committing this ctx
1142
1142
1143 Specifically, this updates backing stores this working context
1143 Specifically, this updates backing stores this working context
1144 wraps to reflect the fact that the changes reflected by this
1144 wraps to reflect the fact that the changes reflected by this
1145 workingctx have been committed. For example, it marks
1145 workingctx have been committed. For example, it marks
1146 modified and added files as normal in the dirstate.
1146 modified and added files as normal in the dirstate.
1147
1147
1148 """
1148 """
1149
1149
1150 for f in self.modified() + self.added():
1150 for f in self.modified() + self.added():
1151 self._repo.dirstate.normal(f)
1151 self._repo.dirstate.normal(f)
1152 for f in self.removed():
1152 for f in self.removed():
1153 self._repo.dirstate.drop(f)
1153 self._repo.dirstate.drop(f)
1154 self._repo.dirstate.setparents(node)
1154 self._repo.dirstate.setparents(node)
1155
1155
1156 def dirs(self):
1156 def dirs(self):
1157 return self._repo.dirstate.dirs()
1157 return self._repo.dirstate.dirs()
1158
1158
1159 class workingctx(committablectx):
1159 class workingctx(committablectx):
1160 """A workingctx object makes access to data related to
1160 """A workingctx object makes access to data related to
1161 the current working directory convenient.
1161 the current working directory convenient.
1162 date - any valid date string or (unixtime, offset), or None.
1162 date - any valid date string or (unixtime, offset), or None.
1163 user - username string, or None.
1163 user - username string, or None.
1164 extra - a dictionary of extra values, or None.
1164 extra - a dictionary of extra values, or None.
1165 changes - a list of file lists as returned by localrepo.status()
1165 changes - a list of file lists as returned by localrepo.status()
1166 or None to use the repository status.
1166 or None to use the repository status.
1167 """
1167 """
1168 def __init__(self, repo, text="", user=None, date=None, extra=None,
1168 def __init__(self, repo, text="", user=None, date=None, extra=None,
1169 changes=None):
1169 changes=None):
1170 super(workingctx, self).__init__(repo, text, user, date, extra, changes)
1170 super(workingctx, self).__init__(repo, text, user, date, extra, changes)
1171
1171
1172 def __iter__(self):
1172 def __iter__(self):
1173 d = self._repo.dirstate
1173 d = self._repo.dirstate
1174 for f in d:
1174 for f in d:
1175 if d[f] != 'r':
1175 if d[f] != 'r':
1176 yield f
1176 yield f
1177
1177
1178 @propertycache
1178 @propertycache
1179 def _parents(self):
1179 def _parents(self):
1180 p = self._repo.dirstate.parents()
1180 p = self._repo.dirstate.parents()
1181 if p[1] == nullid:
1181 if p[1] == nullid:
1182 p = p[:-1]
1182 p = p[:-1]
1183 return [changectx(self._repo, x) for x in p]
1183 return [changectx(self._repo, x) for x in p]
1184
1184
1185 def filectx(self, path, filelog=None):
1185 def filectx(self, path, filelog=None):
1186 """get a file context from the working directory"""
1186 """get a file context from the working directory"""
1187 return workingfilectx(self._repo, path, workingctx=self,
1187 return workingfilectx(self._repo, path, workingctx=self,
1188 filelog=filelog)
1188 filelog=filelog)
1189
1189
1190 def dirty(self, missing=False, merge=True, branch=True):
1190 def dirty(self, missing=False, merge=True, branch=True):
1191 "check whether a working directory is modified"
1191 "check whether a working directory is modified"
1192 # check subrepos first
1192 # check subrepos first
1193 for s in sorted(self.substate):
1193 for s in sorted(self.substate):
1194 if self.sub(s).dirty():
1194 if self.sub(s).dirty():
1195 return True
1195 return True
1196 # check current working dir
1196 # check current working dir
1197 return ((merge and self.p2()) or
1197 return ((merge and self.p2()) or
1198 (branch and self.branch() != self.p1().branch()) or
1198 (branch and self.branch() != self.p1().branch()) or
1199 self.modified() or self.added() or self.removed() or
1199 self.modified() or self.added() or self.removed() or
1200 (missing and self.deleted()))
1200 (missing and self.deleted()))
1201
1201
1202 def add(self, list, prefix=""):
1202 def add(self, list, prefix=""):
1203 join = lambda f: os.path.join(prefix, f)
1203 join = lambda f: os.path.join(prefix, f)
1204 wlock = self._repo.wlock()
1204 wlock = self._repo.wlock()
1205 ui, ds = self._repo.ui, self._repo.dirstate
1205 ui, ds = self._repo.ui, self._repo.dirstate
1206 try:
1206 try:
1207 rejected = []
1207 rejected = []
1208 lstat = self._repo.wvfs.lstat
1208 lstat = self._repo.wvfs.lstat
1209 for f in list:
1209 for f in list:
1210 scmutil.checkportable(ui, join(f))
1210 scmutil.checkportable(ui, join(f))
1211 try:
1211 try:
1212 st = lstat(f)
1212 st = lstat(f)
1213 except OSError:
1213 except OSError:
1214 ui.warn(_("%s does not exist!\n") % join(f))
1214 ui.warn(_("%s does not exist!\n") % join(f))
1215 rejected.append(f)
1215 rejected.append(f)
1216 continue
1216 continue
1217 if st.st_size > 10000000:
1217 if st.st_size > 10000000:
1218 ui.warn(_("%s: up to %d MB of RAM may be required "
1218 ui.warn(_("%s: up to %d MB of RAM may be required "
1219 "to manage this file\n"
1219 "to manage this file\n"
1220 "(use 'hg revert %s' to cancel the "
1220 "(use 'hg revert %s' to cancel the "
1221 "pending addition)\n")
1221 "pending addition)\n")
1222 % (f, 3 * st.st_size // 1000000, join(f)))
1222 % (f, 3 * st.st_size // 1000000, join(f)))
1223 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
1223 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
1224 ui.warn(_("%s not added: only files and symlinks "
1224 ui.warn(_("%s not added: only files and symlinks "
1225 "supported currently\n") % join(f))
1225 "supported currently\n") % join(f))
1226 rejected.append(f)
1226 rejected.append(f)
1227 elif ds[f] in 'amn':
1227 elif ds[f] in 'amn':
1228 ui.warn(_("%s already tracked!\n") % join(f))
1228 ui.warn(_("%s already tracked!\n") % join(f))
1229 elif ds[f] == 'r':
1229 elif ds[f] == 'r':
1230 ds.normallookup(f)
1230 ds.normallookup(f)
1231 else:
1231 else:
1232 ds.add(f)
1232 ds.add(f)
1233 return rejected
1233 return rejected
1234 finally:
1234 finally:
1235 wlock.release()
1235 wlock.release()
1236
1236
1237 def forget(self, files, prefix=""):
1237 def forget(self, files, prefix=""):
1238 join = lambda f: os.path.join(prefix, f)
1238 join = lambda f: os.path.join(prefix, f)
1239 wlock = self._repo.wlock()
1239 wlock = self._repo.wlock()
1240 try:
1240 try:
1241 rejected = []
1241 rejected = []
1242 for f in files:
1242 for f in files:
1243 if f not in self._repo.dirstate:
1243 if f not in self._repo.dirstate:
1244 self._repo.ui.warn(_("%s not tracked!\n") % join(f))
1244 self._repo.ui.warn(_("%s not tracked!\n") % join(f))
1245 rejected.append(f)
1245 rejected.append(f)
1246 elif self._repo.dirstate[f] != 'a':
1246 elif self._repo.dirstate[f] != 'a':
1247 self._repo.dirstate.remove(f)
1247 self._repo.dirstate.remove(f)
1248 else:
1248 else:
1249 self._repo.dirstate.drop(f)
1249 self._repo.dirstate.drop(f)
1250 return rejected
1250 return rejected
1251 finally:
1251 finally:
1252 wlock.release()
1252 wlock.release()
1253
1253
1254 def undelete(self, list):
1254 def undelete(self, list):
1255 pctxs = self.parents()
1255 pctxs = self.parents()
1256 wlock = self._repo.wlock()
1256 wlock = self._repo.wlock()
1257 try:
1257 try:
1258 for f in list:
1258 for f in list:
1259 if self._repo.dirstate[f] != 'r':
1259 if self._repo.dirstate[f] != 'r':
1260 self._repo.ui.warn(_("%s not removed!\n") % f)
1260 self._repo.ui.warn(_("%s not removed!\n") % f)
1261 else:
1261 else:
1262 fctx = f in pctxs[0] and pctxs[0][f] or pctxs[1][f]
1262 fctx = f in pctxs[0] and pctxs[0][f] or pctxs[1][f]
1263 t = fctx.data()
1263 t = fctx.data()
1264 self._repo.wwrite(f, t, fctx.flags())
1264 self._repo.wwrite(f, t, fctx.flags())
1265 self._repo.dirstate.normal(f)
1265 self._repo.dirstate.normal(f)
1266 finally:
1266 finally:
1267 wlock.release()
1267 wlock.release()
1268
1268
1269 def copy(self, source, dest):
1269 def copy(self, source, dest):
1270 try:
1270 try:
1271 st = self._repo.wvfs.lstat(dest)
1271 st = self._repo.wvfs.lstat(dest)
1272 except OSError, err:
1272 except OSError, err:
1273 if err.errno != errno.ENOENT:
1273 if err.errno != errno.ENOENT:
1274 raise
1274 raise
1275 self._repo.ui.warn(_("%s does not exist!\n") % dest)
1275 self._repo.ui.warn(_("%s does not exist!\n") % dest)
1276 return
1276 return
1277 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
1277 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
1278 self._repo.ui.warn(_("copy failed: %s is not a file or a "
1278 self._repo.ui.warn(_("copy failed: %s is not a file or a "
1279 "symbolic link\n") % dest)
1279 "symbolic link\n") % dest)
1280 else:
1280 else:
1281 wlock = self._repo.wlock()
1281 wlock = self._repo.wlock()
1282 try:
1282 try:
1283 if self._repo.dirstate[dest] in '?r':
1283 if self._repo.dirstate[dest] in '?r':
1284 self._repo.dirstate.add(dest)
1284 self._repo.dirstate.add(dest)
1285 self._repo.dirstate.copy(source, dest)
1285 self._repo.dirstate.copy(source, dest)
1286 finally:
1286 finally:
1287 wlock.release()
1287 wlock.release()
1288
1288
1289 def _filtersuspectsymlink(self, files):
1289 def _filtersuspectsymlink(self, files):
1290 if not files or self._repo.dirstate._checklink:
1290 if not files or self._repo.dirstate._checklink:
1291 return files
1291 return files
1292
1292
1293 # Symlink placeholders may get non-symlink-like contents
1293 # Symlink placeholders may get non-symlink-like contents
1294 # via user error or dereferencing by NFS or Samba servers,
1294 # via user error or dereferencing by NFS or Samba servers,
1295 # so we filter out any placeholders that don't look like a
1295 # so we filter out any placeholders that don't look like a
1296 # symlink
1296 # symlink
1297 sane = []
1297 sane = []
1298 for f in files:
1298 for f in files:
1299 if self.flags(f) == 'l':
1299 if self.flags(f) == 'l':
1300 d = self[f].data()
1300 d = self[f].data()
1301 if d == '' or len(d) >= 1024 or '\n' in d or util.binary(d):
1301 if d == '' or len(d) >= 1024 or '\n' in d or util.binary(d):
1302 self._repo.ui.debug('ignoring suspect symlink placeholder'
1302 self._repo.ui.debug('ignoring suspect symlink placeholder'
1303 ' "%s"\n' % f)
1303 ' "%s"\n' % f)
1304 continue
1304 continue
1305 sane.append(f)
1305 sane.append(f)
1306 return sane
1306 return sane
1307
1307
1308 def _checklookup(self, files):
1308 def _checklookup(self, files):
1309 # check for any possibly clean files
1309 # check for any possibly clean files
1310 if not files:
1310 if not files:
1311 return [], []
1311 return [], []
1312
1312
1313 modified = []
1313 modified = []
1314 fixup = []
1314 fixup = []
1315 pctx = self._parents[0]
1315 pctx = self._parents[0]
1316 # do a full compare of any files that might have changed
1316 # do a full compare of any files that might have changed
1317 for f in sorted(files):
1317 for f in sorted(files):
1318 if (f not in pctx or self.flags(f) != pctx.flags(f)
1318 if (f not in pctx or self.flags(f) != pctx.flags(f)
1319 or pctx[f].cmp(self[f])):
1319 or pctx[f].cmp(self[f])):
1320 modified.append(f)
1320 modified.append(f)
1321 else:
1321 else:
1322 fixup.append(f)
1322 fixup.append(f)
1323
1323
1324 # update dirstate for files that are actually clean
1324 # update dirstate for files that are actually clean
1325 if fixup:
1325 if fixup:
1326 try:
1326 try:
1327 # updating the dirstate is optional
1327 # updating the dirstate is optional
1328 # so we don't wait on the lock
1328 # so we don't wait on the lock
1329 normal = self._repo.dirstate.normal
1329 normal = self._repo.dirstate.normal
1330 wlock = self._repo.wlock(False)
1330 wlock = self._repo.wlock(False)
1331 try:
1331 try:
1332 for f in fixup:
1332 for f in fixup:
1333 normal(f)
1333 normal(f)
1334 finally:
1334 finally:
1335 wlock.release()
1335 wlock.release()
1336 except error.LockError:
1336 except error.LockError:
1337 pass
1337 pass
1338 return modified, fixup
1338 return modified, fixup
1339
1339
1340 def _manifestmatches(self, match, s):
1340 def _manifestmatches(self, match, s):
1341 """Slow path for workingctx
1341 """Slow path for workingctx
1342
1342
1343 The fast path is when we compare the working directory to its parent
1343 The fast path is when we compare the working directory to its parent
1344 which means this function is comparing with a non-parent; therefore we
1344 which means this function is comparing with a non-parent; therefore we
1345 need to build a manifest and return what matches.
1345 need to build a manifest and return what matches.
1346 """
1346 """
1347 mf = self._repo['.']._manifestmatches(match, s)
1347 mf = self._repo['.']._manifestmatches(match, s)
1348 modified, added, removed = s[0:3]
1348 modified, added, removed = s[0:3]
1349 for f in modified + added:
1349 for f in modified + added:
1350 mf[f] = None
1350 mf[f] = None
1351 mf.set(f, self.flags(f))
1351 mf.set(f, self.flags(f))
1352 for f in removed:
1352 for f in removed:
1353 if f in mf:
1353 if f in mf:
1354 del mf[f]
1354 del mf[f]
1355 return mf
1355 return mf
1356
1356
1357 def _prestatus(self, other, s, match, listignored, listclean, listunknown):
1357 def _prestatus(self, other, s, match, listignored, listclean, listunknown):
1358 """override the parent hook with a dirstate query
1358 """override the parent hook with a dirstate query
1359
1359
1360 We use this prestatus hook to populate the status with information from
1360 We use this prestatus hook to populate the status with information from
1361 the dirstate.
1361 the dirstate.
1362 """
1362 """
1363 # doesn't need to call super; if that changes, be aware that super
1363 # doesn't need to call super; if that changes, be aware that super
1364 # calls self.manifest which would slow down the common case of calling
1364 # calls self.manifest which would slow down the common case of calling
1365 # status against a workingctx's parent
1365 # status against a workingctx's parent
1366 return self._dirstatestatus(match, listignored, listclean, listunknown)
1366 return self._dirstatestatus(match, listignored, listclean, listunknown)
1367
1367
1368 def _poststatus(self, other, s, match, listignored, listclean, listunknown):
1368 def _poststatus(self, other, s, match, listignored, listclean, listunknown):
1369 """override the parent hook with a filter for suspect symlinks
1369 """override the parent hook with a filter for suspect symlinks
1370
1370
1371 We use this poststatus hook to filter out symlinks that might have
1371 We use this poststatus hook to filter out symlinks that might have
1372 accidentally ended up with the entire contents of the file they are
1372 accidentally ended up with the entire contents of the file they are
1373 susposed to be linking to.
1373 susposed to be linking to.
1374 """
1374 """
1375 s[0] = self._filtersuspectsymlink(s[0])
1375 s[0] = self._filtersuspectsymlink(s[0])
1376 self._status = s
1376 self._status = s
1377 return s
1377 return s
1378
1378
1379 def _dirstatestatus(self, match=None, ignored=False, clean=False,
1379 def _dirstatestatus(self, match=None, ignored=False, clean=False,
1380 unknown=False):
1380 unknown=False):
1381 '''Gets the status from the dirstate -- internal use only.'''
1381 '''Gets the status from the dirstate -- internal use only.'''
1382 listignored, listclean, listunknown = ignored, clean, unknown
1382 listignored, listclean, listunknown = ignored, clean, unknown
1383 match = match or matchmod.always(self._repo.root, self._repo.getcwd())
1383 match = match or matchmod.always(self._repo.root, self._repo.getcwd())
1384 subrepos = []
1384 subrepos = []
1385 if '.hgsub' in self:
1385 if '.hgsub' in self:
1386 subrepos = sorted(self.substate)
1386 subrepos = sorted(self.substate)
1387 s = self._repo.dirstate.status(match, subrepos, listignored,
1387 s = self._repo.dirstate.status(match, subrepos, listignored,
1388 listclean, listunknown)
1388 listclean, listunknown)
1389 cmp, modified, added, removed, deleted, unknown, ignored, clean = s
1389 cmp, modified, added, removed, deleted, unknown, ignored, clean = s
1390
1390
1391 # check for any possibly clean files
1391 # check for any possibly clean files
1392 if cmp:
1392 if cmp:
1393 modified2, fixup = self._checklookup(cmp)
1393 modified2, fixup = self._checklookup(cmp)
1394 modified += modified2
1394 modified += modified2
1395
1395
1396 # update dirstate for files that are actually clean
1396 # update dirstate for files that are actually clean
1397 if fixup and listclean:
1397 if fixup and listclean:
1398 clean += fixup
1398 clean += fixup
1399
1399
1400 return [modified, added, removed, deleted, unknown, ignored, clean]
1400 return [modified, added, removed, deleted, unknown, ignored, clean]
1401
1401
1402 def _buildstatus(self, other, s, match, listignored, listclean,
1402 def _buildstatus(self, other, s, match, listignored, listclean,
1403 listunknown):
1403 listunknown):
1404 """build a status with respect to another context
1404 """build a status with respect to another context
1405
1405
1406 This includes logic for maintaining the fast path of status when
1406 This includes logic for maintaining the fast path of status when
1407 comparing the working directory against its parent, which is to skip
1407 comparing the working directory against its parent, which is to skip
1408 building a new manifest if self (working directory) is not comparing
1408 building a new manifest if self (working directory) is not comparing
1409 against its parent (repo['.']).
1409 against its parent (repo['.']).
1410 """
1410 """
1411 if other != self._repo['.']:
1411 if other != self._repo['.']:
1412 s = super(workingctx, self)._buildstatus(other, s, match,
1412 s = super(workingctx, self)._buildstatus(other, s, match,
1413 listignored, listclean,
1413 listignored, listclean,
1414 listunknown)
1414 listunknown)
1415 return s
1415 return s
1416
1416
1417 def _matchstatus(self, other, s, match, listignored, listclean,
1417 def _matchstatus(self, other, s, match, listignored, listclean,
1418 listunknown):
1418 listunknown):
1419 """override the match method with a filter for directory patterns
1419 """override the match method with a filter for directory patterns
1420
1420
1421 We use inheritance to customize the match.bad method only in cases of
1421 We use inheritance to customize the match.bad method only in cases of
1422 workingctx since it belongs only to the working directory when
1422 workingctx since it belongs only to the working directory when
1423 comparing against the parent changeset.
1423 comparing against the parent changeset.
1424
1424
1425 If we aren't comparing against the working directory's parent, then we
1425 If we aren't comparing against the working directory's parent, then we
1426 just use the default match object sent to us.
1426 just use the default match object sent to us.
1427 """
1427 """
1428 superself = super(workingctx, self)
1428 superself = super(workingctx, self)
1429 match = superself._matchstatus(other, s, match, listignored, listclean,
1429 match = superself._matchstatus(other, s, match, listignored, listclean,
1430 listunknown)
1430 listunknown)
1431 if other != self._repo['.']:
1431 if other != self._repo['.']:
1432 def bad(f, msg):
1432 def bad(f, msg):
1433 # 'f' may be a directory pattern from 'match.files()',
1433 # 'f' may be a directory pattern from 'match.files()',
1434 # so 'f not in ctx1' is not enough
1434 # so 'f not in ctx1' is not enough
1435 if f not in other and f not in other.dirs():
1435 if f not in other and f not in other.dirs():
1436 self._repo.ui.warn('%s: %s\n' %
1436 self._repo.ui.warn('%s: %s\n' %
1437 (self._repo.dirstate.pathto(f), msg))
1437 (self._repo.dirstate.pathto(f), msg))
1438 match.bad = bad
1438 match.bad = bad
1439 return match
1439 return match
1440
1440
1441 def status(self, ignored=False, clean=False, unknown=False, match=None):
1441 def status(self, other='.', match=None, listignored=False,
1442 """Explicit status query
1442 listclean=False, listunknown=False, listsubrepos=False):
1443 Unless this method is used to query the working copy status, the
1443 # yet to be determined: what to do if 'other' is a 'workingctx' or a
1444 _status property will implicitly read the status using its default
1444 # 'memctx'?
1445 arguments."""
1445 s = super(workingctx, self).status(other, match, listignored, listclean,
1446 listignored, listclean, listunknown = ignored, clean, unknown
1446 listunknown, listsubrepos)
1447 s = self._dirstatestatus(match=match, ignored=listignored,
1447 # calling 'super' subtly reveresed the contexts, so we flip the results
1448 clean=listclean, unknown=listunknown)
1448 # (s[1] is 'added' and s[2] is 'removed')
1449
1449 s[1], s[2] = s[2], s[1]
1450 s[0] = self._filtersuspectsymlink(s[0])
1451 self._status = s
1452 return s
1450 return s
1453
1451
1454
1455 class committablefilectx(basefilectx):
1452 class committablefilectx(basefilectx):
1456 """A committablefilectx provides common functionality for a file context
1453 """A committablefilectx provides common functionality for a file context
1457 that wants the ability to commit, e.g. workingfilectx or memfilectx."""
1454 that wants the ability to commit, e.g. workingfilectx or memfilectx."""
1458 def __init__(self, repo, path, filelog=None, ctx=None):
1455 def __init__(self, repo, path, filelog=None, ctx=None):
1459 self._repo = repo
1456 self._repo = repo
1460 self._path = path
1457 self._path = path
1461 self._changeid = None
1458 self._changeid = None
1462 self._filerev = self._filenode = None
1459 self._filerev = self._filenode = None
1463
1460
1464 if filelog is not None:
1461 if filelog is not None:
1465 self._filelog = filelog
1462 self._filelog = filelog
1466 if ctx:
1463 if ctx:
1467 self._changectx = ctx
1464 self._changectx = ctx
1468
1465
1469 def __nonzero__(self):
1466 def __nonzero__(self):
1470 return True
1467 return True
1471
1468
1472 def parents(self):
1469 def parents(self):
1473 '''return parent filectxs, following copies if necessary'''
1470 '''return parent filectxs, following copies if necessary'''
1474 def filenode(ctx, path):
1471 def filenode(ctx, path):
1475 return ctx._manifest.get(path, nullid)
1472 return ctx._manifest.get(path, nullid)
1476
1473
1477 path = self._path
1474 path = self._path
1478 fl = self._filelog
1475 fl = self._filelog
1479 pcl = self._changectx._parents
1476 pcl = self._changectx._parents
1480 renamed = self.renamed()
1477 renamed = self.renamed()
1481
1478
1482 if renamed:
1479 if renamed:
1483 pl = [renamed + (None,)]
1480 pl = [renamed + (None,)]
1484 else:
1481 else:
1485 pl = [(path, filenode(pcl[0], path), fl)]
1482 pl = [(path, filenode(pcl[0], path), fl)]
1486
1483
1487 for pc in pcl[1:]:
1484 for pc in pcl[1:]:
1488 pl.append((path, filenode(pc, path), fl))
1485 pl.append((path, filenode(pc, path), fl))
1489
1486
1490 return [filectx(self._repo, p, fileid=n, filelog=l)
1487 return [filectx(self._repo, p, fileid=n, filelog=l)
1491 for p, n, l in pl if n != nullid]
1488 for p, n, l in pl if n != nullid]
1492
1489
1493 def children(self):
1490 def children(self):
1494 return []
1491 return []
1495
1492
1496 class workingfilectx(committablefilectx):
1493 class workingfilectx(committablefilectx):
1497 """A workingfilectx object makes access to data related to a particular
1494 """A workingfilectx object makes access to data related to a particular
1498 file in the working directory convenient."""
1495 file in the working directory convenient."""
1499 def __init__(self, repo, path, filelog=None, workingctx=None):
1496 def __init__(self, repo, path, filelog=None, workingctx=None):
1500 super(workingfilectx, self).__init__(repo, path, filelog, workingctx)
1497 super(workingfilectx, self).__init__(repo, path, filelog, workingctx)
1501
1498
1502 @propertycache
1499 @propertycache
1503 def _changectx(self):
1500 def _changectx(self):
1504 return workingctx(self._repo)
1501 return workingctx(self._repo)
1505
1502
1506 def data(self):
1503 def data(self):
1507 return self._repo.wread(self._path)
1504 return self._repo.wread(self._path)
1508 def renamed(self):
1505 def renamed(self):
1509 rp = self._repo.dirstate.copied(self._path)
1506 rp = self._repo.dirstate.copied(self._path)
1510 if not rp:
1507 if not rp:
1511 return None
1508 return None
1512 return rp, self._changectx._parents[0]._manifest.get(rp, nullid)
1509 return rp, self._changectx._parents[0]._manifest.get(rp, nullid)
1513
1510
1514 def size(self):
1511 def size(self):
1515 return self._repo.wvfs.lstat(self._path).st_size
1512 return self._repo.wvfs.lstat(self._path).st_size
1516 def date(self):
1513 def date(self):
1517 t, tz = self._changectx.date()
1514 t, tz = self._changectx.date()
1518 try:
1515 try:
1519 return (int(self._repo.wvfs.lstat(self._path).st_mtime), tz)
1516 return (int(self._repo.wvfs.lstat(self._path).st_mtime), tz)
1520 except OSError, err:
1517 except OSError, err:
1521 if err.errno != errno.ENOENT:
1518 if err.errno != errno.ENOENT:
1522 raise
1519 raise
1523 return (t, tz)
1520 return (t, tz)
1524
1521
1525 def cmp(self, fctx):
1522 def cmp(self, fctx):
1526 """compare with other file context
1523 """compare with other file context
1527
1524
1528 returns True if different than fctx.
1525 returns True if different than fctx.
1529 """
1526 """
1530 # fctx should be a filectx (not a workingfilectx)
1527 # fctx should be a filectx (not a workingfilectx)
1531 # invert comparison to reuse the same code path
1528 # invert comparison to reuse the same code path
1532 return fctx.cmp(self)
1529 return fctx.cmp(self)
1533
1530
1534 class memctx(object):
1531 class memctx(object):
1535 """Use memctx to perform in-memory commits via localrepo.commitctx().
1532 """Use memctx to perform in-memory commits via localrepo.commitctx().
1536
1533
1537 Revision information is supplied at initialization time while
1534 Revision information is supplied at initialization time while
1538 related files data and is made available through a callback
1535 related files data and is made available through a callback
1539 mechanism. 'repo' is the current localrepo, 'parents' is a
1536 mechanism. 'repo' is the current localrepo, 'parents' is a
1540 sequence of two parent revisions identifiers (pass None for every
1537 sequence of two parent revisions identifiers (pass None for every
1541 missing parent), 'text' is the commit message and 'files' lists
1538 missing parent), 'text' is the commit message and 'files' lists
1542 names of files touched by the revision (normalized and relative to
1539 names of files touched by the revision (normalized and relative to
1543 repository root).
1540 repository root).
1544
1541
1545 filectxfn(repo, memctx, path) is a callable receiving the
1542 filectxfn(repo, memctx, path) is a callable receiving the
1546 repository, the current memctx object and the normalized path of
1543 repository, the current memctx object and the normalized path of
1547 requested file, relative to repository root. It is fired by the
1544 requested file, relative to repository root. It is fired by the
1548 commit function for every file in 'files', but calls order is
1545 commit function for every file in 'files', but calls order is
1549 undefined. If the file is available in the revision being
1546 undefined. If the file is available in the revision being
1550 committed (updated or added), filectxfn returns a memfilectx
1547 committed (updated or added), filectxfn returns a memfilectx
1551 object. If the file was removed, filectxfn raises an
1548 object. If the file was removed, filectxfn raises an
1552 IOError. Moved files are represented by marking the source file
1549 IOError. Moved files are represented by marking the source file
1553 removed and the new file added with copy information (see
1550 removed and the new file added with copy information (see
1554 memfilectx).
1551 memfilectx).
1555
1552
1556 user receives the committer name and defaults to current
1553 user receives the committer name and defaults to current
1557 repository username, date is the commit date in any format
1554 repository username, date is the commit date in any format
1558 supported by util.parsedate() and defaults to current date, extra
1555 supported by util.parsedate() and defaults to current date, extra
1559 is a dictionary of metadata or is left empty.
1556 is a dictionary of metadata or is left empty.
1560 """
1557 """
1561 def __init__(self, repo, parents, text, files, filectxfn, user=None,
1558 def __init__(self, repo, parents, text, files, filectxfn, user=None,
1562 date=None, extra=None, editor=False):
1559 date=None, extra=None, editor=False):
1563 self._repo = repo
1560 self._repo = repo
1564 self._rev = None
1561 self._rev = None
1565 self._node = None
1562 self._node = None
1566 self._text = text
1563 self._text = text
1567 self._date = date and util.parsedate(date) or util.makedate()
1564 self._date = date and util.parsedate(date) or util.makedate()
1568 self._user = user
1565 self._user = user
1569 parents = [(p or nullid) for p in parents]
1566 parents = [(p or nullid) for p in parents]
1570 p1, p2 = parents
1567 p1, p2 = parents
1571 self._parents = [changectx(self._repo, p) for p in (p1, p2)]
1568 self._parents = [changectx(self._repo, p) for p in (p1, p2)]
1572 files = sorted(set(files))
1569 files = sorted(set(files))
1573 self._status = [files, [], [], [], []]
1570 self._status = [files, [], [], [], []]
1574 self._filectxfn = filectxfn
1571 self._filectxfn = filectxfn
1575
1572
1576 self._extra = extra and extra.copy() or {}
1573 self._extra = extra and extra.copy() or {}
1577 if self._extra.get('branch', '') == '':
1574 if self._extra.get('branch', '') == '':
1578 self._extra['branch'] = 'default'
1575 self._extra['branch'] = 'default'
1579
1576
1580 if editor:
1577 if editor:
1581 self._text = editor(self._repo, self, [])
1578 self._text = editor(self._repo, self, [])
1582 self._repo.savecommitmessage(self._text)
1579 self._repo.savecommitmessage(self._text)
1583
1580
1584 def __str__(self):
1581 def __str__(self):
1585 return str(self._parents[0]) + "+"
1582 return str(self._parents[0]) + "+"
1586
1583
1587 def __int__(self):
1584 def __int__(self):
1588 return self._rev
1585 return self._rev
1589
1586
1590 def __nonzero__(self):
1587 def __nonzero__(self):
1591 return True
1588 return True
1592
1589
1593 def __getitem__(self, key):
1590 def __getitem__(self, key):
1594 return self.filectx(key)
1591 return self.filectx(key)
1595
1592
1596 def p1(self):
1593 def p1(self):
1597 return self._parents[0]
1594 return self._parents[0]
1598 def p2(self):
1595 def p2(self):
1599 return self._parents[1]
1596 return self._parents[1]
1600
1597
1601 def user(self):
1598 def user(self):
1602 return self._user or self._repo.ui.username()
1599 return self._user or self._repo.ui.username()
1603 def date(self):
1600 def date(self):
1604 return self._date
1601 return self._date
1605 def description(self):
1602 def description(self):
1606 return self._text
1603 return self._text
1607 def files(self):
1604 def files(self):
1608 return self.modified()
1605 return self.modified()
1609 def modified(self):
1606 def modified(self):
1610 return self._status[0]
1607 return self._status[0]
1611 def added(self):
1608 def added(self):
1612 return self._status[1]
1609 return self._status[1]
1613 def removed(self):
1610 def removed(self):
1614 return self._status[2]
1611 return self._status[2]
1615 def deleted(self):
1612 def deleted(self):
1616 return self._status[3]
1613 return self._status[3]
1617 def unknown(self):
1614 def unknown(self):
1618 return self._status[4]
1615 return self._status[4]
1619 def ignored(self):
1616 def ignored(self):
1620 return self._status[5]
1617 return self._status[5]
1621 def clean(self):
1618 def clean(self):
1622 return self._status[6]
1619 return self._status[6]
1623 def branch(self):
1620 def branch(self):
1624 return encoding.tolocal(self._extra['branch'])
1621 return encoding.tolocal(self._extra['branch'])
1625 def extra(self):
1622 def extra(self):
1626 return self._extra
1623 return self._extra
1627 def flags(self, f):
1624 def flags(self, f):
1628 return self[f].flags()
1625 return self[f].flags()
1629
1626
1630 def parents(self):
1627 def parents(self):
1631 """return contexts for each parent changeset"""
1628 """return contexts for each parent changeset"""
1632 return self._parents
1629 return self._parents
1633
1630
1634 def filectx(self, path, filelog=None):
1631 def filectx(self, path, filelog=None):
1635 """get a file context from the working directory"""
1632 """get a file context from the working directory"""
1636 return self._filectxfn(self._repo, self, path)
1633 return self._filectxfn(self._repo, self, path)
1637
1634
1638 def commit(self):
1635 def commit(self):
1639 """commit context to the repo"""
1636 """commit context to the repo"""
1640 return self._repo.commitctx(self)
1637 return self._repo.commitctx(self)
1641
1638
1642 class memfilectx(object):
1639 class memfilectx(object):
1643 """memfilectx represents an in-memory file to commit.
1640 """memfilectx represents an in-memory file to commit.
1644
1641
1645 See memctx for more details.
1642 See memctx for more details.
1646 """
1643 """
1647 def __init__(self, path, data, islink=False, isexec=False, copied=None):
1644 def __init__(self, path, data, islink=False, isexec=False, copied=None):
1648 """
1645 """
1649 path is the normalized file path relative to repository root.
1646 path is the normalized file path relative to repository root.
1650 data is the file content as a string.
1647 data is the file content as a string.
1651 islink is True if the file is a symbolic link.
1648 islink is True if the file is a symbolic link.
1652 isexec is True if the file is executable.
1649 isexec is True if the file is executable.
1653 copied is the source file path if current file was copied in the
1650 copied is the source file path if current file was copied in the
1654 revision being committed, or None."""
1651 revision being committed, or None."""
1655 self._path = path
1652 self._path = path
1656 self._data = data
1653 self._data = data
1657 self._flags = (islink and 'l' or '') + (isexec and 'x' or '')
1654 self._flags = (islink and 'l' or '') + (isexec and 'x' or '')
1658 self._copied = None
1655 self._copied = None
1659 if copied:
1656 if copied:
1660 self._copied = (copied, nullid)
1657 self._copied = (copied, nullid)
1661
1658
1662 def __nonzero__(self):
1659 def __nonzero__(self):
1663 return True
1660 return True
1664 def __str__(self):
1661 def __str__(self):
1665 return "%s@%s" % (self.path(), self._changectx)
1662 return "%s@%s" % (self.path(), self._changectx)
1666 def path(self):
1663 def path(self):
1667 return self._path
1664 return self._path
1668 def data(self):
1665 def data(self):
1669 return self._data
1666 return self._data
1670 def flags(self):
1667 def flags(self):
1671 return self._flags
1668 return self._flags
1672 def isexec(self):
1669 def isexec(self):
1673 return 'x' in self._flags
1670 return 'x' in self._flags
1674 def islink(self):
1671 def islink(self):
1675 return 'l' in self._flags
1672 return 'l' in self._flags
1676 def renamed(self):
1673 def renamed(self):
1677 return self._copied
1674 return self._copied
General Comments 0
You need to be logged in to leave comments. Login now