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