Commit 365485e3 by Vicent Martí

Merge pull request #1245 from nulltoken/topic/git_branch_tracking_name

Introduce git_branch_tracking_name()
parents 230010d1 bf031581
...@@ -158,6 +158,30 @@ GIT_EXTERN(int) git_branch_tracking( ...@@ -158,6 +158,30 @@ GIT_EXTERN(int) git_branch_tracking(
git_reference *branch); 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. * Determine if the current local branch is pointed at by HEAD.
* *
* @param branch Current underlying reference of the branch. * @param branch Current underlying reference of the branch.
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "tag.h" #include "tag.h"
#include "config.h" #include "config.h"
#include "refspec.h" #include "refspec.h"
#include "refs.h"
#include "git2/branch.h" #include "git2/branch.h"
...@@ -44,9 +45,11 @@ cleanup: ...@@ -44,9 +45,11 @@ cleanup:
return error; 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; return -1;
} }
...@@ -176,7 +179,7 @@ int git_branch_move( ...@@ -176,7 +179,7 @@ int git_branch_move(
assert(branch && new_branch_name); assert(branch && new_branch_name);
if (!git_reference_is_branch(branch)) 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) if ((error = git_buf_joinpath(&new_reference_name, GIT_REFS_HEADS_DIR, new_branch_name)) < 0)
goto cleanup; goto cleanup;
...@@ -219,17 +222,20 @@ int git_branch_lookup( ...@@ -219,17 +222,20 @@ int git_branch_lookup(
} }
static int retrieve_tracking_configuration( 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_config *config;
git_buf buf = GIT_BUF_INIT; git_buf buf = GIT_BUF_INIT;
int error; int error;
if (git_repository_config__weakptr(&config, git_reference_owner(branch)) < 0) if (git_repository_config__weakptr(&config, repo) < 0)
return -1; return -1;
if (git_buf_printf(&buf, format, 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; return -1;
error = git_config_get_string(out, config, git_buf_cstr(&buf)); error = git_config_get_string(out, config, git_buf_cstr(&buf));
...@@ -237,9 +243,10 @@ static int retrieve_tracking_configuration( ...@@ -237,9 +243,10 @@ static int retrieve_tracking_configuration(
return error; return error;
} }
int git_branch_tracking( int git_branch_tracking__name(
git_reference **tracking_out, git_buf *tracking_name,
git_reference *branch) git_repository *repo,
const char *canonical_branch_name)
{ {
const char *remote_name, *merge_name; const char *remote_name, *merge_name;
git_buf buf = GIT_BUF_INIT; git_buf buf = GIT_BUF_INIT;
...@@ -247,24 +254,26 @@ int git_branch_tracking( ...@@ -247,24 +254,26 @@ int git_branch_tracking(
git_remote *remote = NULL; git_remote *remote = NULL;
const git_refspec *refspec; const git_refspec *refspec;
assert(tracking_out && branch); assert(tracking_name && canonical_branch_name);
if (!git_reference_is_branch(branch)) if (!git_reference__is_branch(canonical_branch_name))
return not_a_local_branch(branch); return not_a_local_branch(canonical_branch_name);
if ((error = retrieve_tracking_configuration(&remote_name, branch, "branch.%s.remote")) < 0) if ((error = retrieve_tracking_configuration(
goto cleanup; &remote_name, repo, canonical_branch_name, "branch.%s.remote")) < 0)
goto cleanup;
if ((error = retrieve_tracking_configuration(&merge_name, branch, "branch.%s.merge")) < 0) if ((error = retrieve_tracking_configuration(
goto cleanup; &merge_name, repo, canonical_branch_name, "branch.%s.merge")) < 0)
goto cleanup;
if (!*remote_name || !*merge_name) { if (!*remote_name || !*merge_name) {
error = GIT_ENOTFOUND; error = GIT_ENOTFOUND;
goto cleanup; goto cleanup;
} }
if (strcmp(".", remote_name) != 0) { 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; goto cleanup;
refspec = git_remote_fetchspec(remote); refspec = git_remote_fetchspec(remote);
...@@ -281,10 +290,7 @@ int git_branch_tracking( ...@@ -281,10 +290,7 @@ int git_branch_tracking(
if (git_buf_sets(&buf, merge_name) < 0) if (git_buf_sets(&buf, merge_name) < 0)
goto cleanup; goto cleanup;
error = git_reference_lookup( error = git_buf_set(tracking_name, git_buf_cstr(&buf), git_buf_len(&buf));
tracking_out,
git_reference_owner(branch),
git_buf_cstr(&buf));
cleanup: cleanup:
git_remote_free(remote); git_remote_free(remote);
...@@ -292,6 +298,62 @@ cleanup: ...@@ -292,6 +298,62 @@ cleanup:
return error; 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( int git_branch_is_head(
git_reference *branch) git_reference *branch)
{ {
......
/*
* 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
...@@ -1905,10 +1905,15 @@ int git_reference_has_log( ...@@ -1905,10 +1905,15 @@ int git_reference_has_log(
return result; 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) int git_reference_is_branch(git_reference *ref)
{ {
assert(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) int git_reference_is_remote(git_reference *ref)
......
...@@ -69,6 +69,7 @@ int git_reference__normalize_name_lax(char *buffer_out, size_t out_size, const c ...@@ -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__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__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__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. * Lookup a reference by name and try to resolve to an OID.
......
...@@ -33,10 +33,21 @@ static void cleanup_repository(void *path) ...@@ -33,10 +33,21 @@ static void cleanup_repository(void *path)
void test_clone_empty__can_clone_an_empty_local_repo_barely(void) 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"); cl_set_cleanup(&cleanup_repository, "./empty");
g_options.bare = true; g_options.bare = true;
cl_git_pass(git_clone(&g_repo_cloned, "./empty_bare.git", "./empty", &g_options)); 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) void test_clone_empty__can_clone_an_empty_local_repo(void)
......
#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"));
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment