##// END OF EJS Templates
revlog: new API to emit revision data...
Gregory Szorc -
r39898:5a9ab91e default
parent child Browse files
Show More
@@ -96,6 +96,14 class filelog(object):
96 def emitrevisiondeltas(self, requests):
96 def emitrevisiondeltas(self, requests):
97 return self._revlog.emitrevisiondeltas(requests)
97 return self._revlog.emitrevisiondeltas(requests)
98
98
99 def emitrevisions(self, nodes, nodesorder=None,
100 revisiondata=False, assumehaveparentrevisions=False,
101 deltaprevious=False):
102 return self._revlog.emitrevisions(
103 nodes, nodesorder=nodesorder, revisiondata=revisiondata,
104 assumehaveparentrevisions=assumehaveparentrevisions,
105 deltaprevious=deltaprevious)
106
99 def addrevision(self, revisiondata, transaction, linkrev, p1, p2,
107 def addrevision(self, revisiondata, transaction, linkrev, p1, p2,
100 node=None, flags=revlog.REVIDX_DEFAULT_FLAGS,
108 node=None, flags=revlog.REVIDX_DEFAULT_FLAGS,
101 cachedelta=None):
109 cachedelta=None):
@@ -1565,6 +1565,14 class manifestrevlog(object):
1565 def emitrevisiondeltas(self, requests):
1565 def emitrevisiondeltas(self, requests):
1566 return self._revlog.emitrevisiondeltas(requests)
1566 return self._revlog.emitrevisiondeltas(requests)
1567
1567
1568 def emitrevisions(self, nodes, nodesorder=None,
1569 revisiondata=False, assumehaveparentrevisions=False,
1570 deltaprevious=False):
1571 return self._revlog.emitrevisions(
1572 nodes, nodesorder=nodesorder, revisiondata=revisiondata,
1573 assumehaveparentrevisions=assumehaveparentrevisions,
1574 deltaprevious=deltaprevious)
1575
1568 def addgroup(self, deltas, linkmapper, transaction, addrevisioncb=None):
1576 def addgroup(self, deltas, linkmapper, transaction, addrevisioncb=None):
1569 return self._revlog.addgroup(deltas, linkmapper, transaction,
1577 return self._revlog.addgroup(deltas, linkmapper, transaction,
1570 addrevisioncb=addrevisioncb)
1578 addrevisioncb=addrevisioncb)
@@ -654,6 +654,59 class ifiledata(interfaceutil.Interface)
654 delta against a censored revision.
654 delta against a censored revision.
655 """
655 """
656
656
657 def emitrevisions(nodes,
658 nodesorder=None,
659 revisiondata=False,
660 assumehaveparentrevisions=False,
661 deltaprevious=False):
662 """Produce ``irevisiondelta`` for revisions.
663
664 Given an iterable of nodes, emits objects conforming to the
665 ``irevisiondelta`` interface that describe revisions in storage.
666
667 This method is a generator.
668
669 The input nodes may be unordered. Implementations must ensure that a
670 node's parents are emitted before the node itself. Transitively, this
671 means that a node may only be emitted once all its ancestors in
672 ``nodes`` have also been emitted.
673
674 By default, emits "index" data (the ``node``, ``p1node``, and
675 ``p2node`` attributes). If ``revisiondata`` is set, revision data
676 will also be present on the emitted objects.
677
678 With default argument values, implementations can choose to emit
679 either fulltext revision data or a delta. When emitting deltas,
680 implementations must consider whether the delta's base revision
681 fulltext is available to the receiver.
682
683 The base revision fulltext is guaranteed to be available if any of
684 the following are met:
685
686 * Its fulltext revision was emitted by this method call.
687 * A delta for that revision was emitted by this method call.
688 * ``assumehaveparentrevisions`` is True and the base revision is a
689 parent of the node.
690
691 ``nodesorder`` can be used to control the order that revisions are
692 emitted. By default, revisions can be reordered as long as they are
693 in DAG topological order (see above). If the value is ``nodes``,
694 the iteration order from ``nodes`` should be used. If the value is
695 ``storage``, then the native order from the backing storage layer
696 is used. (Not all storage layers will have strong ordering and behavior
697 of this mode is storage-dependent.) ``nodes`` ordering can force
698 revisions to be emitted before their ancestors, so consumers should
699 use it with care.
700
701 The ``linknode`` attribute on the returned ``irevisiondelta`` may not
702 be set and it is the caller's responsibility to resolve it, if needed.
703
704 If ``deltaprevious`` is True and revision data is requested, all
705 revision data should be emitted as deltas against the revision
706 emitted just prior. The initial revision should be a delta against
707 its 1st parent.
708 """
709
657 class ifilemutation(interfaceutil.Interface):
710 class ifilemutation(interfaceutil.Interface):
658 """Storage interface for mutation events of a tracked file."""
711 """Storage interface for mutation events of a tracked file."""
659
712
@@ -1127,6 +1180,15 class imanifeststorage(interfaceutil.Int
1127 See the documentation for ``ifiledata`` for more.
1180 See the documentation for ``ifiledata`` for more.
1128 """
1181 """
1129
1182
1183 def emitrevisions(nodes,
1184 nodesorder=None,
1185 revisiondata=False,
1186 assumehaveparentrevisions=False):
1187 """Produce ``irevisiondelta`` describing revisions.
1188
1189 See the documentation for ``ifiledata`` for more.
1190 """
1191
1130 def addgroup(deltas, linkmapper, transaction, addrevisioncb=None):
1192 def addgroup(deltas, linkmapper, transaction, addrevisioncb=None):
1131 """Process a series of deltas for storage.
1193 """Process a series of deltas for storage.
1132
1194
@@ -59,6 +59,7 from .thirdparty import (
59 )
59 )
60 from . import (
60 from . import (
61 ancestor,
61 ancestor,
62 dagop,
62 error,
63 error,
63 mdiff,
64 mdiff,
64 policy,
65 policy,
@@ -242,17 +243,17 class _revisioninfo(object):
242 flags = attr.ib()
243 flags = attr.ib()
243
244
244 @interfaceutil.implementer(repository.irevisiondelta)
245 @interfaceutil.implementer(repository.irevisiondelta)
245 @attr.s(slots=True, frozen=True)
246 @attr.s(slots=True)
246 class revlogrevisiondelta(object):
247 class revlogrevisiondelta(object):
247 node = attr.ib()
248 node = attr.ib()
248 p1node = attr.ib()
249 p1node = attr.ib()
249 p2node = attr.ib()
250 p2node = attr.ib()
250 basenode = attr.ib()
251 basenode = attr.ib()
251 linknode = attr.ib()
252 flags = attr.ib()
252 flags = attr.ib()
253 baserevisionsize = attr.ib()
253 baserevisionsize = attr.ib()
254 revision = attr.ib()
254 revision = attr.ib()
255 delta = attr.ib()
255 delta = attr.ib()
256 linknode = attr.ib(default=None)
256
257
257 @interfaceutil.implementer(repository.iverifyproblem)
258 @interfaceutil.implementer(repository.iverifyproblem)
258 @attr.s(frozen=True)
259 @attr.s(frozen=True)
@@ -2374,6 +2375,122 class revlog(object):
2374
2375
2375 prevrev = rev
2376 prevrev = rev
2376
2377
2378 def emitrevisions(self, nodes, nodesorder=None, revisiondata=False,
2379 assumehaveparentrevisions=False, deltaprevious=False):
2380 if nodesorder not in ('nodes', 'storage', None):
2381 raise error.ProgrammingError('unhandled value for nodesorder: %s' %
2382 nodesorder)
2383
2384 if nodesorder is None and not self._generaldelta:
2385 nodesorder = 'storage'
2386
2387 frev = self.rev
2388 fnode = self.node
2389
2390 if nodesorder == 'nodes':
2391 revs = [frev(n) for n in nodes]
2392 elif nodesorder == 'storage':
2393 revs = sorted(frev(n) for n in nodes)
2394 else:
2395 assert self._generaldelta
2396 revs = set(frev(n) for n in nodes)
2397 revs = dagop.linearize(revs, self.parentrevs)
2398
2399 prevrev = None
2400
2401 if deltaprevious or assumehaveparentrevisions:
2402 prevrev = self.parentrevs(revs[0])[0]
2403
2404 # Set of revs available to delta against.
2405 available = set()
2406
2407 for rev in revs:
2408 if rev == nullrev:
2409 continue
2410
2411 node = fnode(rev)
2412 deltaparentrev = self.deltaparent(rev)
2413 p1rev, p2rev = self.parentrevs(rev)
2414
2415 # Forced delta against previous mode.
2416 if deltaprevious:
2417 baserev = prevrev
2418
2419 # Revlog is configured to use full snapshots. Stick to that.
2420 elif not self._storedeltachains:
2421 baserev = nullrev
2422
2423 # There is a delta in storage. We try to use that because it
2424 # amounts to effectively copying data from storage and is
2425 # therefore the fastest.
2426 elif deltaparentrev != nullrev:
2427 # Base revision was already emitted in this group. We can
2428 # always safely use the delta.
2429 if deltaparentrev in available:
2430 baserev = deltaparentrev
2431
2432 # Base revision is a parent that hasn't been emitted already.
2433 # Use it if we can assume the receiver has the parent revision.
2434 elif (assumehaveparentrevisions
2435 and deltaparentrev in (p1rev, p2rev)):
2436 baserev = deltaparentrev
2437
2438 # No guarantee the receiver has the delta parent. Send delta
2439 # against last revision (if possible), which in the common case
2440 # should be similar enough to this revision that the delta is
2441 # reasonable.
2442 elif prevrev is not None:
2443 baserev = prevrev
2444 else:
2445 baserev = nullrev
2446
2447 # Storage has a fulltext revision.
2448
2449 # Let's use the previous revision, which is as good a guess as any.
2450 # There is definitely room to improve this logic.
2451 elif prevrev is not None:
2452 baserev = prevrev
2453 else:
2454 baserev = nullrev
2455
2456 # But we can't actually use our chosen delta base for whatever
2457 # reason. Reset to fulltext.
2458 if baserev != nullrev and not self.candelta(baserev, rev):
2459 baserev = nullrev
2460
2461 revision = None
2462 delta = None
2463 baserevisionsize = None
2464
2465 if revisiondata:
2466 if self.iscensored(baserev) or self.iscensored(rev):
2467 try:
2468 revision = self.revision(node, raw=True)
2469 except error.CensoredNodeError as e:
2470 revision = e.tombstone
2471
2472 if baserev != nullrev:
2473 baserevisionsize = self.rawsize(baserev)
2474
2475 elif baserev == nullrev and not deltaprevious:
2476 revision = self.revision(node, raw=True)
2477 available.add(rev)
2478 else:
2479 delta = self.revdiff(baserev, rev)
2480 available.add(rev)
2481
2482 yield revlogrevisiondelta(
2483 node=node,
2484 p1node=fnode(p1rev),
2485 p2node=fnode(p2rev),
2486 basenode=fnode(baserev),
2487 flags=self.flags(rev),
2488 baserevisionsize=baserevisionsize,
2489 revision=revision,
2490 delta=delta)
2491
2492 prevrev = rev
2493
2377 DELTAREUSEALWAYS = 'always'
2494 DELTAREUSEALWAYS = 'always'
2378 DELTAREUSESAMEREVS = 'samerevs'
2495 DELTAREUSESAMEREVS = 'samerevs'
2379 DELTAREUSENEVER = 'never'
2496 DELTAREUSENEVER = 'never'
@@ -500,6 +500,20 class ifiledatatests(basetestcase):
500 with self.assertRaises(StopIteration):
500 with self.assertRaises(StopIteration):
501 next(gen)
501 next(gen)
502
502
503 # Emitting empty list is an empty generator.
504 gen = f.emitrevisions([])
505 with self.assertRaises(StopIteration):
506 next(gen)
507
508 # Emitting null node yields nothing.
509 gen = f.emitrevisions([nullid])
510 with self.assertRaises(StopIteration):
511 next(gen)
512
513 # Requesting unknown node fails.
514 with self.assertRaises(error.LookupError):
515 list(f.emitrevisions([b'\x01' * 20]))
516
503 def testsinglerevision(self):
517 def testsinglerevision(self):
504 fulltext = b'initial'
518 fulltext = b'initial'
505
519
@@ -566,6 +580,42 class ifiledatatests(basetestcase):
566 with self.assertRaises(StopIteration):
580 with self.assertRaises(StopIteration):
567 next(gen)
581 next(gen)
568
582
583 # Emitting a single revision works.
584 gen = f.emitrevisions([node])
585 rev = next(gen)
586
587 self.assertEqual(rev.node, node)
588 self.assertEqual(rev.p1node, nullid)
589 self.assertEqual(rev.p2node, nullid)
590 self.assertIsNone(rev.linknode)
591 self.assertEqual(rev.basenode, nullid)
592 self.assertIsNone(rev.baserevisionsize)
593 self.assertIsNone(rev.revision)
594 self.assertIsNone(rev.delta)
595
596 with self.assertRaises(StopIteration):
597 next(gen)
598
599 # Requesting revision data works.
600 gen = f.emitrevisions([node], revisiondata=True)
601 rev = next(gen)
602
603 self.assertEqual(rev.node, node)
604 self.assertEqual(rev.p1node, nullid)
605 self.assertEqual(rev.p2node, nullid)
606 self.assertIsNone(rev.linknode)
607 self.assertEqual(rev.basenode, nullid)
608 self.assertIsNone(rev.baserevisionsize)
609 self.assertEqual(rev.revision, fulltext)
610 self.assertIsNone(rev.delta)
611
612 with self.assertRaises(StopIteration):
613 next(gen)
614
615 # Emitting an unknown node after a known revision results in error.
616 with self.assertRaises(error.LookupError):
617 list(f.emitrevisions([node, b'\x01' * 20]))
618
569 def testmultiplerevisions(self):
619 def testmultiplerevisions(self):
570 fulltext0 = b'x' * 1024
620 fulltext0 = b'x' * 1024
571 fulltext1 = fulltext0 + b'y'
621 fulltext1 = fulltext0 + b'y'
@@ -697,6 +747,208 class ifiledatatests(basetestcase):
697 with self.assertRaises(StopIteration):
747 with self.assertRaises(StopIteration):
698 next(gen)
748 next(gen)
699
749
750 # Nodes should be emitted in order.
751 gen = f.emitrevisions([node0, node1, node2], revisiondata=True)
752
753 rev = next(gen)
754
755 self.assertEqual(rev.node, node0)
756 self.assertEqual(rev.p1node, nullid)
757 self.assertEqual(rev.p2node, nullid)
758 self.assertIsNone(rev.linknode)
759 self.assertEqual(rev.basenode, nullid)
760 self.assertIsNone(rev.baserevisionsize)
761 self.assertEqual(rev.revision, fulltext0)
762 self.assertIsNone(rev.delta)
763
764 rev = next(gen)
765
766 self.assertEqual(rev.node, node1)
767 self.assertEqual(rev.p1node, node0)
768 self.assertEqual(rev.p2node, nullid)
769 self.assertIsNone(rev.linknode)
770 self.assertEqual(rev.basenode, node0)
771 self.assertIsNone(rev.baserevisionsize)
772 self.assertIsNone(rev.revision)
773 self.assertEqual(rev.delta,
774 b'\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x01' +
775 fulltext1)
776
777 rev = next(gen)
778
779 self.assertEqual(rev.node, node2)
780 self.assertEqual(rev.p1node, node1)
781 self.assertEqual(rev.p2node, nullid)
782 self.assertIsNone(rev.linknode)
783 self.assertEqual(rev.basenode, node1)
784 self.assertIsNone(rev.baserevisionsize)
785 self.assertIsNone(rev.revision)
786 self.assertEqual(rev.delta,
787 b'\x00\x00\x00\x00\x00\x00\x04\x01\x00\x00\x04\x02' +
788 fulltext2)
789
790 with self.assertRaises(StopIteration):
791 next(gen)
792
793 # Request not in DAG order is reordered to be in DAG order.
794 gen = f.emitrevisions([node2, node1, node0], revisiondata=True)
795
796 rev = next(gen)
797
798 self.assertEqual(rev.node, node0)
799 self.assertEqual(rev.p1node, nullid)
800 self.assertEqual(rev.p2node, nullid)
801 self.assertIsNone(rev.linknode)
802 self.assertEqual(rev.basenode, nullid)
803 self.assertIsNone(rev.baserevisionsize)
804 self.assertEqual(rev.revision, fulltext0)
805 self.assertIsNone(rev.delta)
806
807 rev = next(gen)
808
809 self.assertEqual(rev.node, node1)
810 self.assertEqual(rev.p1node, node0)
811 self.assertEqual(rev.p2node, nullid)
812 self.assertIsNone(rev.linknode)
813 self.assertEqual(rev.basenode, node0)
814 self.assertIsNone(rev.baserevisionsize)
815 self.assertIsNone(rev.revision)
816 self.assertEqual(rev.delta,
817 b'\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x01' +
818 fulltext1)
819
820 rev = next(gen)
821
822 self.assertEqual(rev.node, node2)
823 self.assertEqual(rev.p1node, node1)
824 self.assertEqual(rev.p2node, nullid)
825 self.assertIsNone(rev.linknode)
826 self.assertEqual(rev.basenode, node1)
827 self.assertIsNone(rev.baserevisionsize)
828 self.assertIsNone(rev.revision)
829 self.assertEqual(rev.delta,
830 b'\x00\x00\x00\x00\x00\x00\x04\x01\x00\x00\x04\x02' +
831 fulltext2)
832
833 with self.assertRaises(StopIteration):
834 next(gen)
835
836 # Unrecognized nodesorder value raises ProgrammingError.
837 with self.assertRaises(error.ProgrammingError):
838 list(f.emitrevisions([], nodesorder='bad'))
839
840 # nodesorder=storage is recognized. But we can't test it thoroughly
841 # because behavior is storage-dependent.
842 res = list(f.emitrevisions([node2, node1, node0],
843 nodesorder='storage'))
844 self.assertEqual(len(res), 3)
845 self.assertEqual({o.node for o in res}, {node0, node1, node2})
846
847 # nodesorder=nodes forces the order.
848 gen = f.emitrevisions([node2, node0], nodesorder='nodes',
849 revisiondata=True)
850
851 rev = next(gen)
852 self.assertEqual(rev.node, node2)
853 self.assertEqual(rev.p1node, node1)
854 self.assertEqual(rev.p2node, nullid)
855 self.assertEqual(rev.basenode, nullid)
856 self.assertIsNone(rev.baserevisionsize)
857 self.assertEqual(rev.revision, fulltext2)
858 self.assertIsNone(rev.delta)
859
860 rev = next(gen)
861 self.assertEqual(rev.node, node0)
862 self.assertEqual(rev.p1node, nullid)
863 self.assertEqual(rev.p2node, nullid)
864 # Delta behavior is storage dependent, so we can't easily test it.
865
866 with self.assertRaises(StopIteration):
867 next(gen)
868
869 # assumehaveparentrevisions=False (the default) won't send a delta for
870 # the first revision.
871 gen = f.emitrevisions({node2, node1}, revisiondata=True)
872
873 rev = next(gen)
874 self.assertEqual(rev.node, node1)
875 self.assertEqual(rev.p1node, node0)
876 self.assertEqual(rev.p2node, nullid)
877 self.assertEqual(rev.basenode, nullid)
878 self.assertIsNone(rev.baserevisionsize)
879 self.assertEqual(rev.revision, fulltext1)
880 self.assertIsNone(rev.delta)
881
882 rev = next(gen)
883 self.assertEqual(rev.node, node2)
884 self.assertEqual(rev.p1node, node1)
885 self.assertEqual(rev.p2node, nullid)
886 self.assertEqual(rev.basenode, node1)
887 self.assertIsNone(rev.baserevisionsize)
888 self.assertIsNone(rev.revision)
889 self.assertEqual(rev.delta,
890 b'\x00\x00\x00\x00\x00\x00\x04\x01\x00\x00\x04\x02' +
891 fulltext2)
892
893 with self.assertRaises(StopIteration):
894 next(gen)
895
896 # assumehaveparentrevisions=True allows delta against initial revision.
897 gen = f.emitrevisions([node2, node1],
898 revisiondata=True, assumehaveparentrevisions=True)
899
900 rev = next(gen)
901 self.assertEqual(rev.node, node1)
902 self.assertEqual(rev.p1node, node0)
903 self.assertEqual(rev.p2node, nullid)
904 self.assertEqual(rev.basenode, node0)
905 self.assertIsNone(rev.baserevisionsize)
906 self.assertIsNone(rev.revision)
907 self.assertEqual(rev.delta,
908 b'\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x01' +
909 fulltext1)
910
911 # forceprevious=True forces a delta against the previous revision.
912 # Special case for initial revision.
913 gen = f.emitrevisions([node0], revisiondata=True, deltaprevious=True)
914
915 rev = next(gen)
916 self.assertEqual(rev.node, node0)
917 self.assertEqual(rev.p1node, nullid)
918 self.assertEqual(rev.p2node, nullid)
919 self.assertEqual(rev.basenode, nullid)
920 self.assertIsNone(rev.baserevisionsize)
921 self.assertIsNone(rev.revision)
922 self.assertEqual(rev.delta,
923 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00' +
924 fulltext0)
925
926 with self.assertRaises(StopIteration):
927 next(gen)
928
929 gen = f.emitrevisions([node0, node2], revisiondata=True,
930 deltaprevious=True)
931
932 rev = next(gen)
933 self.assertEqual(rev.node, node0)
934 self.assertEqual(rev.p1node, nullid)
935 self.assertEqual(rev.p2node, nullid)
936 self.assertEqual(rev.basenode, nullid)
937 self.assertIsNone(rev.baserevisionsize)
938 self.assertIsNone(rev.revision)
939 self.assertEqual(rev.delta,
940 b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00' +
941 fulltext0)
942
943 rev = next(gen)
944 self.assertEqual(rev.node, node2)
945 self.assertEqual(rev.p1node, node1)
946 self.assertEqual(rev.p2node, nullid)
947 self.assertEqual(rev.basenode, node0)
948
949 with self.assertRaises(StopIteration):
950 next(gen)
951
700 def testrenamed(self):
952 def testrenamed(self):
701 fulltext0 = b'foo'
953 fulltext0 = b'foo'
702 fulltext1 = b'bar'
954 fulltext1 = b'bar'
General Comments 0
You need to be logged in to leave comments. Login now