Sfoglia il codice sorgente

make Git protocol v2 work over SSH connections

Stefan Sperling 9 mesi fa
parent
commit
2400933b32

+ 23 - 1
dulwich/client.py

@@ -38,6 +38,7 @@ Known capabilities that are not supported:
  * include-tag
 """
 
+import copy
 import logging
 import os
 import select
@@ -1847,6 +1848,7 @@ class SSHVendor:
         password=None,
         key_filename=None,
         ssh_command=None,
+        protocol_version: Optional[int] = None,
     ):
         """Connect to an SSH server.
 
@@ -1886,6 +1888,7 @@ class SubprocessSSHVendor(SSHVendor):
         password=None,
         key_filename=None,
         ssh_command=None,
+        protocol_version=None,
     ):
         if password is not None:
             raise NotImplementedError(
@@ -1905,6 +1908,9 @@ class SubprocessSSHVendor(SSHVendor):
         if key_filename:
             args.extend(["-i", str(key_filename)])
 
+        if protocol_version is None or protocol_version == 2:
+            args.extend(["-o", "SetEnv GIT_PROTOCOL=version=2"])
+
         if username:
             host = f"{username}@{host}"
         if host.startswith("-"):
@@ -1933,6 +1939,7 @@ class PLinkSSHVendor(SSHVendor):
         password=None,
         key_filename=None,
         ssh_command=None,
+        protocol_version: Optional[int] = None,
     ):
         if ssh_command:
             import shlex
@@ -1964,12 +1971,22 @@ class PLinkSSHVendor(SSHVendor):
             raise StrangeHostname(hostname=host)
         args.append(host)
 
+        # plink.exe does not provide a way to pass environment variables
+        # via the command line. The best we can do is set an environment
+        # variable and hope that plink will pass it to the server. If this
+        # does not work then the server should behave as if we had requested
+        # protocol version 0.
+        env = copy.deepcopy(os.environ)
+        if protocol_version is None or protocol_version == 2:
+            env["GIT_PROTOCOL"] = "version=2"
+
         proc = subprocess.Popen(
             [*args, command],
             bufsize=0,
             stdin=subprocess.PIPE,
             stdout=subprocess.PIPE,
             stderr=subprocess.PIPE,
+            env=env,
         )
         return SubprocessWrapper(proc)
 
@@ -2064,7 +2081,12 @@ class SSHGitClient(TraditionalGitClient):
         if self.ssh_command is not None:
             kwargs["ssh_command"] = self.ssh_command
         con = self.ssh_vendor.run_command(
-            self.host, argv, port=self.port, username=self.username, **kwargs
+            self.host,
+            argv,
+            port=self.port,
+            username=self.username,
+            protocol_version=protocol_version,
+            **kwargs,
         )
         return (
             Protocol(

+ 4 - 0
dulwich/contrib/paramiko_vendor.py

@@ -85,6 +85,7 @@ class ParamikoSSHVendor:
         password=None,
         pkey=None,
         key_filename=None,
+        protocol_version=None,
         **kwargs,
     ):
         client = paramiko.SSHClient()
@@ -110,6 +111,9 @@ class ParamikoSSHVendor:
         # Open SSH session
         channel = client.get_transport().open_session()
 
+        if protocol_version is None or protocol_version == 2:
+            channel.set_environment_variable(name="GIT_PROTOCOL", value="version=2")
+
         # Run commands
         channel.exec_command(command)
 

+ 6 - 0
tests/compat/test_client.py

@@ -429,16 +429,22 @@ class TestSSHVendor:
         port=None,
         password=None,
         key_filename=None,
+        protocol_version=None,
     ):
         cmd, path = command.split(" ")
         cmd = cmd.split("-", 1)
         path = path.replace("'", "")
+        env = copy.deepcopy(os.environ)
+        if protocol_version is None or protocol_version == 2:
+            env["GIT_PROTOCOL"] = "version=2"
+
         p = subprocess.Popen(
             [*cmd, path],
             bufsize=0,
             stdin=subprocess.PIPE,
             stdout=subprocess.PIPE,
             stderr=subprocess.PIPE,
+            env=env,
         )
         return client.SubprocessWrapper(p)
 

+ 8 - 2
tests/test_client.py

@@ -714,6 +714,7 @@ class TestSSHVendor:
         password=None,
         key_filename=None,
         ssh_command=None,
+        protocol_version=None,
     ):
         self.host = host
         self.command = command
@@ -722,6 +723,7 @@ class TestSSHVendor:
         self.password = password
         self.key_filename = key_filename
         self.ssh_command = ssh_command
+        self.protocol_version = protocol_version
 
         class Subprocess:
             pass
@@ -1537,6 +1539,8 @@ class SubprocessSSHVendorTests(TestCase):
             "2200",
             "-i",
             "/tmp/id_rsa",
+            "-o",
+            "SetEnv GIT_PROTOCOL=version=2",
             "user@host",
             "git-clone-url",
         ]
@@ -1560,6 +1564,8 @@ class SubprocessSSHVendorTests(TestCase):
             "-o",
             "Option=Value",
             "-x",
+            "-o",
+            "SetEnv GIT_PROTOCOL=version=2",
             "host",
             "git-clone-url",
         ]
@@ -1702,12 +1708,12 @@ class PLinkSSHVendorTests(TestCase):
     def test_run_with_ssh_command(self):
         expected = [
             "/path/to/plink",
-            "-x",
+            "-ssh",
             "host",
             "git-clone-url",
         ]
 
-        vendor = SubprocessSSHVendor()
+        vendor = PLinkSSHVendor()
         command = vendor.run_command(
             "host",
             "git-clone-url",