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