mercurial/manifest.py
changeset 1089 142b5d5ec9cc
parent 1072 05dc7aba22eb
child 1098 50a0a36dd48a
equal deleted inserted replaced
1088:39b916b1d8e4 1089:142b5d5ec9cc
       
     1 # manifest.py - manifest revision 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 sys, struct
       
     9 from revlog import *
       
    10 from demandload import *
       
    11 demandload(globals(), "bisect")
       
    12 
       
    13 class manifest(revlog):
       
    14     def __init__(self, opener):
       
    15         self.mapcache = None
       
    16         self.listcache = None
       
    17         self.addlist = None
       
    18         revlog.__init__(self, opener, "00manifest.i", "00manifest.d")
       
    19 
       
    20     def read(self, node):
       
    21         if node == nullid: return {} # don't upset local cache
       
    22         if self.mapcache and self.mapcache[0] == node:
       
    23             return self.mapcache[1]
       
    24         text = self.revision(node)
       
    25         map = {}
       
    26         flag = {}
       
    27         self.listcache = (text, text.splitlines(1))
       
    28         for l in self.listcache[1]:
       
    29             (f, n) = l.split('\0')
       
    30             map[f] = bin(n[:40])
       
    31             flag[f] = (n[40:-1] == "x")
       
    32         self.mapcache = (node, map, flag)
       
    33         return map
       
    34 
       
    35     def readflags(self, node):
       
    36         if node == nullid: return {} # don't upset local cache
       
    37         if not self.mapcache or self.mapcache[0] != node:
       
    38             self.read(node)
       
    39         return self.mapcache[2]
       
    40 
       
    41     def diff(self, a, b):
       
    42         # this is sneaky, as we're not actually using a and b
       
    43         if self.listcache and self.addlist and self.listcache[0] == a:
       
    44             d = mdiff.diff(self.listcache[1], self.addlist, 1)
       
    45             if mdiff.patch(a, d) != b:
       
    46                 sys.stderr.write("*** sortdiff failed, falling back ***\n")
       
    47                 return mdiff.textdiff(a, b)
       
    48             return d
       
    49         else:
       
    50             return mdiff.textdiff(a, b)
       
    51 
       
    52     def add(self, map, flags, transaction, link, p1=None, p2=None,
       
    53             changed=None):
       
    54         # directly generate the mdiff delta from the data collected during
       
    55         # the bisect loop below
       
    56         def gendelta(delta):
       
    57             i = 0
       
    58             result = []
       
    59             while i < len(delta):
       
    60                 start = delta[i][2]
       
    61                 end = delta[i][3]
       
    62                 l = delta[i][4]
       
    63                 if l == None:
       
    64                     l = ""
       
    65                 while i < len(delta) - 1 and start <= delta[i+1][2] \
       
    66                           and end >= delta[i+1][2]:
       
    67                     if delta[i+1][3] > end:
       
    68                         end = delta[i+1][3]
       
    69                     if delta[i+1][4]:
       
    70                         l += delta[i+1][4]
       
    71                     i += 1
       
    72                 result.append(struct.pack(">lll", start, end, len(l)) +  l)
       
    73                 i += 1
       
    74             return result
       
    75 
       
    76         # apply the changes collected during the bisect loop to our addlist
       
    77         def addlistdelta(addlist, delta):
       
    78             # apply the deltas to the addlist.  start from the bottom up
       
    79             # so changes to the offsets don't mess things up.
       
    80             i = len(delta)
       
    81             while i > 0:
       
    82                 i -= 1
       
    83                 start = delta[i][0]
       
    84                 end = delta[i][1]
       
    85                 if delta[i][4]:
       
    86                     addlist[start:end] = [delta[i][4]]
       
    87                 else:
       
    88                     del addlist[start:end]
       
    89             return addlist
       
    90 
       
    91         # calculate the byte offset of the start of each line in the
       
    92         # manifest
       
    93         def calcoffsets(addlist):
       
    94             offsets = [0] * (len(addlist) + 1)
       
    95             offset = 0
       
    96             i = 0
       
    97             while i < len(addlist):
       
    98                 offsets[i] = offset
       
    99                 offset += len(addlist[i])
       
   100                 i += 1
       
   101             offsets[i] = offset
       
   102             return offsets
       
   103 
       
   104         # if we're using the listcache, make sure it is valid and
       
   105         # parented by the same node we're diffing against
       
   106         if not changed or not self.listcache or not p1 or \
       
   107                self.mapcache[0] != p1:
       
   108             files = map.keys()
       
   109             files.sort()
       
   110 
       
   111             self.addlist = ["%s\000%s%s\n" %
       
   112                             (f, hex(map[f]), flags[f] and "x" or '')
       
   113                             for f in files]
       
   114             cachedelta = None
       
   115         else:
       
   116             addlist = self.listcache[1]
       
   117 
       
   118             # find the starting offset for each line in the add list
       
   119             offsets = calcoffsets(addlist)
       
   120 
       
   121             # combine the changed lists into one list for sorting
       
   122             work = [[x, 0] for x in changed[0]]
       
   123             work[len(work):] = [[x, 1] for x in changed[1]]
       
   124             work.sort()
       
   125 
       
   126             delta = []
       
   127             bs = 0
       
   128 
       
   129             for w in work:
       
   130                 f = w[0]
       
   131                 # bs will either be the index of the item or the insert point
       
   132                 bs = bisect.bisect(addlist, f, bs)
       
   133                 if bs < len(addlist):
       
   134                     fn = addlist[bs][:addlist[bs].index('\0')]
       
   135                 else:
       
   136                     fn = None
       
   137                 if w[1] == 0:
       
   138                     l = "%s\000%s%s\n" % (f, hex(map[f]),
       
   139                                           flags[f] and "x" or '')
       
   140                 else:
       
   141                     l = None
       
   142                 start = bs
       
   143                 if fn != f:
       
   144                     # item not found, insert a new one
       
   145                     end = bs
       
   146                     if w[1] == 1:
       
   147                         sys.stderr.write("failed to remove %s from manifest\n"
       
   148                                          % f)
       
   149                         sys.exit(1)
       
   150                 else:
       
   151                     # item is found, replace/delete the existing line
       
   152                     end = bs + 1
       
   153                 delta.append([start, end, offsets[start], offsets[end], l])
       
   154 
       
   155             self.addlist = addlistdelta(addlist, delta)
       
   156             if self.mapcache[0] == self.tip():
       
   157                 cachedelta = "".join(gendelta(delta))
       
   158             else:
       
   159                 cachedelta = None
       
   160 
       
   161         text = "".join(self.addlist)
       
   162         if cachedelta and mdiff.patch(self.listcache[0], cachedelta) != text:
       
   163             sys.stderr.write("manifest delta failure\n")
       
   164             sys.exit(1)
       
   165         n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
       
   166         self.mapcache = (n, map, flags)
       
   167         self.listcache = (text, self.addlist)
       
   168         self.addlist = None
       
   169 
       
   170         return n