mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-30 16:43:16 +00:00
Implement ls-remote on local drive
Signed-off-by: Carlos Martín Nieto <carlos@cmartin.tk>
This commit is contained in:
parent
8f866daee5
commit
d6258debbe
@ -58,4 +58,7 @@
|
|||||||
#include "git2/remote.h"
|
#include "git2/remote.h"
|
||||||
#include "git2/refspec.h"
|
#include "git2/refspec.h"
|
||||||
|
|
||||||
|
#include "git2/net.h"
|
||||||
|
#include "git2/transport.h"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -27,7 +27,7 @@ struct git_remote_head {
|
|||||||
|
|
||||||
struct git_headarray {
|
struct git_headarray {
|
||||||
unsigned int len;
|
unsigned int len;
|
||||||
struct git_remote_head *heads;
|
struct git_remote_head **heads;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -43,12 +43,14 @@ GIT_BEGIN_DECL
|
|||||||
* @param tranport the transport for the url
|
* @param tranport the transport for the url
|
||||||
* @param url the url of the repo
|
* @param url the url of the repo
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_transport_get(git_transport *transport, const char *url);
|
GIT_EXTERN(int) git_transport_new(git_transport **transport, git_repository *repo, const char *url);
|
||||||
|
|
||||||
GIT_EXTERN(int) git_transport_connect(git_transport *transport, git_net_direction direction);
|
GIT_EXTERN(int) git_transport_connect(git_transport *transport, git_net_direction direction);
|
||||||
/*
|
|
||||||
GIT_EXTERN(const git_vector *) git_transport_get_refs(git_transport *transport);
|
GIT_EXTERN(int) git_transport_ls(git_transport *transport, git_headarray *array);
|
||||||
*/
|
GIT_EXTERN(int) git_transport_close(git_transport *transport);
|
||||||
|
GIT_EXTERN(void) git_transport_free(git_transport *transport);
|
||||||
|
|
||||||
GIT_EXTERN(int) git_transport_add(git_transport *transport, const char *prefix);
|
GIT_EXTERN(int) git_transport_add(git_transport *transport, const char *prefix);
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
@ -179,6 +179,7 @@ typedef enum git_net_direction git_net_direction;
|
|||||||
|
|
||||||
typedef int (*git_transport_cb)(git_transport *transport);
|
typedef int (*git_transport_cb)(git_transport *transport);
|
||||||
|
|
||||||
|
typedef struct git_remote_head git_remote_head;
|
||||||
typedef struct git_headarray git_headarray;
|
typedef struct git_headarray git_headarray;
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
@ -61,6 +61,8 @@ int git_transport_new(git_transport **out, git_repository *repo, const char *url
|
|||||||
if (transport == NULL)
|
if (transport == NULL)
|
||||||
return GIT_ENOMEM;
|
return GIT_ENOMEM;
|
||||||
|
|
||||||
|
memset(transport, 0x0, sizeof(git_transport));
|
||||||
|
|
||||||
transport->url = git__strdup(url);
|
transport->url = git__strdup(url);
|
||||||
if (transport->url == NULL)
|
if (transport->url == NULL)
|
||||||
return GIT_ENOMEM;
|
return GIT_ENOMEM;
|
||||||
@ -75,3 +77,23 @@ int git_transport_new(git_transport **out, git_repository *repo, const char *url
|
|||||||
|
|
||||||
return GIT_SUCCESS;
|
return GIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int git_transport_connect(git_transport *transport, git_net_direction dir)
|
||||||
|
{
|
||||||
|
return transport->connect(transport, dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
int git_transport_ls(git_transport *transport, git_headarray *array)
|
||||||
|
{
|
||||||
|
return transport->ls(transport, array);
|
||||||
|
}
|
||||||
|
|
||||||
|
int git_transport_close(git_transport *transport)
|
||||||
|
{
|
||||||
|
return transport->close(transport);
|
||||||
|
}
|
||||||
|
|
||||||
|
void git_transport_free(git_transport *transport)
|
||||||
|
{
|
||||||
|
transport->free(transport);
|
||||||
|
}
|
||||||
|
@ -43,6 +43,7 @@ struct git_transport {
|
|||||||
* Whether we want to push or fetch
|
* Whether we want to push or fetch
|
||||||
*/
|
*/
|
||||||
git_net_direction direction;
|
git_net_direction direction;
|
||||||
|
int connected : 1;
|
||||||
/**
|
/**
|
||||||
* Connect and store the remote heads
|
* Connect and store the remote heads
|
||||||
*/
|
*/
|
||||||
@ -71,6 +72,10 @@ struct git_transport {
|
|||||||
* Close the connection
|
* Close the connection
|
||||||
*/
|
*/
|
||||||
int (*close)(struct git_transport *transport);
|
int (*close)(struct git_transport *transport);
|
||||||
|
/**
|
||||||
|
* Free the associated resources
|
||||||
|
*/
|
||||||
|
void (*free)(struct git_transport *transport);
|
||||||
};
|
};
|
||||||
|
|
||||||
int git_transport_local(struct git_transport *transport);
|
int git_transport_local(struct git_transport *transport);
|
||||||
|
@ -3,8 +3,24 @@
|
|||||||
#include "git2/transport.h"
|
#include "git2/transport.h"
|
||||||
#include "git2/net.h"
|
#include "git2/net.h"
|
||||||
#include "git2/repository.h"
|
#include "git2/repository.h"
|
||||||
|
#include "git2/object.h"
|
||||||
|
#include "git2/tag.h"
|
||||||
|
#include "refs.h"
|
||||||
#include "transport.h"
|
#include "transport.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
git_vector *vec;
|
||||||
|
git_repository *repo;
|
||||||
|
} callback_data;
|
||||||
|
|
||||||
|
static int compare_heads(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const git_remote_head *heada = *(const git_remote_head **)a;
|
||||||
|
const git_remote_head *headb = *(const git_remote_head **)b;
|
||||||
|
|
||||||
|
return strcmp(heada->name, headb->name);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Try to open the url as a git directory. The direction doesn't
|
* 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 calulating the heads ourselves.
|
||||||
@ -13,21 +29,142 @@ static int local_connect(git_transport *transport, git_net_direction GIT_UNUSED(
|
|||||||
{
|
{
|
||||||
git_repository *repo;
|
git_repository *repo;
|
||||||
int error;
|
int error;
|
||||||
|
const char *path;
|
||||||
|
const char file_prefix[] = "file://";
|
||||||
|
GIT_UNUSED_ARG(dir);
|
||||||
|
|
||||||
error = git_repository_open(&repo, transport->url);
|
/* The repo layer doesn't want the prefix */
|
||||||
|
if (!git__prefixcmp(transport->url, file_prefix))
|
||||||
|
path = transport->url + STRLEN(file_prefix);
|
||||||
|
else
|
||||||
|
path = transport->url;
|
||||||
|
|
||||||
|
error = git_repository_open(&repo, path);
|
||||||
if (error < GIT_SUCCESS)
|
if (error < GIT_SUCCESS)
|
||||||
return git__rethrow(error, "Can't open remote");
|
return git__rethrow(error, "Failed to open remote");
|
||||||
|
|
||||||
transport->private = repo;
|
transport->private = repo;
|
||||||
|
|
||||||
|
transport->connected = 1;
|
||||||
|
|
||||||
return GIT_SUCCESS;
|
return GIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int heads_cb(const char *name, void *ptr)
|
||||||
|
{
|
||||||
|
callback_data *data = ptr;
|
||||||
|
git_vector *vec = data->vec;
|
||||||
|
git_repository *repo = data->repo;
|
||||||
|
const char peeled[] = "^{}";
|
||||||
|
git_remote_head *head;
|
||||||
|
git_reference *ref;
|
||||||
|
git_object *obj = NULL;
|
||||||
|
int error = GIT_SUCCESS, peel_len, ret;
|
||||||
|
|
||||||
|
head = git__malloc(sizeof(git_remote_head));
|
||||||
|
if (head == NULL)
|
||||||
|
return GIT_ENOMEM;
|
||||||
|
|
||||||
|
head->name = git__strdup(name);
|
||||||
|
if (head->name == NULL) {
|
||||||
|
error = GIT_ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = git_reference_lookup(&ref, repo, name);
|
||||||
|
if (error < GIT_SUCCESS)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
error = git_reference_resolve(&ref, ref);
|
||||||
|
if (error < GIT_SUCCESS)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
git_oid_cpy(&head->oid, git_reference_oid(ref));
|
||||||
|
|
||||||
|
error = git_vector_insert(vec, head);
|
||||||
|
if (error < GIT_SUCCESS)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/* If it's not a tag, we don't need to try to peel it */
|
||||||
|
if (git__prefixcmp(name, GIT_REFS_TAGS_DIR))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
error = git_object_lookup(&obj, repo, &head->oid, GIT_OBJ_ANY);
|
||||||
|
if (error < GIT_SUCCESS) {
|
||||||
|
git__rethrow(error, "Failed to lookup object");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If it's not an annotated tag, just get out */
|
||||||
|
if (git_object_type(obj) != GIT_OBJ_TAG)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/* And if it's a tag, peel it, and add it to the list */
|
||||||
|
head = git__malloc(sizeof(git_remote_head));
|
||||||
|
peel_len = strlen(name) + STRLEN(peeled);
|
||||||
|
head->name = git__malloc(peel_len + 1);
|
||||||
|
ret = snprintf(head->name, peel_len + 1, "%s%s", name, peeled);
|
||||||
|
if (ret >= peel_len + 1) {
|
||||||
|
error = git__throw(GIT_ERROR, "The string is magically to long");
|
||||||
|
}
|
||||||
|
|
||||||
|
git_oid_cpy(&head->oid, git_tag_target_oid((git_tag *) obj));
|
||||||
|
|
||||||
|
error = git_vector_insert(vec, head);
|
||||||
|
if (error < GIT_SUCCESS)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
out:
|
||||||
|
git_object_close(obj);
|
||||||
|
if (error < GIT_SUCCESS) {
|
||||||
|
free(head->name);
|
||||||
|
free(head);
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
static int local_ls(git_transport *transport, git_headarray *array)
|
static int local_ls(git_transport *transport, git_headarray *array)
|
||||||
{
|
{
|
||||||
|
int error;
|
||||||
|
git_repository *repo;
|
||||||
|
git_vector vec;
|
||||||
|
callback_data data;
|
||||||
|
|
||||||
|
assert(transport && transport->connected);
|
||||||
|
|
||||||
|
repo = transport->private;
|
||||||
|
error = git_vector_init(&vec, 16, compare_heads);
|
||||||
|
if (error < GIT_SUCCESS)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
data.vec = &vec;
|
||||||
|
data.repo = repo;
|
||||||
|
error = git_reference_foreach(repo, GIT_REF_LISTALL, heads_cb, &data);
|
||||||
|
if (error < GIT_SUCCESS)
|
||||||
|
return git__rethrow(error, "Failed to list remote heads");
|
||||||
|
|
||||||
|
git_vector_sort(&vec);
|
||||||
|
array->len = vec.length;
|
||||||
|
array->heads = (git_remote_head **) vec.contents;
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int local_close(git_transport *GIT_UNUSED(transport))
|
||||||
|
{
|
||||||
|
/* Nothing to do */
|
||||||
|
GIT_UNUSED_ARG(transport);
|
||||||
return GIT_SUCCESS;
|
return GIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void local_free(git_transport *transport)
|
||||||
|
{
|
||||||
|
assert(transport);
|
||||||
|
|
||||||
|
git_repository_free(transport->private);
|
||||||
|
free(transport->url);
|
||||||
|
free(transport);
|
||||||
|
}
|
||||||
|
|
||||||
/**************
|
/**************
|
||||||
* Public API *
|
* Public API *
|
||||||
**************/
|
**************/
|
||||||
@ -36,6 +173,8 @@ int git_transport_local(git_transport *transport)
|
|||||||
{
|
{
|
||||||
transport->connect = local_connect;
|
transport->connect = local_connect;
|
||||||
transport->ls = local_ls;
|
transport->ls = local_ls;
|
||||||
|
transport->close = local_close;
|
||||||
|
transport->free = local_free;
|
||||||
|
|
||||||
return GIT_SUCCESS;
|
return GIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user