summaryrefslogtreecommitdiffstats
path: root/ssh.py
diff options
context:
space:
mode:
Diffstat (limited to 'ssh.py')
-rw-r--r--ssh.py58
1 files changed, 57 insertions, 1 deletions
diff --git a/ssh.py b/ssh.py
index 54a77304..ffa0d6c0 100644
--- a/ssh.py
+++ b/ssh.py
@@ -24,6 +24,7 @@ import sys
24import tempfile 24import tempfile
25import time 25import time
26 26
27from git_command import git
27import platform_utils 28import platform_utils
28from repo_trace import Trace 29from repo_trace import Trace
29 30
@@ -211,7 +212,33 @@ class ProxyManager:
211 # and print to the log there. 212 # and print to the log there.
212 pass 213 pass
213 214
214 command = command_base[:1] + ["-M", "-N"] + command_base[1:] 215 # Git protocol V2 is a new feature in git 2.18.0, made default in
216 # git 2.26.0
217 # It is faster and more efficient than V1.
218 # To enable it when using SSH, the environment variable GIT_PROTOCOL
219 # must be set in the SSH side channel when establishing the connection
220 # to the git server.
221 # See https://git-scm.com/docs/protocol-v2#_ssh_and_file_transport
222 # Normally git does this by itself. But here, where the SSH connection
223 # is established manually over ControlMaster via the repo-tool, it must
224 # be passed in explicitly instead.
225 # Based on https://git-scm.com/docs/gitprotocol-pack#_extra_parameters,
226 # GIT_PROTOCOL is considered an "Extra Parameter" and must be ignored
227 # by servers that do not understand it. This means that it is safe to
228 # set it even when connecting to older servers.
229 # It should also be safe to set the environment variable for older
230 # local git versions, since it is only part of the ssh side channel.
231 git_protocol_version = _get_git_protocol_version()
232 ssh_git_protocol_args = [
233 "-o",
234 f"SetEnv GIT_PROTOCOL=version={git_protocol_version}",
235 ]
236
237 command = (
238 command_base[:1]
239 + ["-M", "-N", *ssh_git_protocol_args]
240 + command_base[1:]
241 )
215 p = None 242 p = None
216 try: 243 try:
217 with Trace("Call to ssh: %s", " ".join(command)): 244 with Trace("Call to ssh: %s", " ".join(command)):
@@ -293,3 +320,32 @@ class ProxyManager:
293 tempfile.mkdtemp("", "ssh-", tmp_dir), "master-" + tokens 320 tempfile.mkdtemp("", "ssh-", tmp_dir), "master-" + tokens
294 ) 321 )
295 return self._sock_path 322 return self._sock_path
323
324
325@functools.lru_cache(maxsize=1)
326def _get_git_protocol_version() -> str:
327 """Return the git protocol version.
328
329 The version is found by first reading the global git config.
330 If no git config for protocol version exists, try to deduce the default
331 protocol version based on the git version.
332
333 See https://git-scm.com/docs/gitprotocol-v2 for details.
334 """
335 try:
336 return subprocess.check_output(
337 ["git", "config", "--get", "--global", "protocol.version"],
338 encoding="utf-8",
339 stderr=subprocess.PIPE,
340 ).strip()
341 except subprocess.CalledProcessError as e:
342 if e.returncode == 1:
343 # Exit code 1 means that the git config key was not found.
344 # Try to imitate the defaults that git would have used.
345 git_version = git.version_tuple()
346 if git_version >= (2, 26, 0):
347 # Since git version 2.26, protocol v2 is the default.
348 return "2"
349 return "1"
350 # Other exit codes indicate error with reading the config.
351 raise