2 * Copyright (C) the libgit2 contributors. All rights reserved.
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.
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"
25 static int create_branch(
26 git_reference
**branch
,
28 const git_oid
*target
,
31 git_commit
*head_obj
= NULL
;
32 git_reference
*branch_ref
= NULL
;
35 /* Find the target commit */
36 if ((error
= git_commit_lookup(&head_obj
, repo
, target
)) < 0)
39 /* Create the new branch */
40 error
= git_branch_create(&branch_ref
, repo
, name
, head_obj
, 0);
42 git_commit_free(head_obj
);
47 git_reference_free(branch_ref
);
52 static int setup_tracking_config(
54 const char *branch_name
,
55 const char *remote_name
,
56 const char *merge_target
)
59 git_buf remote_key
= GIT_BUF_INIT
, merge_key
= GIT_BUF_INIT
;
62 if (git_repository_config__weakptr(&cfg
, repo
) < 0)
65 if (git_buf_printf(&remote_key
, "branch.%s.remote", branch_name
) < 0)
68 if (git_buf_printf(&merge_key
, "branch.%s.merge", branch_name
) < 0)
71 if (git_config_set_string(cfg
, git_buf_cstr(&remote_key
), remote_name
) < 0)
74 if (git_config_set_string(cfg
, git_buf_cstr(&merge_key
), merge_target
) < 0)
80 git_buf_free(&remote_key
);
81 git_buf_free(&merge_key
);
85 static int create_tracking_branch(
86 git_reference
**branch
,
88 const git_oid
*target
,
89 const char *branch_name
)
93 if ((error
= create_branch(branch
, repo
, target
, branch_name
)) < 0)
96 return setup_tracking_config(
100 git_reference_name(*branch
));
104 git_repository
*repo
;
105 git_oid remote_head_oid
;
107 const git_refspec
*refspec
;
111 static int reference_matches_remote_head(
112 const char *reference_name
,
115 struct head_info
*head_info
= (struct head_info
*)payload
;
118 /* TODO: Should we guard against references
119 * which name doesn't start with refs/heads/ ?
122 /* Stop looking if we've already found a match */
123 if (head_info
->found
)
126 if (git_reference_name_to_id(
129 reference_name
) < 0) {
130 /* If the reference doesn't exists, it obviously cannot match the expected oid. */
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
,
143 if (git_buf_len(&head_info
->branchname
) > 0) {
145 &head_info
->branchname
,
146 git_buf_cstr(&head_info
->branchname
) + strlen(GIT_REFS_HEADS_DIR
)) < 0)
149 head_info
->found
= 1;
156 static int update_head_to_new_branch(
157 git_repository
*repo
,
158 const git_oid
*target
,
161 git_reference
*tracking_branch
= NULL
;
164 if ((error
= create_tracking_branch(
171 error
= git_repository_set_head(repo
, git_reference_name(tracking_branch
));
173 git_reference_free(tracking_branch
);
178 static int get_head_callback(git_remote_head
*head
, void *payload
)
180 git_remote_head
**destination
= (git_remote_head
**)payload
;
182 /* Save the first entry, and terminate the enumeration */
187 static int update_head_to_remote(git_repository
*repo
, git_remote
*remote
)
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
;
195 /* Did we just clone an empty repository? */
196 if (remote
->refs
.length
== 0) {
197 return setup_tracking_config(
201 GIT_REFS_HEADS_MASTER_FILE
);
204 /* Get the remote's HEAD. This is always the first ref in remote->refs. */
207 if (!remote
->transport
->ls(remote
->transport
, get_head_callback
, &remote_head
))
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
);
218 if (head_info
.refspec
== NULL
) {
219 memset(&dummy_spec
, 0, sizeof(git_refspec
));
220 head_info
.refspec
= &dummy_spec
;
223 /* Determine the remote tracking reference name from the local master */
224 if (git_refspec_transform_r(
227 GIT_REFS_HEADS_MASTER_FILE
) < 0)
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)
234 if (head_info
.found
) {
235 retcode
= update_head_to_new_branch(
237 &head_info
.remote_head_oid
,
238 git_buf_cstr(&head_info
.branchname
));
243 /* Not master. Check all the other refs. */
244 if (git_reference_foreach(
247 reference_matches_remote_head
,
251 if (head_info
.found
) {
252 retcode
= update_head_to_new_branch(
254 &head_info
.remote_head_oid
,
255 git_buf_cstr(&head_info
.branchname
));
259 retcode
= git_repository_set_head_detached(
261 &head_info
.remote_head_oid
);
266 git_buf_free(&remote_master_name
);
267 git_buf_free(&head_info
.branchname
);
271 static int update_head_to_branch(
272 git_repository
*repo
,
273 const git_clone_options
*options
)
276 git_buf remote_branch_name
= GIT_BUF_INIT
;
277 git_reference
* remote_ref
= NULL
;
279 assert(options
->checkout_branch
);
281 if ((retcode
= git_buf_printf(&remote_branch_name
, GIT_REFS_REMOTES_DIR
"%s/%s",
282 options
->remote_name
, options
->checkout_branch
)) < 0 )
285 if ((retcode
= git_reference_lookup(&remote_ref
, repo
, git_buf_cstr(&remote_branch_name
))) < 0)
288 retcode
= update_head_to_new_branch(repo
, git_reference_target(remote_ref
),
289 options
->checkout_branch
);
292 git_reference_free(remote_ref
);
293 git_buf_free(&remote_branch_name
);
301 static int create_and_configure_origin(
303 git_repository
*repo
,
305 const git_clone_options
*options
)
308 git_remote
*origin
= NULL
;
310 if ((error
= git_remote_create(&origin
, repo
, options
->remote_name
, url
)) < 0)
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
);
317 * Don't write FETCH_HEAD, we'll check out the remote tracking
318 * branch ourselves based on the server's default.
320 git_remote_set_update_fetchhead(origin
, 0);
322 if (options
->remote_callbacks
&&
323 (error
= git_remote_set_callbacks(origin
, options
->remote_callbacks
)) < 0)
326 if (options
->fetch_spec
) {
327 git_remote_clear_refspecs(origin
);
328 if ((error
= git_remote_add_fetch(origin
, options
->fetch_spec
)) < 0)
332 if (options
->push_spec
&&
333 (error
= git_remote_add_push(origin
, options
->push_spec
)) < 0)
336 if (options
->pushurl
&&
337 (error
= git_remote_set_pushurl(origin
, options
->pushurl
)) < 0)
340 if ((error
= git_remote_save(origin
)) < 0)
347 git_remote_free(origin
);
352 static int setup_remotes_and_fetch(
353 git_repository
*repo
,
355 const git_clone_options
*options
)
357 int retcode
= GIT_ERROR
;
360 /* Construct an origin remote */
361 if (!create_and_configure_origin(&origin
, repo
, url
, options
)) {
362 git_remote_set_update_fetchhead(origin
, 0);
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
))
375 /* Point HEAD to the same ref as the remote's head */
376 else if (!update_head_to_remote(repo
, origin
)) {
381 git_remote_disconnect(origin
);
383 git_remote_free(origin
);
390 static bool should_checkout(
391 git_repository
*repo
,
393 git_checkout_opts
*opts
)
401 if (opts
->checkout_strategy
== GIT_CHECKOUT_NONE
)
404 return !git_repository_head_orphan(repo
);
407 static void normalize_options(git_clone_options
*dst
, const git_clone_options
*src
)
409 git_clone_options default_options
= GIT_CLONE_OPTIONS_INIT
;
410 if (!src
) src
= &default_options
;
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
;
420 git_repository
**out
,
422 const char *local_path
,
423 const git_clone_options
*options
)
425 int retcode
= GIT_ERROR
;
426 git_repository
*repo
= NULL
;
427 git_clone_options normOptions
;
428 int remove_directory_on_failure
= 0;
430 assert(out
&& url
&& local_path
);
432 normalize_options(&normOptions
, options
);
433 GITERR_CHECK_VERSION(&normOptions
, GIT_CLONE_OPTIONS_VERSION
, "git_clone_options");
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
);
442 /* Only remove the directory on failure if we create it */
443 remove_directory_on_failure
= !git_path_exists(local_path
);
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
);
450 if (remove_directory_on_failure
)
451 git_futils_rmdir_r(local_path
, NULL
, GIT_RMDIR_REMOVE_FILES
);
453 git_futils_cleanupdir_r(local_path
);
461 if (!retcode
&& should_checkout(repo
, normOptions
.bare
, &normOptions
.checkout_opts
))
462 retcode
= git_checkout_head(*out
, &normOptions
.checkout_opts
);