From 8f866daee5a0a43702f349c7fa46d3274542650c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 16 May 2011 22:07:08 +0200 Subject: [PATCH] Lay down the fundations for the network code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Carlos Martín Nieto --- include/git2/net.h | 33 +++++++++++++++++ include/git2/transport.h | 56 ++++++++++++++++++++++++++++ include/git2/types.h | 11 ++++++ src/transport.c | 77 +++++++++++++++++++++++++++++++++++++++ src/transport.h | 79 ++++++++++++++++++++++++++++++++++++++++ src/transport_local.c | 41 +++++++++++++++++++++ 6 files changed, 297 insertions(+) create mode 100644 include/git2/net.h create mode 100644 include/git2/transport.h create mode 100644 src/transport.c create mode 100644 src/transport.h create mode 100644 src/transport_local.c diff --git a/include/git2/net.h b/include/git2/net.h new file mode 100644 index 000000000..869309f9d --- /dev/null +++ b/include/git2/net.h @@ -0,0 +1,33 @@ +#ifndef INCLUDE_net_h__ +#define INCLUDE_net_h__ + +#include "common.h" +#include "oid.h" +#include "types.h" + +/* + * We need this because we need to know whether we should call + * git-upload-pack or git-receive-pack on the remote end when get_refs + * gets called. + */ + +enum git_net_direction { + INTENT_PUSH, + INTENT_PULL +}; + +/* + * This is what we give out on ->ls() + */ + +struct git_remote_head { + git_oid oid; + char *name; +}; + +struct git_headarray { + unsigned int len; + struct git_remote_head *heads; +}; + +#endif diff --git a/include/git2/transport.h b/include/git2/transport.h new file mode 100644 index 000000000..dfbc1a84c --- /dev/null +++ b/include/git2/transport.h @@ -0,0 +1,56 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#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_get(git_transport *transport, 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_add(git_transport *transport, const char *prefix); + +/** @} */ +GIT_END_DECL +#endif diff --git a/include/git2/types.h b/include/git2/types.h index 963156f85..69aa28955 100644 --- a/include/git2/types.h +++ b/include/git2/types.h @@ -167,9 +167,20 @@ typedef enum { GIT_REF_LISTALL = GIT_REF_OID|GIT_REF_SYMBOLIC|GIT_REF_PACKED, } git_rtype; + typedef struct git_refspec git_refspec; typedef struct git_remote git_remote; +/** A transport to use */ +typedef struct git_transport git_transport; + +/** Whether to push or pull */ +typedef enum git_net_direction git_net_direction; + +typedef int (*git_transport_cb)(git_transport *transport); + +typedef struct git_headarray git_headarray; + /** @} */ GIT_END_DECL diff --git a/src/transport.c b/src/transport.c new file mode 100644 index 000000000..c08345968 --- /dev/null +++ b/src/transport.c @@ -0,0 +1,77 @@ +#include "common.h" +#include "git2/types.h" +#include "git2/transport.h" +#include "git2/net.h" +#include "transport.h" + +struct { + char *prefix; + git_transport_cb fn; +} transports[] = { + {"git://", git_transport_dummy}, + {"http://", git_transport_dummy}, + {"https://", git_transport_dummy}, + {"file://", git_transport_local}, + {"git+ssh://", git_transport_dummy}, + {"ssh+git://", git_transport_dummy}, + {NULL, 0} +}; + +static git_transport_cb transport_fill_fn(const char *url) +{ + int i = 0; + + while (1) { + if (transports[i].prefix == NULL) + break; + + if (!strncasecmp(url, transports[i].prefix, strlen(transports[i].prefix))) + return transports[i].fn; + + ++i; + } + + /* + * If we still haven't found the transport, we assume we mean a + * local file. + * TODO: Parse "example.com:project.git" as an SSH URL + */ + return git_transport_local; +} + +/************** + * Public API * + **************/ + +int git_transport_dummy(git_transport *GIT_UNUSED(transport)) +{ + GIT_UNUSED_ARG(transport); + return git__throw(GIT_ENOTIMPLEMENTED, "This protocol isn't implemented. Sorry"); +} + +int git_transport_new(git_transport **out, git_repository *repo, const char *url) +{ + git_transport_cb fn; + git_transport *transport; + int error; + + fn = transport_fill_fn(url); + + transport = git__malloc(sizeof(git_transport)); + if (transport == NULL) + return GIT_ENOMEM; + + transport->url = git__strdup(url); + if (transport->url == NULL) + return GIT_ENOMEM; + + transport->repo = repo; + + error = fn(transport); + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to create new transport"); + + *out = transport; + + return GIT_SUCCESS; +} diff --git a/src/transport.h b/src/transport.h new file mode 100644 index 000000000..8585b00f7 --- /dev/null +++ b/src/transport.h @@ -0,0 +1,79 @@ +#ifndef INCLUDE_transport_h__ +#define INCLUDE_transport_h__ + +#include "git2/transport.h" +#include "git2/net.h" +#include "vector.h" + +/* + * A day in the life of a network operation + * ======================================== + * + * The library gets told to ls-remote/push/fetch on/to/from some + * remote. We look at the URL of the remote and fill the function + * table with whatever is appropriate (the remote may be git over git, + * ssh or http(s). It may even be an hg or svn repository, the library + * at this level doesn't care, it just calls the helpers. + * + * The first call is to ->connect() which connects to the remote, + * making use of the direction if necessary. This function must also + * store the remote heads and any other information it needs. + * + * If we just want to execute ls-remote, ->ls() gets + * called. Otherwise, the have/want/need list needs to be built via + * ->wanthaveneed(). We can then ->push() or ->pull(). When we're + * done, we call ->close() to close the connection. ->free() takes + * care of freeing all the resources. + */ + +struct git_transport { + /** + * Where the repo lives + */ + char *url; + /** + * Where each transport stores its private/instance data + */ + void *private; + /** + * The repo we want to act on + */ + git_repository *repo; + /** + * Whether we want to push or fetch + */ + git_net_direction direction; + /** + * Connect and store the remote heads + */ + int (*connect)(struct git_transport *transport, git_net_direction intent); + /** + * Give a list of references, useful for ls-remote + */ + int (*ls)(struct git_transport *transport, git_headarray *headarray); + /** + * Calculate want/have/need. May not even be needed. + */ + int (*wanthaveneed)(struct git_transport *transport, void *something); + /** + * Build the pack + */ + int (*build_pack)(struct git_transport *transport); + /** + * Push the changes over + */ + int (*push)(struct git_transport *transport); + /** + * Fetch the changes + */ + int (*fetch)(struct git_transport *transport); + /** + * Close the connection + */ + int (*close)(struct git_transport *transport); +}; + +int git_transport_local(struct git_transport *transport); +int git_transport_dummy(struct git_transport *transport); + +#endif diff --git a/src/transport_local.c b/src/transport_local.c new file mode 100644 index 000000000..f492a875f --- /dev/null +++ b/src/transport_local.c @@ -0,0 +1,41 @@ +#include "common.h" +#include "git2/types.h" +#include "git2/transport.h" +#include "git2/net.h" +#include "git2/repository.h" +#include "transport.h" + +/* + * 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, git_net_direction GIT_UNUSED(dir)) +{ + git_repository *repo; + int error; + + error = git_repository_open(&repo, transport->url); + if (error < GIT_SUCCESS) + return git__rethrow(error, "Can't open remote"); + + transport->private = repo; + + return GIT_SUCCESS; +} + +static int local_ls(git_transport *transport, git_headarray *array) +{ + return GIT_SUCCESS; +} + +/************** + * Public API * + **************/ + +int git_transport_local(git_transport *transport) +{ + transport->connect = local_connect; + transport->ls = local_ls; + + return GIT_SUCCESS; +}