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