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