]> git.proxmox.com Git - libgit2.git/blame - src/transports/local.c
Merge pull request #2772 from ethomson/case_changing_rename
[libgit2.git] / src / transports / local.c
CommitLineData
bb742ede 1/*
359fc2d2 2 * Copyright (C) the libgit2 contributors. All rights reserved.
bb742ede
VM
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 */
8f866dae
CMN
7#include "common.h"
8#include "git2/types.h"
8f866dae
CMN
9#include "git2/net.h"
10#include "git2/repository.h"
d6258deb
CMN
11#include "git2/object.h"
12#include "git2/tag.h"
41fb1ca0 13#include "git2/transport.h"
505da062
BS
14#include "git2/revwalk.h"
15#include "git2/odb_backend.h"
16#include "git2/pack.h"
17#include "git2/commit.h"
18#include "git2/revparse.h"
20858f6e 19#include "git2/push.h"
505da062
BS
20#include "pack-objects.h"
21#include "refs.h"
84dd3820 22#include "posix.h"
e2580375 23#include "path.h"
24#include "buffer.h"
505da062
BS
25#include "repository.h"
26#include "odb.h"
20858f6e 27#include "push.h"
28#include "remote.h"
8f866dae 29
0a9a38e5 30typedef struct {
4e913309 31 git_transport parent;
613d5eb9 32 git_remote *owner;
41fb1ca0
PK
33 char *url;
34 int direction;
35 int flags;
36 git_atomic cancelled;
0a9a38e5 37 git_repository *repo;
4fd2bda9
CMN
38 git_transport_message_cb progress_cb;
39 git_transport_message_cb error_cb;
40 void *message_cb_payload;
41fb1ca0 41 git_vector refs;
67ba7d20
CMN
42 unsigned connected : 1,
43 have_refs : 1;
4e913309 44} transport_local;
0a9a38e5 45
4c4408c3
CMN
46static void free_head(git_remote_head *head)
47{
48 git__free(head->name);
49 git__free(head->symref_target);
50 git__free(head);
51}
52
f7fcb18f
CMN
53static void free_heads(git_vector *heads)
54{
55 git_remote_head *head;
56 size_t i;
57
58 git_vector_foreach(heads, i, head)
59 free_head(head);
60
61 git_vector_free(heads);
62}
63
d88d4311 64static int add_ref(transport_local *t, const char *name)
d6258deb 65{
d6258deb 66 const char peeled[] = "^{}";
04865aa0 67 git_reference *ref, *resolved;
d6258deb 68 git_remote_head *head;
04865aa0 69 git_oid obj_id;
a62053a0
CMN
70 git_object *obj = NULL, *target = NULL;
71 git_buf buf = GIT_BUF_INIT;
b524fe1a 72 int error;
d6258deb 73
04865aa0
CMN
74 if ((error = git_reference_lookup(&ref, t->repo, name)) < 0)
75 return error;
76
77 error = git_reference_resolve(&resolved, ref);
b524fe1a 78 if (error < 0) {
04865aa0 79 git_reference_free(ref);
2a2d1ab0 80 if (!strcmp(name, GIT_HEAD_FILE) && error == GIT_ENOTFOUND) {
25e0b157
RB
81 /* This is actually okay. Empty repos often have a HEAD that
82 * points to a nonexistent "refs/heads/master". */
b524fe1a
BS
83 giterr_clear();
84 return 0;
85 }
86 return error;
ad4b5beb
CMN
87 }
88
04865aa0
CMN
89 git_oid_cpy(&obj_id, git_reference_target(resolved));
90 git_reference_free(resolved);
91
25e0b157
RB
92 head = git__calloc(1, sizeof(git_remote_head));
93 GITERR_CHECK_ALLOC(head);
94
95 head->name = git__strdup(name);
96 GITERR_CHECK_ALLOC(head->name);
97
04865aa0
CMN
98 git_oid_cpy(&head->oid, &obj_id);
99
100 if (git_reference_type(ref) == GIT_REF_SYMBOLIC) {
101 head->symref_target = git__strdup(git_reference_symbolic_target(ref));
102 GITERR_CHECK_ALLOC(head->symref_target);
103 }
104 git_reference_free(ref);
25e0b157
RB
105
106 if ((error = git_vector_insert(&t->refs, head)) < 0) {
4c4408c3 107 free_head(head);
25e0b157 108 return error;
f201d613 109 }
d6258deb
CMN
110
111 /* If it's not a tag, we don't need to try to peel it */
112 if (git__prefixcmp(name, GIT_REFS_TAGS_DIR))
a62053a0 113 return 0;
d6258deb 114
25e0b157
RB
115 if ((error = git_object_lookup(&obj, t->repo, &head->oid, GIT_OBJ_ANY)) < 0)
116 return error;
d6258deb 117
db1f7e59 118 head = NULL;
119
20858f6e 120 /* If it's not an annotated tag, or if we're mocking
121 * git-receive-pack, just get out */
122 if (git_object_type(obj) != GIT_OBJ_TAG ||
123 t->direction != GIT_DIRECTION_FETCH) {
a62053a0
CMN
124 git_object_free(obj);
125 return 0;
126 }
d6258deb
CMN
127
128 /* And if it's a tag, peel it, and add it to the list */
6762fe08 129 head = git__calloc(1, sizeof(git_remote_head));
a62053a0 130 GITERR_CHECK_ALLOC(head);
25e0b157 131
a62053a0
CMN
132 if (git_buf_join(&buf, 0, name, peeled) < 0)
133 return -1;
a62053a0 134 head->name = git_buf_detach(&buf);
d6258deb 135
25e0b157
RB
136 if (!(error = git_tag_peel(&target, (git_tag *)obj))) {
137 git_oid_cpy(&head->oid, git_object_id(target));
79fd4230 138
25e0b157 139 if ((error = git_vector_insert(&t->refs, head)) < 0) {
4c4408c3 140 free_head(head);
25e0b157
RB
141 }
142 }
75abd2b9 143
45e79e37 144 git_object_free(obj);
a62053a0 145 git_object_free(target);
25e0b157
RB
146
147 return error;
d6258deb
CMN
148}
149
d88d4311 150static int store_refs(transport_local *t)
8f866dae 151{
edbaa63a
AS
152 size_t i;
153 git_remote_head *head;
d88d4311 154 git_strarray ref_names = {0};
d6258deb 155
d88d4311 156 assert(t);
7e305056 157
edbaa63a 158 if (git_reference_list(&ref_names, t->repo) < 0)
a62053a0 159 goto on_error;
d6258deb 160
edbaa63a
AS
161 /* Clear all heads we might have fetched in a previous connect */
162 git_vector_foreach(&t->refs, i, head) {
163 git__free(head->name);
164 git__free(head);
165 }
166
167 /* Clear the vector so we can reuse it */
168 git_vector_clear(&t->refs);
169
7e305056 170 /* Sort the references first */
d88d4311 171 git__tsort((void **)ref_names.strings, ref_names.count, &git__strcmp_cb);
7e305056 172
20858f6e 173 /* Add HEAD iff direction is fetch */
174 if (t->direction == GIT_DIRECTION_FETCH && add_ref(t, GIT_HEAD_FILE) < 0)
a62053a0 175 goto on_error;
7e305056 176
d88d4311 177 for (i = 0; i < ref_names.count; ++i) {
a62053a0
CMN
178 if (add_ref(t, ref_names.strings[i]) < 0)
179 goto on_error;
7e305056 180 }
d6258deb 181
67ba7d20 182 t->have_refs = 1;
a1515693 183 git_strarray_free(&ref_names);
a62053a0
CMN
184 return 0;
185
186on_error:
41fb1ca0 187 git_vector_free(&t->refs);
d88d4311 188 git_strarray_free(&ref_names);
a62053a0 189 return -1;
d88d4311 190}
0a9a38e5 191
d88d4311
VM
192/*
193 * Try to open the url as a git directory. The direction doesn't
04865aa0 194 * matter in this case because we're calculating the heads ourselves.
d88d4311 195 */
091361f5
PK
196static int local_connect(
197 git_transport *transport,
198 const char *url,
199 git_cred_acquire_cb cred_acquire_cb,
59bccf33 200 void *cred_acquire_payload,
091361f5 201 int direction, int flags)
d88d4311
VM
202{
203 git_repository *repo;
204 int error;
205 transport_local *t = (transport_local *) transport;
206 const char *path;
e2580375 207 git_buf buf = GIT_BUF_INIT;
208
091361f5 209 GIT_UNUSED(cred_acquire_cb);
59bccf33 210 GIT_UNUSED(cred_acquire_payload);
091361f5 211
5187b609
CMN
212 if (t->connected)
213 return 0;
214
f7fcb18f
CMN
215 free_heads(&t->refs);
216
41fb1ca0
PK
217 t->url = git__strdup(url);
218 GITERR_CHECK_ALLOC(t->url);
219 t->direction = direction;
220 t->flags = flags;
d88d4311 221
8bf476ac 222 /* 'url' may be a url or path; convert to a path */
18d7896c 223 if ((error = git_path_from_url_or_path(&buf, url)) < 0) {
8bf476ac
GD
224 git_buf_free(&buf);
225 return error;
a62053a0 226 }
8bf476ac 227 path = git_buf_cstr(&buf);
d88d4311
VM
228
229 error = git_repository_open(&repo, path);
e2580375 230
231 git_buf_free(&buf);
232
a62053a0
CMN
233 if (error < 0)
234 return -1;
d88d4311 235
db1f7e59 236 t->repo = repo;
237
a62053a0
CMN
238 if (store_refs(t) < 0)
239 return -1;
d88d4311 240
41fb1ca0
PK
241 t->connected = 1;
242
243 return 0;
244}
245
359dce72 246static int local_ls(const git_remote_head ***out, size_t *size, git_transport *transport)
41fb1ca0
PK
247{
248 transport_local *t = (transport_local *)transport;
41fb1ca0 249
67ba7d20
CMN
250 if (!t->have_refs) {
251 giterr_set(GITERR_NET, "The transport has not yet loaded the refs");
41fb1ca0
PK
252 return -1;
253 }
254
25e0b157 255 *out = (const git_remote_head **)t->refs.contents;
359dce72 256 *size = t->refs.length;
d88d4311 257
a62053a0 258 return 0;
d6258deb
CMN
259}
260
41fb1ca0
PK
261static int local_negotiate_fetch(
262 git_transport *transport,
263 git_repository *repo,
505da062
BS
264 const git_remote_head * const *refs,
265 size_t count)
a167002f 266{
505da062
BS
267 transport_local *t = (transport_local*)transport;
268 git_remote_head *rhead;
269 unsigned int i;
270
41fb1ca0
PK
271 GIT_UNUSED(refs);
272 GIT_UNUSED(count);
a167002f 273
505da062
BS
274 /* Fill in the loids */
275 git_vector_foreach(&t->refs, i, rhead) {
276 git_object *obj;
277
278 int error = git_revparse_single(&obj, repo, rhead->name);
279 if (!error)
280 git_oid_cpy(&rhead->loid, git_object_id(obj));
281 else if (error != GIT_ENOTFOUND)
282 return error;
9f77b3f6
RB
283 else
284 giterr_clear();
3dee3655 285 git_object_free(obj);
505da062
BS
286 }
287
288 return 0;
289}
290
20858f6e 291static int local_push_copy_object(
292 git_odb *local_odb,
293 git_odb *remote_odb,
294 git_pobject *obj)
295{
296 int error = 0;
297 git_odb_object *odb_obj = NULL;
298 git_odb_stream *odb_stream;
299 size_t odb_obj_size;
300 git_otype odb_obj_type;
301 git_oid remote_odb_obj_oid;
302
303 /* Object already exists in the remote ODB; do nothing and return 0*/
304 if (git_odb_exists(remote_odb, &obj->id))
305 return 0;
306
307 if ((error = git_odb_read(&odb_obj, local_odb, &obj->id)) < 0)
308 return error;
309
310 odb_obj_size = git_odb_object_size(odb_obj);
311 odb_obj_type = git_odb_object_type(odb_obj);
312
313 if ((error = git_odb_open_wstream(&odb_stream, remote_odb,
314 odb_obj_size, odb_obj_type)) < 0)
315 goto on_error;
316
376e6c9f 317 if (git_odb_stream_write(odb_stream, (char *)git_odb_object_data(odb_obj),
20858f6e 318 odb_obj_size) < 0 ||
376e6c9f 319 git_odb_stream_finalize_write(&remote_odb_obj_oid, odb_stream) < 0) {
20858f6e 320 error = -1;
b7f167da 321 } else if (git_oid__cmp(&obj->id, &remote_odb_obj_oid) != 0) {
20858f6e 322 giterr_set(GITERR_ODB, "Error when writing object to remote odb "
323 "during local push operation. Remote odb object oid does not "
324 "match local oid.");
325 error = -1;
326 }
327
376e6c9f 328 git_odb_stream_free(odb_stream);
20858f6e 329
330on_error:
331 git_odb_object_free(odb_obj);
332 return error;
333}
334
335static int local_push_update_remote_ref(
336 git_repository *remote_repo,
337 const char *lref,
338 const char *rref,
339 git_oid *loid,
340 git_oid *roid)
341{
342 int error;
343 git_reference *remote_ref = NULL;
344
d5c84f67
CMN
345 /* check for lhs, if it's empty it means to delete */
346 if (lref[0] != '\0') {
20858f6e 347 /* Create or update a ref */
d5c84f67
CMN
348 error = git_reference_create(NULL, remote_repo, rref, loid,
349 !git_oid_iszero(roid), NULL, NULL);
20858f6e 350 } else {
351 /* Delete a ref */
352 if ((error = git_reference_lookup(&remote_ref, remote_repo, rref)) < 0) {
353 if (error == GIT_ENOTFOUND)
354 error = 0;
355 return error;
356 }
357
d5c84f67 358 error = git_reference_delete(remote_ref);
20858f6e 359 git_reference_free(remote_ref);
360 }
361
d5c84f67 362 return error;
20858f6e 363}
364
365static int local_push(
366 git_transport *transport,
367 git_push *push)
368{
369 transport_local *t = (transport_local *)transport;
370 git_odb *remote_odb = NULL;
371 git_odb *local_odb = NULL;
372 git_repository *remote_repo = NULL;
373 push_spec *spec;
374 char *url = NULL;
4e974c97
GD
375 const char *path;
376 git_buf buf = GIT_BUF_INIT;
20858f6e 377 int error;
378 unsigned int i;
379 size_t j;
380
8bf476ac 381 /* 'push->remote->url' may be a url or path; convert to a path */
18d7896c 382 if ((error = git_path_from_url_or_path(&buf, push->remote->url)) < 0) {
8bf476ac
GD
383 git_buf_free(&buf);
384 return error;
4e974c97 385 }
8bf476ac 386 path = git_buf_cstr(&buf);
4e974c97
GD
387
388 error = git_repository_open(&remote_repo, path);
389
390 git_buf_free(&buf);
391
392 if (error < 0)
20858f6e 393 return error;
394
1fed6b07 395 /* We don't currently support pushing locally to non-bare repos. Proper
20858f6e 396 non-bare repo push support would require checking configs to see if
397 we should override the default 'don't let this happen' behavior */
398 if (!remote_repo->is_bare) {
f42d546c
BS
399 error = GIT_EBAREREPO;
400 giterr_set(GITERR_INVALID, "Local push doesn't (yet) support pushing to non-bare repos.");
20858f6e 401 goto on_error;
402 }
403
404 if ((error = git_repository_odb__weakptr(&remote_odb, remote_repo)) < 0 ||
405 (error = git_repository_odb__weakptr(&local_odb, push->repo)) < 0)
406 goto on_error;
407
408 for (i = 0; i < push->pb->nr_objects; i++) {
409 if ((error = local_push_copy_object(local_odb, remote_odb,
410 &push->pb->object_list[i])) < 0)
411 goto on_error;
412 }
413
414 push->unpack_ok = 1;
415
416 git_vector_foreach(&push->specs, j, spec) {
417 push_status *status;
418 const git_error *last;
aad638f3 419 char *ref = spec->refspec.dst;
20858f6e 420
421 status = git__calloc(sizeof(push_status), 1);
422 if (!status)
423 goto on_error;
424
425 status->ref = git__strdup(ref);
426 if (!status->ref) {
427 git_push_status_free(status);
428 goto on_error;
429 }
430
aad638f3 431 error = local_push_update_remote_ref(remote_repo, spec->refspec.src, spec->refspec.dst,
20858f6e 432 &spec->loid, &spec->roid);
433
434 switch (error) {
435 case GIT_OK:
436 break;
437 case GIT_EINVALIDSPEC:
438 status->msg = git__strdup("funny refname");
439 break;
440 case GIT_ENOTFOUND:
441 status->msg = git__strdup("Remote branch not found to delete");
442 break;
443 default:
444 last = giterr_last();
445
446 if (last && last->message)
447 status->msg = git__strdup(last->message);
448 else
449 status->msg = git__strdup("Unspecified error encountered");
450 break;
451 }
452
453 /* failed to allocate memory for a status message */
454 if (error < 0 && !status->msg) {
455 git_push_status_free(status);
456 goto on_error;
457 }
458
459 /* failed to insert the ref update status */
460 if ((error = git_vector_insert(&push->status, status)) < 0) {
461 git_push_status_free(status);
462 goto on_error;
463 }
464 }
465
466 if (push->specs.length) {
467 int flags = t->flags;
468 url = git__strdup(t->url);
469
470 if (!url || t->parent.close(&t->parent) < 0 ||
471 t->parent.connect(&t->parent, url,
e3c131c5 472 push->remote->callbacks.credentials, NULL, GIT_DIRECTION_PUSH, flags))
20858f6e 473 goto on_error;
474 }
475
476 error = 0;
477
478on_error:
479 git_repository_free(remote_repo);
480 git__free(url);
481
482 return error;
483}
484
505da062
BS
485typedef struct foreach_data {
486 git_transfer_progress *stats;
48e60ae7 487 git_transfer_progress_cb progress_cb;
505da062
BS
488 void *progress_payload;
489 git_odb_writepack *writepack;
490} foreach_data;
491
492static int foreach_cb(void *buf, size_t len, void *payload)
493{
494 foreach_data *data = (foreach_data*)payload;
495
496 data->stats->received_bytes += len;
a6154f21 497 return data->writepack->append(data->writepack, buf, len, data->stats);
505da062
BS
498}
499
4fd2bda9
CMN
500static const char *counting_objects_fmt = "Counting objects %d\r";
501
505da062
BS
502static int local_download_pack(
503 git_transport *transport,
504 git_repository *repo,
505 git_transfer_progress *stats,
48e60ae7 506 git_transfer_progress_cb progress_cb,
505da062
BS
507 void *progress_payload)
508{
509 transport_local *t = (transport_local*)transport;
510 git_revwalk *walk = NULL;
511 git_remote_head *rhead;
512 unsigned int i;
513 int error = -1;
514 git_oid oid;
515 git_packbuilder *pack = NULL;
516 git_odb_writepack *writepack = NULL;
90207709 517 git_odb *odb = NULL;
4fd2bda9 518 git_buf progress_info = GIT_BUF_INIT;
505da062
BS
519
520 if ((error = git_revwalk_new(&walk, t->repo)) < 0)
521 goto cleanup;
522 git_revwalk_sorting(walk, GIT_SORT_TIME);
523
524 if ((error = git_packbuilder_new(&pack, t->repo)) < 0)
525 goto cleanup;
526
527 stats->total_objects = 0;
528 stats->indexed_objects = 0;
529 stats->received_objects = 0;
530 stats->received_bytes = 0;
531
532 git_vector_foreach(&t->refs, i, rhead) {
533 git_object *obj;
534 if ((error = git_object_lookup(&obj, t->repo, &rhead->oid, GIT_OBJ_ANY)) < 0)
535 goto cleanup;
536
537 if (git_object_type(obj) == GIT_OBJ_COMMIT) {
538 /* Revwalker includes only wanted commits */
539 error = git_revwalk_push(walk, &rhead->oid);
540 if (!git_oid_iszero(&rhead->loid))
541 error = git_revwalk_hide(walk, &rhead->loid);
542 } else {
543 /* Tag or some other wanted object. Add it on its own */
505da062
BS
544 error = git_packbuilder_insert(pack, &rhead->oid, rhead->name);
545 }
0cb16fe9 546 git_object_free(obj);
505da062
BS
547 }
548
4fd2bda9
CMN
549 if ((error = git_buf_printf(&progress_info, counting_objects_fmt, git_packbuilder_object_count(pack))) < 0)
550 goto cleanup;
551
552 if (t->progress_cb &&
553 (error = t->progress_cb(git_buf_cstr(&progress_info), git_buf_len(&progress_info), t->message_cb_payload)) < 0)
554 goto cleanup;
555
505da062 556 /* Walk the objects, building a packfile */
90207709
BS
557 if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
558 goto cleanup;
505da062
BS
559
560 while ((error = git_revwalk_next(&oid, walk)) == 0) {
561 git_commit *commit;
562
90207709
BS
563 /* Skip commits we already have */
564 if (git_odb_exists(odb, &oid)) continue;
565
505da062 566 if (!git_object_lookup((git_object**)&commit, t->repo, &oid, GIT_OBJ_COMMIT)) {
cfbe4be3 567 const git_oid *tree_oid = git_commit_tree_id(commit);
505da062
BS
568
569 /* Add the commit and its tree */
570 if ((error = git_packbuilder_insert(pack, &oid, NULL)) < 0 ||
88183c19
FL
571 (error = git_packbuilder_insert_tree(pack, tree_oid)) < 0) {
572 git_commit_free(commit);
505da062 573 goto cleanup;
88183c19
FL
574 }
575
576 git_commit_free(commit);
4fd2bda9
CMN
577
578 git_buf_clear(&progress_info);
579 if ((error = git_buf_printf(&progress_info, counting_objects_fmt, git_packbuilder_object_count(pack))) < 0)
580 goto cleanup;
581
582 if (t->progress_cb &&
583 (error = t->progress_cb(git_buf_cstr(&progress_info), git_buf_len(&progress_info), t->message_cb_payload)) < 0)
584 goto cleanup;
585
505da062
BS
586 }
587 }
588
4fd2bda9
CMN
589 /* One last one with the newline */
590 git_buf_clear(&progress_info);
591 git_buf_printf(&progress_info, counting_objects_fmt, git_packbuilder_object_count(pack));
592 if ((error = git_buf_putc(&progress_info, '\n')) < 0)
593 goto cleanup;
594
595 if (t->progress_cb &&
596 (error = t->progress_cb(git_buf_cstr(&progress_info), git_buf_len(&progress_info), t->message_cb_payload)) < 0)
597 goto cleanup;
598
25e0b157 599 if ((error = git_odb_write_pack(&writepack, odb, progress_cb, progress_payload)) != 0)
90207709 600 goto cleanup;
505da062
BS
601
602 /* Write the data to the ODB */
603 {
604 foreach_data data = {0};
605 data.stats = stats;
606 data.progress_cb = progress_cb;
607 data.progress_payload = progress_payload;
608 data.writepack = writepack;
609
25e0b157 610 if ((error = git_packbuilder_foreach(pack, foreach_cb, &data)) != 0)
505da062
BS
611 goto cleanup;
612 }
613 error = writepack->commit(writepack, stats);
614
615cleanup:
616 if (writepack) writepack->free(writepack);
4fd2bda9 617 git_buf_free(&progress_info);
505da062
BS
618 git_packbuilder_free(pack);
619 git_revwalk_free(walk);
620 return error;
a167002f
MS
621}
622
4fd2bda9
CMN
623static int local_set_callbacks(
624 git_transport *transport,
625 git_transport_message_cb progress_cb,
626 git_transport_message_cb error_cb,
627 git_transport_certificate_check_cb certificate_check_cb,
628 void *message_cb_payload)
629{
630 transport_local *t = (transport_local *)transport;
631
632 GIT_UNUSED(certificate_check_cb);
633
634 t->progress_cb = progress_cb;
635 t->error_cb = error_cb;
636 t->message_cb_payload = message_cb_payload;
637
638 return 0;
639}
640
613d5eb9 641static int local_is_connected(git_transport *transport)
41fb1ca0
PK
642{
643 transport_local *t = (transport_local *)transport;
644
613d5eb9 645 return t->connected;
41fb1ca0
PK
646}
647
648static int local_read_flags(git_transport *transport, int *flags)
649{
650 transport_local *t = (transport_local *)transport;
651
652 *flags = t->flags;
653
654 return 0;
655}
656
657static void local_cancel(git_transport *transport)
658{
659 transport_local *t = (transport_local *)transport;
660
661 git_atomic_set(&t->cancelled, 1);
662}
663
854eccbb 664static int local_close(git_transport *transport)
d6258deb 665{
d88d4311
VM
666 transport_local *t = (transport_local *)transport;
667
41fb1ca0 668 t->connected = 0;
20858f6e 669
670 if (t->repo) {
671 git_repository_free(t->repo);
672 t->repo = NULL;
673 }
674
20858f6e 675 if (t->url) {
676 git__free(t->url);
677 t->url = NULL;
678 }
d88d4311 679
a62053a0 680 return 0;
8f866dae
CMN
681}
682
d6258deb
CMN
683static void local_free(git_transport *transport)
684{
20858f6e 685 transport_local *t = (transport_local *)transport;
9728cfde 686
f7fcb18f 687 free_heads(&t->refs);
edbaa63a 688
20858f6e 689 /* Close the transport, if it's still open. */
690 local_close(transport);
a2888919 691
20858f6e 692 /* Free the transport */
3286c408 693 git__free(t);
d6258deb
CMN
694}
695
8f866dae
CMN
696/**************
697 * Public API *
698 **************/
699
613d5eb9 700int git_transport_local(git_transport **out, git_remote *owner, void *param)
8f866dae 701{
4e913309
CMN
702 transport_local *t;
703
41fb1ca0
PK
704 GIT_UNUSED(param);
705
10711769 706 t = git__calloc(1, sizeof(transport_local));
a62053a0 707 GITERR_CHECK_ALLOC(t);
4e913309 708
10711769 709 t->parent.version = GIT_TRANSPORT_VERSION;
4fd2bda9 710 t->parent.set_callbacks = local_set_callbacks;
4e913309 711 t->parent.connect = local_connect;
a167002f 712 t->parent.negotiate_fetch = local_negotiate_fetch;
505da062 713 t->parent.download_pack = local_download_pack;
20858f6e 714 t->parent.push = local_push;
4e913309
CMN
715 t->parent.close = local_close;
716 t->parent.free = local_free;
41fb1ca0
PK
717 t->parent.ls = local_ls;
718 t->parent.is_connected = local_is_connected;
719 t->parent.read_flags = local_read_flags;
720 t->parent.cancel = local_cancel;
4e913309 721
edbaa63a 722 git_vector_init(&t->refs, 0, NULL);
613d5eb9
PK
723 t->owner = owner;
724
4e913309 725 *out = (git_transport *) t;
8f866dae 726
a62053a0 727 return 0;
8f866dae 728}