]>
Commit | Line | Data |
---|---|---|
764df57e | 1 | /* |
359fc2d2 | 2 | * Copyright (C) the libgit2 contributors. All rights reserved. |
764df57e BS |
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 | ||
eae0bfdc PP |
8 | #include "clone.h" |
9 | ||
764df57e BS |
10 | #include "git2/clone.h" |
11 | #include "git2/remote.h" | |
bb1f6087 | 12 | #include "git2/revparse.h" |
4fbc899a BS |
13 | #include "git2/branch.h" |
14 | #include "git2/config.h" | |
14741d62 | 15 | #include "git2/checkout.h" |
2b63db4c BS |
16 | #include "git2/commit.h" |
17 | #include "git2/tree.h" | |
764df57e | 18 | |
764df57e | 19 | #include "remote.h" |
22a2d3d5 | 20 | #include "futils.h" |
4fbc899a | 21 | #include "refs.h" |
e579e0f7 | 22 | #include "fs_path.h" |
114f5a6c | 23 | #include "repository.h" |
4386d80b | 24 | #include "odb.h" |
764df57e | 25 | |
8f0104ec | 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); |
6812afaf | 27 | |
bf0e62a2 | 28 | static int create_branch( |
7eca3c56 | 29 | git_reference **branch, |
30 | git_repository *repo, | |
31 | const git_oid *target, | |
1cc974ab | 32 | const char *name, |
1cc974ab | 33 | const char *log_message) |
4fbc899a | 34 | { |
cfbe4be3 | 35 | git_commit *head_obj = NULL; |
132c2db6 | 36 | git_reference *branch_ref = NULL; |
e579e0f7 | 37 | git_str refname = GIT_STR_INIT; |
bf0e62a2 | 38 | int error; |
ea817863 BS |
39 | |
40 | /* Find the target commit */ | |
cfbe4be3 | 41 | if ((error = git_commit_lookup(&head_obj, repo, target)) < 0) |
bf0e62a2 | 42 | return error; |
ea817863 BS |
43 | |
44 | /* Create the new branch */ | |
e579e0f7 | 45 | if ((error = git_str_printf(&refname, GIT_REFS_HEADS_DIR "%s", name)) < 0) |
6bfb990d | 46 | return error; |
ea817863 | 47 | |
e579e0f7 MB |
48 | error = git_reference_create(&branch_ref, repo, git_str_cstr(&refname), target, 0, log_message); |
49 | git_str_dispose(&refname); | |
cfbe4be3 | 50 | git_commit_free(head_obj); |
7eca3c56 | 51 | |
bf0e62a2 | 52 | if (!error) |
7eca3c56 | 53 | *branch = branch_ref; |
54 | else | |
55 | git_reference_free(branch_ref); | |
56 | ||
bf0e62a2 | 57 | return error; |
58 | } | |
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 | { | |
66 | git_config *cfg; | |
e579e0f7 | 67 | git_str remote_key = GIT_STR_INIT, merge_key = GIT_STR_INIT; |
bf0e62a2 | 68 | int error = -1; |
69 | ||
70 | if (git_repository_config__weakptr(&cfg, repo) < 0) | |
71 | return -1; | |
72 | ||
e579e0f7 | 73 | if (git_str_printf(&remote_key, "branch.%s.remote", branch_name) < 0) |
bf0e62a2 | 74 | goto cleanup; |
75 | ||
e579e0f7 | 76 | if (git_str_printf(&merge_key, "branch.%s.merge", branch_name) < 0) |
bf0e62a2 | 77 | goto cleanup; |
78 | ||
e579e0f7 | 79 | if (git_config_set_string(cfg, git_str_cstr(&remote_key), remote_name) < 0) |
bf0e62a2 | 80 | goto cleanup; |
81 | ||
e579e0f7 | 82 | if (git_config_set_string(cfg, git_str_cstr(&merge_key), merge_target) < 0) |
bf0e62a2 | 83 | goto cleanup; |
84 | ||
85 | error = 0; | |
86 | ||
87 | cleanup: | |
e579e0f7 MB |
88 | git_str_dispose(&remote_key); |
89 | git_str_dispose(&merge_key); | |
bf0e62a2 | 90 | return error; |
91 | } | |
92 | ||
93 | static int create_tracking_branch( | |
94 | git_reference **branch, | |
95 | git_repository *repo, | |
96 | const git_oid *target, | |
1cc974ab | 97 | const char *branch_name, |
1cc974ab | 98 | const char *log_message) |
bf0e62a2 | 99 | { |
100 | int error; | |
101 | ||
659cf202 | 102 | if ((error = create_branch(branch, repo, target, branch_name, log_message)) < 0) |
bf0e62a2 | 103 | return error; |
104 | ||
105 | return setup_tracking_config( | |
106 | repo, | |
107 | branch_name, | |
108 | GIT_REMOTE_ORIGIN, | |
109 | git_reference_name(*branch)); | |
4fbc899a BS |
110 | } |
111 | ||
bf0e62a2 | 112 | static int update_head_to_new_branch( |
113 | git_repository *repo, | |
114 | const git_oid *target, | |
94f263f5 BS |
115 | const char *name, |
116 | const char *reflog_message) | |
af58ec9e | 117 | { |
aa4437f6 | 118 | git_reference *tracking_branch = NULL; |
4c4408c3 | 119 | int error; |
cdb8a608 CMN |
120 | |
121 | if (!git__prefixcmp(name, GIT_REFS_HEADS_DIR)) | |
122 | name += strlen(GIT_REFS_HEADS_DIR); | |
123 | ||
4c4408c3 | 124 | error = create_tracking_branch(&tracking_branch, repo, target, name, |
659cf202 | 125 | reflog_message); |
ea817863 | 126 | |
dab89f9b RB |
127 | if (!error) |
128 | error = git_repository_set_head( | |
4e498646 | 129 | repo, git_reference_name(tracking_branch)); |
7eca3c56 | 130 | |
131 | git_reference_free(tracking_branch); | |
132 | ||
32332fcc CMN |
133 | /* if it already existed, then the user's refspec created it for us, ignore it' */ |
134 | if (error == GIT_EEXISTS) | |
135 | error = 0; | |
136 | ||
7eca3c56 | 137 | return error; |
af58ec9e BS |
138 | } |
139 | ||
22a2d3d5 UG |
140 | static int update_head_to_default(git_repository *repo) |
141 | { | |
e579e0f7 | 142 | git_str initialbranch = GIT_STR_INIT; |
22a2d3d5 UG |
143 | const char *branch_name; |
144 | int error = 0; | |
145 | ||
146 | if ((error = git_repository_initialbranch(&initialbranch, repo)) < 0) | |
147 | goto done; | |
148 | ||
149 | if (git__prefixcmp(initialbranch.ptr, GIT_REFS_HEADS_DIR) != 0) { | |
150 | git_error_set(GIT_ERROR_INVALID, "invalid initial branch '%s'", initialbranch.ptr); | |
151 | error = -1; | |
152 | goto done; | |
153 | } | |
154 | ||
155 | branch_name = initialbranch.ptr + strlen(GIT_REFS_HEADS_DIR); | |
156 | ||
157 | error = setup_tracking_config(repo, branch_name, GIT_REMOTE_ORIGIN, | |
158 | initialbranch.ptr); | |
159 | ||
160 | done: | |
e579e0f7 | 161 | git_str_dispose(&initialbranch); |
22a2d3d5 UG |
162 | return error; |
163 | } | |
164 | ||
165 | static int update_remote_head( | |
166 | git_repository *repo, | |
167 | git_remote *remote, | |
e579e0f7 | 168 | git_str *target, |
22a2d3d5 UG |
169 | const char *reflog_message) |
170 | { | |
171 | git_refspec *refspec; | |
172 | git_reference *remote_head = NULL; | |
e579e0f7 MB |
173 | git_str remote_head_name = GIT_STR_INIT; |
174 | git_str remote_branch_name = GIT_STR_INIT; | |
22a2d3d5 UG |
175 | int error; |
176 | ||
177 | /* Determine the remote tracking ref name from the local branch */ | |
e579e0f7 | 178 | refspec = git_remote__matching_refspec(remote, git_str_cstr(target)); |
22a2d3d5 UG |
179 | |
180 | if (refspec == NULL) { | |
181 | git_error_set(GIT_ERROR_NET, "the remote's default branch does not fit the refspec configuration"); | |
182 | error = GIT_EINVALIDSPEC; | |
183 | goto cleanup; | |
184 | } | |
185 | ||
e579e0f7 | 186 | if ((error = git_refspec__transform( |
22a2d3d5 UG |
187 | &remote_branch_name, |
188 | refspec, | |
e579e0f7 | 189 | git_str_cstr(target))) < 0) |
22a2d3d5 UG |
190 | goto cleanup; |
191 | ||
e579e0f7 | 192 | if ((error = git_str_printf(&remote_head_name, |
22a2d3d5 UG |
193 | "%s%s/%s", |
194 | GIT_REFS_REMOTES_DIR, | |
195 | git_remote_name(remote), | |
196 | GIT_HEAD_FILE)) < 0) | |
197 | goto cleanup; | |
198 | ||
199 | error = git_reference_symbolic_create( | |
200 | &remote_head, | |
201 | repo, | |
e579e0f7 MB |
202 | git_str_cstr(&remote_head_name), |
203 | git_str_cstr(&remote_branch_name), | |
22a2d3d5 UG |
204 | true, |
205 | reflog_message); | |
206 | ||
207 | cleanup: | |
208 | git_reference_free(remote_head); | |
e579e0f7 MB |
209 | git_str_dispose(&remote_branch_name); |
210 | git_str_dispose(&remote_head_name); | |
22a2d3d5 UG |
211 | return error; |
212 | } | |
213 | ||
94f263f5 BS |
214 | static int update_head_to_remote( |
215 | git_repository *repo, | |
216 | git_remote *remote, | |
217 | const char *reflog_message) | |
8340dd5d | 218 | { |
15c30b72 | 219 | int error = 0; |
359dce72 | 220 | size_t refs_len; |
359dce72 | 221 | const git_remote_head *remote_head, **refs; |
2a597116 | 222 | const git_oid *remote_head_id; |
e579e0f7 | 223 | git_str branch = GIT_STR_INIT; |
ea817863 | 224 | |
dab89f9b RB |
225 | if ((error = git_remote_ls(&refs, &refs_len, remote)) < 0) |
226 | return error; | |
359dce72 | 227 | |
e128a1af CMN |
228 | /* We cloned an empty repository or one with an unborn HEAD */ |
229 | if (refs_len == 0 || strcmp(refs[0]->name, GIT_HEAD_FILE)) | |
22a2d3d5 | 230 | return update_head_to_default(repo); |
bf0e62a2 | 231 | |
15c30b72 | 232 | /* We know we have HEAD, let's see where it points */ |
359dce72 | 233 | remote_head = refs[0]; |
c25aa7cd | 234 | GIT_ASSERT(remote_head); |
41fb1ca0 | 235 | |
2a597116 | 236 | remote_head_id = &remote_head->oid; |
15c30b72 | 237 | |
e579e0f7 | 238 | error = git_remote__default_branch(&branch, remote); |
15c30b72 CMN |
239 | if (error == GIT_ENOTFOUND) { |
240 | error = git_repository_set_head_detached( | |
4e498646 | 241 | repo, remote_head_id); |
15c30b72 CMN |
242 | goto cleanup; |
243 | } | |
244 | ||
22a2d3d5 | 245 | if ((error = update_remote_head(repo, remote, &branch, reflog_message)) < 0) |
15c30b72 | 246 | goto cleanup; |
d280c71b | 247 | |
15c30b72 CMN |
248 | error = update_head_to_new_branch( |
249 | repo, | |
250 | remote_head_id, | |
e579e0f7 | 251 | git_str_cstr(&branch), |
659cf202 | 252 | reflog_message); |
ea817863 | 253 | |
15c30b72 | 254 | cleanup: |
e579e0f7 | 255 | git_str_dispose(&branch); |
15c30b72 | 256 | |
dab89f9b | 257 | return error; |
bb1f6087 BS |
258 | } |
259 | ||
88aef766 SC |
260 | static int update_head_to_branch( |
261 | git_repository *repo, | |
c25aa7cd | 262 | git_remote *remote, |
94f263f5 BS |
263 | const char *branch, |
264 | const char *reflog_message) | |
88aef766 SC |
265 | { |
266 | int retcode; | |
e579e0f7 | 267 | git_str remote_branch_name = GIT_STR_INIT; |
c25aa7cd | 268 | git_reference *remote_ref = NULL; |
e579e0f7 | 269 | git_str default_branch = GIT_STR_INIT; |
1fed6b07 | 270 | |
c25aa7cd PP |
271 | GIT_ASSERT_ARG(remote); |
272 | GIT_ASSERT_ARG(branch); | |
88aef766 | 273 | |
e579e0f7 | 274 | if ((retcode = git_str_printf(&remote_branch_name, GIT_REFS_REMOTES_DIR "%s/%s", |
c25aa7cd | 275 | git_remote_name(remote), branch)) < 0 ) |
88aef766 SC |
276 | goto cleanup; |
277 | ||
e579e0f7 | 278 | if ((retcode = git_reference_lookup(&remote_ref, repo, git_str_cstr(&remote_branch_name))) < 0) |
88aef766 SC |
279 | goto cleanup; |
280 | ||
c25aa7cd PP |
281 | if ((retcode = update_head_to_new_branch(repo, git_reference_target(remote_ref), branch, |
282 | reflog_message)) < 0) | |
283 | goto cleanup; | |
284 | ||
e579e0f7 | 285 | if ((retcode = git_remote__default_branch(&default_branch, remote)) < 0) |
c25aa7cd PP |
286 | goto cleanup; |
287 | ||
e579e0f7 | 288 | if (!git_remote__matching_refspec(remote, git_str_cstr(&default_branch))) |
c25aa7cd PP |
289 | goto cleanup; |
290 | ||
291 | retcode = update_remote_head(repo, remote, &default_branch, reflog_message); | |
88aef766 SC |
292 | |
293 | cleanup: | |
294 | git_reference_free(remote_ref); | |
e579e0f7 MB |
295 | git_str_dispose(&remote_branch_name); |
296 | git_str_dispose(&default_branch); | |
88aef766 SC |
297 | return retcode; |
298 | } | |
299 | ||
d58a64e9 CMN |
300 | static int default_repository_create(git_repository **out, const char *path, int bare, void *payload) |
301 | { | |
302 | GIT_UNUSED(payload); | |
303 | ||
304 | return git_repository_init(out, path, bare); | |
305 | } | |
306 | ||
1697cd6f PK |
307 | static int default_remote_create( |
308 | git_remote **out, | |
309 | git_repository *repo, | |
310 | const char *name, | |
311 | const char *url, | |
312 | void *payload) | |
313 | { | |
8f0104ec | 314 | GIT_UNUSED(payload); |
1697cd6f | 315 | |
8f0104ec | 316 | return git_remote_create(out, repo, name, url); |
1697cd6f PK |
317 | } |
318 | ||
764df57e BS |
319 | /* |
320 | * submodules? | |
764df57e BS |
321 | */ |
322 | ||
b412d563 BS |
323 | static int create_and_configure_origin( |
324 | git_remote **out, | |
325 | git_repository *repo, | |
326 | const char *url, | |
327 | const git_clone_options *options) | |
328 | { | |
329 | int error; | |
00998a12 | 330 | git_remote *origin = NULL; |
a0b5f785 | 331 | char buf[GIT_PATH_MAX]; |
1697cd6f PK |
332 | git_remote_create_cb remote_create = options->remote_cb; |
333 | void *payload = options->remote_cb_payload; | |
a0b5f785 CMN |
334 | |
335 | /* If the path exists and is a dir, the url should be the absolute path */ | |
e579e0f7 | 336 | if (git_fs_path_root(url) < 0 && git_fs_path_exists(url) && git_fs_path_isdir(url)) { |
a0b5f785 CMN |
337 | if (p_realpath(url, buf) == NULL) |
338 | return -1; | |
339 | ||
340 | url = buf; | |
341 | } | |
b412d563 | 342 | |
1697cd6f PK |
343 | if (!remote_create) { |
344 | remote_create = default_remote_create; | |
8f0104ec | 345 | payload = NULL; |
1697cd6f | 346 | } |
b9bf5d70 | 347 | |
1697cd6f | 348 | if ((error = remote_create(&origin, repo, "origin", url, payload)) < 0) |
b412d563 BS |
349 | goto on_error; |
350 | ||
b412d563 BS |
351 | *out = origin; |
352 | return 0; | |
353 | ||
354 | on_error: | |
00998a12 | 355 | git_remote_free(origin); |
b412d563 BS |
356 | return error; |
357 | } | |
bb1f6087 | 358 | |
4d968f13 | 359 | static bool should_checkout( |
360 | git_repository *repo, | |
361 | bool is_bare, | |
6affd71f | 362 | const git_checkout_options *opts) |
4d968f13 | 363 | { |
364 | if (is_bare) | |
365 | return false; | |
366 | ||
367 | if (!opts) | |
368 | return false; | |
369 | ||
2850252a | 370 | if (opts->checkout_strategy == GIT_CHECKOUT_NONE) |
730df6d0 BS |
371 | return false; |
372 | ||
605da51a | 373 | return !git_repository_head_unborn(repo); |
4d968f13 | 374 | } |
acdd3d95 | 375 | |
659cf202 | 376 | static int checkout_branch(git_repository *repo, git_remote *remote, const git_checkout_options *co_opts, const char *branch, const char *reflog_message) |
4386d80b CMN |
377 | { |
378 | int error; | |
379 | ||
380 | if (branch) | |
c25aa7cd | 381 | error = update_head_to_branch(repo, remote, branch, reflog_message); |
4386d80b CMN |
382 | /* Point HEAD to the same ref as the remote's head */ |
383 | else | |
659cf202 | 384 | error = update_head_to_remote(repo, remote, reflog_message); |
4386d80b CMN |
385 | |
386 | if (!error && should_checkout(repo, git_repository_is_bare(repo), co_opts)) | |
387 | error = git_checkout_head(repo, co_opts); | |
388 | ||
389 | return error; | |
390 | } | |
391 | ||
8f0104ec | 392 | static int clone_into(git_repository *repo, git_remote *_remote, const git_fetch_options *opts, const git_checkout_options *co_opts, const char *branch) |
d19870d9 | 393 | { |
60cdf495 | 394 | int error; |
e579e0f7 | 395 | git_str reflog_message = GIT_STR_INIT; |
3eff2a57 | 396 | git_fetch_options fetch_opts; |
3c607685 | 397 | git_remote *remote; |
d19870d9 | 398 | |
c25aa7cd PP |
399 | GIT_ASSERT_ARG(repo); |
400 | GIT_ASSERT_ARG(_remote); | |
d19870d9 CMN |
401 | |
402 | if (!git_repository_is_empty(repo)) { | |
ac3d33df | 403 | git_error_set(GIT_ERROR_INVALID, "the repository is not empty"); |
d19870d9 CMN |
404 | return -1; |
405 | } | |
406 | ||
3c607685 | 407 | if ((error = git_remote_dup(&remote, _remote)) < 0) |
266af6d8 CMN |
408 | return error; |
409 | ||
3eff2a57 CMN |
410 | memcpy(&fetch_opts, opts, sizeof(git_fetch_options)); |
411 | fetch_opts.update_fetchhead = 0; | |
77254990 | 412 | fetch_opts.download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL; |
e579e0f7 | 413 | git_str_printf(&reflog_message, "clone: from %s", git_remote_url(remote)); |
d19870d9 | 414 | |
e579e0f7 | 415 | if ((error = git_remote_fetch(remote, NULL, &fetch_opts, git_str_cstr(&reflog_message))) != 0) |
d19870d9 CMN |
416 | goto cleanup; |
417 | ||
e579e0f7 | 418 | error = checkout_branch(repo, remote, co_opts, branch, git_str_cstr(&reflog_message)); |
d19870d9 CMN |
419 | |
420 | cleanup: | |
3c607685 | 421 | git_remote_free(remote); |
e579e0f7 | 422 | git_str_dispose(&reflog_message); |
d19870d9 CMN |
423 | |
424 | return error; | |
425 | } | |
426 | ||
529fd30d | 427 | int git_clone__should_clone_local(const char *url_or_path, git_clone_local_t local) |
121b2673 | 428 | { |
e579e0f7 | 429 | git_str fromurl = GIT_STR_INIT; |
529fd30d ET |
430 | const char *path = url_or_path; |
431 | bool is_url, is_local; | |
121b2673 CMN |
432 | |
433 | if (local == GIT_CLONE_NO_LOCAL) | |
529fd30d | 434 | return 0; |
121b2673 | 435 | |
e579e0f7 MB |
436 | if ((is_url = git_fs_path_is_local_file_url(url_or_path)) != 0) { |
437 | if (git_fs_path_fromurl(&fromurl, url_or_path) < 0) { | |
529fd30d ET |
438 | is_local = -1; |
439 | goto done; | |
440 | } | |
121b2673 | 441 | |
529fd30d ET |
442 | path = fromurl.ptr; |
443 | } | |
121b2673 | 444 | |
529fd30d | 445 | is_local = (!is_url || local != GIT_CLONE_LOCAL_AUTO) && |
e579e0f7 | 446 | git_fs_path_isdir(path); |
121b2673 | 447 | |
529fd30d | 448 | done: |
e579e0f7 | 449 | git_str_dispose(&fromurl); |
529fd30d | 450 | return is_local; |
121b2673 CMN |
451 | } |
452 | ||
22a2d3d5 | 453 | static int git__clone( |
bf0e62a2 | 454 | git_repository **out, |
b412d563 BS |
455 | const char *url, |
456 | const char *local_path, | |
22a2d3d5 UG |
457 | const git_clone_options *_options, |
458 | int use_existing) | |
764df57e | 459 | { |
219d3457 | 460 | int error = 0; |
ea817863 | 461 | git_repository *repo = NULL; |
e3a92f0d | 462 | git_remote *origin; |
fdc7e5e3 | 463 | git_clone_options options = GIT_CLONE_OPTIONS_INIT; |
219d3457 | 464 | uint32_t rmdir_flags = GIT_RMDIR_REMOVE_FILES; |
d58a64e9 | 465 | git_repository_create_cb repository_cb; |
b412d563 | 466 | |
c25aa7cd PP |
467 | GIT_ASSERT_ARG(out); |
468 | GIT_ASSERT_ARG(url); | |
469 | GIT_ASSERT_ARG(local_path); | |
ea817863 | 470 | |
fdc7e5e3 CMN |
471 | if (_options) |
472 | memcpy(&options, _options, sizeof(git_clone_options)); | |
473 | ||
ac3d33df | 474 | GIT_ERROR_CHECK_VERSION(&options, GIT_CLONE_OPTIONS_VERSION, "git_clone_options"); |
b412d563 | 475 | |
f6f48f90 | 476 | /* Only clone to a new directory or an empty directory */ |
e579e0f7 | 477 | if (git_fs_path_exists(local_path) && !use_existing && !git_fs_path_is_empty_dir(local_path)) { |
ac3d33df | 478 | git_error_set(GIT_ERROR_INVALID, |
46779411 | 479 | "'%s' exists and is not an empty directory", local_path); |
219d3457 | 480 | return GIT_EEXISTS; |
ea817863 BS |
481 | } |
482 | ||
219d3457 | 483 | /* Only remove the root directory on failure if we create it */ |
e579e0f7 | 484 | if (git_fs_path_exists(local_path)) |
219d3457 | 485 | rmdir_flags |= GIT_RMDIR_SKIP_ROOT; |
926acbcf | 486 | |
d58a64e9 CMN |
487 | if (options.repository_cb) |
488 | repository_cb = options.repository_cb; | |
489 | else | |
490 | repository_cb = default_repository_create; | |
491 | ||
492 | if ((error = repository_cb(&repo, local_path, options.bare, options.repository_cb_payload)) < 0) | |
219d3457 | 493 | return error; |
e3a92f0d | 494 | |
219d3457 | 495 | if (!(error = create_and_configure_origin(&origin, repo, url, &options))) { |
84a85d1b | 496 | int clone_local = git_clone__should_clone_local(url, options.local); |
529fd30d ET |
497 | int link = options.local != GIT_CLONE_LOCAL_NO_LINKS; |
498 | ||
84a85d1b | 499 | if (clone_local == 1) |
6812afaf | 500 | error = clone_local_into( |
8f0104ec | 501 | repo, origin, &options.fetch_opts, &options.checkout_opts, |
659cf202 | 502 | options.checkout_branch, link); |
84a85d1b | 503 | else if (clone_local == 0) |
6812afaf | 504 | error = clone_into( |
8f0104ec | 505 | repo, origin, &options.fetch_opts, &options.checkout_opts, |
659cf202 | 506 | options.checkout_branch); |
529fd30d ET |
507 | else |
508 | error = -1; | |
926acbcf | 509 | |
219d3457 RB |
510 | git_remote_free(origin); |
511 | } | |
926acbcf | 512 | |
8f1066a0 | 513 | if (error != 0) { |
1df8ad01 | 514 | git_error_state last_error = {0}; |
ac3d33df | 515 | git_error_state_capture(&last_error, error); |
1df8ad01 | 516 | |
219d3457 RB |
517 | git_repository_free(repo); |
518 | repo = NULL; | |
8f1066a0 | 519 | |
219d3457 | 520 | (void)git_futils_rmdir_r(local_path, NULL, rmdir_flags); |
1df8ad01 | 521 | |
ac3d33df | 522 | git_error_state_restore(&last_error); |
219d3457 | 523 | } |
ea817863 | 524 | |
e3a92f0d | 525 | *out = repo; |
219d3457 | 526 | return error; |
764df57e | 527 | } |
b9f81997 | 528 | |
22a2d3d5 UG |
529 | int git_clone( |
530 | git_repository **out, | |
531 | const char *url, | |
532 | const char *local_path, | |
533 | const git_clone_options *_options) | |
534 | { | |
535 | return git__clone(out, url, local_path, _options, 0); | |
536 | } | |
537 | ||
538 | int git_clone__submodule( | |
539 | git_repository **out, | |
540 | const char *url, | |
541 | const char *local_path, | |
542 | const git_clone_options *_options) | |
543 | { | |
544 | return git__clone(out, url, local_path, _options, 1); | |
545 | } | |
546 | ||
547 | int git_clone_options_init(git_clone_options *opts, unsigned int version) | |
b9f81997 | 548 | { |
702efc89 RB |
549 | GIT_INIT_STRUCTURE_FROM_TEMPLATE( |
550 | opts, version, git_clone_options, GIT_CLONE_OPTIONS_INIT); | |
551 | return 0; | |
b9f81997 | 552 | } |
4386d80b | 553 | |
22a2d3d5 UG |
554 | #ifndef GIT_DEPRECATE_HARD |
555 | int git_clone_init_options(git_clone_options *opts, unsigned int version) | |
556 | { | |
557 | return git_clone_options_init(opts, version); | |
558 | } | |
559 | #endif | |
560 | ||
2614819c CMN |
561 | static bool can_link(const char *src, const char *dst, int link) |
562 | { | |
563 | #ifdef GIT_WIN32 | |
959a93e7 JG |
564 | GIT_UNUSED(src); |
565 | GIT_UNUSED(dst); | |
566 | GIT_UNUSED(link); | |
2614819c CMN |
567 | return false; |
568 | #else | |
569 | ||
570 | struct stat st_src, st_dst; | |
571 | ||
572 | if (!link) | |
573 | return false; | |
574 | ||
575 | if (p_stat(src, &st_src) < 0) | |
576 | return false; | |
577 | ||
578 | if (p_stat(dst, &st_dst) < 0) | |
579 | return false; | |
580 | ||
581 | return st_src.st_dev == st_dst.st_dev; | |
582 | #endif | |
583 | } | |
584 | ||
8f0104ec | 585 | 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) |
4386d80b | 586 | { |
18d7896c | 587 | int error, flags; |
4386d80b | 588 | git_repository *src; |
e579e0f7 MB |
589 | git_str src_odb = GIT_STR_INIT, dst_odb = GIT_STR_INIT, src_path = GIT_STR_INIT; |
590 | git_str reflog_message = GIT_STR_INIT; | |
4386d80b | 591 | |
c25aa7cd PP |
592 | GIT_ASSERT_ARG(repo); |
593 | GIT_ASSERT_ARG(remote); | |
4386d80b CMN |
594 | |
595 | if (!git_repository_is_empty(repo)) { | |
ac3d33df | 596 | git_error_set(GIT_ERROR_INVALID, "the repository is not empty"); |
4386d80b CMN |
597 | return -1; |
598 | } | |
599 | ||
600 | /* | |
601 | * Let's figure out what path we should use for the source | |
602 | * repo, if it's not rooted, the path should be relative to | |
603 | * the repository's worktree/gitdir. | |
604 | */ | |
e579e0f7 | 605 | if ((error = git_fs_path_from_url_or_path(&src_path, git_remote_url(remote))) < 0) |
18d7896c | 606 | return error; |
4386d80b CMN |
607 | |
608 | /* Copy .git/objects/ from the source to the target */ | |
e579e0f7 MB |
609 | if ((error = git_repository_open(&src, git_str_cstr(&src_path))) < 0) { |
610 | git_str_dispose(&src_path); | |
4386d80b CMN |
611 | return error; |
612 | } | |
613 | ||
e579e0f7 MB |
614 | if (git_repository__item_path(&src_odb, src, GIT_REPOSITORY_ITEM_OBJECTS) < 0 || |
615 | git_repository__item_path(&dst_odb, repo, GIT_REPOSITORY_ITEM_OBJECTS) < 0) { | |
4386d80b CMN |
616 | error = -1; |
617 | goto cleanup; | |
618 | } | |
619 | ||
2614819c CMN |
620 | flags = 0; |
621 | if (can_link(git_repository_path(src), git_repository_path(repo), link)) | |
622 | flags |= GIT_CPDIR_LINK_FILES; | |
623 | ||
e579e0f7 | 624 | error = git_futils_cp_r(git_str_cstr(&src_odb), git_str_cstr(&dst_odb), |
10940736 CMN |
625 | flags, GIT_OBJECT_DIR_MODE); |
626 | ||
627 | /* | |
628 | * can_link() doesn't catch all variations, so if we hit an | |
629 | * error and did want to link, let's try again without trying | |
630 | * to link. | |
631 | */ | |
632 | if (error < 0 && link) { | |
633 | flags &= ~GIT_CPDIR_LINK_FILES; | |
e579e0f7 | 634 | error = git_futils_cp_r(git_str_cstr(&src_odb), git_str_cstr(&dst_odb), |
10940736 CMN |
635 | flags, GIT_OBJECT_DIR_MODE); |
636 | } | |
637 | ||
638 | if (error < 0) | |
4386d80b CMN |
639 | goto cleanup; |
640 | ||
e579e0f7 | 641 | git_str_printf(&reflog_message, "clone: from %s", git_remote_url(remote)); |
4386d80b | 642 | |
e579e0f7 | 643 | if ((error = git_remote_fetch(remote, NULL, fetch_opts, git_str_cstr(&reflog_message))) != 0) |
4386d80b CMN |
644 | goto cleanup; |
645 | ||
e579e0f7 | 646 | error = checkout_branch(repo, remote, co_opts, branch, git_str_cstr(&reflog_message)); |
4386d80b CMN |
647 | |
648 | cleanup: | |
e579e0f7 MB |
649 | git_str_dispose(&reflog_message); |
650 | git_str_dispose(&src_path); | |
651 | git_str_dispose(&src_odb); | |
652 | git_str_dispose(&dst_odb); | |
4386d80b CMN |
653 | git_repository_free(src); |
654 | return error; | |
655 | } |