2994df401ab1b23674b1347cf6b3e5570dcffa21
   1#include "cache.h"
   2#include "refs.h"
   3#include "pkt-line.h"
   4
   5static const char send_pack_usage[] =
   6"git-send-pack [--exec=git-receive-pack] [host:]directory [heads]*";
   7static const char *exec = "git-receive-pack";
   8static int send_all = 0;
   9
  10static int is_zero_sha1(const unsigned char *sha1)
  11{
  12        int i;
  13
  14        for (i = 0; i < 20; i++) {
  15                if (*sha1++)
  16                        return 0;
  17        }
  18        return 1;
  19}
  20
  21static void exec_pack_objects(void)
  22{
  23        static char *args[] = {
  24                "git-pack-objects",
  25                "--stdout",
  26                NULL
  27        };
  28        execvp("git-pack-objects", args);
  29        die("git-pack-objects exec failed (%s)", strerror(errno));
  30}
  31
  32static void exec_rev_list(struct ref *refs)
  33{
  34        static char *args[1000];
  35        int i = 0;
  36
  37        args[i++] = "git-rev-list";     /* 0 */
  38        args[i++] = "--objects";        /* 1 */
  39        while (refs) {
  40                char *buf = malloc(100);
  41                if (i > 900)
  42                        die("git-rev-list environment overflow");
  43                if (!is_zero_sha1(refs->old_sha1)) {
  44                        args[i++] = buf;
  45                        snprintf(buf, 50, "^%s", sha1_to_hex(refs->old_sha1));
  46                        buf += 50;
  47                }
  48                if (!is_zero_sha1(refs->new_sha1)) {
  49                        args[i++] = buf;
  50                        snprintf(buf, 50, "%s", sha1_to_hex(refs->new_sha1));
  51                }
  52                refs = refs->next;
  53        }
  54        args[i] = NULL;
  55        execvp("git-rev-list", args);
  56        die("git-rev-list exec failed (%s)", strerror(errno));
  57}
  58
  59static void rev_list(int fd, struct ref *refs)
  60{
  61        int pipe_fd[2];
  62        pid_t pack_objects_pid;
  63
  64        if (pipe(pipe_fd) < 0)
  65                die("rev-list setup: pipe failed");
  66        pack_objects_pid = fork();
  67        if (!pack_objects_pid) {
  68                dup2(pipe_fd[0], 0);
  69                dup2(fd, 1);
  70                close(pipe_fd[0]);
  71                close(pipe_fd[1]);
  72                close(fd);
  73                exec_pack_objects();
  74                die("pack-objects setup failed");
  75        }
  76        if (pack_objects_pid < 0)
  77                die("pack-objects fork failed");
  78        dup2(pipe_fd[1], 1);
  79        close(pipe_fd[0]);
  80        close(pipe_fd[1]);
  81        close(fd);
  82        exec_rev_list(refs);
  83}
  84
  85static int pack_objects(int fd, struct ref *refs)
  86{
  87        pid_t rev_list_pid;
  88
  89        rev_list_pid = fork();
  90        if (!rev_list_pid) {
  91                rev_list(fd, refs);
  92                die("rev-list setup failed");
  93        }
  94        if (rev_list_pid < 0)
  95                die("rev-list fork failed");
  96        /*
  97         * We don't wait for the rev-list pipeline in the parent:
  98         * we end up waiting for the other end instead
  99         */
 100        return 0;
 101}
 102
 103static int read_ref(const char *ref, unsigned char *sha1)
 104{
 105        int fd, ret;
 106        char buffer[60];
 107
 108        fd = open(git_path("%s", ref), O_RDONLY);
 109        if (fd < 0)
 110                return -1;
 111        ret = -1;
 112        if (read(fd, buffer, sizeof(buffer)) >= 40)
 113                ret = get_sha1_hex(buffer, sha1);
 114        close(fd);
 115        return ret;
 116}
 117
 118static int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1)
 119{
 120        if (!has_sha1_file(old_sha1))
 121                return 0;
 122        /*
 123         * FIXME! It is not correct to say that the new one is newer
 124         * just because we don't have the old one!
 125         *
 126         * We should really see if we can reach the old_sha1 commit
 127         * from the new_sha1 one.
 128         */
 129        return 1;
 130}
 131
 132static int local_ref_nr_match;
 133static char **local_ref_match;
 134static struct ref *local_ref_list;
 135static struct ref **local_last_ref;
 136
 137static int try_to_match(const char *refname, const unsigned char *sha1)
 138{
 139        struct ref *ref;
 140        int len;
 141
 142        if (!path_match(refname, local_ref_nr_match, local_ref_match)) {
 143                if (!send_all)
 144                        return 0;
 145
 146                /* If we have it listed already, skip it */
 147                for (ref = local_ref_list ; ref ; ref = ref->next) {
 148                        if (!strcmp(ref->name, refname))
 149                                return 0;
 150                }
 151        }
 152
 153        len = strlen(refname)+1;
 154        ref = xmalloc(sizeof(*ref) + len);
 155        memset(ref->old_sha1, 0, 20);
 156        memcpy(ref->new_sha1, sha1, 20);
 157        memcpy(ref->name, refname, len);
 158        ref->next = NULL;
 159        *local_last_ref = ref;
 160        local_last_ref = &ref->next;
 161        return 0;
 162}
 163
 164static int send_pack(int in, int out, int nr_match, char **match)
 165{
 166        struct ref *ref_list, **last_ref;
 167        struct ref *ref;
 168        int new_refs;
 169
 170        /* First we get all heads, whether matching or not.. */
 171        last_ref = get_remote_heads(in, &ref_list, 0, NULL);
 172
 173        /*
 174         * Go through the refs, see if we want to update
 175         * any of them..
 176         */
 177        for (ref = ref_list; ref; ref = ref->next) {
 178                unsigned char new_sha1[20];
 179                char *name = ref->name;
 180
 181                if (nr_match && !path_match(name, nr_match, match))
 182                        continue;
 183
 184                if (read_ref(name, new_sha1) < 0)
 185                        continue;
 186
 187                if (!memcmp(ref->old_sha1, new_sha1, 20)) {
 188                        fprintf(stderr, "'%s' unchanged\n", name);
 189                        continue;
 190                }
 191
 192                if (!ref_newer(new_sha1, ref->old_sha1)) {
 193                        error("remote '%s' points to object I don't have", name);
 194                        continue;
 195                }
 196
 197                /* Ok, mark it for update */
 198                memcpy(ref->new_sha1, new_sha1, 20);
 199        }
 200
 201        /*
 202         * See if we have any refs that the other end didn't have
 203         */
 204        if (nr_match) {
 205                local_ref_nr_match = nr_match;
 206                local_ref_match = match;
 207                local_ref_list = ref_list;
 208                local_last_ref = last_ref;
 209                for_each_ref(try_to_match);
 210        }
 211
 212        /*
 213         * Finally, tell the other end!
 214         */
 215        new_refs = 0;
 216        for (ref = ref_list; ref; ref = ref->next) {
 217                char old_hex[60], *new_hex;
 218                if (is_zero_sha1(ref->new_sha1))
 219                        continue;
 220                new_refs++;
 221                strcpy(old_hex, sha1_to_hex(ref->old_sha1));
 222                new_hex = sha1_to_hex(ref->new_sha1);
 223                packet_write(out, "%s %s %s", old_hex, new_hex, ref->name);
 224                fprintf(stderr, "'%s': updating from %s to %s\n", ref->name, old_hex, new_hex);
 225        }
 226        
 227        packet_flush(out);
 228        if (new_refs)
 229                pack_objects(out, ref_list);
 230        close(out);
 231        return 0;
 232}
 233
 234int main(int argc, char **argv)
 235{
 236        int i, nr_heads = 0;
 237        char *dest = NULL;
 238        char **heads = NULL;
 239        int fd[2], ret;
 240        pid_t pid;
 241
 242        argv++;
 243        for (i = 1; i < argc; i++, argv++) {
 244                char *arg = *argv;
 245
 246                if (*arg == '-') {
 247                        if (!strncmp(arg, "--exec=", 7)) {
 248                                exec = arg + 7;
 249                                continue;
 250                        }
 251                        if (!strcmp(arg, "--all")) {
 252                                send_all = 1;
 253                                continue;
 254                        }
 255                        usage(send_pack_usage);
 256                }
 257                if (!dest) {
 258                        dest = arg;
 259                        continue;
 260                }
 261                heads = argv;
 262                nr_heads = argc - i;
 263                break;
 264        }
 265        if (!dest)
 266                usage(send_pack_usage);
 267        pid = git_connect(fd, dest, exec);
 268        if (pid < 0)
 269                return 1;
 270        ret = send_pack(fd[0], fd[1], nr_heads, heads);
 271        close(fd[0]);
 272        close(fd[1]);
 273        finish_connect(pid);
 274        return ret;
 275}