summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Martín Nieto <carlos@cmartin.tk>2011-06-25 15:10:09 +0200
committerCarlos Martín Nieto <carlos@cmartin.tk>2011-06-26 18:18:12 +0200
commitd6258debbe05e3892466722f897ae932e8e7d8aa (patch)
tree1fe110900abb96bfa97a45a8703b3c3b9ff10f20
parent8f866daee5a0a43702f349c7fa46d3274542650c (diff)
downloadlibgit2-d6258debbe05e3892466722f897ae932e8e7d8aa.tar.gz
Implement ls-remote on local drive
Signed-off-by: Carlos Martín Nieto <carlos@cmartin.tk>
-rw-r--r--include/git2.h3
-rw-r--r--include/git2/net.h2
-rw-r--r--include/git2/transport.h10
-rw-r--r--include/git2/types.h1
-rw-r--r--src/transport.c22
-rw-r--r--src/transport.h5
-rw-r--r--src/transport_local.c143
7 files changed, 179 insertions, 7 deletions
diff --git a/include/git2.h b/include/git2.h
index 6ede73cb5..f94b535c4 100644
--- a/include/git2.h
+++ b/include/git2.h
@@ -58,4 +58,7 @@
#include "git2/remote.h"
#include "git2/refspec.h"
+#include "git2/net.h"
+#include "git2/transport.h"
+
#endif
diff --git a/include/git2/net.h b/include/git2/net.h
index 869309f9d..67e8a44e5 100644
--- a/include/git2/net.h
+++ b/include/git2/net.h
@@ -27,7 +27,7 @@ struct git_remote_head {
struct git_headarray {
unsigned int len;
- struct git_remote_head *heads;
+ struct git_remote_head **heads;
};
#endif
diff --git a/include/git2/transport.h b/include/git2/transport.h
index dfbc1a84c..a12b11b34 100644
--- a/include/git2/transport.h
+++ b/include/git2/transport.h
@@ -43,12 +43,14 @@ GIT_BEGIN_DECL
* @param tranport the transport for the url
* @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(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);
/** @} */
diff --git a/include/git2/types.h b/include/git2/types.h
index 69aa28955..8e0659127 100644
--- a/include/git2/types.h
+++ b/include/git2/types.h
@@ -179,6 +179,7 @@ typedef enum git_net_direction git_net_direction;
typedef int (*git_transport_cb)(git_transport *transport);
+typedef struct git_remote_head git_remote_head;
typedef struct git_headarray git_headarray;
/** @} */
diff --git a/src/transport.c b/src/transport.c
index c08345968..2ef99dc38 100644
--- a/src/transport.c
+++ b/src/transport.c
@@ -61,6 +61,8 @@ int git_transport_new(git_transport **out, git_repository *repo, const char *url
if (transport == NULL)
return GIT_ENOMEM;
+ memset(transport, 0x0, sizeof(git_transport));
+
transport->url = git__strdup(url);
if (transport->url == NULL)
return GIT_ENOMEM;
@@ -75,3 +77,23 @@ int git_transport_new(git_transport **out, git_repository *repo, const char *url
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);
+}
diff --git a/src/transport.h b/src/transport.h
index 8585b00f7..d70caaacf 100644
--- a/src/transport.h
+++ b/src/transport.h
@@ -43,6 +43,7 @@ struct git_transport {
* Whether we want to push or fetch
*/
git_net_direction direction;
+ int connected : 1;
/**
* Connect and store the remote heads
*/
@@ -71,6 +72,10 @@ struct git_transport {
* Close the connection
*/
int (*close)(struct git_transport *transport);
+ /**
+ * Free the associated resources
+ */
+ void (*free)(struct git_transport *transport);
};
int git_transport_local(struct git_transport *transport);
diff --git a/src/transport_local.c b/src/transport_local.c
index f492a875f..2cc78ca31 100644
--- a/src/transport_local.c
+++ b/src/transport_local.c
@@ -3,8 +3,24 @@
#include "git2/transport.h"
#include "git2/net.h"
#include "git2/repository.h"
+#include "git2/object.h"
+#include "git2/tag.h"
+#include "refs.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
* 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;
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)
- return git__rethrow(error, "Can't open remote");
+ return git__rethrow(error, "Failed to open remote");
transport->private = repo;
+ transport->connected = 1;
+
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)
{
+ 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;
}
+static void local_free(git_transport *transport)
+{
+ assert(transport);
+
+ git_repository_free(transport->private);
+ free(transport->url);
+ free(transport);
+}
+
/**************
* Public API *
**************/
@@ -36,6 +173,8 @@ int git_transport_local(git_transport *transport)
{
transport->connect = local_connect;
transport->ls = local_ls;
+ transport->close = local_close;
+ transport->free = local_free;
return GIT_SUCCESS;
}