|
|
|
#include "git-compat-util.h"
|
|
|
|
#include "cache.h"
|
|
|
|
#include "pkt-line.h"
|
|
|
|
#include "quote.h"
|
|
|
|
#include "refs.h"
|
|
|
|
#include "run-command.h"
|
|
|
|
#include "remote.h"
|
|
|
|
|
|
|
|
static char *server_capabilities;
|
|
|
|
|
Improve git-peek-remote
This makes git-peek-remote able to basically do everything that
git-ls-remote does (but obviously just for the native protocol, so no
http[s]: or rsync: support).
The default behaviour is the same, but you can now give a mixture of
"--refs", "--tags" and "--heads" flags, where "--refs" forces
git-peek-remote to only show real refs (ie none of the fakey tag lookups,
but also not the special pseudo-refs like HEAD and MERGE_HEAD).
The "--tags" and "--heads" flags respectively limit the output to just
regular tags and heads, of course.
You can still also ask to limit them by name too.
You can combine the flags, so
git peek-remote --refs --tags .
will show all local _true_ tags, without the generated tag lookups
(compare the output without the "--refs" flag).
And "--tags --heads" will show both tags and heads, but will avoid (for
example) any special refs outside of the standard locations.
I'm also planning on adding a "--ignore-local" flag that allows us to ask
it to ignore any refs that we already have in the local tree, but that's
an independent thing.
All this is obviously gearing up to making "git fetch" cheaper.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
19 years ago
|
|
|
static int check_ref(const char *name, int len, unsigned int flags)
|
|
|
|
{
|
|
|
|
if (!flags)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
if (len < 5 || memcmp(name, "refs/", 5))
|
Improve git-peek-remote
This makes git-peek-remote able to basically do everything that
git-ls-remote does (but obviously just for the native protocol, so no
http[s]: or rsync: support).
The default behaviour is the same, but you can now give a mixture of
"--refs", "--tags" and "--heads" flags, where "--refs" forces
git-peek-remote to only show real refs (ie none of the fakey tag lookups,
but also not the special pseudo-refs like HEAD and MERGE_HEAD).
The "--tags" and "--heads" flags respectively limit the output to just
regular tags and heads, of course.
You can still also ask to limit them by name too.
You can combine the flags, so
git peek-remote --refs --tags .
will show all local _true_ tags, without the generated tag lookups
(compare the output without the "--refs" flag).
And "--tags --heads" will show both tags and heads, but will avoid (for
example) any special refs outside of the standard locations.
I'm also planning on adding a "--ignore-local" flag that allows us to ask
it to ignore any refs that we already have in the local tree, but that's
an independent thing.
All this is obviously gearing up to making "git fetch" cheaper.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
19 years ago
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Skip the "refs/" part */
|
|
|
|
name += 5;
|
|
|
|
len -= 5;
|
|
|
|
|
|
|
|
/* REF_NORMAL means that we don't want the magic fake tag refs */
|
|
|
|
if ((flags & REF_NORMAL) && check_ref_format(name) < 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* REF_HEADS means that we want regular branch heads */
|
|
|
|
if ((flags & REF_HEADS) && !memcmp(name, "heads/", 6))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* REF_TAGS means that we want tags */
|
|
|
|
if ((flags & REF_TAGS) && !memcmp(name, "tags/", 5))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* All type bits clear means that we are ok with anything */
|
|
|
|
return !(flags & ~REF_NORMAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
int check_ref_type(const struct ref *ref, int flags)
|
|
|
|
{
|
|
|
|
return check_ref(ref->name, strlen(ref->name), flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void add_extra_have(struct extra_have_objects *extra, unsigned char *sha1)
|
|
|
|
{
|
|
|
|
ALLOC_GROW(extra->array, extra->nr + 1, extra->alloc);
|
|
|
|
hashcpy(&(extra->array[extra->nr][0]), sha1);
|
|
|
|
extra->nr++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read all the refs from the other end
|
|
|
|
*/
|
|
|
|
struct ref **get_remote_heads(int in, struct ref **list,
|
Improve git-peek-remote
This makes git-peek-remote able to basically do everything that
git-ls-remote does (but obviously just for the native protocol, so no
http[s]: or rsync: support).
The default behaviour is the same, but you can now give a mixture of
"--refs", "--tags" and "--heads" flags, where "--refs" forces
git-peek-remote to only show real refs (ie none of the fakey tag lookups,
but also not the special pseudo-refs like HEAD and MERGE_HEAD).
The "--tags" and "--heads" flags respectively limit the output to just
regular tags and heads, of course.
You can still also ask to limit them by name too.
You can combine the flags, so
git peek-remote --refs --tags .
will show all local _true_ tags, without the generated tag lookups
(compare the output without the "--refs" flag).
And "--tags --heads" will show both tags and heads, but will avoid (for
example) any special refs outside of the standard locations.
I'm also planning on adding a "--ignore-local" flag that allows us to ask
it to ignore any refs that we already have in the local tree, but that's
an independent thing.
All this is obviously gearing up to making "git fetch" cheaper.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
19 years ago
|
|
|
int nr_match, char **match,
|
|
|
|
unsigned int flags,
|
|
|
|
struct extra_have_objects *extra_have)
|
|
|
|
{
|
|
|
|
*list = NULL;
|
|
|
|
for (;;) {
|
|
|
|
struct ref *ref;
|
|
|
|
unsigned char old_sha1[20];
|
|
|
|
static char buffer[1000];
|
|
|
|
char *name;
|
|
|
|
int len, name_len;
|
|
|
|
|
|
|
|
len = packet_read_line(in, buffer, sizeof(buffer));
|
|
|
|
if (!len)
|
|
|
|
break;
|
|
|
|
if (buffer[len-1] == '\n')
|
|
|
|
buffer[--len] = 0;
|
|
|
|
|
|
|
|
if (len > 4 && !prefixcmp(buffer, "ERR "))
|
|
|
|
die("remote error: %s", buffer + 4);
|
|
|
|
|
|
|
|
if (len < 42 || get_sha1_hex(buffer, old_sha1) || buffer[40] != ' ')
|
|
|
|
die("protocol error: expected sha/ref, got '%s'", buffer);
|
|
|
|
name = buffer + 41;
|
|
|
|
|
|
|
|
name_len = strlen(name);
|
|
|
|
if (len != name_len + 41) {
|
Avoid unnecessary "if-before-free" tests.
This change removes all obvious useless if-before-free tests.
E.g., it replaces code like this:
if (some_expression)
free (some_expression);
with the now-equivalent:
free (some_expression);
It is equivalent not just because POSIX has required free(NULL)
to work for a long time, but simply because it has worked for
so long that no reasonable porting target fails the test.
Here's some evidence from nearly 1.5 years ago:
http://www.winehq.org/pipermail/wine-patches/2006-October/031544.html
FYI, the change below was prepared by running the following:
git ls-files -z | xargs -0 \
perl -0x3b -pi -e \
's/\bif\s*\(\s*(\S+?)(?:\s*!=\s*NULL)?\s*\)\s+(free\s*\(\s*\1\s*\))/$2/s'
Note however, that it doesn't handle brace-enclosed blocks like
"if (x) { free (x); }". But that's ok, since there were none like
that in git sources.
Beware: if you do use the above snippet, note that it can
produce syntactically invalid C code. That happens when the
affected "if"-statement has a matching "else".
E.g., it would transform this
if (x)
free (x);
else
foo ();
into this:
free (x);
else
foo ();
There were none of those here, either.
If you're interested in automating detection of the useless
tests, you might like the useless-if-before-free script in gnulib:
[it *does* detect brace-enclosed free statements, and has a --name=S
option to make it detect free-like functions with different names]
http://git.sv.gnu.org/gitweb/?p=gnulib.git;a=blob;f=build-aux/useless-if-before-free
Addendum:
Remove one more (in imap-send.c), spotted by Jean-Luc Herren <jlh@gmx.ch>.
Signed-off-by: Jim Meyering <meyering@redhat.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
17 years ago
|
|
|
free(server_capabilities);
|
|
|
|
server_capabilities = xstrdup(name + name_len + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (extra_have &&
|
|
|
|
name_len == 5 && !memcmp(".have", name, 5)) {
|
|
|
|
add_extra_have(extra_have, old_sha1);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
Improve git-peek-remote
This makes git-peek-remote able to basically do everything that
git-ls-remote does (but obviously just for the native protocol, so no
http[s]: or rsync: support).
The default behaviour is the same, but you can now give a mixture of
"--refs", "--tags" and "--heads" flags, where "--refs" forces
git-peek-remote to only show real refs (ie none of the fakey tag lookups,
but also not the special pseudo-refs like HEAD and MERGE_HEAD).
The "--tags" and "--heads" flags respectively limit the output to just
regular tags and heads, of course.
You can still also ask to limit them by name too.
You can combine the flags, so
git peek-remote --refs --tags .
will show all local _true_ tags, without the generated tag lookups
(compare the output without the "--refs" flag).
And "--tags --heads" will show both tags and heads, but will avoid (for
example) any special refs outside of the standard locations.
I'm also planning on adding a "--ignore-local" flag that allows us to ask
it to ignore any refs that we already have in the local tree, but that's
an independent thing.
All this is obviously gearing up to making "git fetch" cheaper.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
19 years ago
|
|
|
if (!check_ref(name, name_len, flags))
|
|
|
|
continue;
|
|
|
|
if (nr_match && !path_match(name, nr_match, match))
|
|
|
|
continue;
|
|
|
|
ref = alloc_ref(buffer + 41);
|
|
|
|
hashcpy(ref->old_sha1, old_sha1);
|
|
|
|
*list = ref;
|
|
|
|
list = &ref->next;
|
|
|
|
}
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
|
|
|
int server_supports(const char *feature)
|
|
|
|
{
|
|
|
|
return server_capabilities &&
|
|
|
|
strstr(server_capabilities, feature) != NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
int get_ack(int fd, unsigned char *result_sha1)
|
|
|
|
{
|
|
|
|
static char line[1000];
|
|
|
|
int len = packet_read_line(fd, line, sizeof(line));
|
|
|
|
|
|
|
|
if (!len)
|
|
|
|
die("git fetch-pack: expected ACK/NAK, got EOF");
|
|
|
|
if (line[len-1] == '\n')
|
|
|
|
line[--len] = 0;
|
|
|
|
if (!strcmp(line, "NAK"))
|
|
|
|
return 0;
|
Mechanical conversion to use prefixcmp()
This mechanically converts strncmp() to use prefixcmp(), but only when
the parameters match specific patterns, so that they can be verified
easily. Leftover from this will be fixed in a separate step, including
idiotic conversions like
if (!strncmp("foo", arg, 3))
=>
if (!(-prefixcmp(arg, "foo")))
This was done by using this script in px.perl
#!/usr/bin/perl -i.bak -p
if (/strncmp\(([^,]+), "([^\\"]*)", (\d+)\)/ && (length($2) == $3)) {
s|strncmp\(([^,]+), "([^\\"]*)", (\d+)\)|prefixcmp($1, "$2")|;
}
if (/strncmp\("([^\\"]*)", ([^,]+), (\d+)\)/ && (length($1) == $3)) {
s|strncmp\("([^\\"]*)", ([^,]+), (\d+)\)|(-prefixcmp($2, "$1"))|;
}
and running:
$ git grep -l strncmp -- '*.c' | xargs perl px.perl
Signed-off-by: Junio C Hamano <junkio@cox.net>
18 years ago
|
|
|
if (!prefixcmp(line, "ACK ")) {
|
|
|
|
if (!get_sha1_hex(line+4, result_sha1)) {
|
|
|
|
if (strstr(line+45, "continue"))
|
|
|
|
return 2;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
die("git fetch_pack: expected ACK/NAK, got '%s'", line);
|
|
|
|
}
|
|
|
|
|
|
|
|
int path_match(const char *path, int nr, char **match)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int pathlen = strlen(path);
|
|
|
|
|
|
|
|
for (i = 0; i < nr; i++) {
|
|
|
|
char *s = match[i];
|
|
|
|
int len = strlen(s);
|
|
|
|
|
|
|
|
if (!len || len > pathlen)
|
|
|
|
continue;
|
|
|
|
if (memcmp(path + pathlen - len, s, len))
|
|
|
|
continue;
|
|
|
|
if (pathlen > len && path[pathlen - len - 1] != '/')
|
|
|
|
continue;
|
|
|
|
*s = 0;
|
|
|
|
return (i + 1);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
enum protocol {
|
|
|
|
PROTO_LOCAL = 1,
|
|
|
|
PROTO_SSH,
|
|
|
|
PROTO_GIT,
|
|
|
|
};
|
|
|
|
|
|
|
|
static enum protocol get_protocol(const char *name)
|
|
|
|
{
|
|
|
|
if (!strcmp(name, "ssh"))
|
|
|
|
return PROTO_SSH;
|
|
|
|
if (!strcmp(name, "git"))
|
|
|
|
return PROTO_GIT;
|
|
|
|
if (!strcmp(name, "git+ssh"))
|
|
|
|
return PROTO_SSH;
|
|
|
|
if (!strcmp(name, "ssh+git"))
|
|
|
|
return PROTO_SSH;
|
|
|
|
if (!strcmp(name, "file"))
|
|
|
|
return PROTO_LOCAL;
|
|
|
|
die("I don't handle protocol '%s'", name);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define STR_(s) # s
|
|
|
|
#define STR(s) STR_(s)
|
|
|
|
|
|
|
|
#ifndef NO_IPV6
|
|
|
|
|
|
|
|
static const char *ai_name(const struct addrinfo *ai)
|
|
|
|
{
|
|
|
|
static char addr[INET_ADDRSTRLEN];
|
|
|
|
if ( AF_INET == ai->ai_family ) {
|
|
|
|
struct sockaddr_in *in;
|
|
|
|
in = (struct sockaddr_in *)ai->ai_addr;
|
|
|
|
inet_ntop(ai->ai_family, &in->sin_addr, addr, sizeof(addr));
|
|
|
|
} else if ( AF_INET6 == ai->ai_family ) {
|
|
|
|
struct sockaddr_in6 *in;
|
|
|
|
in = (struct sockaddr_in6 *)ai->ai_addr;
|
|
|
|
inet_ntop(ai->ai_family, &in->sin6_addr, addr, sizeof(addr));
|
|
|
|
} else {
|
|
|
|
strcpy(addr, "(unknown)");
|
|
|
|
}
|
|
|
|
return addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns a connected socket() fd, or else die()s.
|
|
|
|
*/
|
|
|
|
static int git_tcp_connect_sock(char *host, int flags)
|
|
|
|
{
|
|
|
|
int sockfd = -1, saved_errno = 0;
|
|
|
|
char *colon, *end;
|
|
|
|
const char *port = STR(DEFAULT_GIT_PORT);
|
|
|
|
struct addrinfo hints, *ai0, *ai;
|
|
|
|
int gai;
|
|
|
|
int cnt = 0;
|
|
|
|
|
|
|
|
if (host[0] == '[') {
|
|
|
|
end = strchr(host + 1, ']');
|
|
|
|
if (end) {
|
|
|
|
*end = 0;
|
|
|
|
end++;
|
|
|
|
host++;
|
|
|
|
} else
|
|
|
|
end = host;
|
|
|
|
} else
|
|
|
|
end = host;
|
|
|
|
colon = strchr(end, ':');
|
|
|
|
|
|
|
|
if (colon) {
|
|
|
|
*colon = 0;
|
|
|
|
port = colon + 1;
|
Fix "getaddrinfo()" buglet
At least in Linux glibc, "getaddrinfo()" has a very irritating feature (or
bug, who knows..).
Namely if you pass it in an empty string for the service name, it will
happily and quietly consider it identical to a NULL port pointer, and
return port number zero and no errors. Which obviously will not work.
Maybe that's what it's really expected to do, although the man-page for
getaddrinfo() certainly implies that it's a bug.
So when somebody passes me a "please pull" request pointing to something
like the following
git://git.kernel.org:/pub/scm/linux/kernel/git/mchehab/v4l-dvb.git
(note the extraneous colon at the end of the host name), git would happily
try to connect to port 0, which would generally just cause the remote to
not even answer, and the "connect()" will take a long time to time out.
So to work around the glibc feature/bug, just notice this empty port case
automatically. Also, add the port information to the error information
when it fails to look up (maybe it's the host-name that fails, maybe it's
the port-name - we should print out both).
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
18 years ago
|
|
|
if (!*port)
|
|
|
|
port = "<none>";
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(&hints, 0, sizeof(hints));
|
|
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
|
|
hints.ai_protocol = IPPROTO_TCP;
|
|
|
|
|
|
|
|
if (flags & CONNECT_VERBOSE)
|
|
|
|
fprintf(stderr, "Looking up %s ... ", host);
|
|
|
|
|
|
|
|
gai = getaddrinfo(host, port, &hints, &ai);
|
|
|
|
if (gai)
|
Fix "getaddrinfo()" buglet
At least in Linux glibc, "getaddrinfo()" has a very irritating feature (or
bug, who knows..).
Namely if you pass it in an empty string for the service name, it will
happily and quietly consider it identical to a NULL port pointer, and
return port number zero and no errors. Which obviously will not work.
Maybe that's what it's really expected to do, although the man-page for
getaddrinfo() certainly implies that it's a bug.
So when somebody passes me a "please pull" request pointing to something
like the following
git://git.kernel.org:/pub/scm/linux/kernel/git/mchehab/v4l-dvb.git
(note the extraneous colon at the end of the host name), git would happily
try to connect to port 0, which would generally just cause the remote to
not even answer, and the "connect()" will take a long time to time out.
So to work around the glibc feature/bug, just notice this empty port case
automatically. Also, add the port information to the error information
when it fails to look up (maybe it's the host-name that fails, maybe it's
the port-name - we should print out both).
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
18 years ago
|
|
|
die("Unable to look up %s (port %s) (%s)", host, port, gai_strerror(gai));
|
|
|
|
|
|
|
|
if (flags & CONNECT_VERBOSE)
|
|
|
|
fprintf(stderr, "done.\nConnecting to %s (port %s) ... ", host, port);
|
|
|
|
|
|
|
|
for (ai0 = ai; ai; ai = ai->ai_next) {
|
|
|
|
sockfd = socket(ai->ai_family,
|
|
|
|
ai->ai_socktype, ai->ai_protocol);
|
|
|
|
if (sockfd < 0) {
|
|
|
|
saved_errno = errno;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (connect(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {
|
|
|
|
saved_errno = errno;
|
|
|
|
fprintf(stderr, "%s[%d: %s]: errno=%s\n",
|
|
|
|
host,
|
|
|
|
cnt,
|
|
|
|
ai_name(ai),
|
|
|
|
strerror(saved_errno));
|
|
|
|
close(sockfd);
|
|
|
|
sockfd = -1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (flags & CONNECT_VERBOSE)
|
|
|
|
fprintf(stderr, "%s ", ai_name(ai));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
freeaddrinfo(ai0);
|
|
|
|
|
|
|
|
if (sockfd < 0)
|
|
|
|
die("unable to connect a socket (%s)", strerror(saved_errno));
|
|
|
|
|
|
|
|
if (flags & CONNECT_VERBOSE)
|
|
|
|
fprintf(stderr, "done.\n");
|
|
|
|
|
|
|
|
return sockfd;
|
|
|
|
}
|
|
|
|
|
|
|
|
#else /* NO_IPV6 */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns a connected socket() fd, or else die()s.
|
|
|
|
*/
|
|
|
|
static int git_tcp_connect_sock(char *host, int flags)
|
|
|
|
{
|
|
|
|
int sockfd = -1, saved_errno = 0;
|
|
|
|
char *colon, *end;
|
|
|
|
char *port = STR(DEFAULT_GIT_PORT), *ep;
|
|
|
|
struct hostent *he;
|
|
|
|
struct sockaddr_in sa;
|
|
|
|
char **ap;
|
|
|
|
unsigned int nport;
|
|
|
|
int cnt;
|
|
|
|
|
|
|
|
if (host[0] == '[') {
|
|
|
|
end = strchr(host + 1, ']');
|
|
|
|
if (end) {
|
|
|
|
*end = 0;
|
|
|
|
end++;
|
|
|
|
host++;
|
|
|
|
} else
|
|
|
|
end = host;
|
|
|
|
} else
|
|
|
|
end = host;
|
|
|
|
colon = strchr(end, ':');
|
|
|
|
|
|
|
|
if (colon) {
|
|
|
|
*colon = 0;
|
|
|
|
port = colon + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & CONNECT_VERBOSE)
|
|
|
|
fprintf(stderr, "Looking up %s ... ", host);
|
|
|
|
|
|
|
|
he = gethostbyname(host);
|
|
|
|
if (!he)
|
|
|
|
die("Unable to look up %s (%s)", host, hstrerror(h_errno));
|
|
|
|
nport = strtoul(port, &ep, 10);
|
|
|
|
if ( ep == port || *ep ) {
|
|
|
|
/* Not numeric */
|
|
|
|
struct servent *se = getservbyname(port,"tcp");
|
|
|
|
if ( !se )
|
|
|
|
die("Unknown port %s", port);
|
|
|
|
nport = se->s_port;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & CONNECT_VERBOSE)
|
|
|
|
fprintf(stderr, "done.\nConnecting to %s (port %s) ... ", host, port);
|
|
|
|
|
|
|
|
for (cnt = 0, ap = he->h_addr_list; *ap; ap++, cnt++) {
|
|
|
|
sockfd = socket(he->h_addrtype, SOCK_STREAM, 0);
|
|
|
|
if (sockfd < 0) {
|
|
|
|
saved_errno = errno;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(&sa, 0, sizeof sa);
|
|
|
|
sa.sin_family = he->h_addrtype;
|
|
|
|
sa.sin_port = htons(nport);
|
|
|
|
memcpy(&sa.sin_addr, *ap, he->h_length);
|
|
|
|
|
|
|
|
if (connect(sockfd, (struct sockaddr *)&sa, sizeof sa) < 0) {
|
|
|
|
saved_errno = errno;
|
|
|
|
fprintf(stderr, "%s[%d: %s]: errno=%s\n",
|
|
|
|
host,
|
|
|
|
cnt,
|
|
|
|
inet_ntoa(*(struct in_addr *)&sa.sin_addr),
|
|
|
|
strerror(saved_errno));
|
|
|
|
close(sockfd);
|
|
|
|
sockfd = -1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (flags & CONNECT_VERBOSE)
|
|
|
|
fprintf(stderr, "%s ",
|
|
|
|
inet_ntoa(*(struct in_addr *)&sa.sin_addr));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sockfd < 0)
|
|
|
|
die("unable to connect a socket (%s)", strerror(saved_errno));
|
|
|
|
|
|
|
|
if (flags & CONNECT_VERBOSE)
|
|
|
|
fprintf(stderr, "done.\n");
|
|
|
|
|
|
|
|
return sockfd;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* NO_IPV6 */
|
|
|
|
|
|
|
|
|
|
|
|
static void git_tcp_connect(int fd[2], char *host, int flags)
|
|
|
|
{
|
|
|
|
int sockfd = git_tcp_connect_sock(host, flags);
|
|
|
|
|
|
|
|
fd[0] = sockfd;
|
|
|
|
fd[1] = dup(sockfd);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static char *git_proxy_command;
|
|
|
|
|
|
|
|
static int git_proxy_command_options(const char *var, const char *value,
|
|
|
|
void *cb)
|
|
|
|
{
|
|
|
|
if (!strcmp(var, "core.gitproxy")) {
|
|
|
|
const char *for_pos;
|
|
|
|
int matchlen = -1;
|
|
|
|
int hostlen;
|
|
|
|
const char *rhost_name = cb;
|
|
|
|
int rhost_len = strlen(rhost_name);
|
|
|
|
|
|
|
|
if (git_proxy_command)
|
|
|
|
return 0;
|
|
|
|
if (!value)
|
|
|
|
return config_error_nonbool(var);
|
|
|
|
/* [core]
|
|
|
|
* ;# matches www.kernel.org as well
|
|
|
|
* gitproxy = netcatter-1 for kernel.org
|
|
|
|
* gitproxy = netcatter-2 for sample.xz
|
|
|
|
* gitproxy = netcatter-default
|
|
|
|
*/
|
|
|
|
for_pos = strstr(value, " for ");
|
|
|
|
if (!for_pos)
|
|
|
|
/* matches everybody */
|
|
|
|
matchlen = strlen(value);
|
|
|
|
else {
|
|
|
|
hostlen = strlen(for_pos + 5);
|
|
|
|
if (rhost_len < hostlen)
|
|
|
|
matchlen = -1;
|
|
|
|
else if (!strncmp(for_pos + 5,
|
|
|
|
rhost_name + rhost_len - hostlen,
|
|
|
|
hostlen) &&
|
|
|
|
((rhost_len == hostlen) ||
|
|
|
|
rhost_name[rhost_len - hostlen -1] == '.'))
|
|
|
|
matchlen = for_pos - value;
|
|
|
|
else
|
|
|
|
matchlen = -1;
|
|
|
|
}
|
|
|
|
if (0 <= matchlen) {
|
|
|
|
/* core.gitproxy = none for kernel.org */
|
|
|
|
if (matchlen == 4 &&
|
|
|
|
!memcmp(value, "none", 4))
|
|
|
|
matchlen = 0;
|
|
|
|
git_proxy_command = xmemdupz(value, matchlen);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return git_default_config(var, value, cb);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int git_use_proxy(const char *host)
|
|
|
|
{
|
|
|
|
git_proxy_command = getenv("GIT_PROXY_COMMAND");
|
|
|
|
git_config(git_proxy_command_options, (void*)host);
|
|
|
|
return (git_proxy_command && *git_proxy_command);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void git_proxy_connect(int fd[2], char *host)
|
|
|
|
{
|
|
|
|
const char *port = STR(DEFAULT_GIT_PORT);
|
|
|
|
char *colon, *end;
|
|
|
|
const char *argv[4];
|
|
|
|
struct child_process proxy;
|
|
|
|
|
|
|
|
if (host[0] == '[') {
|
|
|
|
end = strchr(host + 1, ']');
|
|
|
|
if (end) {
|
|
|
|
*end = 0;
|
|
|
|
end++;
|
|
|
|
host++;
|
|
|
|
} else
|
|
|
|
end = host;
|
|
|
|
} else
|
|
|
|
end = host;
|
|
|
|
colon = strchr(end, ':');
|
|
|
|
|
|
|
|
if (colon) {
|
|
|
|
*colon = 0;
|
|
|
|
port = colon + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
argv[0] = git_proxy_command;
|
|
|
|
argv[1] = host;
|
|
|
|
argv[2] = port;
|
|
|
|
argv[3] = NULL;
|
|
|
|
memset(&proxy, 0, sizeof(proxy));
|
|
|
|
proxy.argv = argv;
|
|
|
|
proxy.in = -1;
|
|
|
|
proxy.out = -1;
|
|
|
|
if (start_command(&proxy))
|
|
|
|
die("cannot start proxy %s", argv[0]);
|
|
|
|
fd[0] = proxy.out; /* read from proxy stdout */
|
|
|
|
fd[1] = proxy.in; /* write to proxy stdin */
|
|
|
|
}
|
|
|
|
|
|
|
|
#define MAX_CMD_LEN 1024
|
|
|
|
|
|
|
|
char *get_port(char *host)
|
|
|
|
{
|
|
|
|
char *end;
|
|
|
|
char *p = strchr(host, ':');
|
|
|
|
|
|
|
|
if (p) {
|
|
|
|
long port = strtol(p + 1, &end, 10);
|
|
|
|
if (end != p + 1 && *end == '\0' && 0 <= port && port < 65536) {
|
|
|
|
*p = '\0';
|
|
|
|
return p+1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct child_process no_fork;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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,
|
|
|
|
* finish the connection with finish_connect() with the value returned from
|
|
|
|
* this function (it is safe to call finish_connect() with NULL to support
|
|
|
|
* the former case).
|
|
|
|
*
|
|
|
|
* If it returns, the connect is successful; it just dies on errors (this
|
|
|
|
* will hopefully be changed in a libification effort, to return NULL when
|
|
|
|
* the connection failed).
|
|
|
|
*/
|
|
|
|
struct child_process *git_connect(int fd[2], const char *url_orig,
|
|
|
|
const char *prog, int flags)
|
|
|
|
{
|
|
|
|
char *url = xstrdup(url_orig);
|
|
|
|
char *host, *path;
|
|
|
|
char *end;
|
|
|
|
int c;
|
|
|
|
struct child_process *conn;
|
|
|
|
enum protocol protocol = PROTO_LOCAL;
|
|
|
|
int free_path = 0;
|
|
|
|
char *port = NULL;
|
|
|
|
const char **arg;
|
|
|
|
struct strbuf cmd;
|
|
|
|
|
|
|
|
/* Without this we cannot rely on waitpid() to tell
|
|
|
|
* what happened to our children.
|
|
|
|
*/
|
|
|
|
signal(SIGCHLD, SIG_DFL);
|
|
|
|
|
|
|
|
host = strstr(url, "://");
|
|
|
|
if(host) {
|
|
|
|
*host = '\0';
|
|
|
|
protocol = get_protocol(url);
|
|
|
|
host += 3;
|
|
|
|
c = '/';
|
|
|
|
} else {
|
|
|
|
host = url;
|
|
|
|
c = ':';
|
|
|
|
}
|
|
|
|
|
|
|
|
if (host[0] == '[') {
|
|
|
|
end = strchr(host + 1, ']');
|
|
|
|
if (end) {
|
|
|
|
*end = 0;
|
|
|
|
end++;
|
|
|
|
host++;
|
|
|
|
} else
|
|
|
|
end = host;
|
|
|
|
} else
|
|
|
|
end = host;
|
|
|
|
|
|
|
|
path = strchr(end, c);
|
|
|
|
if (path && !has_dos_drive_prefix(end)) {
|
|
|
|
if (c == ':') {
|
|
|
|
protocol = PROTO_SSH;
|
|
|
|
*path++ = '\0';
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
path = end;
|
|
|
|
|
|
|
|
if (!path || !*path)
|
|
|
|
die("No path specified. See 'man git-pull' for valid url syntax");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* null-terminate hostname and point path to ~ for URL's like this:
|
|
|
|
* ssh://host.xz/~user/repo
|
|
|
|
*/
|
|
|
|
if (protocol != PROTO_LOCAL && host != url) {
|
|
|
|
char *ptr = path;
|
|
|
|
if (path[1] == '~')
|
|
|
|
path++;
|
|
|
|
else {
|
|
|
|
path = xstrdup(ptr);
|
|
|
|
free_path = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
*ptr = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add support for ssh port: ssh://host.xy:<port>/...
|
|
|
|
*/
|
|
|
|
if (protocol == PROTO_SSH && host != url)
|
|
|
|
port = get_port(host);
|
|
|
|
|
|
|
|
if (protocol == PROTO_GIT) {
|
|
|
|
/* These underlying connection commands die() if they
|
|
|
|
* cannot connect.
|
|
|
|
*/
|
|
|
|
char *target_host = xstrdup(host);
|
|
|
|
if (git_use_proxy(host))
|
|
|
|
git_proxy_connect(fd, host);
|
|
|
|
else
|
|
|
|
git_tcp_connect(fd, host, flags);
|
|
|
|
/*
|
|
|
|
* Separate original protocol components prog and path
|
|
|
|
* from extended components with a NUL byte.
|
|
|
|
*/
|
|
|
|
packet_write(fd[1],
|
|
|
|
"%s %s%chost=%s%c",
|
|
|
|
prog, path, 0,
|
|
|
|
target_host, 0);
|
|
|
|
free(target_host);
|
|
|
|
free(url);
|
|
|
|
if (free_path)
|
|
|
|
free(path);
|
|
|
|
return &no_fork;
|
|
|
|
}
|
|
|
|
|
|
|
|
conn = xcalloc(1, sizeof(*conn));
|
|
|
|
|
|
|
|
strbuf_init(&cmd, MAX_CMD_LEN);
|
|
|
|
strbuf_addstr(&cmd, prog);
|
|
|
|
strbuf_addch(&cmd, ' ');
|
|
|
|
sq_quote_buf(&cmd, path);
|
|
|
|
if (cmd.len >= MAX_CMD_LEN)
|
|
|
|
die("command line too long");
|
|
|
|
|
|
|
|
conn->in = conn->out = -1;
|
|
|
|
conn->argv = arg = xcalloc(6, sizeof(*arg));
|
|
|
|
if (protocol == PROTO_SSH) {
|
|
|
|
const char *ssh = getenv("GIT_SSH");
|
|
|
|
if (!ssh) ssh = "ssh";
|
|
|
|
|
|
|
|
*arg++ = ssh;
|
|
|
|
if (port) {
|
|
|
|
*arg++ = "-p";
|
|
|
|
*arg++ = port;
|
|
|
|
}
|
|
|
|
*arg++ = host;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* remove these from the environment */
|
|
|
|
const char *env[] = {
|
|
|
|
ALTERNATE_DB_ENVIRONMENT,
|
|
|
|
DB_ENVIRONMENT,
|
|
|
|
GIT_DIR_ENVIRONMENT,
|
|
|
|
GIT_WORK_TREE_ENVIRONMENT,
|
|
|
|
GRAFT_ENVIRONMENT,
|
|
|
|
INDEX_ENVIRONMENT,
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
conn->env = env;
|
|
|
|
*arg++ = "sh";
|
|
|
|
*arg++ = "-c";
|
|
|
|
}
|
|
|
|
*arg++ = cmd.buf;
|
|
|
|
*arg = NULL;
|
|
|
|
|
|
|
|
if (start_command(conn))
|
|
|
|
die("unable to fork");
|
|
|
|
|
|
|
|
fd[0] = conn->out; /* read from child's stdout */
|
|
|
|
fd[1] = conn->in; /* write to child's stdin */
|
|
|
|
strbuf_release(&cmd);
|
|
|
|
free(url);
|
|
|
|
if (free_path)
|
|
|
|
free(path);
|
|
|
|
return conn;
|
|
|
|
}
|
|
|
|
|
|
|
|
int finish_connect(struct child_process *conn)
|
|
|
|
{
|
|
|
|
int code;
|
|
|
|
if (!conn || conn == &no_fork)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
code = finish_command(conn);
|
|
|
|
free(conn->argv);
|
|
|
|
free(conn);
|
|
|
|
return code;
|
|
|
|
}
|