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