fetch-pack.con commit Subject: [PATCH] git-fetch-pack: Do not use git-rev-list (23d61f8)
   1#include "cache.h"
   2#include "refs.h"
   3#include "pkt-line.h"
   4#include "commit.h"
   5#include "tag.h"
   6#include <time.h>
   7#include <sys/wait.h>
   8
   9static int quiet;
  10static int verbose;
  11static const char fetch_pack_usage[] =
  12"git-fetch-pack [-q] [-v] [--exec=upload-pack] [host:]directory <refs>...";
  13static const char *exec = "git-upload-pack";
  14
  15#define COMPLETE        (1U << 0)
  16#define COMMON          (1U << 1)
  17#define COMMON_REF      (1U << 2)
  18#define SEEN            (1U << 3)
  19#define POPPED          (1U << 4)
  20
  21static struct commit_list *rev_list = NULL;
  22static int non_common_revs = 0;
  23
  24static void rev_list_push(struct commit *commit, int mark)
  25{
  26        if (!(commit->object.flags & mark)) {
  27                commit->object.flags |= mark;
  28
  29                if (!(commit->object.parsed))
  30                        parse_commit(commit);
  31
  32                insert_by_date(commit, &rev_list);
  33
  34                if (!(commit->object.flags & COMMON))
  35                        non_common_revs++;
  36        }
  37}
  38
  39static int rev_list_insert_ref(const char *path, const unsigned char *sha1)
  40{
  41        struct object *o = deref_tag(parse_object(sha1));
  42
  43        if (o->type == commit_type)
  44                rev_list_push((struct commit *)o, SEEN);
  45
  46        return 0;
  47}
  48
  49/*
  50   This function marks a rev and its ancestors as common.
  51   In some cases, it is desirable to mark only the ancestors (for example
  52   when only the server does not yet know that they are common).
  53*/
  54
  55static void mark_common(struct commit *commit,
  56                int ancestors_only, int dont_parse)
  57{
  58        if (commit != NULL && !(commit->object.flags & COMMON)) {
  59                struct object *o = (struct object *)commit;
  60
  61                if (!ancestors_only)
  62                        o->flags |= COMMON;
  63
  64                if (!(o->flags & SEEN))
  65                        rev_list_push(commit, SEEN);
  66                else {
  67                        struct commit_list *parents;
  68
  69                        if (!ancestors_only && !(o->flags & POPPED))
  70                                non_common_revs--;
  71                        if (!o->parsed && !dont_parse)
  72                                parse_commit(commit);
  73
  74                        for (parents = commit->parents;
  75                                        parents;
  76                                        parents = parents->next)
  77                                mark_common(parents->item, 0, dont_parse);
  78                }
  79        }
  80}
  81
  82/*
  83  Get the next rev to send, ignoring the common.
  84*/
  85
  86static const unsigned char* get_rev()
  87{
  88        struct commit *commit = NULL;
  89
  90        while (commit == NULL) {
  91                unsigned int mark;
  92                struct commit_list* parents;
  93
  94                if (rev_list == NULL || non_common_revs == 0)
  95                        return NULL;
  96
  97                commit = rev_list->item;
  98                if (!(commit->object.parsed))
  99                        parse_commit(commit);
 100                commit->object.flags |= POPPED;
 101                if (!(commit->object.flags & COMMON))
 102                        non_common_revs--;
 103        
 104                parents = commit->parents;
 105
 106                if (commit->object.flags & COMMON) {
 107                        /* do not send "have", and ignore ancestors */
 108                        commit = NULL;
 109                        mark = COMMON | SEEN;
 110                } else if (commit->object.flags & COMMON_REF)
 111                        /* send "have", and ignore ancestors */
 112                        mark = COMMON | SEEN;
 113                else
 114                        /* send "have", also for its ancestors */
 115                        mark = SEEN;
 116
 117                while (parents) {
 118                        if (!(parents->item->object.flags & SEEN))
 119                                rev_list_push(parents->item, mark);
 120                        if (mark & COMMON)
 121                                mark_common(parents->item, 1, 0);
 122                        parents = parents->next;
 123                }
 124
 125                rev_list = rev_list->next;
 126        }
 127
 128        return commit->object.sha1;
 129}
 130
 131static int find_common(int fd[2], unsigned char *result_sha1,
 132                       struct ref *refs)
 133{
 134        int fetching;
 135        int count = 0, flushes = 0, retval;
 136        const unsigned char *sha1;
 137
 138        for_each_ref(rev_list_insert_ref);
 139
 140        fetching = 0;
 141        for ( ; refs ; refs = refs->next) {
 142                unsigned char *remote = refs->old_sha1;
 143                struct object *o;
 144
 145                /*
 146                 * If that object is complete (i.e. it is an ancestor of a
 147                 * local ref), we tell them we have it but do not have to
 148                 * tell them about its ancestors, which they already know
 149                 * about.
 150                 *
 151                 * We use lookup_object here because we are only
 152                 * interested in the case we *know* the object is
 153                 * reachable and we have already scanned it.
 154                 */
 155                if (((o = lookup_object(remote)) != NULL) &&
 156                    (o->flags & COMPLETE)) {
 157                        o = deref_tag(o);
 158
 159                        if (o->type == commit_type) {
 160                                struct commit *commit = (struct commit *)o;
 161
 162                                rev_list_push(commit, COMMON_REF | SEEN);
 163
 164                                mark_common(commit, 1, 1);
 165                        }
 166                        continue;
 167                }
 168
 169                packet_write(fd[1], "want %s\n", sha1_to_hex(remote));
 170                fetching++;
 171        }
 172        packet_flush(fd[1]);
 173        if (!fetching)
 174                return 1;
 175
 176        flushes = 0;
 177        retval = -1;
 178        while ((sha1 = get_rev())) {
 179                packet_write(fd[1], "have %s\n", sha1_to_hex(sha1));
 180                if (verbose)
 181                        fprintf(stderr, "have %s\n", sha1_to_hex(sha1));
 182                if (!(31 & ++count)) {
 183                        packet_flush(fd[1]);
 184                        flushes++;
 185
 186                        /*
 187                         * We keep one window "ahead" of the other side, and
 188                         * will wait for an ACK only on the next one
 189                         */
 190                        if (count == 32)
 191                                continue;
 192                        if (get_ack(fd[0], result_sha1)) {
 193                                flushes = 0;
 194                                retval = 0;
 195                                if (verbose)
 196                                        fprintf(stderr, "got ack\n");
 197                                break;
 198                        }
 199                        flushes--;
 200                }
 201        }
 202
 203        packet_write(fd[1], "done\n");
 204        if (verbose)
 205                fprintf(stderr, "done\n");
 206        if (retval != 0)
 207                flushes++;
 208        while (flushes) {
 209                flushes--;
 210                if (get_ack(fd[0], result_sha1)) {
 211                        if (verbose)
 212                                fprintf(stderr, "got ack\n");
 213                        return 0;
 214                }
 215        }
 216        return retval;
 217}
 218
 219static struct commit_list *complete = NULL;
 220
 221static int mark_complete(const char *path, const unsigned char *sha1)
 222{
 223        struct object *o = parse_object(sha1);
 224
 225        while (o && o->type == tag_type) {
 226                struct tag *t = (struct tag *) o;
 227                if (!t->tagged)
 228                        break; /* broken repository */
 229                o->flags |= COMPLETE;
 230                o = parse_object(t->tagged->sha1);
 231        }
 232        if (o && o->type == commit_type) {
 233                struct commit *commit = (struct commit *)o;
 234                commit->object.flags |= COMPLETE;
 235                insert_by_date(commit, &complete);
 236        }
 237        return 0;
 238}
 239
 240static void mark_recent_complete_commits(unsigned long cutoff)
 241{
 242        while (complete && cutoff <= complete->item->date) {
 243                if (verbose)
 244                        fprintf(stderr, "Marking %s as complete\n",
 245                                sha1_to_hex(complete->item->object.sha1));
 246                pop_most_recent_commit(&complete, COMPLETE);
 247        }
 248}
 249
 250static int everything_local(struct ref *refs)
 251{
 252        struct ref *ref;
 253        int retval;
 254        unsigned long cutoff = 0;
 255
 256        track_object_refs = 0;
 257        save_commit_buffer = 0;
 258
 259        for (ref = refs; ref; ref = ref->next) {
 260                struct object *o;
 261
 262                o = parse_object(ref->old_sha1);
 263                if (!o)
 264                        continue;
 265
 266                /* We already have it -- which may mean that we were
 267                 * in sync with the other side at some time after
 268                 * that (it is OK if we guess wrong here).
 269                 */
 270                if (o->type == commit_type) {
 271                        struct commit *commit = (struct commit *)o;
 272                        if (!cutoff || cutoff < commit->date)
 273                                cutoff = commit->date;
 274                }
 275        }
 276
 277        for_each_ref(mark_complete);
 278        if (cutoff)
 279                mark_recent_complete_commits(cutoff);
 280
 281        for (retval = 1; refs ; refs = refs->next) {
 282                const unsigned char *remote = refs->old_sha1;
 283                unsigned char local[20];
 284                struct object *o;
 285
 286                o = parse_object(remote);
 287                if (!o || !(o->flags & COMPLETE)) {
 288                        retval = 0;
 289                        if (!verbose)
 290                                continue;
 291                        fprintf(stderr,
 292                                "want %s (%s)\n", sha1_to_hex(remote),
 293                                refs->name);
 294                        continue;
 295                }
 296
 297                memcpy(refs->new_sha1, local, 20);
 298                if (!verbose)
 299                        continue;
 300                fprintf(stderr,
 301                        "already have %s (%s)\n", sha1_to_hex(remote),
 302                        refs->name);
 303        }
 304        return retval;
 305}
 306
 307static int fetch_pack(int fd[2], int nr_match, char **match)
 308{
 309        struct ref *ref;
 310        unsigned char sha1[20];
 311        int status;
 312        pid_t pid;
 313
 314        get_remote_heads(fd[0], &ref, nr_match, match, 1);
 315        if (!ref) {
 316                packet_flush(fd[1]);
 317                die("no matching remote head");
 318        }
 319        if (everything_local(ref)) {
 320                packet_flush(fd[1]);
 321                goto all_done;
 322        }
 323        if (find_common(fd, sha1, ref) < 0)
 324                fprintf(stderr, "warning: no common commits\n");
 325        pid = fork();
 326        if (pid < 0)
 327                die("git-fetch-pack: unable to fork off git-unpack-objects");
 328        if (!pid) {
 329                dup2(fd[0], 0);
 330                close(fd[0]);
 331                close(fd[1]);
 332                execlp("git-unpack-objects", "git-unpack-objects",
 333                       quiet ? "-q" : NULL, NULL);
 334                die("git-unpack-objects exec failed");
 335        }
 336        close(fd[0]);
 337        close(fd[1]);
 338        while (waitpid(pid, &status, 0) < 0) {
 339                if (errno != EINTR)
 340                        die("waiting for git-unpack-objects: %s", strerror(errno));
 341        }
 342        if (WIFEXITED(status)) {
 343                int code = WEXITSTATUS(status);
 344                if (code)
 345                        die("git-unpack-objects died with error code %d", code);
 346all_done:
 347                while (ref) {
 348                        printf("%s %s\n",
 349                               sha1_to_hex(ref->old_sha1), ref->name);
 350                        ref = ref->next;
 351                }
 352                return 0;
 353        }
 354        if (WIFSIGNALED(status)) {
 355                int sig = WTERMSIG(status);
 356                die("git-unpack-objects died of signal %d", sig);
 357        }
 358        die("Sherlock Holmes! git-unpack-objects died of unnatural causes %d!", status);
 359}
 360
 361int main(int argc, char **argv)
 362{
 363        int i, ret, nr_heads;
 364        char *dest = NULL, **heads;
 365        int fd[2];
 366        pid_t pid;
 367
 368        nr_heads = 0;
 369        heads = NULL;
 370        for (i = 1; i < argc; i++) {
 371                char *arg = argv[i];
 372
 373                if (*arg == '-') {
 374                        if (!strncmp("--exec=", arg, 7)) {
 375                                exec = arg + 7;
 376                                continue;
 377                        }
 378                        if (!strcmp("-q", arg)) {
 379                                quiet = 1;
 380                                continue;
 381                        }
 382                        if (!strcmp("-v", arg)) {
 383                                verbose = 1;
 384                                continue;
 385                        }
 386                        usage(fetch_pack_usage);
 387                }
 388                dest = arg;
 389                heads = argv + i + 1;
 390                nr_heads = argc - i - 1;
 391                break;
 392        }
 393        if (!dest)
 394                usage(fetch_pack_usage);
 395        pid = git_connect(fd, dest, exec);
 396        if (pid < 0)
 397                return 1;
 398        ret = fetch_pack(fd, nr_heads, heads);
 399        close(fd[0]);
 400        close(fd[1]);
 401        finish_connect(pid);
 402        return ret;
 403}