#include "cache.h"
+#include "commit.h"
#include "refs.h"
#include "pkt-line.h"
static const char send_pack_usage[] =
-"git-send-pack [--exec=git-receive-pack] [host:]directory [heads]*";
+"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";
static int send_all = 0;
+static int force_update = 0;
static int is_zero_sha1(const unsigned char *sha1)
{
char *buf = malloc(100);
if (i > 900)
die("git-rev-list environment overflow");
- if (!is_zero_sha1(refs->old_sha1)) {
+ if (!is_zero_sha1(refs->old_sha1) &&
+ has_sha1_file(refs->old_sha1)) {
args[i++] = buf;
snprintf(buf, 50, "^%s", sha1_to_hex(refs->old_sha1));
buf += 50;
static int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1)
{
- if (!has_sha1_file(old_sha1))
+ struct commit *new, *old;
+ struct commit_list *list;
+
+ if (force_update)
+ return 1;
+ old = lookup_commit_reference(old_sha1);
+ if (!old)
return 0;
- /*
- * FIXME! It is not correct to say that the new one is newer
- * just because we don't have the old one!
- *
- * We should really see if we can reach the old_sha1 commit
- * from the new_sha1 one.
- */
- return 1;
+ new = lookup_commit_reference(new_sha1);
+ if (!new)
+ return 0;
+ if (parse_commit(new) < 0)
+ return 0;
+ list = NULL;
+ commit_list_insert(new, &list);
+ while (list) {
+ new = pop_most_recent_commit(&list, 1);
+ if (new == old)
+ return 1;
+ }
+ return 0;
}
static int local_ref_nr_match;
continue;
}
+ if (!has_sha1_file(ref->old_sha1)) {
+ error("remote '%s' object %s does not exist on local",
+ name, sha1_to_hex(ref->old_sha1));
+ continue;
+ }
+
if (!ref_newer(new_sha1, ref->old_sha1)) {
- error("remote '%s' points to object I don't have", name);
+ error("remote '%s' isn't a strict parent of local", name);
continue;
}
/*
* See if we have any refs that the other end didn't have
*/
- if (nr_match) {
+ if (nr_match || send_all) {
local_ref_nr_match = nr_match;
local_ref_match = match;
local_ref_list = ref_list;
send_all = 1;
continue;
}
+ if (!strcmp(arg, "--force")) {
+ force_update = 1;
+ continue;
+ }
usage(send_pack_usage);
}
if (!dest) {
}
if (!dest)
usage(send_pack_usage);
+ if (heads && send_all)
+ usage(send_pack_usage);
pid = git_connect(fd, dest, exec);
if (pid < 0)
return 1;