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