From 28cbd2e2a891af604c78efd04624215f38185c8a Mon Sep 17 00:00:00 2001 From: nulltoken Date: Mon, 14 Jan 2013 12:09:10 +0100 Subject: [PATCH 1/2] Fix indentations --- src/branch.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/branch.c b/src/branch.c index 094b62ef3..0d429b828 100644 --- a/src/branch.c +++ b/src/branch.c @@ -258,10 +258,10 @@ int git_branch_tracking( if ((error = retrieve_tracking_configuration(&merge_name, branch, "branch.%s.merge")) < 0) goto cleanup; - if (!*remote_name || !*merge_name) { - error = GIT_ENOTFOUND; - goto cleanup; - } + if (!*remote_name || !*merge_name) { + error = GIT_ENOTFOUND; + goto cleanup; + } if (strcmp(".", remote_name) != 0) { if ((error = git_remote_load(&remote, git_reference_owner(branch), remote_name)) < 0) From bf031581d3d87e44da447f0e5a95bd6a24e5bf34 Mon Sep 17 00:00:00 2001 From: nulltoken Date: Mon, 14 Jan 2013 14:22:11 +0100 Subject: [PATCH 2/2] branch: Introduce git_branch_tracking_name() --- include/git2/branch.h | 24 ++++++ src/branch.c | 104 +++++++++++++++++++----- src/branch.h | 17 ++++ src/refs.c | 7 +- src/refs.h | 1 + tests-clar/clone/empty.c | 11 +++ tests-clar/refs/branches/trackingname.c | 42 ++++++++++ 7 files changed, 184 insertions(+), 22 deletions(-) create mode 100644 src/branch.h create mode 100644 tests-clar/refs/branches/trackingname.c diff --git a/include/git2/branch.h b/include/git2/branch.h index 3bda43170..70d609ebe 100644 --- a/include/git2/branch.h +++ b/include/git2/branch.h @@ -157,6 +157,30 @@ GIT_EXTERN(int) git_branch_tracking( git_reference **out, git_reference *branch); +/** + * Return the name of the reference supporting the remote tracking branch, + * given the name of a local branch reference. + * + * @param tracking_branch_name_out The user-allocated buffer which will be + * filled with the name of the reference. Pass NULL if you just want to + * get the needed size of the name of the reference as the output value. + * + * @param buffer_size Size of the `out` buffer in bytes. + * + * @param repo the repository where the branches live + * + * @param canonical_branch_name name of the local branch. + * + * @return number of characters in the reference name + * including the trailing NUL byte; GIT_ENOTFOUND when no remote tracking + * reference exists, otherwise an error code. + */ +GIT_EXTERN(int) git_branch_tracking_name( + char *tracking_branch_name_out, + size_t buffer_size, + git_repository *repo, + const char *canonical_branch_name); + /** * Determine if the current local branch is pointed at by HEAD. * diff --git a/src/branch.c b/src/branch.c index 0d429b828..65c02b8af 100644 --- a/src/branch.c +++ b/src/branch.c @@ -10,6 +10,7 @@ #include "tag.h" #include "config.h" #include "refspec.h" +#include "refs.h" #include "git2/branch.h" @@ -44,9 +45,11 @@ cleanup: return error; } -static int not_a_local_branch(git_reference *ref) +static int not_a_local_branch(const char *reference_name) { - giterr_set(GITERR_INVALID, "Reference '%s' is not a local branch.", git_reference_name(ref)); + giterr_set( + GITERR_INVALID, + "Reference '%s' is not a local branch.", reference_name); return -1; } @@ -176,7 +179,7 @@ int git_branch_move( assert(branch && new_branch_name); if (!git_reference_is_branch(branch)) - return not_a_local_branch(branch); + return not_a_local_branch(git_reference_name(branch)); if ((error = git_buf_joinpath(&new_reference_name, GIT_REFS_HEADS_DIR, new_branch_name)) < 0) goto cleanup; @@ -219,17 +222,20 @@ int git_branch_lookup( } static int retrieve_tracking_configuration( - const char **out, git_reference *branch, const char *format) + const char **out, + git_repository *repo, + const char *canonical_branch_name, + 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) + if (git_repository_config__weakptr(&config, repo) < 0) return -1; if (git_buf_printf(&buf, format, - git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)) < 0) + canonical_branch_name + strlen(GIT_REFS_HEADS_DIR)) < 0) return -1; error = git_config_get_string(out, config, git_buf_cstr(&buf)); @@ -237,9 +243,10 @@ static int retrieve_tracking_configuration( return error; } -int git_branch_tracking( - git_reference **tracking_out, - git_reference *branch) +int git_branch_tracking__name( + git_buf *tracking_name, + git_repository *repo, + const char *canonical_branch_name) { const char *remote_name, *merge_name; git_buf buf = GIT_BUF_INIT; @@ -247,16 +254,18 @@ int git_branch_tracking( git_remote *remote = NULL; const git_refspec *refspec; - assert(tracking_out && branch); + assert(tracking_name && canonical_branch_name); - if (!git_reference_is_branch(branch)) - return not_a_local_branch(branch); + if (!git_reference__is_branch(canonical_branch_name)) + return not_a_local_branch(canonical_branch_name); - if ((error = retrieve_tracking_configuration(&remote_name, branch, "branch.%s.remote")) < 0) - goto cleanup; + if ((error = retrieve_tracking_configuration( + &remote_name, repo, canonical_branch_name, "branch.%s.remote")) < 0) + goto cleanup; - if ((error = retrieve_tracking_configuration(&merge_name, branch, "branch.%s.merge")) < 0) - goto cleanup; + if ((error = retrieve_tracking_configuration( + &merge_name, repo, canonical_branch_name, "branch.%s.merge")) < 0) + goto cleanup; if (!*remote_name || !*merge_name) { error = GIT_ENOTFOUND; @@ -264,7 +273,7 @@ int git_branch_tracking( } if (strcmp(".", remote_name) != 0) { - if ((error = git_remote_load(&remote, git_reference_owner(branch), remote_name)) < 0) + if ((error = git_remote_load(&remote, repo, remote_name)) < 0) goto cleanup; refspec = git_remote_fetchspec(remote); @@ -281,10 +290,7 @@ int git_branch_tracking( if (git_buf_sets(&buf, merge_name) < 0) goto cleanup; - error = git_reference_lookup( - tracking_out, - git_reference_owner(branch), - git_buf_cstr(&buf)); + error = git_buf_set(tracking_name, git_buf_cstr(&buf), git_buf_len(&buf)); cleanup: git_remote_free(remote); @@ -292,6 +298,62 @@ cleanup: return error; } +int git_branch_tracking_name( + char *tracking_branch_name_out, + size_t buffer_size, + git_repository *repo, + const char *canonical_branch_name) +{ + git_buf buf = GIT_BUF_INIT; + int error; + + assert(canonical_branch_name); + + if (tracking_branch_name_out && buffer_size) + *tracking_branch_name_out = '\0'; + + if ((error = git_branch_tracking__name( + &buf, repo, canonical_branch_name)) < 0) + goto cleanup; + + if (tracking_branch_name_out && buf.size + 1 > buffer_size) { /* +1 for NUL byte */ + giterr_set( + GITERR_INVALID, + "Buffer too short to hold the tracked reference name."); + error = -1; + goto cleanup; + } + + if (tracking_branch_name_out) + git_buf_copy_cstr(tracking_branch_name_out, buffer_size, &buf); + + error = buf.size + 1; + +cleanup: + git_buf_free(&buf); + return (int)error; +} + +int git_branch_tracking( + git_reference **tracking_out, + git_reference *branch) +{ + int error; + git_buf tracking_name = GIT_BUF_INIT; + + if ((error = git_branch_tracking__name(&tracking_name, + git_reference_owner(branch), git_reference_name(branch))) < 0) + return error; + + error = git_reference_lookup( + tracking_out, + git_reference_owner(branch), + git_buf_cstr(&tracking_name)); + + git_buf_free(&tracking_name); + return error; +} + int git_branch_is_head( git_reference *branch) { diff --git a/src/branch.h b/src/branch.h new file mode 100644 index 000000000..8a26c4fea --- /dev/null +++ b/src/branch.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_branch_h__ +#define INCLUDE_branch_h__ + +#include "buffer.h" + +int git_branch_tracking__name( + git_buf *tracking_name, + git_repository *repo, + const char *canonical_branch_name); + +#endif diff --git a/src/refs.c b/src/refs.c index db8e9980b..4934a0309 100644 --- a/src/refs.c +++ b/src/refs.c @@ -1905,10 +1905,15 @@ int git_reference_has_log( return result; } +int git_reference__is_branch(const char *ref_name) +{ + return git__prefixcmp(ref_name, GIT_REFS_HEADS_DIR) == 0; +} + int git_reference_is_branch(git_reference *ref) { assert(ref); - return git__prefixcmp(ref->name, GIT_REFS_HEADS_DIR) == 0; + return git_reference__is_branch(ref->name); } int git_reference_is_remote(git_reference *ref) diff --git a/src/refs.h b/src/refs.h index f5ed9328b..1228cea87 100644 --- a/src/refs.h +++ b/src/refs.h @@ -69,6 +69,7 @@ int git_reference__normalize_name_lax(char *buffer_out, size_t out_size, const c int git_reference__normalize_name(git_buf *buf, const char *name, unsigned int flags); int git_reference__is_valid_name(const char *refname, unsigned int flags); int git_reference__update(git_repository *repo, const git_oid *oid, const char *ref_name); +int git_reference__is_branch(const char *ref_name); /** * Lookup a reference by name and try to resolve to an OID. diff --git a/tests-clar/clone/empty.c b/tests-clar/clone/empty.c index 652363747..4e53557a0 100644 --- a/tests-clar/clone/empty.c +++ b/tests-clar/clone/empty.c @@ -33,10 +33,21 @@ static void cleanup_repository(void *path) void test_clone_empty__can_clone_an_empty_local_repo_barely(void) { + char *local_name = "refs/heads/master"; + char tracking_name[1024]; + git_reference *ref; + cl_set_cleanup(&cleanup_repository, "./empty"); g_options.bare = true; cl_git_pass(git_clone(&g_repo_cloned, "./empty_bare.git", "./empty", &g_options)); + + /* Although the HEAD is orphaned... */ + cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&ref, g_repo_cloned, local_name)); + + /* ...one can still retrieve the name of the remote tracking reference */ + cl_assert_equal_i(strlen("refs/remotes/origin/master") + 1, + git_branch_tracking_name(tracking_name, 1024, g_repo_cloned, local_name)); } void test_clone_empty__can_clone_an_empty_local_repo(void) diff --git a/tests-clar/refs/branches/trackingname.c b/tests-clar/refs/branches/trackingname.c new file mode 100644 index 000000000..ea9058357 --- /dev/null +++ b/tests-clar/refs/branches/trackingname.c @@ -0,0 +1,42 @@ +#include "clar_libgit2.h" +#include "branch.h" + +static git_repository *repo; +static git_buf tracking_name; + +void test_refs_branches_trackingname__initialize(void) +{ + cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git"))); + + git_buf_init(&tracking_name, 0); +} + +void test_refs_branches_trackingname__cleanup(void) +{ + git_buf_free(&tracking_name); + + git_repository_free(repo); + repo = NULL; +} + +void test_refs_branches_trackingname__can_retrieve_the_remote_tracking_reference_name_of_a_local_branch(void) +{ + cl_git_pass(git_branch_tracking__name( + &tracking_name, repo, "refs/heads/master")); + + cl_assert_equal_s("refs/remotes/test/master", git_buf_cstr(&tracking_name)); +} + +void test_refs_branches_trackingname__can_retrieve_the_local_tracking_reference_name_of_a_local_branch(void) +{ + cl_git_pass(git_branch_tracking__name( + &tracking_name, repo, "refs/heads/track-local")); + + cl_assert_equal_s("refs/heads/master", git_buf_cstr(&tracking_name)); +} + +void test_refs_branches_trackingname__can_return_the_size_of_thelocal_tracking_reference_name_of_a_local_branch(void) +{ + cl_assert_equal_i(strlen("refs/heads/master") + 1, + git_branch_tracking_name(NULL, 0, repo, "refs/heads/track-local")); +}