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