57e88a7637eb9a0976aaef2664c9d2cc06dc07e6
   1#include "cache.h"
   2#include "pkt-line.h"
   3
   4static const char send_pack_usage[] = "git-send-pack [--exec=other] destination [heads]*";
   5static const char *exec = "git-receive-pack";
   6
   7static int path_match(const char *path, int nr, char **match)
   8{
   9        int i;
  10        int pathlen = strlen(path);
  11
  12        for (i = 0; i < nr; i++) {
  13                char *s = match[i];
  14                int len = strlen(s);
  15
  16                if (!len || len > pathlen)
  17                        continue;
  18                if (memcmp(path + pathlen - len, s, len))
  19                        continue;
  20                if (pathlen > len && path[pathlen - len - 1] != '/')
  21                        continue;
  22                *s = 0;
  23                return 1;
  24        }
  25        return 0;
  26}
  27
  28struct ref {
  29        struct ref *next;
  30        unsigned char old_sha1[20];
  31        unsigned char new_sha1[20];
  32        char name[0];
  33};
  34
  35static void exec_pack_objects(void)
  36{
  37        static char *args[] = {
  38                "git-pack-objects",
  39                "--stdout",
  40                NULL
  41        };
  42        execvp("git-pack-objects", args);
  43        die("git-pack-objects exec failed (%s)", strerror(errno));
  44}
  45
  46static void exec_rev_list(struct ref *refs)
  47{
  48        static char *args[1000];
  49        int i = 0;
  50
  51        args[i++] = "git-rev-list";     /* 0 */
  52        args[i++] = "--objects";        /* 1 */
  53        while (refs) {
  54                char *buf = malloc(100);
  55                if (i > 900)
  56                        die("git-rev-list environment overflow");
  57                args[i++] = buf;
  58                snprintf(buf, 50, "^%s", sha1_to_hex(refs->old_sha1));
  59                buf += 50;
  60                args[i++] = buf;
  61                snprintf(buf, 50, "%s", sha1_to_hex(refs->new_sha1));
  62                refs = refs->next;
  63        }
  64        args[i] = NULL;
  65        execvp("git-rev-list", args);
  66        die("git-rev-list exec failed (%s)", strerror(errno));
  67}
  68
  69static void rev_list(int fd, struct ref *refs)
  70{
  71        int pipe_fd[2];
  72        pid_t pack_objects_pid;
  73
  74        if (pipe(pipe_fd) < 0)
  75                die("rev-list setup: pipe failed");
  76        pack_objects_pid = fork();
  77        if (!pack_objects_pid) {
  78                dup2(pipe_fd[0], 0);
  79                dup2(fd, 1);
  80                close(pipe_fd[0]);
  81                close(pipe_fd[1]);
  82                close(fd);
  83                exec_pack_objects();
  84                die("pack-objects setup failed");
  85        }
  86        if (pack_objects_pid < 0)
  87                die("pack-objects fork failed");
  88        dup2(pipe_fd[1], 1);
  89        close(pipe_fd[0]);
  90        close(pipe_fd[1]);
  91        close(fd);
  92        exec_rev_list(refs);
  93}
  94
  95static int pack_objects(int fd, struct ref *refs)
  96{
  97        pid_t rev_list_pid;
  98
  99        rev_list_pid = fork();
 100        if (!rev_list_pid) {
 101                rev_list(fd, refs);
 102                die("rev-list setup failed");
 103        }
 104        if (rev_list_pid < 0)
 105                die("rev-list fork failed");
 106        /*
 107         * We don't wait for the rev-list pipeline in the parent:
 108         * we end up waiting for the other end instead
 109         */
 110        return 0;
 111}
 112
 113static int read_ref(const char *ref, unsigned char *sha1)
 114{
 115        int fd, ret;
 116        static char pathname[PATH_MAX];
 117        char buffer[60];
 118        const char *git_dir = gitenv(GIT_DIR_ENVIRONMENT) ? : DEFAULT_GIT_DIR_ENVIRONMENT;
 119
 120        snprintf(pathname, sizeof(pathname), "%s/%s", git_dir, ref);
 121        fd = open(pathname, O_RDONLY);
 122        if (fd < 0)
 123                return -1;
 124        ret = -1;
 125        if (read(fd, buffer, sizeof(buffer)) >= 40)
 126                ret = get_sha1_hex(buffer, sha1);
 127        close(fd);
 128        return ret;
 129}
 130
 131static int send_pack(int in, int out, int nr_match, char **match)
 132{
 133        struct ref *ref_list = NULL, **last_ref = &ref_list;
 134        struct ref *ref;
 135
 136        for (;;) {
 137                unsigned char old_sha1[20];
 138                unsigned char new_sha1[20];
 139                static char buffer[1000];
 140                char *name;
 141                int len;
 142
 143                len = packet_read_line(in, buffer, sizeof(buffer));
 144                if (!len)
 145                        break;
 146                if (buffer[len-1] == '\n')
 147                        buffer[--len] = 0;
 148
 149                if (len < 42 || get_sha1_hex(buffer, old_sha1) || buffer[40] != ' ')
 150                        die("protocol error: expected sha/ref, got '%s'", buffer);
 151                name = buffer + 41;
 152                if (nr_match && !path_match(name, nr_match, match))
 153                        continue;
 154                if (read_ref(name, new_sha1) < 0)
 155                        return error("no such local reference '%s'", name);
 156                if (!has_sha1_file(old_sha1))
 157                        return error("remote '%s' points to object I don't have", name);
 158                if (!memcmp(old_sha1, new_sha1, 20)) {
 159                        fprintf(stderr, "'%s' unchanged\n", name);
 160                        continue;
 161                }
 162                ref = xmalloc(sizeof(*ref) + len - 40);
 163                memcpy(ref->old_sha1, old_sha1, 20);
 164                memcpy(ref->new_sha1, new_sha1, 20);
 165                memcpy(ref->name, buffer + 41, len - 40);
 166                ref->next = NULL;
 167                *last_ref = ref;
 168                last_ref = &ref->next;
 169        }
 170
 171        for (ref = ref_list; ref; ref = ref->next) {
 172                char old_hex[60], *new_hex;
 173                strcpy(old_hex, sha1_to_hex(ref->old_sha1));
 174                new_hex = sha1_to_hex(ref->new_sha1);
 175                packet_write(out, "%s %s %s", old_hex, new_hex, ref->name);
 176                fprintf(stderr, "'%s': updating from %s to %s\n", ref->name, old_hex, new_hex);
 177        }
 178        
 179        packet_flush(out);
 180        if (ref_list)
 181                pack_objects(out, ref_list);
 182        close(out);
 183        return 0;
 184}
 185
 186int main(int argc, char **argv)
 187{
 188        int i, nr_heads = 0;
 189        char *dest = NULL;
 190        char **heads = NULL;
 191        int fd[2], ret;
 192        pid_t pid;
 193
 194        argv++;
 195        for (i = 1; i < argc; i++) {
 196                char *arg = *argv++;
 197
 198                if (*arg == '-') {
 199                        if (!strncmp(arg, "--exec=", 7)) {
 200                                exec = arg + 7;
 201                                continue;
 202                        }
 203                        usage(send_pack_usage);
 204                }
 205                dest = arg;
 206                heads = argv;
 207                nr_heads = argc - i -1;
 208                break;
 209        }
 210        if (!dest)
 211                usage(send_pack_usage);
 212        pid = git_connect(fd, dest, exec);
 213        if (pid < 0)
 214                return 1;
 215        ret = send_pack(fd[0], fd[1], nr_heads, heads);
 216        close(fd[0]);
 217        close(fd[1]);
 218        finish_connect(pid);
 219        return ret;
 220}