##// END OF EJS Templates
phases: implement ``phase()`` and ``hidden()`` method for workingctx...
Pierre-Yves David -
r15707:dc3eefe0 default
parent child Browse files
Show More
@@ -1,1151 +1,1160
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
8 from node import nullid, nullrev, short, hex
9 from i18n import _
9 from i18n import _
10 import ancestor, mdiff, error, util, scmutil, subrepo, patch, encoding
10 import ancestor, mdiff, error, util, scmutil, subrepo, patch, encoding
11 import match as matchmod
11 import match as matchmod
12 import os, errno, stat
12 import os, errno, stat
13
13
14 propertycache = util.propertycache
14 propertycache = util.propertycache
15
15
16 class changectx(object):
16 class changectx(object):
17 """A changecontext object makes access to data related to a particular
17 """A changecontext object makes access to data related to a particular
18 changeset convenient."""
18 changeset convenient."""
19 def __init__(self, repo, changeid=''):
19 def __init__(self, repo, changeid=''):
20 """changeid is a revision number, node, or tag"""
20 """changeid is a revision number, node, or tag"""
21 if changeid == '':
21 if changeid == '':
22 changeid = '.'
22 changeid = '.'
23 self._repo = repo
23 self._repo = repo
24 if isinstance(changeid, (long, int)):
24 if isinstance(changeid, (long, int)):
25 self._rev = changeid
25 self._rev = changeid
26 self._node = self._repo.changelog.node(changeid)
26 self._node = self._repo.changelog.node(changeid)
27 else:
27 else:
28 self._node = self._repo.lookup(changeid)
28 self._node = self._repo.lookup(changeid)
29 self._rev = self._repo.changelog.rev(self._node)
29 self._rev = self._repo.changelog.rev(self._node)
30
30
31 def __str__(self):
31 def __str__(self):
32 return short(self.node())
32 return short(self.node())
33
33
34 def __int__(self):
34 def __int__(self):
35 return self.rev()
35 return self.rev()
36
36
37 def __repr__(self):
37 def __repr__(self):
38 return "<changectx %s>" % str(self)
38 return "<changectx %s>" % str(self)
39
39
40 def __hash__(self):
40 def __hash__(self):
41 try:
41 try:
42 return hash(self._rev)
42 return hash(self._rev)
43 except AttributeError:
43 except AttributeError:
44 return id(self)
44 return id(self)
45
45
46 def __eq__(self, other):
46 def __eq__(self, other):
47 try:
47 try:
48 return self._rev == other._rev
48 return self._rev == other._rev
49 except AttributeError:
49 except AttributeError:
50 return False
50 return False
51
51
52 def __ne__(self, other):
52 def __ne__(self, other):
53 return not (self == other)
53 return not (self == other)
54
54
55 def __nonzero__(self):
55 def __nonzero__(self):
56 return self._rev != nullrev
56 return self._rev != nullrev
57
57
58 @propertycache
58 @propertycache
59 def _changeset(self):
59 def _changeset(self):
60 return self._repo.changelog.read(self.node())
60 return self._repo.changelog.read(self.node())
61
61
62 @propertycache
62 @propertycache
63 def _manifest(self):
63 def _manifest(self):
64 return self._repo.manifest.read(self._changeset[0])
64 return self._repo.manifest.read(self._changeset[0])
65
65
66 @propertycache
66 @propertycache
67 def _manifestdelta(self):
67 def _manifestdelta(self):
68 return self._repo.manifest.readdelta(self._changeset[0])
68 return self._repo.manifest.readdelta(self._changeset[0])
69
69
70 @propertycache
70 @propertycache
71 def _parents(self):
71 def _parents(self):
72 p = self._repo.changelog.parentrevs(self._rev)
72 p = self._repo.changelog.parentrevs(self._rev)
73 if p[1] == nullrev:
73 if p[1] == nullrev:
74 p = p[:-1]
74 p = p[:-1]
75 return [changectx(self._repo, x) for x in p]
75 return [changectx(self._repo, x) for x in p]
76
76
77 @propertycache
77 @propertycache
78 def substate(self):
78 def substate(self):
79 return subrepo.state(self, self._repo.ui)
79 return subrepo.state(self, self._repo.ui)
80
80
81 def __contains__(self, key):
81 def __contains__(self, key):
82 return key in self._manifest
82 return key in self._manifest
83
83
84 def __getitem__(self, key):
84 def __getitem__(self, key):
85 return self.filectx(key)
85 return self.filectx(key)
86
86
87 def __iter__(self):
87 def __iter__(self):
88 for f in sorted(self._manifest):
88 for f in sorted(self._manifest):
89 yield f
89 yield f
90
90
91 def changeset(self):
91 def changeset(self):
92 return self._changeset
92 return self._changeset
93 def manifest(self):
93 def manifest(self):
94 return self._manifest
94 return self._manifest
95 def manifestnode(self):
95 def manifestnode(self):
96 return self._changeset[0]
96 return self._changeset[0]
97
97
98 def rev(self):
98 def rev(self):
99 return self._rev
99 return self._rev
100 def node(self):
100 def node(self):
101 return self._node
101 return self._node
102 def hex(self):
102 def hex(self):
103 return hex(self._node)
103 return hex(self._node)
104 def user(self):
104 def user(self):
105 return self._changeset[1]
105 return self._changeset[1]
106 def date(self):
106 def date(self):
107 return self._changeset[2]
107 return self._changeset[2]
108 def files(self):
108 def files(self):
109 return self._changeset[3]
109 return self._changeset[3]
110 def description(self):
110 def description(self):
111 return self._changeset[4]
111 return self._changeset[4]
112 def branch(self):
112 def branch(self):
113 return encoding.tolocal(self._changeset[5].get("branch"))
113 return encoding.tolocal(self._changeset[5].get("branch"))
114 def extra(self):
114 def extra(self):
115 return self._changeset[5]
115 return self._changeset[5]
116 def tags(self):
116 def tags(self):
117 return self._repo.nodetags(self._node)
117 return self._repo.nodetags(self._node)
118 def bookmarks(self):
118 def bookmarks(self):
119 return self._repo.nodebookmarks(self._node)
119 return self._repo.nodebookmarks(self._node)
120 def phase(self):
120 def phase(self):
121 if self._rev == -1:
121 if self._rev == -1:
122 return 0
122 return 0
123 if self._rev >= len(self._repo._phaserev):
123 if self._rev >= len(self._repo._phaserev):
124 # outdated cache
124 # outdated cache
125 del self._repo._phaserev
125 del self._repo._phaserev
126 return self._repo._phaserev[self._rev]
126 return self._repo._phaserev[self._rev]
127 def mutable(self):
127 def mutable(self):
128 return self._repo._phaserev[self._rev] > 0
128 return self._repo._phaserev[self._rev] > 0
129 def hidden(self):
129 def hidden(self):
130 return self._rev in self._repo.changelog.hiddenrevs
130 return self._rev in self._repo.changelog.hiddenrevs
131
131
132 def parents(self):
132 def parents(self):
133 """return contexts for each parent changeset"""
133 """return contexts for each parent changeset"""
134 return self._parents
134 return self._parents
135
135
136 def p1(self):
136 def p1(self):
137 return self._parents[0]
137 return self._parents[0]
138
138
139 def p2(self):
139 def p2(self):
140 if len(self._parents) == 2:
140 if len(self._parents) == 2:
141 return self._parents[1]
141 return self._parents[1]
142 return changectx(self._repo, -1)
142 return changectx(self._repo, -1)
143
143
144 def children(self):
144 def children(self):
145 """return contexts for each child changeset"""
145 """return contexts for each child changeset"""
146 c = self._repo.changelog.children(self._node)
146 c = self._repo.changelog.children(self._node)
147 return [changectx(self._repo, x) for x in c]
147 return [changectx(self._repo, x) for x in c]
148
148
149 def ancestors(self):
149 def ancestors(self):
150 for a in self._repo.changelog.ancestors(self._rev):
150 for a in self._repo.changelog.ancestors(self._rev):
151 yield changectx(self._repo, a)
151 yield changectx(self._repo, a)
152
152
153 def descendants(self):
153 def descendants(self):
154 for d in self._repo.changelog.descendants(self._rev):
154 for d in self._repo.changelog.descendants(self._rev):
155 yield changectx(self._repo, d)
155 yield changectx(self._repo, d)
156
156
157 def _fileinfo(self, path):
157 def _fileinfo(self, path):
158 if '_manifest' in self.__dict__:
158 if '_manifest' in self.__dict__:
159 try:
159 try:
160 return self._manifest[path], self._manifest.flags(path)
160 return self._manifest[path], self._manifest.flags(path)
161 except KeyError:
161 except KeyError:
162 raise error.LookupError(self._node, path,
162 raise error.LookupError(self._node, path,
163 _('not found in manifest'))
163 _('not found in manifest'))
164 if '_manifestdelta' in self.__dict__ or path in self.files():
164 if '_manifestdelta' in self.__dict__ or path in self.files():
165 if path in self._manifestdelta:
165 if path in self._manifestdelta:
166 return self._manifestdelta[path], self._manifestdelta.flags(path)
166 return self._manifestdelta[path], self._manifestdelta.flags(path)
167 node, flag = self._repo.manifest.find(self._changeset[0], path)
167 node, flag = self._repo.manifest.find(self._changeset[0], path)
168 if not node:
168 if not node:
169 raise error.LookupError(self._node, path,
169 raise error.LookupError(self._node, path,
170 _('not found in manifest'))
170 _('not found in manifest'))
171
171
172 return node, flag
172 return node, flag
173
173
174 def filenode(self, path):
174 def filenode(self, path):
175 return self._fileinfo(path)[0]
175 return self._fileinfo(path)[0]
176
176
177 def flags(self, path):
177 def flags(self, path):
178 try:
178 try:
179 return self._fileinfo(path)[1]
179 return self._fileinfo(path)[1]
180 except error.LookupError:
180 except error.LookupError:
181 return ''
181 return ''
182
182
183 def filectx(self, path, fileid=None, filelog=None):
183 def filectx(self, path, fileid=None, filelog=None):
184 """get a file context from this changeset"""
184 """get a file context from this changeset"""
185 if fileid is None:
185 if fileid is None:
186 fileid = self.filenode(path)
186 fileid = self.filenode(path)
187 return filectx(self._repo, path, fileid=fileid,
187 return filectx(self._repo, path, fileid=fileid,
188 changectx=self, filelog=filelog)
188 changectx=self, filelog=filelog)
189
189
190 def ancestor(self, c2):
190 def ancestor(self, c2):
191 """
191 """
192 return the ancestor context of self and c2
192 return the ancestor context of self and c2
193 """
193 """
194 # deal with workingctxs
194 # deal with workingctxs
195 n2 = c2._node
195 n2 = c2._node
196 if n2 is None:
196 if n2 is None:
197 n2 = c2._parents[0]._node
197 n2 = c2._parents[0]._node
198 n = self._repo.changelog.ancestor(self._node, n2)
198 n = self._repo.changelog.ancestor(self._node, n2)
199 return changectx(self._repo, n)
199 return changectx(self._repo, n)
200
200
201 def walk(self, match):
201 def walk(self, match):
202 fset = set(match.files())
202 fset = set(match.files())
203 # for dirstate.walk, files=['.'] means "walk the whole tree".
203 # for dirstate.walk, files=['.'] means "walk the whole tree".
204 # follow that here, too
204 # follow that here, too
205 fset.discard('.')
205 fset.discard('.')
206 for fn in self:
206 for fn in self:
207 for ffn in fset:
207 for ffn in fset:
208 # match if the file is the exact name or a directory
208 # match if the file is the exact name or a directory
209 if ffn == fn or fn.startswith("%s/" % ffn):
209 if ffn == fn or fn.startswith("%s/" % ffn):
210 fset.remove(ffn)
210 fset.remove(ffn)
211 break
211 break
212 if match(fn):
212 if match(fn):
213 yield fn
213 yield fn
214 for fn in sorted(fset):
214 for fn in sorted(fset):
215 if match.bad(fn, _('no such file in rev %s') % self) and match(fn):
215 if match.bad(fn, _('no such file in rev %s') % self) and match(fn):
216 yield fn
216 yield fn
217
217
218 def sub(self, path):
218 def sub(self, path):
219 return subrepo.subrepo(self, path)
219 return subrepo.subrepo(self, path)
220
220
221 def match(self, pats=[], include=None, exclude=None, default='glob'):
221 def match(self, pats=[], include=None, exclude=None, default='glob'):
222 r = self._repo
222 r = self._repo
223 return matchmod.match(r.root, r.getcwd(), pats,
223 return matchmod.match(r.root, r.getcwd(), pats,
224 include, exclude, default,
224 include, exclude, default,
225 auditor=r.auditor, ctx=self)
225 auditor=r.auditor, ctx=self)
226
226
227 def diff(self, ctx2=None, match=None, **opts):
227 def diff(self, ctx2=None, match=None, **opts):
228 """Returns a diff generator for the given contexts and matcher"""
228 """Returns a diff generator for the given contexts and matcher"""
229 if ctx2 is None:
229 if ctx2 is None:
230 ctx2 = self.p1()
230 ctx2 = self.p1()
231 if ctx2 is not None and not isinstance(ctx2, changectx):
231 if ctx2 is not None and not isinstance(ctx2, changectx):
232 ctx2 = self._repo[ctx2]
232 ctx2 = self._repo[ctx2]
233 diffopts = patch.diffopts(self._repo.ui, opts)
233 diffopts = patch.diffopts(self._repo.ui, opts)
234 return patch.diff(self._repo, ctx2.node(), self.node(),
234 return patch.diff(self._repo, ctx2.node(), self.node(),
235 match=match, opts=diffopts)
235 match=match, opts=diffopts)
236
236
237 class filectx(object):
237 class filectx(object):
238 """A filecontext object makes access to data related to a particular
238 """A filecontext object makes access to data related to a particular
239 filerevision convenient."""
239 filerevision convenient."""
240 def __init__(self, repo, path, changeid=None, fileid=None,
240 def __init__(self, repo, path, changeid=None, fileid=None,
241 filelog=None, changectx=None):
241 filelog=None, changectx=None):
242 """changeid can be a changeset revision, node, or tag.
242 """changeid can be a changeset revision, node, or tag.
243 fileid can be a file revision or node."""
243 fileid can be a file revision or node."""
244 self._repo = repo
244 self._repo = repo
245 self._path = path
245 self._path = path
246
246
247 assert (changeid is not None
247 assert (changeid is not None
248 or fileid is not None
248 or fileid is not None
249 or changectx is not None), \
249 or changectx is not None), \
250 ("bad args: changeid=%r, fileid=%r, changectx=%r"
250 ("bad args: changeid=%r, fileid=%r, changectx=%r"
251 % (changeid, fileid, changectx))
251 % (changeid, fileid, changectx))
252
252
253 if filelog:
253 if filelog:
254 self._filelog = filelog
254 self._filelog = filelog
255
255
256 if changeid is not None:
256 if changeid is not None:
257 self._changeid = changeid
257 self._changeid = changeid
258 if changectx is not None:
258 if changectx is not None:
259 self._changectx = changectx
259 self._changectx = changectx
260 if fileid is not None:
260 if fileid is not None:
261 self._fileid = fileid
261 self._fileid = fileid
262
262
263 @propertycache
263 @propertycache
264 def _changectx(self):
264 def _changectx(self):
265 return changectx(self._repo, self._changeid)
265 return changectx(self._repo, self._changeid)
266
266
267 @propertycache
267 @propertycache
268 def _filelog(self):
268 def _filelog(self):
269 return self._repo.file(self._path)
269 return self._repo.file(self._path)
270
270
271 @propertycache
271 @propertycache
272 def _changeid(self):
272 def _changeid(self):
273 if '_changectx' in self.__dict__:
273 if '_changectx' in self.__dict__:
274 return self._changectx.rev()
274 return self._changectx.rev()
275 else:
275 else:
276 return self._filelog.linkrev(self._filerev)
276 return self._filelog.linkrev(self._filerev)
277
277
278 @propertycache
278 @propertycache
279 def _filenode(self):
279 def _filenode(self):
280 if '_fileid' in self.__dict__:
280 if '_fileid' in self.__dict__:
281 return self._filelog.lookup(self._fileid)
281 return self._filelog.lookup(self._fileid)
282 else:
282 else:
283 return self._changectx.filenode(self._path)
283 return self._changectx.filenode(self._path)
284
284
285 @propertycache
285 @propertycache
286 def _filerev(self):
286 def _filerev(self):
287 return self._filelog.rev(self._filenode)
287 return self._filelog.rev(self._filenode)
288
288
289 @propertycache
289 @propertycache
290 def _repopath(self):
290 def _repopath(self):
291 return self._path
291 return self._path
292
292
293 def __nonzero__(self):
293 def __nonzero__(self):
294 try:
294 try:
295 self._filenode
295 self._filenode
296 return True
296 return True
297 except error.LookupError:
297 except error.LookupError:
298 # file is missing
298 # file is missing
299 return False
299 return False
300
300
301 def __str__(self):
301 def __str__(self):
302 return "%s@%s" % (self.path(), short(self.node()))
302 return "%s@%s" % (self.path(), short(self.node()))
303
303
304 def __repr__(self):
304 def __repr__(self):
305 return "<filectx %s>" % str(self)
305 return "<filectx %s>" % str(self)
306
306
307 def __hash__(self):
307 def __hash__(self):
308 try:
308 try:
309 return hash((self._path, self._filenode))
309 return hash((self._path, self._filenode))
310 except AttributeError:
310 except AttributeError:
311 return id(self)
311 return id(self)
312
312
313 def __eq__(self, other):
313 def __eq__(self, other):
314 try:
314 try:
315 return (self._path == other._path
315 return (self._path == other._path
316 and self._filenode == other._filenode)
316 and self._filenode == other._filenode)
317 except AttributeError:
317 except AttributeError:
318 return False
318 return False
319
319
320 def __ne__(self, other):
320 def __ne__(self, other):
321 return not (self == other)
321 return not (self == other)
322
322
323 def filectx(self, fileid):
323 def filectx(self, fileid):
324 '''opens an arbitrary revision of the file without
324 '''opens an arbitrary revision of the file without
325 opening a new filelog'''
325 opening a new filelog'''
326 return filectx(self._repo, self._path, fileid=fileid,
326 return filectx(self._repo, self._path, fileid=fileid,
327 filelog=self._filelog)
327 filelog=self._filelog)
328
328
329 def filerev(self):
329 def filerev(self):
330 return self._filerev
330 return self._filerev
331 def filenode(self):
331 def filenode(self):
332 return self._filenode
332 return self._filenode
333 def flags(self):
333 def flags(self):
334 return self._changectx.flags(self._path)
334 return self._changectx.flags(self._path)
335 def filelog(self):
335 def filelog(self):
336 return self._filelog
336 return self._filelog
337
337
338 def rev(self):
338 def rev(self):
339 if '_changectx' in self.__dict__:
339 if '_changectx' in self.__dict__:
340 return self._changectx.rev()
340 return self._changectx.rev()
341 if '_changeid' in self.__dict__:
341 if '_changeid' in self.__dict__:
342 return self._changectx.rev()
342 return self._changectx.rev()
343 return self._filelog.linkrev(self._filerev)
343 return self._filelog.linkrev(self._filerev)
344
344
345 def linkrev(self):
345 def linkrev(self):
346 return self._filelog.linkrev(self._filerev)
346 return self._filelog.linkrev(self._filerev)
347 def node(self):
347 def node(self):
348 return self._changectx.node()
348 return self._changectx.node()
349 def hex(self):
349 def hex(self):
350 return hex(self.node())
350 return hex(self.node())
351 def user(self):
351 def user(self):
352 return self._changectx.user()
352 return self._changectx.user()
353 def date(self):
353 def date(self):
354 return self._changectx.date()
354 return self._changectx.date()
355 def files(self):
355 def files(self):
356 return self._changectx.files()
356 return self._changectx.files()
357 def description(self):
357 def description(self):
358 return self._changectx.description()
358 return self._changectx.description()
359 def branch(self):
359 def branch(self):
360 return self._changectx.branch()
360 return self._changectx.branch()
361 def extra(self):
361 def extra(self):
362 return self._changectx.extra()
362 return self._changectx.extra()
363 def manifest(self):
363 def manifest(self):
364 return self._changectx.manifest()
364 return self._changectx.manifest()
365 def changectx(self):
365 def changectx(self):
366 return self._changectx
366 return self._changectx
367
367
368 def data(self):
368 def data(self):
369 return self._filelog.read(self._filenode)
369 return self._filelog.read(self._filenode)
370 def path(self):
370 def path(self):
371 return self._path
371 return self._path
372 def size(self):
372 def size(self):
373 return self._filelog.size(self._filerev)
373 return self._filelog.size(self._filerev)
374
374
375 def cmp(self, fctx):
375 def cmp(self, fctx):
376 """compare with other file context
376 """compare with other file context
377
377
378 returns True if different than fctx.
378 returns True if different than fctx.
379 """
379 """
380 if (fctx._filerev is None and self._repo._encodefilterpats
380 if (fctx._filerev is None and self._repo._encodefilterpats
381 or self.size() == fctx.size()):
381 or self.size() == fctx.size()):
382 return self._filelog.cmp(self._filenode, fctx.data())
382 return self._filelog.cmp(self._filenode, fctx.data())
383
383
384 return True
384 return True
385
385
386 def renamed(self):
386 def renamed(self):
387 """check if file was actually renamed in this changeset revision
387 """check if file was actually renamed in this changeset revision
388
388
389 If rename logged in file revision, we report copy for changeset only
389 If rename logged in file revision, we report copy for changeset only
390 if file revisions linkrev points back to the changeset in question
390 if file revisions linkrev points back to the changeset in question
391 or both changeset parents contain different file revisions.
391 or both changeset parents contain different file revisions.
392 """
392 """
393
393
394 renamed = self._filelog.renamed(self._filenode)
394 renamed = self._filelog.renamed(self._filenode)
395 if not renamed:
395 if not renamed:
396 return renamed
396 return renamed
397
397
398 if self.rev() == self.linkrev():
398 if self.rev() == self.linkrev():
399 return renamed
399 return renamed
400
400
401 name = self.path()
401 name = self.path()
402 fnode = self._filenode
402 fnode = self._filenode
403 for p in self._changectx.parents():
403 for p in self._changectx.parents():
404 try:
404 try:
405 if fnode == p.filenode(name):
405 if fnode == p.filenode(name):
406 return None
406 return None
407 except error.LookupError:
407 except error.LookupError:
408 pass
408 pass
409 return renamed
409 return renamed
410
410
411 def parents(self):
411 def parents(self):
412 p = self._path
412 p = self._path
413 fl = self._filelog
413 fl = self._filelog
414 pl = [(p, n, fl) for n in self._filelog.parents(self._filenode)]
414 pl = [(p, n, fl) for n in self._filelog.parents(self._filenode)]
415
415
416 r = self._filelog.renamed(self._filenode)
416 r = self._filelog.renamed(self._filenode)
417 if r:
417 if r:
418 pl[0] = (r[0], r[1], None)
418 pl[0] = (r[0], r[1], None)
419
419
420 return [filectx(self._repo, p, fileid=n, filelog=l)
420 return [filectx(self._repo, p, fileid=n, filelog=l)
421 for p, n, l in pl if n != nullid]
421 for p, n, l in pl if n != nullid]
422
422
423 def p1(self):
423 def p1(self):
424 return self.parents()[0]
424 return self.parents()[0]
425
425
426 def p2(self):
426 def p2(self):
427 p = self.parents()
427 p = self.parents()
428 if len(p) == 2:
428 if len(p) == 2:
429 return p[1]
429 return p[1]
430 return filectx(self._repo, self._path, fileid=-1, filelog=self._filelog)
430 return filectx(self._repo, self._path, fileid=-1, filelog=self._filelog)
431
431
432 def children(self):
432 def children(self):
433 # hard for renames
433 # hard for renames
434 c = self._filelog.children(self._filenode)
434 c = self._filelog.children(self._filenode)
435 return [filectx(self._repo, self._path, fileid=x,
435 return [filectx(self._repo, self._path, fileid=x,
436 filelog=self._filelog) for x in c]
436 filelog=self._filelog) for x in c]
437
437
438 def annotate(self, follow=False, linenumber=None, diffopts=None):
438 def annotate(self, follow=False, linenumber=None, diffopts=None):
439 '''returns a list of tuples of (ctx, line) for each line
439 '''returns a list of tuples of (ctx, line) for each line
440 in the file, where ctx is the filectx of the node where
440 in the file, where ctx is the filectx of the node where
441 that line was last changed.
441 that line was last changed.
442 This returns tuples of ((ctx, linenumber), line) for each line,
442 This returns tuples of ((ctx, linenumber), line) for each line,
443 if "linenumber" parameter is NOT "None".
443 if "linenumber" parameter is NOT "None".
444 In such tuples, linenumber means one at the first appearance
444 In such tuples, linenumber means one at the first appearance
445 in the managed file.
445 in the managed file.
446 To reduce annotation cost,
446 To reduce annotation cost,
447 this returns fixed value(False is used) as linenumber,
447 this returns fixed value(False is used) as linenumber,
448 if "linenumber" parameter is "False".'''
448 if "linenumber" parameter is "False".'''
449
449
450 def decorate_compat(text, rev):
450 def decorate_compat(text, rev):
451 return ([rev] * len(text.splitlines()), text)
451 return ([rev] * len(text.splitlines()), text)
452
452
453 def without_linenumber(text, rev):
453 def without_linenumber(text, rev):
454 return ([(rev, False)] * len(text.splitlines()), text)
454 return ([(rev, False)] * len(text.splitlines()), text)
455
455
456 def with_linenumber(text, rev):
456 def with_linenumber(text, rev):
457 size = len(text.splitlines())
457 size = len(text.splitlines())
458 return ([(rev, i) for i in xrange(1, size + 1)], text)
458 return ([(rev, i) for i in xrange(1, size + 1)], text)
459
459
460 decorate = (((linenumber is None) and decorate_compat) or
460 decorate = (((linenumber is None) and decorate_compat) or
461 (linenumber and with_linenumber) or
461 (linenumber and with_linenumber) or
462 without_linenumber)
462 without_linenumber)
463
463
464 def pair(parent, child):
464 def pair(parent, child):
465 blocks = mdiff.allblocks(parent[1], child[1], opts=diffopts,
465 blocks = mdiff.allblocks(parent[1], child[1], opts=diffopts,
466 refine=True)
466 refine=True)
467 for (a1, a2, b1, b2), t in blocks:
467 for (a1, a2, b1, b2), t in blocks:
468 # Changed blocks ('!') or blocks made only of blank lines ('~')
468 # Changed blocks ('!') or blocks made only of blank lines ('~')
469 # belong to the child.
469 # belong to the child.
470 if t == '=':
470 if t == '=':
471 child[0][b1:b2] = parent[0][a1:a2]
471 child[0][b1:b2] = parent[0][a1:a2]
472 return child
472 return child
473
473
474 getlog = util.lrucachefunc(lambda x: self._repo.file(x))
474 getlog = util.lrucachefunc(lambda x: self._repo.file(x))
475 def getctx(path, fileid):
475 def getctx(path, fileid):
476 log = path == self._path and self._filelog or getlog(path)
476 log = path == self._path and self._filelog or getlog(path)
477 return filectx(self._repo, path, fileid=fileid, filelog=log)
477 return filectx(self._repo, path, fileid=fileid, filelog=log)
478 getctx = util.lrucachefunc(getctx)
478 getctx = util.lrucachefunc(getctx)
479
479
480 def parents(f):
480 def parents(f):
481 # we want to reuse filectx objects as much as possible
481 # we want to reuse filectx objects as much as possible
482 p = f._path
482 p = f._path
483 if f._filerev is None: # working dir
483 if f._filerev is None: # working dir
484 pl = [(n.path(), n.filerev()) for n in f.parents()]
484 pl = [(n.path(), n.filerev()) for n in f.parents()]
485 else:
485 else:
486 pl = [(p, n) for n in f._filelog.parentrevs(f._filerev)]
486 pl = [(p, n) for n in f._filelog.parentrevs(f._filerev)]
487
487
488 if follow:
488 if follow:
489 r = f.renamed()
489 r = f.renamed()
490 if r:
490 if r:
491 pl[0] = (r[0], getlog(r[0]).rev(r[1]))
491 pl[0] = (r[0], getlog(r[0]).rev(r[1]))
492
492
493 return [getctx(p, n) for p, n in pl if n != nullrev]
493 return [getctx(p, n) for p, n in pl if n != nullrev]
494
494
495 # use linkrev to find the first changeset where self appeared
495 # use linkrev to find the first changeset where self appeared
496 if self.rev() != self.linkrev():
496 if self.rev() != self.linkrev():
497 base = self.filectx(self.filerev())
497 base = self.filectx(self.filerev())
498 else:
498 else:
499 base = self
499 base = self
500
500
501 # This algorithm would prefer to be recursive, but Python is a
501 # This algorithm would prefer to be recursive, but Python is a
502 # bit recursion-hostile. Instead we do an iterative
502 # bit recursion-hostile. Instead we do an iterative
503 # depth-first search.
503 # depth-first search.
504
504
505 visit = [base]
505 visit = [base]
506 hist = {}
506 hist = {}
507 pcache = {}
507 pcache = {}
508 needed = {base: 1}
508 needed = {base: 1}
509 while visit:
509 while visit:
510 f = visit[-1]
510 f = visit[-1]
511 if f not in pcache:
511 if f not in pcache:
512 pcache[f] = parents(f)
512 pcache[f] = parents(f)
513
513
514 ready = True
514 ready = True
515 pl = pcache[f]
515 pl = pcache[f]
516 for p in pl:
516 for p in pl:
517 if p not in hist:
517 if p not in hist:
518 ready = False
518 ready = False
519 visit.append(p)
519 visit.append(p)
520 needed[p] = needed.get(p, 0) + 1
520 needed[p] = needed.get(p, 0) + 1
521 if ready:
521 if ready:
522 visit.pop()
522 visit.pop()
523 curr = decorate(f.data(), f)
523 curr = decorate(f.data(), f)
524 for p in pl:
524 for p in pl:
525 curr = pair(hist[p], curr)
525 curr = pair(hist[p], curr)
526 if needed[p] == 1:
526 if needed[p] == 1:
527 del hist[p]
527 del hist[p]
528 else:
528 else:
529 needed[p] -= 1
529 needed[p] -= 1
530
530
531 hist[f] = curr
531 hist[f] = curr
532 pcache[f] = []
532 pcache[f] = []
533
533
534 return zip(hist[base][0], hist[base][1].splitlines(True))
534 return zip(hist[base][0], hist[base][1].splitlines(True))
535
535
536 def ancestor(self, fc2, actx=None):
536 def ancestor(self, fc2, actx=None):
537 """
537 """
538 find the common ancestor file context, if any, of self, and fc2
538 find the common ancestor file context, if any, of self, and fc2
539
539
540 If actx is given, it must be the changectx of the common ancestor
540 If actx is given, it must be the changectx of the common ancestor
541 of self's and fc2's respective changesets.
541 of self's and fc2's respective changesets.
542 """
542 """
543
543
544 if actx is None:
544 if actx is None:
545 actx = self.changectx().ancestor(fc2.changectx())
545 actx = self.changectx().ancestor(fc2.changectx())
546
546
547 # the trivial case: changesets are unrelated, files must be too
547 # the trivial case: changesets are unrelated, files must be too
548 if not actx:
548 if not actx:
549 return None
549 return None
550
550
551 # the easy case: no (relevant) renames
551 # the easy case: no (relevant) renames
552 if fc2.path() == self.path() and self.path() in actx:
552 if fc2.path() == self.path() and self.path() in actx:
553 return actx[self.path()]
553 return actx[self.path()]
554 acache = {}
554 acache = {}
555
555
556 # prime the ancestor cache for the working directory
556 # prime the ancestor cache for the working directory
557 for c in (self, fc2):
557 for c in (self, fc2):
558 if c._filerev is None:
558 if c._filerev is None:
559 pl = [(n.path(), n.filenode()) for n in c.parents()]
559 pl = [(n.path(), n.filenode()) for n in c.parents()]
560 acache[(c._path, None)] = pl
560 acache[(c._path, None)] = pl
561
561
562 flcache = {self._repopath:self._filelog, fc2._repopath:fc2._filelog}
562 flcache = {self._repopath:self._filelog, fc2._repopath:fc2._filelog}
563 def parents(vertex):
563 def parents(vertex):
564 if vertex in acache:
564 if vertex in acache:
565 return acache[vertex]
565 return acache[vertex]
566 f, n = vertex
566 f, n = vertex
567 if f not in flcache:
567 if f not in flcache:
568 flcache[f] = self._repo.file(f)
568 flcache[f] = self._repo.file(f)
569 fl = flcache[f]
569 fl = flcache[f]
570 pl = [(f, p) for p in fl.parents(n) if p != nullid]
570 pl = [(f, p) for p in fl.parents(n) if p != nullid]
571 re = fl.renamed(n)
571 re = fl.renamed(n)
572 if re:
572 if re:
573 pl.append(re)
573 pl.append(re)
574 acache[vertex] = pl
574 acache[vertex] = pl
575 return pl
575 return pl
576
576
577 a, b = (self._path, self._filenode), (fc2._path, fc2._filenode)
577 a, b = (self._path, self._filenode), (fc2._path, fc2._filenode)
578 v = ancestor.ancestor(a, b, parents)
578 v = ancestor.ancestor(a, b, parents)
579 if v:
579 if v:
580 f, n = v
580 f, n = v
581 return filectx(self._repo, f, fileid=n, filelog=flcache[f])
581 return filectx(self._repo, f, fileid=n, filelog=flcache[f])
582
582
583 return None
583 return None
584
584
585 def ancestors(self):
585 def ancestors(self):
586 visit = {}
586 visit = {}
587 c = self
587 c = self
588 while True:
588 while True:
589 for parent in c.parents():
589 for parent in c.parents():
590 visit[(parent.rev(), parent.node())] = parent
590 visit[(parent.rev(), parent.node())] = parent
591 if not visit:
591 if not visit:
592 break
592 break
593 c = visit.pop(max(visit))
593 c = visit.pop(max(visit))
594 yield c
594 yield c
595
595
596 class workingctx(changectx):
596 class workingctx(changectx):
597 """A workingctx object makes access to data related to
597 """A workingctx object makes access to data related to
598 the current working directory convenient.
598 the current working directory convenient.
599 date - any valid date string or (unixtime, offset), or None.
599 date - any valid date string or (unixtime, offset), or None.
600 user - username string, or None.
600 user - username string, or None.
601 extra - a dictionary of extra values, or None.
601 extra - a dictionary of extra values, or None.
602 changes - a list of file lists as returned by localrepo.status()
602 changes - a list of file lists as returned by localrepo.status()
603 or None to use the repository status.
603 or None to use the repository status.
604 """
604 """
605 def __init__(self, repo, text="", user=None, date=None, extra=None,
605 def __init__(self, repo, text="", user=None, date=None, extra=None,
606 changes=None):
606 changes=None):
607 self._repo = repo
607 self._repo = repo
608 self._rev = None
608 self._rev = None
609 self._node = None
609 self._node = None
610 self._text = text
610 self._text = text
611 if date:
611 if date:
612 self._date = util.parsedate(date)
612 self._date = util.parsedate(date)
613 if user:
613 if user:
614 self._user = user
614 self._user = user
615 if changes:
615 if changes:
616 self._status = list(changes[:4])
616 self._status = list(changes[:4])
617 self._unknown = changes[4]
617 self._unknown = changes[4]
618 self._ignored = changes[5]
618 self._ignored = changes[5]
619 self._clean = changes[6]
619 self._clean = changes[6]
620 else:
620 else:
621 self._unknown = None
621 self._unknown = None
622 self._ignored = None
622 self._ignored = None
623 self._clean = None
623 self._clean = None
624
624
625 self._extra = {}
625 self._extra = {}
626 if extra:
626 if extra:
627 self._extra = extra.copy()
627 self._extra = extra.copy()
628 if 'branch' not in self._extra:
628 if 'branch' not in self._extra:
629 try:
629 try:
630 branch = encoding.fromlocal(self._repo.dirstate.branch())
630 branch = encoding.fromlocal(self._repo.dirstate.branch())
631 except UnicodeDecodeError:
631 except UnicodeDecodeError:
632 raise util.Abort(_('branch name not in UTF-8!'))
632 raise util.Abort(_('branch name not in UTF-8!'))
633 self._extra['branch'] = branch
633 self._extra['branch'] = branch
634 if self._extra['branch'] == '':
634 if self._extra['branch'] == '':
635 self._extra['branch'] = 'default'
635 self._extra['branch'] = 'default'
636
636
637 def __str__(self):
637 def __str__(self):
638 return str(self._parents[0]) + "+"
638 return str(self._parents[0]) + "+"
639
639
640 def __repr__(self):
640 def __repr__(self):
641 return "<workingctx %s>" % str(self)
641 return "<workingctx %s>" % str(self)
642
642
643 def __nonzero__(self):
643 def __nonzero__(self):
644 return True
644 return True
645
645
646 def __contains__(self, key):
646 def __contains__(self, key):
647 return self._repo.dirstate[key] not in "?r"
647 return self._repo.dirstate[key] not in "?r"
648
648
649 def _buildflagfunc(self):
649 def _buildflagfunc(self):
650 # Create a fallback function for getting file flags when the
650 # Create a fallback function for getting file flags when the
651 # filesystem doesn't support them
651 # filesystem doesn't support them
652
652
653 copiesget = self._repo.dirstate.copies().get
653 copiesget = self._repo.dirstate.copies().get
654
654
655 if len(self._parents) < 2:
655 if len(self._parents) < 2:
656 # when we have one parent, it's easy: copy from parent
656 # when we have one parent, it's easy: copy from parent
657 man = self._parents[0].manifest()
657 man = self._parents[0].manifest()
658 def func(f):
658 def func(f):
659 f = copiesget(f, f)
659 f = copiesget(f, f)
660 return man.flags(f)
660 return man.flags(f)
661 else:
661 else:
662 # merges are tricky: we try to reconstruct the unstored
662 # merges are tricky: we try to reconstruct the unstored
663 # result from the merge (issue1802)
663 # result from the merge (issue1802)
664 p1, p2 = self._parents
664 p1, p2 = self._parents
665 pa = p1.ancestor(p2)
665 pa = p1.ancestor(p2)
666 m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest()
666 m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest()
667
667
668 def func(f):
668 def func(f):
669 f = copiesget(f, f) # may be wrong for merges with copies
669 f = copiesget(f, f) # may be wrong for merges with copies
670 fl1, fl2, fla = m1.flags(f), m2.flags(f), ma.flags(f)
670 fl1, fl2, fla = m1.flags(f), m2.flags(f), ma.flags(f)
671 if fl1 == fl2:
671 if fl1 == fl2:
672 return fl1
672 return fl1
673 if fl1 == fla:
673 if fl1 == fla:
674 return fl2
674 return fl2
675 if fl2 == fla:
675 if fl2 == fla:
676 return fl1
676 return fl1
677 return '' # punt for conflicts
677 return '' # punt for conflicts
678
678
679 return func
679 return func
680
680
681 @propertycache
681 @propertycache
682 def _flagfunc(self):
682 def _flagfunc(self):
683 return self._repo.dirstate.flagfunc(self._buildflagfunc)
683 return self._repo.dirstate.flagfunc(self._buildflagfunc)
684
684
685 @propertycache
685 @propertycache
686 def _manifest(self):
686 def _manifest(self):
687 """generate a manifest corresponding to the working directory"""
687 """generate a manifest corresponding to the working directory"""
688
688
689 if self._unknown is None:
689 if self._unknown is None:
690 self.status(unknown=True)
690 self.status(unknown=True)
691
691
692 man = self._parents[0].manifest().copy()
692 man = self._parents[0].manifest().copy()
693 if len(self._parents) > 1:
693 if len(self._parents) > 1:
694 man2 = self.p2().manifest()
694 man2 = self.p2().manifest()
695 def getman(f):
695 def getman(f):
696 if f in man:
696 if f in man:
697 return man
697 return man
698 return man2
698 return man2
699 else:
699 else:
700 getman = lambda f: man
700 getman = lambda f: man
701
701
702 copied = self._repo.dirstate.copies()
702 copied = self._repo.dirstate.copies()
703 ff = self._flagfunc
703 ff = self._flagfunc
704 modified, added, removed, deleted = self._status
704 modified, added, removed, deleted = self._status
705 unknown = self._unknown
705 unknown = self._unknown
706 for i, l in (("a", added), ("m", modified), ("u", unknown)):
706 for i, l in (("a", added), ("m", modified), ("u", unknown)):
707 for f in l:
707 for f in l:
708 orig = copied.get(f, f)
708 orig = copied.get(f, f)
709 man[f] = getman(orig).get(orig, nullid) + i
709 man[f] = getman(orig).get(orig, nullid) + i
710 try:
710 try:
711 man.set(f, ff(f))
711 man.set(f, ff(f))
712 except OSError:
712 except OSError:
713 pass
713 pass
714
714
715 for f in deleted + removed:
715 for f in deleted + removed:
716 if f in man:
716 if f in man:
717 del man[f]
717 del man[f]
718
718
719 return man
719 return man
720
720
721 def __iter__(self):
721 def __iter__(self):
722 d = self._repo.dirstate
722 d = self._repo.dirstate
723 for f in d:
723 for f in d:
724 if d[f] != 'r':
724 if d[f] != 'r':
725 yield f
725 yield f
726
726
727 @propertycache
727 @propertycache
728 def _status(self):
728 def _status(self):
729 return self._repo.status()[:4]
729 return self._repo.status()[:4]
730
730
731 @propertycache
731 @propertycache
732 def _user(self):
732 def _user(self):
733 return self._repo.ui.username()
733 return self._repo.ui.username()
734
734
735 @propertycache
735 @propertycache
736 def _date(self):
736 def _date(self):
737 return util.makedate()
737 return util.makedate()
738
738
739 @propertycache
739 @propertycache
740 def _parents(self):
740 def _parents(self):
741 p = self._repo.dirstate.parents()
741 p = self._repo.dirstate.parents()
742 if p[1] == nullid:
742 if p[1] == nullid:
743 p = p[:-1]
743 p = p[:-1]
744 self._parents = [changectx(self._repo, x) for x in p]
744 self._parents = [changectx(self._repo, x) for x in p]
745 return self._parents
745 return self._parents
746
746
747 def status(self, ignored=False, clean=False, unknown=False):
747 def status(self, ignored=False, clean=False, unknown=False):
748 """Explicit status query
748 """Explicit status query
749 Unless this method is used to query the working copy status, the
749 Unless this method is used to query the working copy status, the
750 _status property will implicitly read the status using its default
750 _status property will implicitly read the status using its default
751 arguments."""
751 arguments."""
752 stat = self._repo.status(ignored=ignored, clean=clean, unknown=unknown)
752 stat = self._repo.status(ignored=ignored, clean=clean, unknown=unknown)
753 self._unknown = self._ignored = self._clean = None
753 self._unknown = self._ignored = self._clean = None
754 if unknown:
754 if unknown:
755 self._unknown = stat[4]
755 self._unknown = stat[4]
756 if ignored:
756 if ignored:
757 self._ignored = stat[5]
757 self._ignored = stat[5]
758 if clean:
758 if clean:
759 self._clean = stat[6]
759 self._clean = stat[6]
760 self._status = stat[:4]
760 self._status = stat[:4]
761 return stat
761 return stat
762
762
763 def manifest(self):
763 def manifest(self):
764 return self._manifest
764 return self._manifest
765 def user(self):
765 def user(self):
766 return self._user or self._repo.ui.username()
766 return self._user or self._repo.ui.username()
767 def date(self):
767 def date(self):
768 return self._date
768 return self._date
769 def description(self):
769 def description(self):
770 return self._text
770 return self._text
771 def files(self):
771 def files(self):
772 return sorted(self._status[0] + self._status[1] + self._status[2])
772 return sorted(self._status[0] + self._status[1] + self._status[2])
773
773
774 def modified(self):
774 def modified(self):
775 return self._status[0]
775 return self._status[0]
776 def added(self):
776 def added(self):
777 return self._status[1]
777 return self._status[1]
778 def removed(self):
778 def removed(self):
779 return self._status[2]
779 return self._status[2]
780 def deleted(self):
780 def deleted(self):
781 return self._status[3]
781 return self._status[3]
782 def unknown(self):
782 def unknown(self):
783 assert self._unknown is not None # must call status first
783 assert self._unknown is not None # must call status first
784 return self._unknown
784 return self._unknown
785 def ignored(self):
785 def ignored(self):
786 assert self._ignored is not None # must call status first
786 assert self._ignored is not None # must call status first
787 return self._ignored
787 return self._ignored
788 def clean(self):
788 def clean(self):
789 assert self._clean is not None # must call status first
789 assert self._clean is not None # must call status first
790 return self._clean
790 return self._clean
791 def branch(self):
791 def branch(self):
792 return encoding.tolocal(self._extra['branch'])
792 return encoding.tolocal(self._extra['branch'])
793 def extra(self):
793 def extra(self):
794 return self._extra
794 return self._extra
795
795
796 def tags(self):
796 def tags(self):
797 t = []
797 t = []
798 for p in self.parents():
798 for p in self.parents():
799 t.extend(p.tags())
799 t.extend(p.tags())
800 return t
800 return t
801
801
802 def bookmarks(self):
802 def bookmarks(self):
803 b = []
803 b = []
804 for p in self.parents():
804 for p in self.parents():
805 b.extend(p.bookmarks())
805 b.extend(p.bookmarks())
806 return b
806 return b
807
807
808 def phase(self):
809 phase = 1 # default phase to draft
810 for p in self.parents():
811 phase = max(phase, p.phase())
812 return phase
813
814 def hidden(self):
815 return False
816
808 def children(self):
817 def children(self):
809 return []
818 return []
810
819
811 def flags(self, path):
820 def flags(self, path):
812 if '_manifest' in self.__dict__:
821 if '_manifest' in self.__dict__:
813 try:
822 try:
814 return self._manifest.flags(path)
823 return self._manifest.flags(path)
815 except KeyError:
824 except KeyError:
816 return ''
825 return ''
817
826
818 try:
827 try:
819 return self._flagfunc(path)
828 return self._flagfunc(path)
820 except OSError:
829 except OSError:
821 return ''
830 return ''
822
831
823 def filectx(self, path, filelog=None):
832 def filectx(self, path, filelog=None):
824 """get a file context from the working directory"""
833 """get a file context from the working directory"""
825 return workingfilectx(self._repo, path, workingctx=self,
834 return workingfilectx(self._repo, path, workingctx=self,
826 filelog=filelog)
835 filelog=filelog)
827
836
828 def ancestor(self, c2):
837 def ancestor(self, c2):
829 """return the ancestor context of self and c2"""
838 """return the ancestor context of self and c2"""
830 return self._parents[0].ancestor(c2) # punt on two parents for now
839 return self._parents[0].ancestor(c2) # punt on two parents for now
831
840
832 def walk(self, match):
841 def walk(self, match):
833 return sorted(self._repo.dirstate.walk(match, self.substate.keys(),
842 return sorted(self._repo.dirstate.walk(match, self.substate.keys(),
834 True, False))
843 True, False))
835
844
836 def dirty(self, missing=False):
845 def dirty(self, missing=False):
837 "check whether a working directory is modified"
846 "check whether a working directory is modified"
838 # check subrepos first
847 # check subrepos first
839 for s in self.substate:
848 for s in self.substate:
840 if self.sub(s).dirty():
849 if self.sub(s).dirty():
841 return True
850 return True
842 # check current working dir
851 # check current working dir
843 return (self.p2() or self.branch() != self.p1().branch() or
852 return (self.p2() or self.branch() != self.p1().branch() or
844 self.modified() or self.added() or self.removed() or
853 self.modified() or self.added() or self.removed() or
845 (missing and self.deleted()))
854 (missing and self.deleted()))
846
855
847 def add(self, list, prefix=""):
856 def add(self, list, prefix=""):
848 join = lambda f: os.path.join(prefix, f)
857 join = lambda f: os.path.join(prefix, f)
849 wlock = self._repo.wlock()
858 wlock = self._repo.wlock()
850 ui, ds = self._repo.ui, self._repo.dirstate
859 ui, ds = self._repo.ui, self._repo.dirstate
851 try:
860 try:
852 rejected = []
861 rejected = []
853 for f in list:
862 for f in list:
854 scmutil.checkportable(ui, join(f))
863 scmutil.checkportable(ui, join(f))
855 p = self._repo.wjoin(f)
864 p = self._repo.wjoin(f)
856 try:
865 try:
857 st = os.lstat(p)
866 st = os.lstat(p)
858 except OSError:
867 except OSError:
859 ui.warn(_("%s does not exist!\n") % join(f))
868 ui.warn(_("%s does not exist!\n") % join(f))
860 rejected.append(f)
869 rejected.append(f)
861 continue
870 continue
862 if st.st_size > 10000000:
871 if st.st_size > 10000000:
863 ui.warn(_("%s: up to %d MB of RAM may be required "
872 ui.warn(_("%s: up to %d MB of RAM may be required "
864 "to manage this file\n"
873 "to manage this file\n"
865 "(use 'hg revert %s' to cancel the "
874 "(use 'hg revert %s' to cancel the "
866 "pending addition)\n")
875 "pending addition)\n")
867 % (f, 3 * st.st_size // 1000000, join(f)))
876 % (f, 3 * st.st_size // 1000000, join(f)))
868 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
877 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
869 ui.warn(_("%s not added: only files and symlinks "
878 ui.warn(_("%s not added: only files and symlinks "
870 "supported currently\n") % join(f))
879 "supported currently\n") % join(f))
871 rejected.append(p)
880 rejected.append(p)
872 elif ds[f] in 'amn':
881 elif ds[f] in 'amn':
873 ui.warn(_("%s already tracked!\n") % join(f))
882 ui.warn(_("%s already tracked!\n") % join(f))
874 elif ds[f] == 'r':
883 elif ds[f] == 'r':
875 ds.normallookup(f)
884 ds.normallookup(f)
876 else:
885 else:
877 ds.add(f)
886 ds.add(f)
878 return rejected
887 return rejected
879 finally:
888 finally:
880 wlock.release()
889 wlock.release()
881
890
882 def forget(self, files):
891 def forget(self, files):
883 wlock = self._repo.wlock()
892 wlock = self._repo.wlock()
884 try:
893 try:
885 for f in files:
894 for f in files:
886 if self._repo.dirstate[f] != 'a':
895 if self._repo.dirstate[f] != 'a':
887 self._repo.dirstate.remove(f)
896 self._repo.dirstate.remove(f)
888 elif f not in self._repo.dirstate:
897 elif f not in self._repo.dirstate:
889 self._repo.ui.warn(_("%s not tracked!\n") % f)
898 self._repo.ui.warn(_("%s not tracked!\n") % f)
890 else:
899 else:
891 self._repo.dirstate.drop(f)
900 self._repo.dirstate.drop(f)
892 finally:
901 finally:
893 wlock.release()
902 wlock.release()
894
903
895 def ancestors(self):
904 def ancestors(self):
896 for a in self._repo.changelog.ancestors(
905 for a in self._repo.changelog.ancestors(
897 *[p.rev() for p in self._parents]):
906 *[p.rev() for p in self._parents]):
898 yield changectx(self._repo, a)
907 yield changectx(self._repo, a)
899
908
900 def undelete(self, list):
909 def undelete(self, list):
901 pctxs = self.parents()
910 pctxs = self.parents()
902 wlock = self._repo.wlock()
911 wlock = self._repo.wlock()
903 try:
912 try:
904 for f in list:
913 for f in list:
905 if self._repo.dirstate[f] != 'r':
914 if self._repo.dirstate[f] != 'r':
906 self._repo.ui.warn(_("%s not removed!\n") % f)
915 self._repo.ui.warn(_("%s not removed!\n") % f)
907 else:
916 else:
908 fctx = f in pctxs[0] and pctxs[0][f] or pctxs[1][f]
917 fctx = f in pctxs[0] and pctxs[0][f] or pctxs[1][f]
909 t = fctx.data()
918 t = fctx.data()
910 self._repo.wwrite(f, t, fctx.flags())
919 self._repo.wwrite(f, t, fctx.flags())
911 self._repo.dirstate.normal(f)
920 self._repo.dirstate.normal(f)
912 finally:
921 finally:
913 wlock.release()
922 wlock.release()
914
923
915 def copy(self, source, dest):
924 def copy(self, source, dest):
916 p = self._repo.wjoin(dest)
925 p = self._repo.wjoin(dest)
917 if not os.path.lexists(p):
926 if not os.path.lexists(p):
918 self._repo.ui.warn(_("%s does not exist!\n") % dest)
927 self._repo.ui.warn(_("%s does not exist!\n") % dest)
919 elif not (os.path.isfile(p) or os.path.islink(p)):
928 elif not (os.path.isfile(p) or os.path.islink(p)):
920 self._repo.ui.warn(_("copy failed: %s is not a file or a "
929 self._repo.ui.warn(_("copy failed: %s is not a file or a "
921 "symbolic link\n") % dest)
930 "symbolic link\n") % dest)
922 else:
931 else:
923 wlock = self._repo.wlock()
932 wlock = self._repo.wlock()
924 try:
933 try:
925 if self._repo.dirstate[dest] in '?r':
934 if self._repo.dirstate[dest] in '?r':
926 self._repo.dirstate.add(dest)
935 self._repo.dirstate.add(dest)
927 self._repo.dirstate.copy(source, dest)
936 self._repo.dirstate.copy(source, dest)
928 finally:
937 finally:
929 wlock.release()
938 wlock.release()
930
939
931 class workingfilectx(filectx):
940 class workingfilectx(filectx):
932 """A workingfilectx object makes access to data related to a particular
941 """A workingfilectx object makes access to data related to a particular
933 file in the working directory convenient."""
942 file in the working directory convenient."""
934 def __init__(self, repo, path, filelog=None, workingctx=None):
943 def __init__(self, repo, path, filelog=None, workingctx=None):
935 """changeid can be a changeset revision, node, or tag.
944 """changeid can be a changeset revision, node, or tag.
936 fileid can be a file revision or node."""
945 fileid can be a file revision or node."""
937 self._repo = repo
946 self._repo = repo
938 self._path = path
947 self._path = path
939 self._changeid = None
948 self._changeid = None
940 self._filerev = self._filenode = None
949 self._filerev = self._filenode = None
941
950
942 if filelog:
951 if filelog:
943 self._filelog = filelog
952 self._filelog = filelog
944 if workingctx:
953 if workingctx:
945 self._changectx = workingctx
954 self._changectx = workingctx
946
955
947 @propertycache
956 @propertycache
948 def _changectx(self):
957 def _changectx(self):
949 return workingctx(self._repo)
958 return workingctx(self._repo)
950
959
951 def __nonzero__(self):
960 def __nonzero__(self):
952 return True
961 return True
953
962
954 def __str__(self):
963 def __str__(self):
955 return "%s@%s" % (self.path(), self._changectx)
964 return "%s@%s" % (self.path(), self._changectx)
956
965
957 def __repr__(self):
966 def __repr__(self):
958 return "<workingfilectx %s>" % str(self)
967 return "<workingfilectx %s>" % str(self)
959
968
960 def data(self):
969 def data(self):
961 return self._repo.wread(self._path)
970 return self._repo.wread(self._path)
962 def renamed(self):
971 def renamed(self):
963 rp = self._repo.dirstate.copied(self._path)
972 rp = self._repo.dirstate.copied(self._path)
964 if not rp:
973 if not rp:
965 return None
974 return None
966 return rp, self._changectx._parents[0]._manifest.get(rp, nullid)
975 return rp, self._changectx._parents[0]._manifest.get(rp, nullid)
967
976
968 def parents(self):
977 def parents(self):
969 '''return parent filectxs, following copies if necessary'''
978 '''return parent filectxs, following copies if necessary'''
970 def filenode(ctx, path):
979 def filenode(ctx, path):
971 return ctx._manifest.get(path, nullid)
980 return ctx._manifest.get(path, nullid)
972
981
973 path = self._path
982 path = self._path
974 fl = self._filelog
983 fl = self._filelog
975 pcl = self._changectx._parents
984 pcl = self._changectx._parents
976 renamed = self.renamed()
985 renamed = self.renamed()
977
986
978 if renamed:
987 if renamed:
979 pl = [renamed + (None,)]
988 pl = [renamed + (None,)]
980 else:
989 else:
981 pl = [(path, filenode(pcl[0], path), fl)]
990 pl = [(path, filenode(pcl[0], path), fl)]
982
991
983 for pc in pcl[1:]:
992 for pc in pcl[1:]:
984 pl.append((path, filenode(pc, path), fl))
993 pl.append((path, filenode(pc, path), fl))
985
994
986 return [filectx(self._repo, p, fileid=n, filelog=l)
995 return [filectx(self._repo, p, fileid=n, filelog=l)
987 for p, n, l in pl if n != nullid]
996 for p, n, l in pl if n != nullid]
988
997
989 def children(self):
998 def children(self):
990 return []
999 return []
991
1000
992 def size(self):
1001 def size(self):
993 return os.lstat(self._repo.wjoin(self._path)).st_size
1002 return os.lstat(self._repo.wjoin(self._path)).st_size
994 def date(self):
1003 def date(self):
995 t, tz = self._changectx.date()
1004 t, tz = self._changectx.date()
996 try:
1005 try:
997 return (int(os.lstat(self._repo.wjoin(self._path)).st_mtime), tz)
1006 return (int(os.lstat(self._repo.wjoin(self._path)).st_mtime), tz)
998 except OSError, err:
1007 except OSError, err:
999 if err.errno != errno.ENOENT:
1008 if err.errno != errno.ENOENT:
1000 raise
1009 raise
1001 return (t, tz)
1010 return (t, tz)
1002
1011
1003 def cmp(self, fctx):
1012 def cmp(self, fctx):
1004 """compare with other file context
1013 """compare with other file context
1005
1014
1006 returns True if different than fctx.
1015 returns True if different than fctx.
1007 """
1016 """
1008 # fctx should be a filectx (not a wfctx)
1017 # fctx should be a filectx (not a wfctx)
1009 # invert comparison to reuse the same code path
1018 # invert comparison to reuse the same code path
1010 return fctx.cmp(self)
1019 return fctx.cmp(self)
1011
1020
1012 class memctx(object):
1021 class memctx(object):
1013 """Use memctx to perform in-memory commits via localrepo.commitctx().
1022 """Use memctx to perform in-memory commits via localrepo.commitctx().
1014
1023
1015 Revision information is supplied at initialization time while
1024 Revision information is supplied at initialization time while
1016 related files data and is made available through a callback
1025 related files data and is made available through a callback
1017 mechanism. 'repo' is the current localrepo, 'parents' is a
1026 mechanism. 'repo' is the current localrepo, 'parents' is a
1018 sequence of two parent revisions identifiers (pass None for every
1027 sequence of two parent revisions identifiers (pass None for every
1019 missing parent), 'text' is the commit message and 'files' lists
1028 missing parent), 'text' is the commit message and 'files' lists
1020 names of files touched by the revision (normalized and relative to
1029 names of files touched by the revision (normalized and relative to
1021 repository root).
1030 repository root).
1022
1031
1023 filectxfn(repo, memctx, path) is a callable receiving the
1032 filectxfn(repo, memctx, path) is a callable receiving the
1024 repository, the current memctx object and the normalized path of
1033 repository, the current memctx object and the normalized path of
1025 requested file, relative to repository root. It is fired by the
1034 requested file, relative to repository root. It is fired by the
1026 commit function for every file in 'files', but calls order is
1035 commit function for every file in 'files', but calls order is
1027 undefined. If the file is available in the revision being
1036 undefined. If the file is available in the revision being
1028 committed (updated or added), filectxfn returns a memfilectx
1037 committed (updated or added), filectxfn returns a memfilectx
1029 object. If the file was removed, filectxfn raises an
1038 object. If the file was removed, filectxfn raises an
1030 IOError. Moved files are represented by marking the source file
1039 IOError. Moved files are represented by marking the source file
1031 removed and the new file added with copy information (see
1040 removed and the new file added with copy information (see
1032 memfilectx).
1041 memfilectx).
1033
1042
1034 user receives the committer name and defaults to current
1043 user receives the committer name and defaults to current
1035 repository username, date is the commit date in any format
1044 repository username, date is the commit date in any format
1036 supported by util.parsedate() and defaults to current date, extra
1045 supported by util.parsedate() and defaults to current date, extra
1037 is a dictionary of metadata or is left empty.
1046 is a dictionary of metadata or is left empty.
1038 """
1047 """
1039 def __init__(self, repo, parents, text, files, filectxfn, user=None,
1048 def __init__(self, repo, parents, text, files, filectxfn, user=None,
1040 date=None, extra=None):
1049 date=None, extra=None):
1041 self._repo = repo
1050 self._repo = repo
1042 self._rev = None
1051 self._rev = None
1043 self._node = None
1052 self._node = None
1044 self._text = text
1053 self._text = text
1045 self._date = date and util.parsedate(date) or util.makedate()
1054 self._date = date and util.parsedate(date) or util.makedate()
1046 self._user = user
1055 self._user = user
1047 parents = [(p or nullid) for p in parents]
1056 parents = [(p or nullid) for p in parents]
1048 p1, p2 = parents
1057 p1, p2 = parents
1049 self._parents = [changectx(self._repo, p) for p in (p1, p2)]
1058 self._parents = [changectx(self._repo, p) for p in (p1, p2)]
1050 files = sorted(set(files))
1059 files = sorted(set(files))
1051 self._status = [files, [], [], [], []]
1060 self._status = [files, [], [], [], []]
1052 self._filectxfn = filectxfn
1061 self._filectxfn = filectxfn
1053
1062
1054 self._extra = extra and extra.copy() or {}
1063 self._extra = extra and extra.copy() or {}
1055 if self._extra.get('branch', '') == '':
1064 if self._extra.get('branch', '') == '':
1056 self._extra['branch'] = 'default'
1065 self._extra['branch'] = 'default'
1057
1066
1058 def __str__(self):
1067 def __str__(self):
1059 return str(self._parents[0]) + "+"
1068 return str(self._parents[0]) + "+"
1060
1069
1061 def __int__(self):
1070 def __int__(self):
1062 return self._rev
1071 return self._rev
1063
1072
1064 def __nonzero__(self):
1073 def __nonzero__(self):
1065 return True
1074 return True
1066
1075
1067 def __getitem__(self, key):
1076 def __getitem__(self, key):
1068 return self.filectx(key)
1077 return self.filectx(key)
1069
1078
1070 def p1(self):
1079 def p1(self):
1071 return self._parents[0]
1080 return self._parents[0]
1072 def p2(self):
1081 def p2(self):
1073 return self._parents[1]
1082 return self._parents[1]
1074
1083
1075 def user(self):
1084 def user(self):
1076 return self._user or self._repo.ui.username()
1085 return self._user or self._repo.ui.username()
1077 def date(self):
1086 def date(self):
1078 return self._date
1087 return self._date
1079 def description(self):
1088 def description(self):
1080 return self._text
1089 return self._text
1081 def files(self):
1090 def files(self):
1082 return self.modified()
1091 return self.modified()
1083 def modified(self):
1092 def modified(self):
1084 return self._status[0]
1093 return self._status[0]
1085 def added(self):
1094 def added(self):
1086 return self._status[1]
1095 return self._status[1]
1087 def removed(self):
1096 def removed(self):
1088 return self._status[2]
1097 return self._status[2]
1089 def deleted(self):
1098 def deleted(self):
1090 return self._status[3]
1099 return self._status[3]
1091 def unknown(self):
1100 def unknown(self):
1092 return self._status[4]
1101 return self._status[4]
1093 def ignored(self):
1102 def ignored(self):
1094 return self._status[5]
1103 return self._status[5]
1095 def clean(self):
1104 def clean(self):
1096 return self._status[6]
1105 return self._status[6]
1097 def branch(self):
1106 def branch(self):
1098 return encoding.tolocal(self._extra['branch'])
1107 return encoding.tolocal(self._extra['branch'])
1099 def extra(self):
1108 def extra(self):
1100 return self._extra
1109 return self._extra
1101 def flags(self, f):
1110 def flags(self, f):
1102 return self[f].flags()
1111 return self[f].flags()
1103
1112
1104 def parents(self):
1113 def parents(self):
1105 """return contexts for each parent changeset"""
1114 """return contexts for each parent changeset"""
1106 return self._parents
1115 return self._parents
1107
1116
1108 def filectx(self, path, filelog=None):
1117 def filectx(self, path, filelog=None):
1109 """get a file context from the working directory"""
1118 """get a file context from the working directory"""
1110 return self._filectxfn(self._repo, self, path)
1119 return self._filectxfn(self._repo, self, path)
1111
1120
1112 def commit(self):
1121 def commit(self):
1113 """commit context to the repo"""
1122 """commit context to the repo"""
1114 return self._repo.commitctx(self)
1123 return self._repo.commitctx(self)
1115
1124
1116 class memfilectx(object):
1125 class memfilectx(object):
1117 """memfilectx represents an in-memory file to commit.
1126 """memfilectx represents an in-memory file to commit.
1118
1127
1119 See memctx for more details.
1128 See memctx for more details.
1120 """
1129 """
1121 def __init__(self, path, data, islink=False, isexec=False, copied=None):
1130 def __init__(self, path, data, islink=False, isexec=False, copied=None):
1122 """
1131 """
1123 path is the normalized file path relative to repository root.
1132 path is the normalized file path relative to repository root.
1124 data is the file content as a string.
1133 data is the file content as a string.
1125 islink is True if the file is a symbolic link.
1134 islink is True if the file is a symbolic link.
1126 isexec is True if the file is executable.
1135 isexec is True if the file is executable.
1127 copied is the source file path if current file was copied in the
1136 copied is the source file path if current file was copied in the
1128 revision being committed, or None."""
1137 revision being committed, or None."""
1129 self._path = path
1138 self._path = path
1130 self._data = data
1139 self._data = data
1131 self._flags = (islink and 'l' or '') + (isexec and 'x' or '')
1140 self._flags = (islink and 'l' or '') + (isexec and 'x' or '')
1132 self._copied = None
1141 self._copied = None
1133 if copied:
1142 if copied:
1134 self._copied = (copied, nullid)
1143 self._copied = (copied, nullid)
1135
1144
1136 def __nonzero__(self):
1145 def __nonzero__(self):
1137 return True
1146 return True
1138 def __str__(self):
1147 def __str__(self):
1139 return "%s@%s" % (self.path(), self._changectx)
1148 return "%s@%s" % (self.path(), self._changectx)
1140 def path(self):
1149 def path(self):
1141 return self._path
1150 return self._path
1142 def data(self):
1151 def data(self):
1143 return self._data
1152 return self._data
1144 def flags(self):
1153 def flags(self):
1145 return self._flags
1154 return self._flags
1146 def isexec(self):
1155 def isexec(self):
1147 return 'x' in self._flags
1156 return 'x' in self._flags
1148 def islink(self):
1157 def islink(self):
1149 return 'l' in self._flags
1158 return 'l' in self._flags
1150 def renamed(self):
1159 def renamed(self):
1151 return self._copied
1160 return self._copied
General Comments 0
You need to be logged in to leave comments. Login now