mercurial/filelog.py
changeset 1089 142b5d5ec9cc
parent 1072 05dc7aba22eb
child 1116 0cdd73b0767c
equal deleted inserted replaced
1088:39b916b1d8e4 1089:142b5d5ec9cc
       
     1 # filelog.py - file history class for mercurial
       
     2 #
       
     3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
       
     4 #
       
     5 # This software may be used and distributed according to the terms
       
     6 # of the GNU General Public License, incorporated herein by reference.
       
     7 
       
     8 import os
       
     9 from revlog import *
       
    10 from demandload import *
       
    11 demandload(globals(), "bdiff")
       
    12 
       
    13 class filelog(revlog):
       
    14     def __init__(self, opener, path):
       
    15         revlog.__init__(self, opener,
       
    16                         os.path.join("data", self.encodedir(path + ".i")),
       
    17                         os.path.join("data", self.encodedir(path + ".d")))
       
    18 
       
    19     # This avoids a collision between a file named foo and a dir named
       
    20     # foo.i or foo.d
       
    21     def encodedir(self, path):
       
    22         return (path
       
    23                 .replace(".hg/", ".hg.hg/")
       
    24                 .replace(".i/", ".i.hg/")
       
    25                 .replace(".d/", ".d.hg/"))
       
    26 
       
    27     def decodedir(self, path):
       
    28         return (path
       
    29                 .replace(".d.hg/", ".d/")
       
    30                 .replace(".i.hg/", ".i/")
       
    31                 .replace(".hg.hg/", ".hg/"))
       
    32 
       
    33     def read(self, node):
       
    34         t = self.revision(node)
       
    35         if not t.startswith('\1\n'):
       
    36             return t
       
    37         s = t.find('\1\n', 2)
       
    38         return t[s+2:]
       
    39 
       
    40     def readmeta(self, node):
       
    41         t = self.revision(node)
       
    42         if not t.startswith('\1\n'):
       
    43             return t
       
    44         s = t.find('\1\n', 2)
       
    45         mt = t[2:s]
       
    46         for l in mt.splitlines():
       
    47             k, v = l.split(": ", 1)
       
    48             m[k] = v
       
    49         return m
       
    50 
       
    51     def add(self, text, meta, transaction, link, p1=None, p2=None):
       
    52         if meta or text.startswith('\1\n'):
       
    53             mt = ""
       
    54             if meta:
       
    55                 mt = [ "%s: %s\n" % (k, v) for k,v in meta.items() ]
       
    56             text = "\1\n" + "".join(mt) + "\1\n" + text
       
    57         return self.addrevision(text, transaction, link, p1, p2)
       
    58 
       
    59     def annotate(self, node):
       
    60 
       
    61         def decorate(text, rev):
       
    62             return ([rev] * len(text.splitlines()), text)
       
    63 
       
    64         def pair(parent, child):
       
    65             for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
       
    66                 child[0][b1:b2] = parent[0][a1:a2]
       
    67             return child
       
    68 
       
    69         # find all ancestors
       
    70         needed = {node:1}
       
    71         visit = [node]
       
    72         while visit:
       
    73             n = visit.pop(0)
       
    74             for p in self.parents(n):
       
    75                 if p not in needed:
       
    76                     needed[p] = 1
       
    77                     visit.append(p)
       
    78                 else:
       
    79                     # count how many times we'll use this
       
    80                     needed[p] += 1
       
    81 
       
    82         # sort by revision which is a topological order
       
    83         visit = [ (self.rev(n), n) for n in needed.keys() ]
       
    84         visit.sort()
       
    85         hist = {}
       
    86 
       
    87         for r,n in visit:
       
    88             curr = decorate(self.read(n), self.linkrev(n))
       
    89             for p in self.parents(n):
       
    90                 if p != nullid:
       
    91                     curr = pair(hist[p], curr)
       
    92                     # trim the history of unneeded revs
       
    93                     needed[p] -= 1
       
    94                     if not needed[p]:
       
    95                         del hist[p]
       
    96             hist[n] = curr
       
    97 
       
    98         return zip(hist[n][0], hist[n][1].splitlines(1))