577abc638eb47417277a45a6bd568e24d48e928b
1#include "cache.h"
2#include "transport.h"
3
4#include "run-command.h"
5#include "commit.h"
6#include "diff.h"
7#include "revision.h"
8#include "quote.h"
9
10struct helper_data
11{
12 const char *name;
13 struct child_process *helper;
14 FILE *out;
15 unsigned fetch : 1,
16 option : 1;
17};
18
19static 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;
24
25 if (data->helper)
26 return data->helper;
27
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;
41
42 write_str_in_full(helper->in, "capabilities\n");
43
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 */
48
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}
58
59static 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}
75
76static const char *unsupported_options[] = {
77 TRANS_OPT_UPLOADPACK,
78 TRANS_OPT_RECEIVEPACK,
79 TRANS_OPT_THIN,
80 TRANS_OPT_KEEP
81 };
82static const char *boolean_options[] = {
83 TRANS_OPT_THIN,
84 TRANS_OPT_KEEP,
85 TRANS_OPT_FOLLOWTAGS
86 };
87
88static 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;
95
96 if (!data->option)
97 return 1;
98
99 for (i = 0; i < ARRAY_SIZE(unsupported_options); i++) {
100 if (!strcmp(name, unsupported_options[i]))
101 return 1;
102 }
103
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 }
110
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');
117
118 if (write_in_full(helper->in, buf.buf, buf.len) != buf.len)
119 die_errno("cannot send option to %s", data->name);
120
121 strbuf_reset(&buf);
122 if (strbuf_getline(&buf, data->out, '\n') == EOF)
123 exit(128); /* child died, message supplied already */
124
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;
137}
138
139static void standard_options(struct transport *t)
140{
141 char buf[16];
142 int n;
143 int v = t->verbose;
144 int no_progress = v < 0 || (!t->progress && !isatty(1));
145
146 set_helper_option(t, "progress", !no_progress ? "true" : "false");
147
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);
152}
153
154static int fetch_with_fetch(struct transport *transport,
155 int nr_heads, const struct ref **to_fetch)
156{
157 struct helper_data *data = transport->data;
158 int i;
159 struct strbuf buf = STRBUF_INIT;
160
161 standard_options(transport);
162
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;
167
168 strbuf_addf(&buf, "fetch %s %s\n",
169 sha1_to_hex(posn->old_sha1), posn->name);
170 }
171
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);
175
176 while (1) {
177 strbuf_reset(&buf);
178 if (strbuf_getline(&buf, data->out, '\n') == EOF)
179 exit(128); /* child died, message supplied already */
180
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;
195}
196
197static int fetch(struct transport *transport,
198 int nr_heads, const struct ref **to_fetch)
199{
200 struct helper_data *data = transport->data;
201 int i, count;
202
203 count = 0;
204 for (i = 0; i < nr_heads; i++)
205 if (!(to_fetch[i]->status & REF_STATUS_UPTODATE))
206 count++;
207
208 if (!count)
209 return 0;
210
211 if (data->fetch)
212 return fetch_with_fetch(transport, nr_heads, to_fetch);
213
214 return -1;
215}
216
217static struct ref *get_refs_list(struct transport *transport, int for_push)
218{
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;
225
226 helper = get_helper(transport);
227
228 write_str_in_full(helper->in, "list\n");
229
230 while (1) {
231 char *eov, *eon;
232 if (strbuf_getline(&buf, data->out, '\n') == EOF)
233 exit(128); /* child died, message supplied already */
234
235 if (!*buf.buf)
236 break;
237
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);
253
254 for (posn = ret; posn; posn = posn->next)
255 resolve_remote_symref(posn, ret);
256
257 return ret;
258}
259
260int transport_helper_init(struct transport *transport, const char *name)
261{
262 struct helper_data *data = xcalloc(sizeof(*data), 1);
263 data->name = name;
264
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;
271}