mirror of
				https://git.proxmox.com/git/libgit2
				synced 2025-11-04 11:57:00 +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