mirror of
https://git.proxmox.com/git/libgit2
synced 2026-03-27 04:56:10 +00:00
242 lines
5.0 KiB
C
242 lines
5.0 KiB
C
/*
|
|
* Copyright (C) 2009-2012 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.
|
|
*/
|
|
#include "common.h"
|
|
#include "git2/types.h"
|
|
#include "git2/net.h"
|
|
#include "git2/repository.h"
|
|
#include "git2/object.h"
|
|
#include "git2/tag.h"
|
|
#include "refs.h"
|
|
#include "transport.h"
|
|
#include "posix.h"
|
|
#include "path.h"
|
|
#include "buffer.h"
|
|
|
|
typedef struct {
|
|
git_transport parent;
|
|
git_repository *repo;
|
|
git_vector refs;
|
|
} transport_local;
|
|
|
|
static int add_ref(transport_local *t, const char *name)
|
|
{
|
|
const char peeled[] = "^{}";
|
|
git_remote_head *head;
|
|
git_object *obj = NULL, *target = NULL;
|
|
git_buf buf = GIT_BUF_INIT;
|
|
|
|
head = git__malloc(sizeof(git_remote_head));
|
|
GITERR_CHECK_ALLOC(head);
|
|
|
|
head->name = git__strdup(name);
|
|
GITERR_CHECK_ALLOC(head->name);
|
|
|
|
if (git_reference_name_to_oid(&head->oid, t->repo, name) < 0 ||
|
|
git_vector_insert(&t->refs, head) < 0)
|
|
{
|
|
git__free(head->name);
|
|
git__free(head);
|
|
return -1;
|
|
}
|
|
|
|
/* If it's not a tag, we don't need to try to peel it */
|
|
if (git__prefixcmp(name, GIT_REFS_TAGS_DIR))
|
|
return 0;
|
|
|
|
if (git_object_lookup(&obj, t->repo, &head->oid, GIT_OBJ_ANY) < 0)
|
|
return -1;
|
|
|
|
head = NULL;
|
|
|
|
/* If it's not an annotated tag, just get out */
|
|
if (git_object_type(obj) != GIT_OBJ_TAG) {
|
|
git_object_free(obj);
|
|
return 0;
|
|
}
|
|
|
|
/* And if it's a tag, peel it, and add it to the list */
|
|
head = git__malloc(sizeof(git_remote_head));
|
|
GITERR_CHECK_ALLOC(head);
|
|
if (git_buf_join(&buf, 0, name, peeled) < 0)
|
|
return -1;
|
|
|
|
head->name = git_buf_detach(&buf);
|
|
|
|
if (git_tag_peel(&target, (git_tag *) obj) < 0)
|
|
goto on_error;
|
|
|
|
git_oid_cpy(&head->oid, git_object_id(target));
|
|
git_object_free(obj);
|
|
git_object_free(target);
|
|
|
|
if (git_vector_insert(&t->refs, head) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
|
|
on_error:
|
|
git_object_free(obj);
|
|
git_object_free(target);
|
|
return -1;
|
|
}
|
|
|
|
static int store_refs(transport_local *t)
|
|
{
|
|
unsigned int i;
|
|
git_strarray ref_names = {0};
|
|
|
|
assert(t);
|
|
|
|
if (git_reference_list(&ref_names, t->repo, GIT_REF_LISTALL) < 0 ||
|
|
git_vector_init(&t->refs, (unsigned int)ref_names.count, NULL) < 0)
|
|
goto on_error;
|
|
|
|
/* Sort the references first */
|
|
git__tsort((void **)ref_names.strings, ref_names.count, &git__strcmp_cb);
|
|
|
|
/* Add HEAD */
|
|
if (add_ref(t, GIT_HEAD_FILE) < 0)
|
|
goto on_error;
|
|
|
|
for (i = 0; i < ref_names.count; ++i) {
|
|
if (add_ref(t, ref_names.strings[i]) < 0)
|
|
goto on_error;
|
|
}
|
|
|
|
git_strarray_free(&ref_names);
|
|
return 0;
|
|
|
|
on_error:
|
|
git_vector_free(&t->refs);
|
|
git_strarray_free(&ref_names);
|
|
return -1;
|
|
}
|
|
|
|
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;
|
|
|
|
assert(transport && transport->connected);
|
|
|
|
git_vector_foreach(refs, i, h) {
|
|
if (list_cb(h, payload) < 0)
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* 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 direction)
|
|
{
|
|
git_repository *repo;
|
|
int error;
|
|
transport_local *t = (transport_local *) transport;
|
|
const char *path;
|
|
git_buf buf = GIT_BUF_INIT;
|
|
|
|
GIT_UNUSED(direction);
|
|
|
|
/* The repo layer doesn't want the prefix */
|
|
if (!git__prefixcmp(transport->url, "file://")) {
|
|
if (git_path_fromurl(&buf, transport->url) < 0) {
|
|
git_buf_free(&buf);
|
|
return -1;
|
|
}
|
|
path = git_buf_cstr(&buf);
|
|
|
|
} else { /* We assume transport->url is already a path */
|
|
path = transport->url;
|
|
}
|
|
|
|
error = git_repository_open(&repo, path);
|
|
|
|
git_buf_free(&buf);
|
|
|
|
if (error < 0)
|
|
return -1;
|
|
|
|
t->repo = repo;
|
|
|
|
if (store_refs(t) < 0)
|
|
return -1;
|
|
|
|
t->parent.connected = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int local_negotiate_fetch(git_transport *transport, git_repository *repo, const git_vector *wants)
|
|
{
|
|
GIT_UNUSED(transport);
|
|
GIT_UNUSED(repo);
|
|
GIT_UNUSED(wants);
|
|
|
|
giterr_set(GITERR_NET, "Fetch via local transport isn't implemented. Sorry");
|
|
return -1;
|
|
}
|
|
|
|
static int local_close(git_transport *transport)
|
|
{
|
|
transport_local *t = (transport_local *)transport;
|
|
|
|
t->parent.connected = 0;
|
|
git_repository_free(t->repo);
|
|
t->repo = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void local_free(git_transport *transport)
|
|
{
|
|
unsigned int i;
|
|
transport_local *t = (transport_local *) transport;
|
|
git_vector *vec = &t->refs;
|
|
git_remote_head *h;
|
|
|
|
assert(transport);
|
|
|
|
git_vector_foreach (vec, i, h) {
|
|
git__free(h->name);
|
|
git__free(h);
|
|
}
|
|
git_vector_free(vec);
|
|
|
|
git__free(t->parent.url);
|
|
git__free(t);
|
|
}
|
|
|
|
/**************
|
|
* Public API *
|
|
**************/
|
|
|
|
int git_transport_local(git_transport **out)
|
|
{
|
|
transport_local *t;
|
|
|
|
t = git__malloc(sizeof(transport_local));
|
|
GITERR_CHECK_ALLOC(t);
|
|
|
|
memset(t, 0x0, sizeof(transport_local));
|
|
|
|
t->parent.connect = local_connect;
|
|
t->parent.ls = local_ls;
|
|
t->parent.negotiate_fetch = local_negotiate_fetch;
|
|
t->parent.close = local_close;
|
|
t->parent.free = local_free;
|
|
|
|
*out = (git_transport *) t;
|
|
|
|
return 0;
|
|
}
|