comparison mercurial/manifest.py @ 1089:142b5d5ec9cc

Break apart hg.py - move the various parts of hg.py into their own files - create node.py to store node manipulation functions
author mpm@selenic.com
date Sat, 27 Aug 2005 14:21:25 -0700
parents mercurial/hg.py@05dc7aba22eb
children 50a0a36dd48a
comparison
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