]> git.proxmox.com Git - libgit2.git/blame - src/transports/local.c
New upstream version 1.4.3+dfsg.1
[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 */
eae0bfdc 7
8f866dae 8#include "common.h"
eae0bfdc 9
e579e0f7
MB
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
8f866dae 20#include "git2/types.h"
8f866dae
CMN
21#include "git2/net.h"
22#include "git2/repository.h"
d6258deb
CMN
23#include "git2/object.h"
24#include "git2/tag.h"
41fb1ca0 25#include "git2/transport.h"
505da062
BS
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"
e579e0f7 31#include "git2/sys/remote.h"
8f866dae 32
0a9a38e5 33typedef struct {
4e913309 34 git_transport parent;
613d5eb9 35 git_remote *owner;
41fb1ca0
PK
36 char *url;
37 int direction;
c25aa7cd 38 git_atomic32 cancelled;
0a9a38e5 39 git_repository *repo;
e579e0f7 40 git_remote_connect_options connect_opts;
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 70 git_object *obj = NULL, *target = NULL;
e579e0f7 71 git_str buf = GIT_STR_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". */
ac3d33df 83 git_error_clear();
b524fe1a
BS
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 92 head = git__calloc(1, sizeof(git_remote_head));
ac3d33df 93 GIT_ERROR_CHECK_ALLOC(head);
25e0b157
RB
94
95 head->name = git__strdup(name);
ac3d33df 96 GIT_ERROR_CHECK_ALLOC(head->name);
25e0b157 97
04865aa0
CMN
98 git_oid_cpy(&head->oid, &obj_id);
99
ac3d33df 100 if (git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC) {
04865aa0 101 head->symref_target = git__strdup(git_reference_symbolic_target(ref));
ac3d33df 102 GIT_ERROR_CHECK_ALLOC(head->symref_target);
04865aa0
CMN
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
ac3d33df 115 if ((error = git_object_lookup(&obj, t->repo, &head->oid, GIT_OBJECT_ANY)) < 0)
25e0b157 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 */
ac3d33df 122 if (git_object_type(obj) != GIT_OBJECT_TAG ||
20858f6e 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));
ac3d33df 130 GIT_ERROR_CHECK_ALLOC(head);
25e0b157 131
e579e0f7 132 if (git_str_join(&buf, 0, name, peeled) < 0) {
6f73e026 133 free_head(head);
a62053a0 134 return -1;
6f73e026 135 }
e579e0f7 136 head->name = git_str_detach(&buf);
d6258deb 137
25e0b157
RB
138 if (!(error = git_tag_peel(&target, (git_tag *)obj))) {
139 git_oid_cpy(&head->oid, git_object_id(target));
79fd4230 140
25e0b157 141 if ((error = git_vector_insert(&t->refs, head)) < 0) {
4c4408c3 142 free_head(head);
25e0b157
RB
143 }
144 }
75abd2b9 145
45e79e37 146 git_object_free(obj);
a62053a0 147 git_object_free(target);
25e0b157
RB
148
149 return error;
d6258deb
CMN
150}
151
d88d4311 152static int store_refs(transport_local *t)
8f866dae 153{
edbaa63a
AS
154 size_t i;
155 git_remote_head *head;
d88d4311 156 git_strarray ref_names = {0};
d6258deb 157
c25aa7cd 158 GIT_ASSERT_ARG(t);
7e305056 159
edbaa63a 160 if (git_reference_list(&ref_names, t->repo) < 0)
a62053a0 161 goto on_error;
d6258deb 162
edbaa63a
AS
163 /* Clear all heads we might have fetched in a previous connect */
164 git_vector_foreach(&t->refs, i, head) {
165 git__free(head->name);
166 git__free(head);
167 }
168
169 /* Clear the vector so we can reuse it */
170 git_vector_clear(&t->refs);
171
7e305056 172 /* Sort the references first */
d88d4311 173 git__tsort((void **)ref_names.strings, ref_names.count, &git__strcmp_cb);
7e305056 174
20858f6e 175 /* Add HEAD iff direction is fetch */
176 if (t->direction == GIT_DIRECTION_FETCH && add_ref(t, GIT_HEAD_FILE) < 0)
a62053a0 177 goto on_error;
7e305056 178
d88d4311 179 for (i = 0; i < ref_names.count; ++i) {
a62053a0
CMN
180 if (add_ref(t, ref_names.strings[i]) < 0)
181 goto on_error;
7e305056 182 }
d6258deb 183
67ba7d20 184 t->have_refs = 1;
22a2d3d5 185 git_strarray_dispose(&ref_names);
a62053a0
CMN
186 return 0;
187
188on_error:
41fb1ca0 189 git_vector_free(&t->refs);
22a2d3d5 190 git_strarray_dispose(&ref_names);
a62053a0 191 return -1;
d88d4311 192}
0a9a38e5 193
d88d4311
VM
194/*
195 * Try to open the url as a git directory. The direction doesn't
04865aa0 196 * matter in this case because we're calculating the heads ourselves.
d88d4311 197 */
091361f5
PK
198static int local_connect(
199 git_transport *transport,
200 const char *url,
e579e0f7
MB
201 int direction,
202 const git_remote_connect_options *connect_opts)
d88d4311
VM
203{
204 git_repository *repo;
205 int error;
e579e0f7 206 transport_local *t = (transport_local *)transport;
d88d4311 207 const char *path;
e579e0f7 208 git_str buf = GIT_STR_INIT;
091361f5 209
5187b609
CMN
210 if (t->connected)
211 return 0;
212
e579e0f7
MB
213 if (git_remote_connect_options_normalize(&t->connect_opts, t->owner->repo, connect_opts) < 0)
214 return -1;
215
f7fcb18f
CMN
216 free_heads(&t->refs);
217
41fb1ca0 218 t->url = git__strdup(url);
ac3d33df 219 GIT_ERROR_CHECK_ALLOC(t->url);
41fb1ca0 220 t->direction = direction;
d88d4311 221
8bf476ac 222 /* 'url' may be a url or path; convert to a path */
e579e0f7
MB
223 if ((error = git_fs_path_from_url_or_path(&buf, url)) < 0) {
224 git_str_dispose(&buf);
8bf476ac 225 return error;
a62053a0 226 }
e579e0f7 227 path = git_str_cstr(&buf);
d88d4311
VM
228
229 error = git_repository_open(&repo, path);
e2580375 230
e579e0f7 231 git_str_dispose(&buf);
e2580375 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
e579e0f7
MB
246static int local_set_connect_opts(
247 git_transport *transport,
248 const git_remote_connect_options *connect_opts)
249{
250 transport_local *t = (transport_local *)transport;
251
252 if (!t->connected) {
253 git_error_set(GIT_ERROR_NET, "cannot reconfigure a transport that is not connected");
254 return -1;
255 }
256
257 return git_remote_connect_options_normalize(&t->connect_opts, t->owner->repo, connect_opts);
258}
259
260static int local_capabilities(unsigned int *capabilities, git_transport *transport)
261{
262 GIT_UNUSED(transport);
263
264 *capabilities = GIT_REMOTE_CAPABILITY_TIP_OID |
265 GIT_REMOTE_CAPABILITY_REACHABLE_OID;
266 return 0;
267}
268
359dce72 269static int local_ls(const git_remote_head ***out, size_t *size, git_transport *transport)
41fb1ca0
PK
270{
271 transport_local *t = (transport_local *)transport;
41fb1ca0 272
67ba7d20 273 if (!t->have_refs) {
ac3d33df 274 git_error_set(GIT_ERROR_NET, "the transport has not yet loaded the refs");
41fb1ca0
PK
275 return -1;
276 }
277
25e0b157 278 *out = (const git_remote_head **)t->refs.contents;
359dce72 279 *size = t->refs.length;
d88d4311 280
a62053a0 281 return 0;
d6258deb
CMN
282}
283
41fb1ca0
PK
284static int local_negotiate_fetch(
285 git_transport *transport,
286 git_repository *repo,
505da062
BS
287 const git_remote_head * const *refs,
288 size_t count)
a167002f 289{
505da062
BS
290 transport_local *t = (transport_local*)transport;
291 git_remote_head *rhead;
292 unsigned int i;
293
41fb1ca0
PK
294 GIT_UNUSED(refs);
295 GIT_UNUSED(count);
a167002f 296
505da062
BS
297 /* Fill in the loids */
298 git_vector_foreach(&t->refs, i, rhead) {
299 git_object *obj;
300
301 int error = git_revparse_single(&obj, repo, rhead->name);
302 if (!error)
303 git_oid_cpy(&rhead->loid, git_object_id(obj));
304 else if (error != GIT_ENOTFOUND)
305 return error;
9f77b3f6 306 else
ac3d33df 307 git_error_clear();
3dee3655 308 git_object_free(obj);
505da062
BS
309 }
310
311 return 0;
312}
313
20858f6e 314static 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 git_reference *remote_ref = NULL;
323
d5c84f67
CMN
324 /* check for lhs, if it's empty it means to delete */
325 if (lref[0] != '\0') {
20858f6e 326 /* Create or update a ref */
d5c84f67 327 error = git_reference_create(NULL, remote_repo, rref, loid,
22a2d3d5 328 !git_oid_is_zero(roid), NULL);
20858f6e 329 } else {
330 /* Delete a ref */
331 if ((error = git_reference_lookup(&remote_ref, remote_repo, rref)) < 0) {
332 if (error == GIT_ENOTFOUND)
333 error = 0;
334 return error;
335 }
336
d5c84f67 337 error = git_reference_delete(remote_ref);
20858f6e 338 git_reference_free(remote_ref);
339 }
340
d5c84f67 341 return error;
20858f6e 342}
343
22a2d3d5 344static int transfer_to_push_transfer(const git_indexer_progress *stats, void *payload)
4a5b781a
CMN
345{
346 const git_remote_callbacks *cbs = payload;
347
348 if (!cbs || !cbs->push_transfer_progress)
349 return 0;
350
351 return cbs->push_transfer_progress(stats->received_objects, stats->total_objects, stats->received_bytes,
352 cbs->payload);
353}
354
20858f6e 355static int local_push(
356 git_transport *transport,
e579e0f7 357 git_push *push)
20858f6e 358{
359 transport_local *t = (transport_local *)transport;
e579e0f7 360 git_remote_callbacks *cbs = &t->connect_opts.callbacks;
20858f6e 361 git_repository *remote_repo = NULL;
362 push_spec *spec;
363 char *url = NULL;
4e974c97 364 const char *path;
e579e0f7 365 git_str buf = GIT_STR_INIT, odb_path = GIT_STR_INIT;
20858f6e 366 int error;
20858f6e 367 size_t j;
368
8bf476ac 369 /* 'push->remote->url' may be a url or path; convert to a path */
e579e0f7
MB
370 if ((error = git_fs_path_from_url_or_path(&buf, push->remote->url)) < 0) {
371 git_str_dispose(&buf);
8bf476ac 372 return error;
4e974c97 373 }
e579e0f7 374 path = git_str_cstr(&buf);
4e974c97
GD
375
376 error = git_repository_open(&remote_repo, path);
377
e579e0f7 378 git_str_dispose(&buf);
4e974c97
GD
379
380 if (error < 0)
20858f6e 381 return error;
382
1fed6b07 383 /* We don't currently support pushing locally to non-bare repos. Proper
20858f6e 384 non-bare repo push support would require checking configs to see if
81c0fb08
CMN
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 */
20858f6e 389 if (!remote_repo->is_bare) {
f42d546c 390 error = GIT_EBAREREPO;
ac3d33df 391 git_error_set(GIT_ERROR_INVALID, "local push doesn't (yet) support pushing to non-bare repos.");
20858f6e 392 goto on_error;
393 }
394
e579e0f7
MB
395 if ((error = git_repository__item_path(&odb_path, remote_repo, GIT_REPOSITORY_ITEM_OBJECTS)) < 0
396 || (error = git_str_joinpath(&odb_path, odb_path.ptr, "pack")) < 0)
20858f6e 397 goto on_error;
398
4a5b781a 399 error = git_packbuilder_write(push->pb, odb_path.ptr, 0, transfer_to_push_transfer, (void *) cbs);
e579e0f7 400 git_str_dispose(&odb_path);
4a5b781a
CMN
401
402 if (error < 0)
403 goto on_error;
20858f6e 404
405 push->unpack_ok = 1;
406
407 git_vector_foreach(&push->specs, j, spec) {
408 push_status *status;
409 const git_error *last;
aad638f3 410 char *ref = spec->refspec.dst;
20858f6e 411
392702ee 412 status = git__calloc(1, sizeof(push_status));
20858f6e 413 if (!status)
414 goto on_error;
415
416 status->ref = git__strdup(ref);
417 if (!status->ref) {
418 git_push_status_free(status);
419 goto on_error;
420 }
421
aad638f3 422 error = local_push_update_remote_ref(remote_repo, spec->refspec.src, spec->refspec.dst,
20858f6e 423 &spec->loid, &spec->roid);
424
425 switch (error) {
426 case GIT_OK:
427 break;
428 case GIT_EINVALIDSPEC:
429 status->msg = git__strdup("funny refname");
430 break;
431 case GIT_ENOTFOUND:
432 status->msg = git__strdup("Remote branch not found to delete");
433 break;
434 default:
ac3d33df 435 last = git_error_last();
20858f6e 436
437 if (last && last->message)
438 status->msg = git__strdup(last->message);
439 else
440 status->msg = git__strdup("Unspecified error encountered");
441 break;
442 }
443
444 /* failed to allocate memory for a status message */
445 if (error < 0 && !status->msg) {
446 git_push_status_free(status);
447 goto on_error;
448 }
449
450 /* failed to insert the ref update status */
451 if ((error = git_vector_insert(&push->status, status)) < 0) {
452 git_push_status_free(status);
453 goto on_error;
454 }
455 }
456
457 if (push->specs.length) {
20858f6e 458 url = git__strdup(t->url);
459
460 if (!url || t->parent.close(&t->parent) < 0 ||
461 t->parent.connect(&t->parent, url,
e579e0f7 462 GIT_DIRECTION_PUSH, NULL))
20858f6e 463 goto on_error;
464 }
465
466 error = 0;
467
468on_error:
469 git_repository_free(remote_repo);
470 git__free(url);
471
472 return error;
473}
474
505da062 475typedef struct foreach_data {
22a2d3d5
UG
476 git_indexer_progress *stats;
477 git_indexer_progress_cb progress_cb;
505da062
BS
478 void *progress_payload;
479 git_odb_writepack *writepack;
480} foreach_data;
481
482static int foreach_cb(void *buf, size_t len, void *payload)
483{
484 foreach_data *data = (foreach_data*)payload;
485
486 data->stats->received_bytes += len;
a6154f21 487 return data->writepack->append(data->writepack, buf, len, data->stats);
505da062
BS
488}
489
4fd2bda9 490static const char *counting_objects_fmt = "Counting objects %d\r";
8cec2b8a
CMN
491static const char *compressing_objects_fmt = "Compressing objects: %.0f%% (%d/%d)";
492
493static int local_counting(int stage, unsigned int current, unsigned int total, void *payload)
494{
e579e0f7 495 git_str progress_info = GIT_STR_INIT;
8cec2b8a 496 transport_local *t = payload;
542a7de0 497 int error;
8cec2b8a 498
e579e0f7 499 if (!t->connect_opts.callbacks.sideband_progress)
8cec2b8a
CMN
500 return 0;
501
502 if (stage == GIT_PACKBUILDER_ADDING_OBJECTS) {
e579e0f7 503 git_str_printf(&progress_info, counting_objects_fmt, current);
8cec2b8a
CMN
504 } else if (stage == GIT_PACKBUILDER_DELTAFICATION) {
505 float perc = (((float) current) / total) * 100;
e579e0f7 506 git_str_printf(&progress_info, compressing_objects_fmt, perc, current, total);
8cec2b8a 507 if (current == total)
e579e0f7 508 git_str_printf(&progress_info, ", done\n");
8cec2b8a 509 else
e579e0f7 510 git_str_putc(&progress_info, '\r');
8cec2b8a
CMN
511
512 }
513
e579e0f7
MB
514 if (git_str_oom(&progress_info))
515 return -1;
516
517 if (progress_info.size > INT_MAX) {
518 git_error_set(GIT_ERROR_NET, "remote sent overly large progress data");
519 git_str_dispose(&progress_info);
8cec2b8a 520 return -1;
e579e0f7
MB
521 }
522
8cec2b8a 523
e579e0f7
MB
524 error = t->connect_opts.callbacks.sideband_progress(
525 progress_info.ptr,
526 (int)progress_info.size,
527 t->connect_opts.callbacks.payload);
542a7de0 528
e579e0f7 529 git_str_dispose(&progress_info);
542a7de0 530 return error;
8cec2b8a 531}
4fd2bda9 532
eae0bfdc
PP
533static int foreach_reference_cb(git_reference *reference, void *payload)
534{
535 git_revwalk *walk = (git_revwalk *)payload;
4b3ec53c
XL
536 int error;
537
ac3d33df 538 if (git_reference_type(reference) != GIT_REFERENCE_DIRECT) {
4b3ec53c
XL
539 git_reference_free(reference);
540 return 0;
541 }
eae0bfdc 542
4b3ec53c 543 error = git_revwalk_hide(walk, git_reference_target(reference));
eae0bfdc
PP
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. */
ac3d33df
JK
546 if (error == GIT_ENOTFOUND || error == GIT_ERROR_INVALID) {
547 git_error_clear();
eae0bfdc
PP
548 error = 0;
549 }
550
551 git_reference_free(reference);
552
553 return error;
554}
555
505da062
BS
556static int local_download_pack(
557 git_transport *transport,
558 git_repository *repo,
e579e0f7 559 git_indexer_progress *stats)
505da062
BS
560{
561 transport_local *t = (transport_local*)transport;
562 git_revwalk *walk = NULL;
563 git_remote_head *rhead;
564 unsigned int i;
565 int error = -1;
505da062
BS
566 git_packbuilder *pack = NULL;
567 git_odb_writepack *writepack = NULL;
90207709 568 git_odb *odb = NULL;
e579e0f7
MB
569 git_str progress_info = GIT_STR_INIT;
570 foreach_data data = {0};
505da062
BS
571
572 if ((error = git_revwalk_new(&walk, t->repo)) < 0)
573 goto cleanup;
e579e0f7 574
505da062
BS
575 git_revwalk_sorting(walk, GIT_SORT_TIME);
576
577 if ((error = git_packbuilder_new(&pack, t->repo)) < 0)
578 goto cleanup;
579
8cec2b8a
CMN
580 git_packbuilder_set_callbacks(pack, local_counting, t);
581
505da062
BS
582 stats->total_objects = 0;
583 stats->indexed_objects = 0;
584 stats->received_objects = 0;
585 stats->received_bytes = 0;
586
587 git_vector_foreach(&t->refs, i, rhead) {
588 git_object *obj;
ac3d33df 589 if ((error = git_object_lookup(&obj, t->repo, &rhead->oid, GIT_OBJECT_ANY)) < 0)
505da062
BS
590 goto cleanup;
591
ac3d33df 592 if (git_object_type(obj) == GIT_OBJECT_COMMIT) {
505da062
BS
593 /* Revwalker includes only wanted commits */
594 error = git_revwalk_push(walk, &rhead->oid);
505da062 595 } else {
c84a9dd2
CMN
596 /* Tag or some other wanted object. Add it on its own */
597 error = git_packbuilder_insert_recur(pack, &rhead->oid, rhead->name);
505da062 598 }
0cb16fe9 599 git_object_free(obj);
e68b31a1
CMN
600 if (error < 0)
601 goto cleanup;
505da062
BS
602 }
603
eae0bfdc
PP
604 if ((error = git_reference_foreach(repo, foreach_reference_cb, walk)))
605 goto cleanup;
606
e68b31a1
CMN
607 if ((error = git_packbuilder_insert_walk(pack, walk)))
608 goto cleanup;
609
e579e0f7
MB
610 if (t->connect_opts.callbacks.sideband_progress) {
611 if ((error = git_str_printf(
612 &progress_info,
613 counting_objects_fmt,
614 git_packbuilder_object_count(pack))) < 0 ||
615 (error = t->connect_opts.callbacks.sideband_progress(
616 progress_info.ptr,
617 (int)progress_info.size,
618 t->connect_opts.callbacks.payload)) < 0)
619 goto cleanup;
620 }
4fd2bda9 621
505da062 622 /* Walk the objects, building a packfile */
90207709
BS
623 if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
624 goto cleanup;
505da062 625
4fd2bda9 626 /* One last one with the newline */
e579e0f7
MB
627 if (t->connect_opts.callbacks.sideband_progress) {
628 git_str_clear(&progress_info);
629
630 if ((error = git_str_printf(
631 &progress_info,
632 counting_objects_fmt,
633 git_packbuilder_object_count(pack))) < 0 ||
634 (error = git_str_putc(&progress_info, '\n')) < 0 ||
635 (error = t->connect_opts.callbacks.sideband_progress(
636 progress_info.ptr,
637 (int)progress_info.size,
638 t->connect_opts.callbacks.payload)) < 0)
639 goto cleanup;
640 }
4fd2bda9 641
e579e0f7
MB
642 if ((error = git_odb_write_pack(
643 &writepack,
644 odb,
645 t->connect_opts.callbacks.transfer_progress,
646 t->connect_opts.callbacks.payload)) < 0)
90207709 647 goto cleanup;
505da062
BS
648
649 /* Write the data to the ODB */
e579e0f7
MB
650 data.stats = stats;
651 data.progress_cb = t->connect_opts.callbacks.transfer_progress;
652 data.progress_payload = t->connect_opts.callbacks.payload;
653 data.writepack = writepack;
505da062 654
e579e0f7
MB
655 /* autodetect */
656 git_packbuilder_set_threads(pack, 0);
0ef54a63 657
e579e0f7
MB
658 if ((error = git_packbuilder_foreach(pack, foreach_cb, &data)) != 0)
659 goto cleanup;
e68b31a1 660
505da062
BS
661 error = writepack->commit(writepack, stats);
662
663cleanup:
664 if (writepack) writepack->free(writepack);
e579e0f7 665 git_str_dispose(&progress_info);
505da062
BS
666 git_packbuilder_free(pack);
667 git_revwalk_free(walk);
668 return error;
a167002f
MS
669}
670
613d5eb9 671static int local_is_connected(git_transport *transport)
41fb1ca0
PK
672{
673 transport_local *t = (transport_local *)transport;
674
613d5eb9 675 return t->connected;
41fb1ca0
PK
676}
677
41fb1ca0
PK
678static void local_cancel(git_transport *transport)
679{
680 transport_local *t = (transport_local *)transport;
681
c25aa7cd 682 git_atomic32_set(&t->cancelled, 1);
41fb1ca0
PK
683}
684
854eccbb 685static int local_close(git_transport *transport)
d6258deb 686{
d88d4311
VM
687 transport_local *t = (transport_local *)transport;
688
41fb1ca0 689 t->connected = 0;
20858f6e 690
691 if (t->repo) {
692 git_repository_free(t->repo);
693 t->repo = NULL;
694 }
695
20858f6e 696 if (t->url) {
697 git__free(t->url);
698 t->url = NULL;
699 }
d88d4311 700
a62053a0 701 return 0;
8f866dae
CMN
702}
703
d6258deb
CMN
704static void local_free(git_transport *transport)
705{
20858f6e 706 transport_local *t = (transport_local *)transport;
9728cfde 707
f7fcb18f 708 free_heads(&t->refs);
edbaa63a 709
20858f6e 710 /* Close the transport, if it's still open. */
711 local_close(transport);
a2888919 712
20858f6e 713 /* Free the transport */
3286c408 714 git__free(t);
d6258deb
CMN
715}
716
8f866dae
CMN
717/**************
718 * Public API *
719 **************/
720
613d5eb9 721int git_transport_local(git_transport **out, git_remote *owner, void *param)
8f866dae 722{
6f73e026 723 int error;
4e913309
CMN
724 transport_local *t;
725
41fb1ca0
PK
726 GIT_UNUSED(param);
727
10711769 728 t = git__calloc(1, sizeof(transport_local));
ac3d33df 729 GIT_ERROR_CHECK_ALLOC(t);
4e913309 730
10711769 731 t->parent.version = GIT_TRANSPORT_VERSION;
4e913309 732 t->parent.connect = local_connect;
e579e0f7
MB
733 t->parent.set_connect_opts = local_set_connect_opts;
734 t->parent.capabilities = local_capabilities;
a167002f 735 t->parent.negotiate_fetch = local_negotiate_fetch;
505da062 736 t->parent.download_pack = local_download_pack;
20858f6e 737 t->parent.push = local_push;
4e913309
CMN
738 t->parent.close = local_close;
739 t->parent.free = local_free;
41fb1ca0
PK
740 t->parent.ls = local_ls;
741 t->parent.is_connected = local_is_connected;
41fb1ca0 742 t->parent.cancel = local_cancel;
4e913309 743
6f73e026
JG
744 if ((error = git_vector_init(&t->refs, 0, NULL)) < 0) {
745 git__free(t);
746 return error;
747 }
748
613d5eb9
PK
749 t->owner = owner;
750
4e913309 751 *out = (git_transport *) t;
8f866dae 752
a62053a0 753 return 0;
8f866dae 754}