diff options
-rw-r--r-- | include/git2/refs.h | 21 | ||||
-rw-r--r-- | src/refs.c | 74 | ||||
-rw-r--r-- | tests-clar/refs/remotetracking.c | 49 |
3 files changed, 144 insertions, 0 deletions
diff --git a/include/git2/refs.h b/include/git2/refs.h index 7f6eb0e9..b119e90b 100644 --- a/include/git2/refs.h +++ b/include/git2/refs.h @@ -363,6 +363,27 @@ GIT_EXTERN(int) git_reference_foreach_glob( */ GIT_EXTERN(int) git_reference_has_log(git_reference *ref); + +/** + * Return the reference supporting the remote tracking branch, + * given a reference branch. + * + * The input reference has to be located in the `refs/heads` + * namespace. + * + * @param tracking_ref Pointer where to store the retrieved + * reference. + * + * @param branch_ref A git local branch reference. + * + * @return 0 on success; GIT_ENOTFOUND when no remote tracking + * reference exists, otherwise an error code. + */ +GIT_EXTERN(int) git_reference_remote_tracking_from_branch( + git_reference **tracking_ref, + git_reference *branch_ref +); + /** @} */ GIT_END_DECL #endif @@ -11,6 +11,7 @@ #include "fileops.h" #include "pack.h" #include "reflog.h" +#include "config.h" #include <git2/tag.h> #include <git2/object.h> @@ -1811,3 +1812,76 @@ int git_reference_has_log( return result; } + +//TODO: How about also taking care of local tracking branches? +//cf. http://alblue.bandlem.com/2011/07/git-tip-of-week-tracking-branches.html +int git_reference_remote_tracking_from_branch( + git_reference **tracking_ref, + git_reference *branch_ref) +{ + git_config *config = NULL; + const char *name, *remote, *merge; + git_buf buf = GIT_BUF_INIT; + int error = -1; + + assert(tracking_ref && branch_ref); + + name = git_reference_name(branch_ref); + + if (git__prefixcmp(name, GIT_REFS_HEADS_DIR)) { + giterr_set( + GITERR_INVALID, + "Failed to retrieve tracking reference - '%s' is not a branch.", + name); + return -1; + } + + if (git_repository_config(&config, branch_ref->owner) < 0) + return -1; + + if (git_buf_printf( + &buf, + "branch.%s.remote", + name + strlen(GIT_REFS_HEADS_DIR)) < 0) + goto cleanup; + + if ((error = git_config_get_string(&remote, config, git_buf_cstr(&buf))) < 0) + goto cleanup; + + error = -1; + + git_buf_clear(&buf); + + //TODO: Is it ok to fail when no merge target is found? + if (git_buf_printf( + &buf, + "branch.%s.merge", + name + strlen(GIT_REFS_HEADS_DIR)) < 0) + goto cleanup; + + if (git_config_get_string(&merge, config, git_buf_cstr(&buf)) < 0) + goto cleanup; + + //TODO: Should we test this? + if (git__prefixcmp(merge, GIT_REFS_HEADS_DIR)) + goto cleanup; + + git_buf_clear(&buf); + + if (git_buf_printf( + &buf, + "refs/remotes/%s/%s", + remote, + merge + strlen(GIT_REFS_HEADS_DIR)) < 0) + goto cleanup; + + error = git_reference_lookup( + tracking_ref, + branch_ref->owner, + git_buf_cstr(&buf)); + +cleanup: + git_config_free(config); + git_buf_free(&buf); + return error; +} diff --git a/tests-clar/refs/remotetracking.c b/tests-clar/refs/remotetracking.c new file mode 100644 index 00000000..c4ec588e --- /dev/null +++ b/tests-clar/refs/remotetracking.c @@ -0,0 +1,49 @@ +#include "clar_libgit2.h" + +static git_repository *g_repo; + +void test_refs_remotetracking__initialize(void) +{ + cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git"))); +} + +void test_refs_remotetracking__cleanup(void) +{ + git_repository_free(g_repo); +} + +void test_refs_remotetracking__unfound_returns_GIT_ENOTFOUND(void) +{ + git_reference *branch, *tracking; + + cl_git_pass(git_reference_lookup(&branch, g_repo, "refs/heads/subtrees")); + + cl_assert_equal_i(GIT_ENOTFOUND, git_reference_remote_tracking_from_branch(&tracking, branch)); + + git_reference_free(branch); +} + +void test_refs_remotetracking__retrieving_from_a_non_head_fails(void) +{ + git_reference *branch, *tracking; + + cl_git_pass(git_reference_lookup(&branch, g_repo, "refs/tags/e90810b")); + + cl_git_fail(git_reference_remote_tracking_from_branch(&tracking, branch)); + + git_reference_free(branch); +} + +void test_refs_remotetracking__can_retrieve_a_remote_tracking_branch_reference(void) +{ + git_reference *branch, *tracking; + + cl_git_pass(git_reference_lookup(&branch, g_repo, "refs/heads/master")); + + cl_git_pass(git_reference_remote_tracking_from_branch(&tracking, branch)); + + cl_assert_equal_s("refs/remotes/test/master", git_reference_name(tracking)); + + git_reference_free(branch); + git_reference_free(tracking); +} |