775 Extensions can monkeypatch this function to provide custom caching |
775 Extensions can monkeypatch this function to provide custom caching |
776 backends. |
776 backends. |
777 """ |
777 """ |
778 return None |
778 return None |
779 |
779 |
|
780 def resolvenodes(repo, revisions): |
|
781 """Resolve nodes from a revisions specifier data structure.""" |
|
782 cl = repo.changelog |
|
783 clhasnode = cl.hasnode |
|
784 |
|
785 seen = set() |
|
786 nodes = [] |
|
787 |
|
788 if not isinstance(revisions, list): |
|
789 raise error.WireprotoCommandError('revisions must be defined as an ' |
|
790 'array') |
|
791 |
|
792 for spec in revisions: |
|
793 if b'type' not in spec: |
|
794 raise error.WireprotoCommandError( |
|
795 'type key not present in revision specifier') |
|
796 |
|
797 typ = spec[b'type'] |
|
798 |
|
799 if typ == b'changesetexplicit': |
|
800 if b'nodes' not in spec: |
|
801 raise error.WireprotoCommandError( |
|
802 'nodes key not present in changesetexplicit revision ' |
|
803 'specifier') |
|
804 |
|
805 for node in spec[b'nodes']: |
|
806 if node not in seen: |
|
807 nodes.append(node) |
|
808 seen.add(node) |
|
809 |
|
810 elif typ == b'changesetexplicitdepth': |
|
811 for key in (b'nodes', b'depth'): |
|
812 if key not in spec: |
|
813 raise error.WireprotoCommandError( |
|
814 '%s key not present in changesetexplicitdepth revision ' |
|
815 'specifier', (key,)) |
|
816 |
|
817 for rev in repo.revs(b'ancestors(%ln, %d)', spec[b'nodes'], |
|
818 spec[b'depth'] - 1): |
|
819 node = cl.node(rev) |
|
820 |
|
821 if node not in seen: |
|
822 nodes.append(node) |
|
823 seen.add(node) |
|
824 |
|
825 elif typ == b'changesetdagrange': |
|
826 for key in (b'roots', b'heads'): |
|
827 if key not in spec: |
|
828 raise error.WireprotoCommandError( |
|
829 '%s key not present in changesetdagrange revision ' |
|
830 'specifier', (key,)) |
|
831 |
|
832 if not spec[b'heads']: |
|
833 raise error.WireprotoCommandError( |
|
834 'heads key in changesetdagrange cannot be empty') |
|
835 |
|
836 if spec[b'roots']: |
|
837 common = [n for n in spec[b'roots'] if clhasnode(n)] |
|
838 else: |
|
839 common = [nullid] |
|
840 |
|
841 for n in discovery.outgoing(repo, common, spec[b'heads']).missing: |
|
842 if n not in seen: |
|
843 nodes.append(n) |
|
844 seen.add(n) |
|
845 |
|
846 else: |
|
847 raise error.WireprotoCommandError( |
|
848 'unknown revision specifier type: %s', (typ,)) |
|
849 |
|
850 return nodes |
|
851 |
780 @wireprotocommand('branchmap', permission='pull') |
852 @wireprotocommand('branchmap', permission='pull') |
781 def branchmapv2(repo, proto): |
853 def branchmapv2(repo, proto): |
782 yield {encoding.fromlocal(k): v |
854 yield {encoding.fromlocal(k): v |
783 for k, v in repo.branchmap().iteritems()} |
855 for k, v in repo.branchmap().iteritems()} |
784 |
856 |
787 yield _capabilitiesv2(repo, proto) |
859 yield _capabilitiesv2(repo, proto) |
788 |
860 |
789 @wireprotocommand( |
861 @wireprotocommand( |
790 'changesetdata', |
862 'changesetdata', |
791 args={ |
863 args={ |
792 'noderange': { |
864 'revisions': { |
793 'type': 'list', |
865 'type': 'list', |
794 'default': lambda: None, |
866 'example': [{ |
795 'example': [[b'0123456...'], [b'abcdef...']], |
867 b'type': b'changesetexplicit', |
796 }, |
868 b'nodes': [b'abcdef...'], |
797 'nodes': { |
869 }], |
798 'type': 'list', |
|
799 'default': lambda: None, |
|
800 'example': [b'0123456...'], |
|
801 }, |
|
802 'nodesdepth': { |
|
803 'type': 'int', |
|
804 'default': lambda: None, |
|
805 'example': 10, |
|
806 }, |
870 }, |
807 'fields': { |
871 'fields': { |
808 'type': 'set', |
872 'type': 'set', |
809 'default': set, |
873 'default': set, |
810 'example': {b'parents', b'revision'}, |
874 'example': {b'parents', b'revision'}, |
811 'validvalues': {b'bookmarks', b'parents', b'phase', b'revision'}, |
875 'validvalues': {b'bookmarks', b'parents', b'phase', b'revision'}, |
812 }, |
876 }, |
813 }, |
877 }, |
814 permission='pull') |
878 permission='pull') |
815 def changesetdata(repo, proto, noderange, nodes, nodesdepth, fields): |
879 def changesetdata(repo, proto, revisions, fields): |
816 # TODO look for unknown fields and abort when they can't be serviced. |
880 # TODO look for unknown fields and abort when they can't be serviced. |
817 # This could probably be validated by dispatcher using validvalues. |
881 # This could probably be validated by dispatcher using validvalues. |
818 |
882 |
819 if noderange is None and nodes is None: |
|
820 raise error.WireprotoCommandError( |
|
821 'noderange or nodes must be defined') |
|
822 |
|
823 if nodesdepth is not None and nodes is None: |
|
824 raise error.WireprotoCommandError( |
|
825 'nodesdepth requires the nodes argument') |
|
826 |
|
827 if noderange is not None: |
|
828 if len(noderange) != 2: |
|
829 raise error.WireprotoCommandError( |
|
830 'noderange must consist of 2 elements') |
|
831 |
|
832 if not noderange[1]: |
|
833 raise error.WireprotoCommandError( |
|
834 'heads in noderange request cannot be empty') |
|
835 |
|
836 cl = repo.changelog |
883 cl = repo.changelog |
837 hasnode = cl.hasnode |
884 outgoing = resolvenodes(repo, revisions) |
838 |
|
839 seen = set() |
|
840 outgoing = [] |
|
841 |
|
842 if nodes is not None: |
|
843 outgoing = [n for n in nodes if hasnode(n)] |
|
844 |
|
845 if nodesdepth: |
|
846 outgoing = [cl.node(r) for r in |
|
847 repo.revs(b'ancestors(%ln, %d)', outgoing, |
|
848 nodesdepth - 1)] |
|
849 |
|
850 seen |= set(outgoing) |
|
851 |
|
852 if noderange is not None: |
|
853 if noderange[0]: |
|
854 common = [n for n in noderange[0] if hasnode(n)] |
|
855 else: |
|
856 common = [nullid] |
|
857 |
|
858 for n in discovery.outgoing(repo, common, noderange[1]).missing: |
|
859 if n not in seen: |
|
860 outgoing.append(n) |
|
861 # Don't need to add to seen here because this is the final |
|
862 # source of nodes and there should be no duplicates in this |
|
863 # list. |
|
864 |
|
865 seen.clear() |
|
866 publishing = repo.publishing() |
885 publishing = repo.publishing() |
867 |
886 |
868 if outgoing: |
887 if outgoing: |
869 repo.hook('preoutgoing', throw=True, source='serve') |
888 repo.hook('preoutgoing', throw=True, source='serve') |
870 |
889 |