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