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 | ||
