/src/libgit2/src/libgit2/clone.c
Line | Count | 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 "checkout.h" |
20 | | #include "remote.h" |
21 | | #include "futils.h" |
22 | | #include "refs.h" |
23 | | #include "fs_path.h" |
24 | | #include "repository.h" |
25 | | #include "odb.h" |
26 | | #include "net.h" |
27 | | |
28 | | 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 | 0 | { |
35 | 0 | git_commit *head_obj = NULL; |
36 | 0 | git_reference *branch_ref = NULL; |
37 | 0 | git_str refname = GIT_STR_INIT; |
38 | 0 | int error; |
39 | | |
40 | | /* Find the target commit */ |
41 | 0 | if ((error = git_commit_lookup(&head_obj, repo, target)) < 0) |
42 | 0 | return error; |
43 | | |
44 | | /* Create the new branch */ |
45 | 0 | if ((error = git_str_printf(&refname, GIT_REFS_HEADS_DIR "%s", name)) < 0) |
46 | 0 | return error; |
47 | | |
48 | 0 | error = git_reference_create(&branch_ref, repo, git_str_cstr(&refname), target, 0, log_message); |
49 | 0 | git_str_dispose(&refname); |
50 | 0 | git_commit_free(head_obj); |
51 | |
|
52 | 0 | if (!error) |
53 | 0 | *branch = branch_ref; |
54 | 0 | else |
55 | 0 | git_reference_free(branch_ref); |
56 | |
|
57 | 0 | return error; |
58 | 0 | } |
59 | | |
60 | | 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 | 0 | { |
66 | 0 | git_config *cfg; |
67 | 0 | git_str remote_key = GIT_STR_INIT, merge_key = GIT_STR_INIT; |
68 | 0 | int error = -1; |
69 | |
|
70 | 0 | if (git_repository_config__weakptr(&cfg, repo) < 0) |
71 | 0 | return -1; |
72 | | |
73 | 0 | if (git_str_printf(&remote_key, "branch.%s.remote", branch_name) < 0) |
74 | 0 | goto cleanup; |
75 | | |
76 | 0 | if (git_str_printf(&merge_key, "branch.%s.merge", branch_name) < 0) |
77 | 0 | goto cleanup; |
78 | | |
79 | 0 | if (git_config_set_string(cfg, git_str_cstr(&remote_key), remote_name) < 0) |
80 | 0 | goto cleanup; |
81 | | |
82 | 0 | if (git_config_set_string(cfg, git_str_cstr(&merge_key), merge_target) < 0) |
83 | 0 | goto cleanup; |
84 | | |
85 | 0 | error = 0; |
86 | |
|
87 | 0 | cleanup: |
88 | 0 | git_str_dispose(&remote_key); |
89 | 0 | git_str_dispose(&merge_key); |
90 | 0 | return error; |
91 | 0 | } |
92 | | |
93 | | 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 | 0 | { |
100 | 0 | int error; |
101 | |
|
102 | 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 | 0 | repo, |
107 | 0 | branch_name, |
108 | 0 | GIT_REMOTE_ORIGIN, |
109 | 0 | git_reference_name(*branch)); |
110 | 0 | } |
111 | | |
112 | | 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 | 0 | { |
118 | 0 | git_reference *tracking_branch = NULL; |
119 | 0 | int error; |
120 | |
|
121 | 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 | 0 | reflog_message); |
126 | |
|
127 | 0 | if (!error) |
128 | 0 | error = git_repository_set_head( |
129 | 0 | 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 | if (error == GIT_EEXISTS) |
135 | 0 | error = 0; |
136 | |
|
137 | 0 | return error; |
138 | 0 | } |
139 | | |
140 | | static int update_head_to_default(git_repository *repo) |
141 | 0 | { |
142 | 0 | git_str initialbranch = GIT_STR_INIT; |
143 | 0 | const char *branch_name; |
144 | 0 | int error = 0; |
145 | |
|
146 | 0 | if ((error = git_repository_initialbranch(&initialbranch, repo)) < 0) |
147 | 0 | goto done; |
148 | | |
149 | 0 | if (git__prefixcmp(initialbranch.ptr, GIT_REFS_HEADS_DIR) != 0) { |
150 | 0 | git_error_set(GIT_ERROR_INVALID, "invalid initial branch '%s'", initialbranch.ptr); |
151 | 0 | error = -1; |
152 | 0 | goto done; |
153 | 0 | } |
154 | | |
155 | 0 | branch_name = initialbranch.ptr + strlen(GIT_REFS_HEADS_DIR); |
156 | |
|
157 | 0 | error = setup_tracking_config(repo, branch_name, GIT_REMOTE_ORIGIN, |
158 | 0 | initialbranch.ptr); |
159 | |
|
160 | 0 | done: |
161 | 0 | git_str_dispose(&initialbranch); |
162 | 0 | return error; |
163 | 0 | } |
164 | | |
165 | | static int update_remote_head( |
166 | | git_repository *repo, |
167 | | git_remote *remote, |
168 | | git_str *target, |
169 | | const char *reflog_message) |
170 | 0 | { |
171 | 0 | git_refspec *refspec; |
172 | 0 | git_reference *remote_head = NULL; |
173 | 0 | git_str remote_head_name = GIT_STR_INIT; |
174 | 0 | git_str remote_branch_name = GIT_STR_INIT; |
175 | 0 | int error; |
176 | | |
177 | | /* Determine the remote tracking ref name from the local branch */ |
178 | 0 | refspec = git_remote__matching_refspec(remote, git_str_cstr(target)); |
179 | |
|
180 | 0 | if (refspec == NULL) { |
181 | 0 | git_error_set(GIT_ERROR_NET, "the remote's default branch does not fit the refspec configuration"); |
182 | 0 | error = GIT_EINVALIDSPEC; |
183 | 0 | goto cleanup; |
184 | 0 | } |
185 | | |
186 | 0 | if ((error = git_refspec__transform( |
187 | 0 | &remote_branch_name, |
188 | 0 | refspec, |
189 | 0 | git_str_cstr(target))) < 0) |
190 | 0 | goto cleanup; |
191 | | |
192 | 0 | if ((error = git_str_printf(&remote_head_name, |
193 | 0 | "%s%s/%s", |
194 | 0 | GIT_REFS_REMOTES_DIR, |
195 | 0 | git_remote_name(remote), |
196 | 0 | GIT_HEAD_FILE)) < 0) |
197 | 0 | goto cleanup; |
198 | | |
199 | 0 | error = git_reference_symbolic_create( |
200 | 0 | &remote_head, |
201 | 0 | repo, |
202 | 0 | git_str_cstr(&remote_head_name), |
203 | 0 | git_str_cstr(&remote_branch_name), |
204 | 0 | true, |
205 | 0 | reflog_message); |
206 | |
|
207 | 0 | cleanup: |
208 | 0 | git_reference_free(remote_head); |
209 | 0 | git_str_dispose(&remote_branch_name); |
210 | 0 | git_str_dispose(&remote_head_name); |
211 | 0 | return error; |
212 | 0 | } |
213 | | |
214 | | static int update_head_to_remote( |
215 | | git_repository *repo, |
216 | | git_remote *remote, |
217 | | const char *reflog_message) |
218 | 0 | { |
219 | 0 | int error = 0; |
220 | 0 | size_t refs_len; |
221 | 0 | const git_remote_head *remote_head, **refs; |
222 | 0 | const git_oid *remote_head_id; |
223 | 0 | git_str branch = GIT_STR_INIT; |
224 | |
|
225 | 0 | if ((error = git_remote_ls(&refs, &refs_len, remote)) < 0) |
226 | 0 | return error; |
227 | | |
228 | | /* We cloned an empty repository or one with an unborn HEAD */ |
229 | 0 | if (refs_len == 0 || strcmp(refs[0]->name, GIT_HEAD_FILE)) |
230 | 0 | return update_head_to_default(repo); |
231 | | |
232 | | /* We know we have HEAD, let's see where it points */ |
233 | 0 | remote_head = refs[0]; |
234 | 0 | GIT_ASSERT(remote_head); |
235 | | |
236 | 0 | remote_head_id = &remote_head->oid; |
237 | |
|
238 | 0 | error = git_remote__default_branch(&branch, remote); |
239 | 0 | if (error == GIT_ENOTFOUND) { |
240 | 0 | error = git_repository_set_head_detached( |
241 | 0 | repo, remote_head_id); |
242 | 0 | goto cleanup; |
243 | 0 | } |
244 | | |
245 | 0 | if ((error = update_remote_head(repo, remote, &branch, reflog_message)) < 0) |
246 | 0 | goto cleanup; |
247 | | |
248 | 0 | error = update_head_to_new_branch( |
249 | 0 | repo, |
250 | 0 | remote_head_id, |
251 | 0 | git_str_cstr(&branch), |
252 | 0 | reflog_message); |
253 | |
|
254 | 0 | cleanup: |
255 | 0 | git_str_dispose(&branch); |
256 | |
|
257 | 0 | return error; |
258 | 0 | } |
259 | | |
260 | | static int update_head_to_branch( |
261 | | git_repository *repo, |
262 | | git_remote *remote, |
263 | | const char *branch, |
264 | | const char *reflog_message) |
265 | 0 | { |
266 | 0 | int retcode; |
267 | 0 | git_str remote_branch_name = GIT_STR_INIT; |
268 | 0 | git_reference *remote_ref = NULL; |
269 | 0 | git_str default_branch = GIT_STR_INIT; |
270 | |
|
271 | 0 | GIT_ASSERT_ARG(remote); |
272 | 0 | GIT_ASSERT_ARG(branch); |
273 | | |
274 | 0 | if ((retcode = git_str_printf(&remote_branch_name, GIT_REFS_REMOTES_DIR "%s/%s", |
275 | 0 | git_remote_name(remote), branch)) < 0 ) |
276 | 0 | goto cleanup; |
277 | | |
278 | 0 | if ((retcode = git_reference_lookup(&remote_ref, repo, git_str_cstr(&remote_branch_name))) < 0) |
279 | 0 | goto cleanup; |
280 | | |
281 | 0 | if ((retcode = update_head_to_new_branch(repo, git_reference_target(remote_ref), branch, |
282 | 0 | reflog_message)) < 0) |
283 | 0 | goto cleanup; |
284 | | |
285 | 0 | retcode = git_remote__default_branch(&default_branch, remote); |
286 | |
|
287 | 0 | if (retcode == GIT_ENOTFOUND) |
288 | 0 | retcode = 0; |
289 | 0 | else if (retcode) |
290 | 0 | goto cleanup; |
291 | | |
292 | 0 | if (!git_remote__matching_refspec(remote, git_str_cstr(&default_branch))) |
293 | 0 | goto cleanup; |
294 | | |
295 | 0 | retcode = update_remote_head(repo, remote, &default_branch, reflog_message); |
296 | |
|
297 | 0 | cleanup: |
298 | 0 | git_reference_free(remote_ref); |
299 | 0 | git_str_dispose(&remote_branch_name); |
300 | 0 | git_str_dispose(&default_branch); |
301 | 0 | return retcode; |
302 | 0 | } |
303 | | |
304 | | static int default_repository_create(git_repository **out, const char *path, int bare, void *payload) |
305 | 0 | { |
306 | 0 | GIT_UNUSED(payload); |
307 | |
|
308 | 0 | return git_repository_init(out, path, bare); |
309 | 0 | } |
310 | | |
311 | | static int default_remote_create( |
312 | | git_remote **out, |
313 | | git_repository *repo, |
314 | | const char *name, |
315 | | const char *url, |
316 | | void *payload) |
317 | 0 | { |
318 | 0 | GIT_UNUSED(payload); |
319 | |
|
320 | 0 | return git_remote_create(out, repo, name, url); |
321 | 0 | } |
322 | | |
323 | | /* |
324 | | * submodules? |
325 | | */ |
326 | | |
327 | | static int create_and_configure_origin( |
328 | | git_remote **out, |
329 | | git_repository *repo, |
330 | | const char *url, |
331 | | const git_clone_options *options) |
332 | 0 | { |
333 | 0 | int error; |
334 | 0 | git_remote *origin = NULL; |
335 | 0 | char buf[GIT_PATH_MAX]; |
336 | 0 | git_remote_create_cb remote_create = options->remote_cb; |
337 | 0 | void *payload = options->remote_cb_payload; |
338 | | |
339 | | /* If the path is local and exists it should be the absolute path. */ |
340 | 0 | if (!git_net_str_is_url(url) && git_fs_path_root(url) < 0 && |
341 | 0 | git_fs_path_exists(url)) { |
342 | 0 | if (p_realpath(url, buf) == NULL) |
343 | 0 | return -1; |
344 | | |
345 | 0 | url = buf; |
346 | 0 | } |
347 | | |
348 | 0 | if (!remote_create) { |
349 | 0 | remote_create = default_remote_create; |
350 | 0 | payload = NULL; |
351 | 0 | } |
352 | |
|
353 | 0 | if ((error = remote_create(&origin, repo, "origin", url, payload)) < 0) |
354 | 0 | goto on_error; |
355 | | |
356 | 0 | *out = origin; |
357 | 0 | return 0; |
358 | | |
359 | 0 | on_error: |
360 | 0 | git_remote_free(origin); |
361 | 0 | return error; |
362 | 0 | } |
363 | | |
364 | | static int should_checkout( |
365 | | bool *out, |
366 | | git_repository *repo, |
367 | | bool is_bare, |
368 | | const git_clone_options *opts) |
369 | 0 | { |
370 | 0 | int error; |
371 | |
|
372 | 0 | if (!opts || is_bare || |
373 | 0 | opts->checkout_opts.checkout_strategy == GIT_CHECKOUT_NONE) { |
374 | 0 | *out = false; |
375 | 0 | return 0; |
376 | 0 | } |
377 | | |
378 | 0 | if ((error = git_repository_head_unborn(repo)) < 0) |
379 | 0 | return error; |
380 | | |
381 | 0 | *out = !error; |
382 | 0 | return 0; |
383 | 0 | } |
384 | | |
385 | | static int checkout_branch( |
386 | | git_repository *repo, |
387 | | git_remote *remote, |
388 | | const git_clone_options *opts, |
389 | | const char *reflog_message) |
390 | 0 | { |
391 | 0 | bool checkout; |
392 | 0 | int error; |
393 | |
|
394 | 0 | if (opts->checkout_branch) |
395 | 0 | error = update_head_to_branch(repo, remote, opts->checkout_branch, reflog_message); |
396 | | /* Point HEAD to the same ref as the remote's head */ |
397 | 0 | else |
398 | 0 | error = update_head_to_remote(repo, remote, reflog_message); |
399 | |
|
400 | 0 | if (error < 0) |
401 | 0 | return error; |
402 | | |
403 | 0 | if ((error = should_checkout(&checkout, repo, git_repository_is_bare(repo), opts)) < 0) |
404 | 0 | return error; |
405 | | |
406 | 0 | if (checkout) |
407 | 0 | error = git_checkout_head(repo, &opts->checkout_opts); |
408 | |
|
409 | 0 | return error; |
410 | 0 | } |
411 | | |
412 | | static int clone_into( |
413 | | git_repository *repo, |
414 | | git_remote *_remote, |
415 | | const git_clone_options *opts) |
416 | 0 | { |
417 | 0 | git_str reflog_message = GIT_STR_INIT; |
418 | 0 | git_remote_connect_options connect_opts = GIT_REMOTE_CONNECT_OPTIONS_INIT; |
419 | 0 | git_remote *remote; |
420 | 0 | git_oid_t oid_type; |
421 | 0 | int error; |
422 | |
|
423 | 0 | GIT_ASSERT_ARG(repo); |
424 | 0 | GIT_ASSERT_ARG(_remote); |
425 | | |
426 | 0 | if (!git_repository_is_empty(repo)) { |
427 | 0 | git_error_set(GIT_ERROR_INVALID, "the repository is not empty"); |
428 | 0 | return -1; |
429 | 0 | } |
430 | | |
431 | 0 | if ((error = git_remote_dup(&remote, _remote)) < 0) |
432 | 0 | return error; |
433 | | |
434 | 0 | if ((error = git_remote_connect_options__from_fetch_opts(&connect_opts, remote, &opts->fetch_opts)) < 0) |
435 | 0 | goto cleanup; |
436 | | |
437 | 0 | git_str_printf(&reflog_message, "clone: from %s", git_remote_url(remote)); |
438 | | |
439 | | /* |
440 | | * Connect to the server so that we can identify the remote |
441 | | * object format. |
442 | | */ |
443 | |
|
444 | 0 | if ((error = git_remote_connect_ext(remote, GIT_DIRECTION_FETCH, |
445 | 0 | &connect_opts)) < 0) |
446 | 0 | goto cleanup; |
447 | | |
448 | 0 | if ((error = git_remote_oid_type(&oid_type, remote)) < 0 || |
449 | 0 | (error = git_repository__set_objectformat(repo, oid_type)) < 0) |
450 | 0 | goto cleanup; |
451 | | |
452 | 0 | if ((error = git_remote_fetch(remote, NULL, &opts->fetch_opts, git_str_cstr(&reflog_message))) != 0) |
453 | 0 | goto cleanup; |
454 | | |
455 | 0 | error = checkout_branch(repo, remote, opts, git_str_cstr(&reflog_message)); |
456 | |
|
457 | 0 | cleanup: |
458 | 0 | git_remote_free(remote); |
459 | 0 | git_remote_connect_options_dispose(&connect_opts); |
460 | 0 | git_str_dispose(&reflog_message); |
461 | |
|
462 | 0 | return error; |
463 | 0 | } |
464 | | |
465 | | static bool can_link(const char *src, const char *dst, int link) |
466 | 0 | { |
467 | | #ifdef GIT_WIN32 |
468 | | GIT_UNUSED(src); |
469 | | GIT_UNUSED(dst); |
470 | | GIT_UNUSED(link); |
471 | | return false; |
472 | | #else |
473 | |
|
474 | 0 | struct stat st_src, st_dst; |
475 | |
|
476 | 0 | if (!link) |
477 | 0 | return false; |
478 | | |
479 | 0 | if (p_stat(src, &st_src) < 0) |
480 | 0 | return false; |
481 | | |
482 | 0 | if (p_stat(dst, &st_dst) < 0) |
483 | 0 | return false; |
484 | | |
485 | 0 | return st_src.st_dev == st_dst.st_dev; |
486 | 0 | #endif |
487 | 0 | } |
488 | | |
489 | | static int clone_local_into( |
490 | | git_repository *repo, |
491 | | git_remote *remote, |
492 | | const git_clone_options *opts) |
493 | 0 | { |
494 | 0 | int error, flags; |
495 | 0 | git_repository *src; |
496 | 0 | git_str src_odb = GIT_STR_INIT, dst_odb = GIT_STR_INIT, src_path = GIT_STR_INIT; |
497 | 0 | git_str reflog_message = GIT_STR_INIT; |
498 | 0 | bool link = (opts && opts->local != GIT_CLONE_LOCAL_NO_LINKS); |
499 | |
|
500 | 0 | GIT_ASSERT_ARG(repo); |
501 | 0 | GIT_ASSERT_ARG(remote); |
502 | | |
503 | 0 | if (!git_repository_is_empty(repo)) { |
504 | 0 | git_error_set(GIT_ERROR_INVALID, "the repository is not empty"); |
505 | 0 | return -1; |
506 | 0 | } |
507 | | |
508 | | /* |
509 | | * Let's figure out what path we should use for the source |
510 | | * repo, if it's not rooted, the path should be relative to |
511 | | * the repository's worktree/gitdir. |
512 | | */ |
513 | 0 | if ((error = git_fs_path_from_url_or_path(&src_path, git_remote_url(remote))) < 0) |
514 | 0 | return error; |
515 | | |
516 | | /* Copy .git/objects/ from the source to the target */ |
517 | 0 | if ((error = git_repository_open(&src, git_str_cstr(&src_path))) < 0) { |
518 | 0 | git_str_dispose(&src_path); |
519 | 0 | return error; |
520 | 0 | } |
521 | | |
522 | 0 | if (git_repository__item_path(&src_odb, src, GIT_REPOSITORY_ITEM_OBJECTS) < 0 || |
523 | 0 | git_repository__item_path(&dst_odb, repo, GIT_REPOSITORY_ITEM_OBJECTS) < 0) { |
524 | 0 | error = -1; |
525 | 0 | goto cleanup; |
526 | 0 | } |
527 | | |
528 | 0 | flags = 0; |
529 | 0 | if (can_link(git_repository_path(src), git_repository_path(repo), link)) |
530 | 0 | flags |= GIT_CPDIR_LINK_FILES; |
531 | |
|
532 | 0 | error = git_futils_cp_r(git_str_cstr(&src_odb), git_str_cstr(&dst_odb), |
533 | 0 | flags, GIT_OBJECT_DIR_MODE); |
534 | | |
535 | | /* |
536 | | * can_link() doesn't catch all variations, so if we hit an |
537 | | * error and did want to link, let's try again without trying |
538 | | * to link. |
539 | | */ |
540 | 0 | if (error < 0 && link) { |
541 | 0 | flags &= ~GIT_CPDIR_LINK_FILES; |
542 | 0 | error = git_futils_cp_r(git_str_cstr(&src_odb), git_str_cstr(&dst_odb), |
543 | 0 | flags, GIT_OBJECT_DIR_MODE); |
544 | 0 | } |
545 | |
|
546 | 0 | if (error < 0) |
547 | 0 | goto cleanup; |
548 | | |
549 | 0 | git_str_printf(&reflog_message, "clone: from %s", git_remote_url(remote)); |
550 | |
|
551 | 0 | if ((error = git_remote_fetch(remote, NULL, &opts->fetch_opts, git_str_cstr(&reflog_message))) != 0) |
552 | 0 | goto cleanup; |
553 | | |
554 | 0 | error = checkout_branch(repo, remote, opts, git_str_cstr(&reflog_message)); |
555 | |
|
556 | 0 | cleanup: |
557 | 0 | git_str_dispose(&reflog_message); |
558 | 0 | git_str_dispose(&src_path); |
559 | 0 | git_str_dispose(&src_odb); |
560 | 0 | git_str_dispose(&dst_odb); |
561 | 0 | git_repository_free(src); |
562 | 0 | return error; |
563 | 0 | } |
564 | | |
565 | | int git_clone__should_clone_local( |
566 | | bool *out, |
567 | | const char *url_or_path, |
568 | | git_clone_local_t local) |
569 | 0 | { |
570 | 0 | git_str fromurl = GIT_STR_INIT; |
571 | |
|
572 | 0 | *out = false; |
573 | |
|
574 | 0 | if (local == GIT_CLONE_NO_LOCAL) |
575 | 0 | return 0; |
576 | | |
577 | 0 | if (git_net_str_is_url(url_or_path)) { |
578 | | /* If GIT_CLONE_LOCAL_AUTO is specified, any url should |
579 | | * be treated as remote */ |
580 | 0 | if (local == GIT_CLONE_LOCAL_AUTO || |
581 | 0 | !git_fs_path_is_local_file_url(url_or_path)) |
582 | 0 | return 0; |
583 | | |
584 | 0 | if (git_fs_path_fromurl(&fromurl, url_or_path) < 0) |
585 | 0 | return -1; |
586 | | |
587 | 0 | *out = git_fs_path_isdir(git_str_cstr(&fromurl)); |
588 | 0 | git_str_dispose(&fromurl); |
589 | 0 | } else { |
590 | 0 | *out = git_fs_path_isdir(url_or_path); |
591 | 0 | } |
592 | | |
593 | 0 | return 0; |
594 | 0 | } |
595 | | |
596 | | static int clone_repo( |
597 | | git_repository **out, |
598 | | const char *url, |
599 | | const char *local_path, |
600 | | const git_clone_options *given_opts, |
601 | | int use_existing) |
602 | 0 | { |
603 | 0 | int error = 0; |
604 | 0 | git_repository *repo = NULL; |
605 | 0 | git_remote *origin; |
606 | 0 | git_clone_options options = GIT_CLONE_OPTIONS_INIT; |
607 | 0 | uint32_t rmdir_flags = GIT_RMDIR_REMOVE_FILES; |
608 | 0 | git_repository_create_cb repository_cb; |
609 | |
|
610 | 0 | GIT_ASSERT_ARG(out); |
611 | 0 | GIT_ASSERT_ARG(url); |
612 | 0 | GIT_ASSERT_ARG(local_path); |
613 | | |
614 | 0 | if (given_opts) |
615 | 0 | memcpy(&options, given_opts, sizeof(git_clone_options)); |
616 | |
|
617 | 0 | GIT_ERROR_CHECK_VERSION(&options, GIT_CLONE_OPTIONS_VERSION, "git_clone_options"); |
618 | | |
619 | | /* enforce some behavior on fetch */ |
620 | 0 | options.fetch_opts.update_fetchhead = 0; |
621 | |
|
622 | 0 | if (!options.fetch_opts.depth) |
623 | 0 | options.fetch_opts.download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL; |
624 | | |
625 | | /* Only clone to a new directory or an empty directory */ |
626 | 0 | if (git_fs_path_exists(local_path) && !use_existing && !git_fs_path_is_empty_dir(local_path)) { |
627 | 0 | git_error_set(GIT_ERROR_INVALID, |
628 | 0 | "'%s' exists and is not an empty directory", local_path); |
629 | 0 | return GIT_EEXISTS; |
630 | 0 | } |
631 | | |
632 | | /* Only remove the root directory on failure if we create it */ |
633 | 0 | if (git_fs_path_exists(local_path)) |
634 | 0 | rmdir_flags |= GIT_RMDIR_SKIP_ROOT; |
635 | |
|
636 | 0 | if (options.repository_cb) |
637 | 0 | repository_cb = options.repository_cb; |
638 | 0 | else |
639 | 0 | repository_cb = default_repository_create; |
640 | |
|
641 | 0 | if ((error = repository_cb(&repo, local_path, options.bare, options.repository_cb_payload)) < 0) |
642 | 0 | return error; |
643 | | |
644 | 0 | if (!(error = create_and_configure_origin(&origin, repo, url, &options))) { |
645 | 0 | bool clone_local; |
646 | |
|
647 | 0 | if ((error = git_clone__should_clone_local(&clone_local, url, options.local)) < 0) { |
648 | 0 | git_remote_free(origin); |
649 | 0 | return error; |
650 | 0 | } |
651 | | |
652 | 0 | if (clone_local) |
653 | 0 | error = clone_local_into(repo, origin, &options); |
654 | 0 | else |
655 | 0 | error = clone_into(repo, origin, &options); |
656 | |
|
657 | 0 | git_remote_free(origin); |
658 | 0 | } |
659 | | |
660 | 0 | if (error != 0) { |
661 | 0 | git_error *last_error; |
662 | 0 | git_error_save(&last_error); |
663 | |
|
664 | 0 | git_repository_free(repo); |
665 | 0 | repo = NULL; |
666 | |
|
667 | 0 | (void)git_futils_rmdir_r(local_path, NULL, rmdir_flags); |
668 | |
|
669 | 0 | git_error_restore(last_error); |
670 | 0 | } |
671 | |
|
672 | 0 | *out = repo; |
673 | 0 | return error; |
674 | 0 | } |
675 | | |
676 | | int git_clone( |
677 | | git_repository **out, |
678 | | const char *url, |
679 | | const char *local_path, |
680 | | const git_clone_options *options) |
681 | 0 | { |
682 | 0 | return clone_repo(out, url, local_path, options, 0); |
683 | 0 | } |
684 | | |
685 | | int git_clone__submodule( |
686 | | git_repository **out, |
687 | | const char *url, |
688 | | const char *local_path, |
689 | | const git_clone_options *options) |
690 | 0 | { |
691 | 0 | return clone_repo(out, url, local_path, options, 1); |
692 | 0 | } |
693 | | |
694 | | int git_clone_options_init(git_clone_options *opts, unsigned int version) |
695 | 0 | { |
696 | 0 | GIT_INIT_STRUCTURE_FROM_TEMPLATE( |
697 | 0 | opts, version, git_clone_options, GIT_CLONE_OPTIONS_INIT); |
698 | 0 | return 0; |
699 | 0 | } |
700 | | |
701 | | #ifndef GIT_DEPRECATE_HARD |
702 | | int git_clone_init_options(git_clone_options *opts, unsigned int version) |
703 | 0 | { |
704 | 0 | return git_clone_options_init(opts, version); |
705 | 0 | } |
706 | | #endif |