##// END OF EJS Templates
context: fix workingctx's __contains__ method
Dirkjan Ochtman -
r6845:835a01a0 default
parent child Browse files
Show More
@@ -1,773 +1,773
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
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
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, revlog, util, os, errno
10 import ancestor, bdiff, revlog, util, os, errno
11
11
12 class changectx(object):
12 class changectx(object):
13 """A changecontext object makes access to data related to a particular
13 """A changecontext object makes access to data related to a particular
14 changeset convenient."""
14 changeset convenient."""
15 def __init__(self, repo, changeid=''):
15 def __init__(self, repo, changeid=''):
16 """changeid is a revision number, node, or tag"""
16 """changeid is a revision number, node, or tag"""
17 if changeid == '':
17 if changeid == '':
18 changeid = '.'
18 changeid = '.'
19 self._repo = repo
19 self._repo = repo
20 self._node = self._repo.lookup(changeid)
20 self._node = self._repo.lookup(changeid)
21 self._rev = self._repo.changelog.rev(self._node)
21 self._rev = self._repo.changelog.rev(self._node)
22
22
23 def __str__(self):
23 def __str__(self):
24 return short(self.node())
24 return short(self.node())
25
25
26 def __int__(self):
26 def __int__(self):
27 return self.rev()
27 return self.rev()
28
28
29 def __repr__(self):
29 def __repr__(self):
30 return "<changectx %s>" % str(self)
30 return "<changectx %s>" % str(self)
31
31
32 def __hash__(self):
32 def __hash__(self):
33 try:
33 try:
34 return hash(self._rev)
34 return hash(self._rev)
35 except AttributeError:
35 except AttributeError:
36 return id(self)
36 return id(self)
37
37
38 def __eq__(self, other):
38 def __eq__(self, other):
39 try:
39 try:
40 return self._rev == other._rev
40 return self._rev == other._rev
41 except AttributeError:
41 except AttributeError:
42 return False
42 return False
43
43
44 def __ne__(self, other):
44 def __ne__(self, other):
45 return not (self == other)
45 return not (self == other)
46
46
47 def __nonzero__(self):
47 def __nonzero__(self):
48 return self._rev != nullrev
48 return self._rev != nullrev
49
49
50 def __getattr__(self, name):
50 def __getattr__(self, name):
51 if name == '_changeset':
51 if name == '_changeset':
52 self._changeset = self._repo.changelog.read(self.node())
52 self._changeset = self._repo.changelog.read(self.node())
53 return self._changeset
53 return self._changeset
54 elif name == '_manifest':
54 elif name == '_manifest':
55 self._manifest = self._repo.manifest.read(self._changeset[0])
55 self._manifest = self._repo.manifest.read(self._changeset[0])
56 return self._manifest
56 return self._manifest
57 elif name == '_manifestdelta':
57 elif name == '_manifestdelta':
58 md = self._repo.manifest.readdelta(self._changeset[0])
58 md = self._repo.manifest.readdelta(self._changeset[0])
59 self._manifestdelta = md
59 self._manifestdelta = md
60 return self._manifestdelta
60 return self._manifestdelta
61 elif name == '_parents':
61 elif name == '_parents':
62 p = self._repo.changelog.parents(self._node)
62 p = self._repo.changelog.parents(self._node)
63 if p[1] == nullid:
63 if p[1] == nullid:
64 p = p[:-1]
64 p = p[:-1]
65 self._parents = [changectx(self._repo, x) for x in p]
65 self._parents = [changectx(self._repo, x) for x in p]
66 return self._parents
66 return self._parents
67 else:
67 else:
68 raise AttributeError, name
68 raise AttributeError, name
69
69
70 def __contains__(self, key):
70 def __contains__(self, key):
71 return key in self._manifest
71 return key in self._manifest
72
72
73 def __getitem__(self, key):
73 def __getitem__(self, key):
74 return self.filectx(key)
74 return self.filectx(key)
75
75
76 def __iter__(self):
76 def __iter__(self):
77 for f in util.sort(self._manifest):
77 for f in util.sort(self._manifest):
78 yield f
78 yield f
79
79
80 def changeset(self): return self._changeset
80 def changeset(self): return self._changeset
81 def manifest(self): return self._manifest
81 def manifest(self): return self._manifest
82
82
83 def rev(self): return self._rev
83 def rev(self): return self._rev
84 def node(self): return self._node
84 def node(self): return self._node
85 def hex(self): return hex(self._node)
85 def hex(self): return hex(self._node)
86 def user(self): return self._changeset[1]
86 def user(self): return self._changeset[1]
87 def date(self): return self._changeset[2]
87 def date(self): return self._changeset[2]
88 def files(self): return self._changeset[3]
88 def files(self): return self._changeset[3]
89 def description(self): return self._changeset[4]
89 def description(self): return self._changeset[4]
90 def branch(self): return self._changeset[5].get("branch")
90 def branch(self): return self._changeset[5].get("branch")
91 def extra(self): return self._changeset[5]
91 def extra(self): return self._changeset[5]
92 def tags(self): return self._repo.nodetags(self._node)
92 def tags(self): return self._repo.nodetags(self._node)
93
93
94 def parents(self):
94 def parents(self):
95 """return contexts for each parent changeset"""
95 """return contexts for each parent changeset"""
96 return self._parents
96 return self._parents
97
97
98 def children(self):
98 def children(self):
99 """return contexts for each child changeset"""
99 """return contexts for each child changeset"""
100 c = self._repo.changelog.children(self._node)
100 c = self._repo.changelog.children(self._node)
101 return [changectx(self._repo, x) for x in c]
101 return [changectx(self._repo, x) for x in c]
102
102
103 def _fileinfo(self, path):
103 def _fileinfo(self, path):
104 if '_manifest' in self.__dict__:
104 if '_manifest' in self.__dict__:
105 try:
105 try:
106 return self._manifest[path], self._manifest.flags(path)
106 return self._manifest[path], self._manifest.flags(path)
107 except KeyError:
107 except KeyError:
108 raise revlog.LookupError(self._node, path,
108 raise revlog.LookupError(self._node, path,
109 _('not found in manifest'))
109 _('not found in manifest'))
110 if '_manifestdelta' in self.__dict__ or path in self.files():
110 if '_manifestdelta' in self.__dict__ or path in self.files():
111 if path in self._manifestdelta:
111 if path in self._manifestdelta:
112 return self._manifestdelta[path], self._manifestdelta.flags(path)
112 return self._manifestdelta[path], self._manifestdelta.flags(path)
113 node, flag = self._repo.manifest.find(self._changeset[0], path)
113 node, flag = self._repo.manifest.find(self._changeset[0], path)
114 if not node:
114 if not node:
115 raise revlog.LookupError(self._node, path,
115 raise revlog.LookupError(self._node, path,
116 _('not found in manifest'))
116 _('not found in manifest'))
117
117
118 return node, flag
118 return node, flag
119
119
120 def filenode(self, path):
120 def filenode(self, path):
121 return self._fileinfo(path)[0]
121 return self._fileinfo(path)[0]
122
122
123 def flags(self, path):
123 def flags(self, path):
124 try:
124 try:
125 return self._fileinfo(path)[1]
125 return self._fileinfo(path)[1]
126 except revlog.LookupError:
126 except revlog.LookupError:
127 return ''
127 return ''
128
128
129 def filectx(self, path, fileid=None, filelog=None):
129 def filectx(self, path, fileid=None, filelog=None):
130 """get a file context from this changeset"""
130 """get a file context from this changeset"""
131 if fileid is None:
131 if fileid is None:
132 fileid = self.filenode(path)
132 fileid = self.filenode(path)
133 return filectx(self._repo, path, fileid=fileid,
133 return filectx(self._repo, path, fileid=fileid,
134 changectx=self, filelog=filelog)
134 changectx=self, filelog=filelog)
135
135
136 def filectxs(self):
136 def filectxs(self):
137 """generate a file context for each file in this changeset's
137 """generate a file context for each file in this changeset's
138 manifest"""
138 manifest"""
139 for f in util.sort(mf):
139 for f in util.sort(mf):
140 yield self.filectx(f, fileid=mf[f])
140 yield self.filectx(f, fileid=mf[f])
141
141
142 def ancestor(self, c2):
142 def ancestor(self, c2):
143 """
143 """
144 return the ancestor context of self and c2
144 return the ancestor context of self and c2
145 """
145 """
146 n = self._repo.changelog.ancestor(self._node, c2._node)
146 n = self._repo.changelog.ancestor(self._node, c2._node)
147 return changectx(self._repo, n)
147 return changectx(self._repo, n)
148
148
149 def walk(self, match):
149 def walk(self, match):
150 fdict = dict.fromkeys(match.files())
150 fdict = dict.fromkeys(match.files())
151 # for dirstate.walk, files=['.'] means "walk the whole tree".
151 # for dirstate.walk, files=['.'] means "walk the whole tree".
152 # follow that here, too
152 # follow that here, too
153 fdict.pop('.', None)
153 fdict.pop('.', None)
154 for fn in self:
154 for fn in self:
155 for ffn in fdict:
155 for ffn in fdict:
156 # match if the file is the exact name or a directory
156 # match if the file is the exact name or a directory
157 if ffn == fn or fn.startswith("%s/" % ffn):
157 if ffn == fn or fn.startswith("%s/" % ffn):
158 del fdict[ffn]
158 del fdict[ffn]
159 break
159 break
160 if match(fn):
160 if match(fn):
161 yield fn
161 yield fn
162 for fn in util.sort(fdict):
162 for fn in util.sort(fdict):
163 if match.bad(fn, 'No such file in rev ' + str(self)) and match(fn):
163 if match.bad(fn, 'No such file in rev ' + str(self)) and match(fn):
164 yield fn
164 yield fn
165
165
166 class filectx(object):
166 class filectx(object):
167 """A filecontext object makes access to data related to a particular
167 """A filecontext object makes access to data related to a particular
168 filerevision convenient."""
168 filerevision convenient."""
169 def __init__(self, repo, path, changeid=None, fileid=None,
169 def __init__(self, repo, path, changeid=None, fileid=None,
170 filelog=None, changectx=None):
170 filelog=None, changectx=None):
171 """changeid can be a changeset revision, node, or tag.
171 """changeid can be a changeset revision, node, or tag.
172 fileid can be a file revision or node."""
172 fileid can be a file revision or node."""
173 self._repo = repo
173 self._repo = repo
174 self._path = path
174 self._path = path
175
175
176 assert (changeid is not None
176 assert (changeid is not None
177 or fileid is not None
177 or fileid is not None
178 or changectx is not None)
178 or changectx is not None)
179
179
180 if filelog:
180 if filelog:
181 self._filelog = filelog
181 self._filelog = filelog
182
182
183 if changeid is not None:
183 if changeid is not None:
184 self._changeid = changeid
184 self._changeid = changeid
185 if changectx is not None:
185 if changectx is not None:
186 self._changectx = changectx
186 self._changectx = changectx
187 if fileid is not None:
187 if fileid is not None:
188 self._fileid = fileid
188 self._fileid = fileid
189
189
190 def __getattr__(self, name):
190 def __getattr__(self, name):
191 if name == '_changectx':
191 if name == '_changectx':
192 self._changectx = changectx(self._repo, self._changeid)
192 self._changectx = changectx(self._repo, self._changeid)
193 return self._changectx
193 return self._changectx
194 elif name == '_filelog':
194 elif name == '_filelog':
195 self._filelog = self._repo.file(self._path)
195 self._filelog = self._repo.file(self._path)
196 return self._filelog
196 return self._filelog
197 elif name == '_changeid':
197 elif name == '_changeid':
198 if '_changectx' in self.__dict__:
198 if '_changectx' in self.__dict__:
199 self._changeid = self._changectx.rev()
199 self._changeid = self._changectx.rev()
200 else:
200 else:
201 self._changeid = self._filelog.linkrev(self._filenode)
201 self._changeid = self._filelog.linkrev(self._filenode)
202 return self._changeid
202 return self._changeid
203 elif name == '_filenode':
203 elif name == '_filenode':
204 if '_fileid' in self.__dict__:
204 if '_fileid' in self.__dict__:
205 self._filenode = self._filelog.lookup(self._fileid)
205 self._filenode = self._filelog.lookup(self._fileid)
206 else:
206 else:
207 self._filenode = self._changectx.filenode(self._path)
207 self._filenode = self._changectx.filenode(self._path)
208 return self._filenode
208 return self._filenode
209 elif name == '_filerev':
209 elif name == '_filerev':
210 self._filerev = self._filelog.rev(self._filenode)
210 self._filerev = self._filelog.rev(self._filenode)
211 return self._filerev
211 return self._filerev
212 elif name == '_repopath':
212 elif name == '_repopath':
213 self._repopath = self._path
213 self._repopath = self._path
214 return self._repopath
214 return self._repopath
215 else:
215 else:
216 raise AttributeError, name
216 raise AttributeError, name
217
217
218 def __nonzero__(self):
218 def __nonzero__(self):
219 try:
219 try:
220 n = self._filenode
220 n = self._filenode
221 return True
221 return True
222 except revlog.LookupError:
222 except revlog.LookupError:
223 # file is missing
223 # file is missing
224 return False
224 return False
225
225
226 def __str__(self):
226 def __str__(self):
227 return "%s@%s" % (self.path(), short(self.node()))
227 return "%s@%s" % (self.path(), short(self.node()))
228
228
229 def __repr__(self):
229 def __repr__(self):
230 return "<filectx %s>" % str(self)
230 return "<filectx %s>" % str(self)
231
231
232 def __hash__(self):
232 def __hash__(self):
233 try:
233 try:
234 return hash((self._path, self._fileid))
234 return hash((self._path, self._fileid))
235 except AttributeError:
235 except AttributeError:
236 return id(self)
236 return id(self)
237
237
238 def __eq__(self, other):
238 def __eq__(self, other):
239 try:
239 try:
240 return (self._path == other._path
240 return (self._path == other._path
241 and self._fileid == other._fileid)
241 and self._fileid == other._fileid)
242 except AttributeError:
242 except AttributeError:
243 return False
243 return False
244
244
245 def __ne__(self, other):
245 def __ne__(self, other):
246 return not (self == other)
246 return not (self == other)
247
247
248 def filectx(self, fileid):
248 def filectx(self, fileid):
249 '''opens an arbitrary revision of the file without
249 '''opens an arbitrary revision of the file without
250 opening a new filelog'''
250 opening a new filelog'''
251 return filectx(self._repo, self._path, fileid=fileid,
251 return filectx(self._repo, self._path, fileid=fileid,
252 filelog=self._filelog)
252 filelog=self._filelog)
253
253
254 def filerev(self): return self._filerev
254 def filerev(self): return self._filerev
255 def filenode(self): return self._filenode
255 def filenode(self): return self._filenode
256 def flags(self): return self._changectx.flags(self._path)
256 def flags(self): return self._changectx.flags(self._path)
257 def filelog(self): return self._filelog
257 def filelog(self): return self._filelog
258
258
259 def rev(self):
259 def rev(self):
260 if '_changectx' in self.__dict__:
260 if '_changectx' in self.__dict__:
261 return self._changectx.rev()
261 return self._changectx.rev()
262 if '_changeid' in self.__dict__:
262 if '_changeid' in self.__dict__:
263 return self._changectx.rev()
263 return self._changectx.rev()
264 return self._filelog.linkrev(self._filenode)
264 return self._filelog.linkrev(self._filenode)
265
265
266 def linkrev(self): return self._filelog.linkrev(self._filenode)
266 def linkrev(self): return self._filelog.linkrev(self._filenode)
267 def node(self): return self._changectx.node()
267 def node(self): return self._changectx.node()
268 def user(self): return self._changectx.user()
268 def user(self): return self._changectx.user()
269 def date(self): return self._changectx.date()
269 def date(self): return self._changectx.date()
270 def files(self): return self._changectx.files()
270 def files(self): return self._changectx.files()
271 def description(self): return self._changectx.description()
271 def description(self): return self._changectx.description()
272 def branch(self): return self._changectx.branch()
272 def branch(self): return self._changectx.branch()
273 def manifest(self): return self._changectx.manifest()
273 def manifest(self): return self._changectx.manifest()
274 def changectx(self): return self._changectx
274 def changectx(self): return self._changectx
275
275
276 def data(self): return self._filelog.read(self._filenode)
276 def data(self): return self._filelog.read(self._filenode)
277 def path(self): return self._path
277 def path(self): return self._path
278 def size(self): return self._filelog.size(self._filerev)
278 def size(self): return self._filelog.size(self._filerev)
279
279
280 def cmp(self, text): return self._filelog.cmp(self._filenode, text)
280 def cmp(self, text): return self._filelog.cmp(self._filenode, text)
281
281
282 def renamed(self):
282 def renamed(self):
283 """check if file was actually renamed in this changeset revision
283 """check if file was actually renamed in this changeset revision
284
284
285 If rename logged in file revision, we report copy for changeset only
285 If rename logged in file revision, we report copy for changeset only
286 if file revisions linkrev points back to the changeset in question
286 if file revisions linkrev points back to the changeset in question
287 or both changeset parents contain different file revisions.
287 or both changeset parents contain different file revisions.
288 """
288 """
289
289
290 renamed = self._filelog.renamed(self._filenode)
290 renamed = self._filelog.renamed(self._filenode)
291 if not renamed:
291 if not renamed:
292 return renamed
292 return renamed
293
293
294 if self.rev() == self.linkrev():
294 if self.rev() == self.linkrev():
295 return renamed
295 return renamed
296
296
297 name = self.path()
297 name = self.path()
298 fnode = self._filenode
298 fnode = self._filenode
299 for p in self._changectx.parents():
299 for p in self._changectx.parents():
300 try:
300 try:
301 if fnode == p.filenode(name):
301 if fnode == p.filenode(name):
302 return None
302 return None
303 except revlog.LookupError:
303 except revlog.LookupError:
304 pass
304 pass
305 return renamed
305 return renamed
306
306
307 def parents(self):
307 def parents(self):
308 p = self._path
308 p = self._path
309 fl = self._filelog
309 fl = self._filelog
310 pl = [(p, n, fl) for n in self._filelog.parents(self._filenode)]
310 pl = [(p, n, fl) for n in self._filelog.parents(self._filenode)]
311
311
312 r = self._filelog.renamed(self._filenode)
312 r = self._filelog.renamed(self._filenode)
313 if r:
313 if r:
314 pl[0] = (r[0], r[1], None)
314 pl[0] = (r[0], r[1], None)
315
315
316 return [filectx(self._repo, p, fileid=n, filelog=l)
316 return [filectx(self._repo, p, fileid=n, filelog=l)
317 for p,n,l in pl if n != nullid]
317 for p,n,l in pl if n != nullid]
318
318
319 def children(self):
319 def children(self):
320 # hard for renames
320 # hard for renames
321 c = self._filelog.children(self._filenode)
321 c = self._filelog.children(self._filenode)
322 return [filectx(self._repo, self._path, fileid=x,
322 return [filectx(self._repo, self._path, fileid=x,
323 filelog=self._filelog) for x in c]
323 filelog=self._filelog) for x in c]
324
324
325 def annotate(self, follow=False, linenumber=None):
325 def annotate(self, follow=False, linenumber=None):
326 '''returns a list of tuples of (ctx, line) for each line
326 '''returns a list of tuples of (ctx, line) for each line
327 in the file, where ctx is the filectx of the node where
327 in the file, where ctx is the filectx of the node where
328 that line was last changed.
328 that line was last changed.
329 This returns tuples of ((ctx, linenumber), line) for each line,
329 This returns tuples of ((ctx, linenumber), line) for each line,
330 if "linenumber" parameter is NOT "None".
330 if "linenumber" parameter is NOT "None".
331 In such tuples, linenumber means one at the first appearance
331 In such tuples, linenumber means one at the first appearance
332 in the managed file.
332 in the managed file.
333 To reduce annotation cost,
333 To reduce annotation cost,
334 this returns fixed value(False is used) as linenumber,
334 this returns fixed value(False is used) as linenumber,
335 if "linenumber" parameter is "False".'''
335 if "linenumber" parameter is "False".'''
336
336
337 def decorate_compat(text, rev):
337 def decorate_compat(text, rev):
338 return ([rev] * len(text.splitlines()), text)
338 return ([rev] * len(text.splitlines()), text)
339
339
340 def without_linenumber(text, rev):
340 def without_linenumber(text, rev):
341 return ([(rev, False)] * len(text.splitlines()), text)
341 return ([(rev, False)] * len(text.splitlines()), text)
342
342
343 def with_linenumber(text, rev):
343 def with_linenumber(text, rev):
344 size = len(text.splitlines())
344 size = len(text.splitlines())
345 return ([(rev, i) for i in xrange(1, size + 1)], text)
345 return ([(rev, i) for i in xrange(1, size + 1)], text)
346
346
347 decorate = (((linenumber is None) and decorate_compat) or
347 decorate = (((linenumber is None) and decorate_compat) or
348 (linenumber and with_linenumber) or
348 (linenumber and with_linenumber) or
349 without_linenumber)
349 without_linenumber)
350
350
351 def pair(parent, child):
351 def pair(parent, child):
352 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
352 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
353 child[0][b1:b2] = parent[0][a1:a2]
353 child[0][b1:b2] = parent[0][a1:a2]
354 return child
354 return child
355
355
356 getlog = util.cachefunc(lambda x: self._repo.file(x))
356 getlog = util.cachefunc(lambda x: self._repo.file(x))
357 def getctx(path, fileid):
357 def getctx(path, fileid):
358 log = path == self._path and self._filelog or getlog(path)
358 log = path == self._path and self._filelog or getlog(path)
359 return filectx(self._repo, path, fileid=fileid, filelog=log)
359 return filectx(self._repo, path, fileid=fileid, filelog=log)
360 getctx = util.cachefunc(getctx)
360 getctx = util.cachefunc(getctx)
361
361
362 def parents(f):
362 def parents(f):
363 # we want to reuse filectx objects as much as possible
363 # we want to reuse filectx objects as much as possible
364 p = f._path
364 p = f._path
365 if f._filerev is None: # working dir
365 if f._filerev is None: # working dir
366 pl = [(n.path(), n.filerev()) for n in f.parents()]
366 pl = [(n.path(), n.filerev()) for n in f.parents()]
367 else:
367 else:
368 pl = [(p, n) for n in f._filelog.parentrevs(f._filerev)]
368 pl = [(p, n) for n in f._filelog.parentrevs(f._filerev)]
369
369
370 if follow:
370 if follow:
371 r = f.renamed()
371 r = f.renamed()
372 if r:
372 if r:
373 pl[0] = (r[0], getlog(r[0]).rev(r[1]))
373 pl[0] = (r[0], getlog(r[0]).rev(r[1]))
374
374
375 return [getctx(p, n) for p, n in pl if n != nullrev]
375 return [getctx(p, n) for p, n in pl if n != nullrev]
376
376
377 # use linkrev to find the first changeset where self appeared
377 # use linkrev to find the first changeset where self appeared
378 if self.rev() != self.linkrev():
378 if self.rev() != self.linkrev():
379 base = self.filectx(self.filerev())
379 base = self.filectx(self.filerev())
380 else:
380 else:
381 base = self
381 base = self
382
382
383 # find all ancestors
383 # find all ancestors
384 needed = {base: 1}
384 needed = {base: 1}
385 visit = [base]
385 visit = [base]
386 files = [base._path]
386 files = [base._path]
387 while visit:
387 while visit:
388 f = visit.pop(0)
388 f = visit.pop(0)
389 for p in parents(f):
389 for p in parents(f):
390 if p not in needed:
390 if p not in needed:
391 needed[p] = 1
391 needed[p] = 1
392 visit.append(p)
392 visit.append(p)
393 if p._path not in files:
393 if p._path not in files:
394 files.append(p._path)
394 files.append(p._path)
395 else:
395 else:
396 # count how many times we'll use this
396 # count how many times we'll use this
397 needed[p] += 1
397 needed[p] += 1
398
398
399 # sort by revision (per file) which is a topological order
399 # sort by revision (per file) which is a topological order
400 visit = []
400 visit = []
401 for f in files:
401 for f in files:
402 fn = [(n.rev(), n) for n in needed if n._path == f]
402 fn = [(n.rev(), n) for n in needed if n._path == f]
403 visit.extend(fn)
403 visit.extend(fn)
404
404
405 hist = {}
405 hist = {}
406 for r, f in util.sort(visit):
406 for r, f in util.sort(visit):
407 curr = decorate(f.data(), f)
407 curr = decorate(f.data(), f)
408 for p in parents(f):
408 for p in parents(f):
409 if p != nullid:
409 if p != nullid:
410 curr = pair(hist[p], curr)
410 curr = pair(hist[p], curr)
411 # trim the history of unneeded revs
411 # trim the history of unneeded revs
412 needed[p] -= 1
412 needed[p] -= 1
413 if not needed[p]:
413 if not needed[p]:
414 del hist[p]
414 del hist[p]
415 hist[f] = curr
415 hist[f] = curr
416
416
417 return zip(hist[f][0], hist[f][1].splitlines(1))
417 return zip(hist[f][0], hist[f][1].splitlines(1))
418
418
419 def ancestor(self, fc2):
419 def ancestor(self, fc2):
420 """
420 """
421 find the common ancestor file context, if any, of self, and fc2
421 find the common ancestor file context, if any, of self, and fc2
422 """
422 """
423
423
424 acache = {}
424 acache = {}
425
425
426 # prime the ancestor cache for the working directory
426 # prime the ancestor cache for the working directory
427 for c in (self, fc2):
427 for c in (self, fc2):
428 if c._filerev == None:
428 if c._filerev == None:
429 pl = [(n.path(), n.filenode()) for n in c.parents()]
429 pl = [(n.path(), n.filenode()) for n in c.parents()]
430 acache[(c._path, None)] = pl
430 acache[(c._path, None)] = pl
431
431
432 flcache = {self._repopath:self._filelog, fc2._repopath:fc2._filelog}
432 flcache = {self._repopath:self._filelog, fc2._repopath:fc2._filelog}
433 def parents(vertex):
433 def parents(vertex):
434 if vertex in acache:
434 if vertex in acache:
435 return acache[vertex]
435 return acache[vertex]
436 f, n = vertex
436 f, n = vertex
437 if f not in flcache:
437 if f not in flcache:
438 flcache[f] = self._repo.file(f)
438 flcache[f] = self._repo.file(f)
439 fl = flcache[f]
439 fl = flcache[f]
440 pl = [(f, p) for p in fl.parents(n) if p != nullid]
440 pl = [(f, p) for p in fl.parents(n) if p != nullid]
441 re = fl.renamed(n)
441 re = fl.renamed(n)
442 if re:
442 if re:
443 pl.append(re)
443 pl.append(re)
444 acache[vertex] = pl
444 acache[vertex] = pl
445 return pl
445 return pl
446
446
447 a, b = (self._path, self._filenode), (fc2._path, fc2._filenode)
447 a, b = (self._path, self._filenode), (fc2._path, fc2._filenode)
448 v = ancestor.ancestor(a, b, parents)
448 v = ancestor.ancestor(a, b, parents)
449 if v:
449 if v:
450 f, n = v
450 f, n = v
451 return filectx(self._repo, f, fileid=n, filelog=flcache[f])
451 return filectx(self._repo, f, fileid=n, filelog=flcache[f])
452
452
453 return None
453 return None
454
454
455 class workingctx(changectx):
455 class workingctx(changectx):
456 """A workingctx object makes access to data related to
456 """A workingctx object makes access to data related to
457 the current working directory convenient.
457 the current working directory convenient.
458 parents - a pair of parent nodeids, or None to use the dirstate.
458 parents - a pair of parent nodeids, or None to use the dirstate.
459 date - any valid date string or (unixtime, offset), or None.
459 date - any valid date string or (unixtime, offset), or None.
460 user - username string, or None.
460 user - username string, or None.
461 extra - a dictionary of extra values, or None.
461 extra - a dictionary of extra values, or None.
462 changes - a list of file lists as returned by localrepo.status()
462 changes - a list of file lists as returned by localrepo.status()
463 or None to use the repository status.
463 or None to use the repository status.
464 """
464 """
465 def __init__(self, repo, parents=None, text="", user=None, date=None,
465 def __init__(self, repo, parents=None, text="", user=None, date=None,
466 extra=None, changes=None):
466 extra=None, changes=None):
467 self._repo = repo
467 self._repo = repo
468 self._rev = None
468 self._rev = None
469 self._node = None
469 self._node = None
470 self._text = text
470 self._text = text
471 if date:
471 if date:
472 self._date = util.parsedate(date)
472 self._date = util.parsedate(date)
473 if user:
473 if user:
474 self._user = user
474 self._user = user
475 if parents:
475 if parents:
476 self._parents = [changectx(self._repo, p) for p in parents]
476 self._parents = [changectx(self._repo, p) for p in parents]
477 if changes:
477 if changes:
478 self._status = list(changes)
478 self._status = list(changes)
479
479
480 self._extra = {}
480 self._extra = {}
481 if extra:
481 if extra:
482 self._extra = extra.copy()
482 self._extra = extra.copy()
483 if 'branch' not in self._extra:
483 if 'branch' not in self._extra:
484 branch = self._repo.dirstate.branch()
484 branch = self._repo.dirstate.branch()
485 try:
485 try:
486 branch = branch.decode('UTF-8').encode('UTF-8')
486 branch = branch.decode('UTF-8').encode('UTF-8')
487 except UnicodeDecodeError:
487 except UnicodeDecodeError:
488 raise util.Abort(_('branch name not in UTF-8!'))
488 raise util.Abort(_('branch name not in UTF-8!'))
489 self._extra['branch'] = branch
489 self._extra['branch'] = branch
490 if self._extra['branch'] == '':
490 if self._extra['branch'] == '':
491 self._extra['branch'] = 'default'
491 self._extra['branch'] = 'default'
492
492
493 def __str__(self):
493 def __str__(self):
494 return str(self._parents[0]) + "+"
494 return str(self._parents[0]) + "+"
495
495
496 def __nonzero__(self):
496 def __nonzero__(self):
497 return True
497 return True
498
498
499 def __contains__(self, key):
499 def __contains__(self, key):
500 return self._dirstate[f] not in "?r"
500 return self._dirstate[key] not in "?r"
501
501
502 def __getattr__(self, name):
502 def __getattr__(self, name):
503 if name == '_status':
503 if name == '_status':
504 self._status = self._repo.status(unknown=True)
504 self._status = self._repo.status(unknown=True)
505 return self._status
505 return self._status
506 elif name == '_user':
506 elif name == '_user':
507 self._user = self._repo.ui.username()
507 self._user = self._repo.ui.username()
508 return self._user
508 return self._user
509 elif name == '_date':
509 elif name == '_date':
510 self._date = util.makedate()
510 self._date = util.makedate()
511 return self._date
511 return self._date
512 if name == '_manifest':
512 if name == '_manifest':
513 self._buildmanifest()
513 self._buildmanifest()
514 return self._manifest
514 return self._manifest
515 elif name == '_parents':
515 elif name == '_parents':
516 p = self._repo.dirstate.parents()
516 p = self._repo.dirstate.parents()
517 if p[1] == nullid:
517 if p[1] == nullid:
518 p = p[:-1]
518 p = p[:-1]
519 self._parents = [changectx(self._repo, x) for x in p]
519 self._parents = [changectx(self._repo, x) for x in p]
520 return self._parents
520 return self._parents
521 else:
521 else:
522 raise AttributeError, name
522 raise AttributeError, name
523
523
524 def _buildmanifest(self):
524 def _buildmanifest(self):
525 """generate a manifest corresponding to the working directory"""
525 """generate a manifest corresponding to the working directory"""
526
526
527 man = self._parents[0].manifest().copy()
527 man = self._parents[0].manifest().copy()
528 copied = self._repo.dirstate.copies()
528 copied = self._repo.dirstate.copies()
529 cf = lambda x: man.flags(copied.get(x, x))
529 cf = lambda x: man.flags(copied.get(x, x))
530 ff = self._repo.dirstate.flagfunc(cf)
530 ff = self._repo.dirstate.flagfunc(cf)
531 modified, added, removed, deleted, unknown = self._status[:5]
531 modified, added, removed, deleted, unknown = self._status[:5]
532 for i, l in (("a", added), ("m", modified), ("u", unknown)):
532 for i, l in (("a", added), ("m", modified), ("u", unknown)):
533 for f in l:
533 for f in l:
534 man[f] = man.get(copied.get(f, f), nullid) + i
534 man[f] = man.get(copied.get(f, f), nullid) + i
535 try:
535 try:
536 man.set(f, ff(f))
536 man.set(f, ff(f))
537 except OSError:
537 except OSError:
538 pass
538 pass
539
539
540 for f in deleted + removed:
540 for f in deleted + removed:
541 if f in man:
541 if f in man:
542 del man[f]
542 del man[f]
543
543
544 self._manifest = man
544 self._manifest = man
545
545
546 def manifest(self): return self._manifest
546 def manifest(self): return self._manifest
547
547
548 def user(self): return self._user or self._repo.ui.username()
548 def user(self): return self._user or self._repo.ui.username()
549 def date(self): return self._date
549 def date(self): return self._date
550 def description(self): return self._text
550 def description(self): return self._text
551 def files(self):
551 def files(self):
552 return util.sort(self._status[0] + self._status[1] + self._status[2])
552 return util.sort(self._status[0] + self._status[1] + self._status[2])
553
553
554 def modified(self): return self._status[0]
554 def modified(self): return self._status[0]
555 def added(self): return self._status[1]
555 def added(self): return self._status[1]
556 def removed(self): return self._status[2]
556 def removed(self): return self._status[2]
557 def deleted(self): return self._status[3]
557 def deleted(self): return self._status[3]
558 def unknown(self): return self._status[4]
558 def unknown(self): return self._status[4]
559 def clean(self): return self._status[5]
559 def clean(self): return self._status[5]
560 def branch(self): return self._extra['branch']
560 def branch(self): return self._extra['branch']
561 def extra(self): return self._extra
561 def extra(self): return self._extra
562
562
563 def tags(self):
563 def tags(self):
564 t = []
564 t = []
565 [t.extend(p.tags()) for p in self.parents()]
565 [t.extend(p.tags()) for p in self.parents()]
566 return t
566 return t
567
567
568 def children(self):
568 def children(self):
569 return []
569 return []
570
570
571 def flags(self, path):
571 def flags(self, path):
572 if '_manifest' in self.__dict__:
572 if '_manifest' in self.__dict__:
573 try:
573 try:
574 return self._manifest.flags(path)
574 return self._manifest.flags(path)
575 except KeyError:
575 except KeyError:
576 return ''
576 return ''
577
577
578 pnode = self._parents[0].changeset()[0]
578 pnode = self._parents[0].changeset()[0]
579 orig = self._repo.dirstate.copies().get(path, path)
579 orig = self._repo.dirstate.copies().get(path, path)
580 node, flag = self._repo.manifest.find(pnode, orig)
580 node, flag = self._repo.manifest.find(pnode, orig)
581 try:
581 try:
582 ff = self._repo.dirstate.flagfunc(lambda x: flag or '')
582 ff = self._repo.dirstate.flagfunc(lambda x: flag or '')
583 return ff(path)
583 return ff(path)
584 except OSError:
584 except OSError:
585 pass
585 pass
586
586
587 if not node or path in self.deleted() or path in self.removed():
587 if not node or path in self.deleted() or path in self.removed():
588 return ''
588 return ''
589 return flag
589 return flag
590
590
591 def filectx(self, path, filelog=None):
591 def filectx(self, path, filelog=None):
592 """get a file context from the working directory"""
592 """get a file context from the working directory"""
593 return workingfilectx(self._repo, path, workingctx=self,
593 return workingfilectx(self._repo, path, workingctx=self,
594 filelog=filelog)
594 filelog=filelog)
595
595
596 def ancestor(self, c2):
596 def ancestor(self, c2):
597 """return the ancestor context of self and c2"""
597 """return the ancestor context of self and c2"""
598 return self._parents[0].ancestor(c2) # punt on two parents for now
598 return self._parents[0].ancestor(c2) # punt on two parents for now
599
599
600 def walk(self, match):
600 def walk(self, match):
601 return util.sort(self._repo.dirstate.walk(match, True, False).keys())
601 return util.sort(self._repo.dirstate.walk(match, True, False).keys())
602
602
603 class workingfilectx(filectx):
603 class workingfilectx(filectx):
604 """A workingfilectx object makes access to data related to a particular
604 """A workingfilectx object makes access to data related to a particular
605 file in the working directory convenient."""
605 file in the working directory convenient."""
606 def __init__(self, repo, path, filelog=None, workingctx=None):
606 def __init__(self, repo, path, filelog=None, workingctx=None):
607 """changeid can be a changeset revision, node, or tag.
607 """changeid can be a changeset revision, node, or tag.
608 fileid can be a file revision or node."""
608 fileid can be a file revision or node."""
609 self._repo = repo
609 self._repo = repo
610 self._path = path
610 self._path = path
611 self._changeid = None
611 self._changeid = None
612 self._filerev = self._filenode = None
612 self._filerev = self._filenode = None
613
613
614 if filelog:
614 if filelog:
615 self._filelog = filelog
615 self._filelog = filelog
616 if workingctx:
616 if workingctx:
617 self._changectx = workingctx
617 self._changectx = workingctx
618
618
619 def __getattr__(self, name):
619 def __getattr__(self, name):
620 if name == '_changectx':
620 if name == '_changectx':
621 self._changectx = workingctx(self._repo)
621 self._changectx = workingctx(self._repo)
622 return self._changectx
622 return self._changectx
623 elif name == '_repopath':
623 elif name == '_repopath':
624 self._repopath = (self._repo.dirstate.copied(self._path)
624 self._repopath = (self._repo.dirstate.copied(self._path)
625 or self._path)
625 or self._path)
626 return self._repopath
626 return self._repopath
627 elif name == '_filelog':
627 elif name == '_filelog':
628 self._filelog = self._repo.file(self._repopath)
628 self._filelog = self._repo.file(self._repopath)
629 return self._filelog
629 return self._filelog
630 else:
630 else:
631 raise AttributeError, name
631 raise AttributeError, name
632
632
633 def __nonzero__(self):
633 def __nonzero__(self):
634 return True
634 return True
635
635
636 def __str__(self):
636 def __str__(self):
637 return "%s@%s" % (self.path(), self._changectx)
637 return "%s@%s" % (self.path(), self._changectx)
638
638
639 def filectx(self, fileid):
639 def filectx(self, fileid):
640 '''opens an arbitrary revision of the file without
640 '''opens an arbitrary revision of the file without
641 opening a new filelog'''
641 opening a new filelog'''
642 return filectx(self._repo, self._repopath, fileid=fileid,
642 return filectx(self._repo, self._repopath, fileid=fileid,
643 filelog=self._filelog)
643 filelog=self._filelog)
644
644
645 def rev(self):
645 def rev(self):
646 if '_changectx' in self.__dict__:
646 if '_changectx' in self.__dict__:
647 return self._changectx.rev()
647 return self._changectx.rev()
648 return self._filelog.linkrev(self._filenode)
648 return self._filelog.linkrev(self._filenode)
649
649
650 def data(self): return self._repo.wread(self._path)
650 def data(self): return self._repo.wread(self._path)
651 def renamed(self):
651 def renamed(self):
652 rp = self._repopath
652 rp = self._repopath
653 if rp == self._path:
653 if rp == self._path:
654 return None
654 return None
655 return rp, self._changectx._parents[0]._manifest.get(rp, nullid)
655 return rp, self._changectx._parents[0]._manifest.get(rp, nullid)
656
656
657 def parents(self):
657 def parents(self):
658 '''return parent filectxs, following copies if necessary'''
658 '''return parent filectxs, following copies if necessary'''
659 p = self._path
659 p = self._path
660 rp = self._repopath
660 rp = self._repopath
661 pcl = self._changectx._parents
661 pcl = self._changectx._parents
662 fl = self._filelog
662 fl = self._filelog
663 pl = [(rp, pcl[0]._manifest.get(rp, nullid), fl)]
663 pl = [(rp, pcl[0]._manifest.get(rp, nullid), fl)]
664 if len(pcl) > 1:
664 if len(pcl) > 1:
665 if rp != p:
665 if rp != p:
666 fl = None
666 fl = None
667 pl.append((p, pcl[1]._manifest.get(p, nullid), fl))
667 pl.append((p, pcl[1]._manifest.get(p, nullid), fl))
668
668
669 return [filectx(self._repo, p, fileid=n, filelog=l)
669 return [filectx(self._repo, p, fileid=n, filelog=l)
670 for p,n,l in pl if n != nullid]
670 for p,n,l in pl if n != nullid]
671
671
672 def children(self):
672 def children(self):
673 return []
673 return []
674
674
675 def size(self): return os.stat(self._repo.wjoin(self._path)).st_size
675 def size(self): return os.stat(self._repo.wjoin(self._path)).st_size
676 def date(self):
676 def date(self):
677 t, tz = self._changectx.date()
677 t, tz = self._changectx.date()
678 try:
678 try:
679 return (int(os.lstat(self._repo.wjoin(self._path)).st_mtime), tz)
679 return (int(os.lstat(self._repo.wjoin(self._path)).st_mtime), tz)
680 except OSError, err:
680 except OSError, err:
681 if err.errno != errno.ENOENT: raise
681 if err.errno != errno.ENOENT: raise
682 return (t, tz)
682 return (t, tz)
683
683
684 def cmp(self, text): return self._repo.wread(self._path) == text
684 def cmp(self, text): return self._repo.wread(self._path) == text
685
685
686 class memctx(object):
686 class memctx(object):
687 """A memctx is a subset of changectx supposed to be built on memory
687 """A memctx is a subset of changectx supposed to be built on memory
688 and passed to commit functions.
688 and passed to commit functions.
689
689
690 NOTE: this interface and the related memfilectx are experimental and
690 NOTE: this interface and the related memfilectx are experimental and
691 may change without notice.
691 may change without notice.
692
692
693 parents - a pair of parent nodeids.
693 parents - a pair of parent nodeids.
694 filectxfn - a callable taking (repo, memctx, path) arguments and
694 filectxfn - a callable taking (repo, memctx, path) arguments and
695 returning a memctx object.
695 returning a memctx object.
696 date - any valid date string or (unixtime, offset), or None.
696 date - any valid date string or (unixtime, offset), or None.
697 user - username string, or None.
697 user - username string, or None.
698 extra - a dictionary of extra values, or None.
698 extra - a dictionary of extra values, or None.
699 """
699 """
700 def __init__(self, repo, parents, text, files, filectxfn, user=None,
700 def __init__(self, repo, parents, text, files, filectxfn, user=None,
701 date=None, extra=None):
701 date=None, extra=None):
702 self._repo = repo
702 self._repo = repo
703 self._rev = None
703 self._rev = None
704 self._node = None
704 self._node = None
705 self._text = text
705 self._text = text
706 self._date = date and util.parsedate(date) or util.makedate()
706 self._date = date and util.parsedate(date) or util.makedate()
707 self._user = user
707 self._user = user
708 parents = [(p or nullid) for p in parents]
708 parents = [(p or nullid) for p in parents]
709 p1, p2 = parents
709 p1, p2 = parents
710 self._parents = [changectx(self._repo, p) for p in (p1, p2)]
710 self._parents = [changectx(self._repo, p) for p in (p1, p2)]
711 files = util.sort(list(files))
711 files = util.sort(list(files))
712 self._status = [files, [], [], [], []]
712 self._status = [files, [], [], [], []]
713 self._filectxfn = filectxfn
713 self._filectxfn = filectxfn
714
714
715 self._extra = extra and extra.copy() or {}
715 self._extra = extra and extra.copy() or {}
716 if 'branch' not in self._extra:
716 if 'branch' not in self._extra:
717 self._extra['branch'] = 'default'
717 self._extra['branch'] = 'default'
718 elif self._extra.get('branch') == '':
718 elif self._extra.get('branch') == '':
719 self._extra['branch'] = 'default'
719 self._extra['branch'] = 'default'
720
720
721 def __str__(self):
721 def __str__(self):
722 return str(self._parents[0]) + "+"
722 return str(self._parents[0]) + "+"
723
723
724 def __int__(self):
724 def __int__(self):
725 return self._rev
725 return self._rev
726
726
727 def __nonzero__(self):
727 def __nonzero__(self):
728 return True
728 return True
729
729
730 def user(self): return self._user or self._repo.ui.username()
730 def user(self): return self._user or self._repo.ui.username()
731 def date(self): return self._date
731 def date(self): return self._date
732 def description(self): return self._text
732 def description(self): return self._text
733 def files(self): return self.modified()
733 def files(self): return self.modified()
734 def modified(self): return self._status[0]
734 def modified(self): return self._status[0]
735 def added(self): return self._status[1]
735 def added(self): return self._status[1]
736 def removed(self): return self._status[2]
736 def removed(self): return self._status[2]
737 def deleted(self): return self._status[3]
737 def deleted(self): return self._status[3]
738 def unknown(self): return self._status[4]
738 def unknown(self): return self._status[4]
739 def clean(self): return self._status[5]
739 def clean(self): return self._status[5]
740 def branch(self): return self._extra['branch']
740 def branch(self): return self._extra['branch']
741 def extra(self): return self._extra
741 def extra(self): return self._extra
742 def flags(self, f): return self[f].flags()
742 def flags(self, f): return self[f].flags()
743
743
744 def parents(self):
744 def parents(self):
745 """return contexts for each parent changeset"""
745 """return contexts for each parent changeset"""
746 return self._parents
746 return self._parents
747
747
748 def filectx(self, path, filelog=None):
748 def filectx(self, path, filelog=None):
749 """get a file context from the working directory"""
749 """get a file context from the working directory"""
750 return self._filectxfn(self._repo, self, path)
750 return self._filectxfn(self._repo, self, path)
751
751
752 class memfilectx(object):
752 class memfilectx(object):
753 """A memfilectx is a subset of filectx supposed to be built by client
753 """A memfilectx is a subset of filectx supposed to be built by client
754 code and passed to commit functions.
754 code and passed to commit functions.
755 """
755 """
756 def __init__(self, path, data, islink, isexec, copied):
756 def __init__(self, path, data, islink, isexec, copied):
757 """copied is the source file path, or None."""
757 """copied is the source file path, or None."""
758 self._path = path
758 self._path = path
759 self._data = data
759 self._data = data
760 self._flags = (islink and 'l' or '') + (isexec and 'x' or '')
760 self._flags = (islink and 'l' or '') + (isexec and 'x' or '')
761 self._copied = None
761 self._copied = None
762 if copied:
762 if copied:
763 self._copied = (copied, nullid)
763 self._copied = (copied, nullid)
764
764
765 def __nonzero__(self): return True
765 def __nonzero__(self): return True
766 def __str__(self): return "%s@%s" % (self.path(), self._changectx)
766 def __str__(self): return "%s@%s" % (self.path(), self._changectx)
767 def path(self): return self._path
767 def path(self): return self._path
768 def data(self): return self._data
768 def data(self): return self._data
769 def flags(self): return self._flags
769 def flags(self): return self._flags
770 def isexec(self): return 'x' in self._flags
770 def isexec(self): return 'x' in self._flags
771 def islink(self): return 'l' in self._flags
771 def islink(self): return 'l' in self._flags
772 def renamed(self): return self._copied
772 def renamed(self): return self._copied
773
773
General Comments 0
You need to be logged in to leave comments. Login now