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