From 132c2db6a9f839d7697e8bff05861a92825a9c7c Mon Sep 17 00:00:00 2001 From: Sascha Cunz Date: Fri, 11 Jan 2013 02:18:27 +0100 Subject: [PATCH 1/6] Fix possible free'ing of unitialized pointer in error case --- src/clone.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clone.c b/src/clone.c index 9012c04a9..32ac08bf2 100644 --- a/src/clone.c +++ b/src/clone.c @@ -29,7 +29,7 @@ static int create_branch( const char *name) { git_commit *head_obj = NULL; - git_reference *branch_ref; + git_reference *branch_ref = NULL; int error; /* Find the target commit */ From 88aef76635c012ac8dc770e0f97abc37980decf9 Mon Sep 17 00:00:00 2001 From: Sascha Cunz Date: Fri, 11 Jan 2013 02:45:55 +0100 Subject: [PATCH 2/6] Implement analog for 'git checkout --branch xxx ...' --- include/git2/clone.h | 3 +++ src/clone.c | 32 +++++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/include/git2/clone.h b/include/git2/clone.h index e299c155d..9bb92ebdd 100644 --- a/include/git2/clone.h +++ b/include/git2/clone.h @@ -57,6 +57,8 @@ GIT_BEGIN_DECL * the origin remote before the fetch is initiated. * - `remote_autotag` may be used to specify the autotag setting before the * initial fetch. + * - `checkout_branch` gives the name of the branch to checkout. NULL means + * use the remote's HEAD. */ typedef struct git_clone_options { @@ -76,6 +78,7 @@ typedef struct git_clone_options { git_transport *transport; git_remote_callbacks *remote_callbacks; git_remote_autotag_option_t remote_autotag; + const char* checkout_branch; } git_clone_options; #define GIT_CLONE_OPTIONS_VERSION 1 diff --git a/src/clone.c b/src/clone.c index 32ac08bf2..e9686dce4 100644 --- a/src/clone.c +++ b/src/clone.c @@ -260,6 +260,31 @@ cleanup: return retcode; } +static int update_head_to_branch( + git_repository *repo, + const git_clone_options *options) +{ + int retcode; + git_buf remote_branch_name = GIT_BUF_INIT; + git_reference* remote_ref = NULL; + + assert(options->checkout_branch); + + if ((retcode = git_buf_printf(&remote_branch_name, GIT_REFS_REMOTES_DIR "%s/%s", + options->remote_name, options->checkout_branch)) < 0 ) + goto cleanup; + + if ((retcode = git_reference_lookup(&remote_ref, repo, git_buf_cstr(&remote_branch_name))) < 0) + goto cleanup; + + retcode = update_head_to_new_branch(repo, git_reference_target(remote_ref), + options->checkout_branch); + +cleanup: + git_reference_free(remote_ref); + return retcode; +} + /* * submodules? */ @@ -331,8 +356,13 @@ static int setup_remotes_and_fetch( options->fetch_progress_payload)) { /* Create "origin/foo" branches for all remote branches */ if (!git_remote_update_tips(origin)) { + /* Point HEAD to the requested branch */ + if (options->checkout_branch) { + if (!update_head_to_branch(repo, options)) + retcode = 0; + } /* Point HEAD to the same ref as the remote's head */ - if (!update_head_to_remote(repo, origin)) { + else if (!update_head_to_remote(repo, origin)) { retcode = 0; } } From 1265b51f5be724d42e8c99326f0e5623d28fc67d Mon Sep 17 00:00:00 2001 From: Sascha Cunz Date: Fri, 11 Jan 2013 03:07:50 +0100 Subject: [PATCH 3/6] Add missing git_buf_free --- src/clone.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/clone.c b/src/clone.c index e9686dce4..d60977a3f 100644 --- a/src/clone.c +++ b/src/clone.c @@ -282,6 +282,7 @@ static int update_head_to_branch( cleanup: git_reference_free(remote_ref); + git_buf_free(&remote_branch_name); return retcode; } From b5b281209749b70fb5e023fce02abb1447de6cfa Mon Sep 17 00:00:00 2001 From: Sascha Cunz Date: Sat, 12 Jan 2013 00:07:44 +0100 Subject: [PATCH 4/6] Test: Cleanup some cleaning code --- tests-clar/clone/nonetwork.c | 43 +++++++++++++++++------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/tests-clar/clone/nonetwork.c b/tests-clar/clone/nonetwork.c index 9d22b7356..a451fe313 100644 --- a/tests-clar/clone/nonetwork.c +++ b/tests-clar/clone/nonetwork.c @@ -7,6 +7,8 @@ static git_clone_options g_options; static git_repository *g_repo; +static git_reference* g_ref; +static git_remote* g_remote; void test_clone_nonetwork__initialize(void) { @@ -27,6 +29,16 @@ void test_clone_nonetwork__cleanup(void) g_repo = NULL; } + if (g_ref) { + git_reference_free(g_ref); + g_ref = NULL; + } + + if (g_remote) { + git_remote_free(g_remote); + g_remote = NULL; + } + cl_fixture_cleanup("./foo"); } @@ -73,66 +85,51 @@ void test_clone_nonetwork__fail_with_already_existing_but_non_empty_directory(vo void test_clone_nonetwork__custom_origin_name(void) { - git_remote *remote; - g_options.remote_name = "my_origin"; cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options)); - cl_git_pass(git_remote_load(&remote, g_repo, "my_origin")); - - git_remote_free(remote); + cl_git_pass(git_remote_load(&g_remote, g_repo, "my_origin")); } void test_clone_nonetwork__custom_push_url(void) { - git_remote *remote; const char *url = "http://example.com"; g_options.pushurl = url; cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options)); - cl_git_pass(git_remote_load(&remote, g_repo, "origin")); - cl_assert_equal_s(url, git_remote_pushurl(remote)); - - git_remote_free(remote); + cl_git_pass(git_remote_load(&g_remote, g_repo, "origin")); + cl_assert_equal_s(url, git_remote_pushurl(g_remote)); } void test_clone_nonetwork__custom_fetch_spec(void) { - git_remote *remote; - git_reference *master; const git_refspec *actual_fs; const char *spec = "+refs/heads/master:refs/heads/foo"; g_options.fetch_spec = spec; cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options)); - cl_git_pass(git_remote_load(&remote, g_repo, "origin")); - actual_fs = git_remote_fetchspec(remote); + cl_git_pass(git_remote_load(&g_remote, g_repo, "origin")); + actual_fs = git_remote_fetchspec(g_remote); cl_assert_equal_s("refs/heads/master", git_refspec_src(actual_fs)); cl_assert_equal_s("refs/heads/foo", git_refspec_dst(actual_fs)); - cl_git_pass(git_reference_lookup(&master, g_repo, "refs/heads/foo")); - git_reference_free(master); - - git_remote_free(remote); + cl_git_pass(git_reference_lookup(&g_ref, g_repo, "refs/heads/foo")); } void test_clone_nonetwork__custom_push_spec(void) { - git_remote *remote; const git_refspec *actual_fs; const char *spec = "+refs/heads/master:refs/heads/foo"; g_options.push_spec = spec; cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options)); - cl_git_pass(git_remote_load(&remote, g_repo, "origin")); - actual_fs = git_remote_pushspec(remote); + cl_git_pass(git_remote_load(&g_remote, g_repo, "origin")); + actual_fs = git_remote_pushspec(g_remote); cl_assert_equal_s("refs/heads/master", git_refspec_src(actual_fs)); cl_assert_equal_s("refs/heads/foo", git_refspec_dst(actual_fs)); - - git_remote_free(remote); } void test_clone_nonetwork__custom_autotag(void) From f1d4a35e942afa1a494849e12dfc6631069daf65 Mon Sep 17 00:00:00 2001 From: Sascha Cunz Date: Sat, 12 Jan 2013 00:08:48 +0100 Subject: [PATCH 5/6] Tests: Add test for check out of given branch during clone --- tests-clar/clone/nonetwork.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests-clar/clone/nonetwork.c b/tests-clar/clone/nonetwork.c index a451fe313..50b0bc2d9 100644 --- a/tests-clar/clone/nonetwork.c +++ b/tests-clar/clone/nonetwork.c @@ -164,3 +164,14 @@ void test_clone_nonetwork__can_prevent_the_checkout_of_a_standard_repo(void) git_buf_free(&path); } +void test_clone_nonetwork__can_checkout_given_branch(void) +{ + g_options.checkout_branch = "test"; + cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options)); + + cl_assert_equal_i(0, git_repository_head_orphan(g_repo)); + + cl_git_pass(git_repository_head(&g_ref, g_repo)); + cl_assert_equal_s(git_reference_name(g_ref), "refs/heads/test"); +} + From f31cae8be9e6005148b2e5bafcb46a0d4c77b467 Mon Sep 17 00:00:00 2001 From: Sascha Cunz Date: Sat, 12 Jan 2013 05:51:00 +0100 Subject: [PATCH 6/6] Default git_clone_options' checkout strategy to GIT_CHECKOUT_SAFE_CREATE --- include/git2/clone.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/git2/clone.h b/include/git2/clone.h index 9bb92ebdd..b54676874 100644 --- a/include/git2/clone.h +++ b/include/git2/clone.h @@ -82,7 +82,7 @@ typedef struct git_clone_options { } git_clone_options; #define GIT_CLONE_OPTIONS_VERSION 1 -#define GIT_CLONE_OPTIONS_INIT {GIT_CLONE_OPTIONS_VERSION, {GIT_CHECKOUT_OPTS_VERSION, GIT_CHECKOUT_SAFE}} +#define GIT_CLONE_OPTIONS_INIT {GIT_CLONE_OPTIONS_VERSION, {GIT_CHECKOUT_OPTS_VERSION, GIT_CHECKOUT_SAFE_CREATE}} /** * Clone a remote repository, and checkout the branch pointed to by the remote