Skip to content

Commit 0da0e49

Browse files
jrngitster
authored andcommitted
ssh: 'auto' variant to select between 'ssh' and 'simple'
Android's "repo" tool is a tool for managing a large codebase consisting of multiple smaller repositories, similar to Git's submodule feature. Starting with Git 94b8ae5 (ssh: introduce a 'simple' ssh variant, 2017-10-16), users noticed that it stopped handling the port in ssh:// URLs. The cause: when it encounters ssh:// URLs, repo pre-connects to the server and sets GIT_SSH to a helper ".repo/repo/git_ssh" that reuses that connection. Before 94b8ae5, the helper was assumed to support OpenSSH options for lack of a better guess and got passed a -p option to set the port. After that patch, it uses the new default of a simple helper that does not accept an option to set the port. The next release of "repo" will set GIT_SSH_VARIANT to "ssh" to avoid that. But users of old versions and of other similar GIT_SSH implementations would not get the benefit of that fix. So update the default to use OpenSSH options again, with a twist. As observed in 94b8ae5, we cannot assume that $GIT_SSH always handles OpenSSH options: common helpers such as travis-ci's dpl[*] are configured using GIT_SSH and do not accept OpenSSH options. So make the default a new variant "auto", with the following behavior: 1. First, check for a recognized basename, like today. 2. If the basename is not recognized, check whether $GIT_SSH supports OpenSSH options by running $GIT_SSH -G <options> <host> This returns status 0 and prints configuration in OpenSSH if it recognizes all <options> and returns status 255 if it encounters an unrecognized option. A wrapper script like exec ssh -- "$@" would fail with ssh: Could not resolve hostname -g: Name or service not known , correctly reflecting that it does not support OpenSSH options. The command is run with stdin, stdout, and stderr redirected to /dev/null so even a command that expects a terminal would exit immediately. 3. Based on the result from step (2), behave like "ssh" (if it succeeded) or "simple" (if it failed). This way, the default ssh variant for unrecognized commands can handle both the repo and dpl cases as intended. This autodetection has been running on Google workstations since 2017-10-23 with no reported negative effects. [*] https://github.com/travis-ci/dpl/blob/6c3fddfda1f2a85944c544446b068bac0a77c049/lib/dpl/provider.rb#L215 Reported-by: William Yan <[email protected]> Improved-by: Jonathan Tan <[email protected]> Signed-off-by: Jonathan Nieder <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 957e2ad commit 0da0e49

File tree

3 files changed

+62
-17
lines changed

3 files changed

+62
-17
lines changed

Documentation/config.txt

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2081,16 +2081,22 @@ matched against are those given directly to Git commands. This means any URLs
20812081
visited as a result of a redirection do not participate in matching.
20822082

20832083
ssh.variant::
2084-
Depending on the value of the environment variables `GIT_SSH` or
2085-
`GIT_SSH_COMMAND`, or the config setting `core.sshCommand`, Git
2086-
auto-detects whether to adjust its command-line parameters for use
2087-
with ssh (OpenSSH), plink or tortoiseplink, as opposed to the default
2088-
(simple).
2089-
+
2090-
The config variable `ssh.variant` can be set to override this auto-detection;
2091-
valid values are `ssh`, `simple`, `plink`, `putty` or `tortoiseplink`. Any
2092-
other value will be treated as normal ssh. This setting can be overridden via
2093-
the environment variable `GIT_SSH_VARIANT`.
2084+
By default, Git determines the command line arguments to use
2085+
based on the basename of the configured SSH command (configured
2086+
using the environment variable `GIT_SSH` or `GIT_SSH_COMMAND` or
2087+
the config setting `core.sshCommand`). If the basename is
2088+
unrecognized, Git will attempt to detect support of OpenSSH
2089+
options by first invoking the configured SSH command with the
2090+
`-G` (print configuration) option and will subsequently use
2091+
OpenSSH options (if that is successful) or no options besides
2092+
the host and remote command (if it fails).
2093+
+
2094+
The config variable `ssh.variant` can be set to override this detection.
2095+
Valid values are `ssh` (to use OpenSSH options), `plink`, `putty`,
2096+
`tortoiseplink`, `simple` (no options except the host and remote command).
2097+
The default auto-detection can be explicitly requested using the value
2098+
`auto`. Any other value is treated as `ssh`. This setting can also be
2099+
overridden via the environment variable `GIT_SSH_VARIANT`.
20942100
+
20952101
The current command-line parameters used for each variant are as
20962102
follows:

connect.c

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -788,21 +788,24 @@ static const char *get_ssh_command(void)
788788
}
789789

790790
enum ssh_variant {
791+
VARIANT_AUTO,
791792
VARIANT_SIMPLE,
792793
VARIANT_SSH,
793794
VARIANT_PLINK,
794795
VARIANT_PUTTY,
795796
VARIANT_TORTOISEPLINK,
796797
};
797798

798-
static int override_ssh_variant(enum ssh_variant *ssh_variant)
799+
static void override_ssh_variant(enum ssh_variant *ssh_variant)
799800
{
800801
const char *variant = getenv("GIT_SSH_VARIANT");
801802

802803
if (!variant && git_config_get_string_const("ssh.variant", &variant))
803-
return 0;
804+
return;
804805

805-
if (!strcmp(variant, "plink"))
806+
if (!strcmp(variant, "auto"))
807+
*ssh_variant = VARIANT_AUTO;
808+
else if (!strcmp(variant, "plink"))
806809
*ssh_variant = VARIANT_PLINK;
807810
else if (!strcmp(variant, "putty"))
808811
*ssh_variant = VARIANT_PUTTY;
@@ -812,18 +815,18 @@ static int override_ssh_variant(enum ssh_variant *ssh_variant)
812815
*ssh_variant = VARIANT_SIMPLE;
813816
else
814817
*ssh_variant = VARIANT_SSH;
815-
816-
return 1;
817818
}
818819

819820
static enum ssh_variant determine_ssh_variant(const char *ssh_command,
820821
int is_cmdline)
821822
{
822-
enum ssh_variant ssh_variant = VARIANT_SIMPLE;
823+
enum ssh_variant ssh_variant = VARIANT_AUTO;
823824
const char *variant;
824825
char *p = NULL;
825826

826-
if (override_ssh_variant(&ssh_variant))
827+
override_ssh_variant(&ssh_variant);
828+
829+
if (ssh_variant != VARIANT_AUTO)
827830
return ssh_variant;
828831

829832
if (!is_cmdline) {
@@ -982,6 +985,21 @@ static void fill_ssh_args(struct child_process *conn, const char *ssh_host,
982985
variant = determine_ssh_variant(ssh, 0);
983986
}
984987

988+
if (variant == VARIANT_AUTO) {
989+
struct child_process detect = CHILD_PROCESS_INIT;
990+
991+
detect.use_shell = conn->use_shell;
992+
detect.no_stdin = detect.no_stdout = detect.no_stderr = 1;
993+
994+
argv_array_push(&detect.args, ssh);
995+
argv_array_push(&detect.args, "-G");
996+
push_ssh_options(&detect.args, &detect.env_array,
997+
VARIANT_SSH, port, flags);
998+
argv_array_push(&detect.args, ssh_host);
999+
1000+
variant = run_command(&detect) ? VARIANT_SIMPLE : VARIANT_SSH;
1001+
}
1002+
9851003
argv_array_push(&conn->args, ssh);
9861004
push_ssh_options(&conn->args, &conn->env_array, variant, port, flags);
9871005
argv_array_push(&conn->args, ssh_host);

t/t5601-clone.sh

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,12 @@ test_expect_success 'variant can be overriden' '
369369
expect_ssh myhost src
370370
'
371371

372+
test_expect_success 'variant=auto picks based on basename' '
373+
copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink" &&
374+
git -c ssh.variant=auto clone -4 "[myhost:123]:src" ssh-auto-clone &&
375+
expect_ssh "-4 -P 123" myhost src
376+
'
377+
372378
test_expect_success 'simple is treated as simple' '
373379
copy_ssh_wrapper_as "$TRASH_DIRECTORY/simple" &&
374380
git clone -4 "[myhost:123]:src" ssh-bracket-clone-simple &&
@@ -381,6 +387,21 @@ test_expect_success 'uplink is treated as simple' '
381387
expect_ssh myhost src
382388
'
383389

390+
test_expect_success 'OpenSSH-like uplink is treated as ssh' '
391+
write_script "$TRASH_DIRECTORY/uplink" <<-EOF &&
392+
if test "\$1" = "-G"
393+
then
394+
exit 0
395+
fi &&
396+
exec "\$TRASH_DIRECTORY/ssh$X" "\$@"
397+
EOF
398+
test_when_finished "rm -f \"\$TRASH_DIRECTORY/uplink\"" &&
399+
GIT_SSH="$TRASH_DIRECTORY/uplink" &&
400+
test_when_finished "GIT_SSH=\"\$TRASH_DIRECTORY/ssh\$X\"" &&
401+
git clone "[myhost:123]:src" ssh-bracket-clone-sshlike-uplink &&
402+
expect_ssh "-p 123" myhost src
403+
'
404+
384405
test_expect_success 'plink is treated specially (as putty)' '
385406
copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink" &&
386407
git clone "[myhost:123]:src" ssh-bracket-clone-plink-0 &&

0 commit comments

Comments
 (0)