From fb910281d6598e2c235f6ec93384d4e08838d655 Mon Sep 17 00:00:00 2001 From: nulltoken Date: Fri, 20 Jul 2012 16:38:54 +0200 Subject: [PATCH] branch: introduce git_branch_tracking() --- include/git2/branch.h | 16 +++++ src/branch.c | 68 ++++++++++++++++++ src/revparse.c | 2 +- tests-clar/network/remotelocal.c | 4 +- tests-clar/refs/branches/foreach.c | 4 +- tests-clar/refs/branches/tracking.c | 69 +++++++++++++++++++ tests-clar/refs/foreachglob.c | 4 +- tests-clar/resources/testrepo.git/config | 3 + .../testrepo.git/refs/heads/track-local | 1 + 9 files changed, 164 insertions(+), 7 deletions(-) create mode 100644 tests-clar/refs/branches/tracking.c create mode 100644 tests-clar/resources/testrepo.git/refs/heads/track-local diff --git a/include/git2/branch.h b/include/git2/branch.h index 724cfba12..15894b709 100644 --- a/include/git2/branch.h +++ b/include/git2/branch.h @@ -136,6 +136,22 @@ GIT_EXTERN(int) git_branch_lookup( const char *branch_name, git_branch_t branch_type); +/** + * Return the reference supporting the remote tracking branch, + * given a local branch reference. + * + * @param tracking_out Pointer where to store the retrieved + * reference. + * + * @param branch Current underlying reference of the branch. + * + * @return 0 on success; GIT_ENOTFOUND when no remote tracking + * reference exists, otherwise an error code. + */ +GIT_EXTERN(int) git_branch_tracking( + git_reference **tracking_out, + git_reference *branch); + /** @} */ GIT_END_DECL #endif diff --git a/src/branch.c b/src/branch.c index 4a56fd1b9..d0ebb2dc3 100644 --- a/src/branch.c +++ b/src/branch.c @@ -8,6 +8,8 @@ #include "common.h" #include "commit.h" #include "tag.h" +#include "config.h" +#include "refspec.h" #include "git2/branch.h" @@ -220,3 +222,69 @@ int git_branch_lookup( return retrieve_branch_reference(ref_out, repo, branch_name, branch_type == GIT_BRANCH_REMOTE); } + +int retrieve_tracking_configuration(const char **out, git_reference *branch, const char *format) +{ + git_config *config; + git_buf buf = GIT_BUF_INIT; + int error; + + if (git_repository_config__weakptr(&config, git_reference_owner(branch)) < 0) + return -1; + + if (git_buf_printf(&buf, format, + git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)) < 0) + return -1; + + error = git_config_get_string(out, config, git_buf_cstr(&buf)); + git_buf_free(&buf); + return error; +} + +int git_branch_tracking( + git_reference **tracking_out, + git_reference *branch) +{ + const char *remote_name, *merge_name; + git_buf buf = GIT_BUF_INIT; + int error = -1; + git_remote *remote = NULL; + const git_refspec *refspec; + + assert(tracking_out && branch); + + if (!git_reference_is_branch(branch)) + return not_a_local_branch(branch); + + if ((error = retrieve_tracking_configuration(&remote_name, branch, "branch.%s.remote")) < 0) + goto cleanup; + + if ((error = retrieve_tracking_configuration(&merge_name, branch, "branch.%s.merge")) < 0) + goto cleanup; + + if (strcmp(".", remote_name) != 0) { + if ((error = git_remote_load(&remote, git_reference_owner(branch), remote_name)) < 0) + goto cleanup; + + refspec = git_remote_fetchspec(remote); + if (refspec == NULL) { + error = GIT_ENOTFOUND; + goto cleanup; + } + + if (git_refspec_transform_r(&buf, refspec, merge_name) < 0) + goto cleanup; + } else + if (git_buf_sets(&buf, merge_name) < 0) + goto cleanup; + + error = git_reference_lookup( + tracking_out, + git_reference_owner(branch), + git_buf_cstr(&buf)); + +cleanup: + git_remote_free(remote); + git_buf_free(&buf); + return error; +} diff --git a/src/revparse.c b/src/revparse.c index b0469286b..e2c0de612 100644 --- a/src/revparse.c +++ b/src/revparse.c @@ -328,7 +328,7 @@ static int retrieve_remote_tracking_reference(git_reference **base_ref, const ch *base_ref = NULL; } - if ((error = git_reference_remote_tracking_from_branch(&tracking, ref)) < 0) + if ((error = git_branch_tracking(&tracking, ref)) < 0) goto cleanup; *base_ref = tracking; diff --git a/tests-clar/network/remotelocal.c b/tests-clar/network/remotelocal.c index 5e20b4240..16e3fe2dd 100644 --- a/tests-clar/network/remotelocal.c +++ b/tests-clar/network/remotelocal.c @@ -107,7 +107,7 @@ void test_network_remotelocal__retrieve_advertised_references(void) cl_git_pass(git_remote_ls(remote, &count_ref__cb, &how_many_refs)); - cl_assert_equal_i(how_many_refs, 22); + cl_assert_equal_i(how_many_refs, 23); } void test_network_remotelocal__retrieve_advertised_references_from_spaced_repository(void) @@ -121,7 +121,7 @@ void test_network_remotelocal__retrieve_advertised_references_from_spaced_reposi cl_git_pass(git_remote_ls(remote, &count_ref__cb, &how_many_refs)); - cl_assert_equal_i(how_many_refs, 22); + cl_assert_equal_i(how_many_refs, 23); git_remote_free(remote); /* Disconnect from the "spaced repo" before the cleanup */ remote = NULL; diff --git a/tests-clar/refs/branches/foreach.c b/tests-clar/refs/branches/foreach.c index 185ca36ba..794233cc9 100644 --- a/tests-clar/refs/branches/foreach.c +++ b/tests-clar/refs/branches/foreach.c @@ -47,7 +47,7 @@ static void assert_retrieval(unsigned int flags, unsigned int expected_count) void test_refs_branches_foreach__retrieve_all_branches(void) { - assert_retrieval(GIT_BRANCH_LOCAL | GIT_BRANCH_REMOTE, 10); + assert_retrieval(GIT_BRANCH_LOCAL | GIT_BRANCH_REMOTE, 11); } void test_refs_branches_foreach__retrieve_remote_branches(void) @@ -57,7 +57,7 @@ void test_refs_branches_foreach__retrieve_remote_branches(void) void test_refs_branches_foreach__retrieve_local_branches(void) { - assert_retrieval(GIT_BRANCH_LOCAL, 8); + assert_retrieval(GIT_BRANCH_LOCAL, 9); } struct expectations { diff --git a/tests-clar/refs/branches/tracking.c b/tests-clar/refs/branches/tracking.c new file mode 100644 index 000000000..8f7019437 --- /dev/null +++ b/tests-clar/refs/branches/tracking.c @@ -0,0 +1,69 @@ +#include "clar_libgit2.h" +#include "refs.h" + +static git_repository *repo; +static git_reference *branch; + +void test_refs_branches_tracking__initialize(void) +{ + cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git"))); + + branch = NULL; +} + +void test_refs_branches_tracking__cleanup(void) +{ + git_reference_free(branch); + + git_repository_free(repo); +} + +void test_refs_branches_tracking__can_retrieve_the_remote_tracking_reference_of_a_local_branch(void) +{ + git_reference *branch, *tracking; + + cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master")); + + cl_git_pass(git_branch_tracking(&tracking, branch)); + + cl_assert_equal_s("refs/remotes/test/master", git_reference_name(tracking)); + + git_reference_free(branch); + git_reference_free(tracking); +} + +void test_refs_branches_tracking__can_retrieve_the_local_tracking_reference_of_a_local_branch(void) +{ + git_reference *branch, *tracking; + + cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/track-local")); + + cl_git_pass(git_branch_tracking(&tracking, branch)); + + cl_assert_equal_s("refs/heads/master", git_reference_name(tracking)); + + git_reference_free(branch); + git_reference_free(tracking); +} + +void test_refs_branches_tracking__cannot_retrieve_a_remote_tracking_reference_from_a_non_branch(void) +{ + git_reference *branch, *tracking; + + cl_git_pass(git_reference_lookup(&branch, repo, "refs/tags/e90810b")); + + cl_git_fail(git_branch_tracking(&tracking, branch)); + + git_reference_free(branch); +} + +void test_refs_branches_tracking__trying_to_retrieve_a_remote_tracking_reference_from_a_plain_local_branch_returns_GIT_ENOTFOUND(void) +{ + git_reference *branch, *tracking; + + cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/subtrees")); + + cl_assert_equal_i(GIT_ENOTFOUND, git_branch_tracking(&tracking, branch)); + + git_reference_free(branch); +} diff --git a/tests-clar/refs/foreachglob.c b/tests-clar/refs/foreachglob.c index d1412a94b..b024d36d4 100644 --- a/tests-clar/refs/foreachglob.c +++ b/tests-clar/refs/foreachglob.c @@ -46,7 +46,7 @@ static void assert_retrieval(const char *glob, unsigned int flags, int expected_ void test_refs_foreachglob__retrieve_all_refs(void) { /* 7 heads (including one packed head) + 1 note + 2 remotes + 6 tags */ - assert_retrieval("*", GIT_REF_LISTALL, 17); + assert_retrieval("*", GIT_REF_LISTALL, 18); } void test_refs_foreachglob__retrieve_remote_branches(void) @@ -56,7 +56,7 @@ void test_refs_foreachglob__retrieve_remote_branches(void) void test_refs_foreachglob__retrieve_local_branches(void) { - assert_retrieval("refs/heads/*", GIT_REF_LISTALL, 8); + assert_retrieval("refs/heads/*", GIT_REF_LISTALL, 9); } void test_refs_foreachglob__retrieve_partially_named_references(void) diff --git a/tests-clar/resources/testrepo.git/config b/tests-clar/resources/testrepo.git/config index b4fdac6c2..6b03dacb5 100644 --- a/tests-clar/resources/testrepo.git/config +++ b/tests-clar/resources/testrepo.git/config @@ -10,3 +10,6 @@ [branch "master"] remote = test merge = refs/heads/master +[branch "track-local"] + remote = . + merge = refs/heads/master diff --git a/tests-clar/resources/testrepo.git/refs/heads/track-local b/tests-clar/resources/testrepo.git/refs/heads/track-local new file mode 100644 index 000000000..f37febb2c --- /dev/null +++ b/tests-clar/resources/testrepo.git/refs/heads/track-local @@ -0,0 +1 @@ +9fd738e8f7967c078dceed8190330fc8648ee56a