clone-pack.con commit [PATCH] Expose object ID computation functions. (7672db2)
   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
  15static struct 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;
  72        char *hex;
  73
  74        if (safe_create_leading_directories(path))
  75                die("unable to create leading directory for %s", ref->name);
  76        fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0666);
  77        if (fd < 0)
  78                die("unable to create ref %s", ref->name);
  79        hex = sha1_to_hex(ref->sha1);
  80        hex[40] = '\n';
  81        if (write(fd, hex, 41) != 41)
  82                die("unable to write ref %s", ref->name);
  83        close(fd);
  84}
  85
  86static void write_refs(struct ref *ref)
  87{
  88        struct ref *head = NULL, *head_ptr, *master_ref;
  89        char *head_path;
  90
  91        if (!strcmp(ref->name, "HEAD")) {
  92                head = ref;
  93                ref = ref->next;
  94        }
  95        head_ptr = NULL;
  96        master_ref = NULL;
  97        while (ref) {
  98                if (is_master(ref))
  99                        master_ref = ref;
 100                if (head && !memcmp(ref->sha1, head->sha1, 20)) {
 101                        if (!head_ptr || ref == master_ref)
 102                                head_ptr = ref;
 103                }
 104                write_one_ref(ref);
 105                ref = ref->next;
 106        }
 107        if (!head)
 108                return;
 109
 110        head_path = git_path("HEAD");
 111        if (!head_ptr) {
 112                /*
 113                 * If we had a master ref, and it wasn't HEAD, we need to undo the
 114                 * symlink, and write a standalone HEAD. Give a warning, because that's
 115                 * really really wrong.
 116                 */
 117                if (master_ref) {
 118                        error("HEAD doesn't point to any refs! Making standalone HEAD");
 119                        unlink(head_path);
 120                }
 121                write_one_ref(head);
 122                return;
 123        }
 124
 125        /* We reset to the master branch if it's available */
 126        if (master_ref)
 127                return;
 128
 129        /*
 130         * Uhhuh. Other end didn't have master. We start HEAD off with
 131         * the first branch with the same value.
 132         */
 133        unlink(head_path);
 134        if (symlink(head_ptr->name, head_path) < 0)
 135                die("unable to link HEAD to %s", head_ptr->name);
 136}
 137
 138static int clone_pack(int fd[2], int nr_match, char **match)
 139{
 140        struct ref *refs;
 141        int status;
 142        pid_t pid;
 143
 144        refs = get_remote_refs(fd[0], nr_match, match);
 145        if (!refs) {
 146                packet_flush(fd[1]);
 147                die("no matching remote head");
 148        }
 149        clone_handshake(fd, refs);
 150        pid = fork();
 151        if (pid < 0)
 152                die("git-clone-pack: unable to fork off git-unpack-objects");
 153        if (!pid) {
 154                close(fd[1]);
 155                dup2(fd[0], 0);
 156                close(fd[0]);
 157                execlp("git-unpack-objects", "git-unpack-objects", NULL);
 158                die("git-unpack-objects exec failed");
 159        }
 160        close(fd[0]);
 161        close(fd[1]);
 162        while (waitpid(pid, &status, 0) < 0) {
 163                if (errno != EINTR)
 164                        die("waiting for git-unpack-objects: %s", strerror(errno));
 165        }
 166        if (WIFEXITED(status)) {
 167                int code = WEXITSTATUS(status);
 168                if (code)
 169                        die("git-unpack-objects died with error code %d", code);
 170                write_refs(refs);
 171                return 0;
 172        }
 173        if (WIFSIGNALED(status)) {
 174                int sig = WTERMSIG(status);
 175                die("git-unpack-objects died of signal %d", sig);
 176        }
 177        die("Sherlock Holmes! git-unpack-objects died of unnatural causes %d!", status);
 178}
 179
 180int main(int argc, char **argv)
 181{
 182        int i, ret, nr_heads;
 183        char *dest = NULL, **heads;
 184        int fd[2];
 185        pid_t pid;
 186
 187        nr_heads = 0;
 188        heads = NULL;
 189        for (i = 1; i < argc; i++) {
 190                char *arg = argv[i];
 191
 192                if (*arg == '-') {
 193                        /* Arguments go here */
 194                        usage(clone_pack_usage);
 195                }
 196                dest = arg;
 197                heads = argv + i + 1;
 198                nr_heads = argc - i - 1;
 199                break;
 200        }
 201        if (!dest)
 202                usage(clone_pack_usage);
 203        pid = git_connect(fd, dest, exec);
 204        if (pid < 0)
 205                return 1;
 206        ret = clone_pack(fd, nr_heads, heads);
 207        close(fd[0]);
 208        close(fd[1]);
 209        finish_connect(pid);
 210        return ret;
 211}