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