Code

remote-helpers: Support custom transport options
[git.git] / transport-helper.c
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
11 {
12         const char *name;
13         struct child_process *helper;
14         FILE *out;
15         unsigned fetch : 1,
16                 option : 1;
17 };
19 static struct child_process *get_helper(struct transport *transport)
20 {
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;
55         }
56         return data->helper;
57 }
59 static int disconnect_helper(struct transport *transport)
60 {
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;
71         }
72         free(data);
73         return 0;
74 }
76 static const char *unsupported_options[] = {
77         TRANS_OPT_UPLOADPACK,
78         TRANS_OPT_RECEIVEPACK,
79         TRANS_OPT_THIN,
80         TRANS_OPT_KEEP
81         };
82 static const char *boolean_options[] = {
83         TRANS_OPT_THIN,
84         TRANS_OPT_KEEP,
85         TRANS_OPT_FOLLOWTAGS
86         };
88 static int set_helper_option(struct transport *transport,
89                           const char *name, const char *value)
90 {
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;
102         }
104         for (i = 0; i < ARRAY_SIZE(boolean_options); i++) {
105                 if (!strcmp(name, boolean_options[i])) {
106                         is_bool = 1;
107                         break;
108                 }
109         }
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;
134         }
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);
170         }
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);
187                 }
188                 else if (!buf.len)
189                         break;
190                 else
191                         warning("%s unexpectedly said: '%s'", data->name, buf.buf);
192         }
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);
251         }
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;