Commit d22db24f by Carlos Martín Nieto

remote: add api to guess the remote's default branch

If the remote supports the symref protocol extension, then we return
that, otherwise we guess with git's rules.
parent 04865aa0
......@@ -623,6 +623,24 @@ GIT_EXTERN(int) git_remote_is_valid_name(const char *remote_name);
*/
GIT_EXTERN(int) git_remote_delete(git_remote *remote);
/**
* Retrieve the name of the remote's default branch
*
* The default branch of a repository is the branch which HEAD points
* to. If the remote does not support reporting this information
* directly, it performs the guess as git does; that is, if there are
* multiple branches which point to the same commit, the first one is
* chosen. If the master branch is a candidate, it wins.
*
* This function must only be called after connecting.
*
* @param out the buffern in which to store the reference name
* @param remote the remote
* @return 0, GIT_ENOTFOUND if the remote does not have any references
* or none of them point to HEAD's commit, or an error message.
*/
GIT_EXTERN(int) git_remote_default_branch(git_buf *out, git_remote *remote);
/** @} */
GIT_END_DECL
#endif
......@@ -1885,3 +1885,50 @@ int git_remote_delete(git_remote *remote)
return 0;
}
int git_remote_default_branch(git_buf *out, git_remote *remote)
{
const git_remote_head **heads;
const git_remote_head *guess = NULL;
const git_oid *head_id;
size_t heads_len, i;
int error;
if ((error = git_remote_ls(&heads, &heads_len, remote)) < 0)
return error;
if (heads_len == 0)
return GIT_ENOTFOUND;
git_buf_sanitize(out);
/* the first one must be HEAD so if that has the symref info, we're done */
if (heads[0]->symref_target)
return git_buf_puts(out, heads[0]->symref_target);
/*
* If there's no symref information, we have to look over them
* and guess. We return the first match unless the master
* branch is a candidate. Then we return the master branch.
*/
head_id = &heads[0]->oid;
for (i = 1; i < heads_len; i++) {
if (git_oid_cmp(head_id, &heads[i]->oid))
continue;
if (!guess) {
guess = heads[i];
continue;
}
if (!git__strcmp(GIT_REFS_HEADS_MASTER_FILE, heads[i]->name)) {
guess = heads[i];
break;
}
}
if (!guess)
return GIT_ENOTFOUND;
return git_buf_puts(out, guess->name);
}
#include "clar_libgit2.h"
#include "buffer.h"
#include "refspec.h"
#include "remote.h"
static git_remote *g_remote;
static git_repository *g_repo_a, *g_repo_b;
void test_network_remote_defaultbranch__initialize(void)
{
g_repo_a = cl_git_sandbox_init("testrepo.git");
cl_git_pass(git_repository_init(&g_repo_b, "repo-b.git", true));
cl_git_pass(git_remote_create(&g_remote, g_repo_b, "origin", git_repository_path(g_repo_a)));
}
void test_network_remote_defaultbranch__cleanup(void)
{
git_remote_free(g_remote);
git_repository_free(g_repo_b);
cl_git_sandbox_cleanup();
cl_fixture_cleanup("repo-b.git");
}
static void assert_default_branch(const char *should)
{
git_buf name = GIT_BUF_INIT;
cl_git_pass(git_remote_connect(g_remote, GIT_DIRECTION_FETCH));
cl_git_pass(git_remote_default_branch(&name, g_remote));
cl_assert_equal_s(should, name.ptr);
git_buf_free(&name);
}
void test_network_remote_defaultbranch__master(void)
{
assert_default_branch("refs/heads/master");
}
void test_network_remote_defaultbranch__master_does_not_win(void)
{
cl_git_pass(git_repository_set_head(g_repo_a, "refs/heads/not-good", NULL, NULL));
assert_default_branch("refs/heads/not-good");
}
void test_network_remote_defaultbranch__master_on_detached(void)
{
cl_git_pass(git_repository_detach_head(g_repo_a, NULL, NULL));
assert_default_branch("refs/heads/master");
}
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