##// END OF EJS Templates
Fix recursion depth trouble with ancestor algorithm
mpm@selenic.com -
r45:f2b2d5da default
parent child Browse files
Show More
@@ -1,211 +1,229 b''
1 # revlog.py - storage back-end for mercurial
1 # revlog.py - storage back-end for mercurial
2 #
2 #
3 # This provides efficient delta storage with O(1) retrieve and append
3 # This provides efficient delta storage with O(1) retrieve and append
4 # and O(changes) merge between branches
4 # and O(changes) merge between branches
5 #
5 #
6 # Copyright 2005 Matt Mackall <mpm@selenic.com>
6 # Copyright 2005 Matt Mackall <mpm@selenic.com>
7 #
7 #
8 # This software may be used and distributed according to the terms
8 # This software may be used and distributed according to the terms
9 # of the GNU General Public License, incorporated herein by reference.
9 # of the GNU General Public License, incorporated herein by reference.
10
10
11 import zlib, struct, sha, os, tempfile, binascii
11 import zlib, struct, sha, os, tempfile, binascii
12 from mercurial import mdiff
12 from mercurial import mdiff
13
13
14 def hex(node): return binascii.hexlify(node)
14 def hex(node): return binascii.hexlify(node)
15 def bin(node): return binascii.unhexlify(node)
15 def bin(node): return binascii.unhexlify(node)
16
16
17 def compress(text):
17 def compress(text):
18 return zlib.compress(text)
18 return zlib.compress(text)
19
19
20 def decompress(bin):
20 def decompress(bin):
21 return zlib.decompress(bin)
21 return zlib.decompress(bin)
22
22
23 def hash(text, p1, p2):
23 def hash(text, p1, p2):
24 l = [p1, p2]
24 l = [p1, p2]
25 l.sort()
25 l.sort()
26 return sha.sha(l[0] + l[1] + text).digest()
26 return sha.sha(l[0] + l[1] + text).digest()
27
27
28 nullid = "\0" * 20
28 nullid = "\0" * 20
29 indexformat = ">4l20s20s20s"
29 indexformat = ">4l20s20s20s"
30
30
31 class revlog:
31 class revlog:
32 def __init__(self, opener, indexfile, datafile):
32 def __init__(self, opener, indexfile, datafile):
33 self.indexfile = indexfile
33 self.indexfile = indexfile
34 self.datafile = datafile
34 self.datafile = datafile
35 self.index = []
35 self.index = []
36 self.opener = opener
36 self.opener = opener
37 self.cache = None
37 self.cache = None
38 self.nodemap = {nullid: -1}
38 self.nodemap = {nullid: -1}
39 # read the whole index for now, handle on-demand later
39 # read the whole index for now, handle on-demand later
40 try:
40 try:
41 n = 0
41 n = 0
42 i = self.opener(self.indexfile).read()
42 i = self.opener(self.indexfile).read()
43 s = struct.calcsize(indexformat)
43 s = struct.calcsize(indexformat)
44 for f in range(0, len(i), s):
44 for f in range(0, len(i), s):
45 # offset, size, base, linkrev, p1, p2, nodeid
45 # offset, size, base, linkrev, p1, p2, nodeid
46 e = struct.unpack(indexformat, i[f:f + s])
46 e = struct.unpack(indexformat, i[f:f + s])
47 self.nodemap[e[6]] = n
47 self.nodemap[e[6]] = n
48 self.index.append(e)
48 self.index.append(e)
49 n += 1
49 n += 1
50 except IOError: pass
50 except IOError: pass
51
51
52 def tip(self): return self.node(len(self.index) - 1)
52 def tip(self): return self.node(len(self.index) - 1)
53 def count(self): return len(self.index)
53 def count(self): return len(self.index)
54 def node(self, rev): return (rev < 0) and nullid or self.index[rev][6]
54 def node(self, rev): return (rev < 0) and nullid or self.index[rev][6]
55 def rev(self, node): return self.nodemap[node]
55 def rev(self, node): return self.nodemap[node]
56 def linkrev(self, node): return self.index[self.nodemap[node]][3]
56 def linkrev(self, node): return self.index[self.nodemap[node]][3]
57 def parents(self, node):
57 def parents(self, node):
58 if node == nullid: return (nullid, nullid)
58 if node == nullid: return (nullid, nullid)
59 return self.index[self.nodemap[node]][4:6]
59 return self.index[self.nodemap[node]][4:6]
60
60
61 def start(self, rev): return self.index[rev][0]
61 def start(self, rev): return self.index[rev][0]
62 def length(self, rev): return self.index[rev][1]
62 def length(self, rev): return self.index[rev][1]
63 def end(self, rev): return self.start(rev) + self.length(rev)
63 def end(self, rev): return self.start(rev) + self.length(rev)
64 def base(self, rev): return self.index[rev][2]
64 def base(self, rev): return self.index[rev][2]
65
65
66 def lookup(self, id):
66 def lookup(self, id):
67 try:
67 try:
68 rev = int(id)
68 rev = int(id)
69 return self.node(rev)
69 return self.node(rev)
70 except ValueError:
70 except ValueError:
71 c = []
71 c = []
72 for n in self.nodemap:
72 for n in self.nodemap:
73 if id in hex(n):
73 if id in hex(n):
74 c.append(n)
74 c.append(n)
75 if len(c) > 1: raise KeyError("Ambiguous identifier")
75 if len(c) > 1: raise KeyError("Ambiguous identifier")
76 if len(c) < 1: raise KeyError
76 if len(c) < 1: raise KeyError
77 return c[0]
77 return c[0]
78
78
79 return None
79 return None
80
80
81 def revisions(self, list):
81 def revisions(self, list):
82 # this can be optimized to do spans, etc
82 # this can be optimized to do spans, etc
83 # be stupid for now
83 # be stupid for now
84 for node in list:
84 for node in list:
85 yield self.revision(node)
85 yield self.revision(node)
86
86
87 def diff(self, a, b):
87 def diff(self, a, b):
88 return mdiff.textdiff(a, b)
88 return mdiff.textdiff(a, b)
89
89
90 def patch(self, text, patch):
90 def patch(self, text, patch):
91 return mdiff.patch(text, patch)
91 return mdiff.patch(text, patch)
92
92
93 def revision(self, node):
93 def revision(self, node):
94 if node == nullid: return ""
94 if node == nullid: return ""
95 if self.cache and self.cache[0] == node: return self.cache[2]
95 if self.cache and self.cache[0] == node: return self.cache[2]
96
96
97 text = None
97 text = None
98 rev = self.rev(node)
98 rev = self.rev(node)
99 base = self.base(rev)
99 base = self.base(rev)
100 start = self.start(base)
100 start = self.start(base)
101 end = self.end(rev)
101 end = self.end(rev)
102
102
103 if self.cache and self.cache[1] >= base and self.cache[1] < rev:
103 if self.cache and self.cache[1] >= base and self.cache[1] < rev:
104 base = self.cache[1]
104 base = self.cache[1]
105 start = self.start(base + 1)
105 start = self.start(base + 1)
106 text = self.cache[2]
106 text = self.cache[2]
107 last = 0
107 last = 0
108
108
109 f = self.opener(self.datafile)
109 f = self.opener(self.datafile)
110 f.seek(start)
110 f.seek(start)
111 data = f.read(end - start)
111 data = f.read(end - start)
112
112
113 if not text:
113 if not text:
114 last = self.length(base)
114 last = self.length(base)
115 text = decompress(data[:last])
115 text = decompress(data[:last])
116
116
117 for r in range(base + 1, rev + 1):
117 for r in range(base + 1, rev + 1):
118 s = self.length(r)
118 s = self.length(r)
119 b = decompress(data[last:last + s])
119 b = decompress(data[last:last + s])
120 text = self.patch(text, b)
120 text = self.patch(text, b)
121 last = last + s
121 last = last + s
122
122
123 (p1, p2) = self.parents(node)
123 (p1, p2) = self.parents(node)
124 if node != hash(text, p1, p2):
124 if node != hash(text, p1, p2):
125 raise "integrity check failed on %s:%d" % (self.datafile, rev)
125 raise "integrity check failed on %s:%d" % (self.datafile, rev)
126
126
127 self.cache = (node, rev, text)
127 self.cache = (node, rev, text)
128 return text
128 return text
129
129
130 def addrevision(self, text, transaction, link, p1=None, p2=None):
130 def addrevision(self, text, transaction, link, p1=None, p2=None):
131 if text is None: text = ""
131 if text is None: text = ""
132 if p1 is None: p1 = self.tip()
132 if p1 is None: p1 = self.tip()
133 if p2 is None: p2 = nullid
133 if p2 is None: p2 = nullid
134
134
135 node = hash(text, p1, p2)
135 node = hash(text, p1, p2)
136
136
137 n = self.count()
137 n = self.count()
138 t = n - 1
138 t = n - 1
139
139
140 if n:
140 if n:
141 start = self.start(self.base(t))
141 start = self.start(self.base(t))
142 end = self.end(t)
142 end = self.end(t)
143 prev = self.revision(self.tip())
143 prev = self.revision(self.tip())
144 data = compress(self.diff(prev, text))
144 data = compress(self.diff(prev, text))
145
145
146 # full versions are inserted when the needed deltas
146 # full versions are inserted when the needed deltas
147 # become comparable to the uncompressed text
147 # become comparable to the uncompressed text
148 if not n or (end + len(data) - start) > len(text) * 2:
148 if not n or (end + len(data) - start) > len(text) * 2:
149 data = compress(text)
149 data = compress(text)
150 base = n
150 base = n
151 else:
151 else:
152 base = self.base(t)
152 base = self.base(t)
153
153
154 offset = 0
154 offset = 0
155 if t >= 0:
155 if t >= 0:
156 offset = self.end(t)
156 offset = self.end(t)
157
157
158 e = (offset, len(data), base, link, p1, p2, node)
158 e = (offset, len(data), base, link, p1, p2, node)
159
159
160 self.index.append(e)
160 self.index.append(e)
161 self.nodemap[node] = n
161 self.nodemap[node] = n
162 entry = struct.pack(indexformat, *e)
162 entry = struct.pack(indexformat, *e)
163
163
164 transaction.add(self.datafile, e[0])
164 transaction.add(self.datafile, e[0])
165 self.opener(self.datafile, "a").write(data)
165 self.opener(self.datafile, "a").write(data)
166 transaction.add(self.indexfile, n * len(entry))
166 transaction.add(self.indexfile, n * len(entry))
167 self.opener(self.indexfile, "a").write(entry)
167 self.opener(self.indexfile, "a").write(entry)
168
168
169 self.cache = (node, n, text)
169 self.cache = (node, n, text)
170 return node
170 return node
171
171
172 def ancestor(self, a, b):
172 def ancestor(self, a, b):
173 def expand(e1, e2, a1, a2):
173 def expand(list, map):
174 ne = []
174 a = []
175 for n in e1:
175 while list:
176 (p1, p2) = self.parents(n)
176 n = list.pop(0)
177 if p1 in a2: return p1
177 map[n] = 1
178 if p2 in a2: return p2
178 yield n
179 if p1 != nullid and p1 not in a1:
179 for p in self.parents(n):
180 a1[p1] = 1
180 if p != nullid and p not in map:
181 ne.append(p1)
181 list.append(p)
182 if p2 != nullid and p2 not in a1:
182 yield nullid
183 a1[p2] = 1
183
184 ne.append(p2)
184 amap = {}
185 return expand(e2, ne, a2, a1)
185 bmap = {}
186 return expand([a], [b], {a:1}, {b:1})
186 ag = expand([a], amap)
187 bg = expand([b], bmap)
188 adone = bdone = 0
189
190 while not adone or not bdone:
191 if not adone:
192 an = ag.next()
193 if an == nullid:
194 adone = 1
195 elif an in bmap:
196 return an
197 if not bdone:
198 bn = bg.next()
199 if bn == nullid:
200 bdone = 1
201 elif bn in amap:
202 return bn
203
204 return nullid
187
205
188 def mergedag(self, other, transaction, linkseq, accumulate = None):
206 def mergedag(self, other, transaction, linkseq, accumulate = None):
189 """combine the nodes from other's DAG into ours"""
207 """combine the nodes from other's DAG into ours"""
190 old = self.tip()
208 old = self.tip()
191 i = self.count()
209 i = self.count()
192 l = []
210 l = []
193
211
194 # merge the other revision log into our DAG
212 # merge the other revision log into our DAG
195 for r in range(other.count()):
213 for r in range(other.count()):
196 id = other.node(r)
214 id = other.node(r)
197 if id not in self.nodemap:
215 if id not in self.nodemap:
198 (xn, yn) = other.parents(id)
216 (xn, yn) = other.parents(id)
199 l.append((id, xn, yn))
217 l.append((id, xn, yn))
200 self.nodemap[id] = i
218 self.nodemap[id] = i
201 i += 1
219 i += 1
202
220
203 # merge node date for new nodes
221 # merge node date for new nodes
204 r = other.revisions([e[0] for e in l])
222 r = other.revisions([e[0] for e in l])
205 for e in l:
223 for e in l:
206 t = r.next()
224 t = r.next()
207 if accumulate: accumulate(t)
225 if accumulate: accumulate(t)
208 self.addrevision(t, transaction, linkseq.next(), e[1], e[2])
226 self.addrevision(t, transaction, linkseq.next(), e[1], e[2])
209
227
210 # return the unmerged heads for later resolving
228 # return the unmerged heads for later resolving
211 return (old, self.tip())
229 return (old, self.tip())
General Comments 0
You need to be logged in to leave comments. Login now