Commit 8f866daee5a0a43702f349c7fa46d3274542650c

Carlos Martín Nieto 2011-05-16T22:07:08

Lay down the fundations for the network code Signed-off-by: Carlos Martín Nieto <carlos@cmartin.tk>

diff --git a/include/git2/net.h b/include/git2/net.h
new file mode 100644
index 0000000..869309f
--- /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 0000000..dfbc1a8
--- /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 963156f..69aa289 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 0000000..c083459
--- /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 0000000..8585b00
--- /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 0000000..f492a87
--- /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;
+}