mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-28 06:47:53 +00:00
Merge pull request #1245 from nulltoken/topic/git_branch_tracking_name
Introduce git_branch_tracking_name()
This commit is contained in:
commit
365485e3c6
@ -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.
|
||||
*
|
||||
|
114
src/branch.c
114
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,24 +254,26 @@ 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)
|
||||
if ((error = retrieve_tracking_configuration(
|
||||
&remote_name, repo, canonical_branch_name, "branch.%s.remote")) < 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;
|
||||
goto cleanup;
|
||||
|
||||
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 (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)
|
||||
{
|
||||
|
17
src/branch.h
Normal file
17
src/branch.h
Normal file
@ -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
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
42
tests-clar/refs/branches/trackingname.c
Normal file
42
tests-clar/refs/branches/trackingname.c
Normal file
@ -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"));
|
||||
}
|
Loading…
Reference in New Issue
Block a user