b8367a4d625fdfe59cf30e0a4aed40443139c40c
   1#include "cache.h"
   2#include "refs.h"
   3#include "pkt-line.h"
   4#include <sys/wait.h>
   5
   6static const char fetch_pack_usage[] = "git-fetch-pack [host:]directory [heads]* < mycommitlist";
   7static const char *exec = "git-upload-pack";
   8
   9static int get_ack(int fd, unsigned char *result_sha1)
  10{
  11        static char line[1000];
  12        int len = packet_read_line(fd, line, sizeof(line));
  13
  14        if (!len)
  15                die("git-fetch-pack: expected ACK/NAK, got EOF");
  16        if (line[len-1] == '\n')
  17                line[--len] = 0;
  18        if (!strcmp(line, "NAK"))
  19                return 0;
  20        if (!strncmp(line, "ACK ", 3)) {
  21                if (!get_sha1_hex(line+4, result_sha1))
  22                        return 1;
  23        }
  24        die("git-fetch_pack: expected ACK/NAK, got '%s'", line);
  25}
  26
  27static int find_common(int fd[2], unsigned char *result_sha1, unsigned char *remote)
  28{
  29        static char line[1000];
  30        int count = 0, flushes = 0, retval;
  31        FILE *revs;
  32
  33        revs = popen("git-rev-list $(git-rev-parse --all)", "r");
  34        if (!revs)
  35                die("unable to run 'git-rev-list'");
  36        packet_write(fd[1], "want %s\n", sha1_to_hex(remote));
  37        packet_flush(fd[1]);
  38        flushes = 1;
  39        retval = -1;
  40        while (fgets(line, sizeof(line), revs) != NULL) {
  41                unsigned char sha1[20];
  42                if (get_sha1_hex(line, sha1))
  43                        die("git-fetch-pack: expected object name, got crud");
  44                packet_write(fd[1], "have %s\n", sha1_to_hex(sha1));
  45                if (!(31 & ++count)) {
  46                        packet_flush(fd[1]);
  47                        flushes++;
  48
  49                        /*
  50                         * We keep one window "ahead" of the other side, and
  51                         * will wait for an ACK only on the next one
  52                         */
  53                        if (count == 32)
  54                                continue;
  55                        if (get_ack(fd[0], result_sha1)) {
  56                                flushes = 0;
  57                                retval = 0;
  58                                break;
  59                        }
  60                        flushes--;
  61                }
  62        }
  63        pclose(revs);
  64        packet_write(fd[1], "done\n");
  65        while (flushes) {
  66                flushes--;
  67                if (get_ack(fd[0], result_sha1))
  68                        return 0;
  69        }
  70        return retval;
  71}
  72
  73static int get_old_sha1(const char *refname, unsigned char *sha1)
  74{
  75        static char pathname[PATH_MAX];
  76        const char *git_dir;
  77        int fd, ret;
  78
  79        git_dir = gitenv(GIT_DIR_ENVIRONMENT) ? : DEFAULT_GIT_DIR_ENVIRONMENT;
  80        snprintf(pathname, sizeof(pathname), "%s/%s", git_dir, refname);
  81        fd = open(pathname, O_RDONLY);
  82        ret = -1;
  83        if (fd >= 0) {
  84                char buffer[60];
  85                if (read(fd, buffer, sizeof(buffer)) >= 40)
  86                        ret = get_sha1_hex(buffer, sha1);
  87                close(fd);
  88        }
  89        return ret;
  90}
  91
  92static int check_ref(const char *refname, const unsigned char *sha1)
  93{
  94        unsigned char mysha1[20];
  95        char oldhex[41];
  96
  97        if (get_old_sha1(refname, mysha1) < 0)
  98                memset(mysha1, 0, 20);
  99
 100        if (!memcmp(sha1, mysha1, 20)) {
 101                fprintf(stderr, "%s: unchanged\n", refname);
 102                return 0;
 103        }
 104        
 105        memcpy(oldhex, sha1_to_hex(mysha1), 41);
 106        fprintf(stderr, "%s: %s (%s)\n", refname, sha1_to_hex(sha1), oldhex);
 107        return 1;
 108}
 109
 110static int get_remote_heads(int fd, int nr_match, char **match, unsigned char *result)
 111{
 112        int count = 0;
 113
 114        for (;;) {
 115                static char line[1000];
 116                unsigned char sha1[20];
 117                char *refname;
 118                int len;
 119
 120                len = packet_read_line(fd, line, sizeof(line));
 121                if (!len)
 122                        break;
 123                if (line[len-1] == '\n')
 124                        line[--len] = 0;
 125                if (len < 42 || get_sha1_hex(line, sha1))
 126                        die("git-fetch-pack: protocol error - expected ref descriptor, got '%sä'", line);
 127                refname = line+41;
 128                if (nr_match && !path_match(refname, nr_match, match))
 129                        continue;
 130                if (check_ref(refname, sha1)) {
 131                        count++;
 132                        memcpy(result, sha1, 20);
 133                }
 134        }
 135        return count;
 136}
 137
 138static int fetch_pack(int fd[2], int nr_match, char **match)
 139{
 140        unsigned char sha1[20], remote[20];
 141        int heads, status;
 142        pid_t pid;
 143
 144        heads = get_remote_heads(fd[0], nr_match, match, remote);
 145        if (heads != 1) {
 146                packet_flush(fd[1]);
 147                die(heads ? "multiple remote heads" : "no matching remote head");
 148        }
 149        if (find_common(fd, sha1, remote) < 0)
 150                die("git-fetch-pack: no common commits");
 151        pid = fork();
 152        if (pid < 0)
 153                die("git-fetch-pack: unable to fork off git-unpack-objects");
 154        if (!pid) {
 155                close(fd[1]);
 156                dup2(fd[0], 0);
 157                close(fd[0]);
 158                execlp("git-unpack-objects", "git-unpack-objects", NULL);
 159                die("git-unpack-objects exec failed");
 160        }
 161        close(fd[0]);
 162        close(fd[1]);
 163        while (waitpid(pid, &status, 0) < 0) {
 164                if (errno != EINTR)
 165                        die("waiting for git-unpack-objects: %s", strerror(errno));
 166        }
 167        if (WIFEXITED(status)) {
 168                int code = WEXITSTATUS(status);
 169                if (code)
 170                        die("git-unpack-objects died with error code %d", code);
 171                puts(sha1_to_hex(remote));
 172                return 0;
 173        }
 174        if (WIFSIGNALED(status)) {
 175                int sig = WTERMSIG(status);
 176                die("git-unpack-objects died of signal %d", sig);
 177        }
 178        die("Sherlock Holmes! git-unpack-objects died of unnatural causes %d!", status);
 179}
 180
 181int main(int argc, char **argv)
 182{
 183        int i, ret, nr_heads;
 184        char *dest = NULL, **heads;
 185        int fd[2];
 186        pid_t pid;
 187
 188        nr_heads = 0;
 189        heads = NULL;
 190        for (i = 1; i < argc; i++) {
 191                char *arg = argv[i];
 192
 193                if (*arg == '-') {
 194                        /* Arguments go here */
 195                        usage(fetch_pack_usage);
 196                }
 197                dest = arg;
 198                heads = argv + i + 1;
 199                nr_heads = argc - i - 1;
 200                break;
 201        }
 202        if (!dest)
 203                usage(fetch_pack_usage);
 204        pid = git_connect(fd, dest, exec);
 205        if (pid < 0)
 206                return 1;
 207        ret = fetch_pack(fd, nr_heads, heads);
 208        close(fd[0]);
 209        close(fd[1]);
 210        finish_connect(pid);
 211        return ret;
 212}