##// END OF EJS Templates
simplestore: shore up lookup errors...
Gregory Szorc -
r37426:dd275372 default
parent child Browse files
Show More
@@ -1,598 +1,595 b''
1 # simplestorerepo.py - Extension that swaps in alternate repository storage.
1 # simplestorerepo.py - Extension that swaps in alternate repository storage.
2 #
2 #
3 # Copyright 2018 Gregory Szorc <gregory.szorc@gmail.com>
3 # Copyright 2018 Gregory Szorc <gregory.szorc@gmail.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
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.
6 # GNU General Public License version 2 or any later version.
7
7
8 # To use this with the test suite:
8 # To use this with the test suite:
9 #
9 #
10 # $ HGREPOFEATURES="simplestore" ./run-tests.py \
10 # $ HGREPOFEATURES="simplestore" ./run-tests.py \
11 # --extra-config-opt extensions.simplestore=`pwd`/simplestorerepo.py
11 # --extra-config-opt extensions.simplestore=`pwd`/simplestorerepo.py
12
12
13 from __future__ import absolute_import
13 from __future__ import absolute_import
14
14
15 from mercurial.i18n import _
15 from mercurial.i18n import _
16 from mercurial.node import (
16 from mercurial.node import (
17 bin,
17 bin,
18 hex,
18 hex,
19 nullid,
19 nullid,
20 nullrev,
20 nullrev,
21 )
21 )
22 from mercurial.thirdparty import (
22 from mercurial.thirdparty import (
23 cbor,
23 cbor,
24 )
24 )
25 from mercurial import (
25 from mercurial import (
26 ancestor,
26 ancestor,
27 bundlerepo,
27 bundlerepo,
28 error,
28 error,
29 filelog,
29 filelog,
30 mdiff,
30 mdiff,
31 pycompat,
31 pycompat,
32 revlog,
32 revlog,
33 )
33 )
34
34
35 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
35 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
36 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
36 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
37 # be specifying the version(s) of Mercurial they are tested with, or
37 # be specifying the version(s) of Mercurial they are tested with, or
38 # leave the attribute unspecified.
38 # leave the attribute unspecified.
39 testedwith = 'ships-with-hg-core'
39 testedwith = 'ships-with-hg-core'
40
40
41 def validatenode(node):
41 def validatenode(node):
42 if isinstance(node, int):
42 if isinstance(node, int):
43 raise ValueError('expected node; got int')
43 raise ValueError('expected node; got int')
44
44
45 if len(node) != 20:
45 if len(node) != 20:
46 raise ValueError('expected 20 byte node')
46 raise ValueError('expected 20 byte node')
47
47
48 def validaterev(rev):
48 def validaterev(rev):
49 if not isinstance(rev, int):
49 if not isinstance(rev, int):
50 raise ValueError('expected int')
50 raise ValueError('expected int')
51
51
52 class filestorage(object):
52 class filestorage(object):
53 """Implements storage for a tracked path.
53 """Implements storage for a tracked path.
54
54
55 Data is stored in the VFS in a directory corresponding to the tracked
55 Data is stored in the VFS in a directory corresponding to the tracked
56 path.
56 path.
57
57
58 Index data is stored in an ``index`` file using CBOR.
58 Index data is stored in an ``index`` file using CBOR.
59
59
60 Fulltext data is stored in files having names of the node.
60 Fulltext data is stored in files having names of the node.
61 """
61 """
62
62
63 def __init__(self, svfs, path):
63 def __init__(self, svfs, path):
64 self._svfs = svfs
64 self._svfs = svfs
65 self._path = path
65 self._path = path
66
66
67 self._storepath = b'/'.join([b'data', path])
67 self._storepath = b'/'.join([b'data', path])
68 self._indexpath = b'/'.join([self._storepath, b'index'])
68 self._indexpath = b'/'.join([self._storepath, b'index'])
69
69
70 indexdata = self._svfs.tryread(self._indexpath)
70 indexdata = self._svfs.tryread(self._indexpath)
71 if indexdata:
71 if indexdata:
72 indexdata = cbor.loads(indexdata)
72 indexdata = cbor.loads(indexdata)
73
73
74 self._indexdata = indexdata or []
74 self._indexdata = indexdata or []
75 self._indexbynode = {}
75 self._indexbynode = {}
76 self._indexbyrev = {}
76 self._indexbyrev = {}
77 self.index = []
77 self.index = []
78 self._refreshindex()
78 self._refreshindex()
79
79
80 # This is used by changegroup code :/
80 # This is used by changegroup code :/
81 self._generaldelta = True
81 self._generaldelta = True
82 self.storedeltachains = False
82 self.storedeltachains = False
83
83
84 self.version = 1
84 self.version = 1
85
85
86 def _refreshindex(self):
86 def _refreshindex(self):
87 self._indexbynode.clear()
87 self._indexbynode.clear()
88 self._indexbyrev.clear()
88 self._indexbyrev.clear()
89 self.index = []
89 self.index = []
90
90
91 for i, entry in enumerate(self._indexdata):
91 for i, entry in enumerate(self._indexdata):
92 self._indexbynode[entry[b'node']] = entry
92 self._indexbynode[entry[b'node']] = entry
93 self._indexbyrev[i] = entry
93 self._indexbyrev[i] = entry
94
94
95 self._indexbynode[nullid] = {
95 self._indexbynode[nullid] = {
96 b'node': nullid,
96 b'node': nullid,
97 b'p1': nullid,
97 b'p1': nullid,
98 b'p2': nullid,
98 b'p2': nullid,
99 b'linkrev': nullrev,
99 b'linkrev': nullrev,
100 b'flags': 0,
100 b'flags': 0,
101 }
101 }
102
102
103 self._indexbyrev[nullrev] = {
103 self._indexbyrev[nullrev] = {
104 b'node': nullid,
104 b'node': nullid,
105 b'p1': nullid,
105 b'p1': nullid,
106 b'p2': nullid,
106 b'p2': nullid,
107 b'linkrev': nullrev,
107 b'linkrev': nullrev,
108 b'flags': 0,
108 b'flags': 0,
109 }
109 }
110
110
111 for i, entry in enumerate(self._indexdata):
111 for i, entry in enumerate(self._indexdata):
112 p1rev, p2rev = self.parentrevs(self.rev(entry[b'node']))
112 p1rev, p2rev = self.parentrevs(self.rev(entry[b'node']))
113
113
114 # start, length, rawsize, chainbase, linkrev, p1, p2, node
114 # start, length, rawsize, chainbase, linkrev, p1, p2, node
115 self.index.append((0, 0, 0, -1, entry[b'linkrev'], p1rev, p2rev,
115 self.index.append((0, 0, 0, -1, entry[b'linkrev'], p1rev, p2rev,
116 entry[b'node']))
116 entry[b'node']))
117
117
118 self.index.append((0, 0, 0, -1, -1, -1, -1, nullid))
118 self.index.append((0, 0, 0, -1, -1, -1, -1, nullid))
119
119
120 def __len__(self):
120 def __len__(self):
121 return len(self._indexdata)
121 return len(self._indexdata)
122
122
123 def __iter__(self):
123 def __iter__(self):
124 return iter(range(len(self)))
124 return iter(range(len(self)))
125
125
126 def revs(self, start=0, stop=None):
126 def revs(self, start=0, stop=None):
127 step = 1
127 step = 1
128 if stop is not None:
128 if stop is not None:
129 if start > stop:
129 if start > stop:
130 step = -1
130 step = -1
131
131
132 stop += step
132 stop += step
133 else:
133 else:
134 stop = len(self)
134 stop = len(self)
135
135
136 return range(start, stop, step)
136 return range(start, stop, step)
137
137
138 def parents(self, node):
138 def parents(self, node):
139 validatenode(node)
139 validatenode(node)
140
140
141 if node not in self._indexbynode:
141 if node not in self._indexbynode:
142 raise KeyError('unknown node')
142 raise KeyError('unknown node')
143
143
144 entry = self._indexbynode[node]
144 entry = self._indexbynode[node]
145
145
146 return entry[b'p1'], entry[b'p2']
146 return entry[b'p1'], entry[b'p2']
147
147
148 def parentrevs(self, rev):
148 def parentrevs(self, rev):
149 p1, p2 = self.parents(self._indexbyrev[rev][b'node'])
149 p1, p2 = self.parents(self._indexbyrev[rev][b'node'])
150 return self.rev(p1), self.rev(p2)
150 return self.rev(p1), self.rev(p2)
151
151
152 def rev(self, node):
152 def rev(self, node):
153 validatenode(node)
153 validatenode(node)
154
154
155 # Will raise KeyError.
155 try:
156 self._indexbynode[node]
156 self._indexbynode[node]
157 except KeyError:
158 raise error.LookupError(node, self._indexpath, _('no node'))
157
159
158 for rev, entry in self._indexbyrev.items():
160 for rev, entry in self._indexbyrev.items():
159 if entry[b'node'] == node:
161 if entry[b'node'] == node:
160 return rev
162 return rev
161
163
162 raise error.ProgrammingError('this should not occur')
164 raise error.ProgrammingError('this should not occur')
163
165
164 def node(self, rev):
166 def node(self, rev):
165 validaterev(rev)
167 validaterev(rev)
166
168
167 return self._indexbyrev[rev][b'node']
169 return self._indexbyrev[rev][b'node']
168
170
169 def lookup(self, node):
171 def lookup(self, node):
170 if isinstance(node, int):
172 if isinstance(node, int):
171 return self.node(node)
173 return self.node(node)
172
174
173 if len(node) == 20:
175 if len(node) == 20:
174 try:
175 self.rev(node)
176 self.rev(node)
176 return node
177 return node
177 except LookupError:
178 pass
179
178
180 try:
179 try:
181 rev = int(node)
180 rev = int(node)
182 if '%d' % rev != node:
181 if '%d' % rev != node:
183 raise ValueError
182 raise ValueError
184
183
185 if rev < 0:
184 if rev < 0:
186 rev = len(self) + rev
185 rev = len(self) + rev
187 if rev < 0 or rev >= len(self):
186 if rev < 0 or rev >= len(self):
188 raise ValueError
187 raise ValueError
189
188
190 return self.node(rev)
189 return self.node(rev)
191 except (ValueError, OverflowError):
190 except (ValueError, OverflowError):
192 pass
191 pass
193
192
194 if len(node) == 40:
193 if len(node) == 40:
195 try:
194 try:
196 rawnode = bin(node)
195 rawnode = bin(node)
197 self.rev(rawnode)
196 self.rev(rawnode)
198 return rawnode
197 return rawnode
199 except (TypeError, LookupError):
198 except TypeError:
200 pass
199 pass
201
200
202 raise LookupError(node, self._path, _('invalid lookup input'))
201 raise error.LookupError(node, self._path, _('invalid lookup input'))
203
202
204 def linkrev(self, rev):
203 def linkrev(self, rev):
205 validaterev(rev)
204 validaterev(rev)
206
205
207 return self._indexbyrev[rev][b'linkrev']
206 return self._indexbyrev[rev][b'linkrev']
208
207
209 def flags(self, rev):
208 def flags(self, rev):
210 validaterev(rev)
209 validaterev(rev)
211
210
212 return self._indexbyrev[rev][b'flags']
211 return self._indexbyrev[rev][b'flags']
213
212
214 def deltaparent(self, rev):
213 def deltaparent(self, rev):
215 validaterev(rev)
214 validaterev(rev)
216
215
217 p1node = self.parents(self.node(rev))[0]
216 p1node = self.parents(self.node(rev))[0]
218 return self.rev(p1node)
217 return self.rev(p1node)
219
218
220 def candelta(self, baserev, rev):
219 def candelta(self, baserev, rev):
221 validaterev(baserev)
220 validaterev(baserev)
222 validaterev(rev)
221 validaterev(rev)
223
222
224 if ((self.flags(baserev) & revlog.REVIDX_RAWTEXT_CHANGING_FLAGS)
223 if ((self.flags(baserev) & revlog.REVIDX_RAWTEXT_CHANGING_FLAGS)
225 or (self.flags(rev) & revlog.REVIDX_RAWTEXT_CHANGING_FLAGS)):
224 or (self.flags(rev) & revlog.REVIDX_RAWTEXT_CHANGING_FLAGS)):
226 return False
225 return False
227
226
228 return True
227 return True
229
228
230 def rawsize(self, rev):
229 def rawsize(self, rev):
231 validaterev(rev)
230 validaterev(rev)
232 node = self.node(rev)
231 node = self.node(rev)
233 return len(self.revision(node, raw=True))
232 return len(self.revision(node, raw=True))
234
233
235 def _processflags(self, text, flags, operation, raw=False):
234 def _processflags(self, text, flags, operation, raw=False):
236 if flags == 0:
235 if flags == 0:
237 return text, True
236 return text, True
238
237
239 validatehash = True
238 validatehash = True
240 # Depending on the operation (read or write), the order might be
239 # Depending on the operation (read or write), the order might be
241 # reversed due to non-commutative transforms.
240 # reversed due to non-commutative transforms.
242 orderedflags = revlog.REVIDX_FLAGS_ORDER
241 orderedflags = revlog.REVIDX_FLAGS_ORDER
243 if operation == 'write':
242 if operation == 'write':
244 orderedflags = reversed(orderedflags)
243 orderedflags = reversed(orderedflags)
245
244
246 for flag in orderedflags:
245 for flag in orderedflags:
247 # If a flagprocessor has been registered for a known flag, apply the
246 # If a flagprocessor has been registered for a known flag, apply the
248 # related operation transform and update result tuple.
247 # related operation transform and update result tuple.
249 if flag & flags:
248 if flag & flags:
250 vhash = True
249 vhash = True
251
250
252 if flag not in revlog._flagprocessors:
251 if flag not in revlog._flagprocessors:
253 message = _("missing processor for flag '%#x'") % (flag)
252 message = _("missing processor for flag '%#x'") % (flag)
254 raise revlog.RevlogError(message)
253 raise revlog.RevlogError(message)
255
254
256 processor = revlog._flagprocessors[flag]
255 processor = revlog._flagprocessors[flag]
257 if processor is not None:
256 if processor is not None:
258 readtransform, writetransform, rawtransform = processor
257 readtransform, writetransform, rawtransform = processor
259
258
260 if raw:
259 if raw:
261 vhash = rawtransform(self, text)
260 vhash = rawtransform(self, text)
262 elif operation == 'read':
261 elif operation == 'read':
263 text, vhash = readtransform(self, text)
262 text, vhash = readtransform(self, text)
264 else: # write operation
263 else: # write operation
265 text, vhash = writetransform(self, text)
264 text, vhash = writetransform(self, text)
266 validatehash = validatehash and vhash
265 validatehash = validatehash and vhash
267
266
268 return text, validatehash
267 return text, validatehash
269
268
270 def checkhash(self, text, node, p1=None, p2=None, rev=None):
269 def checkhash(self, text, node, p1=None, p2=None, rev=None):
271 if p1 is None and p2 is None:
270 if p1 is None and p2 is None:
272 p1, p2 = self.parents(node)
271 p1, p2 = self.parents(node)
273 if node != revlog.hash(text, p1, p2):
272 if node != revlog.hash(text, p1, p2):
274 raise error.RevlogError(_("integrity check failed on %s") %
273 raise error.RevlogError(_("integrity check failed on %s") %
275 self._path)
274 self._path)
276
275
277 def revision(self, node, raw=False):
276 def revision(self, node, raw=False):
278 validatenode(node)
277 validatenode(node)
279
278
280 if node == nullid:
279 if node == nullid:
281 return b''
280 return b''
282
281
283 self._indexbynode[node]
284
285 rev = self.rev(node)
282 rev = self.rev(node)
286 flags = self.flags(rev)
283 flags = self.flags(rev)
287
284
288 path = b'/'.join([self._storepath, hex(node)])
285 path = b'/'.join([self._storepath, hex(node)])
289 rawtext = self._svfs.read(path)
286 rawtext = self._svfs.read(path)
290
287
291 text, validatehash = self._processflags(rawtext, flags, 'read', raw=raw)
288 text, validatehash = self._processflags(rawtext, flags, 'read', raw=raw)
292 if validatehash:
289 if validatehash:
293 self.checkhash(text, node, rev=rev)
290 self.checkhash(text, node, rev=rev)
294
291
295 return text
292 return text
296
293
297 def read(self, node):
294 def read(self, node):
298 validatenode(node)
295 validatenode(node)
299
296
300 revision = self.revision(node)
297 revision = self.revision(node)
301
298
302 if not revision.startswith(b'\1\n'):
299 if not revision.startswith(b'\1\n'):
303 return revision
300 return revision
304
301
305 start = revision.index(b'\1\n', 2)
302 start = revision.index(b'\1\n', 2)
306 return revision[start + 2:]
303 return revision[start + 2:]
307
304
308 def renamed(self, node):
305 def renamed(self, node):
309 validatenode(node)
306 validatenode(node)
310
307
311 if self.parents(node)[0] != nullid:
308 if self.parents(node)[0] != nullid:
312 return False
309 return False
313
310
314 fulltext = self.revision(node)
311 fulltext = self.revision(node)
315 m = filelog.parsemeta(fulltext)[0]
312 m = filelog.parsemeta(fulltext)[0]
316
313
317 if m and 'copy' in m:
314 if m and 'copy' in m:
318 return m['copy'], bin(m['copyrev'])
315 return m['copy'], bin(m['copyrev'])
319
316
320 return False
317 return False
321
318
322 def cmp(self, node, text):
319 def cmp(self, node, text):
323 validatenode(node)
320 validatenode(node)
324
321
325 t = text
322 t = text
326
323
327 if text.startswith(b'\1\n'):
324 if text.startswith(b'\1\n'):
328 t = b'\1\n\1\n' + text
325 t = b'\1\n\1\n' + text
329
326
330 p1, p2 = self.parents(node)
327 p1, p2 = self.parents(node)
331
328
332 if revlog.hash(t, p1, p2) == node:
329 if revlog.hash(t, p1, p2) == node:
333 return False
330 return False
334
331
335 if self.iscensored(self.rev(node)):
332 if self.iscensored(self.rev(node)):
336 return text != b''
333 return text != b''
337
334
338 if self.renamed(node):
335 if self.renamed(node):
339 t2 = self.read(node)
336 t2 = self.read(node)
340 return t2 != text
337 return t2 != text
341
338
342 return True
339 return True
343
340
344 def size(self, rev):
341 def size(self, rev):
345 validaterev(rev)
342 validaterev(rev)
346
343
347 node = self._indexbyrev[rev][b'node']
344 node = self._indexbyrev[rev][b'node']
348
345
349 if self.renamed(node):
346 if self.renamed(node):
350 return len(self.read(node))
347 return len(self.read(node))
351
348
352 if self.iscensored(rev):
349 if self.iscensored(rev):
353 return 0
350 return 0
354
351
355 return len(self.revision(node))
352 return len(self.revision(node))
356
353
357 def iscensored(self, rev):
354 def iscensored(self, rev):
358 validaterev(rev)
355 validaterev(rev)
359
356
360 return self.flags(rev) & revlog.REVIDX_ISCENSORED
357 return self.flags(rev) & revlog.REVIDX_ISCENSORED
361
358
362 def commonancestorsheads(self, a, b):
359 def commonancestorsheads(self, a, b):
363 validatenode(a)
360 validatenode(a)
364 validatenode(b)
361 validatenode(b)
365
362
366 a = self.rev(a)
363 a = self.rev(a)
367 b = self.rev(b)
364 b = self.rev(b)
368
365
369 ancestors = ancestor.commonancestorsheads(self.parentrevs, a, b)
366 ancestors = ancestor.commonancestorsheads(self.parentrevs, a, b)
370 return pycompat.maplist(self.node, ancestors)
367 return pycompat.maplist(self.node, ancestors)
371
368
372 def descendants(self, revs):
369 def descendants(self, revs):
373 # This is a copy of revlog.descendants()
370 # This is a copy of revlog.descendants()
374 first = min(revs)
371 first = min(revs)
375 if first == nullrev:
372 if first == nullrev:
376 for i in self:
373 for i in self:
377 yield i
374 yield i
378 return
375 return
379
376
380 seen = set(revs)
377 seen = set(revs)
381 for i in self.revs(start=first + 1):
378 for i in self.revs(start=first + 1):
382 for x in self.parentrevs(i):
379 for x in self.parentrevs(i):
383 if x != nullrev and x in seen:
380 if x != nullrev and x in seen:
384 seen.add(i)
381 seen.add(i)
385 yield i
382 yield i
386 break
383 break
387
384
388 # Required by verify.
385 # Required by verify.
389 def files(self):
386 def files(self):
390 entries = self._svfs.listdir(self._storepath)
387 entries = self._svfs.listdir(self._storepath)
391
388
392 # Strip out undo.backup.* files created as part of transaction
389 # Strip out undo.backup.* files created as part of transaction
393 # recording.
390 # recording.
394 entries = [f for f in entries if not f.startswith('undo.backup.')]
391 entries = [f for f in entries if not f.startswith('undo.backup.')]
395
392
396 return [b'/'.join((self._storepath, f)) for f in entries]
393 return [b'/'.join((self._storepath, f)) for f in entries]
397
394
398 # Required by verify.
395 # Required by verify.
399 def checksize(self):
396 def checksize(self):
400 return 0, 0
397 return 0, 0
401
398
402 def add(self, text, meta, transaction, linkrev, p1, p2):
399 def add(self, text, meta, transaction, linkrev, p1, p2):
403 if meta or text.startswith(b'\1\n'):
400 if meta or text.startswith(b'\1\n'):
404 text = filelog.packmeta(meta, text)
401 text = filelog.packmeta(meta, text)
405
402
406 return self.addrevision(text, transaction, linkrev, p1, p2)
403 return self.addrevision(text, transaction, linkrev, p1, p2)
407
404
408 def addrevision(self, text, transaction, linkrev, p1, p2, node=None,
405 def addrevision(self, text, transaction, linkrev, p1, p2, node=None,
409 flags=0):
406 flags=0):
410 validatenode(p1)
407 validatenode(p1)
411 validatenode(p2)
408 validatenode(p2)
412
409
413 if flags:
410 if flags:
414 node = node or revlog.hash(text, p1, p2)
411 node = node or revlog.hash(text, p1, p2)
415
412
416 rawtext, validatehash = self._processflags(text, flags, 'write')
413 rawtext, validatehash = self._processflags(text, flags, 'write')
417
414
418 node = node or revlog.hash(text, p1, p2)
415 node = node or revlog.hash(text, p1, p2)
419
416
420 if node in self._indexbynode:
417 if node in self._indexbynode:
421 return node
418 return node
422
419
423 if validatehash:
420 if validatehash:
424 self.checkhash(rawtext, node, p1=p1, p2=p2)
421 self.checkhash(rawtext, node, p1=p1, p2=p2)
425
422
426 path = b'/'.join([self._storepath, hex(node)])
423 path = b'/'.join([self._storepath, hex(node)])
427
424
428 self._svfs.write(path, text)
425 self._svfs.write(path, text)
429
426
430 self._indexdata.append({
427 self._indexdata.append({
431 b'node': node,
428 b'node': node,
432 b'p1': p1,
429 b'p1': p1,
433 b'p2': p2,
430 b'p2': p2,
434 b'linkrev': linkrev,
431 b'linkrev': linkrev,
435 b'flags': flags,
432 b'flags': flags,
436 })
433 })
437
434
438 self._reflectindexupdate()
435 self._reflectindexupdate()
439
436
440 return node
437 return node
441
438
442 def _reflectindexupdate(self):
439 def _reflectindexupdate(self):
443 self._refreshindex()
440 self._refreshindex()
444 self._svfs.write(self._indexpath, cbor.dumps(self._indexdata))
441 self._svfs.write(self._indexpath, cbor.dumps(self._indexdata))
445
442
446 def addgroup(self, deltas, linkmapper, transaction, addrevisioncb=None):
443 def addgroup(self, deltas, linkmapper, transaction, addrevisioncb=None):
447 nodes = []
444 nodes = []
448
445
449 transaction.addbackup(self._indexpath)
446 transaction.addbackup(self._indexpath)
450
447
451 for node, p1, p2, linknode, deltabase, delta, flags in deltas:
448 for node, p1, p2, linknode, deltabase, delta, flags in deltas:
452 linkrev = linkmapper(linknode)
449 linkrev = linkmapper(linknode)
453
450
454 nodes.append(node)
451 nodes.append(node)
455
452
456 if node in self._indexbynode:
453 if node in self._indexbynode:
457 continue
454 continue
458
455
459 # Need to resolve the fulltext from the delta base.
456 # Need to resolve the fulltext from the delta base.
460 if deltabase == nullid:
457 if deltabase == nullid:
461 text = mdiff.patch(b'', delta)
458 text = mdiff.patch(b'', delta)
462 else:
459 else:
463 text = mdiff.patch(self.revision(deltabase), delta)
460 text = mdiff.patch(self.revision(deltabase), delta)
464
461
465 self.addrevision(text, transaction, linkrev, p1, p2, flags)
462 self.addrevision(text, transaction, linkrev, p1, p2, flags)
466
463
467 if addrevisioncb:
464 if addrevisioncb:
468 addrevisioncb(self, node)
465 addrevisioncb(self, node)
469
466
470 return nodes
467 return nodes
471
468
472 def revdiff(self, rev1, rev2):
469 def revdiff(self, rev1, rev2):
473 validaterev(rev1)
470 validaterev(rev1)
474 validaterev(rev2)
471 validaterev(rev2)
475
472
476 node1 = self.node(rev1)
473 node1 = self.node(rev1)
477 node2 = self.node(rev2)
474 node2 = self.node(rev2)
478
475
479 return mdiff.textdiff(self.revision(node1, raw=True),
476 return mdiff.textdiff(self.revision(node1, raw=True),
480 self.revision(node2, raw=True))
477 self.revision(node2, raw=True))
481
478
482 def headrevs(self):
479 def headrevs(self):
483 # Assume all revisions are heads by default.
480 # Assume all revisions are heads by default.
484 revishead = {rev: True for rev in self._indexbyrev}
481 revishead = {rev: True for rev in self._indexbyrev}
485
482
486 for rev, entry in self._indexbyrev.items():
483 for rev, entry in self._indexbyrev.items():
487 # Unset head flag for all seen parents.
484 # Unset head flag for all seen parents.
488 revishead[self.rev(entry[b'p1'])] = False
485 revishead[self.rev(entry[b'p1'])] = False
489 revishead[self.rev(entry[b'p2'])] = False
486 revishead[self.rev(entry[b'p2'])] = False
490
487
491 return [rev for rev, ishead in sorted(revishead.items())
488 return [rev for rev, ishead in sorted(revishead.items())
492 if ishead]
489 if ishead]
493
490
494 def heads(self, start=None, stop=None):
491 def heads(self, start=None, stop=None):
495 # This is copied from revlog.py.
492 # This is copied from revlog.py.
496 if start is None and stop is None:
493 if start is None and stop is None:
497 if not len(self):
494 if not len(self):
498 return [nullid]
495 return [nullid]
499 return [self.node(r) for r in self.headrevs()]
496 return [self.node(r) for r in self.headrevs()]
500
497
501 if start is None:
498 if start is None:
502 start = nullid
499 start = nullid
503 if stop is None:
500 if stop is None:
504 stop = []
501 stop = []
505 stoprevs = set([self.rev(n) for n in stop])
502 stoprevs = set([self.rev(n) for n in stop])
506 startrev = self.rev(start)
503 startrev = self.rev(start)
507 reachable = {startrev}
504 reachable = {startrev}
508 heads = {startrev}
505 heads = {startrev}
509
506
510 parentrevs = self.parentrevs
507 parentrevs = self.parentrevs
511 for r in self.revs(start=startrev + 1):
508 for r in self.revs(start=startrev + 1):
512 for p in parentrevs(r):
509 for p in parentrevs(r):
513 if p in reachable:
510 if p in reachable:
514 if r not in stoprevs:
511 if r not in stoprevs:
515 reachable.add(r)
512 reachable.add(r)
516 heads.add(r)
513 heads.add(r)
517 if p in heads and p not in stoprevs:
514 if p in heads and p not in stoprevs:
518 heads.remove(p)
515 heads.remove(p)
519
516
520 return [self.node(r) for r in heads]
517 return [self.node(r) for r in heads]
521
518
522 def children(self, node):
519 def children(self, node):
523 validatenode(node)
520 validatenode(node)
524
521
525 # This is a copy of revlog.children().
522 # This is a copy of revlog.children().
526 c = []
523 c = []
527 p = self.rev(node)
524 p = self.rev(node)
528 for r in self.revs(start=p + 1):
525 for r in self.revs(start=p + 1):
529 prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
526 prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
530 if prevs:
527 if prevs:
531 for pr in prevs:
528 for pr in prevs:
532 if pr == p:
529 if pr == p:
533 c.append(self.node(r))
530 c.append(self.node(r))
534 elif p == nullrev:
531 elif p == nullrev:
535 c.append(self.node(r))
532 c.append(self.node(r))
536 return c
533 return c
537
534
538 def getstrippoint(self, minlink):
535 def getstrippoint(self, minlink):
539
536
540 # This is largely a copy of revlog.getstrippoint().
537 # This is largely a copy of revlog.getstrippoint().
541 brokenrevs = set()
538 brokenrevs = set()
542 strippoint = len(self)
539 strippoint = len(self)
543
540
544 heads = {}
541 heads = {}
545 futurelargelinkrevs = set()
542 futurelargelinkrevs = set()
546 for head in self.headrevs():
543 for head in self.headrevs():
547 headlinkrev = self.linkrev(head)
544 headlinkrev = self.linkrev(head)
548 heads[head] = headlinkrev
545 heads[head] = headlinkrev
549 if headlinkrev >= minlink:
546 if headlinkrev >= minlink:
550 futurelargelinkrevs.add(headlinkrev)
547 futurelargelinkrevs.add(headlinkrev)
551
548
552 # This algorithm involves walking down the rev graph, starting at the
549 # This algorithm involves walking down the rev graph, starting at the
553 # heads. Since the revs are topologically sorted according to linkrev,
550 # heads. Since the revs are topologically sorted according to linkrev,
554 # once all head linkrevs are below the minlink, we know there are
551 # once all head linkrevs are below the minlink, we know there are
555 # no more revs that could have a linkrev greater than minlink.
552 # no more revs that could have a linkrev greater than minlink.
556 # So we can stop walking.
553 # So we can stop walking.
557 while futurelargelinkrevs:
554 while futurelargelinkrevs:
558 strippoint -= 1
555 strippoint -= 1
559 linkrev = heads.pop(strippoint)
556 linkrev = heads.pop(strippoint)
560
557
561 if linkrev < minlink:
558 if linkrev < minlink:
562 brokenrevs.add(strippoint)
559 brokenrevs.add(strippoint)
563 else:
560 else:
564 futurelargelinkrevs.remove(linkrev)
561 futurelargelinkrevs.remove(linkrev)
565
562
566 for p in self.parentrevs(strippoint):
563 for p in self.parentrevs(strippoint):
567 if p != nullrev:
564 if p != nullrev:
568 plinkrev = self.linkrev(p)
565 plinkrev = self.linkrev(p)
569 heads[p] = plinkrev
566 heads[p] = plinkrev
570 if plinkrev >= minlink:
567 if plinkrev >= minlink:
571 futurelargelinkrevs.add(plinkrev)
568 futurelargelinkrevs.add(plinkrev)
572
569
573 return strippoint, brokenrevs
570 return strippoint, brokenrevs
574
571
575 def strip(self, minlink, transaction):
572 def strip(self, minlink, transaction):
576 if not len(self):
573 if not len(self):
577 return
574 return
578
575
579 rev, _ignored = self.getstrippoint(minlink)
576 rev, _ignored = self.getstrippoint(minlink)
580 if rev == len(self):
577 if rev == len(self):
581 return
578 return
582
579
583 # Purge index data starting at the requested revision.
580 # Purge index data starting at the requested revision.
584 self._indexdata[rev:] = []
581 self._indexdata[rev:] = []
585 self._reflectindexupdate()
582 self._reflectindexupdate()
586
583
587 def reposetup(ui, repo):
584 def reposetup(ui, repo):
588 if not repo.local():
585 if not repo.local():
589 return
586 return
590
587
591 if isinstance(repo, bundlerepo.bundlerepository):
588 if isinstance(repo, bundlerepo.bundlerepository):
592 raise error.Abort(_('cannot use simple store with bundlerepo'))
589 raise error.Abort(_('cannot use simple store with bundlerepo'))
593
590
594 class simplestorerepo(repo.__class__):
591 class simplestorerepo(repo.__class__):
595 def file(self, f):
592 def file(self, f):
596 return filestorage(self.svfs, f)
593 return filestorage(self.svfs, f)
597
594
598 repo.__class__ = simplestorerepo
595 repo.__class__ = simplestorerepo
General Comments 0
You need to be logged in to leave comments. Login now