Add fetch methods to transport library.
[git/jrn.git] / transport.c
blob1e7374977e041cc4f3423f4c84686723929b36f1
1 #include "cache.h"
2 #include "transport.h"
3 #include "run-command.h"
4 #include "http.h"
5 #include "pkt-line.h"
6 #include "fetch-pack.h"
7 #include "walker.h"
9 /* Generic functions for using commit walkers */
11 static int fetch_objs_via_walker(const struct transport *transport,
12 int nr_objs, char **objs)
14 char *dest = xstrdup(transport->url);
15 struct walker *walker = transport->data;
17 walker->get_all = 1;
18 walker->get_tree = 1;
19 walker->get_history = 1;
20 walker->get_verbosely = transport->verbose;
21 walker->get_recover = 0;
23 if (walker_fetch(walker, nr_objs, objs, NULL, dest))
24 die("Fetch failed.");
26 free(dest);
27 return 0;
30 static int disconnect_walker(struct transport *transport)
32 struct walker *walker = transport->data;
33 if (walker)
34 walker_free(walker);
35 return 0;
38 static const struct transport_ops rsync_transport;
40 static int curl_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) {
41 const char **argv;
42 int argc;
43 int err;
45 argv = xmalloc((refspec_nr + 11) * sizeof(char *));
46 argv[0] = "http-push";
47 argc = 1;
48 if (flags & TRANSPORT_PUSH_ALL)
49 argv[argc++] = "--all";
50 if (flags & TRANSPORT_PUSH_FORCE)
51 argv[argc++] = "--force";
52 argv[argc++] = transport->url;
53 while (refspec_nr--)
54 argv[argc++] = *refspec++;
55 argv[argc] = NULL;
56 err = run_command_v_opt(argv, RUN_GIT_CMD);
57 switch (err) {
58 case -ERR_RUN_COMMAND_FORK:
59 error("unable to fork for %s", argv[0]);
60 case -ERR_RUN_COMMAND_EXEC:
61 error("unable to exec %s", argv[0]);
62 break;
63 case -ERR_RUN_COMMAND_WAITPID:
64 case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
65 case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
66 case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
67 error("%s died with strange error", argv[0]);
69 return !!err;
72 #ifndef NO_CURL
73 static int missing__target(int code, int result)
75 return /* file:// URL -- do we ever use one??? */
76 (result == CURLE_FILE_COULDNT_READ_FILE) ||
77 /* http:// and https:// URL */
78 (code == 404 && result == CURLE_HTTP_RETURNED_ERROR) ||
79 /* ftp:// URL */
80 (code == 550 && result == CURLE_FTP_COULDNT_RETR_FILE)
84 #define missing_target(a) missing__target((a)->http_code, (a)->curl_result)
86 static struct ref *get_refs_via_curl(const struct transport *transport)
88 struct buffer buffer;
89 char *data, *start, *mid;
90 char *ref_name;
91 char *refs_url;
92 int i = 0;
94 struct active_request_slot *slot;
95 struct slot_results results;
97 struct ref *refs = NULL;
98 struct ref *ref = NULL;
99 struct ref *last_ref = NULL;
101 data = xmalloc(4096);
102 buffer.size = 4096;
103 buffer.posn = 0;
104 buffer.buffer = data;
106 refs_url = xmalloc(strlen(transport->url) + 11);
107 sprintf(refs_url, "%s/info/refs", transport->url);
109 http_init();
111 slot = get_active_slot();
112 slot->results = &results;
113 curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
114 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
115 curl_easy_setopt(slot->curl, CURLOPT_URL, refs_url);
116 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
117 if (start_active_slot(slot)) {
118 run_active_slot(slot);
119 if (results.curl_result != CURLE_OK) {
120 if (missing_target(&results)) {
121 free(buffer.buffer);
122 return NULL;
123 } else {
124 free(buffer.buffer);
125 error("%s", curl_errorstr);
126 return NULL;
129 } else {
130 free(buffer.buffer);
131 error("Unable to start request");
132 return NULL;
135 http_cleanup();
137 data = buffer.buffer;
138 start = NULL;
139 mid = data;
140 while (i < buffer.posn) {
141 if (!start)
142 start = &data[i];
143 if (data[i] == '\t')
144 mid = &data[i];
145 if (data[i] == '\n') {
146 data[i] = 0;
147 ref_name = mid + 1;
148 ref = xmalloc(sizeof(struct ref) +
149 strlen(ref_name) + 1);
150 memset(ref, 0, sizeof(struct ref));
151 strcpy(ref->name, ref_name);
152 get_sha1_hex(start, ref->old_sha1);
153 if (!refs)
154 refs = ref;
155 if (last_ref)
156 last_ref->next = ref;
157 last_ref = ref;
158 start = NULL;
160 i++;
163 free(buffer.buffer);
165 return refs;
168 #else
170 static struct ref *get_refs_via_curl(const struct transport *transport)
172 die("Cannot fetch from '%s' without curl ...", transport->url);
173 return NULL;
176 #endif
178 static const struct transport_ops curl_transport = {
179 /* set_option */ NULL,
180 /* get_refs_list */ get_refs_via_curl,
181 /* fetch_refs */ NULL,
182 /* fetch_objs */ fetch_objs_via_walker,
183 /* push */ curl_transport_push,
184 /* disconnect */ disconnect_walker
187 static const struct transport_ops bundle_transport = {
190 struct git_transport_data {
191 unsigned thin : 1;
192 unsigned keep : 1;
194 int unpacklimit;
196 int depth;
198 const char *uploadpack;
199 const char *receivepack;
202 static int set_git_option(struct transport *connection,
203 const char *name, const char *value)
205 struct git_transport_data *data = connection->data;
206 if (!strcmp(name, TRANS_OPT_UPLOADPACK)) {
207 data->uploadpack = value;
208 return 0;
209 } else if (!strcmp(name, TRANS_OPT_RECEIVEPACK)) {
210 data->receivepack = value;
211 return 0;
212 } else if (!strcmp(name, TRANS_OPT_THIN)) {
213 data->thin = !!value;
214 return 0;
215 } else if (!strcmp(name, TRANS_OPT_KEEP)) {
216 data->keep = !!value;
217 return 0;
218 } else if (!strcmp(name, TRANS_OPT_UNPACKLIMIT)) {
219 data->unpacklimit = atoi(value);
220 return 0;
221 } else if (!strcmp(name, TRANS_OPT_DEPTH)) {
222 if (!value)
223 data->depth = 0;
224 else
225 data->depth = atoi(value);
226 return 0;
228 return 1;
231 static struct ref *get_refs_via_connect(const struct transport *transport)
233 struct git_transport_data *data = transport->data;
234 struct ref *refs;
235 int fd[2];
236 pid_t pid;
237 char *dest = xstrdup(transport->url);
239 pid = git_connect(fd, dest, data->uploadpack, 0);
241 if (pid < 0)
242 die("Failed to connect to \"%s\"", transport->url);
244 get_remote_heads(fd[0], &refs, 0, NULL, 0);
245 packet_flush(fd[1]);
247 finish_connect(pid);
249 free(dest);
251 return refs;
254 static int fetch_refs_via_pack(const struct transport *transport,
255 int nr_heads, char **heads)
257 struct git_transport_data *data = transport->data;
258 struct ref *refs;
259 char *dest = xstrdup(transport->url);
260 struct fetch_pack_args args;
262 args.uploadpack = data->uploadpack;
263 args.quiet = 0;
264 args.keep_pack = data->keep;
265 args.unpacklimit = data->unpacklimit;
266 args.use_thin_pack = data->thin;
267 args.fetch_all = 0;
268 args.verbose = transport->verbose;
269 args.depth = data->depth;
270 args.no_progress = 0;
272 setup_fetch_pack(&args);
274 refs = fetch_pack(dest, nr_heads, heads);
276 // ???? check that refs got everything?
278 /* free the memory used for the refs list ... */
280 free_refs(refs);
282 free(dest);
283 return 0;
286 static int git_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) {
287 struct git_transport_data *data = transport->data;
288 const char **argv;
289 char *rem;
290 int argc;
291 int err;
293 argv = xmalloc((refspec_nr + 11) * sizeof(char *));
294 argv[0] = "send-pack";
295 argc = 1;
296 if (flags & TRANSPORT_PUSH_ALL)
297 argv[argc++] = "--all";
298 if (flags & TRANSPORT_PUSH_FORCE)
299 argv[argc++] = "--force";
300 if (data->receivepack) {
301 char *rp = xmalloc(strlen(data->receivepack) + 16);
302 sprintf(rp, "--receive-pack=%s", data->receivepack);
303 argv[argc++] = rp;
305 if (data->thin)
306 argv[argc++] = "--thin";
307 rem = xmalloc(strlen(transport->remote->name) + 10);
308 sprintf(rem, "--remote=%s", transport->remote->name);
309 argv[argc++] = rem;
310 argv[argc++] = transport->url;
311 while (refspec_nr--)
312 argv[argc++] = *refspec++;
313 argv[argc] = NULL;
314 err = run_command_v_opt(argv, RUN_GIT_CMD);
315 switch (err) {
316 case -ERR_RUN_COMMAND_FORK:
317 error("unable to fork for %s", argv[0]);
318 case -ERR_RUN_COMMAND_EXEC:
319 error("unable to exec %s", argv[0]);
320 break;
321 case -ERR_RUN_COMMAND_WAITPID:
322 case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
323 case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
324 case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
325 error("%s died with strange error", argv[0]);
327 return !!err;
330 static const struct transport_ops git_transport = {
331 /* set_option */ set_git_option,
332 /* get_refs_list */ get_refs_via_connect,
333 /* fetch_refs */ fetch_refs_via_pack,
334 /* fetch_objs */ NULL,
335 /* push */ git_transport_push
338 static int is_local(const char *url)
340 const char *colon = strchr(url, ':');
341 const char *slash = strchr(url, '/');
342 return !colon || (slash && slash < colon);
345 static int is_file(const char *url)
347 struct stat buf;
348 if (stat(url, &buf))
349 return 0;
350 return S_ISREG(buf.st_mode);
353 struct transport *transport_get(struct remote *remote, const char *url,
354 int fetch)
356 struct transport *ret = NULL;
357 if (!prefixcmp(url, "rsync://")) {
358 ret = xmalloc(sizeof(*ret));
359 ret->data = NULL;
360 ret->ops = &rsync_transport;
361 } else if (!prefixcmp(url, "http://") || !prefixcmp(url, "https://") ||
362 !prefixcmp(url, "ftp://")) {
363 ret = xmalloc(sizeof(*ret));
364 ret->ops = &curl_transport;
365 if (fetch)
366 ret->data = get_http_walker(url);
367 else
368 ret->data = NULL;
369 } else if (is_local(url) && is_file(url)) {
370 ret = xmalloc(sizeof(*ret));
371 ret->data = NULL;
372 ret->ops = &bundle_transport;
373 } else {
374 struct git_transport_data *data = xcalloc(1, sizeof(*data));
375 ret = xcalloc(1, sizeof(*ret));
376 ret->data = data;
377 data->thin = 1;
378 data->uploadpack = "git-upload-pack";
379 if (remote && remote->uploadpack)
380 data->uploadpack = remote->uploadpack;
381 data->receivepack = "git-receive-pack";
382 if (remote && remote->receivepack)
383 data->receivepack = remote->receivepack;
384 data->unpacklimit = -1;
385 ret->ops = &git_transport;
387 if (ret) {
388 ret->remote = remote;
389 ret->url = url;
390 ret->remote_refs = NULL;
391 ret->fetch = !!fetch;
393 return ret;
396 int transport_set_option(struct transport *transport,
397 const char *name, const char *value)
399 int ret = 1;
400 if (transport->ops->set_option)
401 ret = transport->ops->set_option(transport, name, value);
402 if (ret < 0)
403 fprintf(stderr, "For '%s' option %s cannot be set to '%s'\n",
404 transport->url, name, value);
405 if (ret > 0)
406 fprintf(stderr, "For '%s' option %s is ignored\n",
407 transport->url, name);
408 return ret;
411 int transport_push(struct transport *transport,
412 int refspec_nr, const char **refspec, int flags)
414 if (!transport->ops->push)
415 return 1;
416 return transport->ops->push(transport, refspec_nr, refspec, flags);
419 struct ref *transport_get_remote_refs(struct transport *transport)
421 if (!transport->remote_refs)
422 transport->remote_refs =
423 transport->ops->get_refs_list(transport);
424 return transport->remote_refs;
427 #define PACK_HEADS_CHUNK_COUNT 256
429 int transport_fetch_refs(struct transport *transport, struct ref *refs)
431 int i;
432 int nr_heads = 0;
433 char **heads = xmalloc(PACK_HEADS_CHUNK_COUNT * sizeof(char *));
434 struct ref *rm;
435 int use_objs = !transport->ops->fetch_refs;
437 for (rm = refs; rm; rm = rm->next) {
438 if (rm->peer_ref &&
439 !hashcmp(rm->peer_ref->old_sha1, rm->old_sha1))
440 continue;
441 if (use_objs) {
442 heads[nr_heads++] = xstrdup(sha1_to_hex(rm->old_sha1));
443 } else {
444 heads[nr_heads++] = xstrdup(rm->name);
446 if (nr_heads % PACK_HEADS_CHUNK_COUNT == 0)
447 heads = xrealloc(heads,
448 (nr_heads + PACK_HEADS_CHUNK_COUNT) *
449 sizeof(char *));
452 if (use_objs) {
453 if (transport->ops->fetch_objs(transport, nr_heads, heads))
454 return -1;
455 } else {
456 if (transport->ops->fetch_refs(transport, nr_heads, heads))
457 return -1;
460 /* free the memory used for the heads list ... */
461 for (i = 0; i < nr_heads; i++)
462 free(heads[i]);
463 free(heads);
464 return 0;
467 int transport_disconnect(struct transport *transport)
469 int ret = 0;
470 if (transport->ops->disconnect)
471 ret = transport->ops->disconnect(transport);
472 free(transport);
473 return ret;