Add Python support library for remote helpers
[git/dscho.git] / transport-helper.c
blobc87530e87d0891a9e14c8fcddd57051b0fe1bcde
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 "remote.h"
10 struct helper_data
12 const char *name;
13 struct child_process *helper;
14 unsigned fetch : 1;
15 unsigned import : 1;
16 /* These go from remote name (as in "list") to private name */
17 struct refspec *refspecs;
18 int refspec_nr;
21 static struct child_process *get_helper(struct transport *transport)
23 struct helper_data *data = transport->data;
24 struct strbuf buf = STRBUF_INIT;
25 struct child_process *helper;
26 FILE *file;
27 const char **refspecs = NULL;
28 int refspec_nr = 0;
29 int refspec_alloc = 0;
31 if (data->helper)
32 return data->helper;
34 helper = xcalloc(1, sizeof(*helper));
35 helper->in = -1;
36 helper->out = -1;
37 helper->err = 0;
38 helper->argv = xcalloc(4, sizeof(*helper->argv));
39 strbuf_addf(&buf, "remote-%s", data->name);
40 helper->argv[0] = strbuf_detach(&buf, NULL);
41 helper->argv[1] = transport->remote->name;
42 helper->argv[2] = transport->url;
43 helper->git_cmd = 1;
44 if (start_command(helper))
45 die("Unable to run helper: git %s", helper->argv[0]);
46 data->helper = helper;
48 write_str_in_full(helper->in, "capabilities\n");
50 file = xfdopen(helper->out, "r");
51 while (1) {
52 if (strbuf_getline(&buf, file, '\n') == EOF)
53 exit(128); /* child died, message supplied already */
55 if (!*buf.buf)
56 break;
57 if (!strcmp(buf.buf, "fetch"))
58 data->fetch = 1;
59 if (!strcmp(buf.buf, "import"))
60 data->import = 1;
61 if (!data->refspecs && !prefixcmp(buf.buf, "refspec ")) {
62 ALLOC_GROW(refspecs,
63 refspec_nr + 1,
64 refspec_alloc);
65 refspecs[refspec_nr++] = strdup(buf.buf + strlen("refspec "));
68 if (refspecs) {
69 int i;
70 data->refspec_nr = refspec_nr;
71 data->refspecs = parse_fetch_refspec(refspec_nr, refspecs);
72 for (i = 0; i < refspec_nr; i++) {
73 free((char *)refspecs[i]);
75 free(refspecs);
77 strbuf_release(&buf);
78 return data->helper;
81 static int disconnect_helper(struct transport *transport)
83 struct helper_data *data = transport->data;
84 if (data->helper) {
85 write_str_in_full(data->helper->in, "\n");
86 close(data->helper->in);
87 finish_command(data->helper);
88 free((char *)data->helper->argv[0]);
89 free(data->helper->argv);
90 free(data->helper);
91 data->helper = NULL;
93 return 0;
96 static int release_helper(struct transport *transport)
98 struct helper_data *data = transport->data;
99 free_refspec(data->refspec_nr, data->refspecs);
100 data->refspecs = NULL;
101 disconnect_helper(transport);
102 free(transport->data);
103 return 0;
106 static int fetch_with_fetch(struct transport *transport,
107 int nr_heads, struct ref **to_fetch)
109 struct child_process *helper = get_helper(transport);
110 FILE *file = xfdopen(helper->out, "r");
111 int i;
112 struct strbuf buf = STRBUF_INIT;
114 for (i = 0; i < nr_heads; i++) {
115 const struct ref *posn = to_fetch[i];
116 if (posn->status & REF_STATUS_UPTODATE)
117 continue;
119 strbuf_addf(&buf, "fetch %s %s\n",
120 sha1_to_hex(posn->old_sha1), posn->name);
121 write_in_full(helper->in, buf.buf, buf.len);
122 strbuf_reset(&buf);
124 if (strbuf_getline(&buf, file, '\n') == EOF)
125 exit(128); /* child died, message supplied already */
127 return 0;
130 static int get_importer(struct transport *transport, struct child_process *fastimport)
132 struct child_process *helper = get_helper(transport);
133 memset(fastimport, 0, sizeof(*fastimport));
134 fastimport->in = helper->out;
135 fastimport->argv = xcalloc(5, sizeof(*fastimport->argv));
136 fastimport->argv[0] = "fast-import";
137 fastimport->argv[1] = "--quiet";
139 fastimport->git_cmd = 1;
140 return start_command(fastimport);
143 static int fetch_with_import(struct transport *transport,
144 int nr_heads, struct ref **to_fetch)
146 struct child_process fastimport;
147 struct child_process *helper = get_helper(transport);
148 struct helper_data *data = transport->data;
149 int i;
150 struct ref *posn;
151 struct strbuf buf = STRBUF_INIT;
153 if (get_importer(transport, &fastimport))
154 die("Couldn't run fast-import");
156 for (i = 0; i < nr_heads; i++) {
157 posn = to_fetch[i];
158 if (posn->status & REF_STATUS_UPTODATE)
159 continue;
161 strbuf_addf(&buf, "import %s\n", posn->name);
162 write_in_full(helper->in, buf.buf, buf.len);
163 strbuf_reset(&buf);
165 disconnect_helper(transport);
166 finish_command(&fastimport);
167 free(fastimport.argv);
168 fastimport.argv = NULL;
170 for (i = 0; i < nr_heads; i++) {
171 char *private;
172 posn = to_fetch[i];
173 if (posn->status & REF_STATUS_UPTODATE)
174 continue;
175 if (data->refspecs)
176 private = apply_refspecs(data->refspecs, data->refspec_nr, posn->name);
177 else
178 private = strdup(posn->name);
179 read_ref(private, posn->old_sha1);
180 free(private);
182 strbuf_release(&buf);
183 return 0;
186 static int fetch(struct transport *transport,
187 int nr_heads, struct ref **to_fetch)
189 struct helper_data *data = transport->data;
190 int i, count;
192 count = 0;
193 for (i = 0; i < nr_heads; i++)
194 if (!(to_fetch[i]->status & REF_STATUS_UPTODATE))
195 count++;
197 if (!count)
198 return 0;
200 if (data->fetch)
201 return fetch_with_fetch(transport, nr_heads, to_fetch);
203 if (data->import)
204 return fetch_with_import(transport, nr_heads, to_fetch);
206 return -1;
209 static int has_attribute(const char *attrs, const char *attr) {
210 int len;
211 if (!attrs)
212 return 0;
214 len = strlen(attr);
215 for (;;) {
216 const char *space = strchrnul(attrs, ' ');
217 if (len == space - attrs && !strncmp(attrs, attr, len))
218 return 1;
219 if (!*space)
220 return 0;
221 attrs = space + 1;
225 static struct ref *get_refs_list(struct transport *transport, int for_push)
227 struct child_process *helper;
228 struct ref *ret = NULL;
229 struct ref **tail = &ret;
230 struct ref *posn;
231 struct strbuf buf = STRBUF_INIT;
232 FILE *file;
234 helper = get_helper(transport);
236 write_str_in_full(helper->in, "list\n");
238 file = xfdopen(helper->out, "r");
239 while (1) {
240 char *eov, *eon;
241 if (strbuf_getline(&buf, file, '\n') == EOF)
242 exit(128); /* child died, message supplied already */
244 if (!*buf.buf)
245 break;
247 eov = strchr(buf.buf, ' ');
248 if (!eov)
249 die("Malformed response in ref list: %s", buf.buf);
250 eon = strchr(eov + 1, ' ');
251 *eov = '\0';
252 if (eon)
253 *eon = '\0';
254 *tail = alloc_ref(eov + 1);
255 if (buf.buf[0] == '@')
256 (*tail)->symref = xstrdup(buf.buf + 1);
257 else if (buf.buf[0] != '?')
258 get_sha1_hex(buf.buf, (*tail)->old_sha1);
259 if (eon) {
260 if (has_attribute(eon + 1, "unchanged")) {
261 (*tail)->status |= REF_STATUS_UPTODATE;
262 read_ref((*tail)->name, (*tail)->old_sha1);
265 tail = &((*tail)->next);
267 strbuf_release(&buf);
269 for (posn = ret; posn; posn = posn->next)
270 resolve_remote_symref(posn, ret);
272 return ret;
275 int transport_helper_init(struct transport *transport, const char *name)
277 struct helper_data *data = xcalloc(sizeof(*data), 1);
278 data->name = name;
280 transport->data = data;
281 transport->get_refs_list = get_refs_list;
282 transport->fetch = fetch;
283 transport->disconnect = release_helper;
284 return 0;