mercurial/manifest.py
changeset 1089 142b5d5ec9cc
parent 1072 05dc7aba22eb
child 1098 50a0a36dd48a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/manifest.py	Sat Aug 27 14:21:25 2005 -0700
@@ -0,0 +1,170 @@
+# manifest.py - manifest revision class for mercurial
+#
+# Copyright 2005 Matt Mackall <mpm@selenic.com>
+#
+# This software may be used and distributed according to the terms
+# of the GNU General Public License, incorporated herein by reference.
+
+import sys, struct
+from revlog import *
+from demandload import *
+demandload(globals(), "bisect")
+
+class manifest(revlog):
+    def __init__(self, opener):
+        self.mapcache = None
+        self.listcache = None
+        self.addlist = None
+        revlog.__init__(self, opener, "00manifest.i", "00manifest.d")
+
+    def read(self, node):
+        if node == nullid: return {} # don't upset local cache
+        if self.mapcache and self.mapcache[0] == node:
+            return self.mapcache[1]
+        text = self.revision(node)
+        map = {}
+        flag = {}
+        self.listcache = (text, text.splitlines(1))
+        for l in self.listcache[1]:
+            (f, n) = l.split('\0')
+            map[f] = bin(n[:40])
+            flag[f] = (n[40:-1] == "x")
+        self.mapcache = (node, map, flag)
+        return map
+
+    def readflags(self, node):
+        if node == nullid: return {} # don't upset local cache
+        if not self.mapcache or self.mapcache[0] != node:
+            self.read(node)
+        return self.mapcache[2]
+
+    def diff(self, a, b):
+        # this is sneaky, as we're not actually using a and b
+        if self.listcache and self.addlist and self.listcache[0] == a:
+            d = mdiff.diff(self.listcache[1], self.addlist, 1)
+            if mdiff.patch(a, d) != b:
+                sys.stderr.write("*** sortdiff failed, falling back ***\n")
+                return mdiff.textdiff(a, b)
+            return d
+        else:
+            return mdiff.textdiff(a, b)
+
+    def add(self, map, flags, transaction, link, p1=None, p2=None,
+            changed=None):
+        # directly generate the mdiff delta from the data collected during
+        # the bisect loop below
+        def gendelta(delta):
+            i = 0
+            result = []
+            while i < len(delta):
+                start = delta[i][2]
+                end = delta[i][3]
+                l = delta[i][4]
+                if l == None:
+                    l = ""
+                while i < len(delta) - 1 and start <= delta[i+1][2] \
+                          and end >= delta[i+1][2]:
+                    if delta[i+1][3] > end:
+                        end = delta[i+1][3]
+                    if delta[i+1][4]:
+                        l += delta[i+1][4]
+                    i += 1
+                result.append(struct.pack(">lll", start, end, len(l)) +  l)
+                i += 1
+            return result
+
+        # apply the changes collected during the bisect loop to our addlist
+        def addlistdelta(addlist, delta):
+            # apply the deltas to the addlist.  start from the bottom up
+            # so changes to the offsets don't mess things up.
+            i = len(delta)
+            while i > 0:
+                i -= 1
+                start = delta[i][0]
+                end = delta[i][1]
+                if delta[i][4]:
+                    addlist[start:end] = [delta[i][4]]
+                else:
+                    del addlist[start:end]
+            return addlist
+
+        # calculate the byte offset of the start of each line in the
+        # manifest
+        def calcoffsets(addlist):
+            offsets = [0] * (len(addlist) + 1)
+            offset = 0
+            i = 0
+            while i < len(addlist):
+                offsets[i] = offset
+                offset += len(addlist[i])
+                i += 1
+            offsets[i] = offset
+            return offsets
+
+        # if we're using the listcache, make sure it is valid and
+        # parented by the same node we're diffing against
+        if not changed or not self.listcache or not p1 or \
+               self.mapcache[0] != p1:
+            files = map.keys()
+            files.sort()
+
+            self.addlist = ["%s\000%s%s\n" %
+                            (f, hex(map[f]), flags[f] and "x" or '')
+                            for f in files]
+            cachedelta = None
+        else:
+            addlist = self.listcache[1]
+
+            # find the starting offset for each line in the add list
+            offsets = calcoffsets(addlist)
+
+            # combine the changed lists into one list for sorting
+            work = [[x, 0] for x in changed[0]]
+            work[len(work):] = [[x, 1] for x in changed[1]]
+            work.sort()
+
+            delta = []
+            bs = 0
+
+            for w in work:
+                f = w[0]
+                # bs will either be the index of the item or the insert point
+                bs = bisect.bisect(addlist, f, bs)
+                if bs < len(addlist):
+                    fn = addlist[bs][:addlist[bs].index('\0')]
+                else:
+                    fn = None
+                if w[1] == 0:
+                    l = "%s\000%s%s\n" % (f, hex(map[f]),
+                                          flags[f] and "x" or '')
+                else:
+                    l = None
+                start = bs
+                if fn != f:
+                    # item not found, insert a new one
+                    end = bs
+                    if w[1] == 1:
+                        sys.stderr.write("failed to remove %s from manifest\n"
+                                         % f)
+                        sys.exit(1)
+                else:
+                    # item is found, replace/delete the existing line
+                    end = bs + 1
+                delta.append([start, end, offsets[start], offsets[end], l])
+
+            self.addlist = addlistdelta(addlist, delta)
+            if self.mapcache[0] == self.tip():
+                cachedelta = "".join(gendelta(delta))
+            else:
+                cachedelta = None
+
+        text = "".join(self.addlist)
+        if cachedelta and mdiff.patch(self.listcache[0], cachedelta) != text:
+            sys.stderr.write("manifest delta failure\n")
+            sys.exit(1)
+        n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
+        self.mapcache = (n, map, flags)
+        self.listcache = (text, self.addlist)
+        self.addlist = None
+
+        return n