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:
Daniel Barkalow 2007-09-10 23:03:04 -04:00 committed by Junio C Hamano
parent 2d4177c01c
commit 9b288516ee
4 changed files with 280 additions and 62 deletions

View file

@ -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 \

View file

@ -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
View 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
View 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