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