comparison mercurial/revlog.py @ 1680:c21b54f7f7b8

Merge with crew
author Benoit Boissinot <benoit.boissinot@ens-lyon.org>
date Wed, 01 Feb 2006 19:18:15 +0100
parents b345cc4c22c0 daff3ef0de8d
children
comparison
equal deleted inserted replaced
1679:675ca845c2f8 1680:c21b54f7f7b8
186 """ 186 """
187 self.indexfile = indexfile 187 self.indexfile = indexfile
188 self.datafile = datafile 188 self.datafile = datafile
189 self.opener = opener 189 self.opener = opener
190 self.cache = None 190 self.cache = None
191 self.chunkcache = None
191 192
192 try: 193 try:
193 i = self.opener(self.indexfile).read() 194 i = self.opener(self.indexfile).read()
194 except IOError, inst: 195 except IOError, inst:
195 if inst.errno != errno.ENOENT: 196 if inst.errno != errno.ENOENT:
196 raise 197 raise
197 i = "" 198 i = ""
199
200 if i and i[:4] != "\0\0\0\0":
201 raise RevlogError(_("incompatible revlog signature on %s") %
202 self.indexfile)
198 203
199 if len(i) > 10000: 204 if len(i) > 10000:
200 # big index, let's parse it on demand 205 # big index, let's parse it on demand
201 parser = lazyparser(i, self) 206 parser = lazyparser(i, self)
202 self.index = lazyindex(parser) 207 self.index = lazyindex(parser)
206 l = len(i) / s 211 l = len(i) / s
207 self.index = [None] * l 212 self.index = [None] * l
208 m = [None] * l 213 m = [None] * l
209 214
210 n = 0 215 n = 0
211 for f in xrange(0, len(i), s): 216 for f in xrange(0, l * s, s):
212 # offset, size, base, linkrev, p1, p2, nodeid 217 # offset, size, base, linkrev, p1, p2, nodeid
213 e = struct.unpack(indexformat, i[f:f + s]) 218 e = struct.unpack(indexformat, i[f:f + s])
214 m[n] = (e[6], n) 219 m[n] = (e[6], n)
215 self.index[n] = e 220 self.index[n] = e
216 n += 1 221 n += 1
471 476
472 def patches(self, t, pl): 477 def patches(self, t, pl):
473 """apply a list of patches to a string""" 478 """apply a list of patches to a string"""
474 return mdiff.patches(t, pl) 479 return mdiff.patches(t, pl)
475 480
481 def chunk(self, rev):
482 start, length = self.start(rev), self.length(rev)
483 end = start + length
484
485 def loadcache():
486 cache_length = max(4096 * 1024, length) # 4Mo
487 df = self.opener(self.datafile)
488 df.seek(start)
489 self.chunkcache = (start, df.read(cache_length))
490
491 if not self.chunkcache:
492 loadcache()
493
494 cache_start = self.chunkcache[0]
495 cache_end = cache_start + len(self.chunkcache[1])
496 if start >= cache_start and end <= cache_end:
497 # it is cached
498 offset = start - cache_start
499 else:
500 loadcache()
501 offset = 0
502
503 #def checkchunk():
504 # df = self.opener(self.datafile)
505 # df.seek(start)
506 # return df.read(length)
507 #assert s == checkchunk()
508 return decompress(self.chunkcache[1][offset:offset + length])
509
476 def delta(self, node): 510 def delta(self, node):
477 """return or calculate a delta between a node and its predecessor""" 511 """return or calculate a delta between a node and its predecessor"""
478 r = self.rev(node) 512 r = self.rev(node)
479 b = self.base(r) 513 b = self.base(r)
480 if r == b: 514 if r == b:
481 return self.diff(self.revision(self.node(r - 1)), 515 return self.diff(self.revision(self.node(r - 1)),
482 self.revision(node)) 516 self.revision(node))
483 else: 517 else:
484 f = self.opener(self.datafile) 518 return self.chunk(r)
485 f.seek(self.start(r))
486 data = f.read(self.length(r))
487 return decompress(data)
488 519
489 def revision(self, node): 520 def revision(self, node):
490 """return an uncompressed revision of a given""" 521 """return an uncompressed revision of a given"""
491 if node == nullid: return "" 522 if node == nullid: return ""
492 if self.cache and self.cache[0] == node: return self.cache[2] 523 if self.cache and self.cache[0] == node: return self.cache[2]
493 524
494 # look up what we need to read 525 # look up what we need to read
495 text = None 526 text = None
496 rev = self.rev(node) 527 rev = self.rev(node)
497 start, length, base, link, p1, p2, node = self.index[rev] 528 base = self.base(rev)
498 end = start + length
499 if base != rev: start = self.start(base)
500 529
501 # do we have useful data cached? 530 # do we have useful data cached?
502 if self.cache and self.cache[1] >= base and self.cache[1] < rev: 531 if self.cache and self.cache[1] >= base and self.cache[1] < rev:
503 base = self.cache[1] 532 base = self.cache[1]
504 start = self.start(base + 1)
505 text = self.cache[2] 533 text = self.cache[2]
506 last = 0 534 else:
507 535 text = self.chunk(base)
508 f = self.opener(self.datafile)
509 f.seek(start)
510 data = f.read(end - start)
511
512 if text is None:
513 last = self.length(base)
514 text = decompress(data[:last])
515 536
516 bins = [] 537 bins = []
517 for r in xrange(base + 1, rev + 1): 538 for r in xrange(base + 1, rev + 1):
518 s = self.length(r) 539 bins.append(self.chunk(r))
519 bins.append(decompress(data[last:last + s]))
520 last = last + s
521 540
522 text = mdiff.patches(text, bins) 541 text = mdiff.patches(text, bins)
523 542
543 p1, p2 = self.parents(node)
524 if node != hash(text, p1, p2): 544 if node != hash(text, p1, p2):
525 raise RevlogError(_("integrity check failed on %s:%d") 545 raise RevlogError(_("integrity check failed on %s:%d")
526 % (self.datafile, rev)) 546 % (self.datafile, rev))
527 547
528 self.cache = (node, rev, text) 548 self.cache = (node, rev, text)
648 gy = y.next() 668 gy = y.next()
649 else: 669 else:
650 #print "next x" 670 #print "next x"
651 gx = x.next() 671 gx = x.next()
652 672
653 def group(self, nodelist, lookup, infocollect = None): 673 def group(self, nodelist, lookup, infocollect=None):
654 """calculate a delta group 674 """calculate a delta group
655 675
656 Given a list of changeset revs, return a set of deltas and 676 Given a list of changeset revs, return a set of deltas and
657 metadata corresponding to nodes. the first delta is 677 metadata corresponding to nodes. the first delta is
658 parent(nodes[0]) -> nodes[0] the receiver is guaranteed to 678 parent(nodes[0]) -> nodes[0] the receiver is guaranteed to
659 have this parent as it has all history before these 679 have this parent as it has all history before these
660 changesets. parent is parent[0] 680 changesets. parent is parent[0]
661 """ 681 """
662 revs = [self.rev(n) for n in nodelist] 682 revs = [self.rev(n) for n in nodelist]
663 needed = dict.fromkeys(revs, 1)
664 683
665 # if we don't have any revisions touched by these changesets, bail 684 # if we don't have any revisions touched by these changesets, bail
666 if not revs: 685 if not revs:
667 yield struct.pack(">l", 0) 686 yield struct.pack(">l", 0)
668 return 687 return
669 688
670 # add the parent of the first rev 689 # add the parent of the first rev
671 p = self.parents(self.node(revs[0]))[0] 690 p = self.parents(self.node(revs[0]))[0]
672 revs.insert(0, self.rev(p)) 691 revs.insert(0, self.rev(p))
673 692
674 # for each delta that isn't contiguous in the log, we need to
675 # reconstruct the base, reconstruct the result, and then
676 # calculate the delta. We also need to do this where we've
677 # stored a full version and not a delta
678 for i in xrange(0, len(revs) - 1):
679 a, b = revs[i], revs[i + 1]
680 if a + 1 != b or self.base(b) == b:
681 for j in xrange(self.base(a), a + 1):
682 needed[j] = 1
683 for j in xrange(self.base(b), b + 1):
684 needed[j] = 1
685
686 # calculate spans to retrieve from datafile
687 needed = needed.keys()
688 needed.sort()
689 spans = []
690 oo = -1
691 ol = 0
692 for n in needed:
693 if n < 0: continue
694 o = self.start(n)
695 l = self.length(n)
696 if oo + ol == o: # can we merge with the previous?
697 nl = spans[-1][2]
698 nl.append((n, l))
699 ol += l
700 spans[-1] = (oo, ol, nl)
701 else:
702 oo = o
703 ol = l
704 spans.append((oo, ol, [(n, l)]))
705
706 # read spans in, divide up chunks
707 chunks = {}
708 for span in spans:
709 # we reopen the file for each span to make http happy for now
710 f = self.opener(self.datafile)
711 f.seek(span[0])
712 data = f.read(span[1])
713
714 # divide up the span
715 pos = 0
716 for r, l in span[2]:
717 chunks[r] = decompress(data[pos: pos + l])
718 pos += l
719
720 # helper to reconstruct intermediate versions 693 # helper to reconstruct intermediate versions
721 def construct(text, base, rev): 694 def construct(text, base, rev):
722 bins = [chunks[r] for r in xrange(base + 1, rev + 1)] 695 bins = [self.chunk(r) for r in xrange(base + 1, rev + 1)]
723 return mdiff.patches(text, bins) 696 return mdiff.patches(text, bins)
724 697
725 # build deltas 698 # build deltas
726 deltas = []
727 for d in xrange(0, len(revs) - 1): 699 for d in xrange(0, len(revs) - 1):
728 a, b = revs[d], revs[d + 1] 700 a, b = revs[d], revs[d + 1]
729 n = self.node(b) 701 na = self.node(a)
702 nb = self.node(b)
730 703
731 if infocollect is not None: 704 if infocollect is not None:
732 infocollect(n) 705 infocollect(nb)
733 706
734 # do we need to construct a new delta? 707 # do we need to construct a new delta?
735 if a + 1 != b or self.base(b) == b: 708 if a + 1 != b or self.base(b) == b:
736 if a >= 0: 709 ta = self.revision(na)
737 base = self.base(a) 710 tb = self.revision(nb)
738 ta = chunks[self.base(a)]
739 ta = construct(ta, base, a)
740 else:
741 ta = ""
742
743 base = self.base(b)
744 if a > base:
745 base = a
746 tb = ta
747 else:
748 tb = chunks[self.base(b)]
749 tb = construct(tb, base, b)
750 d = self.diff(ta, tb) 711 d = self.diff(ta, tb)
751 else: 712 else:
752 d = chunks[b] 713 d = self.chunk(b)
753 714
754 p = self.parents(n) 715 p = self.parents(nb)
755 meta = n + p[0] + p[1] + lookup(n) 716 meta = nb + p[0] + p[1] + lookup(nb)
756 l = struct.pack(">l", len(meta) + len(d) + 4) 717 l = struct.pack(">l", len(meta) + len(d) + 4)
757 yield l 718 yield l
758 yield meta 719 yield meta
759 yield d 720 yield d
760 721
878 839
879 def checksize(self): 840 def checksize(self):
880 expected = 0 841 expected = 0
881 if self.count(): 842 if self.count():
882 expected = self.end(self.count() - 1) 843 expected = self.end(self.count() - 1)
844
883 try: 845 try:
884 f = self.opener(self.datafile) 846 f = self.opener(self.datafile)
885 f.seek(0, 2) 847 f.seek(0, 2)
886 actual = f.tell() 848 actual = f.tell()
887 return expected - actual 849 dd = actual - expected
888 except IOError, inst: 850 except IOError, inst:
889 if inst.errno == errno.ENOENT: 851 if inst.errno != errno.ENOENT:
890 return 0 852 raise
891 raise 853 dd = 0
892 854
893 855 try:
856 f = self.opener(self.indexfile)
857 f.seek(0, 2)
858 actual = f.tell()
859 s = struct.calcsize(indexformat)
860 i = actual / s
861 di = actual - (i * s)
862 except IOError, inst:
863 if inst.errno != errno.ENOENT:
864 raise
865 di = 0
866
867 return (dd, di)
868
869