Restore default verbosity for http fetches.
[git/dscho.git] / transport.c
blob6fe6ec85031b849b569a2f3f11acca4c6c560f85
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"
8 #include "bundle.h"
9 #include "dir.h"
10 #include "refs.h"
12 /* rsync support */
15 * We copy packed-refs and refs/ into a temporary file, then read the
16 * loose refs recursively (sorting whenever possible), and then inserting
17 * those packed refs that are not yet in the list (not validating, but
18 * assuming that the file is sorted).
20 * Appears refactoring this from refs.c is too cumbersome.
23 static int str_cmp(const void *a, const void *b)
25 const char *s1 = a;
26 const char *s2 = b;
28 return strcmp(s1, s2);
31 /* path->buf + name_offset is expected to point to "refs/" */
33 static int read_loose_refs(struct strbuf *path, int name_offset,
34 struct ref **tail)
36 DIR *dir = opendir(path->buf);
37 struct dirent *de;
38 struct {
39 char **entries;
40 int nr, alloc;
41 } list;
42 int i, pathlen;
44 if (!dir)
45 return -1;
47 memset (&list, 0, sizeof(list));
49 while ((de = readdir(dir))) {
50 if (de->d_name[0] == '.' && (de->d_name[1] == '\0' ||
51 (de->d_name[1] == '.' &&
52 de->d_name[2] == '\0')))
53 continue;
54 ALLOC_GROW(list.entries, list.nr + 1, list.alloc);
55 list.entries[list.nr++] = xstrdup(de->d_name);
57 closedir(dir);
59 /* sort the list */
61 qsort(list.entries, list.nr, sizeof(char *), str_cmp);
63 pathlen = path->len;
64 strbuf_addch(path, '/');
66 for (i = 0; i < list.nr; i++, strbuf_setlen(path, pathlen + 1)) {
67 strbuf_addstr(path, list.entries[i]);
68 if (read_loose_refs(path, name_offset, tail)) {
69 int fd = open(path->buf, O_RDONLY);
70 char buffer[40];
71 struct ref *next;
73 if (fd < 0)
74 continue;
75 next = alloc_ref(path->len - name_offset + 1);
76 if (read_in_full(fd, buffer, 40) != 40 ||
77 get_sha1_hex(buffer, next->old_sha1)) {
78 close(fd);
79 free(next);
80 continue;
82 close(fd);
83 strcpy(next->name, path->buf + name_offset);
84 (*tail)->next = next;
85 *tail = next;
88 strbuf_setlen(path, pathlen);
90 for (i = 0; i < list.nr; i++)
91 free(list.entries[i]);
92 free(list.entries);
94 return 0;
97 /* insert the packed refs for which no loose refs were found */
99 static void insert_packed_refs(const char *packed_refs, struct ref **list)
101 FILE *f = fopen(packed_refs, "r");
102 static char buffer[PATH_MAX];
104 if (!f)
105 return;
107 for (;;) {
108 int cmp, len;
110 if (!fgets(buffer, sizeof(buffer), f)) {
111 fclose(f);
112 return;
115 if (hexval(buffer[0]) > 0xf)
116 continue;
117 len = strlen(buffer);
118 if (buffer[len - 1] == '\n')
119 buffer[--len] = '\0';
120 if (len < 41)
121 continue;
122 while ((*list)->next &&
123 (cmp = strcmp(buffer + 41,
124 (*list)->next->name)) > 0)
125 list = &(*list)->next;
126 if (!(*list)->next || cmp < 0) {
127 struct ref *next = alloc_ref(len - 40);
128 buffer[40] = '\0';
129 if (get_sha1_hex(buffer, next->old_sha1)) {
130 warning ("invalid SHA-1: %s", buffer);
131 free(next);
132 continue;
134 strcpy(next->name, buffer + 41);
135 next->next = (*list)->next;
136 (*list)->next = next;
137 list = &(*list)->next;
142 static struct ref *get_refs_via_rsync(const struct transport *transport)
144 struct strbuf buf = STRBUF_INIT, temp_dir = STRBUF_INIT;
145 struct ref dummy, *tail = &dummy;
146 struct child_process rsync;
147 const char *args[5];
148 int temp_dir_len;
150 /* copy the refs to the temporary directory */
152 strbuf_addstr(&temp_dir, git_path("rsync-refs-XXXXXX"));
153 if (!mkdtemp(temp_dir.buf))
154 die ("Could not make temporary directory");
155 temp_dir_len = temp_dir.len;
157 strbuf_addstr(&buf, transport->url);
158 strbuf_addstr(&buf, "/refs");
160 memset(&rsync, 0, sizeof(rsync));
161 rsync.argv = args;
162 rsync.stdout_to_stderr = 1;
163 args[0] = "rsync";
164 args[1] = (transport->verbose > 0) ? "-rv" : "-r";
165 args[2] = buf.buf;
166 args[3] = temp_dir.buf;
167 args[4] = NULL;
169 if (run_command(&rsync))
170 die ("Could not run rsync to get refs");
172 strbuf_reset(&buf);
173 strbuf_addstr(&buf, transport->url);
174 strbuf_addstr(&buf, "/packed-refs");
176 args[2] = buf.buf;
178 if (run_command(&rsync))
179 die ("Could not run rsync to get refs");
181 /* read the copied refs */
183 strbuf_addstr(&temp_dir, "/refs");
184 read_loose_refs(&temp_dir, temp_dir_len + 1, &tail);
185 strbuf_setlen(&temp_dir, temp_dir_len);
187 tail = &dummy;
188 strbuf_addstr(&temp_dir, "/packed-refs");
189 insert_packed_refs(temp_dir.buf, &tail);
190 strbuf_setlen(&temp_dir, temp_dir_len);
192 if (remove_dir_recursively(&temp_dir, 0))
193 warning ("Error removing temporary directory %s.",
194 temp_dir.buf);
196 strbuf_release(&buf);
197 strbuf_release(&temp_dir);
199 return dummy.next;
202 static int fetch_objs_via_rsync(struct transport *transport,
203 int nr_objs, struct ref **to_fetch)
205 struct strbuf buf = STRBUF_INIT;
206 struct child_process rsync;
207 const char *args[8];
208 int result;
210 strbuf_addstr(&buf, transport->url);
211 strbuf_addstr(&buf, "/objects/");
213 memset(&rsync, 0, sizeof(rsync));
214 rsync.argv = args;
215 rsync.stdout_to_stderr = 1;
216 args[0] = "rsync";
217 args[1] = (transport->verbose > 0) ? "-rv" : "-r";
218 args[2] = "--ignore-existing";
219 args[3] = "--exclude";
220 args[4] = "info";
221 args[5] = buf.buf;
222 args[6] = get_object_directory();
223 args[7] = NULL;
225 /* NEEDSWORK: handle one level of alternates */
226 result = run_command(&rsync);
228 strbuf_release(&buf);
230 return result;
233 static int write_one_ref(const char *name, const unsigned char *sha1,
234 int flags, void *data)
236 struct strbuf *buf = data;
237 int len = buf->len;
238 FILE *f;
240 /* when called via for_each_ref(), flags is non-zero */
241 if (flags && prefixcmp(name, "refs/heads/") &&
242 prefixcmp(name, "refs/tags/"))
243 return 0;
245 strbuf_addstr(buf, name);
246 if (safe_create_leading_directories(buf->buf) ||
247 !(f = fopen(buf->buf, "w")) ||
248 fprintf(f, "%s\n", sha1_to_hex(sha1)) < 0 ||
249 fclose(f))
250 return error("problems writing temporary file %s", buf->buf);
251 strbuf_setlen(buf, len);
252 return 0;
255 static int write_refs_to_temp_dir(struct strbuf *temp_dir,
256 int refspec_nr, const char **refspec)
258 int i;
260 for (i = 0; i < refspec_nr; i++) {
261 unsigned char sha1[20];
262 char *ref;
264 if (dwim_ref(refspec[i], strlen(refspec[i]), sha1, &ref) != 1)
265 return error("Could not get ref %s", refspec[i]);
267 if (write_one_ref(ref, sha1, 0, temp_dir)) {
268 free(ref);
269 return -1;
271 free(ref);
273 return 0;
276 static int rsync_transport_push(struct transport *transport,
277 int refspec_nr, const char **refspec, int flags)
279 struct strbuf buf = STRBUF_INIT, temp_dir = STRBUF_INIT;
280 int result = 0, i;
281 struct child_process rsync;
282 const char *args[8];
284 /* first push the objects */
286 strbuf_addstr(&buf, transport->url);
287 strbuf_addch(&buf, '/');
289 memset(&rsync, 0, sizeof(rsync));
290 rsync.argv = args;
291 rsync.stdout_to_stderr = 1;
292 args[0] = "rsync";
293 args[1] = (transport->verbose > 0) ? "-av" : "-a";
294 args[2] = "--ignore-existing";
295 args[3] = "--exclude";
296 args[4] = "info";
297 args[5] = get_object_directory();;
298 args[6] = buf.buf;
299 args[7] = NULL;
301 if (run_command(&rsync))
302 return error("Could not push objects to %s", transport->url);
304 /* copy the refs to the temporary directory; they could be packed. */
306 strbuf_addstr(&temp_dir, git_path("rsync-refs-XXXXXX"));
307 if (!mkdtemp(temp_dir.buf))
308 die ("Could not make temporary directory");
309 strbuf_addch(&temp_dir, '/');
311 if (flags & TRANSPORT_PUSH_ALL) {
312 if (for_each_ref(write_one_ref, &temp_dir))
313 return -1;
314 } else if (write_refs_to_temp_dir(&temp_dir, refspec_nr, refspec))
315 return -1;
317 i = (flags & TRANSPORT_PUSH_FORCE) ? 2 : 3;
318 args[i++] = temp_dir.buf;
319 args[i++] = transport->url;
320 args[i++] = NULL;
321 if (run_command(&rsync))
322 result = error("Could not push to %s", transport->url);
324 if (remove_dir_recursively(&temp_dir, 0))
325 warning ("Could not remove temporary directory %s.",
326 temp_dir.buf);
328 strbuf_release(&buf);
329 strbuf_release(&temp_dir);
331 return result;
334 /* Generic functions for using commit walkers */
336 static int fetch_objs_via_walker(struct transport *transport,
337 int nr_objs, struct ref **to_fetch)
339 char *dest = xstrdup(transport->url);
340 struct walker *walker = transport->data;
341 char **objs = xmalloc(nr_objs * sizeof(*objs));
342 int i;
344 walker->get_all = 1;
345 walker->get_tree = 1;
346 walker->get_history = 1;
347 walker->get_verbosely = transport->verbose >= 0;
348 walker->get_recover = 0;
350 for (i = 0; i < nr_objs; i++)
351 objs[i] = xstrdup(sha1_to_hex(to_fetch[i]->old_sha1));
353 if (walker_fetch(walker, nr_objs, objs, NULL, NULL))
354 die("Fetch failed.");
356 for (i = 0; i < nr_objs; i++)
357 free(objs[i]);
358 free(objs);
359 free(dest);
360 return 0;
363 static int disconnect_walker(struct transport *transport)
365 struct walker *walker = transport->data;
366 if (walker)
367 walker_free(walker);
368 return 0;
371 static int curl_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) {
372 const char **argv;
373 int argc;
374 int err;
376 argv = xmalloc((refspec_nr + 11) * sizeof(char *));
377 argv[0] = "http-push";
378 argc = 1;
379 if (flags & TRANSPORT_PUSH_ALL)
380 argv[argc++] = "--all";
381 if (flags & TRANSPORT_PUSH_FORCE)
382 argv[argc++] = "--force";
383 argv[argc++] = transport->url;
384 while (refspec_nr--)
385 argv[argc++] = *refspec++;
386 argv[argc] = NULL;
387 err = run_command_v_opt(argv, RUN_GIT_CMD);
388 switch (err) {
389 case -ERR_RUN_COMMAND_FORK:
390 error("unable to fork for %s", argv[0]);
391 case -ERR_RUN_COMMAND_EXEC:
392 error("unable to exec %s", argv[0]);
393 break;
394 case -ERR_RUN_COMMAND_WAITPID:
395 case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
396 case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
397 case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
398 error("%s died with strange error", argv[0]);
400 return !!err;
403 #ifndef NO_CURL
404 static int missing__target(int code, int result)
406 return /* file:// URL -- do we ever use one??? */
407 (result == CURLE_FILE_COULDNT_READ_FILE) ||
408 /* http:// and https:// URL */
409 (code == 404 && result == CURLE_HTTP_RETURNED_ERROR) ||
410 /* ftp:// URL */
411 (code == 550 && result == CURLE_FTP_COULDNT_RETR_FILE)
415 #define missing_target(a) missing__target((a)->http_code, (a)->curl_result)
417 static struct ref *get_refs_via_curl(const struct transport *transport)
419 struct buffer buffer;
420 char *data, *start, *mid;
421 char *ref_name;
422 char *refs_url;
423 int i = 0;
425 struct active_request_slot *slot;
426 struct slot_results results;
428 struct ref *refs = NULL;
429 struct ref *ref = NULL;
430 struct ref *last_ref = NULL;
432 data = xmalloc(4096);
433 buffer.size = 4096;
434 buffer.posn = 0;
435 buffer.buffer = data;
437 refs_url = xmalloc(strlen(transport->url) + 11);
438 sprintf(refs_url, "%s/info/refs", transport->url);
440 http_init();
442 slot = get_active_slot();
443 slot->results = &results;
444 curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
445 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
446 curl_easy_setopt(slot->curl, CURLOPT_URL, refs_url);
447 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
448 if (start_active_slot(slot)) {
449 run_active_slot(slot);
450 if (results.curl_result != CURLE_OK) {
451 if (missing_target(&results)) {
452 free(buffer.buffer);
453 return NULL;
454 } else {
455 free(buffer.buffer);
456 error("%s", curl_errorstr);
457 return NULL;
460 } else {
461 free(buffer.buffer);
462 error("Unable to start request");
463 return NULL;
466 http_cleanup();
468 data = buffer.buffer;
469 start = NULL;
470 mid = data;
471 while (i < buffer.posn) {
472 if (!start)
473 start = &data[i];
474 if (data[i] == '\t')
475 mid = &data[i];
476 if (data[i] == '\n') {
477 data[i] = 0;
478 ref_name = mid + 1;
479 ref = xmalloc(sizeof(struct ref) +
480 strlen(ref_name) + 1);
481 memset(ref, 0, sizeof(struct ref));
482 strcpy(ref->name, ref_name);
483 get_sha1_hex(start, ref->old_sha1);
484 if (!refs)
485 refs = ref;
486 if (last_ref)
487 last_ref->next = ref;
488 last_ref = ref;
489 start = NULL;
491 i++;
494 free(buffer.buffer);
496 return refs;
499 static int fetch_objs_via_curl(struct transport *transport,
500 int nr_objs, struct ref **to_fetch)
502 if (!transport->data)
503 transport->data = get_http_walker(transport->url);
504 return fetch_objs_via_walker(transport, nr_objs, to_fetch);
507 #else
509 static struct ref *get_refs_via_curl(const struct transport *transport)
511 die("Cannot fetch from '%s' without curl ...", transport->url);
512 return NULL;
515 static int fetch_objs_via_curl(struct transport *transport,
516 int nr_objs, struct ref **to_fetch)
518 die("Cannot fetch from '%s' without curl ...", transport->url);
519 return -1;
522 #endif
524 struct bundle_transport_data {
525 int fd;
526 struct bundle_header header;
529 static struct ref *get_refs_from_bundle(const struct transport *transport)
531 struct bundle_transport_data *data = transport->data;
532 struct ref *result = NULL;
533 int i;
535 if (data->fd > 0)
536 close(data->fd);
537 data->fd = read_bundle_header(transport->url, &data->header);
538 if (data->fd < 0)
539 die ("Could not read bundle '%s'.", transport->url);
540 for (i = 0; i < data->header.references.nr; i++) {
541 struct ref_list_entry *e = data->header.references.list + i;
542 struct ref *ref = alloc_ref(strlen(e->name) + 1);
543 hashcpy(ref->old_sha1, e->sha1);
544 strcpy(ref->name, e->name);
545 ref->next = result;
546 result = ref;
548 return result;
551 static int fetch_refs_from_bundle(struct transport *transport,
552 int nr_heads, struct ref **to_fetch)
554 struct bundle_transport_data *data = transport->data;
555 return unbundle(&data->header, data->fd);
558 static int close_bundle(struct transport *transport)
560 struct bundle_transport_data *data = transport->data;
561 if (data->fd > 0)
562 close(data->fd);
563 free(data);
564 return 0;
567 struct git_transport_data {
568 unsigned thin : 1;
569 unsigned keep : 1;
570 int depth;
571 const char *uploadpack;
572 const char *receivepack;
575 static int set_git_option(struct transport *connection,
576 const char *name, const char *value)
578 struct git_transport_data *data = connection->data;
579 if (!strcmp(name, TRANS_OPT_UPLOADPACK)) {
580 data->uploadpack = value;
581 return 0;
582 } else if (!strcmp(name, TRANS_OPT_RECEIVEPACK)) {
583 data->receivepack = value;
584 return 0;
585 } else if (!strcmp(name, TRANS_OPT_THIN)) {
586 data->thin = !!value;
587 return 0;
588 } else if (!strcmp(name, TRANS_OPT_KEEP)) {
589 data->keep = !!value;
590 return 0;
591 } else if (!strcmp(name, TRANS_OPT_DEPTH)) {
592 if (!value)
593 data->depth = 0;
594 else
595 data->depth = atoi(value);
596 return 0;
598 return 1;
601 static struct ref *get_refs_via_connect(const struct transport *transport)
603 struct git_transport_data *data = transport->data;
604 struct ref *refs;
605 int fd[2];
606 pid_t pid;
607 char *dest = xstrdup(transport->url);
609 pid = git_connect(fd, dest, data->uploadpack, 0);
611 if (pid < 0)
612 die("Failed to connect to \"%s\"", transport->url);
614 get_remote_heads(fd[0], &refs, 0, NULL, 0);
615 packet_flush(fd[1]);
617 finish_connect(pid);
619 free(dest);
621 return refs;
624 static int fetch_refs_via_pack(struct transport *transport,
625 int nr_heads, struct ref **to_fetch)
627 struct git_transport_data *data = transport->data;
628 char **heads = xmalloc(nr_heads * sizeof(*heads));
629 char **origh = xmalloc(nr_heads * sizeof(*origh));
630 struct ref *refs;
631 char *dest = xstrdup(transport->url);
632 struct fetch_pack_args args;
633 int i;
635 memset(&args, 0, sizeof(args));
636 args.uploadpack = data->uploadpack;
637 args.keep_pack = data->keep;
638 args.lock_pack = 1;
639 args.use_thin_pack = data->thin;
640 args.verbose = transport->verbose > 0;
641 args.depth = data->depth;
643 for (i = 0; i < nr_heads; i++)
644 origh[i] = heads[i] = xstrdup(to_fetch[i]->name);
645 refs = fetch_pack(&args, dest, nr_heads, heads, &transport->pack_lockfile);
647 for (i = 0; i < nr_heads; i++)
648 free(origh[i]);
649 free(origh);
650 free(heads);
651 free_refs(refs);
652 free(dest);
653 return 0;
656 static int git_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) {
657 struct git_transport_data *data = transport->data;
658 const char **argv;
659 char *rem;
660 int argc;
661 int err;
663 argv = xmalloc((refspec_nr + 11) * sizeof(char *));
664 argv[0] = "send-pack";
665 argc = 1;
666 if (flags & TRANSPORT_PUSH_ALL)
667 argv[argc++] = "--all";
668 if (flags & TRANSPORT_PUSH_FORCE)
669 argv[argc++] = "--force";
670 if (data->receivepack) {
671 char *rp = xmalloc(strlen(data->receivepack) + 16);
672 sprintf(rp, "--receive-pack=%s", data->receivepack);
673 argv[argc++] = rp;
675 if (data->thin)
676 argv[argc++] = "--thin";
677 rem = xmalloc(strlen(transport->remote->name) + 10);
678 sprintf(rem, "--remote=%s", transport->remote->name);
679 argv[argc++] = rem;
680 argv[argc++] = transport->url;
681 while (refspec_nr--)
682 argv[argc++] = *refspec++;
683 argv[argc] = NULL;
684 err = run_command_v_opt(argv, RUN_GIT_CMD);
685 switch (err) {
686 case -ERR_RUN_COMMAND_FORK:
687 error("unable to fork for %s", argv[0]);
688 case -ERR_RUN_COMMAND_EXEC:
689 error("unable to exec %s", argv[0]);
690 break;
691 case -ERR_RUN_COMMAND_WAITPID:
692 case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
693 case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
694 case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
695 error("%s died with strange error", argv[0]);
697 return !!err;
700 static int disconnect_git(struct transport *transport)
702 free(transport->data);
703 return 0;
706 static int is_local(const char *url)
708 const char *colon = strchr(url, ':');
709 const char *slash = strchr(url, '/');
710 return !colon || (slash && slash < colon);
713 static int is_file(const char *url)
715 struct stat buf;
716 if (stat(url, &buf))
717 return 0;
718 return S_ISREG(buf.st_mode);
721 struct transport *transport_get(struct remote *remote, const char *url)
723 struct transport *ret = xcalloc(1, sizeof(*ret));
725 ret->remote = remote;
726 ret->url = url;
728 if (!prefixcmp(url, "rsync://")) {
729 ret->get_refs_list = get_refs_via_rsync;
730 ret->fetch = fetch_objs_via_rsync;
731 ret->push = rsync_transport_push;
733 } else if (!prefixcmp(url, "http://")
734 || !prefixcmp(url, "https://")
735 || !prefixcmp(url, "ftp://")) {
736 ret->get_refs_list = get_refs_via_curl;
737 ret->fetch = fetch_objs_via_curl;
738 ret->push = curl_transport_push;
739 ret->disconnect = disconnect_walker;
741 } else if (is_local(url) && is_file(url)) {
742 struct bundle_transport_data *data = xcalloc(1, sizeof(*data));
743 ret->data = data;
744 ret->get_refs_list = get_refs_from_bundle;
745 ret->fetch = fetch_refs_from_bundle;
746 ret->disconnect = close_bundle;
748 } else {
749 struct git_transport_data *data = xcalloc(1, sizeof(*data));
750 ret->data = data;
751 ret->set_option = set_git_option;
752 ret->get_refs_list = get_refs_via_connect;
753 ret->fetch = fetch_refs_via_pack;
754 ret->push = git_transport_push;
755 ret->disconnect = disconnect_git;
757 data->thin = 1;
758 data->uploadpack = "git-upload-pack";
759 if (remote && remote->uploadpack)
760 data->uploadpack = remote->uploadpack;
761 data->receivepack = "git-receive-pack";
762 if (remote && remote->receivepack)
763 data->receivepack = remote->receivepack;
766 return ret;
769 int transport_set_option(struct transport *transport,
770 const char *name, const char *value)
772 if (transport->set_option)
773 return transport->set_option(transport, name, value);
774 return 1;
777 int transport_push(struct transport *transport,
778 int refspec_nr, const char **refspec, int flags)
780 if (!transport->push)
781 return 1;
782 return transport->push(transport, refspec_nr, refspec, flags);
785 struct ref *transport_get_remote_refs(struct transport *transport)
787 if (!transport->remote_refs)
788 transport->remote_refs = transport->get_refs_list(transport);
789 return transport->remote_refs;
792 int transport_fetch_refs(struct transport *transport, struct ref *refs)
794 int rc;
795 int nr_heads = 0, nr_alloc = 0;
796 struct ref **heads = NULL;
797 struct ref *rm;
799 for (rm = refs; rm; rm = rm->next) {
800 if (rm->peer_ref &&
801 !hashcmp(rm->peer_ref->old_sha1, rm->old_sha1))
802 continue;
803 ALLOC_GROW(heads, nr_heads + 1, nr_alloc);
804 heads[nr_heads++] = rm;
807 rc = transport->fetch(transport, nr_heads, heads);
808 free(heads);
809 return rc;
812 void transport_unlock_pack(struct transport *transport)
814 if (transport->pack_lockfile) {
815 unlink(transport->pack_lockfile);
816 free(transport->pack_lockfile);
817 transport->pack_lockfile = NULL;
821 int transport_disconnect(struct transport *transport)
823 int ret = 0;
824 if (transport->disconnect)
825 ret = transport->disconnect(transport);
826 free(transport);
827 return ret;