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