mirror of
https://github.com/git/git
synced 2024-10-29 17:08:46 +00:00
Push code for transport library
This moves the code to call push backends into a library that can be extended to make matching fetch and push decisions based on the URL it gets, and which could be changed to have built-in implementations instead of calling external programs. Signed-off-by: Daniel Barkalow <barkalow@iabervon.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
2d4177c01c
commit
9b288516ee
4 changed files with 280 additions and 62 deletions
3
Makefile
3
Makefile
|
@ -310,7 +310,8 @@ LIB_OBJS = \
|
||||||
write_or_die.o trace.o list-objects.o grep.o match-trees.o \
|
write_or_die.o trace.o list-objects.o grep.o match-trees.o \
|
||||||
alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \
|
alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \
|
||||||
color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \
|
color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \
|
||||||
convert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o
|
convert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o \
|
||||||
|
transport.o
|
||||||
|
|
||||||
BUILTIN_OBJS = \
|
BUILTIN_OBJS = \
|
||||||
builtin-add.o \
|
builtin-add.o \
|
||||||
|
|
|
@ -6,10 +6,11 @@
|
||||||
#include "run-command.h"
|
#include "run-command.h"
|
||||||
#include "builtin.h"
|
#include "builtin.h"
|
||||||
#include "remote.h"
|
#include "remote.h"
|
||||||
|
#include "transport.h"
|
||||||
|
|
||||||
static const char push_usage[] = "git-push [--all] [--tags] [--receive-pack=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]";
|
static const char push_usage[] = "git-push [--all] [--tags] [--receive-pack=<git-receive-pack>] [--repo=all] [-f | --force] [-v] [<repository> <refspec>...]";
|
||||||
|
|
||||||
static int all, force, thin, verbose;
|
static int all, thin, verbose;
|
||||||
static const char *receivepack;
|
static const char *receivepack;
|
||||||
|
|
||||||
static const char **refspec;
|
static const char **refspec;
|
||||||
|
@ -43,80 +44,38 @@ static void set_refspecs(const char **refs, int nr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int do_push(const char *repo)
|
static int do_push(const char *repo, int flags)
|
||||||
{
|
{
|
||||||
int i, errs;
|
int i, errs;
|
||||||
int common_argc;
|
|
||||||
const char **argv;
|
|
||||||
int argc;
|
|
||||||
struct remote *remote = remote_get(repo);
|
struct remote *remote = remote_get(repo);
|
||||||
|
|
||||||
if (!remote)
|
if (!remote)
|
||||||
die("bad repository '%s'", repo);
|
die("bad repository '%s'", repo);
|
||||||
|
|
||||||
if (remote->receivepack) {
|
|
||||||
char *rp = xmalloc(strlen(remote->receivepack) + 16);
|
|
||||||
sprintf(rp, "--receive-pack=%s", remote->receivepack);
|
|
||||||
receivepack = rp;
|
|
||||||
}
|
|
||||||
if (!refspec && !all && remote->push_refspec_nr) {
|
if (!refspec && !all && remote->push_refspec_nr) {
|
||||||
refspec = remote->push_refspec;
|
refspec = remote->push_refspec;
|
||||||
refspec_nr = remote->push_refspec_nr;
|
refspec_nr = remote->push_refspec_nr;
|
||||||
}
|
}
|
||||||
|
|
||||||
argv = xmalloc((refspec_nr + 10) * sizeof(char *));
|
|
||||||
argv[0] = "dummy-send-pack";
|
|
||||||
argc = 1;
|
|
||||||
if (all)
|
|
||||||
argv[argc++] = "--all";
|
|
||||||
if (force)
|
|
||||||
argv[argc++] = "--force";
|
|
||||||
if (receivepack)
|
|
||||||
argv[argc++] = receivepack;
|
|
||||||
common_argc = argc;
|
|
||||||
|
|
||||||
errs = 0;
|
errs = 0;
|
||||||
for (i = 0; i < remote->uri_nr; i++) {
|
for (i = 0; i < remote->uri_nr; i++) {
|
||||||
|
struct transport *transport =
|
||||||
|
transport_get(remote, remote->uri[i], 0);
|
||||||
int err;
|
int err;
|
||||||
int dest_argc = common_argc;
|
if (receivepack)
|
||||||
int dest_refspec_nr = refspec_nr;
|
transport_set_option(transport,
|
||||||
const char **dest_refspec = refspec;
|
TRANS_OPT_RECEIVEPACK, receivepack);
|
||||||
const char *dest = remote->uri[i];
|
|
||||||
const char *sender = "send-pack";
|
|
||||||
if (!prefixcmp(dest, "http://") ||
|
|
||||||
!prefixcmp(dest, "https://"))
|
|
||||||
sender = "http-push";
|
|
||||||
else {
|
|
||||||
char *rem = xmalloc(strlen(remote->name) + 10);
|
|
||||||
sprintf(rem, "--remote=%s", remote->name);
|
|
||||||
argv[dest_argc++] = rem;
|
|
||||||
if (thin)
|
if (thin)
|
||||||
argv[dest_argc++] = "--thin";
|
transport_set_option(transport, TRANS_OPT_THIN, "yes");
|
||||||
}
|
|
||||||
argv[0] = sender;
|
|
||||||
argv[dest_argc++] = dest;
|
|
||||||
while (dest_refspec_nr--)
|
|
||||||
argv[dest_argc++] = *dest_refspec++;
|
|
||||||
argv[dest_argc] = NULL;
|
|
||||||
if (verbose)
|
if (verbose)
|
||||||
fprintf(stderr, "Pushing to %s\n", dest);
|
fprintf(stderr, "Pushing to %s\n", remote->uri[i]);
|
||||||
err = run_command_v_opt(argv, RUN_GIT_CMD);
|
err = transport_push(transport, refspec_nr, refspec, flags);
|
||||||
|
err |= transport_disconnect(transport);
|
||||||
|
|
||||||
if (!err)
|
if (!err)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
error("failed to push to '%s'", remote->uri[i]);
|
error("failed to push to '%s'", remote->uri[i]);
|
||||||
switch (err) {
|
|
||||||
case -ERR_RUN_COMMAND_FORK:
|
|
||||||
error("unable to fork for %s", sender);
|
|
||||||
case -ERR_RUN_COMMAND_EXEC:
|
|
||||||
error("unable to exec %s", sender);
|
|
||||||
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", sender);
|
|
||||||
}
|
|
||||||
errs++;
|
errs++;
|
||||||
}
|
}
|
||||||
return !!errs;
|
return !!errs;
|
||||||
|
@ -125,6 +84,7 @@ static int do_push(const char *repo)
|
||||||
int cmd_push(int argc, const char **argv, const char *prefix)
|
int cmd_push(int argc, const char **argv, const char *prefix)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
int flags = 0;
|
||||||
const char *repo = NULL; /* default repository */
|
const char *repo = NULL; /* default repository */
|
||||||
|
|
||||||
for (i = 1; i < argc; i++) {
|
for (i = 1; i < argc; i++) {
|
||||||
|
@ -144,7 +104,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!strcmp(arg, "--all")) {
|
if (!strcmp(arg, "--all")) {
|
||||||
all = 1;
|
flags |= TRANSPORT_PUSH_ALL;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!strcmp(arg, "--tags")) {
|
if (!strcmp(arg, "--tags")) {
|
||||||
|
@ -152,7 +112,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!strcmp(arg, "--force") || !strcmp(arg, "-f")) {
|
if (!strcmp(arg, "--force") || !strcmp(arg, "-f")) {
|
||||||
force = 1;
|
flags |= TRANSPORT_PUSH_FORCE;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!strcmp(arg, "--thin")) {
|
if (!strcmp(arg, "--thin")) {
|
||||||
|
@ -164,11 +124,11 @@ int cmd_push(int argc, const char **argv, const char *prefix)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!prefixcmp(arg, "--receive-pack=")) {
|
if (!prefixcmp(arg, "--receive-pack=")) {
|
||||||
receivepack = arg;
|
receivepack = arg + 15;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!prefixcmp(arg, "--exec=")) {
|
if (!prefixcmp(arg, "--exec=")) {
|
||||||
receivepack = arg;
|
receivepack = arg + 7;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
usage(push_usage);
|
usage(push_usage);
|
||||||
|
@ -177,5 +137,5 @@ int cmd_push(int argc, const char **argv, const char *prefix)
|
||||||
if (all && refspec)
|
if (all && refspec)
|
||||||
usage(push_usage);
|
usage(push_usage);
|
||||||
|
|
||||||
return do_push(repo);
|
return do_push(repo, flags);
|
||||||
}
|
}
|
||||||
|
|
196
transport.c
Normal file
196
transport.c
Normal file
|
@ -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;
|
||||||
|
}
|
61
transport.h
Normal file
61
transport.h
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
#ifndef TRANSPORT_H
|
||||||
|
#define TRANSPORT_H
|
||||||
|
|
||||||
|
#include "cache.h"
|
||||||
|
#include "remote.h"
|
||||||
|
|
||||||
|
struct transport {
|
||||||
|
unsigned verbose : 1;
|
||||||
|
unsigned fetch : 1;
|
||||||
|
struct remote *remote;
|
||||||
|
const char *url;
|
||||||
|
|
||||||
|
void *data;
|
||||||
|
|
||||||
|
struct ref *remote_refs;
|
||||||
|
|
||||||
|
const struct transport_ops *ops;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define TRANSPORT_PUSH_ALL 1
|
||||||
|
#define TRANSPORT_PUSH_FORCE 2
|
||||||
|
|
||||||
|
struct transport_ops {
|
||||||
|
/**
|
||||||
|
* Returns 0 if successful, positive if the option is not
|
||||||
|
* recognized or is inapplicable, and negative if the option
|
||||||
|
* is applicable but the value is invalid.
|
||||||
|
**/
|
||||||
|
int (*set_option)(struct transport *connection, const char *name,
|
||||||
|
const char *value);
|
||||||
|
|
||||||
|
int (*push)(struct transport *connection, int refspec_nr, const char **refspec, int flags);
|
||||||
|
|
||||||
|
int (*disconnect)(struct transport *connection);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Returns a transport suitable for the url */
|
||||||
|
struct transport *transport_get(struct remote *remote, const char *url,
|
||||||
|
int fetch);
|
||||||
|
|
||||||
|
/* Transport options which apply to git:// and scp-style URLs */
|
||||||
|
|
||||||
|
/* The program to use on the remote side to receive a pack */
|
||||||
|
#define TRANS_OPT_RECEIVEPACK "receivepack"
|
||||||
|
|
||||||
|
/* Transfer the data as a thin pack if not null */
|
||||||
|
#define TRANS_OPT_THIN "thin"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns 0 if the option was used, non-zero otherwise. Prints a
|
||||||
|
* message to stderr if the option is not used.
|
||||||
|
**/
|
||||||
|
int transport_set_option(struct transport *transport, const char *name,
|
||||||
|
const char *value);
|
||||||
|
|
||||||
|
int transport_push(struct transport *connection,
|
||||||
|
int refspec_nr, const char **refspec, int flags);
|
||||||
|
|
||||||
|
int transport_disconnect(struct transport *transport);
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in a new issue