clone-pack.con commit Increase the number of possible heads requested from git-upload-pack (319aae2)
   1#include "cache.h"
   2#include "refs.h"
   3#include "pkt-line.h"
   4#include <sys/wait.h>
   5
   6static const char clone_pack_usage[] = "git-clone-pack [host:]directory [heads]*";
   7static const char *exec = "git-upload-pack";
   8
   9struct ref {
  10        struct ref *next;
  11        unsigned char sha1[20];
  12        char name[0];
  13};
  14
  15struct ref *get_remote_refs(int fd, int nr_match, char **match)
  16{
  17        struct ref *ref_list = NULL, **next_ref = &ref_list;
  18
  19        for (;;) {
  20                static char line[1000];
  21                unsigned char sha1[20];
  22                struct ref *ref;
  23                char *refname;
  24                int len;
  25
  26                len = packet_read_line(fd, line, sizeof(line));
  27                if (!len)
  28                        break;
  29                if (line[len-1] == '\n')
  30                        line[--len] = 0;
  31                if (len < 42 || get_sha1_hex(line, sha1))
  32                        die("git-fetch-pack: protocol error - expected ref descriptor, got '%sยค'", line);
  33                refname = line+41;
  34                len = len-40;
  35                if (nr_match && !path_match(refname, nr_match, match))
  36                        continue;
  37                ref = xmalloc(sizeof(struct ref) + len);
  38                ref->next = NULL;
  39                memcpy(ref->sha1, sha1, 20);
  40                memcpy(ref->name, refname, len);
  41                *next_ref = ref;
  42                next_ref = &ref->next;
  43        }
  44        return ref_list;
  45}
  46
  47static void clone_handshake(int fd[2], struct ref *ref)
  48{
  49        unsigned char sha1[20];
  50
  51        while (ref) {
  52                packet_write(fd[1], "want %s\n", sha1_to_hex(ref->sha1));
  53                ref = ref->next;
  54        }
  55        packet_flush(fd[1]);
  56
  57        /* We don't have nuttin' */
  58        packet_write(fd[1], "done\n");
  59        if (get_ack(fd[0], sha1))
  60                error("Huh! git-clone-pack got positive ack for %s", sha1_to_hex(sha1));
  61}
  62
  63static int is_master(struct ref *ref)
  64{
  65        return !strcmp(ref->name, "refs/heads/master");
  66}
  67
  68static void write_one_ref(struct ref *ref)
  69{
  70        char *path = git_path(ref->name);
  71        int fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0666);
  72        char *hex;
  73
  74        if (fd < 0)
  75                die("unable to create ref %s", ref->name);
  76        hex = sha1_to_hex(ref->sha1);
  77        hex[40] = '\n';
  78        if (write(fd, hex, 41) != 41)
  79                die("unable to write ref %s", ref->name);
  80        close(fd);
  81}
  82
  83static void write_refs(struct ref *ref)
  84{
  85        struct ref *head = NULL, *head_ptr, *master_ref;
  86        char *head_path;
  87
  88        if (!strcmp(ref->name, "HEAD")) {
  89                head = ref;
  90                ref = ref->next;
  91        }
  92        head_ptr = NULL;
  93        master_ref = NULL;
  94        while (ref) {
  95                if (is_master(ref))
  96                        master_ref = ref;
  97                if (head && !memcmp(ref->sha1, head->sha1, 20)) {
  98                        if (!head_ptr || ref == master_ref)
  99                                head_ptr = ref;
 100                }
 101                write_one_ref(ref);
 102                ref = ref->next;
 103        }
 104        if (!head)
 105                return;
 106
 107        head_path = git_path("HEAD");
 108        if (!head_ptr) {
 109                /*
 110                 * If we had a master ref, and it wasn't HEAD, we need to undo the
 111                 * symlink, and write a standalone HEAD. Give a warning, because that's
 112                 * really really wrong.
 113                 */
 114                if (master_ref) {
 115                        error("HEAD doesn't point to any refs! Making standalone HEAD");
 116                        unlink(head_path);
 117                }
 118                write_one_ref(head);
 119                return;
 120        }
 121
 122        /* We reset to the master branch if it's available */
 123        if (master_ref)
 124                return;
 125
 126        /*
 127         * Uhhuh. Other end didn't have master. We start HEAD off with
 128         * the first branch with the same value.
 129         */
 130        unlink(head_path);
 131        if (symlink(head_ptr->name, head_path) < 0)
 132                die("unable to link HEAD to %s", head_ptr->name);
 133}
 134
 135static int clone_pack(int fd[2], int nr_match, char **match)
 136{
 137        struct ref *refs;
 138        int status;
 139        pid_t pid;
 140
 141        refs = get_remote_refs(fd[0], nr_match, match);
 142        if (!refs) {
 143                packet_flush(fd[1]);
 144                die("no matching remote head");
 145        }
 146        clone_handshake(fd, refs);
 147        pid = fork();
 148        if (pid < 0)
 149                die("git-clone-pack: unable to fork off git-unpack-objects");
 150        if (!pid) {
 151                close(fd[1]);
 152                dup2(fd[0], 0);
 153                close(fd[0]);
 154                execlp("git-unpack-objects", "git-unpack-objects", NULL);
 155                die("git-unpack-objects exec failed");
 156        }
 157        close(fd[0]);
 158        close(fd[1]);
 159        while (waitpid(pid, &status, 0) < 0) {
 160                if (errno != EINTR)
 161                        die("waiting for git-unpack-objects: %s", strerror(errno));
 162        }
 163        if (WIFEXITED(status)) {
 164                int code = WEXITSTATUS(status);
 165                if (code)
 166                        die("git-unpack-objects died with error code %d", code);
 167                write_refs(refs);
 168                return 0;
 169        }
 170        if (WIFSIGNALED(status)) {
 171                int sig = WTERMSIG(status);
 172                die("git-unpack-objects died of signal %d", sig);
 173        }
 174        die("Sherlock Holmes! git-unpack-objects died of unnatural causes %d!", status);
 175}
 176
 177int main(int argc, char **argv)
 178{
 179        int i, ret, nr_heads;
 180        char *dest = NULL, **heads;
 181        int fd[2];
 182        pid_t pid;
 183
 184        nr_heads = 0;
 185        heads = NULL;
 186        for (i = 1; i < argc; i++) {
 187                char *arg = argv[i];
 188
 189                if (*arg == '-') {
 190                        /* Arguments go here */
 191                        usage(clone_pack_usage);
 192                }
 193                dest = arg;
 194                heads = argv + i + 1;
 195                nr_heads = argc - i - 1;
 196                break;
 197        }
 198        if (!dest)
 199                usage(clone_pack_usage);
 200        pid = git_connect(fd, dest, exec);
 201        if (pid < 0)
 202                return 1;
 203        ret = clone_pack(fd, nr_heads, heads);
 204        close(fd[0]);
 205        close(fd[1]);
 206        finish_connect(pid);
 207        return ret;
 208}