Add support for "import" helper command
[git/git.git] / transport-helper.c
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
9 struct helper_data
10 {
11 const char *name;
12 struct child_process *helper;
13 unsigned fetch : 1;
14 unsigned import : 1;
15 };
16
17 static struct child_process *get_helper(struct transport *transport)
18 {
19 struct helper_data *data = transport->data;
20 struct strbuf buf = STRBUF_INIT;
21 struct child_process *helper;
22 FILE *file;
23
24 if (data->helper)
25 return data->helper;
26
27 helper = xcalloc(1, sizeof(*helper));
28 helper->in = -1;
29 helper->out = -1;
30 helper->err = 0;
31 helper->argv = xcalloc(4, sizeof(*helper->argv));
32 strbuf_addf(&buf, "remote-%s", data->name);
33 helper->argv[0] = strbuf_detach(&buf, NULL);
34 helper->argv[1] = transport->remote->name;
35 helper->argv[2] = transport->url;
36 helper->git_cmd = 1;
37 if (start_command(helper))
38 die("Unable to run helper: git %s", helper->argv[0]);
39 data->helper = helper;
40
41 write_str_in_full(helper->in, "capabilities\n");
42
43 file = xfdopen(helper->out, "r");
44 while (1) {
45 if (strbuf_getline(&buf, file, '\n') == EOF)
46 exit(128); /* child died, message supplied already */
47
48 if (!*buf.buf)
49 break;
50 if (!strcmp(buf.buf, "fetch"))
51 data->fetch = 1;
52 if (!strcmp(buf.buf, "import"))
53 data->import = 1;
54 }
55 return data->helper;
56 }
57
58 static int disconnect_helper(struct transport *transport)
59 {
60 struct helper_data *data = transport->data;
61 if (data->helper) {
62 write_str_in_full(data->helper->in, "\n");
63 close(data->helper->in);
64 finish_command(data->helper);
65 free((char *)data->helper->argv[0]);
66 free(data->helper->argv);
67 free(data->helper);
68 data->helper = NULL;
69 }
70 return 0;
71 }
72
73 static int release_helper(struct transport *transport)
74 {
75 disconnect_helper(transport);
76 free(transport->data);
77 return 0;
78 }
79
80 static int fetch_with_fetch(struct transport *transport,
81 int nr_heads, struct ref **to_fetch)
82 {
83 struct child_process *helper = get_helper(transport);
84 FILE *file = xfdopen(helper->out, "r");
85 int i;
86 struct strbuf buf = STRBUF_INIT;
87
88 for (i = 0; i < nr_heads; i++) {
89 const struct ref *posn = to_fetch[i];
90 if (posn->status & REF_STATUS_UPTODATE)
91 continue;
92
93 strbuf_addf(&buf, "fetch %s %s\n",
94 sha1_to_hex(posn->old_sha1), posn->name);
95 write_in_full(helper->in, buf.buf, buf.len);
96 strbuf_reset(&buf);
97
98 if (strbuf_getline(&buf, file, '\n') == EOF)
99 exit(128); /* child died, message supplied already */
100 }
101 return 0;
102 }
103
104 static int get_importer(struct transport *transport, struct child_process *fastimport)
105 {
106 struct child_process *helper = get_helper(transport);
107 memset(fastimport, 0, sizeof(*fastimport));
108 fastimport->in = helper->out;
109 fastimport->argv = xcalloc(5, sizeof(*fastimport->argv));
110 fastimport->argv[0] = "fast-import";
111 fastimport->argv[1] = "--quiet";
112
113 fastimport->git_cmd = 1;
114 return start_command(fastimport);
115 }
116
117 static int fetch_with_import(struct transport *transport,
118 int nr_heads, struct ref **to_fetch)
119 {
120 struct child_process fastimport;
121 struct child_process *helper = get_helper(transport);
122 int i;
123 struct ref *posn;
124 struct strbuf buf = STRBUF_INIT;
125
126 if (get_importer(transport, &fastimport))
127 die("Couldn't run fast-import");
128
129 for (i = 0; i < nr_heads; i++) {
130 posn = to_fetch[i];
131 if (posn->status & REF_STATUS_UPTODATE)
132 continue;
133
134 strbuf_addf(&buf, "import %s\n", posn->name);
135 write_in_full(helper->in, buf.buf, buf.len);
136 strbuf_reset(&buf);
137 }
138 disconnect_helper(transport);
139 finish_command(&fastimport);
140
141 for (i = 0; i < nr_heads; i++) {
142 posn = to_fetch[i];
143 if (posn->status & REF_STATUS_UPTODATE)
144 continue;
145 read_ref(posn->name, posn->old_sha1);
146 }
147 return 0;
148 }
149
150 static int fetch(struct transport *transport,
151 int nr_heads, struct ref **to_fetch)
152 {
153 struct helper_data *data = transport->data;
154 int i, count;
155
156 count = 0;
157 for (i = 0; i < nr_heads; i++)
158 if (!(to_fetch[i]->status & REF_STATUS_UPTODATE))
159 count++;
160
161 if (!count)
162 return 0;
163
164 if (data->fetch)
165 return fetch_with_fetch(transport, nr_heads, to_fetch);
166
167 if (data->import)
168 return fetch_with_import(transport, nr_heads, to_fetch);
169
170 return -1;
171 }
172
173 static struct ref *get_refs_list(struct transport *transport, int for_push)
174 {
175 struct child_process *helper;
176 struct ref *ret = NULL;
177 struct ref **tail = &ret;
178 struct ref *posn;
179 struct strbuf buf = STRBUF_INIT;
180 FILE *file;
181
182 helper = get_helper(transport);
183
184 write_str_in_full(helper->in, "list\n");
185
186 file = xfdopen(helper->out, "r");
187 while (1) {
188 char *eov, *eon;
189 if (strbuf_getline(&buf, file, '\n') == EOF)
190 exit(128); /* child died, message supplied already */
191
192 if (!*buf.buf)
193 break;
194
195 eov = strchr(buf.buf, ' ');
196 if (!eov)
197 die("Malformed response in ref list: %s", buf.buf);
198 eon = strchr(eov + 1, ' ');
199 *eov = '\0';
200 if (eon)
201 *eon = '\0';
202 *tail = alloc_ref(eov + 1);
203 if (buf.buf[0] == '@')
204 (*tail)->symref = xstrdup(buf.buf + 1);
205 else if (buf.buf[0] != '?')
206 get_sha1_hex(buf.buf, (*tail)->old_sha1);
207 tail = &((*tail)->next);
208 }
209 strbuf_release(&buf);
210
211 for (posn = ret; posn; posn = posn->next)
212 resolve_remote_symref(posn, ret);
213
214 return ret;
215 }
216
217 int transport_helper_init(struct transport *transport, const char *name)
218 {
219 struct helper_data *data = xcalloc(sizeof(*data), 1);
220 data->name = name;
221
222 transport->data = data;
223 transport->get_refs_list = get_refs_list;
224 transport->fetch = fetch;
225 transport->disconnect = release_helper;
226 return 0;
227 }