Code

fix minor memory leak in get_tree_entry()
[git.git] / transport-helper.c
1 #include "cache.h"
2 #include "transport.h"
3 #include "quote.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                 push : 1;
18 };
20 static struct child_process *get_helper(struct transport *transport)
21 {
22         struct helper_data *data = transport->data;
23         struct strbuf buf = STRBUF_INIT;
24         struct child_process *helper;
26         if (data->helper)
27                 return data->helper;
29         helper = xcalloc(1, sizeof(*helper));
30         helper->in = -1;
31         helper->out = -1;
32         helper->err = 0;
33         helper->argv = xcalloc(4, sizeof(*helper->argv));
34         strbuf_addf(&buf, "remote-%s", data->name);
35         helper->argv[0] = strbuf_detach(&buf, NULL);
36         helper->argv[1] = transport->remote->name;
37         helper->argv[2] = transport->url;
38         helper->git_cmd = 1;
39         if (start_command(helper))
40                 die("Unable to run helper: git %s", helper->argv[0]);
41         data->helper = helper;
43         write_str_in_full(helper->in, "capabilities\n");
45         data->out = xfdopen(helper->out, "r");
46         while (1) {
47                 if (strbuf_getline(&buf, data->out, '\n') == EOF)
48                         exit(128); /* child died, message supplied already */
50                 if (!*buf.buf)
51                         break;
52                 if (!strcmp(buf.buf, "fetch"))
53                         data->fetch = 1;
54                 if (!strcmp(buf.buf, "option"))
55                         data->option = 1;
56                 if (!strcmp(buf.buf, "push"))
57                         data->push = 1;
58         }
59         return data->helper;
60 }
62 static int disconnect_helper(struct transport *transport)
63 {
64         struct helper_data *data = transport->data;
65         if (data->helper) {
66                 write_str_in_full(data->helper->in, "\n");
67                 close(data->helper->in);
68                 fclose(data->out);
69                 finish_command(data->helper);
70                 free((char *)data->helper->argv[0]);
71                 free(data->helper->argv);
72                 free(data->helper);
73                 data->helper = NULL;
74         }
75         free(data);
76         return 0;
77 }
79 static const char *unsupported_options[] = {
80         TRANS_OPT_UPLOADPACK,
81         TRANS_OPT_RECEIVEPACK,
82         TRANS_OPT_THIN,
83         TRANS_OPT_KEEP
84         };
85 static const char *boolean_options[] = {
86         TRANS_OPT_THIN,
87         TRANS_OPT_KEEP,
88         TRANS_OPT_FOLLOWTAGS
89         };
91 static int set_helper_option(struct transport *transport,
92                           const char *name, const char *value)
93 {
94         struct helper_data *data = transport->data;
95         struct child_process *helper = get_helper(transport);
96         struct strbuf buf = STRBUF_INIT;
97         int i, ret, is_bool = 0;
99         if (!data->option)
100                 return 1;
102         for (i = 0; i < ARRAY_SIZE(unsupported_options); i++) {
103                 if (!strcmp(name, unsupported_options[i]))
104                         return 1;
105         }
107         for (i = 0; i < ARRAY_SIZE(boolean_options); i++) {
108                 if (!strcmp(name, boolean_options[i])) {
109                         is_bool = 1;
110                         break;
111                 }
112         }
114         strbuf_addf(&buf, "option %s ", name);
115         if (is_bool)
116                 strbuf_addstr(&buf, value ? "true" : "false");
117         else
118                 quote_c_style(value, &buf, NULL, 0);
119         strbuf_addch(&buf, '\n');
121         if (write_in_full(helper->in, buf.buf, buf.len) != buf.len)
122                 die_errno("cannot send option to %s", data->name);
124         strbuf_reset(&buf);
125         if (strbuf_getline(&buf, data->out, '\n') == EOF)
126                 exit(128); /* child died, message supplied already */
128         if (!strcmp(buf.buf, "ok"))
129                 ret = 0;
130         else if (!prefixcmp(buf.buf, "error")) {
131                 ret = -1;
132         } else if (!strcmp(buf.buf, "unsupported"))
133                 ret = 1;
134         else {
135                 warning("%s unexpectedly said: '%s'", data->name, buf.buf);
136                 ret = 1;
137         }
138         strbuf_release(&buf);
139         return ret;
142 static void standard_options(struct transport *t)
144         char buf[16];
145         int n;
146         int v = t->verbose;
147         int no_progress = v < 0 || (!t->progress && !isatty(1));
149         set_helper_option(t, "progress", !no_progress ? "true" : "false");
151         n = snprintf(buf, sizeof(buf), "%d", v + 1);
152         if (n >= sizeof(buf))
153                 die("impossibly large verbosity value");
154         set_helper_option(t, "verbosity", buf);
157 static int fetch_with_fetch(struct transport *transport,
158                             int nr_heads, const struct ref **to_fetch)
160         struct helper_data *data = transport->data;
161         int i;
162         struct strbuf buf = STRBUF_INIT;
164         standard_options(transport);
166         for (i = 0; i < nr_heads; i++) {
167                 const struct ref *posn = to_fetch[i];
168                 if (posn->status & REF_STATUS_UPTODATE)
169                         continue;
171                 strbuf_addf(&buf, "fetch %s %s\n",
172                             sha1_to_hex(posn->old_sha1), posn->name);
173         }
175         strbuf_addch(&buf, '\n');
176         if (write_in_full(data->helper->in, buf.buf, buf.len) != buf.len)
177                 die_errno("cannot send fetch to %s", data->name);
179         while (1) {
180                 strbuf_reset(&buf);
181                 if (strbuf_getline(&buf, data->out, '\n') == EOF)
182                         exit(128); /* child died, message supplied already */
184                 if (!prefixcmp(buf.buf, "lock ")) {
185                         const char *name = buf.buf + 5;
186                         if (transport->pack_lockfile)
187                                 warning("%s also locked %s", data->name, name);
188                         else
189                                 transport->pack_lockfile = xstrdup(name);
190                 }
191                 else if (!buf.len)
192                         break;
193                 else
194                         warning("%s unexpectedly said: '%s'", data->name, buf.buf);
195         }
196         strbuf_release(&buf);
197         return 0;
200 static int fetch(struct transport *transport,
201                  int nr_heads, const struct ref **to_fetch)
203         struct helper_data *data = transport->data;
204         int i, count;
206         count = 0;
207         for (i = 0; i < nr_heads; i++)
208                 if (!(to_fetch[i]->status & REF_STATUS_UPTODATE))
209                         count++;
211         if (!count)
212                 return 0;
214         if (data->fetch)
215                 return fetch_with_fetch(transport, nr_heads, to_fetch);
217         return -1;
220 static int push_refs(struct transport *transport,
221                 struct ref *remote_refs, int flags)
223         int force_all = flags & TRANSPORT_PUSH_FORCE;
224         int mirror = flags & TRANSPORT_PUSH_MIRROR;
225         struct helper_data *data = transport->data;
226         struct strbuf buf = STRBUF_INIT;
227         struct child_process *helper;
228         struct ref *ref;
230         if (!remote_refs)
231                 return 0;
233         helper = get_helper(transport);
234         if (!data->push)
235                 return 1;
237         for (ref = remote_refs; ref; ref = ref->next) {
238                 if (ref->peer_ref)
239                         hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
240                 else if (!mirror)
241                         continue;
243                 ref->deletion = is_null_sha1(ref->new_sha1);
244                 if (!ref->deletion &&
245                         !hashcmp(ref->old_sha1, ref->new_sha1)) {
246                         ref->status = REF_STATUS_UPTODATE;
247                         continue;
248                 }
250                 if (force_all)
251                         ref->force = 1;
253                 strbuf_addstr(&buf, "push ");
254                 if (!ref->deletion) {
255                         if (ref->force)
256                                 strbuf_addch(&buf, '+');
257                         if (ref->peer_ref)
258                                 strbuf_addstr(&buf, ref->peer_ref->name);
259                         else
260                                 strbuf_addstr(&buf, sha1_to_hex(ref->new_sha1));
261                 }
262                 strbuf_addch(&buf, ':');
263                 strbuf_addstr(&buf, ref->name);
264                 strbuf_addch(&buf, '\n');
265         }
266         if (buf.len == 0)
267                 return 0;
269         transport->verbose = flags & TRANSPORT_PUSH_VERBOSE ? 1 : 0;
270         standard_options(transport);
272         if (flags & TRANSPORT_PUSH_DRY_RUN) {
273                 if (set_helper_option(transport, "dry-run", "true") != 0)
274                         die("helper %s does not support dry-run", data->name);
275         }
277         strbuf_addch(&buf, '\n');
278         if (write_in_full(helper->in, buf.buf, buf.len) != buf.len)
279                 exit(128);
281         ref = remote_refs;
282         while (1) {
283                 char *refname, *msg;
284                 int status;
286                 strbuf_reset(&buf);
287                 if (strbuf_getline(&buf, data->out, '\n') == EOF)
288                         exit(128); /* child died, message supplied already */
289                 if (!buf.len)
290                         break;
292                 if (!prefixcmp(buf.buf, "ok ")) {
293                         status = REF_STATUS_OK;
294                         refname = buf.buf + 3;
295                 } else if (!prefixcmp(buf.buf, "error ")) {
296                         status = REF_STATUS_REMOTE_REJECT;
297                         refname = buf.buf + 6;
298                 } else
299                         die("expected ok/error, helper said '%s'\n", buf.buf);
301                 msg = strchr(refname, ' ');
302                 if (msg) {
303                         struct strbuf msg_buf = STRBUF_INIT;
304                         const char *end;
306                         *msg++ = '\0';
307                         if (!unquote_c_style(&msg_buf, msg, &end))
308                                 msg = strbuf_detach(&msg_buf, NULL);
309                         else
310                                 msg = xstrdup(msg);
311                         strbuf_release(&msg_buf);
313                         if (!strcmp(msg, "no match")) {
314                                 status = REF_STATUS_NONE;
315                                 free(msg);
316                                 msg = NULL;
317                         }
318                         else if (!strcmp(msg, "up to date")) {
319                                 status = REF_STATUS_UPTODATE;
320                                 free(msg);
321                                 msg = NULL;
322                         }
323                         else if (!strcmp(msg, "non-fast forward")) {
324                                 status = REF_STATUS_REJECT_NONFASTFORWARD;
325                                 free(msg);
326                                 msg = NULL;
327                         }
328                 }
330                 if (ref)
331                         ref = find_ref_by_name(ref, refname);
332                 if (!ref)
333                         ref = find_ref_by_name(remote_refs, refname);
334                 if (!ref) {
335                         warning("helper reported unexpected status of %s", refname);
336                         continue;
337                 }
339                 ref->status = status;
340                 ref->remote_status = msg;
341         }
342         strbuf_release(&buf);
343         return 0;
346 static struct ref *get_refs_list(struct transport *transport, int for_push)
348         struct helper_data *data = transport->data;
349         struct child_process *helper;
350         struct ref *ret = NULL;
351         struct ref **tail = &ret;
352         struct ref *posn;
353         struct strbuf buf = STRBUF_INIT;
355         helper = get_helper(transport);
357         if (data->push && for_push)
358                 write_str_in_full(helper->in, "list for-push\n");
359         else
360                 write_str_in_full(helper->in, "list\n");
362         while (1) {
363                 char *eov, *eon;
364                 if (strbuf_getline(&buf, data->out, '\n') == EOF)
365                         exit(128); /* child died, message supplied already */
367                 if (!*buf.buf)
368                         break;
370                 eov = strchr(buf.buf, ' ');
371                 if (!eov)
372                         die("Malformed response in ref list: %s", buf.buf);
373                 eon = strchr(eov + 1, ' ');
374                 *eov = '\0';
375                 if (eon)
376                         *eon = '\0';
377                 *tail = alloc_ref(eov + 1);
378                 if (buf.buf[0] == '@')
379                         (*tail)->symref = xstrdup(buf.buf + 1);
380                 else if (buf.buf[0] != '?')
381                         get_sha1_hex(buf.buf, (*tail)->old_sha1);
382                 tail = &((*tail)->next);
383         }
384         strbuf_release(&buf);
386         for (posn = ret; posn; posn = posn->next)
387                 resolve_remote_symref(posn, ret);
389         return ret;
392 int transport_helper_init(struct transport *transport, const char *name)
394         struct helper_data *data = xcalloc(sizeof(*data), 1);
395         data->name = name;
397         transport->data = data;
398         transport->set_option = set_helper_option;
399         transport->get_refs_list = get_refs_list;
400         transport->fetch = fetch;
401         transport->push_refs = push_refs;
402         transport->disconnect = disconnect_helper;
403         return 0;