File Coverage

deps/libgit2/src/libgit2/transports/local.c
Criterion Covered Total %
statement 154 359 42.9
branch 46 178 25.8
condition n/a
subroutine n/a
pod n/a
total 200 537 37.2


line stmt bran cond sub pod time code
1             /*
2             * Copyright (C) the libgit2 contributors. All rights reserved.
3             *
4             * This file is part of libgit2, distributed under the GNU GPL v2 with
5             * a Linking Exception. For full terms see the included COPYING file.
6             */
7              
8             #include "common.h"
9              
10             #include "pack-objects.h"
11             #include "refs.h"
12             #include "posix.h"
13             #include "fs_path.h"
14             #include "repository.h"
15             #include "odb.h"
16             #include "push.h"
17             #include "remote.h"
18             #include "proxy.h"
19              
20             #include "git2/types.h"
21             #include "git2/net.h"
22             #include "git2/repository.h"
23             #include "git2/object.h"
24             #include "git2/tag.h"
25             #include "git2/transport.h"
26             #include "git2/revwalk.h"
27             #include "git2/odb_backend.h"
28             #include "git2/pack.h"
29             #include "git2/commit.h"
30             #include "git2/revparse.h"
31             #include "git2/sys/remote.h"
32              
33             typedef struct {
34             git_transport parent;
35             git_remote *owner;
36             char *url;
37             int direction;
38             git_atomic32 cancelled;
39             git_repository *repo;
40             git_remote_connect_options connect_opts;
41             git_vector refs;
42             unsigned connected : 1,
43             have_refs : 1;
44             } transport_local;
45              
46 2           static void free_head(git_remote_head *head)
47             {
48 2           git__free(head->name);
49 2           git__free(head->symref_target);
50 2           git__free(head);
51 2           }
52              
53 6           static void free_heads(git_vector *heads)
54             {
55             git_remote_head *head;
56             size_t i;
57              
58 8 100         git_vector_foreach(heads, i, head)
59 2           free_head(head);
60              
61 6           git_vector_free(heads);
62 6           }
63              
64 2           static int add_ref(transport_local *t, const char *name)
65             {
66 2           const char peeled[] = "^{}";
67             git_reference *ref, *resolved;
68             git_remote_head *head;
69             git_oid obj_id;
70 2           git_object *obj = NULL, *target = NULL;
71 2           git_str buf = GIT_STR_INIT;
72             int error;
73              
74 2 50         if ((error = git_reference_lookup(&ref, t->repo, name)) < 0)
75 0           return error;
76              
77 2           error = git_reference_resolve(&resolved, ref);
78 2 50         if (error < 0) {
79 0           git_reference_free(ref);
80 0 0         if (!strcmp(name, GIT_HEAD_FILE) && error == GIT_ENOTFOUND) {
    0          
81             /* This is actually okay. Empty repos often have a HEAD that
82             * points to a nonexistent "refs/heads/master". */
83 0           git_error_clear();
84 0           return 0;
85             }
86 0           return error;
87             }
88              
89 2           git_oid_cpy(&obj_id, git_reference_target(resolved));
90 2           git_reference_free(resolved);
91              
92 2           head = git__calloc(1, sizeof(git_remote_head));
93 2 50         GIT_ERROR_CHECK_ALLOC(head);
94              
95 2           head->name = git__strdup(name);
96 2 50         GIT_ERROR_CHECK_ALLOC(head->name);
97              
98 2           git_oid_cpy(&head->oid, &obj_id);
99              
100 2 50         if (git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC) {
101 0           head->symref_target = git__strdup(git_reference_symbolic_target(ref));
102 0 0         GIT_ERROR_CHECK_ALLOC(head->symref_target);
103             }
104 2           git_reference_free(ref);
105              
106 2 50         if ((error = git_vector_insert(&t->refs, head)) < 0) {
107 0           free_head(head);
108 0           return error;
109             }
110              
111             /* If it's not a tag, we don't need to try to peel it */
112 2 50         if (git__prefixcmp(name, GIT_REFS_TAGS_DIR))
113 2           return 0;
114              
115 0 0         if ((error = git_object_lookup(&obj, t->repo, &head->oid, GIT_OBJECT_ANY)) < 0)
116 0           return error;
117              
118 0           head = NULL;
119              
120             /* If it's not an annotated tag, or if we're mocking
121             * git-receive-pack, just get out */
122 0 0         if (git_object_type(obj) != GIT_OBJECT_TAG ||
    0          
123 0           t->direction != GIT_DIRECTION_FETCH) {
124 0           git_object_free(obj);
125 0           return 0;
126             }
127              
128             /* And if it's a tag, peel it, and add it to the list */
129 0           head = git__calloc(1, sizeof(git_remote_head));
130 0 0         GIT_ERROR_CHECK_ALLOC(head);
131              
132 0 0         if (git_str_join(&buf, 0, name, peeled) < 0) {
133 0           free_head(head);
134 0           return -1;
135             }
136 0           head->name = git_str_detach(&buf);
137              
138 0 0         if (!(error = git_tag_peel(&target, (git_tag *)obj))) {
139 0           git_oid_cpy(&head->oid, git_object_id(target));
140              
141 0 0         if ((error = git_vector_insert(&t->refs, head)) < 0) {
142 0           free_head(head);
143             }
144             }
145              
146 0           git_object_free(obj);
147 0           git_object_free(target);
148              
149 2           return error;
150             }
151              
152 4           static int store_refs(transport_local *t)
153             {
154             size_t i;
155             git_remote_head *head;
156 4           git_strarray ref_names = {0};
157              
158 4 50         GIT_ASSERT_ARG(t);
159              
160 4 50         if (git_reference_list(&ref_names, t->repo) < 0)
161 0           goto on_error;
162              
163             /* Clear all heads we might have fetched in a previous connect */
164 4 50         git_vector_foreach(&t->refs, i, head) {
165 0           git__free(head->name);
166 0           git__free(head);
167             }
168              
169             /* Clear the vector so we can reuse it */
170 4           git_vector_clear(&t->refs);
171              
172             /* Sort the references first */
173 4           git__tsort((void **)ref_names.strings, ref_names.count, &git__strcmp_cb);
174              
175             /* Add HEAD iff direction is fetch */
176 4 50         if (t->direction == GIT_DIRECTION_FETCH && add_ref(t, GIT_HEAD_FILE) < 0)
    0          
177 0           goto on_error;
178              
179 6 100         for (i = 0; i < ref_names.count; ++i) {
180 2 50         if (add_ref(t, ref_names.strings[i]) < 0)
181 0           goto on_error;
182             }
183              
184 4           t->have_refs = 1;
185 4           git_strarray_dispose(&ref_names);
186 4           return 0;
187              
188             on_error:
189 0           git_vector_free(&t->refs);
190 0           git_strarray_dispose(&ref_names);
191 4           return -1;
192             }
193              
194             /*
195             * Try to open the url as a git directory. The direction doesn't
196             * matter in this case because we're calculating the heads ourselves.
197             */
198 4           static int local_connect(
199             git_transport *transport,
200             const char *url,
201             int direction,
202             const git_remote_connect_options *connect_opts)
203             {
204             git_repository *repo;
205             int error;
206 4           transport_local *t = (transport_local *)transport;
207             const char *path;
208 4           git_str buf = GIT_STR_INIT;
209              
210 4 50         if (t->connected)
211 0           return 0;
212              
213 4 50         if (git_remote_connect_options_normalize(&t->connect_opts, t->owner->repo, connect_opts) < 0)
214 0           return -1;
215              
216 4           free_heads(&t->refs);
217              
218 4           t->url = git__strdup(url);
219 4 50         GIT_ERROR_CHECK_ALLOC(t->url);
220 4           t->direction = direction;
221              
222             /* 'url' may be a url or path; convert to a path */
223 4 50         if ((error = git_fs_path_from_url_or_path(&buf, url)) < 0) {
224 0           git_str_dispose(&buf);
225 0           return error;
226             }
227 4           path = git_str_cstr(&buf);
228              
229 4           error = git_repository_open(&repo, path);
230              
231 4           git_str_dispose(&buf);
232              
233 4 50         if (error < 0)
234 0           return -1;
235              
236 4           t->repo = repo;
237              
238 4 50         if (store_refs(t) < 0)
239 0           return -1;
240              
241 4           t->connected = 1;
242              
243 4           return 0;
244             }
245              
246 0           static int local_set_connect_opts(
247             git_transport *transport,
248             const git_remote_connect_options *connect_opts)
249             {
250 0           transport_local *t = (transport_local *)transport;
251              
252 0 0         if (!t->connected) {
253 0           git_error_set(GIT_ERROR_NET, "cannot reconfigure a transport that is not connected");
254 0           return -1;
255             }
256              
257 0           return git_remote_connect_options_normalize(&t->connect_opts, t->owner->repo, connect_opts);
258             }
259              
260 0           static int local_capabilities(unsigned int *capabilities, git_transport *transport)
261             {
262 0           GIT_UNUSED(transport);
263              
264 0           *capabilities = GIT_REMOTE_CAPABILITY_TIP_OID |
265             GIT_REMOTE_CAPABILITY_REACHABLE_OID;
266 0           return 0;
267             }
268              
269 2           static int local_ls(const git_remote_head ***out, size_t *size, git_transport *transport)
270             {
271 2           transport_local *t = (transport_local *)transport;
272              
273 2 50         if (!t->have_refs) {
274 0           git_error_set(GIT_ERROR_NET, "the transport has not yet loaded the refs");
275 0           return -1;
276             }
277              
278 2           *out = (const git_remote_head **)t->refs.contents;
279 2           *size = t->refs.length;
280              
281 2           return 0;
282             }
283              
284 0           static int local_negotiate_fetch(
285             git_transport *transport,
286             git_repository *repo,
287             const git_remote_head * const *refs,
288             size_t count)
289             {
290 0           transport_local *t = (transport_local*)transport;
291             git_remote_head *rhead;
292             unsigned int i;
293              
294 0           GIT_UNUSED(refs);
295 0           GIT_UNUSED(count);
296              
297             /* Fill in the loids */
298 0 0         git_vector_foreach(&t->refs, i, rhead) {
299             git_object *obj;
300              
301 0           int error = git_revparse_single(&obj, repo, rhead->name);
302 0 0         if (!error)
303 0           git_oid_cpy(&rhead->loid, git_object_id(obj));
304 0 0         else if (error != GIT_ENOTFOUND)
305 0           return error;
306             else
307 0           git_error_clear();
308 0           git_object_free(obj);
309             }
310              
311 0           return 0;
312             }
313              
314 2           static int local_push_update_remote_ref(
315             git_repository *remote_repo,
316             const char *lref,
317             const char *rref,
318             git_oid *loid,
319             git_oid *roid)
320             {
321             int error;
322 2           git_reference *remote_ref = NULL;
323              
324             /* check for lhs, if it's empty it means to delete */
325 2 50         if (lref[0] != '\0') {
326             /* Create or update a ref */
327 2           error = git_reference_create(NULL, remote_repo, rref, loid,
328 2           !git_oid_is_zero(roid), NULL);
329             } else {
330             /* Delete a ref */
331 0 0         if ((error = git_reference_lookup(&remote_ref, remote_repo, rref)) < 0) {
332 0 0         if (error == GIT_ENOTFOUND)
333 0           error = 0;
334 0           return error;
335             }
336              
337 0           error = git_reference_delete(remote_ref);
338 0           git_reference_free(remote_ref);
339             }
340              
341 2           return error;
342             }
343              
344 36           static int transfer_to_push_transfer(const git_indexer_progress *stats, void *payload)
345             {
346 36           const git_remote_callbacks *cbs = payload;
347              
348 36 50         if (!cbs || !cbs->push_transfer_progress)
    50          
349 36           return 0;
350              
351 0           return cbs->push_transfer_progress(stats->received_objects, stats->total_objects, stats->received_bytes,
352             cbs->payload);
353             }
354              
355 2           static int local_push(
356             git_transport *transport,
357             git_push *push)
358             {
359 2           transport_local *t = (transport_local *)transport;
360 2           git_remote_callbacks *cbs = &t->connect_opts.callbacks;
361 2           git_repository *remote_repo = NULL;
362             push_spec *spec;
363 2           char *url = NULL;
364             const char *path;
365 2           git_str buf = GIT_STR_INIT, odb_path = GIT_STR_INIT;
366             int error;
367             size_t j;
368              
369             /* 'push->remote->url' may be a url or path; convert to a path */
370 2 50         if ((error = git_fs_path_from_url_or_path(&buf, push->remote->url)) < 0) {
371 0           git_str_dispose(&buf);
372 0           return error;
373             }
374 2           path = git_str_cstr(&buf);
375              
376 2           error = git_repository_open(&remote_repo, path);
377              
378 2           git_str_dispose(&buf);
379              
380 2 50         if (error < 0)
381 0           return error;
382              
383             /* We don't currently support pushing locally to non-bare repos. Proper
384             non-bare repo push support would require checking configs to see if
385             we should override the default 'don't let this happen' behavior.
386              
387             Note that this is only an issue when pushing to the current branch,
388             but we forbid all pushes just in case */
389 2 50         if (!remote_repo->is_bare) {
390 0           error = GIT_EBAREREPO;
391 0           git_error_set(GIT_ERROR_INVALID, "local push doesn't (yet) support pushing to non-bare repos.");
392 0           goto on_error;
393             }
394              
395 2 50         if ((error = git_repository__item_path(&odb_path, remote_repo, GIT_REPOSITORY_ITEM_OBJECTS)) < 0
396 2 50         || (error = git_str_joinpath(&odb_path, odb_path.ptr, "pack")) < 0)
397             goto on_error;
398              
399 2           error = git_packbuilder_write(push->pb, odb_path.ptr, 0, transfer_to_push_transfer, (void *) cbs);
400 2           git_str_dispose(&odb_path);
401              
402 2 50         if (error < 0)
403 0           goto on_error;
404              
405 2           push->unpack_ok = 1;
406              
407 4 100         git_vector_foreach(&push->specs, j, spec) {
408             push_status *status;
409             const git_error *last;
410 2           char *ref = spec->refspec.dst;
411              
412 2           status = git__calloc(1, sizeof(push_status));
413 2 50         if (!status)
414 0           goto on_error;
415              
416 2           status->ref = git__strdup(ref);
417 2 50         if (!status->ref) {
418 0           git_push_status_free(status);
419 0           goto on_error;
420             }
421              
422 2           error = local_push_update_remote_ref(remote_repo, spec->refspec.src, spec->refspec.dst,
423             &spec->loid, &spec->roid);
424              
425 2           switch (error) {
426             case GIT_OK:
427 2           break;
428             case GIT_EINVALIDSPEC:
429 0           status->msg = git__strdup("funny refname");
430 0           break;
431             case GIT_ENOTFOUND:
432 0           status->msg = git__strdup("Remote branch not found to delete");
433 0           break;
434             default:
435 0           last = git_error_last();
436              
437 0 0         if (last && last->message)
    0          
438 0           status->msg = git__strdup(last->message);
439             else
440 0           status->msg = git__strdup("Unspecified error encountered");
441 0           break;
442             }
443              
444             /* failed to allocate memory for a status message */
445 2 50         if (error < 0 && !status->msg) {
    0          
446 0           git_push_status_free(status);
447 0           goto on_error;
448             }
449              
450             /* failed to insert the ref update status */
451 2 50         if ((error = git_vector_insert(&push->status, status)) < 0) {
452 0           git_push_status_free(status);
453 0           goto on_error;
454             }
455             }
456              
457 2 50         if (push->specs.length) {
458 2           url = git__strdup(t->url);
459              
460 4 50         if (!url || t->parent.close(&t->parent) < 0 ||
461 2           t->parent.connect(&t->parent, url,
462             GIT_DIRECTION_PUSH, NULL))
463             goto on_error;
464             }
465              
466 2           error = 0;
467              
468             on_error:
469 2           git_repository_free(remote_repo);
470 2           git__free(url);
471              
472 2           return error;
473             }
474              
475             typedef struct foreach_data {
476             git_indexer_progress *stats;
477             git_indexer_progress_cb progress_cb;
478             void *progress_payload;
479             git_odb_writepack *writepack;
480             } foreach_data;
481              
482 0           static int foreach_cb(void *buf, size_t len, void *payload)
483             {
484 0           foreach_data *data = (foreach_data*)payload;
485              
486 0           data->stats->received_bytes += len;
487 0           return data->writepack->append(data->writepack, buf, len, data->stats);
488             }
489              
490             static const char *counting_objects_fmt = "Counting objects %d\r";
491             static const char *compressing_objects_fmt = "Compressing objects: %.0f%% (%d/%d)";
492              
493 0           static int local_counting(int stage, unsigned int current, unsigned int total, void *payload)
494             {
495 0           git_str progress_info = GIT_STR_INIT;
496 0           transport_local *t = payload;
497             int error;
498              
499 0 0         if (!t->connect_opts.callbacks.sideband_progress)
500 0           return 0;
501              
502 0 0         if (stage == GIT_PACKBUILDER_ADDING_OBJECTS) {
503 0           git_str_printf(&progress_info, counting_objects_fmt, current);
504 0 0         } else if (stage == GIT_PACKBUILDER_DELTAFICATION) {
505 0           float perc = (((float) current) / total) * 100;
506 0           git_str_printf(&progress_info, compressing_objects_fmt, perc, current, total);
507 0 0         if (current == total)
508 0           git_str_printf(&progress_info, ", done\n");
509             else
510 0           git_str_putc(&progress_info, '\r');
511              
512             }
513              
514 0 0         if (git_str_oom(&progress_info))
515 0           return -1;
516              
517 0 0         if (progress_info.size > INT_MAX) {
518 0           git_error_set(GIT_ERROR_NET, "remote sent overly large progress data");
519 0           git_str_dispose(&progress_info);
520 0           return -1;
521             }
522              
523              
524 0           error = t->connect_opts.callbacks.sideband_progress(
525 0           progress_info.ptr,
526 0           (int)progress_info.size,
527             t->connect_opts.callbacks.payload);
528              
529 0           git_str_dispose(&progress_info);
530 0           return error;
531             }
532              
533 0           static int foreach_reference_cb(git_reference *reference, void *payload)
534             {
535 0           git_revwalk *walk = (git_revwalk *)payload;
536             int error;
537              
538 0 0         if (git_reference_type(reference) != GIT_REFERENCE_DIRECT) {
539 0           git_reference_free(reference);
540 0           return 0;
541             }
542              
543 0           error = git_revwalk_hide(walk, git_reference_target(reference));
544             /* The reference is in the local repository, so the target may not
545             * exist on the remote. It also may not be a commit. */
546 0 0         if (error == GIT_ENOTFOUND || error == GIT_ERROR_INVALID) {
    0          
547 0           git_error_clear();
548 0           error = 0;
549             }
550              
551 0           git_reference_free(reference);
552              
553 0           return error;
554             }
555              
556 0           static int local_download_pack(
557             git_transport *transport,
558             git_repository *repo,
559             git_indexer_progress *stats)
560             {
561 0           transport_local *t = (transport_local*)transport;
562 0           git_revwalk *walk = NULL;
563             git_remote_head *rhead;
564             unsigned int i;
565 0           int error = -1;
566 0           git_packbuilder *pack = NULL;
567 0           git_odb_writepack *writepack = NULL;
568 0           git_odb *odb = NULL;
569 0           git_str progress_info = GIT_STR_INIT;
570 0           foreach_data data = {0};
571              
572 0 0         if ((error = git_revwalk_new(&walk, t->repo)) < 0)
573 0           goto cleanup;
574              
575 0           git_revwalk_sorting(walk, GIT_SORT_TIME);
576              
577 0 0         if ((error = git_packbuilder_new(&pack, t->repo)) < 0)
578 0           goto cleanup;
579              
580 0           git_packbuilder_set_callbacks(pack, local_counting, t);
581              
582 0           stats->total_objects = 0;
583 0           stats->indexed_objects = 0;
584 0           stats->received_objects = 0;
585 0           stats->received_bytes = 0;
586              
587 0 0         git_vector_foreach(&t->refs, i, rhead) {
588             git_object *obj;
589 0 0         if ((error = git_object_lookup(&obj, t->repo, &rhead->oid, GIT_OBJECT_ANY)) < 0)
590 0           goto cleanup;
591              
592 0 0         if (git_object_type(obj) == GIT_OBJECT_COMMIT) {
593             /* Revwalker includes only wanted commits */
594 0           error = git_revwalk_push(walk, &rhead->oid);
595             } else {
596             /* Tag or some other wanted object. Add it on its own */
597 0           error = git_packbuilder_insert_recur(pack, &rhead->oid, rhead->name);
598             }
599 0           git_object_free(obj);
600 0 0         if (error < 0)
601 0           goto cleanup;
602             }
603              
604 0 0         if ((error = git_reference_foreach(repo, foreach_reference_cb, walk)))
605 0           goto cleanup;
606              
607 0 0         if ((error = git_packbuilder_insert_walk(pack, walk)))
608 0           goto cleanup;
609              
610 0 0         if (t->connect_opts.callbacks.sideband_progress) {
611 0 0         if ((error = git_str_printf(
612             &progress_info,
613             counting_objects_fmt,
614 0 0         git_packbuilder_object_count(pack))) < 0 ||
615 0           (error = t->connect_opts.callbacks.sideband_progress(
616 0           progress_info.ptr,
617 0           (int)progress_info.size,
618             t->connect_opts.callbacks.payload)) < 0)
619             goto cleanup;
620             }
621              
622             /* Walk the objects, building a packfile */
623 0 0         if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
624 0           goto cleanup;
625              
626             /* One last one with the newline */
627 0 0         if (t->connect_opts.callbacks.sideband_progress) {
628 0           git_str_clear(&progress_info);
629              
630 0 0         if ((error = git_str_printf(
631             &progress_info,
632             counting_objects_fmt,
633 0 0         git_packbuilder_object_count(pack))) < 0 ||
634 0 0         (error = git_str_putc(&progress_info, '\n')) < 0 ||
635 0           (error = t->connect_opts.callbacks.sideband_progress(
636 0           progress_info.ptr,
637 0           (int)progress_info.size,
638             t->connect_opts.callbacks.payload)) < 0)
639             goto cleanup;
640             }
641              
642 0 0         if ((error = git_odb_write_pack(
643             &writepack,
644             odb,
645             t->connect_opts.callbacks.transfer_progress,
646             t->connect_opts.callbacks.payload)) < 0)
647 0           goto cleanup;
648              
649             /* Write the data to the ODB */
650 0           data.stats = stats;
651 0           data.progress_cb = t->connect_opts.callbacks.transfer_progress;
652 0           data.progress_payload = t->connect_opts.callbacks.payload;
653 0           data.writepack = writepack;
654              
655             /* autodetect */
656 0           git_packbuilder_set_threads(pack, 0);
657              
658 0 0         if ((error = git_packbuilder_foreach(pack, foreach_cb, &data)) != 0)
659 0           goto cleanup;
660              
661 0           error = writepack->commit(writepack, stats);
662              
663             cleanup:
664 0 0         if (writepack) writepack->free(writepack);
665 0           git_str_dispose(&progress_info);
666 0           git_packbuilder_free(pack);
667 0           git_revwalk_free(walk);
668 0           return error;
669             }
670              
671 4           static int local_is_connected(git_transport *transport)
672             {
673 4           transport_local *t = (transport_local *)transport;
674              
675 4           return t->connected;
676             }
677              
678 0           static void local_cancel(git_transport *transport)
679             {
680 0           transport_local *t = (transport_local *)transport;
681              
682 0           git_atomic32_set(&t->cancelled, 1);
683 0           }
684              
685 6           static int local_close(git_transport *transport)
686             {
687 6           transport_local *t = (transport_local *)transport;
688              
689 6           t->connected = 0;
690              
691 6 100         if (t->repo) {
692 4           git_repository_free(t->repo);
693 4           t->repo = NULL;
694             }
695              
696 6 100         if (t->url) {
697 4           git__free(t->url);
698 4           t->url = NULL;
699             }
700              
701 6           return 0;
702             }
703              
704 2           static void local_free(git_transport *transport)
705             {
706 2           transport_local *t = (transport_local *)transport;
707              
708 2           free_heads(&t->refs);
709              
710             /* Close the transport, if it's still open. */
711 2           local_close(transport);
712              
713             /* Free the transport */
714 2           git__free(t);
715 2           }
716              
717             /**************
718             * Public API *
719             **************/
720              
721 2           int git_transport_local(git_transport **out, git_remote *owner, void *param)
722             {
723             int error;
724             transport_local *t;
725              
726 2           GIT_UNUSED(param);
727              
728 2           t = git__calloc(1, sizeof(transport_local));
729 2 50         GIT_ERROR_CHECK_ALLOC(t);
730              
731 2           t->parent.version = GIT_TRANSPORT_VERSION;
732 2           t->parent.connect = local_connect;
733 2           t->parent.set_connect_opts = local_set_connect_opts;
734 2           t->parent.capabilities = local_capabilities;
735 2           t->parent.negotiate_fetch = local_negotiate_fetch;
736 2           t->parent.download_pack = local_download_pack;
737 2           t->parent.push = local_push;
738 2           t->parent.close = local_close;
739 2           t->parent.free = local_free;
740 2           t->parent.ls = local_ls;
741 2           t->parent.is_connected = local_is_connected;
742 2           t->parent.cancel = local_cancel;
743              
744 2 50         if ((error = git_vector_init(&t->refs, 0, NULL)) < 0) {
745 0           git__free(t);
746 0           return error;
747             }
748              
749 2           t->owner = owner;
750              
751 2           *out = (git_transport *) t;
752              
753 2           return 0;
754             }