send-pack.con commit [PATCH] tools/applymbox: allow manual fixing and continuing after a failure. (3727531)
   1#include "cache.h"
   2#include "commit.h"
   3#include "refs.h"
   4#include "pkt-line.h"
   5
   6static const char send_pack_usage[] =
   7"git-send-pack [--exec=git-receive-pack] [host:]directory [heads]*";
   8static const char *exec = "git-receive-pack";
   9static int send_all = 0;
  10static int force_update = 0;
  11
  12static int is_zero_sha1(const unsigned char *sha1)
  13{
  14        int i;
  15
  16        for (i = 0; i < 20; i++) {
  17                if (*sha1++)
  18                        return 0;
  19        }
  20        return 1;
  21}
  22
  23static void exec_pack_objects(void)
  24{
  25        static char *args[] = {
  26                "git-pack-objects",
  27                "--stdout",
  28                NULL
  29        };
  30        execvp("git-pack-objects", args);
  31        die("git-pack-objects exec failed (%s)", strerror(errno));
  32}
  33
  34static void exec_rev_list(struct ref *refs)
  35{
  36        static char *args[1000];
  37        int i = 0;
  38
  39        args[i++] = "git-rev-list";     /* 0 */
  40        args[i++] = "--objects";        /* 1 */
  41        while (refs) {
  42                char *buf = malloc(100);
  43                if (i > 900)
  44                        die("git-rev-list environment overflow");
  45                if (!is_zero_sha1(refs->old_sha1)) {
  46                        args[i++] = buf;
  47                        snprintf(buf, 50, "^%s", sha1_to_hex(refs->old_sha1));
  48                        buf += 50;
  49                }
  50                if (!is_zero_sha1(refs->new_sha1)) {
  51                        args[i++] = buf;
  52                        snprintf(buf, 50, "%s", sha1_to_hex(refs->new_sha1));
  53                }
  54                refs = refs->next;
  55        }
  56        args[i] = NULL;
  57        execvp("git-rev-list", args);
  58        die("git-rev-list exec failed (%s)", strerror(errno));
  59}
  60
  61static void rev_list(int fd, struct ref *refs)
  62{
  63        int pipe_fd[2];
  64        pid_t pack_objects_pid;
  65
  66        if (pipe(pipe_fd) < 0)
  67                die("rev-list setup: pipe failed");
  68        pack_objects_pid = fork();
  69        if (!pack_objects_pid) {
  70                dup2(pipe_fd[0], 0);
  71                dup2(fd, 1);
  72                close(pipe_fd[0]);
  73                close(pipe_fd[1]);
  74                close(fd);
  75                exec_pack_objects();
  76                die("pack-objects setup failed");
  77        }
  78        if (pack_objects_pid < 0)
  79                die("pack-objects fork failed");
  80        dup2(pipe_fd[1], 1);
  81        close(pipe_fd[0]);
  82        close(pipe_fd[1]);
  83        close(fd);
  84        exec_rev_list(refs);
  85}
  86
  87static int pack_objects(int fd, struct ref *refs)
  88{
  89        pid_t rev_list_pid;
  90
  91        rev_list_pid = fork();
  92        if (!rev_list_pid) {
  93                rev_list(fd, refs);
  94                die("rev-list setup failed");
  95        }
  96        if (rev_list_pid < 0)
  97                die("rev-list fork failed");
  98        /*
  99         * We don't wait for the rev-list pipeline in the parent:
 100         * we end up waiting for the other end instead
 101         */
 102        return 0;
 103}
 104
 105static int read_ref(const char *ref, unsigned char *sha1)
 106{
 107        int fd, ret;
 108        char buffer[60];
 109
 110        fd = open(git_path("%s", ref), O_RDONLY);
 111        if (fd < 0)
 112                return -1;
 113        ret = -1;
 114        if (read(fd, buffer, sizeof(buffer)) >= 40)
 115                ret = get_sha1_hex(buffer, sha1);
 116        close(fd);
 117        return ret;
 118}
 119
 120static int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1)
 121{
 122        struct commit *new, *old;
 123        struct commit_list *list;
 124
 125        if (force_update)
 126                return 1;
 127        old = lookup_commit_reference(old_sha1);
 128        if (!old)
 129                return 0;
 130        new = lookup_commit_reference(new_sha1);
 131        if (!new)
 132                return 0;
 133        if (parse_commit(new) < 0)
 134                return 0;
 135        list = NULL;
 136        commit_list_insert(new, &list);
 137        while ((new = pop_most_recent_commit(&list, 1)) != NULL) {
 138                if (new == old)
 139                        return 1;
 140        }
 141        return 0;
 142}
 143
 144static int local_ref_nr_match;
 145static char **local_ref_match;
 146static struct ref *local_ref_list;
 147static struct ref **local_last_ref;
 148
 149static int try_to_match(const char *refname, const unsigned char *sha1)
 150{
 151        struct ref *ref;
 152        int len;
 153
 154        if (!path_match(refname, local_ref_nr_match, local_ref_match)) {
 155                if (!send_all)
 156                        return 0;
 157
 158                /* If we have it listed already, skip it */
 159                for (ref = local_ref_list ; ref ; ref = ref->next) {
 160                        if (!strcmp(ref->name, refname))
 161                                return 0;
 162                }
 163        }
 164
 165        len = strlen(refname)+1;
 166        ref = xmalloc(sizeof(*ref) + len);
 167        memset(ref->old_sha1, 0, 20);
 168        memcpy(ref->new_sha1, sha1, 20);
 169        memcpy(ref->name, refname, len);
 170        ref->next = NULL;
 171        *local_last_ref = ref;
 172        local_last_ref = &ref->next;
 173        return 0;
 174}
 175
 176static int send_pack(int in, int out, int nr_match, char **match)
 177{
 178        struct ref *ref_list, **last_ref;
 179        struct ref *ref;
 180        int new_refs;
 181
 182        /* First we get all heads, whether matching or not.. */
 183        last_ref = get_remote_heads(in, &ref_list, 0, NULL);
 184
 185        /*
 186         * Go through the refs, see if we want to update
 187         * any of them..
 188         */
 189        for (ref = ref_list; ref; ref = ref->next) {
 190                unsigned char new_sha1[20];
 191                char *name = ref->name;
 192
 193                if (nr_match && !path_match(name, nr_match, match))
 194                        continue;
 195
 196                if (read_ref(name, new_sha1) < 0)
 197                        continue;
 198
 199                if (!memcmp(ref->old_sha1, new_sha1, 20)) {
 200                        fprintf(stderr, "'%s' unchanged\n", name);
 201                        continue;
 202                }
 203
 204                if (!ref_newer(new_sha1, ref->old_sha1)) {
 205                        error("remote '%s' isn't a strict parent of local", name);
 206                        continue;
 207                }
 208
 209                /* Ok, mark it for update */
 210                memcpy(ref->new_sha1, new_sha1, 20);
 211        }
 212
 213        /*
 214         * See if we have any refs that the other end didn't have
 215         */
 216        if (nr_match) {
 217                local_ref_nr_match = nr_match;
 218                local_ref_match = match;
 219                local_ref_list = ref_list;
 220                local_last_ref = last_ref;
 221                for_each_ref(try_to_match);
 222        }
 223
 224        /*
 225         * Finally, tell the other end!
 226         */
 227        new_refs = 0;
 228        for (ref = ref_list; ref; ref = ref->next) {
 229                char old_hex[60], *new_hex;
 230                if (is_zero_sha1(ref->new_sha1))
 231                        continue;
 232                new_refs++;
 233                strcpy(old_hex, sha1_to_hex(ref->old_sha1));
 234                new_hex = sha1_to_hex(ref->new_sha1);
 235                packet_write(out, "%s %s %s", old_hex, new_hex, ref->name);
 236                fprintf(stderr, "'%s': updating from %s to %s\n", ref->name, old_hex, new_hex);
 237        }
 238        
 239        packet_flush(out);
 240        if (new_refs)
 241                pack_objects(out, ref_list);
 242        close(out);
 243        return 0;
 244}
 245
 246int main(int argc, char **argv)
 247{
 248        int i, nr_heads = 0;
 249        char *dest = NULL;
 250        char **heads = NULL;
 251        int fd[2], ret;
 252        pid_t pid;
 253
 254        argv++;
 255        for (i = 1; i < argc; i++, argv++) {
 256                char *arg = *argv;
 257
 258                if (*arg == '-') {
 259                        if (!strncmp(arg, "--exec=", 7)) {
 260                                exec = arg + 7;
 261                                continue;
 262                        }
 263                        if (!strcmp(arg, "--all")) {
 264                                send_all = 1;
 265                                continue;
 266                        }
 267                        if (!strcmp(arg, "--force")) {
 268                                force_update = 1;
 269                                continue;
 270                        }
 271                        usage(send_pack_usage);
 272                }
 273                if (!dest) {
 274                        dest = arg;
 275                        continue;
 276                }
 277                heads = argv;
 278                nr_heads = argc - i;
 279                break;
 280        }
 281        if (!dest)
 282                usage(send_pack_usage);
 283        pid = git_connect(fd, dest, exec);
 284        if (pid < 0)
 285                return 1;
 286        ret = send_pack(fd[0], fd[1], nr_heads, heads);
 287        close(fd[0]);
 288        close(fd[1]);
 289        finish_connect(pid);
 290        return ret;
 291}