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