Sfoglia il codice sorgente

client: Add http support for 'git-upload-pack'.

Jelmer Vernooij 13 anni fa
parent
commit
20f6a5c165
2 ha cambiato i file con 85 aggiunte e 44 eliminazioni
  1. 3 0
      NEWS
  2. 82 44
      dulwich/client.py

+ 3 - 0
NEWS

@@ -11,6 +11,9 @@
   * ``GitClient.send_pack`` now supports the 'side-band-64k' capability.
     (Jelmer Vernooij)
 
+  * ``HttpGitClient`` which supports the smart server protocol over
+    HTTP. "dumb" access is not yet supported. (Jelmer Vernooij)
+
  CHANGES
 
   * unittest2 or python >= 2.7 is now required for the testsuite.

+ 82 - 44
dulwich/client.py

@@ -312,6 +312,71 @@ class GitClient(object):
         if data:
             raise SendPackError('Unexpected response %r' % data)
 
+    def _handle_upload_pack_head(self, proto, capabilities, graph_walker,
+                                 wants, can_read):
+        """Handle the head of a 'git-upload-pack' request.
+
+        :param proto: Protocol object to read from
+        :param capabilities: List of negotiated capabilities
+        :param graph_walker: GraphWalker instance to call .ack() on
+        :param wants: List of commits to fetch
+        :param can_read: function that returns a boolean that indicates
+            whether there is extra graph data to read on proto
+        """
+        assert isinstance(wants, list) and type(wants[0]) == str
+        proto.write_pkt_line('want %s %s\n' % (
+            wants[0], ' '.join(capabilities)))
+        for want in wants[1:]:
+            proto.write_pkt_line('want %s\n' % want)
+        proto.write_pkt_line(None)
+        have = graph_walker.next()
+        while have:
+            proto.write_pkt_line('have %s\n' % have)
+            if can_read():
+                pkt = proto.read_pkt_line()
+                parts = pkt.rstrip('\n').split(' ')
+                if parts[0] == 'ACK':
+                    graph_walker.ack(parts[1])
+                    if parts[2] in ('continue', 'common'):
+                        pass
+                    elif parts[2] == 'ready':
+                        break
+                    else:
+                        raise AssertionError(
+                            "%s not in ('continue', 'ready', 'common)" %
+                            parts[2])
+            have = graph_walker.next()
+        proto.write_pkt_line('done\n')
+
+    def _handle_upload_pack_tail(self, proto, capabilities, graph_walker,
+                                 pack_data, progress):
+        """Handle the tail of a 'git-upload-pack' request.
+
+        :param proto: Protocol object to read from
+        :param capabilities: List of negotiated capabilities
+        :param graph_walker: GraphWalker instance to call .ack() on
+        :param pack_data: Function to call with pack data
+        :param progress: Optional progress reporting function
+        """
+        pkt = proto.read_pkt_line()
+        while pkt:
+            parts = pkt.rstrip('\n').split(' ')
+            if parts[0] == 'ACK':
+                graph_walker.ack(pkt.split(' ')[1])
+            if len(parts) < 3 or parts[2] not in ('continue', 'common'):
+                break
+            pkt = proto.read_pkt_line()
+        if "side-band-64k" in capabilities:
+            self._read_side_band64k_data(proto, {1: pack_data, 2: progress})
+            # wait for EOF before returning
+            data = proto.read()
+            if data:
+                raise Exception('Unexpected response %r' % data)
+        else:
+            # FIXME: Buffering?
+            pack_data(self.read())
+
+
 
 class TraditionalGitClient(GitClient):
     """Traditional Git client."""
@@ -378,47 +443,10 @@ class TraditionalGitClient(GitClient):
         if not wants:
             proto.write_pkt_line(None)
             return refs
-        assert isinstance(wants, list) and type(wants[0]) == str
-        proto.write_pkt_line('want %s %s\n' % (
-            wants[0], ' '.join(negotiated_capabilities)))
-        for want in wants[1:]:
-            proto.write_pkt_line('want %s\n' % want)
-        proto.write_pkt_line(None)
-        have = graph_walker.next()
-        while have:
-            proto.write_pkt_line('have %s\n' % have)
-            if can_read():
-                pkt = proto.read_pkt_line()
-                parts = pkt.rstrip('\n').split(' ')
-                if parts[0] == 'ACK':
-                    graph_walker.ack(parts[1])
-                    if parts[2] in ('continue', 'common'):
-                        pass
-                    elif parts[2] == 'ready':
-                        break
-                    else:
-                        raise AssertionError(
-                            "%s not in ('continue', 'ready', 'common)" %
-                            parts[2])
-            have = graph_walker.next()
-        proto.write_pkt_line('done\n')
-        pkt = proto.read_pkt_line()
-        while pkt:
-            parts = pkt.rstrip('\n').split(' ')
-            if parts[0] == 'ACK':
-                graph_walker.ack(pkt.split(' ')[1])
-            if len(parts) < 3 or parts[2] not in ('continue', 'common'):
-                break
-            pkt = proto.read_pkt_line()
-        if "side-band-64k" in negotiated_capabilities:
-            self._read_side_band64k_data(proto, {1: pack_data, 2: progress})
-            # wait for EOF before returning
-            data = proto.read()
-            if data:
-                raise Exception('Unexpected response %r' % data)
-        else:
-            # FIXME: Buffering?
-            pack_data(self.read())
+        self._handle_upload_pack_head(proto, negotiated_capabilities,
+            graph_walker, wants, can_read)
+        self._handle_upload_pack_tail(proto, negotiated_capabilities,
+            graph_walker, pack_data, progress)
         return refs
 
 
@@ -574,7 +602,7 @@ class HttpGitClient(GitClient):
             # The first line should mention the service
             pkts = list(proto.read_pkt_seq())
             if pkts != [('# service=%s\n' % service)]:
-                raise ValueError("unexpected first line %r from smart server" % pkt)
+                raise ValueError("unexpected first line %r from smart server" % pkts)
         return self._read_refs(proto)
 
     def _smart_request(self, service, url, data):
@@ -640,13 +668,23 @@ class HttpGitClient(GitClient):
         url = urlparse.urljoin(self.url, path)
         refs, server_capabilities = self._discover_references(
             "git-upload-pack", url)
+        negotiated_capabilities = list(server_capabilities)
         wants = determine_wants(refs)
         if not wants:
             return refs
         if self.dumb:
             raise NotImplementedError(self.send_pack)
-        resp = self._smart_request("git-upload-pack", url)
-        raise NotImplementedError(self.send_pack)
+        req_data = StringIO()
+        req_proto = Protocol(None, req_data.write)
+        self._handle_upload_pack_head(req_proto,
+            negotiated_capabilities, graph_walker, wants,
+            lambda: False)
+        resp = self._smart_request("git-upload-pack", url,
+            data=req_data.getvalue())
+        resp_proto = Protocol(resp.read, None)
+        self._handle_upload_pack_tail(resp_proto, negotiated_capabilities,
+            graph_walker, pack_data, progress)
+        return refs
 
 
 def get_transport_and_path(uri):