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