]>
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 | ||
8 | #include <assert.h> | |
830388a7 | 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 BS |
18 | |
19 | #include "common.h" | |
20 | #include "remote.h" | |
21 | #include "fileops.h" | |
4fbc899a | 22 | #include "refs.h" |
c3b5099f | 23 | #include "path.h" |
114f5a6c | 24 | #include "repository.h" |
764df57e | 25 | |
bf0e62a2 | 26 | static int create_branch( |
7eca3c56 | 27 | git_reference **branch, |
28 | git_repository *repo, | |
29 | const git_oid *target, | |
1cc974ab BS |
30 | const char *name, |
31 | const git_signature *signature, | |
32 | const char *log_message) | |
4fbc899a | 33 | { |
cfbe4be3 | 34 | git_commit *head_obj = NULL; |
132c2db6 | 35 | git_reference *branch_ref = NULL; |
bf0e62a2 | 36 | int error; |
ea817863 BS |
37 | |
38 | /* Find the target commit */ | |
cfbe4be3 | 39 | if ((error = git_commit_lookup(&head_obj, repo, target)) < 0) |
bf0e62a2 | 40 | return error; |
ea817863 BS |
41 | |
42 | /* Create the new branch */ | |
1cc974ab | 43 | error = git_branch_create(&branch_ref, repo, name, head_obj, 0, signature, log_message); |
ea817863 | 44 | |
cfbe4be3 | 45 | git_commit_free(head_obj); |
7eca3c56 | 46 | |
bf0e62a2 | 47 | if (!error) |
7eca3c56 | 48 | *branch = branch_ref; |
49 | else | |
50 | git_reference_free(branch_ref); | |
51 | ||
bf0e62a2 | 52 | return error; |
53 | } | |
54 | ||
55 | static int setup_tracking_config( | |
56 | git_repository *repo, | |
57 | const char *branch_name, | |
58 | const char *remote_name, | |
59 | const char *merge_target) | |
60 | { | |
61 | git_config *cfg; | |
62 | git_buf remote_key = GIT_BUF_INIT, merge_key = GIT_BUF_INIT; | |
63 | int error = -1; | |
64 | ||
65 | if (git_repository_config__weakptr(&cfg, repo) < 0) | |
66 | return -1; | |
67 | ||
68 | if (git_buf_printf(&remote_key, "branch.%s.remote", branch_name) < 0) | |
69 | goto cleanup; | |
70 | ||
71 | if (git_buf_printf(&merge_key, "branch.%s.merge", branch_name) < 0) | |
72 | goto cleanup; | |
73 | ||
74 | if (git_config_set_string(cfg, git_buf_cstr(&remote_key), remote_name) < 0) | |
75 | goto cleanup; | |
76 | ||
77 | if (git_config_set_string(cfg, git_buf_cstr(&merge_key), merge_target) < 0) | |
78 | goto cleanup; | |
79 | ||
80 | error = 0; | |
81 | ||
82 | cleanup: | |
83 | git_buf_free(&remote_key); | |
84 | git_buf_free(&merge_key); | |
85 | return error; | |
86 | } | |
87 | ||
88 | static int create_tracking_branch( | |
89 | git_reference **branch, | |
90 | git_repository *repo, | |
91 | const git_oid *target, | |
1cc974ab BS |
92 | const char *branch_name, |
93 | const git_signature *signature, | |
94 | const char *log_message) | |
bf0e62a2 | 95 | { |
96 | int error; | |
97 | ||
1cc974ab | 98 | if ((error = create_branch(branch, repo, target, branch_name, signature, log_message)) < 0) |
bf0e62a2 | 99 | return error; |
100 | ||
101 | return setup_tracking_config( | |
102 | repo, | |
103 | branch_name, | |
104 | GIT_REMOTE_ORIGIN, | |
105 | git_reference_name(*branch)); | |
4fbc899a BS |
106 | } |
107 | ||
bf0e62a2 | 108 | static int update_head_to_new_branch( |
109 | git_repository *repo, | |
110 | const git_oid *target, | |
94f263f5 | 111 | const char *name, |
1cc974ab | 112 | const git_signature *signature, |
94f263f5 | 113 | const char *reflog_message) |
af58ec9e | 114 | { |
aa4437f6 | 115 | git_reference *tracking_branch = NULL; |
cdb8a608 CMN |
116 | |
117 | if (!git__prefixcmp(name, GIT_REFS_HEADS_DIR)) | |
118 | name += strlen(GIT_REFS_HEADS_DIR); | |
119 | ||
1cc974ab BS |
120 | int error = create_tracking_branch(&tracking_branch, repo, target, name, |
121 | signature, reflog_message); | |
ea817863 | 122 | |
dab89f9b RB |
123 | if (!error) |
124 | error = git_repository_set_head( | |
94f263f5 | 125 | repo, git_reference_name(tracking_branch), |
1cc974ab | 126 | signature, reflog_message); |
7eca3c56 | 127 | |
128 | git_reference_free(tracking_branch); | |
129 | ||
32332fcc CMN |
130 | /* if it already existed, then the user's refspec created it for us, ignore it' */ |
131 | if (error == GIT_EEXISTS) | |
132 | error = 0; | |
133 | ||
7eca3c56 | 134 | return error; |
af58ec9e BS |
135 | } |
136 | ||
94f263f5 BS |
137 | static int update_head_to_remote( |
138 | git_repository *repo, | |
139 | git_remote *remote, | |
1cc974ab | 140 | const git_signature *signature, |
94f263f5 | 141 | const char *reflog_message) |
8340dd5d | 142 | { |
2a597116 | 143 | int error = 0, found_branch = 0; |
359dce72 | 144 | size_t refs_len; |
2a597116 | 145 | git_refspec dummy_spec, *refspec; |
359dce72 | 146 | const git_remote_head *remote_head, **refs; |
2a597116 | 147 | const git_oid *remote_head_id; |
d280c71b | 148 | git_buf remote_master_name = GIT_BUF_INIT; |
cdb8a608 | 149 | git_buf branch = GIT_BUF_INIT; |
ea817863 | 150 | |
dab89f9b RB |
151 | if ((error = git_remote_ls(&refs, &refs_len, remote)) < 0) |
152 | return error; | |
359dce72 | 153 | |
bf0e62a2 | 154 | /* Did we just clone an empty repository? */ |
dab89f9b | 155 | if (refs_len == 0) |
bf0e62a2 | 156 | return setup_tracking_config( |
dab89f9b | 157 | repo, "master", GIT_REMOTE_ORIGIN, GIT_REFS_HEADS_MASTER_FILE); |
bf0e62a2 | 158 | |
cdb8a608 CMN |
159 | error = git_remote_default_branch(&branch, remote); |
160 | if (error == GIT_ENOTFOUND) { | |
161 | git_buf_puts(&branch, GIT_REFS_HEADS_MASTER_FILE); | |
162 | } else { | |
2a597116 | 163 | found_branch = 1; |
cdb8a608 CMN |
164 | } |
165 | ||
359dce72 CMN |
166 | /* Get the remote's HEAD. This is always the first ref in the list. */ |
167 | remote_head = refs[0]; | |
41fb1ca0 PK |
168 | assert(remote_head); |
169 | ||
2a597116 CMN |
170 | remote_head_id = &remote_head->oid; |
171 | refspec = git_remote__matching_refspec(remote, git_buf_cstr(&branch)); | |
4330ab26 | 172 | |
2a597116 | 173 | if (refspec == NULL) { |
4330ab26 | 174 | memset(&dummy_spec, 0, sizeof(git_refspec)); |
2a597116 | 175 | refspec = &dummy_spec; |
4330ab26 | 176 | } |
55ededfd | 177 | |
d280c71b | 178 | /* Determine the remote tracking reference name from the local master */ |
bf522e08 | 179 | if ((error = git_refspec_transform( |
d280c71b | 180 | &remote_master_name, |
2a597116 | 181 | refspec, |
cdb8a608 | 182 | git_buf_cstr(&branch))) < 0) |
dab89f9b | 183 | return error; |
d280c71b | 184 | |
2a597116 | 185 | if (found_branch) { |
dab89f9b | 186 | error = update_head_to_new_branch( |
d280c71b | 187 | repo, |
2a597116 | 188 | remote_head_id, |
cdb8a608 | 189 | git_buf_cstr(&branch), |
1cc974ab | 190 | signature, reflog_message); |
d280c71b | 191 | } else { |
dab89f9b | 192 | error = git_repository_set_head_detached( |
2a597116 | 193 | repo, remote_head_id, signature, reflog_message); |
ea817863 BS |
194 | } |
195 | ||
d280c71b | 196 | git_buf_free(&remote_master_name); |
cdb8a608 | 197 | git_buf_free(&branch); |
dab89f9b | 198 | return error; |
bb1f6087 BS |
199 | } |
200 | ||
88aef766 SC |
201 | static int update_head_to_branch( |
202 | git_repository *repo, | |
d19870d9 | 203 | const char *remote_name, |
94f263f5 | 204 | const char *branch, |
1cc974ab | 205 | const git_signature *signature, |
94f263f5 | 206 | const char *reflog_message) |
88aef766 SC |
207 | { |
208 | int retcode; | |
209 | git_buf remote_branch_name = GIT_BUF_INIT; | |
210 | git_reference* remote_ref = NULL; | |
1fed6b07 | 211 | |
d19870d9 | 212 | assert(remote_name && branch); |
88aef766 SC |
213 | |
214 | if ((retcode = git_buf_printf(&remote_branch_name, GIT_REFS_REMOTES_DIR "%s/%s", | |
d19870d9 | 215 | remote_name, branch)) < 0 ) |
88aef766 SC |
216 | goto cleanup; |
217 | ||
218 | if ((retcode = git_reference_lookup(&remote_ref, repo, git_buf_cstr(&remote_branch_name))) < 0) | |
219 | goto cleanup; | |
220 | ||
1cc974ab BS |
221 | retcode = update_head_to_new_branch(repo, git_reference_target(remote_ref), branch, |
222 | signature, reflog_message); | |
88aef766 SC |
223 | |
224 | cleanup: | |
225 | git_reference_free(remote_ref); | |
1265b51f | 226 | git_buf_free(&remote_branch_name); |
88aef766 SC |
227 | return retcode; |
228 | } | |
229 | ||
764df57e BS |
230 | /* |
231 | * submodules? | |
764df57e BS |
232 | */ |
233 | ||
b412d563 BS |
234 | static int create_and_configure_origin( |
235 | git_remote **out, | |
236 | git_repository *repo, | |
237 | const char *url, | |
238 | const git_clone_options *options) | |
239 | { | |
240 | int error; | |
00998a12 | 241 | git_remote *origin = NULL; |
c833893c | 242 | const char *name; |
b412d563 | 243 | |
c833893c CMN |
244 | name = options->remote_name ? options->remote_name : "origin"; |
245 | if ((error = git_remote_create(&origin, repo, name, url)) < 0) | |
b412d563 BS |
246 | goto on_error; |
247 | ||
b9bf5d70 CMN |
248 | if (options->ignore_cert_errors) |
249 | git_remote_check_cert(origin, 0); | |
250 | ||
0e0cf787 | 251 | if ((error = git_remote_set_callbacks(origin, &options->remote_callbacks)) < 0) |
b412d563 BS |
252 | goto on_error; |
253 | ||
621b50e4 BS |
254 | if ((error = git_remote_save(origin)) < 0) |
255 | goto on_error; | |
256 | ||
b412d563 BS |
257 | *out = origin; |
258 | return 0; | |
259 | ||
260 | on_error: | |
00998a12 | 261 | git_remote_free(origin); |
b412d563 BS |
262 | return error; |
263 | } | |
bb1f6087 | 264 | |
4d968f13 | 265 | static bool should_checkout( |
266 | git_repository *repo, | |
267 | bool is_bare, | |
6affd71f | 268 | const git_checkout_options *opts) |
4d968f13 | 269 | { |
270 | if (is_bare) | |
271 | return false; | |
272 | ||
273 | if (!opts) | |
274 | return false; | |
275 | ||
2850252a | 276 | if (opts->checkout_strategy == GIT_CHECKOUT_NONE) |
730df6d0 BS |
277 | return false; |
278 | ||
605da51a | 279 | return !git_repository_head_unborn(repo); |
4d968f13 | 280 | } |
acdd3d95 | 281 | |
3c607685 | 282 | int git_clone_into(git_repository *repo, git_remote *_remote, const git_checkout_options *co_opts, const char *branch, const git_signature *signature) |
d19870d9 | 283 | { |
60cdf495 | 284 | int error; |
94f263f5 | 285 | git_buf reflog_message = GIT_BUF_INIT; |
3c607685 CMN |
286 | git_remote *remote; |
287 | const git_remote_callbacks *callbacks; | |
d19870d9 | 288 | |
3c607685 | 289 | assert(repo && _remote); |
d19870d9 CMN |
290 | |
291 | if (!git_repository_is_empty(repo)) { | |
292 | giterr_set(GITERR_INVALID, "the repository is not empty"); | |
293 | return -1; | |
294 | } | |
295 | ||
3c607685 | 296 | if ((error = git_remote_dup(&remote, _remote)) < 0) |
266af6d8 CMN |
297 | return error; |
298 | ||
3c607685 CMN |
299 | callbacks = git_remote_get_callbacks(_remote); |
300 | if (!giterr__check_version(callbacks, 1, "git_remote_callbacks") && | |
60cdf495 | 301 | (error = git_remote_set_callbacks(remote, callbacks)) < 0) |
3c607685 CMN |
302 | goto cleanup; |
303 | ||
d19870d9 | 304 | if ((error = git_remote_add_fetch(remote, "refs/tags/*:refs/tags/*")) < 0) |
3c607685 | 305 | goto cleanup; |
d19870d9 | 306 | |
d19870d9 | 307 | git_remote_set_update_fetchhead(remote, 0); |
94f263f5 | 308 | git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote)); |
d19870d9 | 309 | |
c3ab1e5a | 310 | if ((error = git_remote_fetch(remote, signature, git_buf_cstr(&reflog_message))) != 0) |
d19870d9 CMN |
311 | goto cleanup; |
312 | ||
313 | if (branch) | |
1cc974ab BS |
314 | error = update_head_to_branch(repo, git_remote_name(remote), branch, |
315 | signature, git_buf_cstr(&reflog_message)); | |
d19870d9 CMN |
316 | /* Point HEAD to the same ref as the remote's head */ |
317 | else | |
1cc974ab | 318 | error = update_head_to_remote(repo, remote, signature, git_buf_cstr(&reflog_message)); |
d19870d9 CMN |
319 | |
320 | if (!error && should_checkout(repo, git_repository_is_bare(repo), co_opts)) | |
321 | error = git_checkout_head(repo, co_opts); | |
322 | ||
323 | cleanup: | |
3c607685 | 324 | git_remote_free(remote); |
94f263f5 | 325 | git_buf_free(&reflog_message); |
d19870d9 CMN |
326 | |
327 | return error; | |
328 | } | |
329 | ||
b412d563 | 330 | int git_clone( |
bf0e62a2 | 331 | git_repository **out, |
b412d563 BS |
332 | const char *url, |
333 | const char *local_path, | |
fdc7e5e3 | 334 | const git_clone_options *_options) |
764df57e | 335 | { |
219d3457 | 336 | int error = 0; |
ea817863 | 337 | git_repository *repo = NULL; |
e3a92f0d | 338 | git_remote *origin; |
fdc7e5e3 | 339 | git_clone_options options = GIT_CLONE_OPTIONS_INIT; |
219d3457 | 340 | uint32_t rmdir_flags = GIT_RMDIR_REMOVE_FILES; |
b412d563 BS |
341 | |
342 | assert(out && url && local_path); | |
ea817863 | 343 | |
fdc7e5e3 CMN |
344 | if (_options) |
345 | memcpy(&options, _options, sizeof(git_clone_options)); | |
346 | ||
347 | GITERR_CHECK_VERSION(&options, GIT_CLONE_OPTIONS_VERSION, "git_clone_options"); | |
b412d563 | 348 | |
f6f48f90 RB |
349 | /* Only clone to a new directory or an empty directory */ |
350 | if (git_path_exists(local_path) && !git_path_is_empty_dir(local_path)) { | |
351 | giterr_set(GITERR_INVALID, | |
46779411 | 352 | "'%s' exists and is not an empty directory", local_path); |
219d3457 | 353 | return GIT_EEXISTS; |
ea817863 BS |
354 | } |
355 | ||
219d3457 RB |
356 | /* Only remove the root directory on failure if we create it */ |
357 | if (git_path_exists(local_path)) | |
358 | rmdir_flags |= GIT_RMDIR_SKIP_ROOT; | |
926acbcf | 359 | |
219d3457 RB |
360 | if ((error = git_repository_init(&repo, local_path, options.bare)) < 0) |
361 | return error; | |
e3a92f0d | 362 | |
219d3457 RB |
363 | if (!(error = create_and_configure_origin(&origin, repo, url, &options))) { |
364 | error = git_clone_into( | |
1cc974ab | 365 | repo, origin, &options.checkout_opts, options.checkout_branch, options.signature); |
926acbcf | 366 | |
219d3457 RB |
367 | git_remote_free(origin); |
368 | } | |
926acbcf | 369 | |
8f1066a0 | 370 | if (error != 0) { |
1df8ad01 ET |
371 | git_error_state last_error = {0}; |
372 | giterr_capture(&last_error, error); | |
373 | ||
219d3457 RB |
374 | git_repository_free(repo); |
375 | repo = NULL; | |
8f1066a0 | 376 | |
219d3457 | 377 | (void)git_futils_rmdir_r(local_path, NULL, rmdir_flags); |
1df8ad01 ET |
378 | |
379 | giterr_restore(&last_error); | |
219d3457 | 380 | } |
ea817863 | 381 | |
e3a92f0d | 382 | *out = repo; |
219d3457 | 383 | return error; |
764df57e | 384 | } |
b9f81997 | 385 | |
702efc89 | 386 | int git_clone_init_options(git_clone_options *opts, unsigned int version) |
b9f81997 | 387 | { |
702efc89 RB |
388 | GIT_INIT_STRUCTURE_FROM_TEMPLATE( |
389 | opts, version, git_clone_options, GIT_CLONE_OPTIONS_INIT); | |
390 | return 0; | |
b9f81997 | 391 | } |