Skip to content

Commit c610988

Browse files
committed
Merge pull request #1006 from segevfiner/git-ssh-command-putty
connect: recognize [tortoise]plink in GIT_SSH_COMMAND
2 parents d0bc813 + 34a8c59 commit c610988

File tree

4 files changed

+114
-19
lines changed

4 files changed

+114
-19
lines changed

Documentation/config.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1975,6 +1975,17 @@ Environment variable settings always override any matches. The URLs that are
19751975
matched against are those given directly to Git commands. This means any URLs
19761976
visited as a result of a redirection do not participate in matching.
19771977

1978+
ssh.variant::
1979+
Depending on the value of the environment variables `GIT_SSH` or
1980+
`GIT_SSH_COMMAND`, or the config setting `core.sshCommand`, Git
1981+
auto-detects whether to adjust its command-line parameters for use
1982+
with plink or tortoiseplink, as opposed to the default (OpenSSH).
1983+
+
1984+
The config variable `ssh.variant` can be set to override this auto-detection;
1985+
valid values are `ssh`, `plink`, `putty` or `tortoiseplink`. Any other value
1986+
will be treated as normal ssh. This setting can be overridden via the
1987+
environment variable `GIT_SSH_VARIANT`.
1988+
19781989
i18n.commitEncoding::
19791990
Character encoding the commit messages are stored in; Git itself
19801991
does not care per se, but this information is necessary e.g. when

Documentation/git.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,6 +1020,12 @@ Usually it is easier to configure any desired options through your
10201020
personal `.ssh/config` file. Please consult your ssh documentation
10211021
for further details.
10221022

1023+
`GIT_SSH_VARIANT`::
1024+
If this environment variable is set, it overrides Git's autodetection
1025+
whether `GIT_SSH`/`GIT_SSH_COMMAND`/`core.sshCommand` refer to OpenSSH,
1026+
plink or tortoiseplink. This variable overrides the config setting
1027+
`ssh.variant` that serves the same purpose.
1028+
10231029
`GIT_ASKPASS`::
10241030
If this environment variable is set, then Git commands which need to
10251031
acquire passwords or passphrases (e.g. for HTTP or IMAP authentication)

connect.c

Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,49 @@ static const char *get_ssh_command(void)
691691
return NULL;
692692
}
693693

694+
static int handle_ssh_variant(const char *ssh_command, int is_cmdline,
695+
int *port_option, int *needs_batch)
696+
{
697+
const char *variant = getenv("GIT_SSH_VARIANT");
698+
char *p = NULL;
699+
700+
if (variant)
701+
; /* okay, fall through */
702+
else if (!git_config_get_string("ssh.variant", &p))
703+
variant = p;
704+
else if (!is_cmdline) {
705+
p = xstrdup(ssh_command);
706+
variant = basename(p);
707+
} else {
708+
const char **ssh_argv;
709+
710+
p = xstrdup(ssh_command);
711+
if (split_cmdline(p, &ssh_argv)) {
712+
variant = basename((char *)ssh_argv[0]);
713+
/*
714+
* At this point, variant points into the buffer
715+
* referenced by p, hence we do not need ssh_argv
716+
* any longer.
717+
*/
718+
free(ssh_argv);
719+
} else
720+
return 0;
721+
}
722+
723+
if (!strcasecmp(variant, "plink") ||
724+
!strcasecmp(variant, "plink.exe") ||
725+
!strcasecmp(variant, "putty"))
726+
*port_option = 'P';
727+
else if (!strcasecmp(variant, "tortoiseplink") ||
728+
!strcasecmp(variant, "tortoiseplink.exe")) {
729+
*port_option = 'P';
730+
*needs_batch = 1;
731+
}
732+
free(p);
733+
734+
return 1;
735+
}
736+
694737
/*
695738
* This returns a dummy child_process if the transport protocol does not
696739
* need fork(2), or a struct child_process object if it does. Once done,
@@ -769,7 +812,8 @@ struct child_process *git_connect(int fd[2], const char *url,
769812
conn->in = conn->out = -1;
770813
if (protocol == PROTO_SSH) {
771814
const char *ssh;
772-
int putty = 0, tortoiseplink = 0;
815+
int needs_batch = 0;
816+
int port_option = 'p';
773817
char *ssh_host = hostandport;
774818
const char *port = NULL;
775819
transport_check_allowed("ssh");
@@ -792,10 +836,10 @@ struct child_process *git_connect(int fd[2], const char *url,
792836
}
793837

794838
ssh = get_ssh_command();
795-
if (!ssh) {
796-
const char *base;
797-
char *ssh_dup;
798-
839+
if (ssh)
840+
handle_ssh_variant(ssh, 1, &port_option,
841+
&needs_batch);
842+
else {
799843
/*
800844
* GIT_SSH is the no-shell version of
801845
* GIT_SSH_COMMAND (and must remain so for
@@ -806,29 +850,22 @@ struct child_process *git_connect(int fd[2], const char *url,
806850
ssh = getenv("GIT_SSH");
807851
if (!ssh)
808852
ssh = "ssh";
809-
810-
ssh_dup = xstrdup(ssh);
811-
base = basename(ssh_dup);
812-
813-
tortoiseplink = !strcasecmp(base, "tortoiseplink") ||
814-
!strcasecmp(base, "tortoiseplink.exe");
815-
putty = tortoiseplink ||
816-
!strcasecmp(base, "plink") ||
817-
!strcasecmp(base, "plink.exe");
818-
819-
free(ssh_dup);
853+
else
854+
handle_ssh_variant(ssh, 0,
855+
&port_option,
856+
&needs_batch);
820857
}
821858

822859
argv_array_push(&conn->args, ssh);
823860
if (flags & CONNECT_IPV4)
824861
argv_array_push(&conn->args, "-4");
825862
else if (flags & CONNECT_IPV6)
826863
argv_array_push(&conn->args, "-6");
827-
if (tortoiseplink)
864+
if (needs_batch)
828865
argv_array_push(&conn->args, "-batch");
829866
if (port) {
830-
/* P is for PuTTY, p is for OpenSSH */
831-
argv_array_push(&conn->args, putty ? "-P" : "-p");
867+
argv_array_pushf(&conn->args,
868+
"-%c", port_option);
832869
argv_array_push(&conn->args, port);
833870
}
834871
argv_array_push(&conn->args, ssh_host);

t/t5601-clone.sh

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,47 @@ test_expect_success 'tortoiseplink is like putty, with extra arguments' '
386386
expect_ssh "-batch -P 123" myhost src
387387
'
388388

389+
test_expect_success 'double quoted plink.exe in GIT_SSH_COMMAND' '
390+
copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink.exe" &&
391+
GIT_SSH_COMMAND="\"$TRASH_DIRECTORY/plink.exe\" -v" \
392+
git clone "[myhost:123]:src" ssh-bracket-clone-plink-3 &&
393+
expect_ssh "-v -P 123" myhost src
394+
'
395+
396+
SQ="'"
397+
test_expect_success 'single quoted plink.exe in GIT_SSH_COMMAND' '
398+
copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink.exe" &&
399+
GIT_SSH_COMMAND="$SQ$TRASH_DIRECTORY/plink.exe$SQ -v" \
400+
git clone "[myhost:123]:src" ssh-bracket-clone-plink-4 &&
401+
expect_ssh "-v -P 123" myhost src
402+
'
403+
404+
test_expect_success 'GIT_SSH_VARIANT overrides plink detection' '
405+
copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink" &&
406+
GIT_SSH_VARIANT=ssh \
407+
git clone "[myhost:123]:src" ssh-bracket-clone-variant-1 &&
408+
expect_ssh "-p 123" myhost src
409+
'
410+
411+
test_expect_success 'ssh.variant overrides plink detection' '
412+
copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink" &&
413+
git -c ssh.variant=ssh \
414+
clone "[myhost:123]:src" ssh-bracket-clone-variant-2 &&
415+
expect_ssh "-p 123" myhost src
416+
'
417+
418+
test_expect_success 'GIT_SSH_VARIANT overrides plink detection to plink' '
419+
GIT_SSH_VARIANT=plink \
420+
git clone "[myhost:123]:src" ssh-bracket-clone-variant-3 &&
421+
expect_ssh "-P 123" myhost src
422+
'
423+
424+
test_expect_success 'GIT_SSH_VARIANT overrides plink to tortoiseplink' '
425+
GIT_SSH_VARIANT=tortoiseplink \
426+
git clone "[myhost:123]:src" ssh-bracket-clone-variant-4 &&
427+
expect_ssh "-batch -P 123" myhost src
428+
'
429+
389430
# Reset the GIT_SSH environment variable for clone tests.
390431
setup_ssh_wrapper
391432

0 commit comments

Comments
 (0)