File Coverage

deps/libgit2/src/clone.c
Criterion Covered Total %
statement 0 242 0.0
branch 0 150 0.0
condition n/a
subroutine n/a
pod n/a
total 0 392 0.0


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