Commit Diff


commit - 325de7d3731d19a8290c08a540a09024b4b1c7fd
commit + 2400933b32d3f966f3bcdbfa2dad3c4e9e712100
blob - 80b203c147d755d8dc92268c9b30b1954670e04f
blob + d29341f13ea650841c8f93db9a5ba2e3262bb2ba
--- dulwich/client.py
+++ 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(
@@ -1904,6 +1907,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}"
@@ -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
@@ -1963,6 +1970,15 @@ class PLinkSSHVendor(SSHVendor):
         if host.startswith("-"):
             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],
@@ -1970,6 +1986,7 @@ class PLinkSSHVendor(SSHVendor):
             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(
blob - f3189a3c04aef30ef342851ab68cf441b1a6170b
blob + 4d6da7f464136e8b4c2d476f062395a4343e837b
--- dulwich/contrib/paramiko_vendor.py
+++ 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)
 
blob - 7763d62967420378b4d71d935f47a3c781a01656
blob + f58683b13734e562019c2ede56f3d1029ba87b39
--- tests/compat/test_client.py
+++ 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)
 
blob - 4c577545c96c8286d0eceac5624dfecf0d6fdf85
blob + 5bc1ada62218f301e6261516d1f0f1ed2c2c6a2a
--- tests/test_client.py
+++ 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",