3 #include "run-command.h"
6 #include "fetch-pack.h"
10 /* Generic functions for using commit walkers */
12 static int fetch_objs_via_walker(const struct transport *transport,
13 int nr_objs, char **objs)
15 char *dest = xstrdup(transport->url);
16 struct walker *walker = transport->data;
20 walker->get_history = 1;
21 walker->get_verbosely = transport->verbose;
22 walker->get_recover = 0;
24 if (walker_fetch(walker, nr_objs, objs, NULL, dest))
31 static int disconnect_walker(struct transport *transport)
33 struct walker *walker = transport->data;
39 static const struct transport_ops rsync_transport;
41 static int curl_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) {
46 argv = xmalloc((refspec_nr + 11) * sizeof(char *));
47 argv[0] = "http-push";
49 if (flags & TRANSPORT_PUSH_ALL)
50 argv[argc++] = "--all";
51 if (flags & TRANSPORT_PUSH_FORCE)
52 argv[argc++] = "--force";
53 argv[argc++] = transport->url;
55 argv[argc++] = *refspec++;
57 err = run_command_v_opt(argv, RUN_GIT_CMD);
59 case -ERR_RUN_COMMAND_FORK:
60 error("unable to fork for %s", argv[0]);
61 case -ERR_RUN_COMMAND_EXEC:
62 error("unable to exec %s", argv[0]);
64 case -ERR_RUN_COMMAND_WAITPID:
65 case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
66 case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
67 case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
68 error("%s died with strange error", argv[0]);
74 static int missing__target(int code, int result)
76 return /* file:// URL -- do we ever use one??? */
77 (result == CURLE_FILE_COULDNT_READ_FILE) ||
78 /* http:// and https:// URL */
79 (code == 404 && result == CURLE_HTTP_RETURNED_ERROR) ||
81 (code == 550 && result == CURLE_FTP_COULDNT_RETR_FILE)
85 #define missing_target(a) missing__target((a)->http_code, (a)->curl_result)
87 static struct ref *get_refs_via_curl(const struct transport *transport)
90 char *data, *start, *mid;
95 struct active_request_slot *slot;
96 struct slot_results results;
98 struct ref *refs = NULL;
99 struct ref *ref = NULL;
100 struct ref *last_ref = NULL;
102 data = xmalloc(4096);
105 buffer.buffer = data;
107 refs_url = xmalloc(strlen(transport->url) + 11);
108 sprintf(refs_url, "%s/info/refs", transport->url);
112 slot = get_active_slot();
113 slot->results = &results;
114 curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
115 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
116 curl_easy_setopt(slot->curl, CURLOPT_URL, refs_url);
117 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
118 if (start_active_slot(slot)) {
119 run_active_slot(slot);
120 if (results.curl_result != CURLE_OK) {
121 if (missing_target(&results)) {
126 error("%s", curl_errorstr);
132 error("Unable to start request");
138 data = buffer.buffer;
141 while (i < buffer.posn) {
146 if (data[i] == '\n') {
149 ref = xmalloc(sizeof(struct ref) +
150 strlen(ref_name) + 1);
151 memset(ref, 0, sizeof(struct ref));
152 strcpy(ref->name, ref_name);
153 get_sha1_hex(start, ref->old_sha1);
157 last_ref->next = ref;
171 static struct ref *get_refs_via_curl(const struct transport *transport)
173 die("Cannot fetch from '%s' without curl ...", transport->url);
179 static const struct transport_ops curl_transport = {
180 /* set_option */ NULL,
181 /* get_refs_list */ get_refs_via_curl,
182 /* fetch_refs */ NULL,
183 /* fetch_objs */ fetch_objs_via_walker,
184 /* push */ curl_transport_push,
185 /* disconnect */ disconnect_walker
188 struct bundle_transport_data {
190 struct bundle_header header;
193 static struct ref *get_refs_from_bundle(const struct transport *transport)
195 struct bundle_transport_data *data = transport->data;
196 struct ref *result = NULL;
201 data->fd = read_bundle_header(transport->url, &data->header);
203 die ("Could not read bundle '%s'.", transport->url);
204 for (i = 0; i < data->header.references.nr; i++) {
205 struct ref_list_entry *e = data->header.references.list + i;
206 struct ref *ref = alloc_ref(strlen(e->name));
207 hashcpy(ref->old_sha1, e->sha1);
208 strcpy(ref->name, e->name);
215 static int fetch_refs_from_bundle(const struct transport *transport,
216 int nr_heads, char **heads)
218 struct bundle_transport_data *data = transport->data;
219 return unbundle(&data->header, data->fd);
222 static int close_bundle(struct transport *transport)
224 struct bundle_transport_data *data = transport->data;
230 static const struct transport_ops bundle_transport = {
231 /* set_option */ NULL,
232 /* get_refs_list */ get_refs_from_bundle,
233 /* fetch_refs */ fetch_refs_from_bundle,
234 /* fetch_objs */ NULL,
236 /* disconnect */ close_bundle
239 struct git_transport_data {
247 const char *uploadpack;
248 const char *receivepack;
251 static int set_git_option(struct transport *connection,
252 const char *name, const char *value)
254 struct git_transport_data *data = connection->data;
255 if (!strcmp(name, TRANS_OPT_UPLOADPACK)) {
256 data->uploadpack = value;
258 } else if (!strcmp(name, TRANS_OPT_RECEIVEPACK)) {
259 data->receivepack = value;
261 } else if (!strcmp(name, TRANS_OPT_THIN)) {
262 data->thin = !!value;
264 } else if (!strcmp(name, TRANS_OPT_KEEP)) {
265 data->keep = !!value;
267 } else if (!strcmp(name, TRANS_OPT_UNPACKLIMIT)) {
268 data->unpacklimit = atoi(value);
270 } else if (!strcmp(name, TRANS_OPT_DEPTH)) {
274 data->depth = atoi(value);
280 static struct ref *get_refs_via_connect(const struct transport *transport)
282 struct git_transport_data *data = transport->data;
286 char *dest = xstrdup(transport->url);
288 pid = git_connect(fd, dest, data->uploadpack, 0);
291 die("Failed to connect to \"%s\"", transport->url);
293 get_remote_heads(fd[0], &refs, 0, NULL, 0);
303 static int fetch_refs_via_pack(const struct transport *transport,
304 int nr_heads, char **heads)
306 struct git_transport_data *data = transport->data;
308 char *dest = xstrdup(transport->url);
309 struct fetch_pack_args args;
311 args.uploadpack = data->uploadpack;
313 args.keep_pack = data->keep;
314 args.unpacklimit = data->unpacklimit;
315 args.use_thin_pack = data->thin;
317 args.verbose = transport->verbose;
318 args.depth = data->depth;
319 args.no_progress = 0;
321 setup_fetch_pack(&args);
323 refs = fetch_pack(dest, nr_heads, heads);
325 // ???? check that refs got everything?
327 /* free the memory used for the refs list ... */
335 static int git_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) {
336 struct git_transport_data *data = transport->data;
342 argv = xmalloc((refspec_nr + 11) * sizeof(char *));
343 argv[0] = "send-pack";
345 if (flags & TRANSPORT_PUSH_ALL)
346 argv[argc++] = "--all";
347 if (flags & TRANSPORT_PUSH_FORCE)
348 argv[argc++] = "--force";
349 if (data->receivepack) {
350 char *rp = xmalloc(strlen(data->receivepack) + 16);
351 sprintf(rp, "--receive-pack=%s", data->receivepack);
355 argv[argc++] = "--thin";
356 rem = xmalloc(strlen(transport->remote->name) + 10);
357 sprintf(rem, "--remote=%s", transport->remote->name);
359 argv[argc++] = transport->url;
361 argv[argc++] = *refspec++;
363 err = run_command_v_opt(argv, RUN_GIT_CMD);
365 case -ERR_RUN_COMMAND_FORK:
366 error("unable to fork for %s", argv[0]);
367 case -ERR_RUN_COMMAND_EXEC:
368 error("unable to exec %s", argv[0]);
370 case -ERR_RUN_COMMAND_WAITPID:
371 case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
372 case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
373 case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
374 error("%s died with strange error", argv[0]);
379 static const struct transport_ops git_transport = {
380 /* set_option */ set_git_option,
381 /* get_refs_list */ get_refs_via_connect,
382 /* fetch_refs */ fetch_refs_via_pack,
383 /* fetch_objs */ NULL,
384 /* push */ git_transport_push
387 static int is_local(const char *url)
389 const char *colon = strchr(url, ':');
390 const char *slash = strchr(url, '/');
391 return !colon || (slash && slash < colon);
394 static int is_file(const char *url)
399 return S_ISREG(buf.st_mode);
402 struct transport *transport_get(struct remote *remote, const char *url,
405 struct transport *ret = NULL;
406 if (!prefixcmp(url, "rsync://")) {
407 ret = xmalloc(sizeof(*ret));
409 ret->ops = &rsync_transport;
410 } else if (!prefixcmp(url, "http://") || !prefixcmp(url, "https://") ||
411 !prefixcmp(url, "ftp://")) {
412 ret = xmalloc(sizeof(*ret));
413 ret->ops = &curl_transport;
415 ret->data = get_http_walker(url);
418 } else if (is_local(url) && is_file(url)) {
419 struct bundle_transport_data *data = xcalloc(1, sizeof(*data));
420 ret = xmalloc(sizeof(*ret));
422 ret->ops = &bundle_transport;
424 struct git_transport_data *data = xcalloc(1, sizeof(*data));
425 ret = xcalloc(1, sizeof(*ret));
428 data->uploadpack = "git-upload-pack";
429 if (remote && remote->uploadpack)
430 data->uploadpack = remote->uploadpack;
431 data->receivepack = "git-receive-pack";
432 if (remote && remote->receivepack)
433 data->receivepack = remote->receivepack;
434 data->unpacklimit = -1;
435 ret->ops = &git_transport;
438 ret->remote = remote;
440 ret->remote_refs = NULL;
441 ret->fetch = !!fetch;
446 int transport_set_option(struct transport *transport,
447 const char *name, const char *value)
450 if (transport->ops->set_option)
451 ret = transport->ops->set_option(transport, name, value);
453 fprintf(stderr, "For '%s' option %s cannot be set to '%s'\n",
454 transport->url, name, value);
456 fprintf(stderr, "For '%s' option %s is ignored\n",
457 transport->url, name);
461 int transport_push(struct transport *transport,
462 int refspec_nr, const char **refspec, int flags)
464 if (!transport->ops->push)
466 return transport->ops->push(transport, refspec_nr, refspec, flags);
469 struct ref *transport_get_remote_refs(struct transport *transport)
471 if (!transport->remote_refs)
472 transport->remote_refs =
473 transport->ops->get_refs_list(transport);
474 return transport->remote_refs;
477 #define PACK_HEADS_CHUNK_COUNT 256
479 int transport_fetch_refs(struct transport *transport, struct ref *refs)
483 char **heads = xmalloc(PACK_HEADS_CHUNK_COUNT * sizeof(char *));
485 int use_objs = !transport->ops->fetch_refs;
487 for (rm = refs; rm; rm = rm->next) {
489 !hashcmp(rm->peer_ref->old_sha1, rm->old_sha1))
492 heads[nr_heads++] = xstrdup(sha1_to_hex(rm->old_sha1));
494 heads[nr_heads++] = xstrdup(rm->name);
496 if (nr_heads % PACK_HEADS_CHUNK_COUNT == 0)
497 heads = xrealloc(heads,
498 (nr_heads + PACK_HEADS_CHUNK_COUNT) *
503 if (transport->ops->fetch_objs(transport, nr_heads, heads))
506 if (transport->ops->fetch_refs(transport, nr_heads, heads))
510 /* free the memory used for the heads list ... */
511 for (i = 0; i < nr_heads; i++)
517 int transport_disconnect(struct transport *transport)
520 if (transport->ops->disconnect)
521 ret = transport->ops->disconnect(transport);