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