summaryrefslogtreecommitdiffstats
path: root/ssh.py
diff options
context:
space:
mode:
authorErik Elmeke <erik@haleytek.corp-partner.google.com>2024-02-29 17:33:51 +0100
committerLUCI <gerrit-scoped@luci-project-accounts.iam.gserviceaccount.com>2024-05-16 13:26:46 +0000
commiteede374e3ec446d5f03c12a886efcb2d8f946917 (patch)
tree7d6ba67193c8a4972280c4b16fb571099ad4ce2a /ssh.py
parent2c5fb84d357d9df797b9186b4dc711cf723cb535 (diff)
downloadgit-repo-eede374e3ec446d5f03c12a886efcb2d8f946917.tar.gz
ssh: Set git protocol version 2 on SSH ControlMaster
According to https://git-scm.com/docs/protocol-v2#_ssh_and_file_transport, when using SSH, the environment variable GIT_PROTOCOL must be set when establishing the connection to the git server. Normally git does this by itself. But in repo-tool where the SSH connection is managed by the repo-tool, it must be passed in explicitly instead. Under some circumstances of environment configuration, this caused all repo sync commands over ssh to always use git protocol version 1. Even when git was configured to use version 2. Using git protocol v2 can significantly improve fetch speeds, since it uses server side filtering of refs, reducing the amount of unneccessary objects to send. Change-Id: I6d4c3b7300a6090d707480b1a638ed03622fa71a Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/411362 Tested-by: Erik Elmeke <erik@haleytek.corp-partner.google.com> Reviewed-by: Mike Frysinger <vapier@google.com> Commit-Queue: Erik Elmeke <erik@haleytek.corp-partner.google.com>
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