Code

a721dc25beb31350f6fc64f7446b340f83c707c8
[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"
9 #include "remote.h"
11 static int debug;
13 struct helper_data
14 {
15         const char *name;
16         struct child_process *helper;
17         FILE *out;
18         unsigned fetch : 1,
19                 import : 1,
20                 option : 1,
21                 push : 1;
22         /* These go from remote name (as in "list") to private name */
23         struct refspec *refspecs;
24         int refspec_nr;
25 };
27 static void sendline(struct helper_data *helper, struct strbuf *buffer)
28 {
29         if (debug)
30                 fprintf(stderr, "Debug: Remote helper: -> %s", buffer->buf);
31         if (write_in_full(helper->helper->in, buffer->buf, buffer->len)
32                 != buffer->len)
33                 die_errno("Full write to remote helper failed");
34 }
36 static int recvline(struct helper_data *helper, struct strbuf *buffer)
37 {
38         strbuf_reset(buffer);
39         if (debug)
40                 fprintf(stderr, "Debug: Remote helper: Waiting...\n");
41         if (strbuf_getline(buffer, helper->out, '\n') == EOF) {
42                 if (debug)
43                         fprintf(stderr, "Debug: Remote helper quit.\n");
44                 exit(128);
45         }
47         if (debug)
48                 fprintf(stderr, "Debug: Remote helper: <- %s\n", buffer->buf);
49         return 0;
50 }
52 static void xchgline(struct helper_data *helper, struct strbuf *buffer)
53 {
54         sendline(helper, buffer);
55         recvline(helper, buffer);
56 }
58 static void write_constant(int fd, const char *str)
59 {
60         if (debug)
61                 fprintf(stderr, "Debug: Remote helper: -> %s", str);
62         if (write_in_full(fd, str, strlen(str)) != strlen(str))
63                 die_errno("Full write to remote helper failed");
64 }
66 static struct child_process *get_helper(struct transport *transport)
67 {
68         struct helper_data *data = transport->data;
69         struct strbuf buf = STRBUF_INIT;
70         struct child_process *helper;
71         const char **refspecs = NULL;
72         int refspec_nr = 0;
73         int refspec_alloc = 0;
75         if (data->helper)
76                 return data->helper;
78         helper = xcalloc(1, sizeof(*helper));
79         helper->in = -1;
80         helper->out = -1;
81         helper->err = 0;
82         helper->argv = xcalloc(4, sizeof(*helper->argv));
83         strbuf_addf(&buf, "remote-%s", data->name);
84         helper->argv[0] = strbuf_detach(&buf, NULL);
85         helper->argv[1] = transport->remote->name;
86         helper->argv[2] = transport->url;
87         helper->git_cmd = 1;
88         if (start_command(helper))
89                 die("Unable to run helper: git %s", helper->argv[0]);
90         data->helper = helper;
92         write_constant(helper->in, "capabilities\n");
94         data->out = xfdopen(helper->out, "r");
95         while (1) {
96                 recvline(data, &buf);
98                 if (!*buf.buf)
99                         break;
100                 if (debug)
101                         fprintf(stderr, "Debug: Got cap %s\n", buf.buf);
102                 if (!strcmp(buf.buf, "fetch"))
103                         data->fetch = 1;
104                 if (!strcmp(buf.buf, "option"))
105                         data->option = 1;
106                 if (!strcmp(buf.buf, "push"))
107                         data->push = 1;
108                 if (!strcmp(buf.buf, "import"))
109                         data->import = 1;
110                 if (!data->refspecs && !prefixcmp(buf.buf, "refspec ")) {
111                         ALLOC_GROW(refspecs,
112                                    refspec_nr + 1,
113                                    refspec_alloc);
114                         refspecs[refspec_nr++] = strdup(buf.buf + strlen("refspec "));
115                 }
116         }
117         if (refspecs) {
118                 int i;
119                 data->refspec_nr = refspec_nr;
120                 data->refspecs = parse_fetch_refspec(refspec_nr, refspecs);
121                 for (i = 0; i < refspec_nr; i++) {
122                         free((char *)refspecs[i]);
123                 }
124                 free(refspecs);
125         }
126         strbuf_release(&buf);
127         if (debug)
128                 fprintf(stderr, "Debug: Capabilities complete.\n");
129         return data->helper;
132 static int disconnect_helper(struct transport *transport)
134         struct helper_data *data = transport->data;
135         struct strbuf buf = STRBUF_INIT;
137         if (data->helper) {
138                 if (debug)
139                         fprintf(stderr, "Debug: Disconnecting.\n");
140                 strbuf_addf(&buf, "\n");
141                 sendline(data, &buf);
142                 close(data->helper->in);
143                 fclose(data->out);
144                 finish_command(data->helper);
145                 free((char *)data->helper->argv[0]);
146                 free(data->helper->argv);
147                 free(data->helper);
148                 data->helper = NULL;
149         }
150         return 0;
153 static const char *unsupported_options[] = {
154         TRANS_OPT_UPLOADPACK,
155         TRANS_OPT_RECEIVEPACK,
156         TRANS_OPT_THIN,
157         TRANS_OPT_KEEP
158         };
159 static const char *boolean_options[] = {
160         TRANS_OPT_THIN,
161         TRANS_OPT_KEEP,
162         TRANS_OPT_FOLLOWTAGS
163         };
165 static int set_helper_option(struct transport *transport,
166                           const char *name, const char *value)
168         struct helper_data *data = transport->data;
169         struct strbuf buf = STRBUF_INIT;
170         int i, ret, is_bool = 0;
172         get_helper(transport);
174         if (!data->option)
175                 return 1;
177         for (i = 0; i < ARRAY_SIZE(unsupported_options); i++) {
178                 if (!strcmp(name, unsupported_options[i]))
179                         return 1;
180         }
182         for (i = 0; i < ARRAY_SIZE(boolean_options); i++) {
183                 if (!strcmp(name, boolean_options[i])) {
184                         is_bool = 1;
185                         break;
186                 }
187         }
189         strbuf_addf(&buf, "option %s ", name);
190         if (is_bool)
191                 strbuf_addstr(&buf, value ? "true" : "false");
192         else
193                 quote_c_style(value, &buf, NULL, 0);
194         strbuf_addch(&buf, '\n');
196         xchgline(data, &buf);
198         if (!strcmp(buf.buf, "ok"))
199                 ret = 0;
200         else if (!prefixcmp(buf.buf, "error")) {
201                 ret = -1;
202         } else if (!strcmp(buf.buf, "unsupported"))
203                 ret = 1;
204         else {
205                 warning("%s unexpectedly said: '%s'", data->name, buf.buf);
206                 ret = 1;
207         }
208         strbuf_release(&buf);
209         return ret;
212 static void standard_options(struct transport *t)
214         char buf[16];
215         int n;
216         int v = t->verbose;
217         int no_progress = v < 0 || (!t->progress && !isatty(1));
219         set_helper_option(t, "progress", !no_progress ? "true" : "false");
221         n = snprintf(buf, sizeof(buf), "%d", v + 1);
222         if (n >= sizeof(buf))
223                 die("impossibly large verbosity value");
224         set_helper_option(t, "verbosity", buf);
227 static int release_helper(struct transport *transport)
229         struct helper_data *data = transport->data;
230         free_refspec(data->refspec_nr, data->refspecs);
231         data->refspecs = NULL;
232         disconnect_helper(transport);
233         free(transport->data);
234         return 0;
237 static int fetch_with_fetch(struct transport *transport,
238                             int nr_heads, struct ref **to_fetch)
240         struct helper_data *data = transport->data;
241         int i;
242         struct strbuf buf = STRBUF_INIT;
244         standard_options(transport);
246         for (i = 0; i < nr_heads; i++) {
247                 const struct ref *posn = to_fetch[i];
248                 if (posn->status & REF_STATUS_UPTODATE)
249                         continue;
251                 strbuf_addf(&buf, "fetch %s %s\n",
252                             sha1_to_hex(posn->old_sha1), posn->name);
253         }
255         strbuf_addch(&buf, '\n');
256         sendline(data, &buf);
258         while (1) {
259                 recvline(data, &buf);
261                 if (!prefixcmp(buf.buf, "lock ")) {
262                         const char *name = buf.buf + 5;
263                         if (transport->pack_lockfile)
264                                 warning("%s also locked %s", data->name, name);
265                         else
266                                 transport->pack_lockfile = xstrdup(name);
267                 }
268                 else if (!buf.len)
269                         break;
270                 else
271                         warning("%s unexpectedly said: '%s'", data->name, buf.buf);
272         }
273         strbuf_release(&buf);
274         return 0;
277 static int get_importer(struct transport *transport, struct child_process *fastimport)
279         struct child_process *helper = get_helper(transport);
280         memset(fastimport, 0, sizeof(*fastimport));
281         fastimport->in = helper->out;
282         fastimport->argv = xcalloc(5, sizeof(*fastimport->argv));
283         fastimport->argv[0] = "fast-import";
284         fastimport->argv[1] = "--quiet";
286         fastimport->git_cmd = 1;
287         return start_command(fastimport);
290 static int fetch_with_import(struct transport *transport,
291                              int nr_heads, struct ref **to_fetch)
293         struct child_process fastimport;
294         struct helper_data *data = transport->data;
295         int i;
296         struct ref *posn;
297         struct strbuf buf = STRBUF_INIT;
299         get_helper(transport);
301         if (get_importer(transport, &fastimport))
302                 die("Couldn't run fast-import");
304         for (i = 0; i < nr_heads; i++) {
305                 posn = to_fetch[i];
306                 if (posn->status & REF_STATUS_UPTODATE)
307                         continue;
309                 strbuf_addf(&buf, "import %s\n", posn->name);
310                 sendline(data, &buf);
311                 strbuf_reset(&buf);
312         }
313         disconnect_helper(transport);
314         finish_command(&fastimport);
315         free(fastimport.argv);
316         fastimport.argv = NULL;
318         for (i = 0; i < nr_heads; i++) {
319                 char *private;
320                 posn = to_fetch[i];
321                 if (posn->status & REF_STATUS_UPTODATE)
322                         continue;
323                 if (data->refspecs)
324                         private = apply_refspecs(data->refspecs, data->refspec_nr, posn->name);
325                 else
326                         private = strdup(posn->name);
327                 read_ref(private, posn->old_sha1);
328                 free(private);
329         }
330         strbuf_release(&buf);
331         return 0;
334 static int fetch(struct transport *transport,
335                  int nr_heads, struct ref **to_fetch)
337         struct helper_data *data = transport->data;
338         int i, count;
340         count = 0;
341         for (i = 0; i < nr_heads; i++)
342                 if (!(to_fetch[i]->status & REF_STATUS_UPTODATE))
343                         count++;
345         if (!count)
346                 return 0;
348         if (data->fetch)
349                 return fetch_with_fetch(transport, nr_heads, to_fetch);
351         if (data->import)
352                 return fetch_with_import(transport, nr_heads, to_fetch);
354         return -1;
357 static int push_refs(struct transport *transport,
358                 struct ref *remote_refs, int flags)
360         int force_all = flags & TRANSPORT_PUSH_FORCE;
361         int mirror = flags & TRANSPORT_PUSH_MIRROR;
362         struct helper_data *data = transport->data;
363         struct strbuf buf = STRBUF_INIT;
364         struct child_process *helper;
365         struct ref *ref;
367         if (!remote_refs)
368                 return 0;
370         helper = get_helper(transport);
371         if (!data->push)
372                 return 1;
374         for (ref = remote_refs; ref; ref = ref->next) {
375                 if (ref->peer_ref)
376                         hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
377                 else if (!mirror)
378                         continue;
380                 ref->deletion = is_null_sha1(ref->new_sha1);
381                 if (!ref->deletion &&
382                         !hashcmp(ref->old_sha1, ref->new_sha1)) {
383                         ref->status = REF_STATUS_UPTODATE;
384                         continue;
385                 }
387                 if (force_all)
388                         ref->force = 1;
390                 strbuf_addstr(&buf, "push ");
391                 if (!ref->deletion) {
392                         if (ref->force)
393                                 strbuf_addch(&buf, '+');
394                         if (ref->peer_ref)
395                                 strbuf_addstr(&buf, ref->peer_ref->name);
396                         else
397                                 strbuf_addstr(&buf, sha1_to_hex(ref->new_sha1));
398                 }
399                 strbuf_addch(&buf, ':');
400                 strbuf_addstr(&buf, ref->name);
401                 strbuf_addch(&buf, '\n');
402         }
403         if (buf.len == 0)
404                 return 0;
406         transport->verbose = flags & TRANSPORT_PUSH_VERBOSE ? 1 : 0;
407         standard_options(transport);
409         if (flags & TRANSPORT_PUSH_DRY_RUN) {
410                 if (set_helper_option(transport, "dry-run", "true") != 0)
411                         die("helper %s does not support dry-run", data->name);
412         }
414         strbuf_addch(&buf, '\n');
415         sendline(data, &buf);
417         ref = remote_refs;
418         while (1) {
419                 char *refname, *msg;
420                 int status;
422                 recvline(data, &buf);
423                 if (!buf.len)
424                         break;
426                 if (!prefixcmp(buf.buf, "ok ")) {
427                         status = REF_STATUS_OK;
428                         refname = buf.buf + 3;
429                 } else if (!prefixcmp(buf.buf, "error ")) {
430                         status = REF_STATUS_REMOTE_REJECT;
431                         refname = buf.buf + 6;
432                 } else
433                         die("expected ok/error, helper said '%s'\n", buf.buf);
435                 msg = strchr(refname, ' ');
436                 if (msg) {
437                         struct strbuf msg_buf = STRBUF_INIT;
438                         const char *end;
440                         *msg++ = '\0';
441                         if (!unquote_c_style(&msg_buf, msg, &end))
442                                 msg = strbuf_detach(&msg_buf, NULL);
443                         else
444                                 msg = xstrdup(msg);
445                         strbuf_release(&msg_buf);
447                         if (!strcmp(msg, "no match")) {
448                                 status = REF_STATUS_NONE;
449                                 free(msg);
450                                 msg = NULL;
451                         }
452                         else if (!strcmp(msg, "up to date")) {
453                                 status = REF_STATUS_UPTODATE;
454                                 free(msg);
455                                 msg = NULL;
456                         }
457                         else if (!strcmp(msg, "non-fast forward")) {
458                                 status = REF_STATUS_REJECT_NONFASTFORWARD;
459                                 free(msg);
460                                 msg = NULL;
461                         }
462                 }
464                 if (ref)
465                         ref = find_ref_by_name(ref, refname);
466                 if (!ref)
467                         ref = find_ref_by_name(remote_refs, refname);
468                 if (!ref) {
469                         warning("helper reported unexpected status of %s", refname);
470                         continue;
471                 }
473                 ref->status = status;
474                 ref->remote_status = msg;
475         }
476         strbuf_release(&buf);
477         return 0;
480 static int has_attribute(const char *attrs, const char *attr) {
481         int len;
482         if (!attrs)
483                 return 0;
485         len = strlen(attr);
486         for (;;) {
487                 const char *space = strchrnul(attrs, ' ');
488                 if (len == space - attrs && !strncmp(attrs, attr, len))
489                         return 1;
490                 if (!*space)
491                         return 0;
492                 attrs = space + 1;
493         }
496 static struct ref *get_refs_list(struct transport *transport, int for_push)
498         struct helper_data *data = transport->data;
499         struct child_process *helper;
500         struct ref *ret = NULL;
501         struct ref **tail = &ret;
502         struct ref *posn;
503         struct strbuf buf = STRBUF_INIT;
505         helper = get_helper(transport);
507         if (data->push && for_push)
508                 write_str_in_full(helper->in, "list for-push\n");
509         else
510                 write_str_in_full(helper->in, "list\n");
512         while (1) {
513                 char *eov, *eon;
514                 recvline(data, &buf);
516                 if (!*buf.buf)
517                         break;
519                 eov = strchr(buf.buf, ' ');
520                 if (!eov)
521                         die("Malformed response in ref list: %s", buf.buf);
522                 eon = strchr(eov + 1, ' ');
523                 *eov = '\0';
524                 if (eon)
525                         *eon = '\0';
526                 *tail = alloc_ref(eov + 1);
527                 if (buf.buf[0] == '@')
528                         (*tail)->symref = xstrdup(buf.buf + 1);
529                 else if (buf.buf[0] != '?')
530                         get_sha1_hex(buf.buf, (*tail)->old_sha1);
531                 if (eon) {
532                         if (has_attribute(eon + 1, "unchanged")) {
533                                 (*tail)->status |= REF_STATUS_UPTODATE;
534                                 read_ref((*tail)->name, (*tail)->old_sha1);
535                         }
536                 }
537                 tail = &((*tail)->next);
538         }
539         if (debug)
540                 fprintf(stderr, "Debug: Read ref listing.\n");
541         strbuf_release(&buf);
543         for (posn = ret; posn; posn = posn->next)
544                 resolve_remote_symref(posn, ret);
546         return ret;
549 int transport_helper_init(struct transport *transport, const char *name)
551         struct helper_data *data = xcalloc(sizeof(*data), 1);
552         data->name = name;
554         if (getenv("GIT_TRANSPORT_HELPER_DEBUG"))
555                 debug = 1;
557         transport->data = data;
558         transport->set_option = set_helper_option;
559         transport->get_refs_list = get_refs_list;
560         transport->fetch = fetch;
561         transport->push_refs = push_refs;
562         transport->disconnect = release_helper;
563         return 0;