##// END OF EJS Templates
Switch to simpler manifestdict
Matt Mackall -
r2839:b4f05ecf default
parent child Browse files
Show More
@@ -1,214 +1,202 b''
1 # manifest.py - manifest revision class for mercurial
1 # manifest.py - manifest revision class for mercurial
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from revlog import *
8 from revlog import *
9 from i18n import gettext as _
9 from i18n import gettext as _
10 from demandload import *
10 from demandload import *
11 demandload(globals(), "array bisect struct")
11 demandload(globals(), "array bisect struct")
12
12
13 class manifestdict(dict):
13 class manifestdict(dict):
14 def __init__(self, mapping={}):
14 def __init__(self, mapping={}, flags={}):
15 dict.__init__(self, mapping)
15 dict.__init__(self, mapping)
16 def __getitem__(self, f):
16 self._flags = flags
17 return self.node(f)
18 def get(self, f, default=None):
19 try:
20 return dict.__getitem__(self, f)[:20]
21 except KeyError:
22 return default
23 def __setitem__(self, f, node):
24 fl = self.flags(f)
25 dict.__setitem__(self, f, node + fl)
26 def node(self, f):
27 return dict.__getitem__(self, f)[:20]
28 def flags(self, f):
17 def flags(self, f):
29 return dict.get(self, f, "")[20:]
18 return self._flags.get(f, "")
30 def execf(self, f):
19 def execf(self, f):
31 "test for executable in manifest flags"
20 "test for executable in manifest flags"
32 return "x" in self.flags(f)
21 return "x" in self.flags(f)
33 def linkf(self, f):
22 def linkf(self, f):
34 "test for symlink in manifest flags"
23 "test for symlink in manifest flags"
35 return "l" in self.flags(f)
24 return "l" in self.flags(f)
36 def rawset(self, f, node, flags):
25 def rawset(self, f, entry):
37 dict.__setitem__(self, f, node + flags)
26 self[f] = bin(entry[:40])
27 fl = entry[40:-1]
28 if fl: self._flags[f] = fl
38 def set(self, f, execf=False, linkf=False):
29 def set(self, f, execf=False, linkf=False):
39 n = dict.get(self, f, nullid)[:20]
30 if execf: self._flags[f] = "x"
40 fl = ""
31 if linkf: self._flags[f] = "x"
41 if execf: fl = "x"
42 if linkf: fl = "l"
43 dict.__setitem__(self, f, n + fl)
44 def copy(self):
32 def copy(self):
45 return manifestdict(dict.copy(self))
33 return manifestdict(dict.copy(self), dict.copy(self._flags))
46
34
47 class manifest(revlog):
35 class manifest(revlog):
48 def __init__(self, opener, defversion=REVLOGV0):
36 def __init__(self, opener, defversion=REVLOGV0):
49 self.mapcache = None
37 self.mapcache = None
50 self.listcache = None
38 self.listcache = None
51 revlog.__init__(self, opener, "00manifest.i", "00manifest.d",
39 revlog.__init__(self, opener, "00manifest.i", "00manifest.d",
52 defversion)
40 defversion)
53
41
54 def read(self, node):
42 def read(self, node):
55 if node == nullid: return manifestdict() # don't upset local cache
43 if node == nullid: return manifestdict() # don't upset local cache
56 if self.mapcache and self.mapcache[0] == node:
44 if self.mapcache and self.mapcache[0] == node:
57 return self.mapcache[1]
45 return self.mapcache[1]
58 text = self.revision(node)
46 text = self.revision(node)
59 self.listcache = array.array('c', text)
47 self.listcache = array.array('c', text)
60 lines = text.splitlines(1)
48 lines = text.splitlines(1)
61 mapping = manifestdict()
49 mapping = manifestdict()
62 for l in lines:
50 for l in lines:
63 (f, n) = l.split('\0')
51 (f, n) = l.split('\0')
64 mapping.rawset(f, bin(n[:40]), n[40:-1])
52 mapping.rawset(f, n)
65 self.mapcache = (node, mapping)
53 self.mapcache = (node, mapping)
66 return mapping
54 return mapping
67
55
68 def readflags(self, node):
56 def readflags(self, node):
69 return self.read(node)
57 return self.read(node)
70
58
71 def diff(self, a, b):
59 def diff(self, a, b):
72 return mdiff.textdiff(str(a), str(b))
60 return mdiff.textdiff(str(a), str(b))
73
61
74 def _search(self, m, s, lo=0, hi=None):
62 def _search(self, m, s, lo=0, hi=None):
75 '''return a tuple (start, end) that says where to find s within m.
63 '''return a tuple (start, end) that says where to find s within m.
76
64
77 If the string is found m[start:end] are the line containing
65 If the string is found m[start:end] are the line containing
78 that string. If start == end the string was not found and
66 that string. If start == end the string was not found and
79 they indicate the proper sorted insertion point. This was
67 they indicate the proper sorted insertion point. This was
80 taken from bisect_left, and modified to find line start/end as
68 taken from bisect_left, and modified to find line start/end as
81 it goes along.
69 it goes along.
82
70
83 m should be a buffer or a string
71 m should be a buffer or a string
84 s is a string'''
72 s is a string'''
85 def advance(i, c):
73 def advance(i, c):
86 while i < lenm and m[i] != c:
74 while i < lenm and m[i] != c:
87 i += 1
75 i += 1
88 return i
76 return i
89 lenm = len(m)
77 lenm = len(m)
90 if not hi:
78 if not hi:
91 hi = lenm
79 hi = lenm
92 while lo < hi:
80 while lo < hi:
93 mid = (lo + hi) // 2
81 mid = (lo + hi) // 2
94 start = mid
82 start = mid
95 while start > 0 and m[start-1] != '\n':
83 while start > 0 and m[start-1] != '\n':
96 start -= 1
84 start -= 1
97 end = advance(start, '\0')
85 end = advance(start, '\0')
98 if m[start:end] < s:
86 if m[start:end] < s:
99 # we know that after the null there are 40 bytes of sha1
87 # we know that after the null there are 40 bytes of sha1
100 # this translates to the bisect lo = mid + 1
88 # this translates to the bisect lo = mid + 1
101 lo = advance(end + 40, '\n') + 1
89 lo = advance(end + 40, '\n') + 1
102 else:
90 else:
103 # this translates to the bisect hi = mid
91 # this translates to the bisect hi = mid
104 hi = start
92 hi = start
105 end = advance(lo, '\0')
93 end = advance(lo, '\0')
106 found = m[lo:end]
94 found = m[lo:end]
107 if cmp(s, found) == 0:
95 if cmp(s, found) == 0:
108 # we know that after the null there are 40 bytes of sha1
96 # we know that after the null there are 40 bytes of sha1
109 end = advance(end + 40, '\n')
97 end = advance(end + 40, '\n')
110 return (lo, end+1)
98 return (lo, end+1)
111 else:
99 else:
112 return (lo, lo)
100 return (lo, lo)
113
101
114 def find(self, node, f):
102 def find(self, node, f):
115 '''look up entry for a single file efficiently.
103 '''look up entry for a single file efficiently.
116 return (node, flag) pair if found, (None, None) if not.'''
104 return (node, flag) pair if found, (None, None) if not.'''
117 if self.mapcache and node == self.mapcache[0]:
105 if self.mapcache and node == self.mapcache[0]:
118 return self.mapcache[1].get(f), self.mapcache[1].flags(f)
106 return self.mapcache[1].get(f), self.mapcache[1].flags(f)
119 text = self.revision(node)
107 text = self.revision(node)
120 start, end = self._search(text, f)
108 start, end = self._search(text, f)
121 if start == end:
109 if start == end:
122 return None, None
110 return None, None
123 l = text[start:end]
111 l = text[start:end]
124 f, n = l.split('\0')
112 f, n = l.split('\0')
125 return bin(n[:40]), n[40:-1] == 'x'
113 return bin(n[:40]), n[40:-1] == 'x'
126
114
127 def add(self, map, flags, transaction, link, p1=None, p2=None,
115 def add(self, map, flags, transaction, link, p1=None, p2=None,
128 changed=None):
116 changed=None):
129 # apply the changes collected during the bisect loop to our addlist
117 # apply the changes collected during the bisect loop to our addlist
130 # return a delta suitable for addrevision
118 # return a delta suitable for addrevision
131 def addlistdelta(addlist, x):
119 def addlistdelta(addlist, x):
132 # start from the bottom up
120 # start from the bottom up
133 # so changes to the offsets don't mess things up.
121 # so changes to the offsets don't mess things up.
134 i = len(x)
122 i = len(x)
135 while i > 0:
123 while i > 0:
136 i -= 1
124 i -= 1
137 start = x[i][0]
125 start = x[i][0]
138 end = x[i][1]
126 end = x[i][1]
139 if x[i][2]:
127 if x[i][2]:
140 addlist[start:end] = array.array('c', x[i][2])
128 addlist[start:end] = array.array('c', x[i][2])
141 else:
129 else:
142 del addlist[start:end]
130 del addlist[start:end]
143 return "".join([struct.pack(">lll", d[0], d[1], len(d[2])) + d[2] \
131 return "".join([struct.pack(">lll", d[0], d[1], len(d[2])) + d[2] \
144 for d in x ])
132 for d in x ])
145
133
146 # if we're using the listcache, make sure it is valid and
134 # if we're using the listcache, make sure it is valid and
147 # parented by the same node we're diffing against
135 # parented by the same node we're diffing against
148 if not changed or not self.listcache or not p1 or \
136 if not changed or not self.listcache or not p1 or \
149 self.mapcache[0] != p1:
137 self.mapcache[0] != p1:
150 files = map.keys()
138 files = map.keys()
151 files.sort()
139 files.sort()
152
140
153 # if this is changed to support newlines in filenames,
141 # if this is changed to support newlines in filenames,
154 # be sure to check the templates/ dir again (especially *-raw.tmpl)
142 # be sure to check the templates/ dir again (especially *-raw.tmpl)
155 text = ["%s\000%s%s\n" % (f, hex(map[f]), flags.flags(f)) for f in files]
143 text = ["%s\000%s%s\n" % (f, hex(map[f]), flags.flags(f)) for f in files]
156 self.listcache = array.array('c', "".join(text))
144 self.listcache = array.array('c', "".join(text))
157 cachedelta = None
145 cachedelta = None
158 else:
146 else:
159 addlist = self.listcache
147 addlist = self.listcache
160
148
161 # combine the changed lists into one list for sorting
149 # combine the changed lists into one list for sorting
162 work = [[x, 0] for x in changed[0]]
150 work = [[x, 0] for x in changed[0]]
163 work[len(work):] = [[x, 1] for x in changed[1]]
151 work[len(work):] = [[x, 1] for x in changed[1]]
164 work.sort()
152 work.sort()
165
153
166 delta = []
154 delta = []
167 dstart = None
155 dstart = None
168 dend = None
156 dend = None
169 dline = [""]
157 dline = [""]
170 start = 0
158 start = 0
171 # zero copy representation of addlist as a buffer
159 # zero copy representation of addlist as a buffer
172 addbuf = buffer(addlist)
160 addbuf = buffer(addlist)
173
161
174 # start with a readonly loop that finds the offset of
162 # start with a readonly loop that finds the offset of
175 # each line and creates the deltas
163 # each line and creates the deltas
176 for w in work:
164 for w in work:
177 f = w[0]
165 f = w[0]
178 # bs will either be the index of the item or the insert point
166 # bs will either be the index of the item or the insert point
179 start, end = self._search(addbuf, f, start)
167 start, end = self._search(addbuf, f, start)
180 if w[1] == 0:
168 if w[1] == 0:
181 l = "%s\000%s%s\n" % (f, hex(map[f]), flags.flags(f))
169 l = "%s\000%s%s\n" % (f, hex(map[f]), flags.flags(f))
182 else:
170 else:
183 l = ""
171 l = ""
184 if start == end and w[1] == 1:
172 if start == end and w[1] == 1:
185 # item we want to delete was not found, error out
173 # item we want to delete was not found, error out
186 raise AssertionError(
174 raise AssertionError(
187 _("failed to remove %s from manifest\n") % f)
175 _("failed to remove %s from manifest\n") % f)
188 if dstart != None and dstart <= start and dend >= start:
176 if dstart != None and dstart <= start and dend >= start:
189 if dend < end:
177 if dend < end:
190 dend = end
178 dend = end
191 if l:
179 if l:
192 dline.append(l)
180 dline.append(l)
193 else:
181 else:
194 if dstart != None:
182 if dstart != None:
195 delta.append([dstart, dend, "".join(dline)])
183 delta.append([dstart, dend, "".join(dline)])
196 dstart = start
184 dstart = start
197 dend = end
185 dend = end
198 dline = [l]
186 dline = [l]
199
187
200 if dstart != None:
188 if dstart != None:
201 delta.append([dstart, dend, "".join(dline)])
189 delta.append([dstart, dend, "".join(dline)])
202 # apply the delta to the addlist, and get a delta for addrevision
190 # apply the delta to the addlist, and get a delta for addrevision
203 cachedelta = addlistdelta(addlist, delta)
191 cachedelta = addlistdelta(addlist, delta)
204
192
205 # the delta is only valid if we've been processing the tip revision
193 # the delta is only valid if we've been processing the tip revision
206 if self.mapcache[0] != self.tip():
194 if self.mapcache[0] != self.tip():
207 cachedelta = None
195 cachedelta = None
208 self.listcache = addlist
196 self.listcache = addlist
209
197
210 n = self.addrevision(buffer(self.listcache), transaction, link, p1, \
198 n = self.addrevision(buffer(self.listcache), transaction, link, p1, \
211 p2, cachedelta)
199 p2, cachedelta)
212 self.mapcache = (n, map)
200 self.mapcache = (n, map)
213
201
214 return n
202 return n
General Comments 0
You need to be logged in to leave comments. Login now