]> git.proxmox.com Git - libgit2.git/blob - src/clone.c
fix typo
[libgit2.git] / src / clone.c
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 <assert.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 "common.h"
20 #include "remote.h"
21 #include "fileops.h"
22 #include "refs.h"
23 #include "path.h"
24
25 static int create_branch(
26 git_reference **branch,
27 git_repository *repo,
28 const git_oid *target,
29 const char *name)
30 {
31 git_commit *head_obj = NULL;
32 git_reference *branch_ref = NULL;
33 int error;
34
35 /* Find the target commit */
36 if ((error = git_commit_lookup(&head_obj, repo, target)) < 0)
37 return error;
38
39 /* Create the new branch */
40 error = git_branch_create(&branch_ref, repo, name, head_obj, 0);
41
42 git_commit_free(head_obj);
43
44 if (!error)
45 *branch = branch_ref;
46 else
47 git_reference_free(branch_ref);
48
49 return error;
50 }
51
52 static int setup_tracking_config(
53 git_repository *repo,
54 const char *branch_name,
55 const char *remote_name,
56 const char *merge_target)
57 {
58 git_config *cfg;
59 git_buf remote_key = GIT_BUF_INIT, merge_key = GIT_BUF_INIT;
60 int error = -1;
61
62 if (git_repository_config__weakptr(&cfg, repo) < 0)
63 return -1;
64
65 if (git_buf_printf(&remote_key, "branch.%s.remote", branch_name) < 0)
66 goto cleanup;
67
68 if (git_buf_printf(&merge_key, "branch.%s.merge", branch_name) < 0)
69 goto cleanup;
70
71 if (git_config_set_string(cfg, git_buf_cstr(&remote_key), remote_name) < 0)
72 goto cleanup;
73
74 if (git_config_set_string(cfg, git_buf_cstr(&merge_key), merge_target) < 0)
75 goto cleanup;
76
77 error = 0;
78
79 cleanup:
80 git_buf_free(&remote_key);
81 git_buf_free(&merge_key);
82 return error;
83 }
84
85 static int create_tracking_branch(
86 git_reference **branch,
87 git_repository *repo,
88 const git_oid *target,
89 const char *branch_name)
90 {
91 int error;
92
93 if ((error = create_branch(branch, repo, target, branch_name)) < 0)
94 return error;
95
96 return setup_tracking_config(
97 repo,
98 branch_name,
99 GIT_REMOTE_ORIGIN,
100 git_reference_name(*branch));
101 }
102
103 struct head_info {
104 git_repository *repo;
105 git_oid remote_head_oid;
106 git_buf branchname;
107 const git_refspec *refspec;
108 bool found;
109 };
110
111 static int reference_matches_remote_head(
112 const char *reference_name,
113 void *payload)
114 {
115 struct head_info *head_info = (struct head_info *)payload;
116 git_oid oid;
117
118 /* TODO: Should we guard against references
119 * which name doesn't start with refs/heads/ ?
120 */
121
122 /* Stop looking if we've already found a match */
123 if (head_info->found)
124 return 0;
125
126 if (git_reference_name_to_id(
127 &oid,
128 head_info->repo,
129 reference_name) < 0) {
130 /* If the reference doesn't exists, it obviously cannot match the expected oid. */
131 giterr_clear();
132 return 0;
133 }
134
135 if (git_oid__cmp(&head_info->remote_head_oid, &oid) == 0) {
136 /* Determine the local reference name from the remote tracking one */
137 if (git_refspec_transform_l(
138 &head_info->branchname,
139 head_info->refspec,
140 reference_name) < 0)
141 return -1;
142
143 if (git_buf_len(&head_info->branchname) > 0) {
144 if (git_buf_sets(
145 &head_info->branchname,
146 git_buf_cstr(&head_info->branchname) + strlen(GIT_REFS_HEADS_DIR)) < 0)
147 return -1;
148
149 head_info->found = 1;
150 }
151 }
152
153 return 0;
154 }
155
156 static int update_head_to_new_branch(
157 git_repository *repo,
158 const git_oid *target,
159 const char *name)
160 {
161 git_reference *tracking_branch = NULL;
162 int error;
163
164 if ((error = create_tracking_branch(
165 &tracking_branch,
166 repo,
167 target,
168 name)) < 0)
169 return error;
170
171 error = git_repository_set_head(repo, git_reference_name(tracking_branch));
172
173 git_reference_free(tracking_branch);
174
175 return error;
176 }
177
178 static int get_head_callback(git_remote_head *head, void *payload)
179 {
180 git_remote_head **destination = (git_remote_head **)payload;
181
182 /* Save the first entry, and terminate the enumeration */
183 *destination = head;
184 return 1;
185 }
186
187 static int update_head_to_remote(git_repository *repo, git_remote *remote)
188 {
189 int retcode = -1;
190 git_refspec dummy_spec;
191 git_remote_head *remote_head;
192 struct head_info head_info;
193 git_buf remote_master_name = GIT_BUF_INIT;
194
195 /* Did we just clone an empty repository? */
196 if (remote->refs.length == 0) {
197 return setup_tracking_config(
198 repo,
199 "master",
200 GIT_REMOTE_ORIGIN,
201 GIT_REFS_HEADS_MASTER_FILE);
202 }
203
204 /* Get the remote's HEAD. This is always the first ref in remote->refs. */
205 remote_head = NULL;
206
207 if (!remote->transport->ls(remote->transport, get_head_callback, &remote_head))
208 return -1;
209
210 assert(remote_head);
211
212 git_oid_cpy(&head_info.remote_head_oid, &remote_head->oid);
213 git_buf_init(&head_info.branchname, 16);
214 head_info.repo = repo;
215 head_info.refspec = git_remote__matching_refspec(remote, GIT_REFS_HEADS_MASTER_FILE);
216 head_info.found = 0;
217
218 if (head_info.refspec == NULL) {
219 memset(&dummy_spec, 0, sizeof(git_refspec));
220 head_info.refspec = &dummy_spec;
221 }
222
223 /* Determine the remote tracking reference name from the local master */
224 if (git_refspec_transform_r(
225 &remote_master_name,
226 head_info.refspec,
227 GIT_REFS_HEADS_MASTER_FILE) < 0)
228 return -1;
229
230 /* Check to see if the remote HEAD points to the remote master */
231 if (reference_matches_remote_head(git_buf_cstr(&remote_master_name), &head_info) < 0)
232 goto cleanup;
233
234 if (head_info.found) {
235 retcode = update_head_to_new_branch(
236 repo,
237 &head_info.remote_head_oid,
238 git_buf_cstr(&head_info.branchname));
239
240 goto cleanup;
241 }
242
243 /* Not master. Check all the other refs. */
244 if (git_reference_foreach(
245 repo,
246 GIT_REF_LISTALL,
247 reference_matches_remote_head,
248 &head_info) < 0)
249 goto cleanup;
250
251 if (head_info.found) {
252 retcode = update_head_to_new_branch(
253 repo,
254 &head_info.remote_head_oid,
255 git_buf_cstr(&head_info.branchname));
256
257 goto cleanup;
258 } else {
259 retcode = git_repository_set_head_detached(
260 repo,
261 &head_info.remote_head_oid);
262 goto cleanup;
263 }
264
265 cleanup:
266 git_buf_free(&remote_master_name);
267 git_buf_free(&head_info.branchname);
268 return retcode;
269 }
270
271 static int update_head_to_branch(
272 git_repository *repo,
273 const git_clone_options *options)
274 {
275 int retcode;
276 git_buf remote_branch_name = GIT_BUF_INIT;
277 git_reference* remote_ref = NULL;
278
279 assert(options->checkout_branch);
280
281 if ((retcode = git_buf_printf(&remote_branch_name, GIT_REFS_REMOTES_DIR "%s/%s",
282 options->remote_name, options->checkout_branch)) < 0 )
283 goto cleanup;
284
285 if ((retcode = git_reference_lookup(&remote_ref, repo, git_buf_cstr(&remote_branch_name))) < 0)
286 goto cleanup;
287
288 retcode = update_head_to_new_branch(repo, git_reference_target(remote_ref),
289 options->checkout_branch);
290
291 cleanup:
292 git_reference_free(remote_ref);
293 git_buf_free(&remote_branch_name);
294 return retcode;
295 }
296
297 /*
298 * submodules?
299 */
300
301 static int create_and_configure_origin(
302 git_remote **out,
303 git_repository *repo,
304 const char *url,
305 const git_clone_options *options)
306 {
307 int error;
308 git_remote *origin = NULL;
309
310 if ((error = git_remote_create(&origin, repo, options->remote_name, url)) < 0)
311 goto on_error;
312
313 git_remote_set_cred_acquire_cb(origin, options->cred_acquire_cb,
314 options->cred_acquire_payload);
315 git_remote_set_autotag(origin, options->remote_autotag);
316 /*
317 * Don't write FETCH_HEAD, we'll check out the remote tracking
318 * branch ourselves based on the server's default.
319 */
320 git_remote_set_update_fetchhead(origin, 0);
321
322 if (options->remote_callbacks &&
323 (error = git_remote_set_callbacks(origin, options->remote_callbacks)) < 0)
324 goto on_error;
325
326 if (options->fetch_spec) {
327 git_remote_clear_refspecs(origin);
328 if ((error = git_remote_add_fetch(origin, options->fetch_spec)) < 0)
329 goto on_error;
330 }
331
332 if (options->push_spec &&
333 (error = git_remote_add_push(origin, options->push_spec)) < 0)
334 goto on_error;
335
336 if (options->pushurl &&
337 (error = git_remote_set_pushurl(origin, options->pushurl)) < 0)
338 goto on_error;
339
340 if ((error = git_remote_save(origin)) < 0)
341 goto on_error;
342
343 *out = origin;
344 return 0;
345
346 on_error:
347 git_remote_free(origin);
348 return error;
349 }
350
351
352 static int setup_remotes_and_fetch(
353 git_repository *repo,
354 const char *url,
355 const git_clone_options *options)
356 {
357 int retcode = GIT_ERROR;
358 git_remote *origin;
359
360 /* Construct an origin remote */
361 if (!create_and_configure_origin(&origin, repo, url, options)) {
362 git_remote_set_update_fetchhead(origin, 0);
363
364 /* Connect and download everything */
365 if (!git_remote_connect(origin, GIT_DIRECTION_FETCH)) {
366 if (!(retcode = git_remote_download(origin, options->fetch_progress_cb,
367 options->fetch_progress_payload))) {
368 /* Create "origin/foo" branches for all remote branches */
369 if (!git_remote_update_tips(origin)) {
370 /* Point HEAD to the requested branch */
371 if (options->checkout_branch) {
372 if (!update_head_to_branch(repo, options))
373 retcode = 0;
374 }
375 /* Point HEAD to the same ref as the remote's head */
376 else if (!update_head_to_remote(repo, origin)) {
377 retcode = 0;
378 }
379 }
380 }
381 git_remote_disconnect(origin);
382 }
383 git_remote_free(origin);
384 }
385
386 return retcode;
387 }
388
389
390 static bool should_checkout(
391 git_repository *repo,
392 bool is_bare,
393 git_checkout_opts *opts)
394 {
395 if (is_bare)
396 return false;
397
398 if (!opts)
399 return false;
400
401 if (opts->checkout_strategy == GIT_CHECKOUT_NONE)
402 return false;
403
404 return !git_repository_head_orphan(repo);
405 }
406
407 static void normalize_options(git_clone_options *dst, const git_clone_options *src)
408 {
409 git_clone_options default_options = GIT_CLONE_OPTIONS_INIT;
410 if (!src) src = &default_options;
411
412 *dst = *src;
413
414 /* Provide defaults for null pointers */
415 if (!dst->remote_name) dst->remote_name = "origin";
416 if (!dst->remote_autotag) dst->remote_autotag = GIT_REMOTE_DOWNLOAD_TAGS_ALL;
417 }
418
419 int git_clone(
420 git_repository **out,
421 const char *url,
422 const char *local_path,
423 const git_clone_options *options)
424 {
425 int retcode = GIT_ERROR;
426 git_repository *repo = NULL;
427 git_clone_options normOptions;
428 int remove_directory_on_failure = 0;
429
430 assert(out && url && local_path);
431
432 normalize_options(&normOptions, options);
433 GITERR_CHECK_VERSION(&normOptions, GIT_CLONE_OPTIONS_VERSION, "git_clone_options");
434
435 /* Only clone to a new directory or an empty directory */
436 if (git_path_exists(local_path) && !git_path_is_empty_dir(local_path)) {
437 giterr_set(GITERR_INVALID,
438 "'%s' exists and is not an empty directory", local_path);
439 return GIT_ERROR;
440 }
441
442 /* Only remove the directory on failure if we create it */
443 remove_directory_on_failure = !git_path_exists(local_path);
444
445 if (!(retcode = git_repository_init(&repo, local_path, normOptions.bare))) {
446 if ((retcode = setup_remotes_and_fetch(repo, url, &normOptions)) < 0) {
447 /* Failed to fetch; clean up */
448 git_repository_free(repo);
449
450 if (remove_directory_on_failure)
451 git_futils_rmdir_r(local_path, NULL, GIT_RMDIR_REMOVE_FILES);
452 else
453 git_futils_cleanupdir_r(local_path);
454
455 } else {
456 *out = repo;
457 retcode = 0;
458 }
459 }
460
461 if (!retcode && should_checkout(repo, normOptions.bare, &normOptions.checkout_opts))
462 retcode = git_checkout_head(*out, &normOptions.checkout_opts);
463
464 return retcode;
465 }