2 # |
2 # |
3 # Copyright 2006 Matt Mackall <mpm@selenic.com> |
3 # Copyright 2006 Matt Mackall <mpm@selenic.com> |
4 # |
4 # |
5 # This software may be used and distributed according to the terms |
5 # This software may be used and distributed according to the terms |
6 # of the GNU General Public License, incorporated herein by reference. |
6 # of the GNU General Public License, incorporated herein by reference. |
|
7 |
|
8 from demandload import * |
|
9 from node import * |
|
10 demandload(globals(), 'bdiff') |
7 |
11 |
8 from node import * |
12 from node import * |
9 from demandload import demandload |
13 from demandload import demandload |
10 demandload(globals(), "ancestor util") |
14 demandload(globals(), "ancestor util") |
11 |
15 |
163 # hard for renames |
167 # hard for renames |
164 c = self._filelog.children(self._filenode) |
168 c = self._filelog.children(self._filenode) |
165 return [ filectx(self._repo, self._path, fileid=x, |
169 return [ filectx(self._repo, self._path, fileid=x, |
166 filelog=self._filelog) for x in c ] |
170 filelog=self._filelog) for x in c ] |
167 |
171 |
168 def annotate(self): |
172 def annotate(self, follow=False): |
169 getctx = util.cachefunc(lambda x: filectx(self._repo, self._path, |
173 '''returns a list of tuples of (ctx, line) for each line |
170 changeid=x, |
174 in the file, where ctx is the filectx of the node where |
171 filelog=self._filelog)) |
175 that line was last changed''' |
172 hist = self._filelog.annotate(self._filenode) |
176 |
173 |
177 def decorate(text, rev): |
174 return [(getctx(rev), line) for rev, line in hist] |
178 return ([rev] * len(text.splitlines()), text) |
|
179 |
|
180 def pair(parent, child): |
|
181 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]): |
|
182 child[0][b1:b2] = parent[0][a1:a2] |
|
183 return child |
|
184 |
|
185 getlog = util.cachefunc(lambda x: self._repo.file(x)) |
|
186 def getctx(path, fileid): |
|
187 log = path == self._path and self._filelog or getlog(path) |
|
188 return filectx(self._repo, path, fileid=fileid, filelog=log) |
|
189 getctx = util.cachefunc(getctx) |
|
190 |
|
191 def parents(f): |
|
192 # we want to reuse filectx objects as much as possible |
|
193 p = f._path |
|
194 pl = [ (p, f._filelog.rev(n)) for n in f._filelog.parents(f._filenode) ] |
|
195 |
|
196 if follow: |
|
197 r = f.renamed() |
|
198 if r: |
|
199 pl[0] = (r[0], getlog(r[0]).rev(r[1])) |
|
200 |
|
201 return [ getctx(p, n) for p, n in pl if n != -1 ] |
|
202 |
|
203 # find all ancestors |
|
204 needed = {self: 1} |
|
205 visit = [self] |
|
206 files = [self._path] |
|
207 while visit: |
|
208 f = visit.pop(0) |
|
209 for p in parents(f): |
|
210 if p not in needed: |
|
211 needed[p] = 1 |
|
212 visit.append(p) |
|
213 if p._path not in files: |
|
214 files.append(p._path) |
|
215 else: |
|
216 # count how many times we'll use this |
|
217 needed[p] += 1 |
|
218 |
|
219 # sort by revision (per file) which is a topological order |
|
220 visit = [] |
|
221 files.reverse() |
|
222 for f in files: |
|
223 fn = [(n._filerev, n) for n in needed.keys() if n._path == f] |
|
224 fn.sort() |
|
225 visit.extend(fn) |
|
226 hist = {} |
|
227 |
|
228 for r, f in visit: |
|
229 curr = decorate(f.data(), f) |
|
230 for p in parents(f): |
|
231 if p != nullid: |
|
232 curr = pair(hist[p], curr) |
|
233 # trim the history of unneeded revs |
|
234 needed[p] -= 1 |
|
235 if not needed[p]: |
|
236 del hist[p] |
|
237 hist[f] = curr |
|
238 |
|
239 return zip(hist[f][0], hist[f][1].splitlines(1)) |
175 |
240 |
176 def ancestor(self, fc2): |
241 def ancestor(self, fc2): |
177 """ |
242 """ |
178 find the common ancestor file context, if any, of self, and fc2 |
243 find the common ancestor file context, if any, of self, and fc2 |
179 """ |
244 """ |