summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@edwardthomson.com>2023-04-11 21:50:32 +0100
committerEdward Thomson <ethomson@edwardthomson.com>2023-04-11 21:50:32 +0100
commitb3e23030982f3613ca426cc73089ed8f2b9e8d62 (patch)
treeb9074e01ed62192c6b003ddccb568197b024addc
parentf15c8ac71a916bf186cd5ff81f07ca85eef82afb (diff)
downloadlibgit2-ethomson/smartrefactor.tar.gz
-rw-r--r--include/git2/sys/transport.h13
-rw-r--r--src/libgit2/transport.c2
-rw-r--r--src/libgit2/transports/git.c453
-rw-r--r--src/libgit2/transports/smartnew.c22
-rw-r--r--src/libgit2/transports/smartnew.h19
5 files changed, 238 insertions, 271 deletions
diff --git a/include/git2/sys/transport.h b/include/git2/sys/transport.h
index b70582188..d7d6c3c3d 100644
--- a/include/git2/sys/transport.h
+++ b/include/git2/sys/transport.h
@@ -233,6 +233,19 @@ GIT_EXTERN(int) git_transport_local(
/* NULL */ void *payload);
/**
+ * Create an instance of the git transport.
+ *
+ * @param out The newly created transport (out)
+ * @param owner The git_remote which will own this transport
+ * @param payload You must pass NULL for this parameter.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_transport_git(
+ git_transport **out,
+ git_remote *owner,
+ /* NULL */ void *payload);
+
+/**
* Create an instance of the smart transport.
*
* @param out The newly created transport (out)
diff --git a/src/libgit2/transport.c b/src/libgit2/transport.c
index 640ccacae..1d9781b61 100644
--- a/src/libgit2/transport.c
+++ b/src/libgit2/transport.c
@@ -29,7 +29,7 @@ static git_smart_subtransport_definition ssh_subtransport_definition = { git_sma
static transport_definition local_transport_definition = { "file://", git_transport_local, NULL };
static transport_definition transports[] = {
- { "git://", git_transport_smart, &git_subtransport_definition },
+ { "git://", git_transport_git, NULL },
{ "http://", git_transport_smart, &http_subtransport_definition },
{ "https://", git_transport_smart, &http_subtransport_definition },
{ "file://", git_transport_local, NULL },
diff --git a/src/libgit2/transports/git.c b/src/libgit2/transports/git.c
index 591e2ab03..096de4d35 100644
--- a/src/libgit2/transports/git.c
+++ b/src/libgit2/transports/git.c
@@ -7,355 +7,268 @@
#include "common.h"
+#include "smartnew.h"
#include "netops.h"
#include "stream.h"
#include "streams/socket.h"
#include "git2/sys/transport.h"
-#define OWNING_SUBTRANSPORT(s) ((git_subtransport *)(s)->parent.subtransport)
-
-static const char prefix_git[] = "git://";
-static const char cmd_uploadpack[] = "git-upload-pack";
-static const char cmd_receivepack[] = "git-receive-pack";
-
typedef struct {
- git_smart_subtransport_stream parent;
- git_stream *io;
- const char *cmd;
- char *url;
- unsigned sent_command : 1;
-} git_proto_stream;
-
-typedef struct {
- git_smart_subtransport parent;
- git_transport *owner;
- git_proto_stream *current_stream;
-} git_subtransport;
-
-/*
- * Create a git protocol request.
- *
- * For example: 0035git-upload-pack /libgit2/libgit2\0host=github.com\0
- */
-static int gen_proto(git_str *request, const char *cmd, const char *url)
-{
- char *delim, *repo;
- char host[] = "host=";
- size_t len;
+ git_transport parent;
+ git_smart smart;
- delim = strchr(url, '/');
- if (delim == NULL) {
- git_error_set(GIT_ERROR_NET, "malformed URL");
- return -1;
- }
-
- repo = delim;
- if (repo[1] == '~')
- ++repo;
-
- delim = strchr(url, ':');
- if (delim == NULL)
- delim = strchr(url, '/');
-
- len = 4 + strlen(cmd) + 1 + strlen(repo) + 1 + strlen(host) + (delim - url) + 1;
-
- git_str_grow(request, len);
- git_str_printf(request, "%04x%s %s%c%s",
- (unsigned int)(len & 0x0FFFF), cmd, repo, 0, host);
- git_str_put(request, url, delim - url);
- git_str_putc(request, '\0');
-
- if (git_str_oom(request))
- return -1;
+ git_remote *owner;
+} transport_git;
- return 0;
-}
-
-static int send_command(git_proto_stream *s)
+static int transport_git_connect(
+ git_transport *transport,
+ const char *url,
+ int direction,
+ const git_remote_connect_options *connect_opts)
{
- git_str request = GIT_STR_INIT;
+ transport_git *t = (transport_git *)transport;
+ git_net_url urldata = GIT_NET_URL_INIT;
int error;
- if ((error = gen_proto(&request, s->cmd, s->url)) < 0)
- goto cleanup;
-
- if ((error = git_stream__write_full(s->io, request.ptr, request.size, 0)) < 0)
- goto cleanup;
-
- s->sent_command = 1;
+ if ((error = git_remote_connect_options_normalize(&t->connect_opts,
+ t->owner->repo, connect_opts)) < 0 ||
+ (error = git_net_url_parse(&urldata, url) < 0) ||
+ (error = git_socket_stream_new(&t->stream, host, port)) < 0 ||
+ (error = git_stream_connect(t->stream)) < 0)
+ goto done;
-cleanup:
- git_str_dispose(&request);
+done:
+ git_net_url_dispose(&urldata);
return error;
}
-static int git_proto_stream_read(
- git_smart_subtransport_stream *stream,
- char *buffer,
- size_t buf_size,
- size_t *bytes_read)
-{
- int error;
- git_proto_stream *s = (git_proto_stream *)stream;
- gitno_buffer buf;
-
- *bytes_read = 0;
-
- if (!s->sent_command && (error = send_command(s)) < 0)
- return error;
-
- gitno_buffer_setup_fromstream(s->io, &buf, buffer, buf_size);
-
- if ((error = gitno_recv(&buf)) < 0)
- return error;
-
- *bytes_read = buf.offset;
-
- return 0;
-}
-
-static int git_proto_stream_write(
- git_smart_subtransport_stream *stream,
- const char *buffer,
- size_t len)
-{
- git_proto_stream *s = (git_proto_stream *)stream;
- int error;
-
- if (!s->sent_command && (error = send_command(s)) < 0)
- return error;
-
- return git_stream__write_full(s->io, buffer, len, 0);
-}
-
-static void git_proto_stream_free(git_smart_subtransport_stream *stream)
-{
- git_proto_stream *s;
- git_subtransport *t;
-
- if (!stream)
- return;
-
- s = (git_proto_stream *)stream;
- t = OWNING_SUBTRANSPORT(s);
-
- t->current_stream = NULL;
-
- git_stream_close(s->io);
- git_stream_free(s->io);
- git__free(s->url);
- git__free(s);
-}
-
-static int git_proto_stream_alloc(
- git_subtransport *t,
+//
+static int git_smart__connect(
+ git_transport *transport,
const char *url,
- const char *cmd,
- const char *host,
- const char *port,
- git_smart_subtransport_stream **stream)
+ int direction,
+ const git_remote_connect_options *connect_opts)
{
- git_proto_stream *s;
+ transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
+ git_smart_subtransport_stream *stream;
+ int error;
+ git_pkt *pkt;
+ git_pkt_ref *first;
+ git_vector symrefs;
+ git_smart_service_t service;
- if (!stream)
+ if (git_smart__reset_stream(t, true) < 0)
return -1;
- s = git__calloc(1, sizeof(git_proto_stream));
- GIT_ERROR_CHECK_ALLOC(s);
-
- s->parent.subtransport = &t->parent;
- s->parent.read = git_proto_stream_read;
- s->parent.write = git_proto_stream_write;
- s->parent.free = git_proto_stream_free;
+ t->url = git__strdup(url);
+ GIT_ERROR_CHECK_ALLOC(t->url);
- s->cmd = cmd;
- s->url = git__strdup(url);
+ t->direction = direction;
- if (!s->url) {
- git__free(s);
+ if (GIT_DIRECTION_FETCH == t->direction) {
+ service = GIT_SERVICE_UPLOADPACK_LS;
+ } else if (GIT_DIRECTION_PUSH == t->direction) {
+ service = GIT_SERVICE_RECEIVEPACK_LS;
+ } else {
+ git_error_set(GIT_ERROR_NET, "invalid direction");
return -1;
}
- if ((git_socket_stream_new(&s->io, host, port)) < 0)
- return -1;
-
- GIT_ERROR_CHECK_VERSION(s->io, GIT_STREAM_VERSION, "git_stream");
-
- *stream = &s->parent;
- return 0;
-}
-
-static int _git_uploadpack_ls(
- git_subtransport *t,
- const char *url,
- git_smart_subtransport_stream **stream)
-{
- git_net_url urldata = GIT_NET_URL_INIT;
- const char *stream_url = url;
- const char *host, *port;
- git_proto_stream *s;
- int error;
+ if ((error = t->wrapped->action(&stream, t->wrapped, t->url, service)) < 0)
+ return error;
- *stream = NULL;
+ /* Save off the current stream (i.e. socket) that we are working with */
+ t->current_stream = stream;
- if (!git__prefixcmp(url, prefix_git))
- stream_url += strlen(prefix_git);
+ gitno_buffer_setup_callback(&t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t);
- if ((error = git_net_url_parse(&urldata, url)) < 0)
+ /* 2 flushes for RPC; 1 for stateful */
+ if ((error = git_smart__store_refs(t, t->rpc ? 2 : 1)) < 0)
return error;
- host = urldata.host;
- port = urldata.port ? urldata.port : GIT_DEFAULT_PORT;
-
- error = git_proto_stream_alloc(t, stream_url, cmd_uploadpack, host, port, stream);
+ /* Strip the comment packet for RPC */
+ if (t->rpc) {
+ pkt = (git_pkt *)git_vector_get(&t->refs, 0);
+
+ if (!pkt || GIT_PKT_COMMENT != pkt->type) {
+ git_error_set(GIT_ERROR_NET, "invalid response");
+ return -1;
+ } else {
+ /* Remove the comment pkt from the list */
+ git_vector_remove(&t->refs, 0);
+ git__free(pkt);
+ }
+ }
- git_net_url_dispose(&urldata);
+ /* We now have loaded the refs. */
+ t->have_refs = 1;
- if (error < 0) {
- git_proto_stream_free(*stream);
- return error;
+ pkt = (git_pkt *)git_vector_get(&t->refs, 0);
+ if (pkt && GIT_PKT_REF != pkt->type) {
+ git_error_set(GIT_ERROR_NET, "invalid response");
+ return -1;
}
+ first = (git_pkt_ref *)pkt;
- s = (git_proto_stream *) *stream;
- if ((error = git_stream_connect(s->io)) < 0) {
- git_proto_stream_free(*stream);
+ if ((error = git_vector_init(&symrefs, 1, NULL)) < 0)
return error;
+
+ /* Detect capabilities */
+ if ((error = git_smart__detect_caps(first, &t->caps, &symrefs)) == 0) {
+ /* If the only ref in the list is capabilities^{} with OID_ZERO, remove it */
+ if (1 == t->refs.length && !strcmp(first->head.name, "capabilities^{}") &&
+ git_oid_is_zero(&first->head.oid)) {
+ git_vector_clear(&t->refs);
+ git_pkt_free((git_pkt *)first);
+ }
+
+ /* Keep a list of heads for _ls */
+ git_smart__update_heads(t, &symrefs);
+ } else if (error == GIT_ENOTFOUND) {
+ /* There was no ref packet received, or the cap list was empty */
+ error = 0;
+ } else {
+ git_error_set(GIT_ERROR_NET, "invalid response");
+ goto cleanup;
}
- t->current_stream = s;
+ if (t->rpc && (error = git_smart__reset_stream(t, false)) < 0)
+ goto cleanup;
- return 0;
-}
+ /* We're now logically connected. */
+ t->connected = 1;
-static int _git_uploadpack(
- git_subtransport *t,
- const char *url,
- git_smart_subtransport_stream **stream)
-{
- GIT_UNUSED(url);
+cleanup:
+ free_symrefs(&symrefs);
- if (t->current_stream) {
- *stream = &t->current_stream->parent;
- return 0;
- }
+ return error;
+}
- git_error_set(GIT_ERROR_NET, "must call UPLOADPACK_LS before UPLOADPACK");
+static int transport_git_set_connect_opts(
+ git_transport *transport,
+ const git_remote_connect_options *connect_opts)
+{
+ transport_git *t = (transport_git *)transport;
return -1;
}
-static int _git_receivepack_ls(
- git_subtransport *t,
- const char *url,
- git_smart_subtransport_stream **stream)
+static int transport_git_capabilities(
+ unsigned int *capabilities,
+ git_transport *transport)
{
- git_net_url urldata = GIT_NET_URL_INIT;
- const char *stream_url = url;
- git_proto_stream *s;
- int error;
-
- *stream = NULL;
- if (!git__prefixcmp(url, prefix_git))
- stream_url += strlen(prefix_git);
-
- if ((error = git_net_url_parse(&urldata, url)) < 0)
- return error;
-
- error = git_proto_stream_alloc(t, stream_url, cmd_receivepack, urldata.host, urldata.port, stream);
-
- git_net_url_dispose(&urldata);
-
- if (error < 0) {
- git_proto_stream_free(*stream);
- return error;
- }
+ transport_git *t = (transport_git *)transport;
+ return -1;
+}
- s = (git_proto_stream *) *stream;
+#ifdef GIT_EXPERIMENTAL_SHA256
- if ((error = git_stream_connect(s->io)) < 0)
- return error;
+static int transport_git_oid_type(
+ git_oid_t *object_type,
+ git_transport *transport)
+{
+ transport_git *t = (transport_git *)transport;
+ return -1;
+}
- t->current_stream = s;
+#endif
- return 0;
+static int transport_git_ls(
+ const git_remote_head ***out,
+ size_t *size,
+ git_transport *transport)
+{
+ transport_git *t = (transport_git *)transport;
+ return -1;
}
-static int _git_receivepack(
- git_subtransport *t,
- const char *url,
- git_smart_subtransport_stream **stream)
+static int transport_git_push(
+ git_transport *transport,
+ git_push *push)
{
- GIT_UNUSED(url);
-
- if (t->current_stream) {
- *stream = &t->current_stream->parent;
- return 0;
- }
-
- git_error_set(GIT_ERROR_NET, "must call RECEIVEPACK_LS before RECEIVEPACK");
+ transport_git *t = (transport_git *)transport;
return -1;
}
-static int _git_action(
- git_smart_subtransport_stream **stream,
- git_smart_subtransport *subtransport,
- const char *url,
- git_smart_service_t action)
+static int transport_git_negotiate_fetch(
+ git_transport *transport,
+ git_repository *repo,
+ const git_remote_head * const *refs,
+ size_t count)
{
- git_subtransport *t = (git_subtransport *) subtransport;
-
- switch (action) {
- case GIT_SERVICE_UPLOADPACK_LS:
- return _git_uploadpack_ls(t, url, stream);
-
- case GIT_SERVICE_UPLOADPACK:
- return _git_uploadpack(t, url, stream);
-
- case GIT_SERVICE_RECEIVEPACK_LS:
- return _git_receivepack_ls(t, url, stream);
-
- case GIT_SERVICE_RECEIVEPACK:
- return _git_receivepack(t, url, stream);
- }
+ transport_git *t = (transport_git *)transport;
+ return -1;
+}
- *stream = NULL;
+static int transport_git_download_pack(
+ git_transport *transport,
+ git_repository *repo,
+ git_indexer_progress *stats)
+{
+ transport_git *t = (transport_git *)transport;
return -1;
}
-static int _git_close(git_smart_subtransport *subtransport)
+static void transport_git_cancel(git_transport *transport)
{
- git_subtransport *t = (git_subtransport *) subtransport;
+ transport_git *t = (transport_git *)transport;
- GIT_ASSERT(!t->current_stream);
+ return;
+}
- GIT_UNUSED(t);
+static int transport_git_is_connected(git_transport *transport)
+{
+ transport_git *t = (transport_git *)transport;
return 0;
}
-static void _git_free(git_smart_subtransport *subtransport)
+static int transport_git_close(git_transport *transport)
{
- git_subtransport *t = (git_subtransport *) subtransport;
+ transport_git *t = (transport_git *)transport;
+ return git_smart_close(&t->smart);
+}
+
+static void transport_git_free(git_transport *transport)
+{
+ transport_git *t = (transport_git *)transport;
+
+ transport_git_close(transport);
+
+ git_smart_dispose(&t->smart);
git__free(t);
}
-int git_smart_subtransport_git(git_smart_subtransport **out, git_transport *owner, void *param)
+int git_transport_git(git_transport **out, git_remote *owner, void *param)
{
- git_subtransport *t;
+ transport_git *t;
- GIT_UNUSED(param);
+ t = git__calloc(1, sizeof(transport_git));
+ GIT_ERROR_CHECK_ALLOC(t);
- if (!out)
+ if (git_smart_init(&t->smart) < 0) {
+ git__free(t);
return -1;
+ }
- t = git__calloc(1, sizeof(git_subtransport));
- GIT_ERROR_CHECK_ALLOC(t);
+ t->parent.version = GIT_TRANSPORT_VERSION;
+ t->parent.connect = transport_git_connect;
+ t->parent.set_connect_opts = transport_git_set_connect_opts;
+ t->parent.capabilities = transport_git_capabilities;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ t->parent.oid_type = transport_git_oid_type;
+#endif
+ t->parent.negotiate_fetch = transport_git_negotiate_fetch;
+ t->parent.download_pack = transport_git_download_pack;
+ t->parent.push = transport_git_push;
+ t->parent.ls = transport_git_ls;
+ t->parent.is_connected = transport_git_is_connected;
+ t->parent.cancel = transport_git_cancel;
+ t->parent.close = transport_git_close;
+ t->parent.free = transport_git_free;
t->owner = owner;
- t->parent.action = _git_action;
- t->parent.close = _git_close;
- t->parent.free = _git_free;
- *out = (git_smart_subtransport *) t;
+ *out = (git_transport *)t;
return 0;
}
diff --git a/src/libgit2/transports/smartnew.c b/src/libgit2/transports/smartnew.c
new file mode 100644
index 000000000..5d0ce60b7
--- /dev/null
+++ b/src/libgit2/transports/smartnew.c
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * 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 "smartnew.h"
+
+int git_smart_init(git_smart *smart)
+{
+ return 0;
+}
+
+int git_smart_close(git_smart *smart)
+{
+ return 0;
+}
+
+void git_smart_dispose(git_smart *smart)
+{
+}
diff --git a/src/libgit2/transports/smartnew.h b/src/libgit2/transports/smartnew.h
new file mode 100644
index 000000000..83e221f9f
--- /dev/null
+++ b/src/libgit2/transports/smartnew.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * 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_transports_smartnew_h__
+#define INCLUDE_transports_smartnew_h__
+
+#include "common.h"
+
+typedef struct {
+} git_smart;
+
+extern int git_smart_init(git_smart *smart);
+extern int git_smart_close(git_smart *smart);
+extern void git_smart_dispose(git_smart *smart);
+
+#endif