@ -398,6 +398,7 @@ struct git_transport_data {
struct git_transport_options options;
struct git_transport_options options;
struct child_process *conn;
struct child_process *conn;
int fd[2];
int fd[2];
unsigned got_remote_heads : 1;
struct extra_have_objects extra_have;
struct extra_have_objects extra_have;
};
};
@ -432,10 +433,15 @@ static int set_git_option(struct git_transport_options *opts,
static int connect_setup(struct transport *transport, int for_push, int verbose)
static int connect_setup(struct transport *transport, int for_push, int verbose)
{
{
struct git_transport_data *data = transport->data;
struct git_transport_data *data = transport->data;
if (data->conn)
return 0;
data->conn = git_connect(data->fd, transport->url,
data->conn = git_connect(data->fd, transport->url,
for_push ? data->options.receivepack :
for_push ? data->options.receivepack :
data->options.uploadpack,
data->options.uploadpack,
verbose ? CONNECT_VERBOSE : 0);
verbose ? CONNECT_VERBOSE : 0);
return 0;
return 0;
}
}
@ -447,6 +453,7 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus
connect_setup(transport, for_push, 0);
connect_setup(transport, for_push, 0);
get_remote_heads(data->fd[0], &refs, 0, NULL,
get_remote_heads(data->fd[0], &refs, 0, NULL,
for_push ? REF_NORMAL : 0, &data->extra_have);
for_push ? REF_NORMAL : 0, &data->extra_have);
data->got_remote_heads = 1;
return refs;
return refs;
}
}
@ -477,9 +484,10 @@ static int fetch_refs_via_pack(struct transport *transport,
for (i = 0; i < nr_heads; i++)
for (i = 0; i < nr_heads; i++)
origh[i] = heads[i] = xstrdup(to_fetch[i]->name);
origh[i] = heads[i] = xstrdup(to_fetch[i]->name);
if (!data->conn) {
if (!data->got_remote_heads) {
connect_setup(transport, 0, 0);
connect_setup(transport, 0, 0);
get_remote_heads(data->fd[0], &refs_tmp, 0, NULL, 0, NULL);
get_remote_heads(data->fd[0], &refs_tmp, 0, NULL, 0, NULL);
data->got_remote_heads = 1;
}
}
refs = fetch_pack(&args, data->fd, data->conn,
refs = fetch_pack(&args, data->fd, data->conn,
@ -490,6 +498,7 @@ static int fetch_refs_via_pack(struct transport *transport,
if (finish_connect(data->conn))
if (finish_connect(data->conn))
refs = NULL;
refs = NULL;
data->conn = NULL;
data->conn = NULL;
data->got_remote_heads = 0;
free_refs(refs_tmp);
free_refs(refs_tmp);
@ -718,12 +727,13 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
struct send_pack_args args;
struct send_pack_args args;
int ret;
int ret;
if (!data->conn) {
if (!data->got_remote_heads) {
struct ref *tmp_refs;
struct ref *tmp_refs;
connect_setup(transport, 1, 0);
connect_setup(transport, 1, 0);
get_remote_heads(data->fd[0], &tmp_refs, 0, NULL, REF_NORMAL,
get_remote_heads(data->fd[0], &tmp_refs, 0, NULL, REF_NORMAL,
NULL);
NULL);
data->got_remote_heads = 1;
}
}
memset(&args, 0, sizeof(args));
memset(&args, 0, sizeof(args));
@ -741,6 +751,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
close(data->fd[0]);
close(data->fd[0]);
ret |= finish_connect(data->conn);
ret |= finish_connect(data->conn);
data->conn = NULL;
data->conn = NULL;
data->got_remote_heads = 0;
return ret;
return ret;
}
}
@ -749,7 +760,8 @@ static int disconnect_git(struct transport *transport)
{
{
struct git_transport_data *data = transport->data;
struct git_transport_data *data = transport->data;
if (data->conn) {
if (data->conn) {
packet_flush(data->fd[1]);
if (data->got_remote_heads)
packet_flush(data->fd[1]);
close(data->fd[0]);
close(data->fd[0]);
close(data->fd[1]);
close(data->fd[1]);
finish_connect(data->conn);
finish_connect(data->conn);
@ -759,6 +771,32 @@ static int disconnect_git(struct transport *transport)
return 0;
return 0;
}
}
void transport_take_over(struct transport *transport,
struct child_process *child)
{
struct git_transport_data *data;
if (!transport->smart_options)
die("Bug detected: Taking over transport requires non-NULL "
"smart_options field.");
data = xcalloc(1, sizeof(*data));
data->options = *transport->smart_options;
data->conn = child;
data->fd[0] = data->conn->out;
data->fd[1] = data->conn->in;
data->got_remote_heads = 0;
transport->data = data;
transport->set_option = NULL;
transport->get_refs_list = get_refs_via_connect;
transport->fetch = fetch_refs_via_pack;
transport->push = NULL;
transport->push_refs = git_transport_push;
transport->disconnect = disconnect_git;
transport->smart_options = &(data->options);
}
static int is_local(const char *url)
static int is_local(const char *url)
{
{
const char *colon = strchr(url, ':');
const char *colon = strchr(url, ':');
@ -867,6 +905,7 @@ struct transport *transport_get(struct remote *remote, const char *url)
ret->smart_options = &(data->options);
ret->smart_options = &(data->options);
data->conn = NULL;
data->conn = NULL;
data->got_remote_heads = 0;
} else if (!prefixcmp(url, "http://")
} else if (!prefixcmp(url, "http://")
|| !prefixcmp(url, "https://")
|| !prefixcmp(url, "https://")
|| !prefixcmp(url, "ftp://")) {
|| !prefixcmp(url, "ftp://")) {
@ -927,9 +966,9 @@ int transport_push(struct transport *transport,
*nonfastforward = 0;
*nonfastforward = 0;
verify_remote_names(refspec_nr, refspec);
verify_remote_names(refspec_nr, refspec);
if (transport->push)
if (transport->push) {
return transport->push(transport, refspec_nr, refspec, flags);
return transport->push(transport, refspec_nr, refspec, flags);
if (transport->push_refs) {
} else if (transport->push_refs) {
struct ref *remote_refs =
struct ref *remote_refs =
transport->get_refs_list(transport, 1);
transport->get_refs_list(transport, 1);
struct ref *local_refs = get_local_heads();
struct ref *local_refs = get_local_heads();
@ -973,6 +1012,7 @@ const struct ref *transport_get_remote_refs(struct transport *transport)
{
{
if (!transport->remote_refs)
if (!transport->remote_refs)
transport->remote_refs = transport->get_refs_list(transport, 0);
transport->remote_refs = transport->get_refs_list(transport, 0);
return transport->remote_refs;
return transport->remote_refs;
}
}
@ -1007,6 +1047,7 @@ int transport_fetch_refs(struct transport *transport, struct ref *refs)
}
}
rc = transport->fetch(transport, nr_heads, heads);
rc = transport->fetch(transport, nr_heads, heads);
free(heads);
free(heads);
return rc;
return rc;
}
}