197 lines
5.0 KiB
C
197 lines
5.0 KiB
C
|
#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;
|
||
|
}
|