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