summaryrefslogtreecommitdiff
path: root/transport.c
diff options
context:
space:
mode:
Diffstat (limited to 'transport.c')
-rw-r--r--transport.c196
1 files changed, 196 insertions, 0 deletions
diff --git a/transport.c b/transport.c
new file mode 100644
index 0000000..edbdc3c
--- /dev/null
+++ b/transport.c
@@ -0,0 +1,196 @@
+#include "cache.h"
+#include "transport.h"
+#include "run-command.h"
+
+static const struct transport_ops rsync_transport;
+
+static int curl_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) {
+ const char **argv;
+ int argc;
+ int err;
+
+ argv = xmalloc((refspec_nr + 11) * sizeof(char *));
+ argv[0] = "http-push";
+ argc = 1;
+ if (flags & TRANSPORT_PUSH_ALL)
+ argv[argc++] = "--all";
+ if (flags & TRANSPORT_PUSH_FORCE)
+ argv[argc++] = "--force";
+ argv[argc++] = transport->url;
+ while (refspec_nr--)
+ argv[argc++] = *refspec++;
+ argv[argc] = NULL;
+ err = run_command_v_opt(argv, RUN_GIT_CMD);
+ switch (err) {
+ case -ERR_RUN_COMMAND_FORK:
+ error("unable to fork for %s", argv[0]);
+ case -ERR_RUN_COMMAND_EXEC:
+ error("unable to exec %s", argv[0]);
+ break;
+ case -ERR_RUN_COMMAND_WAITPID:
+ case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
+ case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
+ case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
+ error("%s died with strange error", argv[0]);
+ }
+ return !!err;
+}
+
+static const struct transport_ops curl_transport = {
+ /* set_option */ NULL,
+ /* push */ curl_transport_push
+};
+
+static const struct transport_ops bundle_transport = {
+};
+
+struct git_transport_data {
+ unsigned thin : 1;
+
+ const char *receivepack;
+};
+
+static int set_git_option(struct transport *connection,
+ const char *name, const char *value)
+{
+ struct git_transport_data *data = connection->data;
+ if (!strcmp(name, TRANS_OPT_RECEIVEPACK)) {
+ data->receivepack = value;
+ return 0;
+ } else if (!strcmp(name, TRANS_OPT_THIN)) {
+ data->thin = !!value;
+ return 0;
+ }
+ return 1;
+}
+
+static int git_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) {
+ struct git_transport_data *data = transport->data;
+ const char **argv;
+ char *rem;
+ int argc;
+ int err;
+
+ argv = xmalloc((refspec_nr + 11) * sizeof(char *));
+ argv[0] = "send-pack";
+ argc = 1;
+ if (flags & TRANSPORT_PUSH_ALL)
+ argv[argc++] = "--all";
+ if (flags & TRANSPORT_PUSH_FORCE)
+ argv[argc++] = "--force";
+ if (data->receivepack) {
+ char *rp = xmalloc(strlen(data->receivepack) + 16);
+ sprintf(rp, "--receive-pack=%s", data->receivepack);
+ argv[argc++] = rp;
+ }
+ if (data->thin)
+ argv[argc++] = "--thin";
+ rem = xmalloc(strlen(transport->remote->name) + 10);
+ sprintf(rem, "--remote=%s", transport->remote->name);
+ argv[argc++] = rem;
+ argv[argc++] = transport->url;
+ while (refspec_nr--)
+ argv[argc++] = *refspec++;
+ argv[argc] = NULL;
+ err = run_command_v_opt(argv, RUN_GIT_CMD);
+ switch (err) {
+ case -ERR_RUN_COMMAND_FORK:
+ error("unable to fork for %s", argv[0]);
+ case -ERR_RUN_COMMAND_EXEC:
+ error("unable to exec %s", argv[0]);
+ break;
+ case -ERR_RUN_COMMAND_WAITPID:
+ case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
+ case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
+ case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
+ error("%s died with strange error", argv[0]);
+ }
+ return !!err;
+}
+
+static const struct transport_ops git_transport = {
+ /* set_option */ set_git_option,
+ /* push */ git_transport_push
+};
+
+static int is_local(const char *url)
+{
+ const char *colon = strchr(url, ':');
+ const char *slash = strchr(url, '/');
+ return !colon || (slash && slash < colon);
+}
+
+static int is_file(const char *url)
+{
+ struct stat buf;
+ if (stat(url, &buf))
+ return 0;
+ return S_ISREG(buf.st_mode);
+}
+
+struct transport *transport_get(struct remote *remote, const char *url,
+ int fetch)
+{
+ struct transport *ret = NULL;
+ if (!prefixcmp(url, "rsync://")) {
+ ret = xmalloc(sizeof(*ret));
+ ret->data = NULL;
+ ret->ops = &rsync_transport;
+ } else if (!prefixcmp(url, "http://") || !prefixcmp(url, "https://") ||
+ !prefixcmp(url, "ftp://")) {
+ ret = xmalloc(sizeof(*ret));
+ ret->ops = &curl_transport;
+ ret->data = NULL;
+ } else if (is_local(url) && is_file(url)) {
+ ret = xmalloc(sizeof(*ret));
+ ret->data = NULL;
+ ret->ops = &bundle_transport;
+ } else {
+ struct git_transport_data *data = xcalloc(1, sizeof(*data));
+ ret = xcalloc(1, sizeof(*ret));
+ ret->data = data;
+ data->thin = 1;
+ data->receivepack = "git-receive-pack";
+ if (remote && remote->receivepack)
+ data->receivepack = remote->receivepack;
+ ret->ops = &git_transport;
+ }
+ if (ret) {
+ ret->remote = remote;
+ ret->url = url;
+ ret->fetch = !!fetch;
+ }
+ return ret;
+}
+
+int transport_set_option(struct transport *transport,
+ const char *name, const char *value)
+{
+ int ret = 1;
+ if (transport->ops->set_option)
+ ret = transport->ops->set_option(transport, name, value);
+ if (ret < 0)
+ fprintf(stderr, "For '%s' option %s cannot be set to '%s'\n",
+ transport->url, name, value);
+ if (ret > 0)
+ fprintf(stderr, "For '%s' option %s is ignored\n",
+ transport->url, name);
+ return ret;
+}
+
+int transport_push(struct transport *transport,
+ int refspec_nr, const char **refspec, int flags)
+{
+ if (!transport->ops->push)
+ return 1;
+ return transport->ops->push(transport, refspec_nr, refspec, flags);
+}
+
+int transport_disconnect(struct transport *transport)
+{
+ int ret = 0;
+ if (transport->ops->disconnect)
+ ret = transport->ops->disconnect(transport);
+ free(transport);
+ return ret;
+}