##// END OF EJS Templates
context: add __contains__, __getitem__, and __iter__
Matt Mackall -
r4909:1fd7a99d default
parent child Browse files
Show More
@@ -1,543 +1,555 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 *
8 from node import *
9 from i18n import _
9 from i18n import _
10 import ancestor, bdiff, repo, revlog, util, os, errno
10 import ancestor, bdiff, repo, 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=None):
15 def __init__(self, repo, changeid=None):
16 """changeid is a revision number, node, or tag"""
16 """changeid is a revision number, node, or tag"""
17 self._repo = repo
17 self._repo = repo
18
18
19 if not changeid and changeid != 0:
19 if not changeid and changeid != 0:
20 p1, p2 = self._repo.dirstate.parents()
20 p1, p2 = self._repo.dirstate.parents()
21 self._rev = self._repo.changelog.rev(p1)
21 self._rev = self._repo.changelog.rev(p1)
22 if self._rev == -1:
22 if self._rev == -1:
23 changeid = 'tip'
23 changeid = 'tip'
24 else:
24 else:
25 self._node = p1
25 self._node = p1
26 return
26 return
27
27
28 self._node = self._repo.lookup(changeid)
28 self._node = self._repo.lookup(changeid)
29 self._rev = self._repo.changelog.rev(self._node)
29 self._rev = self._repo.changelog.rev(self._node)
30
30
31 def __str__(self):
31 def __str__(self):
32 return short(self.node())
32 return short(self.node())
33
33
34 def __repr__(self):
34 def __repr__(self):
35 return "<changectx %s>" % str(self)
35 return "<changectx %s>" % str(self)
36
36
37 def __eq__(self, other):
37 def __eq__(self, other):
38 try:
38 try:
39 return self._rev == other._rev
39 return self._rev == other._rev
40 except AttributeError:
40 except AttributeError:
41 return False
41 return False
42
42
43 def __ne__(self, other):
43 def __ne__(self, other):
44 return not (self == other)
44 return not (self == other)
45
45
46 def __nonzero__(self):
46 def __nonzero__(self):
47 return self._rev != nullrev
47 return self._rev != nullrev
48
48
49 def __getattr__(self, name):
49 def __getattr__(self, name):
50 if name == '_changeset':
50 if name == '_changeset':
51 self._changeset = self._repo.changelog.read(self.node())
51 self._changeset = self._repo.changelog.read(self.node())
52 return self._changeset
52 return self._changeset
53 elif name == '_manifest':
53 elif name == '_manifest':
54 self._manifest = self._repo.manifest.read(self._changeset[0])
54 self._manifest = self._repo.manifest.read(self._changeset[0])
55 return self._manifest
55 return self._manifest
56 elif name == '_manifestdelta':
56 elif name == '_manifestdelta':
57 md = self._repo.manifest.readdelta(self._changeset[0])
57 md = self._repo.manifest.readdelta(self._changeset[0])
58 self._manifestdelta = md
58 self._manifestdelta = md
59 return self._manifestdelta
59 return self._manifestdelta
60 else:
60 else:
61 raise AttributeError, name
61 raise AttributeError, name
62
62
63 def __contains__(self, key):
64 return key in self._manifest
65
66 def __getitem__(self, key):
67 return self.filectx(key)
68
69 def __iter__(self):
70 a = self._manifest.keys()
71 a.sort()
72 for f in a:
73 return f
74
63 def changeset(self): return self._changeset
75 def changeset(self): return self._changeset
64 def manifest(self): return self._manifest
76 def manifest(self): return self._manifest
65
77
66 def rev(self): return self._rev
78 def rev(self): return self._rev
67 def node(self): return self._node
79 def node(self): return self._node
68 def user(self): return self._changeset[1]
80 def user(self): return self._changeset[1]
69 def date(self): return self._changeset[2]
81 def date(self): return self._changeset[2]
70 def files(self): return self._changeset[3]
82 def files(self): return self._changeset[3]
71 def description(self): return self._changeset[4]
83 def description(self): return self._changeset[4]
72 def branch(self): return self._changeset[5].get("branch")
84 def branch(self): return self._changeset[5].get("branch")
73 def tags(self): return self._repo.nodetags(self._node)
85 def tags(self): return self._repo.nodetags(self._node)
74
86
75 def parents(self):
87 def parents(self):
76 """return contexts for each parent changeset"""
88 """return contexts for each parent changeset"""
77 p = self._repo.changelog.parents(self._node)
89 p = self._repo.changelog.parents(self._node)
78 return [changectx(self._repo, x) for x in p]
90 return [changectx(self._repo, x) for x in p]
79
91
80 def children(self):
92 def children(self):
81 """return contexts for each child changeset"""
93 """return contexts for each child changeset"""
82 c = self._repo.changelog.children(self._node)
94 c = self._repo.changelog.children(self._node)
83 return [changectx(self._repo, x) for x in c]
95 return [changectx(self._repo, x) for x in c]
84
96
85 def filenode(self, path):
97 def filenode(self, path):
86 if '_manifest' in self.__dict__:
98 if '_manifest' in self.__dict__:
87 try:
99 try:
88 return self._manifest[path]
100 return self._manifest[path]
89 except KeyError:
101 except KeyError:
90 raise revlog.LookupError(_("'%s' not found in manifest") % path)
102 raise revlog.LookupError(_("'%s' not found in manifest") % path)
91 if '_manifestdelta' in self.__dict__ or path in self.files():
103 if '_manifestdelta' in self.__dict__ or path in self.files():
92 if path in self._manifestdelta:
104 if path in self._manifestdelta:
93 return self._manifestdelta[path]
105 return self._manifestdelta[path]
94 node, flag = self._repo.manifest.find(self._changeset[0], path)
106 node, flag = self._repo.manifest.find(self._changeset[0], path)
95 if not node:
107 if not node:
96 raise revlog.LookupError(_("'%s' not found in manifest") % path)
108 raise revlog.LookupError(_("'%s' not found in manifest") % path)
97
109
98 return node
110 return node
99
111
100 def filectx(self, path, fileid=None, filelog=None):
112 def filectx(self, path, fileid=None, filelog=None):
101 """get a file context from this changeset"""
113 """get a file context from this changeset"""
102 if fileid is None:
114 if fileid is None:
103 fileid = self.filenode(path)
115 fileid = self.filenode(path)
104 return filectx(self._repo, path, fileid=fileid,
116 return filectx(self._repo, path, fileid=fileid,
105 changectx=self, filelog=filelog)
117 changectx=self, filelog=filelog)
106
118
107 def filectxs(self):
119 def filectxs(self):
108 """generate a file context for each file in this changeset's
120 """generate a file context for each file in this changeset's
109 manifest"""
121 manifest"""
110 mf = self.manifest()
122 mf = self.manifest()
111 m = mf.keys()
123 m = mf.keys()
112 m.sort()
124 m.sort()
113 for f in m:
125 for f in m:
114 yield self.filectx(f, fileid=mf[f])
126 yield self.filectx(f, fileid=mf[f])
115
127
116 def ancestor(self, c2):
128 def ancestor(self, c2):
117 """
129 """
118 return the ancestor context of self and c2
130 return the ancestor context of self and c2
119 """
131 """
120 n = self._repo.changelog.ancestor(self._node, c2._node)
132 n = self._repo.changelog.ancestor(self._node, c2._node)
121 return changectx(self._repo, n)
133 return changectx(self._repo, n)
122
134
123 class filectx(object):
135 class filectx(object):
124 """A filecontext object makes access to data related to a particular
136 """A filecontext object makes access to data related to a particular
125 filerevision convenient."""
137 filerevision convenient."""
126 def __init__(self, repo, path, changeid=None, fileid=None,
138 def __init__(self, repo, path, changeid=None, fileid=None,
127 filelog=None, changectx=None):
139 filelog=None, changectx=None):
128 """changeid can be a changeset revision, node, or tag.
140 """changeid can be a changeset revision, node, or tag.
129 fileid can be a file revision or node."""
141 fileid can be a file revision or node."""
130 self._repo = repo
142 self._repo = repo
131 self._path = path
143 self._path = path
132
144
133 assert (changeid is not None
145 assert (changeid is not None
134 or fileid is not None
146 or fileid is not None
135 or changectx is not None)
147 or changectx is not None)
136
148
137 if filelog:
149 if filelog:
138 self._filelog = filelog
150 self._filelog = filelog
139
151
140 if fileid is None:
152 if fileid is None:
141 if changectx is None:
153 if changectx is None:
142 self._changeid = changeid
154 self._changeid = changeid
143 else:
155 else:
144 self._changectx = changectx
156 self._changectx = changectx
145 else:
157 else:
146 self._fileid = fileid
158 self._fileid = fileid
147
159
148 def __getattr__(self, name):
160 def __getattr__(self, name):
149 if name == '_changectx':
161 if name == '_changectx':
150 self._changectx = changectx(self._repo, self._changeid)
162 self._changectx = changectx(self._repo, self._changeid)
151 return self._changectx
163 return self._changectx
152 elif name == '_filelog':
164 elif name == '_filelog':
153 self._filelog = self._repo.file(self._path)
165 self._filelog = self._repo.file(self._path)
154 return self._filelog
166 return self._filelog
155 elif name == '_changeid':
167 elif name == '_changeid':
156 self._changeid = self._filelog.linkrev(self._filenode)
168 self._changeid = self._filelog.linkrev(self._filenode)
157 return self._changeid
169 return self._changeid
158 elif name == '_filenode':
170 elif name == '_filenode':
159 if '_fileid' in self.__dict__:
171 if '_fileid' in self.__dict__:
160 self._filenode = self._filelog.lookup(self._fileid)
172 self._filenode = self._filelog.lookup(self._fileid)
161 else:
173 else:
162 self._filenode = self._changectx.filenode(self._path)
174 self._filenode = self._changectx.filenode(self._path)
163 return self._filenode
175 return self._filenode
164 elif name == '_filerev':
176 elif name == '_filerev':
165 self._filerev = self._filelog.rev(self._filenode)
177 self._filerev = self._filelog.rev(self._filenode)
166 return self._filerev
178 return self._filerev
167 else:
179 else:
168 raise AttributeError, name
180 raise AttributeError, name
169
181
170 def __nonzero__(self):
182 def __nonzero__(self):
171 try:
183 try:
172 n = self._filenode
184 n = self._filenode
173 return True
185 return True
174 except revlog.LookupError:
186 except revlog.LookupError:
175 # file is missing
187 # file is missing
176 return False
188 return False
177
189
178 def __str__(self):
190 def __str__(self):
179 return "%s@%s" % (self.path(), short(self.node()))
191 return "%s@%s" % (self.path(), short(self.node()))
180
192
181 def __repr__(self):
193 def __repr__(self):
182 return "<filectx %s>" % str(self)
194 return "<filectx %s>" % str(self)
183
195
184 def __eq__(self, other):
196 def __eq__(self, other):
185 try:
197 try:
186 return (self._path == other._path
198 return (self._path == other._path
187 and self._fileid == other._fileid)
199 and self._fileid == other._fileid)
188 except AttributeError:
200 except AttributeError:
189 return False
201 return False
190
202
191 def __ne__(self, other):
203 def __ne__(self, other):
192 return not (self == other)
204 return not (self == other)
193
205
194 def filectx(self, fileid):
206 def filectx(self, fileid):
195 '''opens an arbitrary revision of the file without
207 '''opens an arbitrary revision of the file without
196 opening a new filelog'''
208 opening a new filelog'''
197 return filectx(self._repo, self._path, fileid=fileid,
209 return filectx(self._repo, self._path, fileid=fileid,
198 filelog=self._filelog)
210 filelog=self._filelog)
199
211
200 def filerev(self): return self._filerev
212 def filerev(self): return self._filerev
201 def filenode(self): return self._filenode
213 def filenode(self): return self._filenode
202 def filelog(self): return self._filelog
214 def filelog(self): return self._filelog
203
215
204 def rev(self):
216 def rev(self):
205 if '_changectx' in self.__dict__:
217 if '_changectx' in self.__dict__:
206 return self._changectx.rev()
218 return self._changectx.rev()
207 return self._filelog.linkrev(self._filenode)
219 return self._filelog.linkrev(self._filenode)
208
220
209 def node(self): return self._changectx.node()
221 def node(self): return self._changectx.node()
210 def user(self): return self._changectx.user()
222 def user(self): return self._changectx.user()
211 def date(self): return self._changectx.date()
223 def date(self): return self._changectx.date()
212 def files(self): return self._changectx.files()
224 def files(self): return self._changectx.files()
213 def description(self): return self._changectx.description()
225 def description(self): return self._changectx.description()
214 def branch(self): return self._changectx.branch()
226 def branch(self): return self._changectx.branch()
215 def manifest(self): return self._changectx.manifest()
227 def manifest(self): return self._changectx.manifest()
216 def changectx(self): return self._changectx
228 def changectx(self): return self._changectx
217
229
218 def data(self): return self._filelog.read(self._filenode)
230 def data(self): return self._filelog.read(self._filenode)
219 def renamed(self): return self._filelog.renamed(self._filenode)
231 def renamed(self): return self._filelog.renamed(self._filenode)
220 def path(self): return self._path
232 def path(self): return self._path
221 def size(self): return self._filelog.size(self._filerev)
233 def size(self): return self._filelog.size(self._filerev)
222
234
223 def cmp(self, text): return self._filelog.cmp(self._filenode, text)
235 def cmp(self, text): return self._filelog.cmp(self._filenode, text)
224
236
225 def parents(self):
237 def parents(self):
226 p = self._path
238 p = self._path
227 fl = self._filelog
239 fl = self._filelog
228 pl = [(p, n, fl) for n in self._filelog.parents(self._filenode)]
240 pl = [(p, n, fl) for n in self._filelog.parents(self._filenode)]
229
241
230 r = self.renamed()
242 r = self.renamed()
231 if r:
243 if r:
232 pl[0] = (r[0], r[1], None)
244 pl[0] = (r[0], r[1], None)
233
245
234 return [filectx(self._repo, p, fileid=n, filelog=l)
246 return [filectx(self._repo, p, fileid=n, filelog=l)
235 for p,n,l in pl if n != nullid]
247 for p,n,l in pl if n != nullid]
236
248
237 def children(self):
249 def children(self):
238 # hard for renames
250 # hard for renames
239 c = self._filelog.children(self._filenode)
251 c = self._filelog.children(self._filenode)
240 return [filectx(self._repo, self._path, fileid=x,
252 return [filectx(self._repo, self._path, fileid=x,
241 filelog=self._filelog) for x in c]
253 filelog=self._filelog) for x in c]
242
254
243 def annotate(self, follow=False, linenumber=None):
255 def annotate(self, follow=False, linenumber=None):
244 '''returns a list of tuples of (ctx, line) for each line
256 '''returns a list of tuples of (ctx, line) for each line
245 in the file, where ctx is the filectx of the node where
257 in the file, where ctx is the filectx of the node where
246 that line was last changed.
258 that line was last changed.
247 This returns tuples of ((ctx, linenumber), line) for each line,
259 This returns tuples of ((ctx, linenumber), line) for each line,
248 if "linenumber" parameter is NOT "None".
260 if "linenumber" parameter is NOT "None".
249 In such tuples, linenumber means one at the first appearance
261 In such tuples, linenumber means one at the first appearance
250 in the managed file.
262 in the managed file.
251 To reduce annotation cost,
263 To reduce annotation cost,
252 this returns fixed value(False is used) as linenumber,
264 this returns fixed value(False is used) as linenumber,
253 if "linenumber" parameter is "False".'''
265 if "linenumber" parameter is "False".'''
254
266
255 def decorate_compat(text, rev):
267 def decorate_compat(text, rev):
256 return ([rev] * len(text.splitlines()), text)
268 return ([rev] * len(text.splitlines()), text)
257
269
258 def without_linenumber(text, rev):
270 def without_linenumber(text, rev):
259 return ([(rev, False)] * len(text.splitlines()), text)
271 return ([(rev, False)] * len(text.splitlines()), text)
260
272
261 def with_linenumber(text, rev):
273 def with_linenumber(text, rev):
262 size = len(text.splitlines())
274 size = len(text.splitlines())
263 return ([(rev, i) for i in xrange(1, size + 1)], text)
275 return ([(rev, i) for i in xrange(1, size + 1)], text)
264
276
265 decorate = (((linenumber is None) and decorate_compat) or
277 decorate = (((linenumber is None) and decorate_compat) or
266 (linenumber and with_linenumber) or
278 (linenumber and with_linenumber) or
267 without_linenumber)
279 without_linenumber)
268
280
269 def pair(parent, child):
281 def pair(parent, child):
270 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
282 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
271 child[0][b1:b2] = parent[0][a1:a2]
283 child[0][b1:b2] = parent[0][a1:a2]
272 return child
284 return child
273
285
274 getlog = util.cachefunc(lambda x: self._repo.file(x))
286 getlog = util.cachefunc(lambda x: self._repo.file(x))
275 def getctx(path, fileid):
287 def getctx(path, fileid):
276 log = path == self._path and self._filelog or getlog(path)
288 log = path == self._path and self._filelog or getlog(path)
277 return filectx(self._repo, path, fileid=fileid, filelog=log)
289 return filectx(self._repo, path, fileid=fileid, filelog=log)
278 getctx = util.cachefunc(getctx)
290 getctx = util.cachefunc(getctx)
279
291
280 def parents(f):
292 def parents(f):
281 # we want to reuse filectx objects as much as possible
293 # we want to reuse filectx objects as much as possible
282 p = f._path
294 p = f._path
283 if f._filerev is None: # working dir
295 if f._filerev is None: # working dir
284 pl = [(n.path(), n.filerev()) for n in f.parents()]
296 pl = [(n.path(), n.filerev()) for n in f.parents()]
285 else:
297 else:
286 pl = [(p, n) for n in f._filelog.parentrevs(f._filerev)]
298 pl = [(p, n) for n in f._filelog.parentrevs(f._filerev)]
287
299
288 if follow:
300 if follow:
289 r = f.renamed()
301 r = f.renamed()
290 if r:
302 if r:
291 pl[0] = (r[0], getlog(r[0]).rev(r[1]))
303 pl[0] = (r[0], getlog(r[0]).rev(r[1]))
292
304
293 return [getctx(p, n) for p, n in pl if n != nullrev]
305 return [getctx(p, n) for p, n in pl if n != nullrev]
294
306
295 # use linkrev to find the first changeset where self appeared
307 # use linkrev to find the first changeset where self appeared
296 if self.rev() != self._filelog.linkrev(self._filenode):
308 if self.rev() != self._filelog.linkrev(self._filenode):
297 base = self.filectx(self.filerev())
309 base = self.filectx(self.filerev())
298 else:
310 else:
299 base = self
311 base = self
300
312
301 # find all ancestors
313 # find all ancestors
302 needed = {base: 1}
314 needed = {base: 1}
303 visit = [base]
315 visit = [base]
304 files = [base._path]
316 files = [base._path]
305 while visit:
317 while visit:
306 f = visit.pop(0)
318 f = visit.pop(0)
307 for p in parents(f):
319 for p in parents(f):
308 if p not in needed:
320 if p not in needed:
309 needed[p] = 1
321 needed[p] = 1
310 visit.append(p)
322 visit.append(p)
311 if p._path not in files:
323 if p._path not in files:
312 files.append(p._path)
324 files.append(p._path)
313 else:
325 else:
314 # count how many times we'll use this
326 # count how many times we'll use this
315 needed[p] += 1
327 needed[p] += 1
316
328
317 # sort by revision (per file) which is a topological order
329 # sort by revision (per file) which is a topological order
318 visit = []
330 visit = []
319 for f in files:
331 for f in files:
320 fn = [(n.rev(), n) for n in needed.keys() if n._path == f]
332 fn = [(n.rev(), n) for n in needed.keys() if n._path == f]
321 visit.extend(fn)
333 visit.extend(fn)
322 visit.sort()
334 visit.sort()
323 hist = {}
335 hist = {}
324
336
325 for r, f in visit:
337 for r, f in visit:
326 curr = decorate(f.data(), f)
338 curr = decorate(f.data(), f)
327 for p in parents(f):
339 for p in parents(f):
328 if p != nullid:
340 if p != nullid:
329 curr = pair(hist[p], curr)
341 curr = pair(hist[p], curr)
330 # trim the history of unneeded revs
342 # trim the history of unneeded revs
331 needed[p] -= 1
343 needed[p] -= 1
332 if not needed[p]:
344 if not needed[p]:
333 del hist[p]
345 del hist[p]
334 hist[f] = curr
346 hist[f] = curr
335
347
336 return zip(hist[f][0], hist[f][1].splitlines(1))
348 return zip(hist[f][0], hist[f][1].splitlines(1))
337
349
338 def ancestor(self, fc2):
350 def ancestor(self, fc2):
339 """
351 """
340 find the common ancestor file context, if any, of self, and fc2
352 find the common ancestor file context, if any, of self, and fc2
341 """
353 """
342
354
343 acache = {}
355 acache = {}
344
356
345 # prime the ancestor cache for the working directory
357 # prime the ancestor cache for the working directory
346 for c in (self, fc2):
358 for c in (self, fc2):
347 if c._filerev == None:
359 if c._filerev == None:
348 pl = [(n.path(), n.filenode()) for n in c.parents()]
360 pl = [(n.path(), n.filenode()) for n in c.parents()]
349 acache[(c._path, None)] = pl
361 acache[(c._path, None)] = pl
350
362
351 flcache = {self._path:self._filelog, fc2._path:fc2._filelog}
363 flcache = {self._path:self._filelog, fc2._path:fc2._filelog}
352 def parents(vertex):
364 def parents(vertex):
353 if vertex in acache:
365 if vertex in acache:
354 return acache[vertex]
366 return acache[vertex]
355 f, n = vertex
367 f, n = vertex
356 if f not in flcache:
368 if f not in flcache:
357 flcache[f] = self._repo.file(f)
369 flcache[f] = self._repo.file(f)
358 fl = flcache[f]
370 fl = flcache[f]
359 pl = [(f, p) for p in fl.parents(n) if p != nullid]
371 pl = [(f, p) for p in fl.parents(n) if p != nullid]
360 re = fl.renamed(n)
372 re = fl.renamed(n)
361 if re:
373 if re:
362 pl.append(re)
374 pl.append(re)
363 acache[vertex] = pl
375 acache[vertex] = pl
364 return pl
376 return pl
365
377
366 a, b = (self._path, self._filenode), (fc2._path, fc2._filenode)
378 a, b = (self._path, self._filenode), (fc2._path, fc2._filenode)
367 v = ancestor.ancestor(a, b, parents)
379 v = ancestor.ancestor(a, b, parents)
368 if v:
380 if v:
369 f, n = v
381 f, n = v
370 return filectx(self._repo, f, fileid=n, filelog=flcache[f])
382 return filectx(self._repo, f, fileid=n, filelog=flcache[f])
371
383
372 return None
384 return None
373
385
374 class workingctx(changectx):
386 class workingctx(changectx):
375 """A workingctx object makes access to data related to
387 """A workingctx object makes access to data related to
376 the current working directory convenient."""
388 the current working directory convenient."""
377 def __init__(self, repo):
389 def __init__(self, repo):
378 self._repo = repo
390 self._repo = repo
379 self._rev = None
391 self._rev = None
380 self._node = None
392 self._node = None
381
393
382 def __str__(self):
394 def __str__(self):
383 return str(self._parents[0]) + "+"
395 return str(self._parents[0]) + "+"
384
396
385 def __nonzero__(self):
397 def __nonzero__(self):
386 return True
398 return True
387
399
388 def __getattr__(self, name):
400 def __getattr__(self, name):
389 if name == '_parents':
401 if name == '_parents':
390 self._parents = self._repo.parents()
402 self._parents = self._repo.parents()
391 return self._parents
403 return self._parents
392 if name == '_status':
404 if name == '_status':
393 self._status = self._repo.status()
405 self._status = self._repo.status()
394 return self._status
406 return self._status
395 if name == '_manifest':
407 if name == '_manifest':
396 self._buildmanifest()
408 self._buildmanifest()
397 return self._manifest
409 return self._manifest
398 else:
410 else:
399 raise AttributeError, name
411 raise AttributeError, name
400
412
401 def _buildmanifest(self):
413 def _buildmanifest(self):
402 """generate a manifest corresponding to the working directory"""
414 """generate a manifest corresponding to the working directory"""
403
415
404 man = self._parents[0].manifest().copy()
416 man = self._parents[0].manifest().copy()
405 is_exec = util.execfunc(self._repo.root, man.execf)
417 is_exec = util.execfunc(self._repo.root, man.execf)
406 is_link = util.linkfunc(self._repo.root, man.linkf)
418 is_link = util.linkfunc(self._repo.root, man.linkf)
407 copied = self._repo.dirstate.copies()
419 copied = self._repo.dirstate.copies()
408 modified, added, removed, deleted, unknown = self._status[:5]
420 modified, added, removed, deleted, unknown = self._status[:5]
409 for i, l in (("a", added), ("m", modified), ("u", unknown)):
421 for i, l in (("a", added), ("m", modified), ("u", unknown)):
410 for f in l:
422 for f in l:
411 man[f] = man.get(copied.get(f, f), nullid) + i
423 man[f] = man.get(copied.get(f, f), nullid) + i
412 try:
424 try:
413 man.set(f, is_exec(f), is_link(f))
425 man.set(f, is_exec(f), is_link(f))
414 except OSError:
426 except OSError:
415 pass
427 pass
416
428
417 for f in deleted + removed:
429 for f in deleted + removed:
418 if f in man:
430 if f in man:
419 del man[f]
431 del man[f]
420
432
421 self._manifest = man
433 self._manifest = man
422
434
423 def manifest(self): return self._manifest
435 def manifest(self): return self._manifest
424
436
425 def user(self): return self._repo.ui.username()
437 def user(self): return self._repo.ui.username()
426 def date(self): return util.makedate()
438 def date(self): return util.makedate()
427 def description(self): return ""
439 def description(self): return ""
428 def files(self):
440 def files(self):
429 f = self.modified() + self.added() + self.removed()
441 f = self.modified() + self.added() + self.removed()
430 f.sort()
442 f.sort()
431 return f
443 return f
432
444
433 def modified(self): return self._status[0]
445 def modified(self): return self._status[0]
434 def added(self): return self._status[1]
446 def added(self): return self._status[1]
435 def removed(self): return self._status[2]
447 def removed(self): return self._status[2]
436 def deleted(self): return self._status[3]
448 def deleted(self): return self._status[3]
437 def unknown(self): return self._status[4]
449 def unknown(self): return self._status[4]
438 def clean(self): return self._status[5]
450 def clean(self): return self._status[5]
439 def branch(self): return self._repo.dirstate.branch()
451 def branch(self): return self._repo.dirstate.branch()
440
452
441 def tags(self):
453 def tags(self):
442 t = []
454 t = []
443 [t.extend(p.tags()) for p in self.parents()]
455 [t.extend(p.tags()) for p in self.parents()]
444 return t
456 return t
445
457
446 def parents(self):
458 def parents(self):
447 """return contexts for each parent changeset"""
459 """return contexts for each parent changeset"""
448 return self._parents
460 return self._parents
449
461
450 def children(self):
462 def children(self):
451 return []
463 return []
452
464
453 def filectx(self, path, filelog=None):
465 def filectx(self, path, filelog=None):
454 """get a file context from the working directory"""
466 """get a file context from the working directory"""
455 return workingfilectx(self._repo, path, workingctx=self,
467 return workingfilectx(self._repo, path, workingctx=self,
456 filelog=filelog)
468 filelog=filelog)
457
469
458 def ancestor(self, c2):
470 def ancestor(self, c2):
459 """return the ancestor context of self and c2"""
471 """return the ancestor context of self and c2"""
460 return self._parents[0].ancestor(c2) # punt on two parents for now
472 return self._parents[0].ancestor(c2) # punt on two parents for now
461
473
462 class workingfilectx(filectx):
474 class workingfilectx(filectx):
463 """A workingfilectx object makes access to data related to a particular
475 """A workingfilectx object makes access to data related to a particular
464 file in the working directory convenient."""
476 file in the working directory convenient."""
465 def __init__(self, repo, path, filelog=None, workingctx=None):
477 def __init__(self, repo, path, filelog=None, workingctx=None):
466 """changeid can be a changeset revision, node, or tag.
478 """changeid can be a changeset revision, node, or tag.
467 fileid can be a file revision or node."""
479 fileid can be a file revision or node."""
468 self._repo = repo
480 self._repo = repo
469 self._path = path
481 self._path = path
470 self._changeid = None
482 self._changeid = None
471 self._filerev = self._filenode = None
483 self._filerev = self._filenode = None
472
484
473 if filelog:
485 if filelog:
474 self._filelog = filelog
486 self._filelog = filelog
475 if workingctx:
487 if workingctx:
476 self._changectx = workingctx
488 self._changectx = workingctx
477
489
478 def __getattr__(self, name):
490 def __getattr__(self, name):
479 if name == '_changectx':
491 if name == '_changectx':
480 self._changectx = workingctx(self._repo)
492 self._changectx = workingctx(self._repo)
481 return self._changectx
493 return self._changectx
482 elif name == '_repopath':
494 elif name == '_repopath':
483 self._repopath = (self._repo.dirstate.copied(self._path)
495 self._repopath = (self._repo.dirstate.copied(self._path)
484 or self._path)
496 or self._path)
485 return self._repopath
497 return self._repopath
486 elif name == '_filelog':
498 elif name == '_filelog':
487 self._filelog = self._repo.file(self._repopath)
499 self._filelog = self._repo.file(self._repopath)
488 return self._filelog
500 return self._filelog
489 else:
501 else:
490 raise AttributeError, name
502 raise AttributeError, name
491
503
492 def __nonzero__(self):
504 def __nonzero__(self):
493 return True
505 return True
494
506
495 def __str__(self):
507 def __str__(self):
496 return "%s@%s" % (self.path(), self._changectx)
508 return "%s@%s" % (self.path(), self._changectx)
497
509
498 def filectx(self, fileid):
510 def filectx(self, fileid):
499 '''opens an arbitrary revision of the file without
511 '''opens an arbitrary revision of the file without
500 opening a new filelog'''
512 opening a new filelog'''
501 return filectx(self._repo, self._repopath, fileid=fileid,
513 return filectx(self._repo, self._repopath, fileid=fileid,
502 filelog=self._filelog)
514 filelog=self._filelog)
503
515
504 def rev(self):
516 def rev(self):
505 if '_changectx' in self.__dict__:
517 if '_changectx' in self.__dict__:
506 return self._changectx.rev()
518 return self._changectx.rev()
507 return self._filelog.linkrev(self._filenode)
519 return self._filelog.linkrev(self._filenode)
508
520
509 def data(self): return self._repo.wread(self._path)
521 def data(self): return self._repo.wread(self._path)
510 def renamed(self):
522 def renamed(self):
511 rp = self._repopath
523 rp = self._repopath
512 if rp == self._path:
524 if rp == self._path:
513 return None
525 return None
514 return rp, self._changectx._parents[0]._manifest.get(rp, nullid)
526 return rp, self._changectx._parents[0]._manifest.get(rp, nullid)
515
527
516 def parents(self):
528 def parents(self):
517 '''return parent filectxs, following copies if necessary'''
529 '''return parent filectxs, following copies if necessary'''
518 p = self._path
530 p = self._path
519 rp = self._repopath
531 rp = self._repopath
520 pcl = self._changectx._parents
532 pcl = self._changectx._parents
521 fl = self._filelog
533 fl = self._filelog
522 pl = [(rp, pcl[0]._manifest.get(rp, nullid), fl)]
534 pl = [(rp, pcl[0]._manifest.get(rp, nullid), fl)]
523 if len(pcl) > 1:
535 if len(pcl) > 1:
524 if rp != p:
536 if rp != p:
525 fl = None
537 fl = None
526 pl.append((p, pcl[1]._manifest.get(p, nullid), fl))
538 pl.append((p, pcl[1]._manifest.get(p, nullid), fl))
527
539
528 return [filectx(self._repo, p, fileid=n, filelog=l)
540 return [filectx(self._repo, p, fileid=n, filelog=l)
529 for p,n,l in pl if n != nullid]
541 for p,n,l in pl if n != nullid]
530
542
531 def children(self):
543 def children(self):
532 return []
544 return []
533
545
534 def size(self): return os.stat(self._repo.wjoin(self._path)).st_size
546 def size(self): return os.stat(self._repo.wjoin(self._path)).st_size
535 def date(self):
547 def date(self):
536 t, tz = self._changectx.date()
548 t, tz = self._changectx.date()
537 try:
549 try:
538 return (int(os.lstat(self._repo.wjoin(self._path)).st_mtime), tz)
550 return (int(os.lstat(self._repo.wjoin(self._path)).st_mtime), tz)
539 except OSError, err:
551 except OSError, err:
540 if err.errno != errno.ENOENT: raise
552 if err.errno != errno.ENOENT: raise
541 return (t, tz)
553 return (t, tz)
542
554
543 def cmp(self, text): return self._repo.wread(self._path) == text
555 def cmp(self, text): return self._repo.wread(self._path) == text
General Comments 0
You need to be logged in to leave comments. Login now