Support taking over transports
Add support for taking over transports that turn out to be smart. Signed-off-by: Ilari Liusvaara <ilari.liusvaara@elisanet.fi> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Ilari Liusvaara committed
Dec 9, 2009 at 17:26 UTC
61b075bd3e69cbdc9010a7f817b2017455dd36a7
3 files changed
+66
-6
transport-helper.c
+18
-1
@@ -22,6 +22,10 @@ struct helper_data
22
/* These go from remote name (as in "list") to private name */
23
struct refspec *refspecs;
24
int refspec_nr;
25
+ /* Transport options for fetch-pack/send-pack (should one of
26
+ * those be invoked).
27
+ */
28
+ struct git_transport_options transport_options;
29
};
30
31
static void sendline(struct helper_data *helper, struct strbuf *buffer)
@@ -81,6 +85,7 @@ static struct child_process *get_helper(struct transport *transport)
85
const char **refspecs = NULL;
86
int refspec_nr = 0;
87
int refspec_alloc = 0;
88
+ int duped;
89
90
if (data->helper)
91
return data->helper;
@@ -99,9 +104,19 @@ static struct child_process *get_helper(struct transport *transport)
104
die("Unable to run helper: git %s", helper->argv[0]);
105
data->helper = helper;
106
107
+ /*
108
+ * Open the output as FILE* so strbuf_getline() can be used.
109
+ * Do this with duped fd because fclose() will close the fd,
110
+ * and stuff like taking over will require the fd to remain.
111
+ *
112
+ */
113
+ duped = dup(helper->out);
114
+ if (duped < 0)
115
+ die_errno("Can't dup helper output fd");
116
+ data->out = xfdopen(duped, "r");
117
+
118
write_constant(helper->in, "capabilities\n");
119
104
- data->out = xfdopen(helper->out, "r");
120
while (1) {
121
const char *capname;
122
int mandatory = 0;
@@ -163,6 +178,7 @@ static int disconnect_helper(struct transport *transport)
178
strbuf_addf(&buf, "\n");
179
sendline(data, &buf);
180
close(data->helper->in);
181
+ close(data->helper->out);
182
fclose(data->out);
183
finish_command(data->helper);
184
free((char *)data->helper->argv[0]);
@@ -583,5 +599,6 @@ int transport_helper_init(struct transport *transport, const char *name)
599
transport->fetch = fetch;
600
transport->push_refs = push_refs;
601
transport->disconnect = release_helper;
602
+ transport->smart_options = &(data->transport_options);
603
return 0;
604
}
transport.c
+46
-5
@@ -398,6 +398,7 @@ struct git_transport_data {
398
struct git_transport_options options;
399
struct child_process *conn;
400
int fd[2];
401
+ unsigned got_remote_heads : 1;
402
struct extra_have_objects extra_have;
403
};
404
@@ -432,10 +433,15 @@ static int set_git_option(struct git_transport_options *opts,
433
static int connect_setup(struct transport *transport, int for_push, int verbose)
434
{
435
struct git_transport_data *data = transport->data;
436
+
437
+ if (data->conn)
438
+ return 0;
439
+
440
data->conn = git_connect(data->fd, transport->url,
441
for_push ? data->options.receivepack :
442
data->options.uploadpack,
443
verbose ? CONNECT_VERBOSE : 0);
444
+
445
return 0;
446
}
447
@@ -447,6 +453,7 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus
453
connect_setup(transport, for_push, 0);
454
get_remote_heads(data->fd[0], &refs, 0, NULL,
455
for_push ? REF_NORMAL : 0, &data->extra_have);
456
+ data->got_remote_heads = 1;
457
458
return refs;
459
}
@@ -477,9 +484,10 @@ static int fetch_refs_via_pack(struct transport *transport,
484
for (i = 0; i < nr_heads; i++)
485
origh[i] = heads[i] = xstrdup(to_fetch[i]->name);
486
480
- if (!data->conn) {
487
+ if (!data->got_remote_heads) {
488
connect_setup(transport, 0, 0);
489
get_remote_heads(data->fd[0], &refs_tmp, 0, NULL, 0, NULL);
490
+ data->got_remote_heads = 1;
491
}
492
493
refs = fetch_pack(&args, data->fd, data->conn,
@@ -490,6 +498,7 @@ static int fetch_refs_via_pack(struct transport *transport,
498
if (finish_connect(data->conn))
499
refs = NULL;
500
data->conn = NULL;
501
+ data->got_remote_heads = 0;
502
503
free_refs(refs_tmp);
504
@@ -718,12 +727,13 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
727
struct send_pack_args args;
728
int ret;
729
721
- if (!data->conn) {
730
+ if (!data->got_remote_heads) {
731
struct ref *tmp_refs;
732
connect_setup(transport, 1, 0);
733
734
get_remote_heads(data->fd[0], &tmp_refs, 0, NULL, REF_NORMAL,
735
NULL);
736
+ data->got_remote_heads = 1;
737
}
738
739
memset(&args, 0, sizeof(args));
@@ -741,6 +751,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
751
close(data->fd[0]);
752
ret |= finish_connect(data->conn);
753
data->conn = NULL;
754
+ data->got_remote_heads = 0;
755
756
return ret;
757
}
@@ -749,7 +760,8 @@ static int disconnect_git(struct transport *transport)
760
{
761
struct git_transport_data *data = transport->data;
762
if (data->conn) {
752
- packet_flush(data->fd[1]);
763
+ if (data->got_remote_heads)
764
+ packet_flush(data->fd[1]);
765
close(data->fd[0]);
766
close(data->fd[1]);
767
finish_connect(data->conn);
@@ -759,6 +771,32 @@ static int disconnect_git(struct transport *transport)
771
return 0;
772
}
773
774
+void transport_take_over(struct transport *transport,
775
+ struct child_process *child)
776
+{
777
+ struct git_transport_data *data;
778
+
779
+ if (!transport->smart_options)
780
+ die("Bug detected: Taking over transport requires non-NULL "
781
+ "smart_options field.");
782
+
783
+ data = xcalloc(1, sizeof(*data));
784
+ data->options = *transport->smart_options;
785
+ data->conn = child;
786
+ data->fd[0] = data->conn->out;
787
+ data->fd[1] = data->conn->in;
788
+ data->got_remote_heads = 0;
789
+ transport->data = data;
790
+
791
+ transport->set_option = NULL;
792
+ transport->get_refs_list = get_refs_via_connect;
793
+ transport->fetch = fetch_refs_via_pack;
794
+ transport->push = NULL;
795
+ transport->push_refs = git_transport_push;
796
+ transport->disconnect = disconnect_git;
797
+ transport->smart_options = &(data->options);
798
+}
799
+
800
static int is_local(const char *url)
801
{
802
const char *colon = strchr(url, ':');
@@ -867,6 +905,7 @@ struct transport *transport_get(struct remote *remote, const char *url)
905
ret->smart_options = &(data->options);
906
907
data->conn = NULL;
908
+ data->got_remote_heads = 0;
909
} else if (!prefixcmp(url, "http://")
910
|| !prefixcmp(url, "https://")
911
|| !prefixcmp(url, "ftp://")) {
@@ -927,9 +966,9 @@ int transport_push(struct transport *transport,
966
*nonfastforward = 0;
967
verify_remote_names(refspec_nr, refspec);
968
930
- if (transport->push)
969
+ if (transport->push) {
970
return transport->push(transport, refspec_nr, refspec, flags);
932
- if (transport->push_refs) {
971
+ } else if (transport->push_refs) {
972
struct ref *remote_refs =
973
transport->get_refs_list(transport, 1);
974
struct ref *local_refs = get_local_heads();
@@ -973,6 +1012,7 @@ const struct ref *transport_get_remote_refs(struct transport *transport)
1012
{
1013
if (!transport->remote_refs)
1014
transport->remote_refs = transport->get_refs_list(transport, 0);
1015
+
1016
return transport->remote_refs;
1017
}
1018
@@ -1007,6 +1047,7 @@ int transport_fetch_refs(struct transport *transport, struct ref *refs)
1047
}
1048
1049
rc = transport->fetch(transport, nr_heads, heads);
1050
+
1051
free(heads);
1052
return rc;
1053
}
transport.h
+2
@@ -130,6 +130,8 @@ int transport_fetch_refs(struct transport *transport, struct ref *refs);
130
void transport_unlock_pack(struct transport *transport);
131
int transport_disconnect(struct transport *transport);
132
char *transport_anonymize_url(const char *url);
133
+void transport_take_over(struct transport *transport,
134
+ struct child_process *child);
135
136
/* Transport methods defined outside transport.c */
137
int transport_helper_init(struct transport *transport, const char *name);