Mercurial > public > mercurial-scm > hg
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 |