Skip to content

Commit

Permalink
Merge branch 'sf/putty-w-args'
Browse files Browse the repository at this point in the history
The command line options for ssh invocation needs to be tweaked for
some implementations of SSH (e.g. PuTTY plink wants "-P <port>"
while OpenSSH wants "-p <port>" to specify port to connect to), and
the variant was guessed when GIT_SSH environment variable is used
to specify it.  The logic to guess now applies to the command
specified by the newer GIT_SSH_COMMAND and also core.sshcommand
configuration variable, and comes with an escape hatch for users to
deal with misdetected cases.

* sf/putty-w-args:
  connect.c: stop conflating ssh command names and overrides
  connect: Add the envvar GIT_SSH_VARIANT and ssh.variant config
  git_connect(): factor out SSH variant handling
  connect: rename tortoiseplink and putty variables
  connect: handle putty/plink also in GIT_SSH_COMMAND
  • Loading branch information
gitster committed Feb 27, 2017
2 parents 098ed50 + 486c8e8 commit be6ab59
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 19 deletions.
11 changes: 11 additions & 0 deletions Documentation/config.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1957,6 +1957,17 @@ Environment variable settings always override any matches. The URLs that are
matched against are those given directly to Git commands. This means any URLs
visited as a result of a redirection do not participate in matching.

ssh.variant::
Depending on the value of the environment variables `GIT_SSH` or
`GIT_SSH_COMMAND`, or the config setting `core.sshCommand`, Git
auto-detects whether to adjust its command-line parameters for use
with plink or tortoiseplink, as opposed to the default (OpenSSH).
+
The config variable `ssh.variant` can be set to override this auto-detection;
valid values are `ssh`, `plink`, `putty` or `tortoiseplink`. Any other value
will be treated as normal ssh. This setting can be overridden via the
environment variable `GIT_SSH_VARIANT`.

i18n.commitEncoding::
Character encoding the commit messages are stored in; Git itself
does not care per se, but this information is necessary e.g. when
Expand Down
6 changes: 6 additions & 0 deletions Documentation/git.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1025,6 +1025,12 @@ Usually it is easier to configure any desired options through your
personal `.ssh/config` file. Please consult your ssh documentation
for further details.

`GIT_SSH_VARIANT`::
If this environment variable is set, it overrides Git's autodetection
whether `GIT_SSH`/`GIT_SSH_COMMAND`/`core.sshCommand` refer to OpenSSH,
plink or tortoiseplink. This variable overrides the config setting
`ssh.variant` that serves the same purpose.

`GIT_ASKPASS`::
If this environment variable is set, then Git commands which need to
acquire passwords or passphrases (e.g. for HTTP or IMAP authentication)
Expand Down
94 changes: 75 additions & 19 deletions connect.c
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,68 @@ static const char *get_ssh_command(void)
return NULL;
}

static int override_ssh_variant(int *port_option, int *needs_batch)
{
char *variant;

variant = xstrdup_or_null(getenv("GIT_SSH_VARIANT"));
if (!variant &&
git_config_get_string("ssh.variant", &variant))
return 0;

if (!strcmp(variant, "plink") || !strcmp(variant, "putty")) {
*port_option = 'P';
*needs_batch = 0;
} else if (!strcmp(variant, "tortoiseplink")) {
*port_option = 'P';
*needs_batch = 1;
} else {
*port_option = 'p';
*needs_batch = 0;
}
free(variant);
return 1;
}

static void handle_ssh_variant(const char *ssh_command, int is_cmdline,
int *port_option, int *needs_batch)
{
const char *variant;
char *p = NULL;

if (override_ssh_variant(port_option, needs_batch))
return;

if (!is_cmdline) {
p = xstrdup(ssh_command);
variant = basename(p);
} else {
const char **ssh_argv;

p = xstrdup(ssh_command);
if (split_cmdline(p, &ssh_argv)) {
variant = basename((char *)ssh_argv[0]);
/*
* At this point, variant points into the buffer
* referenced by p, hence we do not need ssh_argv
* any longer.
*/
free(ssh_argv);
} else
return;
}

if (!strcasecmp(variant, "plink") ||
!strcasecmp(variant, "plink.exe"))
*port_option = 'P';
else if (!strcasecmp(variant, "tortoiseplink") ||
!strcasecmp(variant, "tortoiseplink.exe")) {
*port_option = 'P';
*needs_batch = 1;
}
free(p);
}

/*
* This returns a dummy child_process if the transport protocol does not
* need fork(2), or a struct child_process object if it does. Once done,
Expand Down Expand Up @@ -769,7 +831,8 @@ struct child_process *git_connect(int fd[2], const char *url,
conn->in = conn->out = -1;
if (protocol == PROTO_SSH) {
const char *ssh;
int putty = 0, tortoiseplink = 0;
int needs_batch = 0;
int port_option = 'p';
char *ssh_host = hostandport;
const char *port = NULL;
transport_check_allowed("ssh");
Expand All @@ -792,10 +855,10 @@ struct child_process *git_connect(int fd[2], const char *url,
}

ssh = get_ssh_command();
if (!ssh) {
const char *base;
char *ssh_dup;

if (ssh)
handle_ssh_variant(ssh, 1, &port_option,
&needs_batch);
else {
/*
* GIT_SSH is the no-shell version of
* GIT_SSH_COMMAND (and must remain so for
Expand All @@ -806,29 +869,22 @@ struct child_process *git_connect(int fd[2], const char *url,
ssh = getenv("GIT_SSH");
if (!ssh)
ssh = "ssh";

ssh_dup = xstrdup(ssh);
base = basename(ssh_dup);

tortoiseplink = !strcasecmp(base, "tortoiseplink") ||
!strcasecmp(base, "tortoiseplink.exe");
putty = tortoiseplink ||
!strcasecmp(base, "plink") ||
!strcasecmp(base, "plink.exe");

free(ssh_dup);
else
handle_ssh_variant(ssh, 0,
&port_option,
&needs_batch);
}

argv_array_push(&conn->args, ssh);
if (flags & CONNECT_IPV4)
argv_array_push(&conn->args, "-4");
else if (flags & CONNECT_IPV6)
argv_array_push(&conn->args, "-6");
if (tortoiseplink)
if (needs_batch)
argv_array_push(&conn->args, "-batch");
if (port) {
/* P is for PuTTY, p is for OpenSSH */
argv_array_push(&conn->args, putty ? "-P" : "-p");
argv_array_pushf(&conn->args,
"-%c", port_option);
argv_array_push(&conn->args, port);
}
argv_array_push(&conn->args, ssh_host);
Expand Down
41 changes: 41 additions & 0 deletions t/t5601-clone.sh
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,47 @@ test_expect_success 'tortoiseplink is like putty, with extra arguments' '
expect_ssh "-batch -P 123" myhost src
'

test_expect_success 'double quoted plink.exe in GIT_SSH_COMMAND' '
copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink.exe" &&
GIT_SSH_COMMAND="\"$TRASH_DIRECTORY/plink.exe\" -v" \
git clone "[myhost:123]:src" ssh-bracket-clone-plink-3 &&
expect_ssh "-v -P 123" myhost src
'

SQ="'"
test_expect_success 'single quoted plink.exe in GIT_SSH_COMMAND' '
copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink.exe" &&
GIT_SSH_COMMAND="$SQ$TRASH_DIRECTORY/plink.exe$SQ -v" \
git clone "[myhost:123]:src" ssh-bracket-clone-plink-4 &&
expect_ssh "-v -P 123" myhost src
'

test_expect_success 'GIT_SSH_VARIANT overrides plink detection' '
copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink" &&
GIT_SSH_VARIANT=ssh \
git clone "[myhost:123]:src" ssh-bracket-clone-variant-1 &&
expect_ssh "-p 123" myhost src
'

test_expect_success 'ssh.variant overrides plink detection' '
copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink" &&
git -c ssh.variant=ssh \
clone "[myhost:123]:src" ssh-bracket-clone-variant-2 &&
expect_ssh "-p 123" myhost src
'

test_expect_success 'GIT_SSH_VARIANT overrides plink detection to plink' '
GIT_SSH_VARIANT=plink \
git clone "[myhost:123]:src" ssh-bracket-clone-variant-3 &&
expect_ssh "-P 123" myhost src
'

test_expect_success 'GIT_SSH_VARIANT overrides plink to tortoiseplink' '
GIT_SSH_VARIANT=tortoiseplink \
git clone "[myhost:123]:src" ssh-bracket-clone-variant-4 &&
expect_ssh "-batch -P 123" myhost src
'

# Reset the GIT_SSH environment variable for clone tests.
setup_ssh_wrapper

Expand Down

0 comments on commit be6ab59

Please sign in to comment.