clone-pack.con commit Forward port the "funny ref avoidance" in clone and fetch from maint branch. (cdb3950)
   1#include "cache.h"
   2#include "refs.h"
   3#include "pkt-line.h"
   4#include <sys/wait.h>
   5
   6static int quiet;
   7static int keep_pack;
   8static const char clone_pack_usage[] =
   9"git-clone-pack [-q] [--keep] [--exec=<git-upload-pack>] [<host>:]<directory> [<heads>]*";
  10static const char *exec = "git-upload-pack";
  11
  12static void clone_handshake(int fd[2], struct ref *ref)
  13{
  14        unsigned char sha1[20];
  15
  16        while (ref) {
  17                packet_write(fd[1], "want %s\n", sha1_to_hex(ref->old_sha1));
  18                ref = ref->next;
  19        }
  20        packet_flush(fd[1]);
  21
  22        /* We don't have nuttin' */
  23        packet_write(fd[1], "done\n");
  24        if (get_ack(fd[0], sha1))
  25                error("Huh! git-clone-pack got positive ack for %s", sha1_to_hex(sha1));
  26}
  27
  28static int is_master(struct ref *ref)
  29{
  30        return !strcmp(ref->name, "refs/heads/master");
  31}
  32
  33static void write_one_ref(struct ref *ref)
  34{
  35        char *path = git_path("%s", ref->name);
  36        int fd;
  37        char *hex;
  38
  39        if (!strncmp(ref->name, "refs/", 5) &&
  40            check_ref_format(ref->name + 5)) {
  41                error("refusing to create funny ref '%s' locally", ref->name);
  42                return;
  43        }
  44
  45        if (safe_create_leading_directories(path))
  46                die("unable to create leading directory for %s", ref->name);
  47        fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0666);
  48        if (fd < 0)
  49                die("unable to create ref %s", ref->name);
  50        hex = sha1_to_hex(ref->old_sha1);
  51        hex[40] = '\n';
  52        if (write(fd, hex, 41) != 41)
  53                die("unable to write ref %s", ref->name);
  54        close(fd);
  55}
  56
  57static void write_refs(struct ref *ref)
  58{
  59        struct ref *head = NULL, *head_ptr, *master_ref;
  60        char *head_path;
  61
  62        /* Upload-pack must report HEAD first */
  63        if (!strcmp(ref->name, "HEAD")) {
  64                head = ref;
  65                ref = ref->next;
  66        }
  67        head_ptr = NULL;
  68        master_ref = NULL;
  69        while (ref) {
  70                if (is_master(ref))
  71                        master_ref = ref;
  72                if (head &&
  73                    !memcmp(ref->old_sha1, head->old_sha1, 20) &&
  74                    !strncmp(ref->name, "refs/heads/",11) &&
  75                    (!head_ptr || ref == master_ref))
  76                        head_ptr = ref;
  77
  78                write_one_ref(ref);
  79                ref = ref->next;
  80        }
  81        if (!head) {
  82                fprintf(stderr, "No HEAD in remote.\n");
  83                return;
  84        }
  85
  86        head_path = strdup(git_path("HEAD"));
  87        if (!head_ptr) {
  88                /*
  89                 * If we had a master ref, and it wasn't HEAD, we need to undo the
  90                 * symlink, and write a standalone HEAD. Give a warning, because that's
  91                 * really really wrong.
  92                 */
  93                if (master_ref) {
  94                        error("HEAD doesn't point to any refs! Making standalone HEAD");
  95                        unlink(head_path);
  96                }
  97                write_one_ref(head);
  98                free(head_path);
  99                return;
 100        }
 101
 102        /* We reset to the master branch if it's available */
 103        if (master_ref)
 104                return;
 105
 106        fprintf(stderr, "Setting HEAD to %s\n", head_ptr->name);
 107
 108        /*
 109         * Uhhuh. Other end didn't have master. We start HEAD off with
 110         * the first branch with the same value.
 111         */
 112        if (create_symref(head_path, head_ptr->name) < 0)
 113                die("unable to link HEAD to %s", head_ptr->name);
 114        free(head_path);
 115}
 116
 117static int clone_by_unpack(int fd[2])
 118{
 119        int status;
 120        pid_t pid;
 121
 122        pid = fork();
 123        if (pid < 0)
 124                die("git-clone-pack: unable to fork off git-unpack-objects");
 125        if (!pid) {
 126                dup2(fd[0], 0);
 127                close(fd[0]);
 128                close(fd[1]);
 129                execlp("git-unpack-objects", "git-unpack-objects",
 130                        quiet ? "-q" : NULL, NULL);
 131                die("git-unpack-objects exec failed");
 132        }
 133        close(fd[0]);
 134        close(fd[1]);
 135        while (waitpid(pid, &status, 0) < 0) {
 136                if (errno != EINTR)
 137                        die("waiting for git-unpack-objects: %s", strerror(errno));
 138        }
 139        if (WIFEXITED(status)) {
 140                int code = WEXITSTATUS(status);
 141                if (code)
 142                        die("git-unpack-objects died with error code %d", code);
 143                return 0;
 144        }
 145        if (WIFSIGNALED(status)) {
 146                int sig = WTERMSIG(status);
 147                die("git-unpack-objects died of signal %d", sig);
 148        }
 149        die("Sherlock Holmes! git-unpack-objects died of unnatural causes %d!", status);
 150}
 151
 152static int finish_pack(const char *pack_tmp_name)
 153{
 154        int pipe_fd[2];
 155        pid_t pid;
 156        char idx[PATH_MAX];
 157        char final[PATH_MAX];
 158        char hash[41];
 159        unsigned char sha1[20];
 160        char *cp;
 161        int err = 0;
 162
 163        if (pipe(pipe_fd) < 0)
 164                die("git-clone-pack: unable to set up pipe");
 165
 166        strcpy(idx, pack_tmp_name); /* ".git/objects/pack-XXXXXX" */
 167        cp = strrchr(idx, '/');
 168        memcpy(cp, "/pidx", 5);
 169
 170        pid = fork();
 171        if (pid < 0)
 172                die("git-clone-pack: unable to fork off git-index-pack");
 173        if (!pid) {
 174                close(0);
 175                dup2(pipe_fd[1], 1);
 176                close(pipe_fd[0]);
 177                close(pipe_fd[1]);
 178                execlp("git-index-pack","git-index-pack",
 179                       "-o", idx, pack_tmp_name, NULL);
 180                error("cannot exec git-index-pack <%s> <%s>",
 181                      idx, pack_tmp_name);
 182                exit(1);
 183        }
 184        close(pipe_fd[1]);
 185        if (read(pipe_fd[0], hash, 40) != 40) {
 186                error("git-clone-pack: unable to read from git-index-pack");
 187                err = 1;
 188        }
 189        close(pipe_fd[0]);
 190
 191        for (;;) {
 192                int status, code;
 193                int retval = waitpid(pid, &status, 0);
 194
 195                if (retval < 0) {
 196                        if (errno == EINTR)
 197                                continue;
 198                        error("waitpid failed (%s)", strerror(retval));
 199                        goto error_die;
 200                }
 201                if (WIFSIGNALED(status)) {
 202                        int sig = WTERMSIG(status);
 203                        error("git-index-pack died of signal %d", sig);
 204                        goto error_die;
 205                }
 206                if (!WIFEXITED(status)) {
 207                        error("git-index-pack died of unnatural causes %d",
 208                              status);
 209                        goto error_die;
 210                }
 211                code = WEXITSTATUS(status);
 212                if (code) {
 213                        error("git-index-pack died with error code %d", code);
 214                        goto error_die;
 215                }
 216                if (err)
 217                        goto error_die;
 218                break;
 219        }
 220        hash[40] = 0;
 221        if (get_sha1_hex(hash, sha1)) {
 222                error("git-index-pack reported nonsense '%s'", hash);
 223                goto error_die;
 224        }
 225        /* Now we have pack in pack_tmp_name[], and
 226         * idx in idx[]; rename them to their final names.
 227         */
 228        snprintf(final, sizeof(final),
 229                 "%s/pack/pack-%s.pack", get_object_directory(), hash);
 230        move_temp_to_file(pack_tmp_name, final);
 231        chmod(final, 0444);
 232        snprintf(final, sizeof(final),
 233                 "%s/pack/pack-%s.idx", get_object_directory(), hash);
 234        move_temp_to_file(idx, final);
 235        chmod(final, 0444);
 236        return 0;
 237
 238 error_die:
 239        unlink(idx);
 240        unlink(pack_tmp_name);
 241        exit(1);
 242}
 243
 244static int clone_without_unpack(int fd[2])
 245{
 246        char tmpfile[PATH_MAX];
 247        int ofd, ifd;
 248
 249        ifd = fd[0];
 250        snprintf(tmpfile, sizeof(tmpfile),
 251                 "%s/pack-XXXXXX", get_object_directory());
 252        ofd = mkstemp(tmpfile);
 253        if (ofd < 0)
 254                return error("unable to create temporary file %s", tmpfile);
 255
 256        while (1) {
 257                char buf[8192];
 258                ssize_t sz, wsz, pos;
 259                sz = read(ifd, buf, sizeof(buf));
 260                if (sz == 0)
 261                        break;
 262                if (sz < 0) {
 263                        error("error reading pack (%s)", strerror(errno));
 264                        close(ofd);
 265                        unlink(tmpfile);
 266                        return -1;
 267                }
 268                pos = 0;
 269                while (pos < sz) {
 270                        wsz = write(ofd, buf + pos, sz - pos);
 271                        if (wsz < 0) {
 272                                error("error writing pack (%s)",
 273                                      strerror(errno));
 274                                close(ofd);
 275                                unlink(tmpfile);
 276                                return -1;
 277                        }
 278                        pos += wsz;
 279                }
 280        }
 281        close(ofd);
 282        return finish_pack(tmpfile);
 283}
 284
 285static int clone_pack(int fd[2], int nr_match, char **match)
 286{
 287        struct ref *refs;
 288        int status;
 289
 290        get_remote_heads(fd[0], &refs, nr_match, match, 1);
 291        if (!refs) {
 292                packet_flush(fd[1]);
 293                die("no matching remote head");
 294        }
 295        clone_handshake(fd, refs);
 296
 297        if (keep_pack)
 298                status = clone_without_unpack(fd);
 299        else
 300                status = clone_by_unpack(fd);
 301
 302        if (!status)
 303                write_refs(refs);
 304        return status;
 305}
 306
 307static int clone_options(const char *var, const char *value)
 308{
 309        if (!strcmp("clone.keeppack", var)) {
 310                keep_pack = git_config_bool(var, value);
 311                return 0;
 312        }
 313        if (!strcmp("clone.quiet", var)) {
 314                quiet = git_config_bool(var, value);
 315                return 0;
 316        }
 317        /*
 318         * Put other local option parsing for this program
 319         * here ...
 320         */
 321
 322        /* Fall back on the default ones */
 323        return git_default_config(var, value);
 324}
 325
 326int main(int argc, char **argv)
 327{
 328        int i, ret, nr_heads;
 329        char *dest = NULL, **heads;
 330        int fd[2];
 331        pid_t pid;
 332
 333        git_config(clone_options);
 334        nr_heads = 0;
 335        heads = NULL;
 336        for (i = 1; i < argc; i++) {
 337                char *arg = argv[i];
 338
 339                if (*arg == '-') {
 340                        if (!strcmp("-q", arg)) {
 341                                quiet = 1;
 342                                continue;
 343                        }
 344                        if (!strncmp("--exec=", arg, 7)) {
 345                                exec = arg + 7;
 346                                continue;
 347                        }
 348                        if (!strcmp("--keep", arg)) {
 349                                keep_pack = 1;
 350                                continue;
 351                        }
 352                        usage(clone_pack_usage);
 353                }
 354                dest = arg;
 355                heads = argv + i + 1;
 356                nr_heads = argc - i - 1;
 357                break;
 358        }
 359        if (!dest)
 360                usage(clone_pack_usage);
 361        pid = git_connect(fd, dest, exec);
 362        if (pid < 0)
 363                return 1;
 364        ret = clone_pack(fd, nr_heads, heads);
 365        close(fd[0]);
 366        close(fd[1]);
 367        finish_connect(pid);
 368        return ret;
 369}