/plugins/hg4idea/testData/bin/mercurial/manifest.py

https://bitbucket.org/nbargnesi/idea · Python · 200 lines · 141 code · 19 blank · 40 comment · 47 complexity · 438c801f2403c930833019aa88c39fc6 MD5 · raw file

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