#include "exec_cmd.h"
static const char send_pack_usage[] =
-"git-send-pack [--all] [--exec=git-receive-pack] <remote> [<head>...]\n"
-" --all and explicit <head> specification are mutually exclusive.";
-static const char *exec = "git-receive-pack";
+"git-send-pack [--all] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n"
+" --all and explicit <ref> specification are mutually exclusive.";
+static const char *receivepack = "git-receive-pack";
static int verbose;
static int send_all;
static int force_update;
static int use_thin_pack;
-static int is_zero_sha1(const unsigned char *sha1)
-{
- int i;
-
- for (i = 0; i < 20; i++) {
- if (*sha1++)
- return 0;
- }
- return 1;
-}
-
-static void exec_pack_objects(void)
-{
- static const char *args[] = {
- "pack-objects",
- "--all-progress",
- "--stdout",
- NULL
- };
- execv_git_cmd(args);
- die("git-pack-objects exec failed (%s)", strerror(errno));
-}
-
-static void exec_rev_list(struct ref *refs)
-{
- static const char *args[4];
- int i = 0;
-
- args[i++] = "rev-list"; /* 0 */
- if (use_thin_pack) /* 1 */
- args[i++] = "--objects-edge";
- else
- args[i++] = "--objects";
-
- args[i++] = "--stdin";
-
- args[i] = NULL;
- execv_git_cmd(args);
- die("git-rev-list exec failed (%s)", strerror(errno));
-}
-
-/*
- * Run "rev-list --stdin | pack-objects" pipe.
- */
-static void rev_list(int fd, struct ref *refs)
-{
- int pipe_fd[2];
- pid_t pack_objects_pid;
-
- if (pipe(pipe_fd) < 0)
- die("rev-list setup: pipe failed");
- pack_objects_pid = fork();
- if (!pack_objects_pid) {
- /* The child becomes pack-objects; reads from pipe
- * and writes to the original fd
- */
- dup2(pipe_fd[0], 0);
- dup2(fd, 1);
- close(pipe_fd[0]);
- close(pipe_fd[1]);
- close(fd);
- exec_pack_objects();
- die("pack-objects setup failed");
- }
- if (pack_objects_pid < 0)
- die("pack-objects fork failed");
-
- /* We become rev-list --stdin; output goes to pipe. */
- dup2(pipe_fd[1], 1);
- close(pipe_fd[0]);
- close(pipe_fd[1]);
- close(fd);
- exec_rev_list(refs);
-}
-
/*
- * Create "rev-list --stdin | pack-objects" pipe and feed
- * the refs into the pipeline.
+ * Make a pack stream and spit it out into file descriptor fd
*/
-static void rev_list_generate(int fd, struct ref *refs)
+static int pack_objects(int fd, struct ref *refs)
{
int pipe_fd[2];
- pid_t rev_list_generate_pid;
+ pid_t pid;
if (pipe(pipe_fd) < 0)
- die("rev-list-generate setup: pipe failed");
- rev_list_generate_pid = fork();
- if (!rev_list_generate_pid) {
- /* The child becomes the "rev-list | pack-objects"
- * pipeline. It takes input from us, and its output
- * goes to fd.
+ return error("send-pack: pipe failed");
+ pid = fork();
+ if (pid < 0)
+ return error("send-pack: unable to fork git-pack-objects");
+ if (!pid) {
+ /*
+ * The child becomes pack-objects --revs; we feed
+ * the revision parameters to it via its stdin and
+ * let its stdout go back to the other end.
*/
+ static const char *args[] = {
+ "pack-objects",
+ "--all-progress",
+ "--revs",
+ "--stdout",
+ NULL,
+ NULL,
+ };
+ if (use_thin_pack)
+ args[4] = "--thin";
dup2(pipe_fd[0], 0);
dup2(fd, 1);
close(pipe_fd[0]);
close(pipe_fd[1]);
close(fd);
- rev_list(fd, refs);
- die("rev-list setup failed");
+ execv_git_cmd(args);
+ die("git-pack-objects exec failed (%s)", strerror(errno));
}
- if (rev_list_generate_pid < 0)
- die("rev-list-generate fork failed");
- /* We feed the rev parameters to them. We do not write into
- * fd nor read from the pipe.
+ /*
+ * We feed the pack-objects we just spawned with revision
+ * parameters by writing to the pipe.
*/
close(pipe_fd[0]);
close(fd);
+
while (refs) {
char buf[42];
memcpy(buf + 1, sha1_to_hex(refs->old_sha1), 40);
buf[0] = '^';
buf[41] = '\n';
- write(pipe_fd[1], buf, 42);
+ if (!write_or_whine(pipe_fd[1], buf, 42,
+ "send-pack: send refs"))
+ break;
}
if (!is_null_sha1(refs->new_sha1)) {
memcpy(buf, sha1_to_hex(refs->new_sha1), 40);
buf[40] = '\n';
- write(pipe_fd[1], buf, 41);
+ if (!write_or_whine(pipe_fd[1], buf, 41,
+ "send-pack: send refs"))
+ break;
}
refs = refs->next;
}
close(pipe_fd[1]);
- // waitpid(rev_list_generate_pid);
- exit(0);
-}
-/*
- * Make a pack stream and spit it out into file descriptor fd
- */
-static void pack_objects(int fd, struct ref *refs)
-{
- pid_t rev_list_pid;
+ for (;;) {
+ int status, code;
+ pid_t waiting = waitpid(pid, &status, 0);
- rev_list_pid = fork();
- if (!rev_list_pid) {
- rev_list_generate(fd, refs);
- die("rev-list setup failed");
+ if (waiting < 0) {
+ if (errno == EINTR)
+ continue;
+ return error("waitpid failed (%s)", strerror(errno));
+ }
+ if ((waiting != pid) || WIFSIGNALED(status) ||
+ !WIFEXITED(status))
+ return error("pack-objects died with strange error");
+ code = WEXITSTATUS(status);
+ if (code)
+ return -code;
+ return 0;
}
- if (rev_list_pid < 0)
- die("rev-list fork failed");
- /*
- * We don't wait for the rev-list pipeline in the parent:
- * we end up waiting for the other end instead
- */
}
static void unmark_and_free(struct commit_list *list, unsigned int mark)
int new_refs;
int ret = 0;
int ask_for_status_report = 0;
+ int allow_deleting_refs = 0;
int expect_status_report = 0;
/* No funny business with the matcher */
/* Does the other end support the reporting? */
if (server_supports("report-status"))
ask_for_status_report = 1;
+ if (server_supports("delete-refs"))
+ allow_deleting_refs = 1;
/* match them up */
if (!remote_tail)
new_refs = 0;
for (ref = remote_refs; ref; ref = ref->next) {
char old_hex[60], *new_hex;
+ int delete_ref;
+
if (!ref->peer_ref)
continue;
- if (!hashcmp(ref->old_sha1, ref->peer_ref->new_sha1)) {
+
+ delete_ref = is_null_sha1(ref->peer_ref->new_sha1);
+ if (delete_ref && !allow_deleting_refs) {
+ error("remote does not support deleting refs");
+ ret = -2;
+ continue;
+ }
+ if (!delete_ref &&
+ !hashcmp(ref->old_sha1, ref->peer_ref->new_sha1)) {
if (verbose)
fprintf(stderr, "'%s': up-to-date\n", ref->name);
continue;
*
* (3) if both new and old are commit-ish, and new is a
* descendant of old, it is OK.
+ *
+ * (4) regardless of all of the above, removing :B is
+ * always allowed.
*/
if (!force_update &&
- !is_zero_sha1(ref->old_sha1) &&
+ !delete_ref &&
+ !is_null_sha1(ref->old_sha1) &&
!ref->force) {
if (!has_sha1_file(ref->old_sha1) ||
!ref_newer(ref->peer_ref->new_sha1,
}
}
hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
- if (is_zero_sha1(ref->new_sha1)) {
- error("cannot happen anymore");
- ret = -3;
- continue;
- }
- new_refs++;
+ if (!delete_ref)
+ new_refs++;
strcpy(old_hex, sha1_to_hex(ref->old_sha1));
new_hex = sha1_to_hex(ref->new_sha1);
else
packet_write(out, "%s %s %s",
old_hex, new_hex, ref->name);
- fprintf(stderr, "updating '%s'", ref->name);
- if (strcmp(ref->name, ref->peer_ref->name))
- fprintf(stderr, " using '%s'", ref->peer_ref->name);
- fprintf(stderr, "\n from %s\n to %s\n", old_hex, new_hex);
+ if (delete_ref)
+ fprintf(stderr, "deleting '%s'\n", ref->name);
+ else {
+ fprintf(stderr, "updating '%s'", ref->name);
+ if (strcmp(ref->name, ref->peer_ref->name))
+ fprintf(stderr, " using '%s'",
+ ref->peer_ref->name);
+ fprintf(stderr, "\n from %s\n to %s\n",
+ old_hex, new_hex);
+ }
}
packet_flush(out);
if (new_refs)
- pack_objects(out, remote_refs);
+ ret = pack_objects(out, remote_refs);
close(out);
if (expect_status_report) {
return ret;
}
+static void verify_remote_names(int nr_heads, char **heads)
+{
+ int i;
+
+ for (i = 0; i < nr_heads; i++) {
+ const char *remote = strchr(heads[i], ':');
+
+ remote = remote ? (remote + 1) : heads[i];
+ switch (check_ref_format(remote)) {
+ case 0: /* ok */
+ case -2: /* ok but a single level -- that is fine for
+ * a match pattern.
+ */
+ continue;
+ }
+ die("remote part of refspec is not a valid name in %s",
+ heads[i]);
+ }
+}
int main(int argc, char **argv)
{
char *arg = *argv;
if (*arg == '-') {
- if (!strncmp(arg, "--exec=", 7)) {
- exec = arg + 7;
+ if (!prefixcmp(arg, "--receive-pack=")) {
+ receivepack = arg + 15;
+ continue;
+ }
+ if (!prefixcmp(arg, "--exec=")) {
+ receivepack = arg + 7;
continue;
}
if (!strcmp(arg, "--all")) {
usage(send_pack_usage);
if (heads && send_all)
usage(send_pack_usage);
- pid = git_connect(fd, dest, exec);
+ verify_remote_names(nr_heads, heads);
+
+ pid = git_connect(fd, dest, receivepack);
if (pid < 0)
return 1;
ret = send_pack(fd[0], fd[1], nr_heads, heads);