eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/mercurial/manifest.py
changeset 69 c6bca38c1cbf
equal deleted inserted replaced
68:5ff1fc726848 69:c6bca38c1cbf
       
     1 # manifest.py - manifest revision class for mercurial
       
     2 #
       
     3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
       
     4 #
       
     5 # This software may be used and distributed according to the terms of the
       
     6 # GNU General Public License version 2 or any later version.
       
     7 
       
     8 from i18n import _
       
     9 import mdiff, parsers, error, revlog
       
    10 import array, struct
       
    11 
       
    12 class manifestdict(dict):
       
    13     def __init__(self, mapping=None, flags=None):
       
    14         if mapping is None:
       
    15             mapping = {}
       
    16         if flags is None:
       
    17             flags = {}
       
    18         dict.__init__(self, mapping)
       
    19         self._flags = flags
       
    20     def flags(self, f):
       
    21         return self._flags.get(f, "")
       
    22     def set(self, f, flags):
       
    23         self._flags[f] = flags
       
    24     def copy(self):
       
    25         return manifestdict(self, dict.copy(self._flags))
       
    26 
       
    27 class manifest(revlog.revlog):
       
    28     def __init__(self, opener):
       
    29         self._mancache = None
       
    30         revlog.revlog.__init__(self, opener, "00manifest.i")
       
    31 
       
    32     def parse(self, lines):
       
    33         mfdict = manifestdict()
       
    34         parsers.parse_manifest(mfdict, mfdict._flags, lines)
       
    35         return mfdict
       
    36 
       
    37     def readdelta(self, node):
       
    38         r = self.rev(node)
       
    39         return self.parse(mdiff.patchtext(self.revdiff(self.deltaparent(r), r)))
       
    40 
       
    41     def read(self, node):
       
    42         if node == revlog.nullid:
       
    43             return manifestdict() # don't upset local cache
       
    44         if self._mancache and self._mancache[0] == node:
       
    45             return self._mancache[1]
       
    46         text = self.revision(node)
       
    47         arraytext = array.array('c', text)
       
    48         mapping = self.parse(text)
       
    49         self._mancache = (node, mapping, arraytext)
       
    50         return mapping
       
    51 
       
    52     def _search(self, m, s, lo=0, hi=None):
       
    53         '''return a tuple (start, end) that says where to find s within m.
       
    54 
       
    55         If the string is found m[start:end] are the line containing
       
    56         that string.  If start == end the string was not found and
       
    57         they indicate the proper sorted insertion point.  This was
       
    58         taken from bisect_left, and modified to find line start/end as
       
    59         it goes along.
       
    60 
       
    61         m should be a buffer or a string
       
    62         s is a string'''
       
    63         def advance(i, c):
       
    64             while i < lenm and m[i] != c:
       
    65                 i += 1
       
    66             return i
       
    67         if not s:
       
    68             return (lo, lo)
       
    69         lenm = len(m)
       
    70         if not hi:
       
    71             hi = lenm
       
    72         while lo < hi:
       
    73             mid = (lo + hi) // 2
       
    74             start = mid
       
    75             while start > 0 and m[start - 1] != '\n':
       
    76                 start -= 1
       
    77             end = advance(start, '\0')
       
    78             if m[start:end] < s:
       
    79                 # we know that after the null there are 40 bytes of sha1
       
    80                 # this translates to the bisect lo = mid + 1
       
    81                 lo = advance(end + 40, '\n') + 1
       
    82             else:
       
    83                 # this translates to the bisect hi = mid
       
    84                 hi = start
       
    85         end = advance(lo, '\0')
       
    86         found = m[lo:end]
       
    87         if s == found:
       
    88             # we know that after the null there are 40 bytes of sha1
       
    89             end = advance(end + 40, '\n')
       
    90             return (lo, end + 1)
       
    91         else:
       
    92             return (lo, lo)
       
    93 
       
    94     def find(self, node, f):
       
    95         '''look up entry for a single file efficiently.
       
    96         return (node, flags) pair if found, (None, None) if not.'''
       
    97         if self._mancache and self._mancache[0] == node:
       
    98             return self._mancache[1].get(f), self._mancache[1].flags(f)
       
    99         text = self.revision(node)
       
   100         start, end = self._search(text, f)
       
   101         if start == end:
       
   102             return None, None
       
   103         l = text[start:end]
       
   104         f, n = l.split('\0')
       
   105         return revlog.bin(n[:40]), n[40:-1]
       
   106 
       
   107     def add(self, map, transaction, link, p1=None, p2=None,
       
   108             changed=None):
       
   109         # apply the changes collected during the bisect loop to our addlist
       
   110         # return a delta suitable for addrevision
       
   111         def addlistdelta(addlist, x):
       
   112             # start from the bottom up
       
   113             # so changes to the offsets don't mess things up.
       
   114             for start, end, content in reversed(x):
       
   115                 if content:
       
   116                     addlist[start:end] = array.array('c', content)
       
   117                 else:
       
   118                     del addlist[start:end]
       
   119             return "".join(struct.pack(">lll", start, end, len(content)) + content
       
   120                            for start, end, content in x)
       
   121 
       
   122         def checkforbidden(l):
       
   123             for f in l:
       
   124                 if '\n' in f or '\r' in f:
       
   125                     raise error.RevlogError(
       
   126                         _("'\\n' and '\\r' disallowed in filenames: %r") % f)
       
   127 
       
   128         # if we're using the cache, make sure it is valid and
       
   129         # parented by the same node we're diffing against
       
   130         if not (changed and self._mancache and p1 and self._mancache[0] == p1):
       
   131             files = sorted(map)
       
   132             checkforbidden(files)
       
   133 
       
   134             # if this is changed to support newlines in filenames,
       
   135             # be sure to check the templates/ dir again (especially *-raw.tmpl)
       
   136             hex, flags = revlog.hex, map.flags
       
   137             text = ''.join("%s\000%s%s\n" % (f, hex(map[f]), flags(f))
       
   138                            for f in files)
       
   139             arraytext = array.array('c', text)
       
   140             cachedelta = None
       
   141         else:
       
   142             added, removed = changed
       
   143             addlist = self._mancache[2]
       
   144 
       
   145             checkforbidden(added)
       
   146             # combine the changed lists into one list for sorting
       
   147             work = [(x, False) for x in added]
       
   148             work.extend((x, True) for x in removed)
       
   149             # this could use heapq.merge() (from python2.6+) or equivalent
       
   150             # since the lists are already sorted
       
   151             work.sort()
       
   152 
       
   153             delta = []
       
   154             dstart = None
       
   155             dend = None
       
   156             dline = [""]
       
   157             start = 0
       
   158             # zero copy representation of addlist as a buffer
       
   159             addbuf = buffer(addlist)
       
   160 
       
   161             # start with a readonly loop that finds the offset of
       
   162             # each line and creates the deltas
       
   163             for f, todelete in work:
       
   164                 # bs will either be the index of the item or the insert point
       
   165                 start, end = self._search(addbuf, f, start)
       
   166                 if not todelete:
       
   167                     l = "%s\000%s%s\n" % (f, revlog.hex(map[f]), map.flags(f))
       
   168                 else:
       
   169                     if start == end:
       
   170                         # item we want to delete was not found, error out
       
   171                         raise AssertionError(
       
   172                                 _("failed to remove %s from manifest") % f)
       
   173                     l = ""
       
   174                 if dstart != None and dstart <= start and dend >= start:
       
   175                     if dend < end:
       
   176                         dend = end
       
   177                     if l:
       
   178                         dline.append(l)
       
   179                 else:
       
   180                     if dstart != None:
       
   181                         delta.append([dstart, dend, "".join(dline)])
       
   182                     dstart = start
       
   183                     dend = end
       
   184                     dline = [l]
       
   185 
       
   186             if dstart != None:
       
   187                 delta.append([dstart, dend, "".join(dline)])
       
   188             # apply the delta to the addlist, and get a delta for addrevision
       
   189             cachedelta = (self.rev(p1), addlistdelta(addlist, delta))
       
   190             arraytext = addlist
       
   191             text = buffer(arraytext)
       
   192 
       
   193         n = self.addrevision(text, transaction, link, p1, p2, cachedelta)
       
   194         self._mancache = (n, map, arraytext)
       
   195 
       
   196         return n