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