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