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 "clone.h"
9 -
10 - #include "git2/clone.h"
11 - #include "git2/remote.h"
12 - #include "git2/revparse.h"
13 - #include "git2/branch.h"
14 - #include "git2/config.h"
15 - #include "git2/checkout.h"
16 - #include "git2/commit.h"
17 - #include "git2/tree.h"
18 -
19 - #include "remote.h"
20 - #include "futils.h"
21 - #include "refs.h"
22 - #include "path.h"
23 - #include "repository.h"
24 - #include "odb.h"
25 -
26 - static int clone_local_into(git_repository *repo, git_remote *remote, const git_fetch_options *fetch_opts, const git_checkout_options *co_opts, const char *branch, int link);
27 -
28 55 2 static int create_branch(
29 - git_reference **branch,
30 - git_repository *repo,
31 - const git_oid *target,
32 - const char *name,
33 - const char *log_message)
34 - {
35 55 2 git_commit *head_obj = NULL;
36 55 2 git_reference *branch_ref = NULL;
37 55 2 git_buf refname = GIT_BUF_INIT;
38 - int error;
39 -
40 - /* Find the target commit */
41 55 2,3 if ((error = git_commit_lookup(&head_obj, repo, target)) < 0)
42 ##### 4 return error;
43 -
44 - /* Create the new branch */
45 55 5,6 if ((error = git_buf_printf(&refname, GIT_REFS_HEADS_DIR "%s", name)) < 0)
46 ##### 7 return error;
47 -
48 55 8,9 error = git_reference_create(&branch_ref, repo, git_buf_cstr(&refname), target, 0, log_message);
49 55 10 git_buf_dispose(&refname);
50 55 11 git_commit_free(head_obj);
51 -
52 55 12 if (!error)
53 53 13 *branch = branch_ref;
54 - else
55 2 14 git_reference_free(branch_ref);
56 -
57 55 15 return error;
58 - }
59 -
60 60 2 static int setup_tracking_config(
61 - git_repository *repo,
62 - const char *branch_name,
63 - const char *remote_name,
64 - const char *merge_target)
65 - {
66 - git_config *cfg;
67 60 2 git_buf remote_key = GIT_BUF_INIT, merge_key = GIT_BUF_INIT;
68 60 2 int error = -1;
69 -
70 60 2,3 if (git_repository_config__weakptr(&cfg, repo) < 0)
71 ##### 4 return -1;
72 -
73 60 5,6 if (git_buf_printf(&remote_key, "branch.%s.remote", branch_name) < 0)
74 ##### 7 goto cleanup;
75 -
76 60 8,9 if (git_buf_printf(&merge_key, "branch.%s.merge", branch_name) < 0)
77 ##### 10 goto cleanup;
78 -
79 60 11-13 if (git_config_set_string(cfg, git_buf_cstr(&remote_key), remote_name) < 0)
80 ##### 14 goto cleanup;
81 -
82 60 15-17 if (git_config_set_string(cfg, git_buf_cstr(&merge_key), merge_target) < 0)
83 ##### 18 goto cleanup;
84 -
85 60 19 error = 0;
86 -
87 - cleanup:
88 60 20 git_buf_dispose(&remote_key);
89 60 21 git_buf_dispose(&merge_key);
90 60 22 return error;
91 - }
92 -
93 55 2 static int create_tracking_branch(
94 - git_reference **branch,
95 - git_repository *repo,
96 - const git_oid *target,
97 - const char *branch_name,
98 - const char *log_message)
99 - {
100 - int error;
101 -
102 55 2,3 if ((error = create_branch(branch, repo, target, branch_name, log_message)) < 0)
103 2 4 return error;
104 -
105 53 5 return setup_tracking_config(
106 - repo,
107 - branch_name,
108 - GIT_REMOTE_ORIGIN,
109 - git_reference_name(*branch));
110 - }
111 -
112 55 2 static int update_head_to_new_branch(
113 - git_repository *repo,
114 - const git_oid *target,
115 - const char *name,
116 - const char *reflog_message)
117 - {
118 55 2 git_reference *tracking_branch = NULL;
119 - int error;
120 -
121 55 2,3 if (!git__prefixcmp(name, GIT_REFS_HEADS_DIR))
122 52 4 name += strlen(GIT_REFS_HEADS_DIR);
123 -
124 55 5 error = create_tracking_branch(&tracking_branch, repo, target, name,
125 - reflog_message);
126 -
127 55 6 if (!error)
128 53 7,8 error = git_repository_set_head(
129 - repo, git_reference_name(tracking_branch));
130 -
131 55 9 git_reference_free(tracking_branch);
132 -
133 - /* if it already existed, then the user's refspec created it for us, ignore it' */
134 55 10 if (error == GIT_EEXISTS)
135 2 11 error = 0;
136 -
137 55 12 return error;
138 - }
139 -
140 62 2 static int update_head_to_remote(
141 - git_repository *repo,
142 - git_remote *remote,
143 - const char *reflog_message)
144 - {
145 62 2 int error = 0;
146 - size_t refs_len;
147 - git_refspec *refspec;
148 - const git_remote_head *remote_head, **refs;
149 - const git_oid *remote_head_id;
150 62 2 git_buf remote_master_name = GIT_BUF_INIT;
151 62 2 git_buf branch = GIT_BUF_INIT;
152 -
153 62 2,3 if ((error = git_remote_ls(&refs, &refs_len, remote)) < 0)
154 ##### 4 return error;
155 -
156 - /* We cloned an empty repository or one with an unborn HEAD */
157 62 5,6 if (refs_len == 0 || strcmp(refs[0]->name, GIT_HEAD_FILE))
158 7 7 return setup_tracking_config(
159 - repo, "master", GIT_REMOTE_ORIGIN, GIT_REFS_HEADS_MASTER_FILE);
160 -
161 - /* We know we have HEAD, let's see where it points */
162 55 8 remote_head = refs[0];
163 55 8,9 assert(remote_head);
164 -
165 55 10 remote_head_id = &remote_head->oid;
166 -
167 55 10 error = git_remote_default_branch(&branch, remote);
168 55 11 if (error == GIT_ENOTFOUND) {
169 2 12 error = git_repository_set_head_detached(
170 - repo, remote_head_id);
171 2 13 goto cleanup;
172 - }
173 -
174 53 14,15 refspec = git_remote__matching_refspec(remote, git_buf_cstr(&branch));
175 -
176 53 16 if (refspec == NULL) {
177 1 17 git_error_set(GIT_ERROR_NET, "the remote's default branch does not fit the refspec configuration");
178 1 18 error = GIT_EINVALIDSPEC;
179 1 18 goto cleanup;
180 - }
181 -
182 - /* Determine the remote tracking reference name from the local master */
183 52 19-21 if ((error = git_refspec_transform(
184 - &remote_master_name,
185 - refspec,
186 - git_buf_cstr(&branch))) < 0)
187 ##### 22 goto cleanup;
188 -
189 52 23,24 error = update_head_to_new_branch(
190 - repo,
191 - remote_head_id,
192 - git_buf_cstr(&branch),
193 - reflog_message);
194 -
195 - cleanup:
196 55 25 git_buf_dispose(&remote_master_name);
197 55 26 git_buf_dispose(&branch);
198 -
199 55 27 return error;
200 - }
201 -
202 3 2 static int update_head_to_branch(
203 - git_repository *repo,
204 - const char *remote_name,
205 - const char *branch,
206 - const char *reflog_message)
207 - {
208 - int retcode;
209 3 2 git_buf remote_branch_name = GIT_BUF_INIT;
210 3 2 git_reference* remote_ref = NULL;
211 -
212 3 2-4 assert(remote_name && branch);
213 -
214 3 5,6 if ((retcode = git_buf_printf(&remote_branch_name, GIT_REFS_REMOTES_DIR "%s/%s",
215 - remote_name, branch)) < 0 )
216 ##### 7 goto cleanup;
217 -
218 3 8-10 if ((retcode = git_reference_lookup(&remote_ref, repo, git_buf_cstr(&remote_branch_name))) < 0)
219 ##### 11 goto cleanup;
220 -
221 3 12,13 retcode = update_head_to_new_branch(repo, git_reference_target(remote_ref), branch,
222 - reflog_message);
223 -
224 - cleanup:
225 3 14 git_reference_free(remote_ref);
226 3 15 git_buf_dispose(&remote_branch_name);
227 3 16 return retcode;
228 - }
229 -
230 79 2 static int default_repository_create(git_repository **out, const char *path, int bare, void *payload)
231 - {
232 - GIT_UNUSED(payload);
233 -
234 79 2 return git_repository_init(out, path, bare);
235 - }
236 -
237 81 2 static int default_remote_create(
238 - git_remote **out,
239 - git_repository *repo,
240 - const char *name,
241 - const char *url,
242 - void *payload)
243 - {
244 - GIT_UNUSED(payload);
245 -
246 81 2 return git_remote_create(out, repo, name, url);
247 - }
248 -
249 - /*
250 - * submodules?
251 - */
252 -
253 91 2 static int create_and_configure_origin(
254 - git_remote **out,
255 - git_repository *repo,
256 - const char *url,
257 - const git_clone_options *options)
258 - {
259 - int error;
260 91 2 git_remote *origin = NULL;
261 - char buf[GIT_PATH_MAX];
262 91 2 git_remote_create_cb remote_create = options->remote_cb;
263 91 2 void *payload = options->remote_cb_payload;
264 -
265 - /* If the path exists and is a dir, the url should be the absolute path */
266 91 2-7 if (git_path_root(url) < 0 && git_path_exists(url) && git_path_isdir(url)) {
267 8 8,9 if (p_realpath(url, buf) == NULL)
268 ##### 10 return -1;
269 -
270 8 11 url = buf;
271 - }
272 -
273 91 12 if (!remote_create) {
274 81 13 remote_create = default_remote_create;
275 81 13 payload = NULL;
276 - }
277 -
278 91 14,15 if ((error = remote_create(&origin, repo, "origin", url, payload)) < 0)
279 ##### 16 goto on_error;
280 -
281 91 17 *out = origin;
282 91 17 return 0;
283 -
284 - on_error:
285 ##### 18 git_remote_free(origin);
286 ##### 19 return error;
287 - }
288 -
289 64 2 static bool should_checkout(
290 - git_repository *repo,
291 - bool is_bare,
292 - const git_checkout_options *opts)
293 - {
294 64 2 if (is_bare)
295 9 3 return false;
296 -
297 55 4 if (!opts)
298 ##### 5 return false;
299 -
300 55 6 if (opts->checkout_strategy == GIT_CHECKOUT_NONE)
301 17 7 return false;
302 -
303 38 8 return !git_repository_head_unborn(repo);
304 - }
305 -
306 65 2 static int checkout_branch(git_repository *repo, git_remote *remote, const git_checkout_options *co_opts, const char *branch, const char *reflog_message)
307 - {
308 - int error;
309 -
310 65 2 if (branch)
311 3 3,4 error = update_head_to_branch(repo, git_remote_name(remote), branch,
312 - reflog_message);
313 - /* Point HEAD to the same ref as the remote's head */
314 - else
315 62 5 error = update_head_to_remote(repo, remote, reflog_message);
316 -
317 65 6-9 if (!error && should_checkout(repo, git_repository_is_bare(repo), co_opts))
318 34 10 error = git_checkout_head(repo, co_opts);
319 -
320 65 11 return error;
321 - }
322 -
323 64 2 static int clone_into(git_repository *repo, git_remote *_remote, const git_fetch_options *opts, const git_checkout_options *co_opts, const char *branch)
324 - {
325 - int error;
326 64 2 git_buf reflog_message = GIT_BUF_INIT;
327 - git_fetch_options fetch_opts;
328 - git_remote *remote;
329 -
330 64 2-4 assert(repo && _remote);
331 -
332 64 5,6 if (!git_repository_is_empty(repo)) {
333 ##### 7 git_error_set(GIT_ERROR_INVALID, "the repository is not empty");
334 ##### 8 return -1;
335 - }
336 -
337 64 9,10 if ((error = git_remote_dup(&remote, _remote)) < 0)
338 ##### 11 return error;
339 -
340 64 12 memcpy(&fetch_opts, opts, sizeof(git_fetch_options));
341 64 12 fetch_opts.update_fetchhead = 0;
342 64 12 fetch_opts.download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL;
343 64 12,13 git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote));
344 -
345 64 14-16 if ((error = git_remote_fetch(remote, NULL, &fetch_opts, git_buf_cstr(&reflog_message))) != 0)
346 25 17 goto cleanup;
347 -
348 39 18,19 error = checkout_branch(repo, remote, co_opts, branch, git_buf_cstr(&reflog_message));
349 -
350 - cleanup:
351 64 20 git_remote_free(remote);
352 64 21 git_buf_dispose(&reflog_message);
353 -
354 64 22 return error;
355 - }
356 -
357 111 2 int git_clone__should_clone_local(const char *url_or_path, git_clone_local_t local)
358 - {
359 111 2 git_buf fromurl = GIT_BUF_INIT;
360 111 2 const char *path = url_or_path;
361 - bool is_url, is_local;
362 -
363 111 2 if (local == GIT_CLONE_NO_LOCAL)
364 6 3 return 0;
365 -
366 105 4,5 if ((is_url = git_path_is_local_file_url(url_or_path)) != 0) {
367 23 6,7 if (git_path_fromurl(&fromurl, url_or_path) < 0) {
368 ##### 8 is_local = -1;
369 ##### 8 goto done;
370 - }
371 -
372 23 9 path = fromurl.ptr;
373 - }
374 -
375 105 10,11,13-16 is_local = (!is_url || local != GIT_CLONE_LOCAL_AUTO) &&
376 90 12 git_path_isdir(path);
377 -
378 - done:
379 105 17 git_buf_dispose(&fromurl);
380 105 18 return is_local;
381 - }
382 -
383 93 2 static int git__clone(
384 - git_repository **out,
385 - const char *url,
386 - const char *local_path,
387 - const git_clone_options *_options,
388 - int use_existing)
389 - {
390 93 2 int error = 0;
391 93 2 git_repository *repo = NULL;
392 - git_remote *origin;
393 93 2 git_clone_options options = GIT_CLONE_OPTIONS_INIT;
394 93 2 uint32_t rmdir_flags = GIT_RMDIR_REMOVE_FILES;
395 - git_repository_create_cb repository_cb;
396 -
397 93 2-5 assert(out && url && local_path);
398 -
399 93 6 if (_options)
400 83 7 memcpy(&options, _options, sizeof(git_clone_options));
401 -
402 93 8-10 GIT_ERROR_CHECK_VERSION(&options, GIT_CLONE_OPTIONS_VERSION, "git_clone_options");
403 -
404 - /* Only clone to a new directory or an empty directory */
405 93 11-15 if (git_path_exists(local_path) && !use_existing && !git_path_is_empty_dir(local_path)) {
406 2 16 git_error_set(GIT_ERROR_INVALID,
407 - "'%s' exists and is not an empty directory", local_path);
408 2 17 return GIT_EEXISTS;
409 - }
410 -
411 - /* Only remove the root directory on failure if we create it */
412 91 18,19 if (git_path_exists(local_path))
413 7 20 rmdir_flags |= GIT_RMDIR_SKIP_ROOT;
414 -
415 91 21 if (options.repository_cb)
416 12 22 repository_cb = options.repository_cb;
417 - else
418 79 23 repository_cb = default_repository_create;
419 -
420 91 24,25 if ((error = repository_cb(&repo, local_path, options.bare, options.repository_cb_payload)) < 0)
421 ##### 26 return error;
422 -
423 91 27,28 if (!(error = create_and_configure_origin(&origin, repo, url, &options))) {
424 91 29 int clone_local = git_clone__should_clone_local(url, options.local);
425 91 30 int link = options.local != GIT_CLONE_LOCAL_NO_LINKS;
426 -
427 91 30 if (clone_local == 1)
428 27 31 error = clone_local_into(
429 - repo, origin, &options.fetch_opts, &options.checkout_opts,
430 - options.checkout_branch, link);
431 64 32 else if (clone_local == 0)
432 64 33 error = clone_into(
433 - repo, origin, &options.fetch_opts, &options.checkout_opts,
434 - options.checkout_branch);
435 - else
436 ##### 34 error = -1;
437 -
438 91 35 git_remote_free(origin);
439 - }
440 -
441 91 36 if (error != 0) {
442 28 37 git_error_state last_error = {0};
443 28 37 git_error_state_capture(&last_error, error);
444 -
445 28 38 git_repository_free(repo);
446 28 39 repo = NULL;
447 -
448 28 39 (void)git_futils_rmdir_r(local_path, NULL, rmdir_flags);
449 -
450 28 40,41 git_error_state_restore(&last_error);
451 - }
452 -
453 91 42 *out = repo;
454 91 42 return error;
455 - }
456 -
457 89 2 int git_clone(
458 - git_repository **out,
459 - const char *url,
460 - const char *local_path,
461 - const git_clone_options *_options)
462 - {
463 89 2 return git__clone(out, url, local_path, _options, 0);
464 - }
465 -
466 4 2 int git_clone__submodule(
467 - git_repository **out,
468 - const char *url,
469 - const char *local_path,
470 - const git_clone_options *_options)
471 - {
472 4 2 return git__clone(out, url, local_path, _options, 1);
473 - }
474 -
475 1 2 int git_clone_options_init(git_clone_options *opts, unsigned int version)
476 - {
477 1 2-4 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
478 - opts, version, git_clone_options, GIT_CLONE_OPTIONS_INIT);
479 1 5 return 0;
480 - }
481 -
482 - #ifndef GIT_DEPRECATE_HARD
483 ##### 2 int git_clone_init_options(git_clone_options *opts, unsigned int version)
484 - {
485 ##### 2 return git_clone_options_init(opts, version);
486 - }
487 - #endif
488 -
489 26 2 static bool can_link(const char *src, const char *dst, int link)
490 - {
491 - #ifdef GIT_WIN32
492 - GIT_UNUSED(src);
493 - GIT_UNUSED(dst);
494 - GIT_UNUSED(link);
495 - return false;
496 - #else
497 -
498 - struct stat st_src, st_dst;
499 -
500 26 2 if (!link)
501 2 3 return false;
502 -
503 24 4,5 if (p_stat(src, &st_src) < 0)
504 ##### 6 return false;
505 -
506 24 7,8 if (p_stat(dst, &st_dst) < 0)
507 ##### 9 return false;
508 -
509 24 10 return st_src.st_dev == st_dst.st_dev;
510 - #endif
511 - }
512 -
513 27 2 static int clone_local_into(git_repository *repo, git_remote *remote, const git_fetch_options *fetch_opts, const git_checkout_options *co_opts, const char *branch, int link)
514 - {
515 - int error, flags;
516 - git_repository *src;
517 27 2 git_buf src_odb = GIT_BUF_INIT, dst_odb = GIT_BUF_INIT, src_path = GIT_BUF_INIT;
518 27 2 git_buf reflog_message = GIT_BUF_INIT;
519 -
520 27 2-4 assert(repo && remote);
521 -
522 27 5,6 if (!git_repository_is_empty(repo)) {
523 1 7 git_error_set(GIT_ERROR_INVALID, "the repository is not empty");
524 1 8 return -1;
525 - }
526 -
527 - /*
528 - * Let's figure out what path we should use for the source
529 - * repo, if it's not rooted, the path should be relative to
530 - * the repository's worktree/gitdir.
531 - */
532 26 9-11 if ((error = git_path_from_url_or_path(&src_path, git_remote_url(remote))) < 0)
533 ##### 12 return error;
534 -
535 - /* Copy .git/objects/ from the source to the target */
536 26 13-15 if ((error = git_repository_open(&src, git_buf_cstr(&src_path))) < 0) {
537 ##### 16 git_buf_dispose(&src_path);
538 ##### 17 return error;
539 - }
540 -
541 26 18,19 if (git_repository_item_path(&src_odb, src, GIT_REPOSITORY_ITEM_OBJECTS) < 0
542 26 20,21 || git_repository_item_path(&dst_odb, repo, GIT_REPOSITORY_ITEM_OBJECTS) < 0) {
543 ##### 22 error = -1;
544 ##### 22 goto cleanup;
545 - }
546 -
547 26 23 flags = 0;
548 26 23-26 if (can_link(git_repository_path(src), git_repository_path(repo), link))
549 24 27 flags |= GIT_CPDIR_LINK_FILES;
550 -
551 26 28-30 error = git_futils_cp_r(git_buf_cstr(&src_odb), git_buf_cstr(&dst_odb),
552 - flags, GIT_OBJECT_DIR_MODE);
553 -
554 - /*
555 - * can_link() doesn't catch all variations, so if we hit an
556 - * error and did want to link, let's try again without trying
557 - * to link.
558 - */
559 26 31,32 if (error < 0 && link) {
560 ##### 33 flags &= ~GIT_CPDIR_LINK_FILES;
561 ##### 33-35 error = git_futils_cp_r(git_buf_cstr(&src_odb), git_buf_cstr(&dst_odb),
562 - flags, GIT_OBJECT_DIR_MODE);
563 - }
564 -
565 26 36 if (error < 0)
566 ##### 37 goto cleanup;
567 -
568 26 38,39 git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote));
569 -
570 26 40-42 if ((error = git_remote_fetch(remote, NULL, fetch_opts, git_buf_cstr(&reflog_message))) != 0)
571 ##### 43 goto cleanup;
572 -
573 26 44,45 error = checkout_branch(repo, remote, co_opts, branch, git_buf_cstr(&reflog_message));
574 -
575 - cleanup:
576 26 46 git_buf_dispose(&reflog_message);
577 26 47 git_buf_dispose(&src_path);
578 26 48 git_buf_dispose(&src_odb);
579 26 49 git_buf_dispose(&dst_odb);
580 26 50 git_repository_free(src);
581 26 51 return error;
582 - }