Skip to content

Commit eede374

Browse files
Erik ElmekeLUCI
Erik Elmeke
authored and
LUCI
committed
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 <[email protected]> Reviewed-by: Mike Frysinger <[email protected]> Commit-Queue: Erik Elmeke <[email protected]>
1 parent 2c5fb84 commit eede374

File tree

1 file changed

+57
-1
lines changed

1 file changed

+57
-1
lines changed

ssh.py

+57-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import tempfile
2525
import time
2626

27+
from git_command import git
2728
import platform_utils
2829
from repo_trace import Trace
2930

@@ -211,7 +212,33 @@ def _open_unlocked(self, host, port=None):
211212
# and print to the log there.
212213
pass
213214

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+
)
215242
p = None
216243
try:
217244
with Trace("Call to ssh: %s", " ".join(command)):
@@ -293,3 +320,32 @@ def sock(self, create=True):
293320
tempfile.mkdtemp("", "ssh-", tmp_dir), "master-" + tokens
294321
)
295322
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

0 commit comments

Comments
 (0)