Commit d88d4311 by Vicent Marti

remote: Cleanup the remotes code

- Hide the remaining transports code
- Drop `git_headarray`, switch to using a callback to list refs. Makes
the code cleaner.
parent c94785a9
default: all
# If you've installed libgit2 to a non-standard location, you can use
# these lines to make pkg-config find it.
#LIBGIT2_PATH ?= $(HOME)/staging/libgit2/lib DEPS =
#$(shell PKG_CONFIG_PATH=$(LIBGIT2_PATH)/pkgconfig pkg-config --cflags
#--libs libgit2)
DEPS = $(shell pkg-config --cflags --libs libgit2)
CC = gcc
CFLAGS += -g
CFLAGS += $(DEPS)
CFLAGS += -I../../include -L../../ -lgit2
OBJECTS = \
git2.o \
......
......@@ -4,23 +4,6 @@
#include <stdlib.h>
#include <string.h>
static void show_refs(git_headarray *refs)
{
int i;
git_remote_head *head;
if(refs->len == 0)
puts("Everything up-to-date");
for(i = 0; i < refs->len; ++i){
char oid[GIT_OID_HEXSZ + 1] = {0};
char *havewant;
head = refs->heads[i];
git_oid_fmt(oid, &head->oid);
printf("%s\t%s\n", oid, head->name);
}
}
static int rename_packfile(char *packname, git_indexer *idx)
{
char path[GIT_PATH_MAX], oid[GIT_OID_HEXSZ + 1], *slash;
......@@ -50,20 +33,14 @@ static int rename_packfile(char *packname, git_indexer *idx)
int fetch(git_repository *repo, int argc, char **argv)
{
git_remote *remote = NULL;
git_config *cfg = NULL;
git_indexer *idx = NULL;
git_indexer_stats stats;
int error;
char *packname = NULL;
// Load the repository's configuration
error = git_repository_config(&cfg, repo, NULL, NULL);
if (error < GIT_SUCCESS)
return error;
// Get the remote and connect to it
printf("Fetching %s\n", argv[1]);
error = git_remote_get(&remote, cfg, argv[1]);
error = git_remote_new(&remote, repo, argv[1], NULL);
if (error < GIT_SUCCESS)
return error;
......@@ -71,13 +48,6 @@ int fetch(git_repository *repo, int argc, char **argv)
if (error < GIT_SUCCESS)
return error;
// Perform the packfile negotiation. This is where the two ends
// figure out the minimal amount of data that should be transmitted
// to bring the repository up-to-date
error = git_remote_negotiate(remote);
if (error < GIT_SUCCESS)
return error;
// Download the packfile from the server. As we don't know its hash
// yet, it will get a temporary filename
error = git_remote_download(&packname, remote);
......
......@@ -4,31 +4,22 @@
#include <string.h>
#include "common.h"
static void show_refs(git_headarray *refs)
static int show_ref__cb(git_remote_head *head, void *payload)
{
int i;
git_remote_head *head;
// Take each head that the remote has advertised, store the string
// representation of the OID in a buffer and print it
for(i = 0; i < refs->len; ++i){
char oid[GIT_OID_HEXSZ + 1] = {0};
head = refs->heads[i];
git_oid_fmt(oid, &head->oid);
printf("%s\t%s\n", oid, head->name);
}
char oid[GIT_OID_HEXSZ + 1] = {0};
git_oid_fmt(oid, &head->oid);
printf("%s\t%s\n", oid, head->name);
return GIT_SUCCESS;
}
int use_unnamed(git_repository *repo, const char *url)
{
git_remote *remote = NULL;
git_headarray refs;
int error;
// Create an instance of a remote from the URL. The transport to use
// is detected from the URL
error = git_remote_new(&remote, repo, url);
error = git_remote_new(&remote, repo, url, NULL);
if (error < GIT_SUCCESS)
goto cleanup;
......@@ -39,32 +30,20 @@ int use_unnamed(git_repository *repo, const char *url)
goto cleanup;
// With git_remote_ls we can retrieve the advertised heads
error = git_remote_ls(remote, &refs);
if (error < GIT_SUCCESS)
goto cleanup;
show_refs(&refs);
error = git_remote_ls(remote, &show_ref__cb, NULL);
cleanup:
git_remote_free(remote);
return error;
}
int use_remote(git_repository *repo, char *name)
{
git_remote *remote = NULL;
git_config *cfg = NULL;
git_headarray refs;
int error;
// Load the local configuration for the repository
error = git_repository_config(&cfg, repo, NULL, NULL);
if (error < GIT_SUCCESS)
return error;
// Find the remote by name
error = git_remote_get(&remote, cfg, name);
error = git_remote_load(&remote, repo, name);
if (error < GIT_SUCCESS)
goto cleanup;
......@@ -72,15 +51,10 @@ int use_remote(git_repository *repo, char *name)
if (error < GIT_SUCCESS)
goto cleanup;
error = git_remote_ls(remote, &refs);
if (error < GIT_SUCCESS)
goto cleanup;
show_refs(&refs);
error = git_remote_ls(remote, &show_ref__cb, NULL);
cleanup:
git_remote_free(remote);
return error;
}
......@@ -89,8 +63,6 @@ cleanup:
int ls_remote(git_repository *repo, int argc, char **argv)
{
git_headarray heads;
git_remote_head *head;
int error, i;
/* If there's a ':' in the name, assume it's an URL */
......
......@@ -38,7 +38,6 @@
#include "git2/refspec.h"
#include "git2/net.h"
#include "git2/transport.h"
#include "git2/status.h"
#include "git2/indexer.h"
......
......@@ -30,6 +30,7 @@ GIT_BEGIN_DECL
#define GIT_DIR_FETCH 0
#define GIT_DIR_PUSH 1
/**
* Remote head description, given out on `ls` calls.
*/
......@@ -41,12 +42,9 @@ struct git_remote_head {
};
/**
* Array of remote heads
* Callback for listing the remote heads
*/
struct git_headarray {
unsigned int len;
struct git_remote_head **heads;
};
typedef int (*git_headlist_cb)(git_remote_head *, void *);
/** @} */
GIT_END_DECL
......
......@@ -10,6 +10,8 @@
#include "common.h"
#include "repository.h"
#include "refspec.h"
#include "net.h"
/**
* @file git2/remote.h
* @brief Git remote management functions
......@@ -107,7 +109,7 @@ GIT_EXTERN(int) git_remote_connect(git_remote *remote, int direction);
* @param remote the remote
* @return GIT_SUCCESS or an error code
*/
GIT_EXTERN(int) git_remote_ls(git_remote *remote, git_headarray *refs);
GIT_EXTERN(int) git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void *payload);
/**
* Download the packfile
......@@ -161,6 +163,14 @@ GIT_EXTERN(void) git_remote_free(git_remote *remote);
*/
GIT_EXTERN(int) git_remote_update_tips(git_remote *remote);
/**
* Return whether a string is a valid remote URL
*
* @param tranport the url to check
* @param 1 if the url is valid, 0 otherwise
*/
GIT_EXTERN(int) git_remote_valid_url(const char *url);
/** @} */
GIT_END_DECL
#endif
/*
* Copyright (C) 2009-2011 the libgit2 contributors
*
* 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_git_transport_h__
#define INCLUDE_git_transport_h__
#include "common.h"
#include "types.h"
#include "net.h"
/**
* @file git2/transport.h
* @brief Git protocol transport abstraction
* @defgroup git_transport Git protocol transport abstraction
* @ingroup Git
* @{
*/
GIT_BEGIN_DECL
/**
* Get the appropriate transport for an URL.
* @param tranport the transport for the url
* @param url the url of the repo
*/
GIT_EXTERN(int) git_transport_new(git_transport **transport, const char *url);
/**
* Return whether a string is a valid transport URL
*
* @param tranport the url to check
* @param 1 if the url is valid, 0 otherwise
*/
GIT_EXTERN(int) git_transport_valid_url(const char *url);
/** @} */
GIT_END_DECL
#endif
......@@ -161,13 +161,7 @@ typedef enum {
typedef struct git_refspec git_refspec;
typedef struct git_remote git_remote;
/** A transport to use */
typedef struct git_transport git_transport;
typedef int (*git_transport_cb)(git_transport **transport);
typedef struct git_remote_head git_remote_head;
typedef struct git_headarray git_headarray;
/** @} */
GIT_END_DECL
......
......@@ -18,30 +18,46 @@
#include "fetch.h"
#include "netops.h"
static int filter_wants(git_remote *remote)
{
git_vector list;
git_headarray refs;
git_remote_head *head;
git_transport *t = remote->transport;
git_odb *odb = NULL;
struct filter_payload {
git_remote *remote;
const git_refspec *spec;
git_odb *odb;
int found_head;
};
static int filter_ref__cb(git_remote_head *head, void *payload)
{
struct filter_payload *p = payload;
int error;
unsigned int i = 0;
error = git_vector_init(&list, 16, NULL);
if (error < GIT_SUCCESS)
return error;
if (!p->found_head && strcmp(head->name, GIT_HEAD_FILE) == 0) {
p->found_head = 1;
} else {
/* If it doesn't match the refpec, we don't want it */
error = git_refspec_src_match(p->spec, head->name);
error = t->ls(t, &refs);
if (error < GIT_SUCCESS) {
error = git__rethrow(error, "Failed to get remote ref list");
goto cleanup;
if (error == GIT_ENOMATCH)
return GIT_SUCCESS;
if (error < GIT_SUCCESS)
return git__rethrow(error, "Error matching remote ref name");
}
error = git_repository_odb__weakptr(&odb, remote->repo);
if (error < GIT_SUCCESS)
goto cleanup;
/* If we have the object, mark it so we don't ask for it */
if (git_odb_exists(p->odb, &head->oid))
head->local = 1;
else
p->remote->need_pack = 1;
return git_vector_insert(&p->remote->refs, head);
}
static int filter_wants(git_remote *remote)
{
int error;
struct filter_payload p;
git_vector_clear(&remote->refs);
/*
* The fetch refspec can be NULL, and what this means is that the
......@@ -49,56 +65,15 @@ static int filter_wants(git_remote *remote)
* not interested in any particular branch but just the remote's
* HEAD, which will be stored in FETCH_HEAD after the fetch.
*/
spec = git_remote_fetchspec(remote);
p.spec = git_remote_fetchspec(remote);
p.found_head = 0;
p.remote = remote;
/*
* We need to handle HEAD separately, as we always want it, but it
* probably won't matcht he refspec.
*/
head = refs.heads[0];
if (refs.len > 0 && !strcmp(head->name, GIT_HEAD_FILE)) {
if (git_odb_exists(odb, &head->oid))
head->local = 1;
else
remote->need_pack = 1;
i = 1;
error = git_vector_insert(&list, refs.heads[0]);
if (error < GIT_SUCCESS)
goto cleanup;
}
for (; i < refs.len; ++i) {
head = refs.heads[i];
/* If it doesn't match the refpec, we don't want it */
error = git_refspec_src_match(spec, head->name);
if (error == GIT_ENOMATCH)
continue;
if (error < GIT_SUCCESS) {
error = git__rethrow(error, "Error matching remote ref name");
goto cleanup;
}
/* If we have the object, mark it so we don't ask for it */
if (git_odb_exists(odb, &head->oid))
head->local = 1;
else
remote->need_pack = 1;
error = git_vector_insert(&list, head);
if (error < GIT_SUCCESS)
goto cleanup;
}
remote->refs.len = list.length;
remote->refs.heads = (git_remote_head **) list.contents;
return GIT_SUCCESS;
error = git_repository_odb__weakptr(&p.odb, remote->repo);
if (error < GIT_SUCCESS)
return error;
cleanup:
git_vector_free(&list);
return error;
return remote->transport->ls(remote->transport, &filter_ref__cb, &p);
}
/*
......@@ -116,8 +91,9 @@ int git_fetch_negotiate(git_remote *remote)
return git__rethrow(error, "Failed to filter the reference list for wants");
/* Don't try to negotiate when we don't want anything */
if (remote->refs.len == 0)
if (remote->refs.length == 0)
return GIT_SUCCESS;
if (!remote->need_pack)
return GIT_SUCCESS;
......
......@@ -316,30 +316,30 @@ static int send_want_with_caps(git_remote_head *head, git_transport_caps *caps,
* is overwrite the OID each time.
*/
int git_pkt_buffer_wants(git_headarray *refs, git_transport_caps *caps, git_buf *buf)
int git_pkt_buffer_wants(const git_vector *refs, git_transport_caps *caps, git_buf *buf)
{
unsigned int i = 0;
int error;
git_remote_head *head;
if (caps->common) {
for (; i < refs->len; ++i) {
head = refs->heads[i];
for (; i < refs->length; ++i) {
head = refs->contents[i];
if (!head->local)
break;
}
error = buffer_want_with_caps(refs->heads[i], caps, buf);
error = buffer_want_with_caps(refs->contents[i], caps, buf);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to buffer want with caps");
i++;
}
for (; i < refs->len; ++i) {
for (; i < refs->length; ++i) {
char oid[GIT_OID_HEXSZ];
head = refs->heads[i];
head = refs->contents[i];
if (head->local)
continue;
......@@ -352,7 +352,7 @@ int git_pkt_buffer_wants(git_headarray *refs, git_transport_caps *caps, git_buf
return git_pkt_buffer_flush(buf);
}
int git_pkt_send_wants(git_headarray *refs, git_transport_caps *caps, int fd)
int git_pkt_send_wants(const git_vector *refs, git_transport_caps *caps, int fd)
{
unsigned int i = 0;
int error = GIT_SUCCESS;
......@@ -365,15 +365,15 @@ int git_pkt_send_wants(git_headarray *refs, git_transport_caps *caps, int fd)
/* If there are common caps, find the first one */
if (caps->common) {
for (; i < refs->len; ++i) {
head = refs->heads[i];
for (; i < refs->length; ++i) {
head = refs->contents[i];
if (head->local)
continue;
else
break;
}
error = send_want_with_caps(refs->heads[i], caps, fd);
error = send_want_with_caps(refs->contents[i], caps, fd);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to send want pkt with caps");
/* Increase it here so it's correct whether we run this or not */
......@@ -381,8 +381,8 @@ int git_pkt_send_wants(git_headarray *refs, git_transport_caps *caps, int fd)
}
/* Continue from where we left off */
for (; i < refs->len; ++i) {
head = refs->heads[i];
for (; i < refs->length; ++i) {
head = refs->contents[i];
if (head->local)
continue;
......
......@@ -68,8 +68,8 @@ int git_pkt_buffer_flush(git_buf *buf);
int git_pkt_send_flush(int s);
int git_pkt_buffer_done(git_buf *buf);
int git_pkt_send_done(int s);
int git_pkt_buffer_wants(git_headarray *refs, git_transport_caps *caps, git_buf *buf);
int git_pkt_send_wants(git_headarray *refs, git_transport_caps *caps, int fd);
int git_pkt_buffer_wants(const git_vector *refs, git_transport_caps *caps, git_buf *buf);
int git_pkt_send_wants(const git_vector *refs, git_transport_caps *caps, int fd);
int git_pkt_buffer_have(git_oid *oid, git_buf *buf);
int git_pkt_send_have(git_oid *oid, int fd);
void git_pkt_free(git_pkt *pkt);
......
......@@ -70,16 +70,21 @@ int git_remote_new(git_remote **out, git_repository *repo, const char *url, cons
memset(remote, 0x0, sizeof(git_remote));
remote->repo = repo;
if (git_vector_init(&remote->refs, 32, NULL) < 0) {
git_remote_free(remote);
return GIT_ENOMEM;
}
remote->url = git__strdup(url);
if (remote->url == NULL) {
git__free(remote);
git_remote_free(remote);
return GIT_ENOMEM;
}
if (name != NULL) {
remote->name = git__strdup(name);
if (remote->name == NULL) {
git__free(remote);
git_remote_free(remote);
return GIT_ENOMEM;
}
}
......@@ -113,6 +118,11 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name)
goto cleanup;
}
if (git_vector_init(&remote->refs, 32, NULL) < 0) {
error = GIT_ENOMEM;
goto cleanup;
}
/* "fetch" is the longest var name we're interested in */
buf_len = strlen("remote.") + strlen(".fetch") + strlen(name) + 1;
buf = git__malloc(buf_len);
......@@ -227,10 +237,14 @@ cleanup:
return error;
}
int git_remote_ls(git_remote *remote, git_headarray *refs)
int git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void *payload)
{
assert(remote && refs);
return remote->transport->ls(remote->transport, refs);
assert(remote);
if (!remote->transport)
return git__throw(GIT_ERROR, "The remote is not connected");
return remote->transport->ls(remote->transport, list_cb, payload);
}
int git_remote_download(char **filename, git_remote *remote)
......@@ -250,7 +264,7 @@ int git_remote_update_tips(git_remote *remote)
int error = GIT_SUCCESS;
unsigned int i = 0;
char refname[GIT_PATH_MAX];
git_headarray *refs = &remote->refs;
git_vector *refs = &remote->refs;
git_remote_head *head;
git_reference *ref;
struct git_refspec *spec = &remote->fetch;
......@@ -259,11 +273,11 @@ int git_remote_update_tips(git_remote *remote)
memset(refname, 0x0, sizeof(refname));
if (refs->len == 0)
if (refs->length == 0)
return GIT_SUCCESS;
/* HEAD is only allowed to be the first in the list */
head = refs->heads[0];
head = refs->contents[0];
if (!strcmp(head->name, GIT_HEAD_FILE)) {
error = git_reference_create_oid(&ref, remote->repo, GIT_FETCH_HEAD_FILE, &head->oid, 1);
i = 1;
......@@ -272,8 +286,8 @@ int git_remote_update_tips(git_remote *remote)
return git__rethrow(error, "Failed to update FETCH_HEAD");
}
for (; i < refs->len; ++i) {
head = refs->heads[i];
for (; i < refs->length; ++i) {
head = refs->contents[i];
error = git_refspec_transform(refname, sizeof(refname), spec, head->name);
if (error < GIT_SUCCESS)
......@@ -319,6 +333,7 @@ void git_remote_free(git_remote *remote)
git__free(remote->push.dst);
git__free(remote->url);
git__free(remote->name);
git_vector_free(&remote->refs);
git_remote_disconnect(remote);
git__free(remote);
}
......@@ -14,7 +14,7 @@
struct git_remote {
char *name;
char *url;
git_headarray refs;
git_vector refs;
struct git_refspec fetch;
struct git_refspec push;
git_transport *transport;
......
......@@ -6,7 +6,7 @@
*/
#include "common.h"
#include "git2/types.h"
#include "git2/transport.h"
#include "git2/remote.h"
#include "git2/net.h"
#include "transport.h"
......@@ -49,11 +49,6 @@ int git_transport_dummy(git_transport **GIT_UNUSED(transport))
return git__throw(GIT_ENOTIMPLEMENTED, "This protocol isn't implemented. Sorry");
}
int git_transport_valid_url(const char *url)
{
return transport_find_fn(url) != NULL;
}
int git_transport_new(git_transport **out, const char *url)
{
git_transport_cb fn;
......@@ -81,3 +76,10 @@ int git_transport_new(git_transport **out, const char *url)
return GIT_SUCCESS;
}
/* from remote.h */
int git_remote_valid_url(const char *url)
{
return transport_find_fn(url) != NULL;
}
......@@ -7,7 +7,6 @@
#ifndef INCLUDE_transport_h__
#define INCLUDE_transport_h__
#include "git2/transport.h"
#include "git2/net.h"
#include "vector.h"
......@@ -61,7 +60,7 @@ struct git_transport {
/**
* Give a list of references, useful for ls-remote
*/
int (*ls)(struct git_transport *transport, git_headarray *headarray);
int (*ls)(struct git_transport *transport, git_headlist_cb list_cb, void *opaque);
/**
* Push the changes over
*/
......@@ -74,7 +73,7 @@ struct git_transport {
* Negotiate the minimal amount of objects that need to be
* retrieved
*/
int (*negotiate_fetch)(struct git_transport *transport, git_repository *repo, git_headarray *list);
int (*negotiate_fetch)(struct git_transport *transport, git_repository *repo, const git_vector *wants);
/**
* Send a flush
*/
......@@ -97,9 +96,15 @@ struct git_transport {
void (*free)(struct git_transport *transport);
};
int git_transport_new(struct git_transport **transport, const char *url);
int git_transport_local(struct git_transport **transport);
int git_transport_git(struct git_transport **transport);
int git_transport_http(struct git_transport **transport);
int git_transport_dummy(struct git_transport **transport);
int git_transport_valid_url(const char *url);
typedef struct git_transport git_transport;
typedef int (*git_transport_cb)(git_transport **transport);
#endif
......@@ -226,32 +226,30 @@ cleanup:
return error;
}
static int git_ls(git_transport *transport, git_headarray *array)
static int git_ls(git_transport *transport, git_headlist_cb list_cb, void *opaque)
{
transport_git *t = (transport_git *) transport;
git_vector *refs = &t->refs;
int len = 0;
unsigned int i;
git_pkt *p = NULL;
array->heads = git__calloc(refs->length, sizeof(git_remote_head *));
if (array->heads == NULL)
return GIT_ENOMEM;
git_vector_foreach(refs, i, p) {
git_pkt_ref *pkt = NULL;
for (i = 0; i < refs->length; ++i) {
git_pkt *p = git_vector_get(refs, i);
if (p->type != GIT_PKT_REF)
continue;
++len;
array->heads[i] = &(((git_pkt_ref *) p)->head);
pkt = (git_pkt_ref *)p;
if (list_cb(&pkt->head, opaque) < 0)
return git__throw(GIT_ERROR,
"The user callback returned an error code");
}
array->len = len;
t->heads = array->heads;
return GIT_SUCCESS;
}
static int git_negotiate_fetch(git_transport *transport, git_repository *repo, git_headarray *wants)
static int git_negotiate_fetch(git_transport *transport, git_repository *repo, const git_vector *wants)
{
transport_git *t = (transport_git *) transport;
git_revwalk *walk;
......@@ -290,6 +288,7 @@ static int git_negotiate_fetch(git_transport *transport, git_repository *repo, g
if (git_reference_type(ref) == GIT_REF_SYMBOLIC)
continue;
error = git_revwalk_push(walk, git_reference_oid(ref));
if (error < GIT_ERROR) {
error = git__rethrow(error, "Failed to push %s", refs.strings[i]);
......
......@@ -301,29 +301,22 @@ cleanup:
return error;
}
static int http_ls(git_transport *transport, git_headarray *array)
static int http_ls(git_transport *transport, git_headlist_cb list_cb, void *opaque)
{
transport_http *t = (transport_http *) transport;
git_vector *refs = &t->refs;
unsigned int i;
int len = 0;
git_pkt_ref *p;
array->heads = git__calloc(refs->length, sizeof(git_remote_head*));
if (array->heads == NULL)
return GIT_ENOMEM;
git_vector_foreach(refs, i, p) {
if (p->type != GIT_PKT_REF)
continue;
array->heads[len] = &p->head;
len++;
if (list_cb(&p->head, opaque) < 0)
return git__throw(GIT_ERROR,
"The user callback returned an error code");
}
array->len = len;
t->heads = array->heads;
return GIT_SUCCESS;
}
......@@ -470,7 +463,7 @@ cleanup:
return error;
}
static int http_negotiate_fetch(git_transport *transport, git_repository *repo, git_headarray *wants)
static int http_negotiate_fetch(git_transport *transport, git_repository *repo, const git_vector *wants)
{
transport_http *t = (transport_http *) transport;
int error;
......
......@@ -6,7 +6,6 @@
*/
#include "common.h"
#include "git2/types.h"
#include "git2/transport.h"
#include "git2/net.h"
#include "git2/repository.h"
#include "git2/object.h"
......@@ -18,39 +17,10 @@
typedef struct {
git_transport parent;
git_repository *repo;
git_vector *refs;
git_vector refs;
} transport_local;
/*
* Try to open the url as a git directory. The direction doesn't
* matter in this case because we're calulating the heads ourselves.
*/
static int local_connect(git_transport *transport, int GIT_UNUSED(direction))
{
git_repository *repo;
int error;
transport_local *t = (transport_local *) transport;
const char *path;
const char file_prefix[] = "file://";
GIT_UNUSED_ARG(direction);
/* 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, "Failed to open remote");
t->repo = repo;
t->parent.connected = 1;
return GIT_SUCCESS;
}
static int add_ref(const char *name, git_repository *repo, git_vector *vec)
static int add_ref(transport_local *t, const char *name)
{
const char peeled[] = "^{}";
git_remote_head *head;
......@@ -68,7 +38,7 @@ static int add_ref(const char *name, git_repository *repo, git_vector *vec)
goto out;
}
error = git_reference_lookup(&ref, repo, name);
error = git_reference_lookup(&ref, t->repo, name);
if (error < GIT_SUCCESS)
goto out;
......@@ -78,15 +48,17 @@ static int add_ref(const char *name, git_repository *repo, git_vector *vec)
git_oid_cpy(&head->oid, git_reference_oid(ref));
error = git_vector_insert(vec, head);
error = git_vector_insert(&t->refs, head);
if (error < GIT_SUCCESS)
goto out;
head = NULL;
/* 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);
error = git_object_lookup(&obj, t->repo, &head->oid, GIT_OBJ_ANY);
if (error < GIT_SUCCESS) {
git__rethrow(error, "Failed to lookup object");
}
......@@ -100,13 +72,12 @@ static int add_ref(const char *name, git_repository *repo, git_vector *vec)
peel_len = strlen(name) + strlen(peeled);
head->name = git__malloc(peel_len + 1);
ret = p_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");
}
assert(ret < peel_len + 1);
git_oid_cpy(&head->oid, git_tag_target_oid((git_tag *) obj));
error = git_vector_insert(vec, head);
error = git_vector_insert(&t->refs, head);
if (error < GIT_SUCCESS)
goto out;
......@@ -115,70 +86,108 @@ static int add_ref(const char *name, git_repository *repo, git_vector *vec)
git_reference_free(resolved_ref);
git_object_free(obj);
if (error < GIT_SUCCESS) {
if (head && error < GIT_SUCCESS) {
git__free(head->name);
git__free(head);
}
return error;
}
static int local_ls(git_transport *transport, git_headarray *array)
static int store_refs(transport_local *t)
{
int error;
unsigned int i;
git_repository *repo;
git_vector *vec;
git_strarray refs;
transport_local *t = (transport_local *) transport;
git_strarray ref_names = {0};
assert(transport && transport->connected);
repo = t->repo;
assert(t);
error = git_reference_listall(&refs, repo, GIT_REF_LISTALL);
error = git_vector_init(&t->refs, ref_names.count, NULL);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to list remote heads");
vec = git__malloc(sizeof(git_vector));
if (vec == NULL) {
error = GIT_ENOMEM;
goto out;
}
return error;
error = git_vector_init(vec, refs.count, NULL);
error = git_reference_listall(&ref_names, t->repo, GIT_REF_LISTALL);
if (error < GIT_SUCCESS)
return error;
return git__rethrow(error, "Failed to list remote heads");
/* Sort the references first */
git__tsort((void **)refs.strings, refs.count, &git__strcmp_cb);
git__tsort((void **)ref_names.strings, ref_names.count, &git__strcmp_cb);
/* Add HEAD */
error = add_ref(GIT_HEAD_FILE, repo, vec);
error = add_ref(t, GIT_HEAD_FILE);
if (error < GIT_SUCCESS)
goto out;
goto cleanup;
for (i = 0; i < refs.count; ++i) {
error = add_ref(refs.strings[i], repo, vec);
for (i = 0; i < ref_names.count; ++i) {
error = add_ref(t, ref_names.strings[i]);
if (error < GIT_SUCCESS)
goto out;
goto cleanup;
}
array->len = vec->length;
array->heads = (git_remote_head **)vec->contents;
cleanup:
git_strarray_free(&ref_names);
return error;
}
t->refs = vec;
static int local_ls(git_transport *transport, git_headlist_cb list_cb, void *payload)
{
transport_local *t = (transport_local *) transport;
git_vector *refs = &t->refs;
unsigned int i;
git_remote_head *h;
out:
assert(transport && transport->connected);
git_strarray_free(&refs);
git_vector_foreach(refs, i, h) {
if (list_cb(h, payload) < 0)
return git__throw(GIT_ERROR,
"The user callback returned an error code");
}
return error;
return GIT_SUCCESS;
}
/*
* Try to open the url as a git directory. The direction doesn't
* matter in this case because we're calulating the heads ourselves.
*/
static int local_connect(git_transport *transport, int GIT_UNUSED(direction))
{
git_repository *repo;
int error;
transport_local *t = (transport_local *) transport;
const char *path;
const char file_prefix[] = "file://";
GIT_UNUSED_ARG(direction);
/* 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, "Failed to open remote");
error = store_refs(t);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to retrieve references");
t->repo = repo;
t->parent.connected = 1;
return GIT_SUCCESS;
}
static int local_close(git_transport *GIT_UNUSED(transport))
{
/* Nothing to do */
GIT_UNUSED_ARG(transport);
transport_local *t = (transport_local *)transport;
git_repository_free(t->repo);
t->repo = NULL;
return GIT_SUCCESS;
}
......@@ -186,21 +195,17 @@ static void local_free(git_transport *transport)
{
unsigned int i;
transport_local *t = (transport_local *) transport;
git_vector *vec = t->refs;
git_vector *vec = &t->refs;
git_remote_head *h;
assert(transport);
if (t->refs != NULL) {
git_vector_foreach (vec, i, h) {
git__free(h->name);
git__free(h);
}
git_vector_free(vec);
git__free(vec);
git_vector_foreach (vec, i, h) {
git__free(h->name);
git__free(h);
}
git_vector_free(vec);
git_repository_free(t->repo);
git__free(t->parent.url);
git__free(t);
}
......
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