##// END OF EJS Templates
memctx: call super.__init__ instead of duplicating code
Sean Farley -
r21666:31bdc51b 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
329
330 # we return a tuple to signify that this list isn't changing
330 # we return a tuple to signify that this list isn't changing
331 return tuple(r)
331 return tuple(r)
332
332
333
333
334 def makememctx(repo, parents, text, user, date, branch, files, store,
334 def makememctx(repo, parents, text, user, date, branch, files, store,
335 editor=None):
335 editor=None):
336 def getfilectx(repo, memctx, path):
336 def getfilectx(repo, memctx, path):
337 data, (islink, isexec), copied = store.getfile(path)
337 data, (islink, isexec), copied = store.getfile(path)
338 return memfilectx(path, data, islink=islink, isexec=isexec,
338 return memfilectx(path, data, islink=islink, isexec=isexec,
339 copied=copied)
339 copied=copied)
340 extra = {}
340 extra = {}
341 if branch:
341 if branch:
342 extra['branch'] = encoding.fromlocal(branch)
342 extra['branch'] = encoding.fromlocal(branch)
343 ctx = memctx(repo, parents, text, files, getfilectx, user,
343 ctx = memctx(repo, parents, text, files, getfilectx, user,
344 date, extra, editor)
344 date, extra, editor)
345 return ctx
345 return ctx
346
346
347 class changectx(basectx):
347 class changectx(basectx):
348 """A changecontext object makes access to data related to a particular
348 """A changecontext object makes access to data related to a particular
349 changeset convenient. It represents a read-only context already present in
349 changeset convenient. It represents a read-only context already present in
350 the repo."""
350 the repo."""
351 def __init__(self, repo, changeid=''):
351 def __init__(self, repo, changeid=''):
352 """changeid is a revision number, node, or tag"""
352 """changeid is a revision number, node, or tag"""
353
353
354 # since basectx.__new__ already took care of copying the object, we
354 # since basectx.__new__ already took care of copying the object, we
355 # don't need to do anything in __init__, so we just exit here
355 # don't need to do anything in __init__, so we just exit here
356 if isinstance(changeid, basectx):
356 if isinstance(changeid, basectx):
357 return
357 return
358
358
359 if changeid == '':
359 if changeid == '':
360 changeid = '.'
360 changeid = '.'
361 self._repo = repo
361 self._repo = repo
362
362
363 if isinstance(changeid, int):
363 if isinstance(changeid, int):
364 try:
364 try:
365 self._node = repo.changelog.node(changeid)
365 self._node = repo.changelog.node(changeid)
366 except IndexError:
366 except IndexError:
367 raise error.RepoLookupError(
367 raise error.RepoLookupError(
368 _("unknown revision '%s'") % changeid)
368 _("unknown revision '%s'") % changeid)
369 self._rev = changeid
369 self._rev = changeid
370 return
370 return
371 if isinstance(changeid, long):
371 if isinstance(changeid, long):
372 changeid = str(changeid)
372 changeid = str(changeid)
373 if changeid == '.':
373 if changeid == '.':
374 self._node = repo.dirstate.p1()
374 self._node = repo.dirstate.p1()
375 self._rev = repo.changelog.rev(self._node)
375 self._rev = repo.changelog.rev(self._node)
376 return
376 return
377 if changeid == 'null':
377 if changeid == 'null':
378 self._node = nullid
378 self._node = nullid
379 self._rev = nullrev
379 self._rev = nullrev
380 return
380 return
381 if changeid == 'tip':
381 if changeid == 'tip':
382 self._node = repo.changelog.tip()
382 self._node = repo.changelog.tip()
383 self._rev = repo.changelog.rev(self._node)
383 self._rev = repo.changelog.rev(self._node)
384 return
384 return
385 if len(changeid) == 20:
385 if len(changeid) == 20:
386 try:
386 try:
387 self._node = changeid
387 self._node = changeid
388 self._rev = repo.changelog.rev(changeid)
388 self._rev = repo.changelog.rev(changeid)
389 return
389 return
390 except LookupError:
390 except LookupError:
391 pass
391 pass
392
392
393 try:
393 try:
394 r = int(changeid)
394 r = int(changeid)
395 if str(r) != changeid:
395 if str(r) != changeid:
396 raise ValueError
396 raise ValueError
397 l = len(repo.changelog)
397 l = len(repo.changelog)
398 if r < 0:
398 if r < 0:
399 r += l
399 r += l
400 if r < 0 or r >= l:
400 if r < 0 or r >= l:
401 raise ValueError
401 raise ValueError
402 self._rev = r
402 self._rev = r
403 self._node = repo.changelog.node(r)
403 self._node = repo.changelog.node(r)
404 return
404 return
405 except (ValueError, OverflowError, IndexError):
405 except (ValueError, OverflowError, IndexError):
406 pass
406 pass
407
407
408 if len(changeid) == 40:
408 if len(changeid) == 40:
409 try:
409 try:
410 self._node = bin(changeid)
410 self._node = bin(changeid)
411 self._rev = repo.changelog.rev(self._node)
411 self._rev = repo.changelog.rev(self._node)
412 return
412 return
413 except (TypeError, LookupError):
413 except (TypeError, LookupError):
414 pass
414 pass
415
415
416 if changeid in repo._bookmarks:
416 if changeid in repo._bookmarks:
417 self._node = repo._bookmarks[changeid]
417 self._node = repo._bookmarks[changeid]
418 self._rev = repo.changelog.rev(self._node)
418 self._rev = repo.changelog.rev(self._node)
419 return
419 return
420 if changeid in repo._tagscache.tags:
420 if changeid in repo._tagscache.tags:
421 self._node = repo._tagscache.tags[changeid]
421 self._node = repo._tagscache.tags[changeid]
422 self._rev = repo.changelog.rev(self._node)
422 self._rev = repo.changelog.rev(self._node)
423 return
423 return
424 try:
424 try:
425 self._node = repo.branchtip(changeid)
425 self._node = repo.branchtip(changeid)
426 self._rev = repo.changelog.rev(self._node)
426 self._rev = repo.changelog.rev(self._node)
427 return
427 return
428 except error.RepoLookupError:
428 except error.RepoLookupError:
429 pass
429 pass
430
430
431 self._node = repo.changelog._partialmatch(changeid)
431 self._node = repo.changelog._partialmatch(changeid)
432 if self._node is not None:
432 if self._node is not None:
433 self._rev = repo.changelog.rev(self._node)
433 self._rev = repo.changelog.rev(self._node)
434 return
434 return
435
435
436 # lookup failed
436 # lookup failed
437 # check if it might have come from damaged dirstate
437 # check if it might have come from damaged dirstate
438 #
438 #
439 # XXX we could avoid the unfiltered if we had a recognizable exception
439 # XXX we could avoid the unfiltered if we had a recognizable exception
440 # for filtered changeset access
440 # for filtered changeset access
441 if changeid in repo.unfiltered().dirstate.parents():
441 if changeid in repo.unfiltered().dirstate.parents():
442 raise error.Abort(_("working directory has unknown parent '%s'!")
442 raise error.Abort(_("working directory has unknown parent '%s'!")
443 % short(changeid))
443 % short(changeid))
444 try:
444 try:
445 if len(changeid) == 20:
445 if len(changeid) == 20:
446 changeid = hex(changeid)
446 changeid = hex(changeid)
447 except TypeError:
447 except TypeError:
448 pass
448 pass
449 raise error.RepoLookupError(
449 raise error.RepoLookupError(
450 _("unknown revision '%s'") % changeid)
450 _("unknown revision '%s'") % changeid)
451
451
452 def __hash__(self):
452 def __hash__(self):
453 try:
453 try:
454 return hash(self._rev)
454 return hash(self._rev)
455 except AttributeError:
455 except AttributeError:
456 return id(self)
456 return id(self)
457
457
458 def __nonzero__(self):
458 def __nonzero__(self):
459 return self._rev != nullrev
459 return self._rev != nullrev
460
460
461 @propertycache
461 @propertycache
462 def _changeset(self):
462 def _changeset(self):
463 return self._repo.changelog.read(self.rev())
463 return self._repo.changelog.read(self.rev())
464
464
465 @propertycache
465 @propertycache
466 def _manifest(self):
466 def _manifest(self):
467 return self._repo.manifest.read(self._changeset[0])
467 return self._repo.manifest.read(self._changeset[0])
468
468
469 @propertycache
469 @propertycache
470 def _manifestdelta(self):
470 def _manifestdelta(self):
471 return self._repo.manifest.readdelta(self._changeset[0])
471 return self._repo.manifest.readdelta(self._changeset[0])
472
472
473 @propertycache
473 @propertycache
474 def _parents(self):
474 def _parents(self):
475 p = self._repo.changelog.parentrevs(self._rev)
475 p = self._repo.changelog.parentrevs(self._rev)
476 if p[1] == nullrev:
476 if p[1] == nullrev:
477 p = p[:-1]
477 p = p[:-1]
478 return [changectx(self._repo, x) for x in p]
478 return [changectx(self._repo, x) for x in p]
479
479
480 def changeset(self):
480 def changeset(self):
481 return self._changeset
481 return self._changeset
482 def manifestnode(self):
482 def manifestnode(self):
483 return self._changeset[0]
483 return self._changeset[0]
484
484
485 def user(self):
485 def user(self):
486 return self._changeset[1]
486 return self._changeset[1]
487 def date(self):
487 def date(self):
488 return self._changeset[2]
488 return self._changeset[2]
489 def files(self):
489 def files(self):
490 return self._changeset[3]
490 return self._changeset[3]
491 def description(self):
491 def description(self):
492 return self._changeset[4]
492 return self._changeset[4]
493 def branch(self):
493 def branch(self):
494 return encoding.tolocal(self._changeset[5].get("branch"))
494 return encoding.tolocal(self._changeset[5].get("branch"))
495 def closesbranch(self):
495 def closesbranch(self):
496 return 'close' in self._changeset[5]
496 return 'close' in self._changeset[5]
497 def extra(self):
497 def extra(self):
498 return self._changeset[5]
498 return self._changeset[5]
499 def tags(self):
499 def tags(self):
500 return self._repo.nodetags(self._node)
500 return self._repo.nodetags(self._node)
501 def bookmarks(self):
501 def bookmarks(self):
502 return self._repo.nodebookmarks(self._node)
502 return self._repo.nodebookmarks(self._node)
503 def phase(self):
503 def phase(self):
504 return self._repo._phasecache.phase(self._repo, self._rev)
504 return self._repo._phasecache.phase(self._repo, self._rev)
505 def hidden(self):
505 def hidden(self):
506 return self._rev in repoview.filterrevs(self._repo, 'visible')
506 return self._rev in repoview.filterrevs(self._repo, 'visible')
507
507
508 def children(self):
508 def children(self):
509 """return contexts for each child changeset"""
509 """return contexts for each child changeset"""
510 c = self._repo.changelog.children(self._node)
510 c = self._repo.changelog.children(self._node)
511 return [changectx(self._repo, x) for x in c]
511 return [changectx(self._repo, x) for x in c]
512
512
513 def ancestors(self):
513 def ancestors(self):
514 for a in self._repo.changelog.ancestors([self._rev]):
514 for a in self._repo.changelog.ancestors([self._rev]):
515 yield changectx(self._repo, a)
515 yield changectx(self._repo, a)
516
516
517 def descendants(self):
517 def descendants(self):
518 for d in self._repo.changelog.descendants([self._rev]):
518 for d in self._repo.changelog.descendants([self._rev]):
519 yield changectx(self._repo, d)
519 yield changectx(self._repo, d)
520
520
521 def filectx(self, path, fileid=None, filelog=None):
521 def filectx(self, path, fileid=None, filelog=None):
522 """get a file context from this changeset"""
522 """get a file context from this changeset"""
523 if fileid is None:
523 if fileid is None:
524 fileid = self.filenode(path)
524 fileid = self.filenode(path)
525 return filectx(self._repo, path, fileid=fileid,
525 return filectx(self._repo, path, fileid=fileid,
526 changectx=self, filelog=filelog)
526 changectx=self, filelog=filelog)
527
527
528 def ancestor(self, c2, warn=False):
528 def ancestor(self, c2, warn=False):
529 """
529 """
530 return the "best" ancestor context of self and c2
530 return the "best" ancestor context of self and c2
531 """
531 """
532 # deal with workingctxs
532 # deal with workingctxs
533 n2 = c2._node
533 n2 = c2._node
534 if n2 is None:
534 if n2 is None:
535 n2 = c2._parents[0]._node
535 n2 = c2._parents[0]._node
536 cahs = self._repo.changelog.commonancestorsheads(self._node, n2)
536 cahs = self._repo.changelog.commonancestorsheads(self._node, n2)
537 if not cahs:
537 if not cahs:
538 anc = nullid
538 anc = nullid
539 elif len(cahs) == 1:
539 elif len(cahs) == 1:
540 anc = cahs[0]
540 anc = cahs[0]
541 else:
541 else:
542 for r in self._repo.ui.configlist('merge', 'preferancestor'):
542 for r in self._repo.ui.configlist('merge', 'preferancestor'):
543 ctx = changectx(self._repo, r)
543 ctx = changectx(self._repo, r)
544 anc = ctx.node()
544 anc = ctx.node()
545 if anc in cahs:
545 if anc in cahs:
546 break
546 break
547 else:
547 else:
548 anc = self._repo.changelog.ancestor(self._node, n2)
548 anc = self._repo.changelog.ancestor(self._node, n2)
549 if warn:
549 if warn:
550 self._repo.ui.status(
550 self._repo.ui.status(
551 (_("note: using %s as ancestor of %s and %s\n") %
551 (_("note: using %s as ancestor of %s and %s\n") %
552 (short(anc), short(self._node), short(n2))) +
552 (short(anc), short(self._node), short(n2))) +
553 ''.join(_(" alternatively, use --config "
553 ''.join(_(" alternatively, use --config "
554 "merge.preferancestor=%s\n") %
554 "merge.preferancestor=%s\n") %
555 short(n) for n in sorted(cahs) if n != anc))
555 short(n) for n in sorted(cahs) if n != anc))
556 return changectx(self._repo, anc)
556 return changectx(self._repo, anc)
557
557
558 def descendant(self, other):
558 def descendant(self, other):
559 """True if other is descendant of this changeset"""
559 """True if other is descendant of this changeset"""
560 return self._repo.changelog.descendant(self._rev, other._rev)
560 return self._repo.changelog.descendant(self._rev, other._rev)
561
561
562 def walk(self, match):
562 def walk(self, match):
563 fset = set(match.files())
563 fset = set(match.files())
564 # for dirstate.walk, files=['.'] means "walk the whole tree".
564 # for dirstate.walk, files=['.'] means "walk the whole tree".
565 # follow that here, too
565 # follow that here, too
566 fset.discard('.')
566 fset.discard('.')
567
567
568 # avoid the entire walk if we're only looking for specific files
568 # avoid the entire walk if we're only looking for specific files
569 if fset and not match.anypats():
569 if fset and not match.anypats():
570 if util.all([fn in self for fn in fset]):
570 if util.all([fn in self for fn in fset]):
571 for fn in sorted(fset):
571 for fn in sorted(fset):
572 if match(fn):
572 if match(fn):
573 yield fn
573 yield fn
574 raise StopIteration
574 raise StopIteration
575
575
576 for fn in self:
576 for fn in self:
577 if fn in fset:
577 if fn in fset:
578 # specified pattern is the exact name
578 # specified pattern is the exact name
579 fset.remove(fn)
579 fset.remove(fn)
580 if match(fn):
580 if match(fn):
581 yield fn
581 yield fn
582 for fn in sorted(fset):
582 for fn in sorted(fset):
583 if fn in self._dirs:
583 if fn in self._dirs:
584 # specified pattern is a directory
584 # specified pattern is a directory
585 continue
585 continue
586 match.bad(fn, _('no such file in rev %s') % self)
586 match.bad(fn, _('no such file in rev %s') % self)
587
587
588 class basefilectx(object):
588 class basefilectx(object):
589 """A filecontext object represents the common logic for its children:
589 """A filecontext object represents the common logic for its children:
590 filectx: read-only access to a filerevision that is already present
590 filectx: read-only access to a filerevision that is already present
591 in the repo,
591 in the repo,
592 workingfilectx: a filecontext that represents files from the working
592 workingfilectx: a filecontext that represents files from the working
593 directory,
593 directory,
594 memfilectx: a filecontext that represents files in-memory."""
594 memfilectx: a filecontext that represents files in-memory."""
595 def __new__(cls, repo, path, *args, **kwargs):
595 def __new__(cls, repo, path, *args, **kwargs):
596 return super(basefilectx, cls).__new__(cls)
596 return super(basefilectx, cls).__new__(cls)
597
597
598 @propertycache
598 @propertycache
599 def _filelog(self):
599 def _filelog(self):
600 return self._repo.file(self._path)
600 return self._repo.file(self._path)
601
601
602 @propertycache
602 @propertycache
603 def _changeid(self):
603 def _changeid(self):
604 if '_changeid' in self.__dict__:
604 if '_changeid' in self.__dict__:
605 return self._changeid
605 return self._changeid
606 elif '_changectx' in self.__dict__:
606 elif '_changectx' in self.__dict__:
607 return self._changectx.rev()
607 return self._changectx.rev()
608 else:
608 else:
609 return self._filelog.linkrev(self._filerev)
609 return self._filelog.linkrev(self._filerev)
610
610
611 @propertycache
611 @propertycache
612 def _filenode(self):
612 def _filenode(self):
613 if '_fileid' in self.__dict__:
613 if '_fileid' in self.__dict__:
614 return self._filelog.lookup(self._fileid)
614 return self._filelog.lookup(self._fileid)
615 else:
615 else:
616 return self._changectx.filenode(self._path)
616 return self._changectx.filenode(self._path)
617
617
618 @propertycache
618 @propertycache
619 def _filerev(self):
619 def _filerev(self):
620 return self._filelog.rev(self._filenode)
620 return self._filelog.rev(self._filenode)
621
621
622 @propertycache
622 @propertycache
623 def _repopath(self):
623 def _repopath(self):
624 return self._path
624 return self._path
625
625
626 def __nonzero__(self):
626 def __nonzero__(self):
627 try:
627 try:
628 self._filenode
628 self._filenode
629 return True
629 return True
630 except error.LookupError:
630 except error.LookupError:
631 # file is missing
631 # file is missing
632 return False
632 return False
633
633
634 def __str__(self):
634 def __str__(self):
635 return "%s@%s" % (self.path(), self._changectx)
635 return "%s@%s" % (self.path(), self._changectx)
636
636
637 def __repr__(self):
637 def __repr__(self):
638 return "<%s %s>" % (type(self).__name__, str(self))
638 return "<%s %s>" % (type(self).__name__, str(self))
639
639
640 def __hash__(self):
640 def __hash__(self):
641 try:
641 try:
642 return hash((self._path, self._filenode))
642 return hash((self._path, self._filenode))
643 except AttributeError:
643 except AttributeError:
644 return id(self)
644 return id(self)
645
645
646 def __eq__(self, other):
646 def __eq__(self, other):
647 try:
647 try:
648 return (type(self) == type(other) and self._path == other._path
648 return (type(self) == type(other) and self._path == other._path
649 and self._filenode == other._filenode)
649 and self._filenode == other._filenode)
650 except AttributeError:
650 except AttributeError:
651 return False
651 return False
652
652
653 def __ne__(self, other):
653 def __ne__(self, other):
654 return not (self == other)
654 return not (self == other)
655
655
656 def filerev(self):
656 def filerev(self):
657 return self._filerev
657 return self._filerev
658 def filenode(self):
658 def filenode(self):
659 return self._filenode
659 return self._filenode
660 def flags(self):
660 def flags(self):
661 return self._changectx.flags(self._path)
661 return self._changectx.flags(self._path)
662 def filelog(self):
662 def filelog(self):
663 return self._filelog
663 return self._filelog
664 def rev(self):
664 def rev(self):
665 return self._changeid
665 return self._changeid
666 def linkrev(self):
666 def linkrev(self):
667 return self._filelog.linkrev(self._filerev)
667 return self._filelog.linkrev(self._filerev)
668 def node(self):
668 def node(self):
669 return self._changectx.node()
669 return self._changectx.node()
670 def hex(self):
670 def hex(self):
671 return self._changectx.hex()
671 return self._changectx.hex()
672 def user(self):
672 def user(self):
673 return self._changectx.user()
673 return self._changectx.user()
674 def date(self):
674 def date(self):
675 return self._changectx.date()
675 return self._changectx.date()
676 def files(self):
676 def files(self):
677 return self._changectx.files()
677 return self._changectx.files()
678 def description(self):
678 def description(self):
679 return self._changectx.description()
679 return self._changectx.description()
680 def branch(self):
680 def branch(self):
681 return self._changectx.branch()
681 return self._changectx.branch()
682 def extra(self):
682 def extra(self):
683 return self._changectx.extra()
683 return self._changectx.extra()
684 def phase(self):
684 def phase(self):
685 return self._changectx.phase()
685 return self._changectx.phase()
686 def phasestr(self):
686 def phasestr(self):
687 return self._changectx.phasestr()
687 return self._changectx.phasestr()
688 def manifest(self):
688 def manifest(self):
689 return self._changectx.manifest()
689 return self._changectx.manifest()
690 def changectx(self):
690 def changectx(self):
691 return self._changectx
691 return self._changectx
692
692
693 def path(self):
693 def path(self):
694 return self._path
694 return self._path
695
695
696 def isbinary(self):
696 def isbinary(self):
697 try:
697 try:
698 return util.binary(self.data())
698 return util.binary(self.data())
699 except IOError:
699 except IOError:
700 return False
700 return False
701
701
702 def cmp(self, fctx):
702 def cmp(self, fctx):
703 """compare with other file context
703 """compare with other file context
704
704
705 returns True if different than fctx.
705 returns True if different than fctx.
706 """
706 """
707 if (fctx._filerev is None
707 if (fctx._filerev is None
708 and (self._repo._encodefilterpats
708 and (self._repo._encodefilterpats
709 # if file data starts with '\1\n', empty metadata block is
709 # if file data starts with '\1\n', empty metadata block is
710 # prepended, which adds 4 bytes to filelog.size().
710 # prepended, which adds 4 bytes to filelog.size().
711 or self.size() - 4 == fctx.size())
711 or self.size() - 4 == fctx.size())
712 or self.size() == fctx.size()):
712 or self.size() == fctx.size()):
713 return self._filelog.cmp(self._filenode, fctx.data())
713 return self._filelog.cmp(self._filenode, fctx.data())
714
714
715 return True
715 return True
716
716
717 def parents(self):
717 def parents(self):
718 p = self._path
718 p = self._path
719 fl = self._filelog
719 fl = self._filelog
720 pl = [(p, n, fl) for n in self._filelog.parents(self._filenode)]
720 pl = [(p, n, fl) for n in self._filelog.parents(self._filenode)]
721
721
722 r = self._filelog.renamed(self._filenode)
722 r = self._filelog.renamed(self._filenode)
723 if r:
723 if r:
724 pl[0] = (r[0], r[1], None)
724 pl[0] = (r[0], r[1], None)
725
725
726 return [filectx(self._repo, p, fileid=n, filelog=l)
726 return [filectx(self._repo, p, fileid=n, filelog=l)
727 for p, n, l in pl if n != nullid]
727 for p, n, l in pl if n != nullid]
728
728
729 def p1(self):
729 def p1(self):
730 return self.parents()[0]
730 return self.parents()[0]
731
731
732 def p2(self):
732 def p2(self):
733 p = self.parents()
733 p = self.parents()
734 if len(p) == 2:
734 if len(p) == 2:
735 return p[1]
735 return p[1]
736 return filectx(self._repo, self._path, fileid=-1, filelog=self._filelog)
736 return filectx(self._repo, self._path, fileid=-1, filelog=self._filelog)
737
737
738 def annotate(self, follow=False, linenumber=None, diffopts=None):
738 def annotate(self, follow=False, linenumber=None, diffopts=None):
739 '''returns a list of tuples of (ctx, line) for each line
739 '''returns a list of tuples of (ctx, line) for each line
740 in the file, where ctx is the filectx of the node where
740 in the file, where ctx is the filectx of the node where
741 that line was last changed.
741 that line was last changed.
742 This returns tuples of ((ctx, linenumber), line) for each line,
742 This returns tuples of ((ctx, linenumber), line) for each line,
743 if "linenumber" parameter is NOT "None".
743 if "linenumber" parameter is NOT "None".
744 In such tuples, linenumber means one at the first appearance
744 In such tuples, linenumber means one at the first appearance
745 in the managed file.
745 in the managed file.
746 To reduce annotation cost,
746 To reduce annotation cost,
747 this returns fixed value(False is used) as linenumber,
747 this returns fixed value(False is used) as linenumber,
748 if "linenumber" parameter is "False".'''
748 if "linenumber" parameter is "False".'''
749
749
750 def decorate_compat(text, rev):
750 def decorate_compat(text, rev):
751 return ([rev] * len(text.splitlines()), text)
751 return ([rev] * len(text.splitlines()), text)
752
752
753 def without_linenumber(text, rev):
753 def without_linenumber(text, rev):
754 return ([(rev, False)] * len(text.splitlines()), text)
754 return ([(rev, False)] * len(text.splitlines()), text)
755
755
756 def with_linenumber(text, rev):
756 def with_linenumber(text, rev):
757 size = len(text.splitlines())
757 size = len(text.splitlines())
758 return ([(rev, i) for i in xrange(1, size + 1)], text)
758 return ([(rev, i) for i in xrange(1, size + 1)], text)
759
759
760 decorate = (((linenumber is None) and decorate_compat) or
760 decorate = (((linenumber is None) and decorate_compat) or
761 (linenumber and with_linenumber) or
761 (linenumber and with_linenumber) or
762 without_linenumber)
762 without_linenumber)
763
763
764 def pair(parent, child):
764 def pair(parent, child):
765 blocks = mdiff.allblocks(parent[1], child[1], opts=diffopts,
765 blocks = mdiff.allblocks(parent[1], child[1], opts=diffopts,
766 refine=True)
766 refine=True)
767 for (a1, a2, b1, b2), t in blocks:
767 for (a1, a2, b1, b2), t in blocks:
768 # Changed blocks ('!') or blocks made only of blank lines ('~')
768 # Changed blocks ('!') or blocks made only of blank lines ('~')
769 # belong to the child.
769 # belong to the child.
770 if t == '=':
770 if t == '=':
771 child[0][b1:b2] = parent[0][a1:a2]
771 child[0][b1:b2] = parent[0][a1:a2]
772 return child
772 return child
773
773
774 getlog = util.lrucachefunc(lambda x: self._repo.file(x))
774 getlog = util.lrucachefunc(lambda x: self._repo.file(x))
775
775
776 def parents(f):
776 def parents(f):
777 pl = f.parents()
777 pl = f.parents()
778
778
779 # Don't return renamed parents if we aren't following.
779 # Don't return renamed parents if we aren't following.
780 if not follow:
780 if not follow:
781 pl = [p for p in pl if p.path() == f.path()]
781 pl = [p for p in pl if p.path() == f.path()]
782
782
783 # renamed filectx won't have a filelog yet, so set it
783 # renamed filectx won't have a filelog yet, so set it
784 # from the cache to save time
784 # from the cache to save time
785 for p in pl:
785 for p in pl:
786 if not '_filelog' in p.__dict__:
786 if not '_filelog' in p.__dict__:
787 p._filelog = getlog(p.path())
787 p._filelog = getlog(p.path())
788
788
789 return pl
789 return pl
790
790
791 # use linkrev to find the first changeset where self appeared
791 # use linkrev to find the first changeset where self appeared
792 if self.rev() != self.linkrev():
792 if self.rev() != self.linkrev():
793 base = self.filectx(self.filenode())
793 base = self.filectx(self.filenode())
794 else:
794 else:
795 base = self
795 base = self
796
796
797 # This algorithm would prefer to be recursive, but Python is a
797 # This algorithm would prefer to be recursive, but Python is a
798 # bit recursion-hostile. Instead we do an iterative
798 # bit recursion-hostile. Instead we do an iterative
799 # depth-first search.
799 # depth-first search.
800
800
801 visit = [base]
801 visit = [base]
802 hist = {}
802 hist = {}
803 pcache = {}
803 pcache = {}
804 needed = {base: 1}
804 needed = {base: 1}
805 while visit:
805 while visit:
806 f = visit[-1]
806 f = visit[-1]
807 pcached = f in pcache
807 pcached = f in pcache
808 if not pcached:
808 if not pcached:
809 pcache[f] = parents(f)
809 pcache[f] = parents(f)
810
810
811 ready = True
811 ready = True
812 pl = pcache[f]
812 pl = pcache[f]
813 for p in pl:
813 for p in pl:
814 if p not in hist:
814 if p not in hist:
815 ready = False
815 ready = False
816 visit.append(p)
816 visit.append(p)
817 if not pcached:
817 if not pcached:
818 needed[p] = needed.get(p, 0) + 1
818 needed[p] = needed.get(p, 0) + 1
819 if ready:
819 if ready:
820 visit.pop()
820 visit.pop()
821 reusable = f in hist
821 reusable = f in hist
822 if reusable:
822 if reusable:
823 curr = hist[f]
823 curr = hist[f]
824 else:
824 else:
825 curr = decorate(f.data(), f)
825 curr = decorate(f.data(), f)
826 for p in pl:
826 for p in pl:
827 if not reusable:
827 if not reusable:
828 curr = pair(hist[p], curr)
828 curr = pair(hist[p], curr)
829 if needed[p] == 1:
829 if needed[p] == 1:
830 del hist[p]
830 del hist[p]
831 del needed[p]
831 del needed[p]
832 else:
832 else:
833 needed[p] -= 1
833 needed[p] -= 1
834
834
835 hist[f] = curr
835 hist[f] = curr
836 pcache[f] = []
836 pcache[f] = []
837
837
838 return zip(hist[base][0], hist[base][1].splitlines(True))
838 return zip(hist[base][0], hist[base][1].splitlines(True))
839
839
840 def ancestors(self, followfirst=False):
840 def ancestors(self, followfirst=False):
841 visit = {}
841 visit = {}
842 c = self
842 c = self
843 cut = followfirst and 1 or None
843 cut = followfirst and 1 or None
844 while True:
844 while True:
845 for parent in c.parents()[:cut]:
845 for parent in c.parents()[:cut]:
846 visit[(parent.rev(), parent.node())] = parent
846 visit[(parent.rev(), parent.node())] = parent
847 if not visit:
847 if not visit:
848 break
848 break
849 c = visit.pop(max(visit))
849 c = visit.pop(max(visit))
850 yield c
850 yield c
851
851
852 class filectx(basefilectx):
852 class filectx(basefilectx):
853 """A filecontext object makes access to data related to a particular
853 """A filecontext object makes access to data related to a particular
854 filerevision convenient."""
854 filerevision convenient."""
855 def __init__(self, repo, path, changeid=None, fileid=None,
855 def __init__(self, repo, path, changeid=None, fileid=None,
856 filelog=None, changectx=None):
856 filelog=None, changectx=None):
857 """changeid can be a changeset revision, node, or tag.
857 """changeid can be a changeset revision, node, or tag.
858 fileid can be a file revision or node."""
858 fileid can be a file revision or node."""
859 self._repo = repo
859 self._repo = repo
860 self._path = path
860 self._path = path
861
861
862 assert (changeid is not None
862 assert (changeid is not None
863 or fileid is not None
863 or fileid is not None
864 or changectx is not None), \
864 or changectx is not None), \
865 ("bad args: changeid=%r, fileid=%r, changectx=%r"
865 ("bad args: changeid=%r, fileid=%r, changectx=%r"
866 % (changeid, fileid, changectx))
866 % (changeid, fileid, changectx))
867
867
868 if filelog is not None:
868 if filelog is not None:
869 self._filelog = filelog
869 self._filelog = filelog
870
870
871 if changeid is not None:
871 if changeid is not None:
872 self._changeid = changeid
872 self._changeid = changeid
873 if changectx is not None:
873 if changectx is not None:
874 self._changectx = changectx
874 self._changectx = changectx
875 if fileid is not None:
875 if fileid is not None:
876 self._fileid = fileid
876 self._fileid = fileid
877
877
878 @propertycache
878 @propertycache
879 def _changectx(self):
879 def _changectx(self):
880 try:
880 try:
881 return changectx(self._repo, self._changeid)
881 return changectx(self._repo, self._changeid)
882 except error.RepoLookupError:
882 except error.RepoLookupError:
883 # Linkrev may point to any revision in the repository. When the
883 # Linkrev may point to any revision in the repository. When the
884 # repository is filtered this may lead to `filectx` trying to build
884 # repository is filtered this may lead to `filectx` trying to build
885 # `changectx` for filtered revision. In such case we fallback to
885 # `changectx` for filtered revision. In such case we fallback to
886 # creating `changectx` on the unfiltered version of the reposition.
886 # creating `changectx` on the unfiltered version of the reposition.
887 # This fallback should not be an issue because `changectx` from
887 # This fallback should not be an issue because `changectx` from
888 # `filectx` are not used in complex operations that care about
888 # `filectx` are not used in complex operations that care about
889 # filtering.
889 # filtering.
890 #
890 #
891 # This fallback is a cheap and dirty fix that prevent several
891 # This fallback is a cheap and dirty fix that prevent several
892 # crashes. It does not ensure the behavior is correct. However the
892 # crashes. It does not ensure the behavior is correct. However the
893 # behavior was not correct before filtering either and "incorrect
893 # behavior was not correct before filtering either and "incorrect
894 # behavior" is seen as better as "crash"
894 # behavior" is seen as better as "crash"
895 #
895 #
896 # Linkrevs have several serious troubles with filtering that are
896 # Linkrevs have several serious troubles with filtering that are
897 # complicated to solve. Proper handling of the issue here should be
897 # complicated to solve. Proper handling of the issue here should be
898 # considered when solving linkrev issue are on the table.
898 # considered when solving linkrev issue are on the table.
899 return changectx(self._repo.unfiltered(), self._changeid)
899 return changectx(self._repo.unfiltered(), self._changeid)
900
900
901 def filectx(self, fileid):
901 def filectx(self, fileid):
902 '''opens an arbitrary revision of the file without
902 '''opens an arbitrary revision of the file without
903 opening a new filelog'''
903 opening a new filelog'''
904 return filectx(self._repo, self._path, fileid=fileid,
904 return filectx(self._repo, self._path, fileid=fileid,
905 filelog=self._filelog)
905 filelog=self._filelog)
906
906
907 def data(self):
907 def data(self):
908 return self._filelog.read(self._filenode)
908 return self._filelog.read(self._filenode)
909 def size(self):
909 def size(self):
910 return self._filelog.size(self._filerev)
910 return self._filelog.size(self._filerev)
911
911
912 def renamed(self):
912 def renamed(self):
913 """check if file was actually renamed in this changeset revision
913 """check if file was actually renamed in this changeset revision
914
914
915 If rename logged in file revision, we report copy for changeset only
915 If rename logged in file revision, we report copy for changeset only
916 if file revisions linkrev points back to the changeset in question
916 if file revisions linkrev points back to the changeset in question
917 or both changeset parents contain different file revisions.
917 or both changeset parents contain different file revisions.
918 """
918 """
919
919
920 renamed = self._filelog.renamed(self._filenode)
920 renamed = self._filelog.renamed(self._filenode)
921 if not renamed:
921 if not renamed:
922 return renamed
922 return renamed
923
923
924 if self.rev() == self.linkrev():
924 if self.rev() == self.linkrev():
925 return renamed
925 return renamed
926
926
927 name = self.path()
927 name = self.path()
928 fnode = self._filenode
928 fnode = self._filenode
929 for p in self._changectx.parents():
929 for p in self._changectx.parents():
930 try:
930 try:
931 if fnode == p.filenode(name):
931 if fnode == p.filenode(name):
932 return None
932 return None
933 except error.LookupError:
933 except error.LookupError:
934 pass
934 pass
935 return renamed
935 return renamed
936
936
937 def children(self):
937 def children(self):
938 # hard for renames
938 # hard for renames
939 c = self._filelog.children(self._filenode)
939 c = self._filelog.children(self._filenode)
940 return [filectx(self._repo, self._path, fileid=x,
940 return [filectx(self._repo, self._path, fileid=x,
941 filelog=self._filelog) for x in c]
941 filelog=self._filelog) for x in c]
942
942
943 class committablectx(basectx):
943 class committablectx(basectx):
944 """A committablectx object provides common functionality for a context that
944 """A committablectx object provides common functionality for a context that
945 wants the ability to commit, e.g. workingctx or memctx."""
945 wants the ability to commit, e.g. workingctx or memctx."""
946 def __init__(self, repo, text="", user=None, date=None, extra=None,
946 def __init__(self, repo, text="", user=None, date=None, extra=None,
947 changes=None):
947 changes=None):
948 self._repo = repo
948 self._repo = repo
949 self._rev = None
949 self._rev = None
950 self._node = None
950 self._node = None
951 self._text = text
951 self._text = text
952 if date:
952 if date:
953 self._date = util.parsedate(date)
953 self._date = util.parsedate(date)
954 if user:
954 if user:
955 self._user = user
955 self._user = user
956 if changes:
956 if changes:
957 self._status = changes
957 self._status = changes
958
958
959 self._extra = {}
959 self._extra = {}
960 if extra:
960 if extra:
961 self._extra = extra.copy()
961 self._extra = extra.copy()
962 if 'branch' not in self._extra:
962 if 'branch' not in self._extra:
963 try:
963 try:
964 branch = encoding.fromlocal(self._repo.dirstate.branch())
964 branch = encoding.fromlocal(self._repo.dirstate.branch())
965 except UnicodeDecodeError:
965 except UnicodeDecodeError:
966 raise util.Abort(_('branch name not in UTF-8!'))
966 raise util.Abort(_('branch name not in UTF-8!'))
967 self._extra['branch'] = branch
967 self._extra['branch'] = branch
968 if self._extra['branch'] == '':
968 if self._extra['branch'] == '':
969 self._extra['branch'] = 'default'
969 self._extra['branch'] = 'default'
970
970
971 def __str__(self):
971 def __str__(self):
972 return str(self._parents[0]) + "+"
972 return str(self._parents[0]) + "+"
973
973
974 def __nonzero__(self):
974 def __nonzero__(self):
975 return True
975 return True
976
976
977 def __contains__(self, key):
977 def __contains__(self, key):
978 return self._repo.dirstate[key] not in "?r"
978 return self._repo.dirstate[key] not in "?r"
979
979
980 def _buildflagfunc(self):
980 def _buildflagfunc(self):
981 # Create a fallback function for getting file flags when the
981 # Create a fallback function for getting file flags when the
982 # filesystem doesn't support them
982 # filesystem doesn't support them
983
983
984 copiesget = self._repo.dirstate.copies().get
984 copiesget = self._repo.dirstate.copies().get
985
985
986 if len(self._parents) < 2:
986 if len(self._parents) < 2:
987 # when we have one parent, it's easy: copy from parent
987 # when we have one parent, it's easy: copy from parent
988 man = self._parents[0].manifest()
988 man = self._parents[0].manifest()
989 def func(f):
989 def func(f):
990 f = copiesget(f, f)
990 f = copiesget(f, f)
991 return man.flags(f)
991 return man.flags(f)
992 else:
992 else:
993 # merges are tricky: we try to reconstruct the unstored
993 # merges are tricky: we try to reconstruct the unstored
994 # result from the merge (issue1802)
994 # result from the merge (issue1802)
995 p1, p2 = self._parents
995 p1, p2 = self._parents
996 pa = p1.ancestor(p2)
996 pa = p1.ancestor(p2)
997 m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest()
997 m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest()
998
998
999 def func(f):
999 def func(f):
1000 f = copiesget(f, f) # may be wrong for merges with copies
1000 f = copiesget(f, f) # may be wrong for merges with copies
1001 fl1, fl2, fla = m1.flags(f), m2.flags(f), ma.flags(f)
1001 fl1, fl2, fla = m1.flags(f), m2.flags(f), ma.flags(f)
1002 if fl1 == fl2:
1002 if fl1 == fl2:
1003 return fl1
1003 return fl1
1004 if fl1 == fla:
1004 if fl1 == fla:
1005 return fl2
1005 return fl2
1006 if fl2 == fla:
1006 if fl2 == fla:
1007 return fl1
1007 return fl1
1008 return '' # punt for conflicts
1008 return '' # punt for conflicts
1009
1009
1010 return func
1010 return func
1011
1011
1012 @propertycache
1012 @propertycache
1013 def _flagfunc(self):
1013 def _flagfunc(self):
1014 return self._repo.dirstate.flagfunc(self._buildflagfunc)
1014 return self._repo.dirstate.flagfunc(self._buildflagfunc)
1015
1015
1016 @propertycache
1016 @propertycache
1017 def _manifest(self):
1017 def _manifest(self):
1018 """generate a manifest corresponding to the values in self._status"""
1018 """generate a manifest corresponding to the values in self._status"""
1019
1019
1020 man = self._parents[0].manifest().copy()
1020 man = self._parents[0].manifest().copy()
1021 if len(self._parents) > 1:
1021 if len(self._parents) > 1:
1022 man2 = self.p2().manifest()
1022 man2 = self.p2().manifest()
1023 def getman(f):
1023 def getman(f):
1024 if f in man:
1024 if f in man:
1025 return man
1025 return man
1026 return man2
1026 return man2
1027 else:
1027 else:
1028 getman = lambda f: man
1028 getman = lambda f: man
1029
1029
1030 copied = self._repo.dirstate.copies()
1030 copied = self._repo.dirstate.copies()
1031 ff = self._flagfunc
1031 ff = self._flagfunc
1032 modified, added, removed, deleted = self._status[:4]
1032 modified, added, removed, deleted = self._status[:4]
1033 for i, l in (("a", added), ("m", modified)):
1033 for i, l in (("a", added), ("m", modified)):
1034 for f in l:
1034 for f in l:
1035 orig = copied.get(f, f)
1035 orig = copied.get(f, f)
1036 man[f] = getman(orig).get(orig, nullid) + i
1036 man[f] = getman(orig).get(orig, nullid) + i
1037 try:
1037 try:
1038 man.set(f, ff(f))
1038 man.set(f, ff(f))
1039 except OSError:
1039 except OSError:
1040 pass
1040 pass
1041
1041
1042 for f in deleted + removed:
1042 for f in deleted + removed:
1043 if f in man:
1043 if f in man:
1044 del man[f]
1044 del man[f]
1045
1045
1046 return man
1046 return man
1047
1047
1048 @propertycache
1048 @propertycache
1049 def _status(self):
1049 def _status(self):
1050 return self._repo.status()
1050 return self._repo.status()
1051
1051
1052 @propertycache
1052 @propertycache
1053 def _user(self):
1053 def _user(self):
1054 return self._repo.ui.username()
1054 return self._repo.ui.username()
1055
1055
1056 @propertycache
1056 @propertycache
1057 def _date(self):
1057 def _date(self):
1058 return util.makedate()
1058 return util.makedate()
1059
1059
1060 def subrev(self, subpath):
1060 def subrev(self, subpath):
1061 return None
1061 return None
1062
1062
1063 def user(self):
1063 def user(self):
1064 return self._user or self._repo.ui.username()
1064 return self._user or self._repo.ui.username()
1065 def date(self):
1065 def date(self):
1066 return self._date
1066 return self._date
1067 def description(self):
1067 def description(self):
1068 return self._text
1068 return self._text
1069 def files(self):
1069 def files(self):
1070 return sorted(self._status[0] + self._status[1] + self._status[2])
1070 return sorted(self._status[0] + self._status[1] + self._status[2])
1071
1071
1072 def modified(self):
1072 def modified(self):
1073 return self._status[0]
1073 return self._status[0]
1074 def added(self):
1074 def added(self):
1075 return self._status[1]
1075 return self._status[1]
1076 def removed(self):
1076 def removed(self):
1077 return self._status[2]
1077 return self._status[2]
1078 def deleted(self):
1078 def deleted(self):
1079 return self._status[3]
1079 return self._status[3]
1080 def unknown(self):
1080 def unknown(self):
1081 return self._status[4]
1081 return self._status[4]
1082 def ignored(self):
1082 def ignored(self):
1083 return self._status[5]
1083 return self._status[5]
1084 def clean(self):
1084 def clean(self):
1085 return self._status[6]
1085 return self._status[6]
1086 def branch(self):
1086 def branch(self):
1087 return encoding.tolocal(self._extra['branch'])
1087 return encoding.tolocal(self._extra['branch'])
1088 def closesbranch(self):
1088 def closesbranch(self):
1089 return 'close' in self._extra
1089 return 'close' in self._extra
1090 def extra(self):
1090 def extra(self):
1091 return self._extra
1091 return self._extra
1092
1092
1093 def tags(self):
1093 def tags(self):
1094 t = []
1094 t = []
1095 for p in self.parents():
1095 for p in self.parents():
1096 t.extend(p.tags())
1096 t.extend(p.tags())
1097 return t
1097 return t
1098
1098
1099 def bookmarks(self):
1099 def bookmarks(self):
1100 b = []
1100 b = []
1101 for p in self.parents():
1101 for p in self.parents():
1102 b.extend(p.bookmarks())
1102 b.extend(p.bookmarks())
1103 return b
1103 return b
1104
1104
1105 def phase(self):
1105 def phase(self):
1106 phase = phases.draft # default phase to draft
1106 phase = phases.draft # default phase to draft
1107 for p in self.parents():
1107 for p in self.parents():
1108 phase = max(phase, p.phase())
1108 phase = max(phase, p.phase())
1109 return phase
1109 return phase
1110
1110
1111 def hidden(self):
1111 def hidden(self):
1112 return False
1112 return False
1113
1113
1114 def children(self):
1114 def children(self):
1115 return []
1115 return []
1116
1116
1117 def flags(self, path):
1117 def flags(self, path):
1118 if '_manifest' in self.__dict__:
1118 if '_manifest' in self.__dict__:
1119 try:
1119 try:
1120 return self._manifest.flags(path)
1120 return self._manifest.flags(path)
1121 except KeyError:
1121 except KeyError:
1122 return ''
1122 return ''
1123
1123
1124 try:
1124 try:
1125 return self._flagfunc(path)
1125 return self._flagfunc(path)
1126 except OSError:
1126 except OSError:
1127 return ''
1127 return ''
1128
1128
1129 def ancestor(self, c2):
1129 def ancestor(self, c2):
1130 """return the ancestor context of self and c2"""
1130 """return the ancestor context of self and c2"""
1131 return self._parents[0].ancestor(c2) # punt on two parents for now
1131 return self._parents[0].ancestor(c2) # punt on two parents for now
1132
1132
1133 def walk(self, match):
1133 def walk(self, match):
1134 return sorted(self._repo.dirstate.walk(match, sorted(self.substate),
1134 return sorted(self._repo.dirstate.walk(match, sorted(self.substate),
1135 True, False))
1135 True, False))
1136
1136
1137 def ancestors(self):
1137 def ancestors(self):
1138 for a in self._repo.changelog.ancestors(
1138 for a in self._repo.changelog.ancestors(
1139 [p.rev() for p in self._parents]):
1139 [p.rev() for p in self._parents]):
1140 yield changectx(self._repo, a)
1140 yield changectx(self._repo, a)
1141
1141
1142 def markcommitted(self, node):
1142 def markcommitted(self, node):
1143 """Perform post-commit cleanup necessary after committing this ctx
1143 """Perform post-commit cleanup necessary after committing this ctx
1144
1144
1145 Specifically, this updates backing stores this working context
1145 Specifically, this updates backing stores this working context
1146 wraps to reflect the fact that the changes reflected by this
1146 wraps to reflect the fact that the changes reflected by this
1147 workingctx have been committed. For example, it marks
1147 workingctx have been committed. For example, it marks
1148 modified and added files as normal in the dirstate.
1148 modified and added files as normal in the dirstate.
1149
1149
1150 """
1150 """
1151
1151
1152 for f in self.modified() + self.added():
1152 for f in self.modified() + self.added():
1153 self._repo.dirstate.normal(f)
1153 self._repo.dirstate.normal(f)
1154 for f in self.removed():
1154 for f in self.removed():
1155 self._repo.dirstate.drop(f)
1155 self._repo.dirstate.drop(f)
1156 self._repo.dirstate.setparents(node)
1156 self._repo.dirstate.setparents(node)
1157
1157
1158 def dirs(self):
1158 def dirs(self):
1159 return self._repo.dirstate.dirs()
1159 return self._repo.dirstate.dirs()
1160
1160
1161 class workingctx(committablectx):
1161 class workingctx(committablectx):
1162 """A workingctx object makes access to data related to
1162 """A workingctx object makes access to data related to
1163 the current working directory convenient.
1163 the current working directory convenient.
1164 date - any valid date string or (unixtime, offset), or None.
1164 date - any valid date string or (unixtime, offset), or None.
1165 user - username string, or None.
1165 user - username string, or None.
1166 extra - a dictionary of extra values, or None.
1166 extra - a dictionary of extra values, or None.
1167 changes - a list of file lists as returned by localrepo.status()
1167 changes - a list of file lists as returned by localrepo.status()
1168 or None to use the repository status.
1168 or None to use the repository status.
1169 """
1169 """
1170 def __init__(self, repo, text="", user=None, date=None, extra=None,
1170 def __init__(self, repo, text="", user=None, date=None, extra=None,
1171 changes=None):
1171 changes=None):
1172 super(workingctx, self).__init__(repo, text, user, date, extra, changes)
1172 super(workingctx, self).__init__(repo, text, user, date, extra, changes)
1173
1173
1174 def __iter__(self):
1174 def __iter__(self):
1175 d = self._repo.dirstate
1175 d = self._repo.dirstate
1176 for f in d:
1176 for f in d:
1177 if d[f] != 'r':
1177 if d[f] != 'r':
1178 yield f
1178 yield f
1179
1179
1180 @propertycache
1180 @propertycache
1181 def _parents(self):
1181 def _parents(self):
1182 p = self._repo.dirstate.parents()
1182 p = self._repo.dirstate.parents()
1183 if p[1] == nullid:
1183 if p[1] == nullid:
1184 p = p[:-1]
1184 p = p[:-1]
1185 return [changectx(self._repo, x) for x in p]
1185 return [changectx(self._repo, x) for x in p]
1186
1186
1187 def filectx(self, path, filelog=None):
1187 def filectx(self, path, filelog=None):
1188 """get a file context from the working directory"""
1188 """get a file context from the working directory"""
1189 return workingfilectx(self._repo, path, workingctx=self,
1189 return workingfilectx(self._repo, path, workingctx=self,
1190 filelog=filelog)
1190 filelog=filelog)
1191
1191
1192 def dirty(self, missing=False, merge=True, branch=True):
1192 def dirty(self, missing=False, merge=True, branch=True):
1193 "check whether a working directory is modified"
1193 "check whether a working directory is modified"
1194 # check subrepos first
1194 # check subrepos first
1195 for s in sorted(self.substate):
1195 for s in sorted(self.substate):
1196 if self.sub(s).dirty():
1196 if self.sub(s).dirty():
1197 return True
1197 return True
1198 # check current working dir
1198 # check current working dir
1199 return ((merge and self.p2()) or
1199 return ((merge and self.p2()) or
1200 (branch and self.branch() != self.p1().branch()) or
1200 (branch and self.branch() != self.p1().branch()) or
1201 self.modified() or self.added() or self.removed() or
1201 self.modified() or self.added() or self.removed() or
1202 (missing and self.deleted()))
1202 (missing and self.deleted()))
1203
1203
1204 def add(self, list, prefix=""):
1204 def add(self, list, prefix=""):
1205 join = lambda f: os.path.join(prefix, f)
1205 join = lambda f: os.path.join(prefix, f)
1206 wlock = self._repo.wlock()
1206 wlock = self._repo.wlock()
1207 ui, ds = self._repo.ui, self._repo.dirstate
1207 ui, ds = self._repo.ui, self._repo.dirstate
1208 try:
1208 try:
1209 rejected = []
1209 rejected = []
1210 lstat = self._repo.wvfs.lstat
1210 lstat = self._repo.wvfs.lstat
1211 for f in list:
1211 for f in list:
1212 scmutil.checkportable(ui, join(f))
1212 scmutil.checkportable(ui, join(f))
1213 try:
1213 try:
1214 st = lstat(f)
1214 st = lstat(f)
1215 except OSError:
1215 except OSError:
1216 ui.warn(_("%s does not exist!\n") % join(f))
1216 ui.warn(_("%s does not exist!\n") % join(f))
1217 rejected.append(f)
1217 rejected.append(f)
1218 continue
1218 continue
1219 if st.st_size > 10000000:
1219 if st.st_size > 10000000:
1220 ui.warn(_("%s: up to %d MB of RAM may be required "
1220 ui.warn(_("%s: up to %d MB of RAM may be required "
1221 "to manage this file\n"
1221 "to manage this file\n"
1222 "(use 'hg revert %s' to cancel the "
1222 "(use 'hg revert %s' to cancel the "
1223 "pending addition)\n")
1223 "pending addition)\n")
1224 % (f, 3 * st.st_size // 1000000, join(f)))
1224 % (f, 3 * st.st_size // 1000000, join(f)))
1225 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
1225 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
1226 ui.warn(_("%s not added: only files and symlinks "
1226 ui.warn(_("%s not added: only files and symlinks "
1227 "supported currently\n") % join(f))
1227 "supported currently\n") % join(f))
1228 rejected.append(f)
1228 rejected.append(f)
1229 elif ds[f] in 'amn':
1229 elif ds[f] in 'amn':
1230 ui.warn(_("%s already tracked!\n") % join(f))
1230 ui.warn(_("%s already tracked!\n") % join(f))
1231 elif ds[f] == 'r':
1231 elif ds[f] == 'r':
1232 ds.normallookup(f)
1232 ds.normallookup(f)
1233 else:
1233 else:
1234 ds.add(f)
1234 ds.add(f)
1235 return rejected
1235 return rejected
1236 finally:
1236 finally:
1237 wlock.release()
1237 wlock.release()
1238
1238
1239 def forget(self, files, prefix=""):
1239 def forget(self, files, prefix=""):
1240 join = lambda f: os.path.join(prefix, f)
1240 join = lambda f: os.path.join(prefix, f)
1241 wlock = self._repo.wlock()
1241 wlock = self._repo.wlock()
1242 try:
1242 try:
1243 rejected = []
1243 rejected = []
1244 for f in files:
1244 for f in files:
1245 if f not in self._repo.dirstate:
1245 if f not in self._repo.dirstate:
1246 self._repo.ui.warn(_("%s not tracked!\n") % join(f))
1246 self._repo.ui.warn(_("%s not tracked!\n") % join(f))
1247 rejected.append(f)
1247 rejected.append(f)
1248 elif self._repo.dirstate[f] != 'a':
1248 elif self._repo.dirstate[f] != 'a':
1249 self._repo.dirstate.remove(f)
1249 self._repo.dirstate.remove(f)
1250 else:
1250 else:
1251 self._repo.dirstate.drop(f)
1251 self._repo.dirstate.drop(f)
1252 return rejected
1252 return rejected
1253 finally:
1253 finally:
1254 wlock.release()
1254 wlock.release()
1255
1255
1256 def undelete(self, list):
1256 def undelete(self, list):
1257 pctxs = self.parents()
1257 pctxs = self.parents()
1258 wlock = self._repo.wlock()
1258 wlock = self._repo.wlock()
1259 try:
1259 try:
1260 for f in list:
1260 for f in list:
1261 if self._repo.dirstate[f] != 'r':
1261 if self._repo.dirstate[f] != 'r':
1262 self._repo.ui.warn(_("%s not removed!\n") % f)
1262 self._repo.ui.warn(_("%s not removed!\n") % f)
1263 else:
1263 else:
1264 fctx = f in pctxs[0] and pctxs[0][f] or pctxs[1][f]
1264 fctx = f in pctxs[0] and pctxs[0][f] or pctxs[1][f]
1265 t = fctx.data()
1265 t = fctx.data()
1266 self._repo.wwrite(f, t, fctx.flags())
1266 self._repo.wwrite(f, t, fctx.flags())
1267 self._repo.dirstate.normal(f)
1267 self._repo.dirstate.normal(f)
1268 finally:
1268 finally:
1269 wlock.release()
1269 wlock.release()
1270
1270
1271 def copy(self, source, dest):
1271 def copy(self, source, dest):
1272 try:
1272 try:
1273 st = self._repo.wvfs.lstat(dest)
1273 st = self._repo.wvfs.lstat(dest)
1274 except OSError, err:
1274 except OSError, err:
1275 if err.errno != errno.ENOENT:
1275 if err.errno != errno.ENOENT:
1276 raise
1276 raise
1277 self._repo.ui.warn(_("%s does not exist!\n") % dest)
1277 self._repo.ui.warn(_("%s does not exist!\n") % dest)
1278 return
1278 return
1279 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
1279 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
1280 self._repo.ui.warn(_("copy failed: %s is not a file or a "
1280 self._repo.ui.warn(_("copy failed: %s is not a file or a "
1281 "symbolic link\n") % dest)
1281 "symbolic link\n") % dest)
1282 else:
1282 else:
1283 wlock = self._repo.wlock()
1283 wlock = self._repo.wlock()
1284 try:
1284 try:
1285 if self._repo.dirstate[dest] in '?r':
1285 if self._repo.dirstate[dest] in '?r':
1286 self._repo.dirstate.add(dest)
1286 self._repo.dirstate.add(dest)
1287 self._repo.dirstate.copy(source, dest)
1287 self._repo.dirstate.copy(source, dest)
1288 finally:
1288 finally:
1289 wlock.release()
1289 wlock.release()
1290
1290
1291 def _filtersuspectsymlink(self, files):
1291 def _filtersuspectsymlink(self, files):
1292 if not files or self._repo.dirstate._checklink:
1292 if not files or self._repo.dirstate._checklink:
1293 return files
1293 return files
1294
1294
1295 # Symlink placeholders may get non-symlink-like contents
1295 # Symlink placeholders may get non-symlink-like contents
1296 # via user error or dereferencing by NFS or Samba servers,
1296 # via user error or dereferencing by NFS or Samba servers,
1297 # so we filter out any placeholders that don't look like a
1297 # so we filter out any placeholders that don't look like a
1298 # symlink
1298 # symlink
1299 sane = []
1299 sane = []
1300 for f in files:
1300 for f in files:
1301 if self.flags(f) == 'l':
1301 if self.flags(f) == 'l':
1302 d = self[f].data()
1302 d = self[f].data()
1303 if d == '' or len(d) >= 1024 or '\n' in d or util.binary(d):
1303 if d == '' or len(d) >= 1024 or '\n' in d or util.binary(d):
1304 self._repo.ui.debug('ignoring suspect symlink placeholder'
1304 self._repo.ui.debug('ignoring suspect symlink placeholder'
1305 ' "%s"\n' % f)
1305 ' "%s"\n' % f)
1306 continue
1306 continue
1307 sane.append(f)
1307 sane.append(f)
1308 return sane
1308 return sane
1309
1309
1310 def _checklookup(self, files):
1310 def _checklookup(self, files):
1311 # check for any possibly clean files
1311 # check for any possibly clean files
1312 if not files:
1312 if not files:
1313 return [], []
1313 return [], []
1314
1314
1315 modified = []
1315 modified = []
1316 fixup = []
1316 fixup = []
1317 pctx = self._parents[0]
1317 pctx = self._parents[0]
1318 # do a full compare of any files that might have changed
1318 # do a full compare of any files that might have changed
1319 for f in sorted(files):
1319 for f in sorted(files):
1320 if (f not in pctx or self.flags(f) != pctx.flags(f)
1320 if (f not in pctx or self.flags(f) != pctx.flags(f)
1321 or pctx[f].cmp(self[f])):
1321 or pctx[f].cmp(self[f])):
1322 modified.append(f)
1322 modified.append(f)
1323 else:
1323 else:
1324 fixup.append(f)
1324 fixup.append(f)
1325
1325
1326 # update dirstate for files that are actually clean
1326 # update dirstate for files that are actually clean
1327 if fixup:
1327 if fixup:
1328 try:
1328 try:
1329 # updating the dirstate is optional
1329 # updating the dirstate is optional
1330 # so we don't wait on the lock
1330 # so we don't wait on the lock
1331 normal = self._repo.dirstate.normal
1331 normal = self._repo.dirstate.normal
1332 wlock = self._repo.wlock(False)
1332 wlock = self._repo.wlock(False)
1333 try:
1333 try:
1334 for f in fixup:
1334 for f in fixup:
1335 normal(f)
1335 normal(f)
1336 finally:
1336 finally:
1337 wlock.release()
1337 wlock.release()
1338 except error.LockError:
1338 except error.LockError:
1339 pass
1339 pass
1340 return modified, fixup
1340 return modified, fixup
1341
1341
1342 def _manifestmatches(self, match, s):
1342 def _manifestmatches(self, match, s):
1343 """Slow path for workingctx
1343 """Slow path for workingctx
1344
1344
1345 The fast path is when we compare the working directory to its parent
1345 The fast path is when we compare the working directory to its parent
1346 which means this function is comparing with a non-parent; therefore we
1346 which means this function is comparing with a non-parent; therefore we
1347 need to build a manifest and return what matches.
1347 need to build a manifest and return what matches.
1348 """
1348 """
1349 mf = self._repo['.']._manifestmatches(match, s)
1349 mf = self._repo['.']._manifestmatches(match, s)
1350 modified, added, removed = s[0:3]
1350 modified, added, removed = s[0:3]
1351 for f in modified + added:
1351 for f in modified + added:
1352 mf[f] = None
1352 mf[f] = None
1353 mf.set(f, self.flags(f))
1353 mf.set(f, self.flags(f))
1354 for f in removed:
1354 for f in removed:
1355 if f in mf:
1355 if f in mf:
1356 del mf[f]
1356 del mf[f]
1357 return mf
1357 return mf
1358
1358
1359 def _prestatus(self, other, s, match, listignored, listclean, listunknown):
1359 def _prestatus(self, other, s, match, listignored, listclean, listunknown):
1360 """override the parent hook with a dirstate query
1360 """override the parent hook with a dirstate query
1361
1361
1362 We use this prestatus hook to populate the status with information from
1362 We use this prestatus hook to populate the status with information from
1363 the dirstate.
1363 the dirstate.
1364 """
1364 """
1365 # doesn't need to call super; if that changes, be aware that super
1365 # doesn't need to call super; if that changes, be aware that super
1366 # calls self.manifest which would slow down the common case of calling
1366 # calls self.manifest which would slow down the common case of calling
1367 # status against a workingctx's parent
1367 # status against a workingctx's parent
1368 return self._dirstatestatus(match, listignored, listclean, listunknown)
1368 return self._dirstatestatus(match, listignored, listclean, listunknown)
1369
1369
1370 def _poststatus(self, other, s, match, listignored, listclean, listunknown):
1370 def _poststatus(self, other, s, match, listignored, listclean, listunknown):
1371 """override the parent hook with a filter for suspect symlinks
1371 """override the parent hook with a filter for suspect symlinks
1372
1372
1373 We use this poststatus hook to filter out symlinks that might have
1373 We use this poststatus hook to filter out symlinks that might have
1374 accidentally ended up with the entire contents of the file they are
1374 accidentally ended up with the entire contents of the file they are
1375 susposed to be linking to.
1375 susposed to be linking to.
1376 """
1376 """
1377 s[0] = self._filtersuspectsymlink(s[0])
1377 s[0] = self._filtersuspectsymlink(s[0])
1378 self._status = s
1378 self._status = s
1379 return s
1379 return s
1380
1380
1381 def _dirstatestatus(self, match=None, ignored=False, clean=False,
1381 def _dirstatestatus(self, match=None, ignored=False, clean=False,
1382 unknown=False):
1382 unknown=False):
1383 '''Gets the status from the dirstate -- internal use only.'''
1383 '''Gets the status from the dirstate -- internal use only.'''
1384 listignored, listclean, listunknown = ignored, clean, unknown
1384 listignored, listclean, listunknown = ignored, clean, unknown
1385 match = match or matchmod.always(self._repo.root, self._repo.getcwd())
1385 match = match or matchmod.always(self._repo.root, self._repo.getcwd())
1386 subrepos = []
1386 subrepos = []
1387 if '.hgsub' in self:
1387 if '.hgsub' in self:
1388 subrepos = sorted(self.substate)
1388 subrepos = sorted(self.substate)
1389 s = self._repo.dirstate.status(match, subrepos, listignored,
1389 s = self._repo.dirstate.status(match, subrepos, listignored,
1390 listclean, listunknown)
1390 listclean, listunknown)
1391 cmp, modified, added, removed, deleted, unknown, ignored, clean = s
1391 cmp, modified, added, removed, deleted, unknown, ignored, clean = s
1392
1392
1393 # check for any possibly clean files
1393 # check for any possibly clean files
1394 if cmp:
1394 if cmp:
1395 modified2, fixup = self._checklookup(cmp)
1395 modified2, fixup = self._checklookup(cmp)
1396 modified += modified2
1396 modified += modified2
1397
1397
1398 # update dirstate for files that are actually clean
1398 # update dirstate for files that are actually clean
1399 if fixup and listclean:
1399 if fixup and listclean:
1400 clean += fixup
1400 clean += fixup
1401
1401
1402 return [modified, added, removed, deleted, unknown, ignored, clean]
1402 return [modified, added, removed, deleted, unknown, ignored, clean]
1403
1403
1404 def _buildstatus(self, other, s, match, listignored, listclean,
1404 def _buildstatus(self, other, s, match, listignored, listclean,
1405 listunknown):
1405 listunknown):
1406 """build a status with respect to another context
1406 """build a status with respect to another context
1407
1407
1408 This includes logic for maintaining the fast path of status when
1408 This includes logic for maintaining the fast path of status when
1409 comparing the working directory against its parent, which is to skip
1409 comparing the working directory against its parent, which is to skip
1410 building a new manifest if self (working directory) is not comparing
1410 building a new manifest if self (working directory) is not comparing
1411 against its parent (repo['.']).
1411 against its parent (repo['.']).
1412 """
1412 """
1413 if other != self._repo['.']:
1413 if other != self._repo['.']:
1414 s = super(workingctx, self)._buildstatus(other, s, match,
1414 s = super(workingctx, self)._buildstatus(other, s, match,
1415 listignored, listclean,
1415 listignored, listclean,
1416 listunknown)
1416 listunknown)
1417 return s
1417 return s
1418
1418
1419 def _matchstatus(self, other, s, match, listignored, listclean,
1419 def _matchstatus(self, other, s, match, listignored, listclean,
1420 listunknown):
1420 listunknown):
1421 """override the match method with a filter for directory patterns
1421 """override the match method with a filter for directory patterns
1422
1422
1423 We use inheritance to customize the match.bad method only in cases of
1423 We use inheritance to customize the match.bad method only in cases of
1424 workingctx since it belongs only to the working directory when
1424 workingctx since it belongs only to the working directory when
1425 comparing against the parent changeset.
1425 comparing against the parent changeset.
1426
1426
1427 If we aren't comparing against the working directory's parent, then we
1427 If we aren't comparing against the working directory's parent, then we
1428 just use the default match object sent to us.
1428 just use the default match object sent to us.
1429 """
1429 """
1430 superself = super(workingctx, self)
1430 superself = super(workingctx, self)
1431 match = superself._matchstatus(other, s, match, listignored, listclean,
1431 match = superself._matchstatus(other, s, match, listignored, listclean,
1432 listunknown)
1432 listunknown)
1433 if other != self._repo['.']:
1433 if other != self._repo['.']:
1434 def bad(f, msg):
1434 def bad(f, msg):
1435 # 'f' may be a directory pattern from 'match.files()',
1435 # 'f' may be a directory pattern from 'match.files()',
1436 # so 'f not in ctx1' is not enough
1436 # so 'f not in ctx1' is not enough
1437 if f not in other and f not in other.dirs():
1437 if f not in other and f not in other.dirs():
1438 self._repo.ui.warn('%s: %s\n' %
1438 self._repo.ui.warn('%s: %s\n' %
1439 (self._repo.dirstate.pathto(f), msg))
1439 (self._repo.dirstate.pathto(f), msg))
1440 match.bad = bad
1440 match.bad = bad
1441 return match
1441 return match
1442
1442
1443 def status(self, other='.', match=None, listignored=False,
1443 def status(self, other='.', match=None, listignored=False,
1444 listclean=False, listunknown=False, listsubrepos=False):
1444 listclean=False, listunknown=False, listsubrepos=False):
1445 # yet to be determined: what to do if 'other' is a 'workingctx' or a
1445 # yet to be determined: what to do if 'other' is a 'workingctx' or a
1446 # 'memctx'?
1446 # 'memctx'?
1447 s = super(workingctx, self).status(other, match, listignored, listclean,
1447 s = super(workingctx, self).status(other, match, listignored, listclean,
1448 listunknown, listsubrepos)
1448 listunknown, listsubrepos)
1449 # calling 'super' subtly reveresed the contexts, so we flip the results
1449 # calling 'super' subtly reveresed the contexts, so we flip the results
1450 # (s[1] is 'added' and s[2] is 'removed')
1450 # (s[1] is 'added' and s[2] is 'removed')
1451 s = list(s)
1451 s = list(s)
1452 s[1], s[2] = s[2], s[1]
1452 s[1], s[2] = s[2], s[1]
1453 return tuple(s)
1453 return tuple(s)
1454
1454
1455 class committablefilectx(basefilectx):
1455 class committablefilectx(basefilectx):
1456 """A committablefilectx provides common functionality for a file context
1456 """A committablefilectx provides common functionality for a file context
1457 that wants the ability to commit, e.g. workingfilectx or memfilectx."""
1457 that wants the ability to commit, e.g. workingfilectx or memfilectx."""
1458 def __init__(self, repo, path, filelog=None, ctx=None):
1458 def __init__(self, repo, path, filelog=None, ctx=None):
1459 self._repo = repo
1459 self._repo = repo
1460 self._path = path
1460 self._path = path
1461 self._changeid = None
1461 self._changeid = None
1462 self._filerev = self._filenode = None
1462 self._filerev = self._filenode = None
1463
1463
1464 if filelog is not None:
1464 if filelog is not None:
1465 self._filelog = filelog
1465 self._filelog = filelog
1466 if ctx:
1466 if ctx:
1467 self._changectx = ctx
1467 self._changectx = ctx
1468
1468
1469 def __nonzero__(self):
1469 def __nonzero__(self):
1470 return True
1470 return True
1471
1471
1472 def parents(self):
1472 def parents(self):
1473 '''return parent filectxs, following copies if necessary'''
1473 '''return parent filectxs, following copies if necessary'''
1474 def filenode(ctx, path):
1474 def filenode(ctx, path):
1475 return ctx._manifest.get(path, nullid)
1475 return ctx._manifest.get(path, nullid)
1476
1476
1477 path = self._path
1477 path = self._path
1478 fl = self._filelog
1478 fl = self._filelog
1479 pcl = self._changectx._parents
1479 pcl = self._changectx._parents
1480 renamed = self.renamed()
1480 renamed = self.renamed()
1481
1481
1482 if renamed:
1482 if renamed:
1483 pl = [renamed + (None,)]
1483 pl = [renamed + (None,)]
1484 else:
1484 else:
1485 pl = [(path, filenode(pcl[0], path), fl)]
1485 pl = [(path, filenode(pcl[0], path), fl)]
1486
1486
1487 for pc in pcl[1:]:
1487 for pc in pcl[1:]:
1488 pl.append((path, filenode(pc, path), fl))
1488 pl.append((path, filenode(pc, path), fl))
1489
1489
1490 return [filectx(self._repo, p, fileid=n, filelog=l)
1490 return [filectx(self._repo, p, fileid=n, filelog=l)
1491 for p, n, l in pl if n != nullid]
1491 for p, n, l in pl if n != nullid]
1492
1492
1493 def children(self):
1493 def children(self):
1494 return []
1494 return []
1495
1495
1496 class workingfilectx(committablefilectx):
1496 class workingfilectx(committablefilectx):
1497 """A workingfilectx object makes access to data related to a particular
1497 """A workingfilectx object makes access to data related to a particular
1498 file in the working directory convenient."""
1498 file in the working directory convenient."""
1499 def __init__(self, repo, path, filelog=None, workingctx=None):
1499 def __init__(self, repo, path, filelog=None, workingctx=None):
1500 super(workingfilectx, self).__init__(repo, path, filelog, workingctx)
1500 super(workingfilectx, self).__init__(repo, path, filelog, workingctx)
1501
1501
1502 @propertycache
1502 @propertycache
1503 def _changectx(self):
1503 def _changectx(self):
1504 return workingctx(self._repo)
1504 return workingctx(self._repo)
1505
1505
1506 def data(self):
1506 def data(self):
1507 return self._repo.wread(self._path)
1507 return self._repo.wread(self._path)
1508 def renamed(self):
1508 def renamed(self):
1509 rp = self._repo.dirstate.copied(self._path)
1509 rp = self._repo.dirstate.copied(self._path)
1510 if not rp:
1510 if not rp:
1511 return None
1511 return None
1512 return rp, self._changectx._parents[0]._manifest.get(rp, nullid)
1512 return rp, self._changectx._parents[0]._manifest.get(rp, nullid)
1513
1513
1514 def size(self):
1514 def size(self):
1515 return self._repo.wvfs.lstat(self._path).st_size
1515 return self._repo.wvfs.lstat(self._path).st_size
1516 def date(self):
1516 def date(self):
1517 t, tz = self._changectx.date()
1517 t, tz = self._changectx.date()
1518 try:
1518 try:
1519 return (int(self._repo.wvfs.lstat(self._path).st_mtime), tz)
1519 return (int(self._repo.wvfs.lstat(self._path).st_mtime), tz)
1520 except OSError, err:
1520 except OSError, err:
1521 if err.errno != errno.ENOENT:
1521 if err.errno != errno.ENOENT:
1522 raise
1522 raise
1523 return (t, tz)
1523 return (t, tz)
1524
1524
1525 def cmp(self, fctx):
1525 def cmp(self, fctx):
1526 """compare with other file context
1526 """compare with other file context
1527
1527
1528 returns True if different than fctx.
1528 returns True if different than fctx.
1529 """
1529 """
1530 # fctx should be a filectx (not a workingfilectx)
1530 # fctx should be a filectx (not a workingfilectx)
1531 # invert comparison to reuse the same code path
1531 # invert comparison to reuse the same code path
1532 return fctx.cmp(self)
1532 return fctx.cmp(self)
1533
1533
1534 class memctx(committablectx):
1534 class memctx(committablectx):
1535 """Use memctx to perform in-memory commits via localrepo.commitctx().
1535 """Use memctx to perform in-memory commits via localrepo.commitctx().
1536
1536
1537 Revision information is supplied at initialization time while
1537 Revision information is supplied at initialization time while
1538 related files data and is made available through a callback
1538 related files data and is made available through a callback
1539 mechanism. 'repo' is the current localrepo, 'parents' is a
1539 mechanism. 'repo' is the current localrepo, 'parents' is a
1540 sequence of two parent revisions identifiers (pass None for every
1540 sequence of two parent revisions identifiers (pass None for every
1541 missing parent), 'text' is the commit message and 'files' lists
1541 missing parent), 'text' is the commit message and 'files' lists
1542 names of files touched by the revision (normalized and relative to
1542 names of files touched by the revision (normalized and relative to
1543 repository root).
1543 repository root).
1544
1544
1545 filectxfn(repo, memctx, path) is a callable receiving the
1545 filectxfn(repo, memctx, path) is a callable receiving the
1546 repository, the current memctx object and the normalized path of
1546 repository, the current memctx object and the normalized path of
1547 requested file, relative to repository root. It is fired by the
1547 requested file, relative to repository root. It is fired by the
1548 commit function for every file in 'files', but calls order is
1548 commit function for every file in 'files', but calls order is
1549 undefined. If the file is available in the revision being
1549 undefined. If the file is available in the revision being
1550 committed (updated or added), filectxfn returns a memfilectx
1550 committed (updated or added), filectxfn returns a memfilectx
1551 object. If the file was removed, filectxfn raises an
1551 object. If the file was removed, filectxfn raises an
1552 IOError. Moved files are represented by marking the source file
1552 IOError. Moved files are represented by marking the source file
1553 removed and the new file added with copy information (see
1553 removed and the new file added with copy information (see
1554 memfilectx).
1554 memfilectx).
1555
1555
1556 user receives the committer name and defaults to current
1556 user receives the committer name and defaults to current
1557 repository username, date is the commit date in any format
1557 repository username, date is the commit date in any format
1558 supported by util.parsedate() and defaults to current date, extra
1558 supported by util.parsedate() and defaults to current date, extra
1559 is a dictionary of metadata or is left empty.
1559 is a dictionary of metadata or is left empty.
1560 """
1560 """
1561 def __init__(self, repo, parents, text, files, filectxfn, user=None,
1561 def __init__(self, repo, parents, text, files, filectxfn, user=None,
1562 date=None, extra=None, editor=False):
1562 date=None, extra=None, editor=False):
1563 self._repo = repo
1563 super(memctx, self).__init__(repo, text, user, date, extra)
1564 self._rev = None
1564 self._rev = None
1565 self._node = None
1565 self._node = None
1566 self._text = text
1567 self._date = date and util.parsedate(date) or util.makedate()
1568 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