filelog.py
155 lines
| 4.9 KiB
| text/x-python
|
PythonLexer
/ mercurial / filelog.py
mpm@selenic.com
|
r1089 | # filelog.py - file history class for mercurial | ||
# | ||||
Vadim Gelfer
|
r2859 | # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com> | ||
mpm@selenic.com
|
r1089 | # | ||
# This software may be used and distributed according to the terms | ||||
# of the GNU General Public License, incorporated herein by reference. | ||||
from revlog import * | ||||
from demandload import * | ||||
Vadim Gelfer
|
r2470 | demandload(globals(), "bdiff os") | ||
mpm@selenic.com
|
r1089 | |||
class filelog(revlog): | ||||
mason@suse.com
|
r2222 | def __init__(self, opener, path, defversion=REVLOG_DEFAULT_VERSION): | ||
mpm@selenic.com
|
r1089 | revlog.__init__(self, opener, | ||
os.path.join("data", self.encodedir(path + ".i")), | ||||
mason@suse.com
|
r2072 | os.path.join("data", self.encodedir(path + ".d")), | ||
defversion) | ||||
mpm@selenic.com
|
r1089 | |||
# This avoids a collision between a file named foo and a dir named | ||||
# foo.i or foo.d | ||||
def encodedir(self, path): | ||||
return (path | ||||
.replace(".hg/", ".hg.hg/") | ||||
.replace(".i/", ".i.hg/") | ||||
.replace(".d/", ".d.hg/")) | ||||
def decodedir(self, path): | ||||
return (path | ||||
.replace(".d.hg/", ".d/") | ||||
.replace(".i.hg/", ".i/") | ||||
.replace(".hg.hg/", ".hg/")) | ||||
def read(self, node): | ||||
t = self.revision(node) | ||||
if not t.startswith('\1\n'): | ||||
return t | ||||
Benoit Boissinot
|
r2579 | s = t.index('\1\n', 2) | ||
mpm@selenic.com
|
r1089 | return t[s+2:] | ||
Matt Mackall
|
r3123 | def _readmeta(self, node): | ||
mpm@selenic.com
|
r1089 | t = self.revision(node) | ||
if not t.startswith('\1\n'): | ||||
mpm@selenic.com
|
r1116 | return {} | ||
Benoit Boissinot
|
r2579 | s = t.index('\1\n', 2) | ||
mpm@selenic.com
|
r1089 | mt = t[2:s] | ||
mpm@selenic.com
|
r1116 | m = {} | ||
mpm@selenic.com
|
r1089 | for l in mt.splitlines(): | ||
k, v = l.split(": ", 1) | ||||
m[k] = v | ||||
return m | ||||
def add(self, text, meta, transaction, link, p1=None, p2=None): | ||||
if meta or text.startswith('\1\n'): | ||||
mt = "" | ||||
if meta: | ||||
mt = [ "%s: %s\n" % (k, v) for k,v in meta.items() ] | ||||
twaldmann@thinkmo.de
|
r1540 | text = "\1\n%s\1\n%s" % ("".join(mt), text) | ||
mpm@selenic.com
|
r1089 | return self.addrevision(text, transaction, link, p1, p2) | ||
mpm@selenic.com
|
r1116 | def renamed(self, node): | ||
Matt Mackall
|
r1595 | if self.parents(node)[0] != nullid: | ||
mpm@selenic.com
|
r1116 | return False | ||
Matt Mackall
|
r3123 | m = self._readmeta(node) | ||
mpm@selenic.com
|
r1116 | if m and m.has_key("copy"): | ||
return (m["copy"], bin(m["copyrev"])) | ||||
return False | ||||
Matt Mackall
|
r2898 | def size(self, rev): | ||
"""return the size of a given revision""" | ||||
# for revisions with renames, we have to go the slow way | ||||
node = self.node(rev) | ||||
if self.renamed(node): | ||||
return len(self.read(node)) | ||||
return revlog.size(self, rev) | ||||
Matt Mackall
|
r2887 | def cmp(self, node, text): | ||
"""compare text with a given file revision""" | ||||
# for renames, we have to go the slow way | ||||
if self.renamed(node): | ||||
t2 = self.read(node) | ||||
Matt Mackall
|
r2895 | return t2 != text | ||
Matt Mackall
|
r2887 | |||
Matt Mackall
|
r2890 | return revlog.cmp(self, node, text) | ||
Matt Mackall
|
r2887 | |||
mpm@selenic.com
|
r1089 | def annotate(self, node): | ||
def decorate(text, rev): | ||||
return ([rev] * len(text.splitlines()), text) | ||||
def pair(parent, child): | ||||
for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]): | ||||
child[0][b1:b2] = parent[0][a1:a2] | ||||
return child | ||||
# find all ancestors | ||||
Brendan Cully
|
r2948 | needed = {(self, node):1} | ||
files = [self] | ||||
visit = [(self, node)] | ||||
mpm@selenic.com
|
r1089 | while visit: | ||
Brendan Cully
|
r2948 | f, n = visit.pop(0) | ||
rn = f.renamed(n) | ||||
if rn: | ||||
f, n = rn | ||||
f = filelog(self.opener, f, self.defversion) | ||||
files.insert(0, f) | ||||
if (f, n) not in needed: | ||||
needed[(f, n)] = 1 | ||||
else: | ||||
needed[(f, n)] += 1 | ||||
for p in f.parents(n): | ||||
if p == nullid: | ||||
continue | ||||
if (f, p) not in needed: | ||||
needed[(f, p)] = 1 | ||||
visit.append((f, p)) | ||||
mpm@selenic.com
|
r1089 | else: | ||
# count how many times we'll use this | ||||
Brendan Cully
|
r2948 | needed[(f, p)] += 1 | ||
mpm@selenic.com
|
r1089 | |||
Brendan Cully
|
r2948 | # sort by revision (per file) which is a topological order | ||
visit = [] | ||||
for f in files: | ||||
fn = [(f.rev(n[1]), f, n[1]) for n in needed.keys() if n[0] == f] | ||||
fn.sort() | ||||
visit.extend(fn) | ||||
mpm@selenic.com
|
r1089 | hist = {} | ||
Brendan Cully
|
r2948 | for i in range(len(visit)): | ||
r, f, n = visit[i] | ||||
curr = decorate(f.read(n), f.linkrev(n)) | ||||
if r == -1: | ||||
continue | ||||
parents = f.parents(n) | ||||
# follow parents across renames | ||||
if r < 1 and i > 0: | ||||
j = i | ||||
while j > 0 and visit[j][1] == f: | ||||
j -= 1 | ||||
parents = (visit[j][2],) | ||||
f = visit[j][1] | ||||
else: | ||||
parents = f.parents(n) | ||||
for p in parents: | ||||
mpm@selenic.com
|
r1089 | if p != nullid: | ||
curr = pair(hist[p], curr) | ||||
# trim the history of unneeded revs | ||||
Brendan Cully
|
r2948 | needed[(f, p)] -= 1 | ||
if not needed[(f, p)]: | ||||
mpm@selenic.com
|
r1089 | del hist[p] | ||
hist[n] = curr | ||||
return zip(hist[n][0], hist[n][1].splitlines(1)) | ||||