mirror of
https://git.proxmox.com/git/libgit2
synced 2025-12-30 19:51:27 +00:00
Merge pull request #2376 from libgit2/cmn/remote-symref
Add support for the symref extension
This commit is contained in:
commit
a598264463
@ -41,6 +41,11 @@ struct git_remote_head {
|
||||
git_oid oid;
|
||||
git_oid loid;
|
||||
char *name;
|
||||
/**
|
||||
* If the server send a symref mapping for this ref, this will
|
||||
* point to the target.
|
||||
*/
|
||||
char *symref_target;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -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
|
||||
|
||||
113
src/clone.c
113
src/clone.c
@ -105,54 +105,6 @@ static int create_tracking_branch(
|
||||
git_reference_name(*branch));
|
||||
}
|
||||
|
||||
struct head_info {
|
||||
git_repository *repo;
|
||||
git_oid remote_head_oid;
|
||||
git_buf branchname;
|
||||
const git_refspec *refspec;
|
||||
bool found;
|
||||
};
|
||||
|
||||
static int reference_matches_remote_head(
|
||||
const char *reference_name,
|
||||
void *payload)
|
||||
{
|
||||
struct head_info *head_info = (struct head_info *)payload;
|
||||
git_oid oid;
|
||||
int error;
|
||||
|
||||
/* TODO: Should we guard against references
|
||||
* which name doesn't start with refs/heads/ ?
|
||||
*/
|
||||
|
||||
error = git_reference_name_to_id(&oid, head_info->repo, reference_name);
|
||||
if (error == GIT_ENOTFOUND) {
|
||||
/* If the reference doesn't exists, it obviously cannot match the
|
||||
* expected oid. */
|
||||
giterr_clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!error && !git_oid__cmp(&head_info->remote_head_oid, &oid)) {
|
||||
/* Determine the local reference name from the remote tracking one */
|
||||
error = git_refspec_rtransform(
|
||||
&head_info->branchname, head_info->refspec, reference_name);
|
||||
|
||||
if (!error &&
|
||||
git_buf_len(&head_info->branchname) > 0 &&
|
||||
!(error = git_buf_sets(
|
||||
&head_info->branchname,
|
||||
git_buf_cstr(&head_info->branchname) +
|
||||
strlen(GIT_REFS_HEADS_DIR))))
|
||||
{
|
||||
head_info->found = true;
|
||||
error = GIT_ITEROVER;
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int update_head_to_new_branch(
|
||||
git_repository *repo,
|
||||
const git_oid *target,
|
||||
@ -161,6 +113,10 @@ static int update_head_to_new_branch(
|
||||
const char *reflog_message)
|
||||
{
|
||||
git_reference *tracking_branch = NULL;
|
||||
|
||||
if (!git__prefixcmp(name, GIT_REFS_HEADS_DIR))
|
||||
name += strlen(GIT_REFS_HEADS_DIR);
|
||||
|
||||
int error = create_tracking_branch(&tracking_branch, repo, target, name,
|
||||
signature, reflog_message);
|
||||
|
||||
@ -184,12 +140,13 @@ static int update_head_to_remote(
|
||||
const git_signature *signature,
|
||||
const char *reflog_message)
|
||||
{
|
||||
int error = 0;
|
||||
int error = 0, found_branch = 0;
|
||||
size_t refs_len;
|
||||
git_refspec dummy_spec;
|
||||
git_refspec dummy_spec, *refspec;
|
||||
const git_remote_head *remote_head, **refs;
|
||||
struct head_info head_info;
|
||||
const git_oid *remote_head_id;
|
||||
git_buf remote_master_name = GIT_BUF_INIT;
|
||||
git_buf branch = GIT_BUF_INIT;
|
||||
|
||||
if ((error = git_remote_ls(&refs, &refs_len, remote)) < 0)
|
||||
return error;
|
||||
@ -199,63 +156,45 @@ static int update_head_to_remote(
|
||||
return setup_tracking_config(
|
||||
repo, "master", GIT_REMOTE_ORIGIN, GIT_REFS_HEADS_MASTER_FILE);
|
||||
|
||||
error = git_remote_default_branch(&branch, remote);
|
||||
if (error == GIT_ENOTFOUND) {
|
||||
git_buf_puts(&branch, GIT_REFS_HEADS_MASTER_FILE);
|
||||
} else {
|
||||
found_branch = 1;
|
||||
}
|
||||
|
||||
/* Get the remote's HEAD. This is always the first ref in the list. */
|
||||
remote_head = refs[0];
|
||||
assert(remote_head);
|
||||
|
||||
memset(&head_info, 0, sizeof(head_info));
|
||||
git_oid_cpy(&head_info.remote_head_oid, &remote_head->oid);
|
||||
head_info.repo = repo;
|
||||
head_info.refspec =
|
||||
git_remote__matching_refspec(remote, GIT_REFS_HEADS_MASTER_FILE);
|
||||
remote_head_id = &remote_head->oid;
|
||||
refspec = git_remote__matching_refspec(remote, git_buf_cstr(&branch));
|
||||
|
||||
if (head_info.refspec == NULL) {
|
||||
if (refspec == NULL) {
|
||||
memset(&dummy_spec, 0, sizeof(git_refspec));
|
||||
head_info.refspec = &dummy_spec;
|
||||
refspec = &dummy_spec;
|
||||
}
|
||||
|
||||
/* Determine the remote tracking reference name from the local master */
|
||||
if ((error = git_refspec_transform(
|
||||
&remote_master_name,
|
||||
head_info.refspec,
|
||||
GIT_REFS_HEADS_MASTER_FILE)) < 0)
|
||||
refspec,
|
||||
git_buf_cstr(&branch))) < 0)
|
||||
return error;
|
||||
|
||||
/* Check to see if the remote HEAD points to the remote master */
|
||||
error = reference_matches_remote_head(
|
||||
git_buf_cstr(&remote_master_name), &head_info);
|
||||
if (error < 0 && error != GIT_ITEROVER)
|
||||
goto cleanup;
|
||||
|
||||
if (head_info.found) {
|
||||
if (found_branch) {
|
||||
error = update_head_to_new_branch(
|
||||
repo,
|
||||
&head_info.remote_head_oid,
|
||||
git_buf_cstr(&head_info.branchname),
|
||||
signature, reflog_message);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Not master. Check all the other refs. */
|
||||
error = git_reference_foreach_name(
|
||||
repo, reference_matches_remote_head, &head_info);
|
||||
if (error < 0 && error != GIT_ITEROVER)
|
||||
goto cleanup;
|
||||
|
||||
if (head_info.found) {
|
||||
error = update_head_to_new_branch(
|
||||
repo,
|
||||
&head_info.remote_head_oid,
|
||||
git_buf_cstr(&head_info.branchname),
|
||||
remote_head_id,
|
||||
git_buf_cstr(&branch),
|
||||
signature, reflog_message);
|
||||
} else {
|
||||
error = git_repository_set_head_detached(
|
||||
repo, &head_info.remote_head_oid, signature, reflog_message);
|
||||
repo, remote_head_id, signature, reflog_message);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
git_buf_free(&remote_master_name);
|
||||
git_buf_free(&head_info.branchname);
|
||||
git_buf_free(&branch);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
47
src/remote.c
47
src/remote.c
@ -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);
|
||||
}
|
||||
|
||||
@ -43,14 +43,19 @@ typedef struct {
|
||||
static int add_ref(transport_local *t, const char *name)
|
||||
{
|
||||
const char peeled[] = "^{}";
|
||||
git_oid head_oid;
|
||||
git_reference *ref, *resolved;
|
||||
git_remote_head *head;
|
||||
git_oid obj_id;
|
||||
git_object *obj = NULL, *target = NULL;
|
||||
git_buf buf = GIT_BUF_INIT;
|
||||
int error;
|
||||
|
||||
error = git_reference_name_to_id(&head_oid, t->repo, name);
|
||||
if ((error = git_reference_lookup(&ref, t->repo, name)) < 0)
|
||||
return error;
|
||||
|
||||
error = git_reference_resolve(&resolved, ref);
|
||||
if (error < 0) {
|
||||
git_reference_free(ref);
|
||||
if (!strcmp(name, GIT_HEAD_FILE) && error == GIT_ENOTFOUND) {
|
||||
/* This is actually okay. Empty repos often have a HEAD that
|
||||
* points to a nonexistent "refs/heads/master". */
|
||||
@ -60,13 +65,22 @@ static int add_ref(transport_local *t, const char *name)
|
||||
return error;
|
||||
}
|
||||
|
||||
git_oid_cpy(&obj_id, git_reference_target(resolved));
|
||||
git_reference_free(resolved);
|
||||
|
||||
head = git__calloc(1, sizeof(git_remote_head));
|
||||
GITERR_CHECK_ALLOC(head);
|
||||
|
||||
head->name = git__strdup(name);
|
||||
GITERR_CHECK_ALLOC(head->name);
|
||||
|
||||
git_oid_cpy(&head->oid, &head_oid);
|
||||
git_oid_cpy(&head->oid, &obj_id);
|
||||
|
||||
if (git_reference_type(ref) == GIT_REF_SYMBOLIC) {
|
||||
head->symref_target = git__strdup(git_reference_symbolic_target(ref));
|
||||
GITERR_CHECK_ALLOC(head->symref_target);
|
||||
}
|
||||
git_reference_free(ref);
|
||||
|
||||
if ((error = git_vector_insert(&t->refs, head)) < 0) {
|
||||
git__free(head->name);
|
||||
@ -176,7 +190,7 @@ static int path_from_url_or_path(git_buf *local_path_out, const char *url_or_pat
|
||||
|
||||
/*
|
||||
* Try to open the url as a git directory. The direction doesn't
|
||||
* matter in this case because we're calulating the heads ourselves.
|
||||
* matter in this case because we're calculating the heads ourselves.
|
||||
*/
|
||||
static int local_connect(
|
||||
git_transport *transport,
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
#include "git2.h"
|
||||
#include "smart.h"
|
||||
#include "refs.h"
|
||||
#include "refspec.h"
|
||||
|
||||
static int git_smart__recv_cb(gitno_buffer *buf)
|
||||
{
|
||||
@ -63,7 +64,7 @@ static int git_smart__set_callbacks(
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_smart__update_heads(transport_smart *t)
|
||||
int git_smart__update_heads(transport_smart *t, git_vector *symrefs)
|
||||
{
|
||||
size_t i;
|
||||
git_pkt *pkt;
|
||||
@ -74,6 +75,25 @@ int git_smart__update_heads(transport_smart *t)
|
||||
if (pkt->type != GIT_PKT_REF)
|
||||
continue;
|
||||
|
||||
if (symrefs) {
|
||||
git_refspec *spec;
|
||||
git_buf buf = GIT_BUF_INIT;
|
||||
size_t j;
|
||||
int error;
|
||||
|
||||
git_vector_foreach(symrefs, j, spec) {
|
||||
git_buf_clear(&buf);
|
||||
if (git_refspec_src_matches(spec, ref->head.name) &&
|
||||
!(error = git_refspec_transform(&buf, spec, ref->head.name)))
|
||||
ref->head.symref_target = git_buf_detach(&buf);
|
||||
}
|
||||
|
||||
git_buf_free(&buf);
|
||||
|
||||
if (error < 0)
|
||||
return error;
|
||||
}
|
||||
|
||||
if (git_vector_insert(&t->heads, &ref->head) < 0)
|
||||
return -1;
|
||||
}
|
||||
@ -81,6 +101,19 @@ int git_smart__update_heads(transport_smart *t)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void free_symrefs(git_vector *symrefs)
|
||||
{
|
||||
git_refspec *spec;
|
||||
size_t i;
|
||||
|
||||
git_vector_foreach(symrefs, i, spec) {
|
||||
git_refspec__free(spec);
|
||||
git__free(spec);
|
||||
}
|
||||
|
||||
git_vector_free(symrefs);
|
||||
}
|
||||
|
||||
static int git_smart__connect(
|
||||
git_transport *transport,
|
||||
const char *url,
|
||||
@ -94,6 +127,7 @@ static int git_smart__connect(
|
||||
int error;
|
||||
git_pkt *pkt;
|
||||
git_pkt_ref *first;
|
||||
git_vector symrefs;
|
||||
git_smart_service_t service;
|
||||
|
||||
if (git_smart__reset_stream(t, true) < 0)
|
||||
@ -147,8 +181,11 @@ static int git_smart__connect(
|
||||
|
||||
first = (git_pkt_ref *)git_vector_get(&t->refs, 0);
|
||||
|
||||
if ((error = git_vector_init(&symrefs, 1, NULL)) < 0)
|
||||
return error;
|
||||
|
||||
/* Detect capabilities */
|
||||
if (git_smart__detect_caps(first, &t->caps) < 0)
|
||||
if (git_smart__detect_caps(first, &t->caps, &symrefs) < 0)
|
||||
return -1;
|
||||
|
||||
/* If the only ref in the list is capabilities^{} with OID_ZERO, remove it */
|
||||
@ -159,7 +196,9 @@ static int git_smart__connect(
|
||||
}
|
||||
|
||||
/* Keep a list of heads for _ls */
|
||||
git_smart__update_heads(t);
|
||||
git_smart__update_heads(t, &symrefs);
|
||||
|
||||
free_symrefs(&symrefs);
|
||||
|
||||
if (t->rpc && git_smart__reset_stream(t, false) < 0)
|
||||
return -1;
|
||||
|
||||
@ -23,6 +23,7 @@
|
||||
#define GIT_CAP_DELETE_REFS "delete-refs"
|
||||
#define GIT_CAP_REPORT_STATUS "report-status"
|
||||
#define GIT_CAP_THIN_PACK "thin-pack"
|
||||
#define GIT_CAP_SYMREF "symref"
|
||||
|
||||
enum git_pkt_type {
|
||||
GIT_PKT_CMD,
|
||||
@ -154,7 +155,7 @@ typedef struct {
|
||||
|
||||
/* smart_protocol.c */
|
||||
int git_smart__store_refs(transport_smart *t, int flushes);
|
||||
int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps);
|
||||
int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vector *symrefs);
|
||||
int git_smart__push(git_transport *transport, git_push *push);
|
||||
|
||||
int git_smart__negotiate_fetch(
|
||||
@ -174,7 +175,7 @@ int git_smart__download_pack(
|
||||
int git_smart__negotiation_step(git_transport *transport, void *data, size_t len);
|
||||
int git_smart__get_push_stream(transport_smart *t, git_smart_subtransport_stream **out);
|
||||
|
||||
int git_smart__update_heads(transport_smart *t);
|
||||
int git_smart__update_heads(transport_smart *t, git_vector *symrefs);
|
||||
|
||||
/* smart_pkt.c */
|
||||
int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_t len);
|
||||
|
||||
@ -433,6 +433,7 @@ void git_pkt_free(git_pkt *pkt)
|
||||
if (pkt->type == GIT_PKT_REF) {
|
||||
git_pkt_ref *p = (git_pkt_ref *) pkt;
|
||||
git__free(p->head.name);
|
||||
git__free(p->head.symref_target);
|
||||
}
|
||||
|
||||
if (pkt->type == GIT_PKT_OK) {
|
||||
|
||||
@ -78,7 +78,52 @@ int git_smart__store_refs(transport_smart *t, int flushes)
|
||||
return flush;
|
||||
}
|
||||
|
||||
int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps)
|
||||
static int append_symref(const char **out, git_vector *symrefs, const char *ptr)
|
||||
{
|
||||
int error;
|
||||
const char *end;
|
||||
git_buf buf = GIT_BUF_INIT;
|
||||
git_refspec *mapping;
|
||||
|
||||
ptr += strlen(GIT_CAP_SYMREF);
|
||||
if (*ptr != '=')
|
||||
goto on_invalid;
|
||||
|
||||
ptr++;
|
||||
if (!(end = strchr(ptr, ' ')) &&
|
||||
!(end = strchr(ptr, '\0')))
|
||||
goto on_invalid;
|
||||
|
||||
if ((error = git_buf_put(&buf, ptr, end - ptr)) < 0)
|
||||
return error;
|
||||
|
||||
/* symref mapping has refspec format */
|
||||
mapping = git__malloc(sizeof(git_refspec));
|
||||
GITERR_CHECK_ALLOC(mapping);
|
||||
|
||||
error = git_refspec__parse(mapping, git_buf_cstr(&buf), true);
|
||||
git_buf_free(&buf);
|
||||
|
||||
/* if the error isn't OOM, then it's a parse error; let's use a nicer message */
|
||||
if (error < 0) {
|
||||
if (giterr_last()->klass != GITERR_NOMEMORY)
|
||||
goto on_invalid;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
if ((error = git_vector_insert(symrefs, mapping)) < 0)
|
||||
return error;
|
||||
|
||||
*out = end;
|
||||
return 0;
|
||||
|
||||
on_invalid:
|
||||
giterr_set(GITERR_NET, "remote sent invalid symref");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vector *symrefs)
|
||||
{
|
||||
const char *ptr;
|
||||
|
||||
@ -141,6 +186,15 @@ int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!git__prefixcmp(ptr, GIT_CAP_SYMREF)) {
|
||||
int error;
|
||||
|
||||
if ((error = append_symref(&ptr, symrefs, ptr)) < 0)
|
||||
return error;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/* We don't know this capability, so skip it */
|
||||
ptr = strchr(ptr, ' ');
|
||||
}
|
||||
@ -969,7 +1023,7 @@ int git_smart__push(git_transport *transport, git_push *push)
|
||||
if (error < 0)
|
||||
goto done;
|
||||
|
||||
error = git_smart__update_heads(t);
|
||||
error = git_smart__update_heads(t, NULL);
|
||||
}
|
||||
|
||||
done:
|
||||
|
||||
50
tests/network/remote/defaultbranch.c
Normal file
50
tests/network/remote/defaultbranch.c
Normal file
@ -0,0 +1,50 @@
|
||||
#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");
|
||||
}
|
||||
@ -184,3 +184,21 @@ void test_online_fetch__ls_disconnected(void)
|
||||
|
||||
git_remote_free(remote);
|
||||
}
|
||||
|
||||
void test_online_fetch__remote_symrefs(void)
|
||||
{
|
||||
const git_remote_head **refs;
|
||||
size_t refs_len;
|
||||
git_remote *remote;
|
||||
|
||||
cl_git_pass(git_remote_create(&remote, _repo, "test",
|
||||
"http://github.com/libgit2/TestGitRepository.git"));
|
||||
cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
|
||||
git_remote_disconnect(remote);
|
||||
cl_git_pass(git_remote_ls(&refs, &refs_len, remote));
|
||||
|
||||
cl_assert_equal_s("HEAD", refs[0]->name);
|
||||
cl_assert_equal_s("refs/heads/master", refs[0]->symref_target);
|
||||
|
||||
git_remote_free(remote);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user