mercurial/manifest.py
author mpm@selenic.com
Thu, 08 Sep 2005 17:09:31 -0700
changeset 1225 ea90162e210c
parent 1098 50a0a36dd48a
child 1400 cf9a1233738a
permissions -rw-r--r--
Add --debugger global option With this option, hg will drop into the Python debugger on execution. Running 'continue' will execute normally, and the debugger will be reinvoked if an exception is raised.

# 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:
                raise AssertionError("sortdiff failed!")
            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:
                        raise AssertionError(
                            "failed to remove %s from manifest\n" % f)
                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:
            raise AssertionError("manifest delta failure\n")
        n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
        self.mapcache = (n, map, flags)
        self.listcache = (text, self.addlist)
        self.addlist = None

        return n