remote-helpers: Support custom transport options
[git/mingw.git] / transport-helper.c
blob577abc638eb47417277a45a6bd568e24d48e928b
1 #include "cache.h"
2 #include "transport.h"
4 #include "run-command.h"
5 #include "commit.h"
6 #include "diff.h"
7 #include "revision.h"
8 #include "quote.h"
10 struct helper_data
12 const char *name;
13 struct child_process *helper;
14 FILE *out;
15 unsigned fetch : 1,
16 option : 1;
19 static struct child_process *get_helper(struct transport *transport)
21 struct helper_data *data = transport->data;
22 struct strbuf buf = STRBUF_INIT;
23 struct child_process *helper;
25 if (data->helper)
26 return data->helper;
28 helper = xcalloc(1, sizeof(*helper));
29 helper->in = -1;
30 helper->out = -1;
31 helper->err = 0;
32 helper->argv = xcalloc(4, sizeof(*helper->argv));
33 strbuf_addf(&buf, "remote-%s", data->name);
34 helper->argv[0] = strbuf_detach(&buf, NULL);
35 helper->argv[1] = transport->remote->name;
36 helper->argv[2] = transport->url;
37 helper->git_cmd = 1;
38 if (start_command(helper))
39 die("Unable to run helper: git %s", helper->argv[0]);
40 data->helper = helper;
42 write_str_in_full(helper->in, "capabilities\n");
44 data->out = xfdopen(helper->out, "r");
45 while (1) {
46 if (strbuf_getline(&buf, data->out, '\n') == EOF)
47 exit(128); /* child died, message supplied already */
49 if (!*buf.buf)
50 break;
51 if (!strcmp(buf.buf, "fetch"))
52 data->fetch = 1;
53 if (!strcmp(buf.buf, "option"))
54 data->option = 1;
56 return data->helper;
59 static int disconnect_helper(struct transport *transport)
61 struct helper_data *data = transport->data;
62 if (data->helper) {
63 write_str_in_full(data->helper->in, "\n");
64 close(data->helper->in);
65 fclose(data->out);
66 finish_command(data->helper);
67 free((char *)data->helper->argv[0]);
68 free(data->helper->argv);
69 free(data->helper);
70 data->helper = NULL;
72 free(data);
73 return 0;
76 static const char *unsupported_options[] = {
77 TRANS_OPT_UPLOADPACK,
78 TRANS_OPT_RECEIVEPACK,
79 TRANS_OPT_THIN,
80 TRANS_OPT_KEEP
82 static const char *boolean_options[] = {
83 TRANS_OPT_THIN,
84 TRANS_OPT_KEEP,
85 TRANS_OPT_FOLLOWTAGS
88 static int set_helper_option(struct transport *transport,
89 const char *name, const char *value)
91 struct helper_data *data = transport->data;
92 struct child_process *helper = get_helper(transport);
93 struct strbuf buf = STRBUF_INIT;
94 int i, ret, is_bool = 0;
96 if (!data->option)
97 return 1;
99 for (i = 0; i < ARRAY_SIZE(unsupported_options); i++) {
100 if (!strcmp(name, unsupported_options[i]))
101 return 1;
104 for (i = 0; i < ARRAY_SIZE(boolean_options); i++) {
105 if (!strcmp(name, boolean_options[i])) {
106 is_bool = 1;
107 break;
111 strbuf_addf(&buf, "option %s ", name);
112 if (is_bool)
113 strbuf_addstr(&buf, value ? "true" : "false");
114 else
115 quote_c_style(value, &buf, NULL, 0);
116 strbuf_addch(&buf, '\n');
118 if (write_in_full(helper->in, buf.buf, buf.len) != buf.len)
119 die_errno("cannot send option to %s", data->name);
121 strbuf_reset(&buf);
122 if (strbuf_getline(&buf, data->out, '\n') == EOF)
123 exit(128); /* child died, message supplied already */
125 if (!strcmp(buf.buf, "ok"))
126 ret = 0;
127 else if (!prefixcmp(buf.buf, "error")) {
128 ret = -1;
129 } else if (!strcmp(buf.buf, "unsupported"))
130 ret = 1;
131 else {
132 warning("%s unexpectedly said: '%s'", data->name, buf.buf);
133 ret = 1;
135 strbuf_release(&buf);
136 return ret;
139 static void standard_options(struct transport *t)
141 char buf[16];
142 int n;
143 int v = t->verbose;
144 int no_progress = v < 0 || (!t->progress && !isatty(1));
146 set_helper_option(t, "progress", !no_progress ? "true" : "false");
148 n = snprintf(buf, sizeof(buf), "%d", v + 1);
149 if (n >= sizeof(buf))
150 die("impossibly large verbosity value");
151 set_helper_option(t, "verbosity", buf);
154 static int fetch_with_fetch(struct transport *transport,
155 int nr_heads, const struct ref **to_fetch)
157 struct helper_data *data = transport->data;
158 int i;
159 struct strbuf buf = STRBUF_INIT;
161 standard_options(transport);
163 for (i = 0; i < nr_heads; i++) {
164 const struct ref *posn = to_fetch[i];
165 if (posn->status & REF_STATUS_UPTODATE)
166 continue;
168 strbuf_addf(&buf, "fetch %s %s\n",
169 sha1_to_hex(posn->old_sha1), posn->name);
172 strbuf_addch(&buf, '\n');
173 if (write_in_full(data->helper->in, buf.buf, buf.len) != buf.len)
174 die_errno("cannot send fetch to %s", data->name);
176 while (1) {
177 strbuf_reset(&buf);
178 if (strbuf_getline(&buf, data->out, '\n') == EOF)
179 exit(128); /* child died, message supplied already */
181 if (!prefixcmp(buf.buf, "lock ")) {
182 const char *name = buf.buf + 5;
183 if (transport->pack_lockfile)
184 warning("%s also locked %s", data->name, name);
185 else
186 transport->pack_lockfile = xstrdup(name);
188 else if (!buf.len)
189 break;
190 else
191 warning("%s unexpectedly said: '%s'", data->name, buf.buf);
193 strbuf_release(&buf);
194 return 0;
197 static int fetch(struct transport *transport,
198 int nr_heads, const struct ref **to_fetch)
200 struct helper_data *data = transport->data;
201 int i, count;
203 count = 0;
204 for (i = 0; i < nr_heads; i++)
205 if (!(to_fetch[i]->status & REF_STATUS_UPTODATE))
206 count++;
208 if (!count)
209 return 0;
211 if (data->fetch)
212 return fetch_with_fetch(transport, nr_heads, to_fetch);
214 return -1;
217 static struct ref *get_refs_list(struct transport *transport, int for_push)
219 struct helper_data *data = transport->data;
220 struct child_process *helper;
221 struct ref *ret = NULL;
222 struct ref **tail = &ret;
223 struct ref *posn;
224 struct strbuf buf = STRBUF_INIT;
226 helper = get_helper(transport);
228 write_str_in_full(helper->in, "list\n");
230 while (1) {
231 char *eov, *eon;
232 if (strbuf_getline(&buf, data->out, '\n') == EOF)
233 exit(128); /* child died, message supplied already */
235 if (!*buf.buf)
236 break;
238 eov = strchr(buf.buf, ' ');
239 if (!eov)
240 die("Malformed response in ref list: %s", buf.buf);
241 eon = strchr(eov + 1, ' ');
242 *eov = '\0';
243 if (eon)
244 *eon = '\0';
245 *tail = alloc_ref(eov + 1);
246 if (buf.buf[0] == '@')
247 (*tail)->symref = xstrdup(buf.buf + 1);
248 else if (buf.buf[0] != '?')
249 get_sha1_hex(buf.buf, (*tail)->old_sha1);
250 tail = &((*tail)->next);
252 strbuf_release(&buf);
254 for (posn = ret; posn; posn = posn->next)
255 resolve_remote_symref(posn, ret);
257 return ret;
260 int transport_helper_init(struct transport *transport, const char *name)
262 struct helper_data *data = xcalloc(sizeof(*data), 1);
263 data->name = name;
265 transport->data = data;
266 transport->set_option = set_helper_option;
267 transport->get_refs_list = get_refs_list;
268 transport->fetch = fetch;
269 transport->disconnect = disconnect_helper;
270 return 0;