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