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