diff options
-rw-r--r-- | ssh.py | 58 |
1 files changed, 57 insertions, 1 deletions
@@ -24,6 +24,7 @@ import sys | |||
24 | import tempfile | 24 | import tempfile |
25 | import time | 25 | import time |
26 | 26 | ||
27 | from git_command import git | ||
27 | import platform_utils | 28 | import platform_utils |
28 | from repo_trace import Trace | 29 | from 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) | ||
326 | def _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 | ||