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