git builtin "push"
This adds a builtin "push" command, which is largely just a C'ification of
the "git-push.sh" script.
Now, the reason I did it as a built-in is partly because it's yet another
step on relying less on shell, but it's actually mostly because I've
wanted to be able to push to _multiple_ repositories, and the most obvious
and simplest interface for that would seem be to just have a "remotes"
file that has multiple URL entries.
(For "pull", having multiple entries should either just select the first
one, or you could fall back on the others on failure - your choice).
And quite frankly, it just became too damn messy to do that in shell.
Besides, we actually have a fair amount of infrastructure in C, so it just
wasn't that hard to do.
Of course, this is almost totally untested. It probably doesn't work for
anything but the one trial I threw at it. "Simple" doesn't necessarily
mean "obviously correct".
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2006-04-30 08:22:49 +04:00
|
|
|
/*
|
|
|
|
* "git push"
|
|
|
|
*/
|
|
|
|
#include "cache.h"
|
|
|
|
#include "refs.h"
|
|
|
|
#include "run-command.h"
|
|
|
|
#include "builtin.h"
|
|
|
|
|
|
|
|
#define MAX_URI (16)
|
|
|
|
|
|
|
|
static const char push_usage[] = "git push [--all] [--tags] [--force] <repository> [<refspec>...]";
|
|
|
|
|
|
|
|
static int all = 0, tags = 0, force = 0, thin = 1;
|
|
|
|
static const char *execute = NULL;
|
|
|
|
|
|
|
|
#define BUF_SIZE (2084)
|
|
|
|
static char buffer[BUF_SIZE];
|
|
|
|
|
|
|
|
static const char **refspec = NULL;
|
|
|
|
static int refspec_nr = 0;
|
|
|
|
|
|
|
|
static void add_refspec(const char *ref)
|
|
|
|
{
|
|
|
|
int nr = refspec_nr + 1;
|
|
|
|
refspec = xrealloc(refspec, nr * sizeof(char *));
|
|
|
|
refspec[nr-1] = ref;
|
|
|
|
refspec_nr = nr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int expand_one_ref(const char *ref, const unsigned char *sha1)
|
|
|
|
{
|
|
|
|
/* Ignore the "refs/" at the beginning of the refname */
|
|
|
|
ref += 5;
|
|
|
|
|
|
|
|
if (strncmp(ref, "tags/", 5))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
add_refspec(strdup(ref));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void expand_refspecs(void)
|
|
|
|
{
|
|
|
|
if (all) {
|
|
|
|
if (refspec_nr)
|
|
|
|
die("cannot mix '--all' and a refspec");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* No need to expand "--all" - we'll just use
|
|
|
|
* the "--all" flag to send-pack
|
|
|
|
*/
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!tags)
|
|
|
|
return;
|
|
|
|
for_each_ref(expand_one_ref);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void set_refspecs(const char **refs, int nr)
|
|
|
|
{
|
|
|
|
if (nr) {
|
|
|
|
size_t bytes = nr * sizeof(char *);
|
|
|
|
|
|
|
|
refspec = xrealloc(refspec, bytes);
|
|
|
|
memcpy(refspec, refs, bytes);
|
|
|
|
refspec_nr = nr;
|
|
|
|
}
|
|
|
|
expand_refspecs();
|
|
|
|
}
|
|
|
|
|
|
|
|
static int get_remotes_uri(const char *repo, const char *uri[MAX_URI])
|
|
|
|
{
|
|
|
|
int n = 0;
|
|
|
|
FILE *f = fopen(git_path("remotes/%s", repo), "r");
|
2006-05-01 03:03:27 +04:00
|
|
|
int has_explicit_refspec = refspec_nr;
|
git builtin "push"
This adds a builtin "push" command, which is largely just a C'ification of
the "git-push.sh" script.
Now, the reason I did it as a built-in is partly because it's yet another
step on relying less on shell, but it's actually mostly because I've
wanted to be able to push to _multiple_ repositories, and the most obvious
and simplest interface for that would seem be to just have a "remotes"
file that has multiple URL entries.
(For "pull", having multiple entries should either just select the first
one, or you could fall back on the others on failure - your choice).
And quite frankly, it just became too damn messy to do that in shell.
Besides, we actually have a fair amount of infrastructure in C, so it just
wasn't that hard to do.
Of course, this is almost totally untested. It probably doesn't work for
anything but the one trial I threw at it. "Simple" doesn't necessarily
mean "obviously correct".
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2006-04-30 08:22:49 +04:00
|
|
|
|
|
|
|
if (!f)
|
|
|
|
return -1;
|
|
|
|
while (fgets(buffer, BUF_SIZE, f)) {
|
2006-04-30 16:05:55 +04:00
|
|
|
int is_refspec;
|
git builtin "push"
This adds a builtin "push" command, which is largely just a C'ification of
the "git-push.sh" script.
Now, the reason I did it as a built-in is partly because it's yet another
step on relying less on shell, but it's actually mostly because I've
wanted to be able to push to _multiple_ repositories, and the most obvious
and simplest interface for that would seem be to just have a "remotes"
file that has multiple URL entries.
(For "pull", having multiple entries should either just select the first
one, or you could fall back on the others on failure - your choice).
And quite frankly, it just became too damn messy to do that in shell.
Besides, we actually have a fair amount of infrastructure in C, so it just
wasn't that hard to do.
Of course, this is almost totally untested. It probably doesn't work for
anything but the one trial I threw at it. "Simple" doesn't necessarily
mean "obviously correct".
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2006-04-30 08:22:49 +04:00
|
|
|
char *s, *p;
|
|
|
|
|
2006-04-30 16:05:55 +04:00
|
|
|
if (!strncmp("URL: ", buffer, 5)) {
|
|
|
|
is_refspec = 0;
|
|
|
|
s = buffer + 5;
|
|
|
|
} else if (!strncmp("Push: ", buffer, 6)) {
|
|
|
|
is_refspec = 1;
|
|
|
|
s = buffer + 6;
|
|
|
|
} else
|
git builtin "push"
This adds a builtin "push" command, which is largely just a C'ification of
the "git-push.sh" script.
Now, the reason I did it as a built-in is partly because it's yet another
step on relying less on shell, but it's actually mostly because I've
wanted to be able to push to _multiple_ repositories, and the most obvious
and simplest interface for that would seem be to just have a "remotes"
file that has multiple URL entries.
(For "pull", having multiple entries should either just select the first
one, or you could fall back on the others on failure - your choice).
And quite frankly, it just became too damn messy to do that in shell.
Besides, we actually have a fair amount of infrastructure in C, so it just
wasn't that hard to do.
Of course, this is almost totally untested. It probably doesn't work for
anything but the one trial I threw at it. "Simple" doesn't necessarily
mean "obviously correct".
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2006-04-30 08:22:49 +04:00
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Remove whitespace at the head.. */
|
|
|
|
while (isspace(*s))
|
|
|
|
s++;
|
|
|
|
if (!*s)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* ..and at the end */
|
|
|
|
p = s + strlen(s);
|
|
|
|
while (isspace(p[-1]))
|
|
|
|
*--p = 0;
|
|
|
|
|
2006-05-01 03:03:27 +04:00
|
|
|
if (!is_refspec) {
|
|
|
|
if (n < MAX_URI)
|
|
|
|
uri[n++] = strdup(s);
|
|
|
|
else
|
|
|
|
error("more than %d URL's specified, ignoreing the rest", MAX_URI);
|
|
|
|
}
|
|
|
|
else if (is_refspec && !has_explicit_refspec)
|
|
|
|
add_refspec(strdup(s));
|
git builtin "push"
This adds a builtin "push" command, which is largely just a C'ification of
the "git-push.sh" script.
Now, the reason I did it as a built-in is partly because it's yet another
step on relying less on shell, but it's actually mostly because I've
wanted to be able to push to _multiple_ repositories, and the most obvious
and simplest interface for that would seem be to just have a "remotes"
file that has multiple URL entries.
(For "pull", having multiple entries should either just select the first
one, or you could fall back on the others on failure - your choice).
And quite frankly, it just became too damn messy to do that in shell.
Besides, we actually have a fair amount of infrastructure in C, so it just
wasn't that hard to do.
Of course, this is almost totally untested. It probably doesn't work for
anything but the one trial I threw at it. "Simple" doesn't necessarily
mean "obviously correct".
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2006-04-30 08:22:49 +04:00
|
|
|
}
|
|
|
|
fclose(f);
|
|
|
|
if (!n)
|
|
|
|
die("remote '%s' has no URL", repo);
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
2006-04-30 17:23:48 +04:00
|
|
|
static const char **config_uri;
|
|
|
|
static const char *config_repo;
|
|
|
|
static int config_repo_len;
|
|
|
|
static int config_current_uri;
|
|
|
|
static int config_get_refspecs;
|
|
|
|
|
|
|
|
static int get_remote_config(const char* key, const char* value)
|
|
|
|
{
|
|
|
|
if (!strncmp(key, "remote.", 7) &&
|
|
|
|
!strncmp(key + 7, config_repo, config_repo_len)) {
|
|
|
|
if (!strcmp(key + 7 + config_repo_len, ".url")) {
|
|
|
|
if (config_current_uri < MAX_URI)
|
|
|
|
config_uri[config_current_uri++] = strdup(value);
|
|
|
|
else
|
|
|
|
error("more than %d URL's specified, ignoring the rest", MAX_URI);
|
|
|
|
}
|
|
|
|
else if (config_get_refspecs &&
|
|
|
|
!strcmp(key + 7 + config_repo_len, ".push"))
|
|
|
|
add_refspec(strdup(value));
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int get_config_remotes_uri(const char *repo, const char *uri[MAX_URI])
|
|
|
|
{
|
|
|
|
config_repo_len = strlen(repo);
|
|
|
|
config_repo = repo;
|
|
|
|
config_current_uri = 0;
|
|
|
|
config_uri = uri;
|
|
|
|
config_get_refspecs = !refspec_nr;
|
|
|
|
|
|
|
|
git_config(get_remote_config);
|
|
|
|
return config_current_uri;
|
|
|
|
}
|
|
|
|
|
git builtin "push"
This adds a builtin "push" command, which is largely just a C'ification of
the "git-push.sh" script.
Now, the reason I did it as a built-in is partly because it's yet another
step on relying less on shell, but it's actually mostly because I've
wanted to be able to push to _multiple_ repositories, and the most obvious
and simplest interface for that would seem be to just have a "remotes"
file that has multiple URL entries.
(For "pull", having multiple entries should either just select the first
one, or you could fall back on the others on failure - your choice).
And quite frankly, it just became too damn messy to do that in shell.
Besides, we actually have a fair amount of infrastructure in C, so it just
wasn't that hard to do.
Of course, this is almost totally untested. It probably doesn't work for
anything but the one trial I threw at it. "Simple" doesn't necessarily
mean "obviously correct".
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2006-04-30 08:22:49 +04:00
|
|
|
static int get_branches_uri(const char *repo, const char *uri[MAX_URI])
|
|
|
|
{
|
|
|
|
const char *slash = strchr(repo, '/');
|
|
|
|
int n = slash ? slash - repo : 1000;
|
|
|
|
FILE *f = fopen(git_path("branches/%.*s", n, repo), "r");
|
|
|
|
char *s, *p;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
if (!f)
|
|
|
|
return 0;
|
|
|
|
s = fgets(buffer, BUF_SIZE, f);
|
|
|
|
fclose(f);
|
|
|
|
if (!s)
|
|
|
|
return 0;
|
|
|
|
while (isspace(*s))
|
|
|
|
s++;
|
|
|
|
if (!*s)
|
|
|
|
return 0;
|
|
|
|
p = s + strlen(s);
|
|
|
|
while (isspace(p[-1]))
|
|
|
|
*--p = 0;
|
|
|
|
len = p - s;
|
|
|
|
if (slash)
|
|
|
|
len += strlen(slash);
|
|
|
|
p = xmalloc(len + 1);
|
|
|
|
strcpy(p, s);
|
|
|
|
if (slash)
|
|
|
|
strcat(p, slash);
|
|
|
|
uri[0] = p;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2006-05-01 03:03:27 +04:00
|
|
|
/*
|
|
|
|
* Read remotes and branches file, fill the push target URI
|
|
|
|
* list. If there is no command line refspecs, read Push: lines
|
|
|
|
* to set up the *refspec list as well.
|
|
|
|
* return the number of push target URIs
|
|
|
|
*/
|
|
|
|
static int read_config(const char *repo, const char *uri[MAX_URI])
|
git builtin "push"
This adds a builtin "push" command, which is largely just a C'ification of
the "git-push.sh" script.
Now, the reason I did it as a built-in is partly because it's yet another
step on relying less on shell, but it's actually mostly because I've
wanted to be able to push to _multiple_ repositories, and the most obvious
and simplest interface for that would seem be to just have a "remotes"
file that has multiple URL entries.
(For "pull", having multiple entries should either just select the first
one, or you could fall back on the others on failure - your choice).
And quite frankly, it just became too damn messy to do that in shell.
Besides, we actually have a fair amount of infrastructure in C, so it just
wasn't that hard to do.
Of course, this is almost totally untested. It probably doesn't work for
anything but the one trial I threw at it. "Simple" doesn't necessarily
mean "obviously correct".
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2006-04-30 08:22:49 +04:00
|
|
|
{
|
|
|
|
int n;
|
|
|
|
|
|
|
|
if (*repo != '/') {
|
|
|
|
n = get_remotes_uri(repo, uri);
|
|
|
|
if (n > 0)
|
|
|
|
return n;
|
|
|
|
|
2006-04-30 17:23:48 +04:00
|
|
|
n = get_config_remotes_uri(repo, uri);
|
|
|
|
if (n > 0)
|
|
|
|
return n;
|
|
|
|
|
git builtin "push"
This adds a builtin "push" command, which is largely just a C'ification of
the "git-push.sh" script.
Now, the reason I did it as a built-in is partly because it's yet another
step on relying less on shell, but it's actually mostly because I've
wanted to be able to push to _multiple_ repositories, and the most obvious
and simplest interface for that would seem be to just have a "remotes"
file that has multiple URL entries.
(For "pull", having multiple entries should either just select the first
one, or you could fall back on the others on failure - your choice).
And quite frankly, it just became too damn messy to do that in shell.
Besides, we actually have a fair amount of infrastructure in C, so it just
wasn't that hard to do.
Of course, this is almost totally untested. It probably doesn't work for
anything but the one trial I threw at it. "Simple" doesn't necessarily
mean "obviously correct".
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2006-04-30 08:22:49 +04:00
|
|
|
n = get_branches_uri(repo, uri);
|
|
|
|
if (n > 0)
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
uri[0] = repo;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int do_push(const char *repo)
|
|
|
|
{
|
|
|
|
const char *uri[MAX_URI];
|
2006-05-01 03:03:27 +04:00
|
|
|
int i, n;
|
git builtin "push"
This adds a builtin "push" command, which is largely just a C'ification of
the "git-push.sh" script.
Now, the reason I did it as a built-in is partly because it's yet another
step on relying less on shell, but it's actually mostly because I've
wanted to be able to push to _multiple_ repositories, and the most obvious
and simplest interface for that would seem be to just have a "remotes"
file that has multiple URL entries.
(For "pull", having multiple entries should either just select the first
one, or you could fall back on the others on failure - your choice).
And quite frankly, it just became too damn messy to do that in shell.
Besides, we actually have a fair amount of infrastructure in C, so it just
wasn't that hard to do.
Of course, this is almost totally untested. It probably doesn't work for
anything but the one trial I threw at it. "Simple" doesn't necessarily
mean "obviously correct".
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2006-04-30 08:22:49 +04:00
|
|
|
int remote;
|
|
|
|
const char **argv;
|
|
|
|
int argc;
|
|
|
|
|
2006-05-01 03:03:27 +04:00
|
|
|
n = read_config(repo, uri);
|
git builtin "push"
This adds a builtin "push" command, which is largely just a C'ification of
the "git-push.sh" script.
Now, the reason I did it as a built-in is partly because it's yet another
step on relying less on shell, but it's actually mostly because I've
wanted to be able to push to _multiple_ repositories, and the most obvious
and simplest interface for that would seem be to just have a "remotes"
file that has multiple URL entries.
(For "pull", having multiple entries should either just select the first
one, or you could fall back on the others on failure - your choice).
And quite frankly, it just became too damn messy to do that in shell.
Besides, we actually have a fair amount of infrastructure in C, so it just
wasn't that hard to do.
Of course, this is almost totally untested. It probably doesn't work for
anything but the one trial I threw at it. "Simple" doesn't necessarily
mean "obviously correct".
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2006-04-30 08:22:49 +04:00
|
|
|
if (n <= 0)
|
|
|
|
die("bad repository '%s'", repo);
|
|
|
|
|
|
|
|
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 (execute)
|
|
|
|
argv[argc++] = execute;
|
|
|
|
if (thin)
|
|
|
|
argv[argc++] = "--thin";
|
|
|
|
remote = argc;
|
|
|
|
argv[argc++] = "dummy-remote";
|
|
|
|
while (refspec_nr--)
|
|
|
|
argv[argc++] = *refspec++;
|
|
|
|
argv[argc] = NULL;
|
|
|
|
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
int error;
|
|
|
|
const char *dest = uri[i];
|
|
|
|
const char *sender = "git-send-pack";
|
|
|
|
if (!strncmp(dest, "http://", 7) ||
|
|
|
|
!strncmp(dest, "https://", 8))
|
|
|
|
sender = "git-http-push";
|
|
|
|
argv[0] = sender;
|
|
|
|
argv[remote] = dest;
|
|
|
|
error = run_command_v(argc, argv);
|
|
|
|
if (!error)
|
|
|
|
continue;
|
|
|
|
switch (error) {
|
|
|
|
case -ERR_RUN_COMMAND_FORK:
|
|
|
|
die("unable to fork for %s", sender);
|
|
|
|
case -ERR_RUN_COMMAND_EXEC:
|
|
|
|
die("unable to exec %s", sender);
|
|
|
|
case -ERR_RUN_COMMAND_WAITPID:
|
|
|
|
case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
|
|
|
|
case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
|
|
|
|
case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
|
|
|
|
die("%s died with strange error", sender);
|
|
|
|
default:
|
|
|
|
return -error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int cmd_push(int argc, const char **argv, char **envp)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
const char *repo = "origin"; // default repository
|
|
|
|
|
|
|
|
for (i = 1; i < argc; i++) {
|
|
|
|
const char *arg = argv[i];
|
|
|
|
|
|
|
|
if (arg[0] != '-') {
|
|
|
|
repo = arg;
|
|
|
|
i++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!strcmp(arg, "--all")) {
|
|
|
|
all = 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(arg, "--tags")) {
|
|
|
|
tags = 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(arg, "--force")) {
|
|
|
|
force = 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(arg, "--thin")) {
|
|
|
|
thin = 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strcmp(arg, "--no-thin")) {
|
|
|
|
thin = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strncmp(arg, "--exec=", 7)) {
|
|
|
|
execute = arg;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
usage(push_usage);
|
|
|
|
}
|
|
|
|
set_refspecs(argv + i, argc - i);
|
|
|
|
return do_push(repo);
|
|
|
|
}
|