bundle.con commit rebase -i: rewrite setup_reflog_action() in C (2c58483)
   1#include "cache.h"
   2#include "lockfile.h"
   3#include "bundle.h"
   4#include "object-store.h"
   5#include "object.h"
   6#include "commit.h"
   7#include "diff.h"
   8#include "revision.h"
   9#include "list-objects.h"
  10#include "run-command.h"
  11#include "refs.h"
  12#include "argv-array.h"
  13
  14static const char bundle_signature[] = "# v2 git bundle\n";
  15
  16static void add_to_ref_list(const struct object_id *oid, const char *name,
  17                struct ref_list *list)
  18{
  19        ALLOC_GROW(list->list, list->nr + 1, list->alloc);
  20        oidcpy(&list->list[list->nr].oid, oid);
  21        list->list[list->nr].name = xstrdup(name);
  22        list->nr++;
  23}
  24
  25static int parse_bundle_header(int fd, struct bundle_header *header,
  26                               const char *report_path)
  27{
  28        struct strbuf buf = STRBUF_INIT;
  29        int status = 0;
  30
  31        /* The bundle header begins with the signature */
  32        if (strbuf_getwholeline_fd(&buf, fd, '\n') ||
  33            strcmp(buf.buf, bundle_signature)) {
  34                if (report_path)
  35                        error(_("'%s' does not look like a v2 bundle file"),
  36                              report_path);
  37                status = -1;
  38                goto abort;
  39        }
  40
  41        /* The bundle header ends with an empty line */
  42        while (!strbuf_getwholeline_fd(&buf, fd, '\n') &&
  43               buf.len && buf.buf[0] != '\n') {
  44                struct object_id oid;
  45                int is_prereq = 0;
  46                const char *p;
  47
  48                if (*buf.buf == '-') {
  49                        is_prereq = 1;
  50                        strbuf_remove(&buf, 0, 1);
  51                }
  52                strbuf_rtrim(&buf);
  53
  54                /*
  55                 * Tip lines have object name, SP, and refname.
  56                 * Prerequisites have object name that is optionally
  57                 * followed by SP and subject line.
  58                 */
  59                if (parse_oid_hex(buf.buf, &oid, &p) ||
  60                    (*p && !isspace(*p)) ||
  61                    (!is_prereq && !*p)) {
  62                        if (report_path)
  63                                error(_("unrecognized header: %s%s (%d)"),
  64                                      (is_prereq ? "-" : ""), buf.buf, (int)buf.len);
  65                        status = -1;
  66                        break;
  67                } else {
  68                        if (is_prereq)
  69                                add_to_ref_list(&oid, "", &header->prerequisites);
  70                        else
  71                                add_to_ref_list(&oid, p + 1, &header->references);
  72                }
  73        }
  74
  75 abort:
  76        if (status) {
  77                close(fd);
  78                fd = -1;
  79        }
  80        strbuf_release(&buf);
  81        return fd;
  82}
  83
  84int read_bundle_header(const char *path, struct bundle_header *header)
  85{
  86        int fd = open(path, O_RDONLY);
  87
  88        if (fd < 0)
  89                return error(_("could not open '%s'"), path);
  90        return parse_bundle_header(fd, header, path);
  91}
  92
  93int is_bundle(const char *path, int quiet)
  94{
  95        struct bundle_header header;
  96        int fd = open(path, O_RDONLY);
  97
  98        if (fd < 0)
  99                return 0;
 100        memset(&header, 0, sizeof(header));
 101        fd = parse_bundle_header(fd, &header, quiet ? NULL : path);
 102        if (fd >= 0)
 103                close(fd);
 104        return (fd >= 0);
 105}
 106
 107static int list_refs(struct ref_list *r, int argc, const char **argv)
 108{
 109        int i;
 110
 111        for (i = 0; i < r->nr; i++) {
 112                if (argc > 1) {
 113                        int j;
 114                        for (j = 1; j < argc; j++)
 115                                if (!strcmp(r->list[i].name, argv[j]))
 116                                        break;
 117                        if (j == argc)
 118                                continue;
 119                }
 120                printf("%s %s\n", oid_to_hex(&r->list[i].oid),
 121                                r->list[i].name);
 122        }
 123        return 0;
 124}
 125
 126/* Remember to update object flag allocation in object.h */
 127#define PREREQ_MARK (1u<<16)
 128
 129int verify_bundle(struct bundle_header *header, int verbose)
 130{
 131        /*
 132         * Do fast check, then if any prereqs are missing then go line by line
 133         * to be verbose about the errors
 134         */
 135        struct ref_list *p = &header->prerequisites;
 136        struct rev_info revs;
 137        const char *argv[] = {NULL, "--all", NULL};
 138        struct commit *commit;
 139        int i, ret = 0, req_nr;
 140        const char *message = _("Repository lacks these prerequisite commits:");
 141
 142        init_revisions(&revs, NULL);
 143        for (i = 0; i < p->nr; i++) {
 144                struct ref_list_entry *e = p->list + i;
 145                struct object *o = parse_object(&e->oid);
 146                if (o) {
 147                        o->flags |= PREREQ_MARK;
 148                        add_pending_object(&revs, o, e->name);
 149                        continue;
 150                }
 151                if (++ret == 1)
 152                        error("%s", message);
 153                error("%s %s", oid_to_hex(&e->oid), e->name);
 154        }
 155        if (revs.pending.nr != p->nr)
 156                return ret;
 157        req_nr = revs.pending.nr;
 158        setup_revisions(2, argv, &revs, NULL);
 159
 160        if (prepare_revision_walk(&revs))
 161                die(_("revision walk setup failed"));
 162
 163        i = req_nr;
 164        while (i && (commit = get_revision(&revs)))
 165                if (commit->object.flags & PREREQ_MARK)
 166                        i--;
 167
 168        for (i = 0; i < p->nr; i++) {
 169                struct ref_list_entry *e = p->list + i;
 170                struct object *o = parse_object(&e->oid);
 171                assert(o); /* otherwise we'd have returned early */
 172                if (o->flags & SHOWN)
 173                        continue;
 174                if (++ret == 1)
 175                        error("%s", message);
 176                error("%s %s", oid_to_hex(&e->oid), e->name);
 177        }
 178
 179        /* Clean up objects used, as they will be reused. */
 180        for (i = 0; i < p->nr; i++) {
 181                struct ref_list_entry *e = p->list + i;
 182                commit = lookup_commit_reference_gently(&e->oid, 1);
 183                if (commit)
 184                        clear_commit_marks(commit, ALL_REV_FLAGS);
 185        }
 186
 187        if (verbose) {
 188                struct ref_list *r;
 189
 190                r = &header->references;
 191                printf_ln(Q_("The bundle contains this ref:",
 192                             "The bundle contains these %d refs:",
 193                             r->nr),
 194                          r->nr);
 195                list_refs(r, 0, NULL);
 196                r = &header->prerequisites;
 197                if (!r->nr) {
 198                        printf_ln(_("The bundle records a complete history."));
 199                } else {
 200                        printf_ln(Q_("The bundle requires this ref:",
 201                                     "The bundle requires these %d refs:",
 202                                     r->nr),
 203                                  r->nr);
 204                        list_refs(r, 0, NULL);
 205                }
 206        }
 207        return ret;
 208}
 209
 210int list_bundle_refs(struct bundle_header *header, int argc, const char **argv)
 211{
 212        return list_refs(&header->references, argc, argv);
 213}
 214
 215static int is_tag_in_date_range(struct object *tag, struct rev_info *revs)
 216{
 217        unsigned long size;
 218        enum object_type type;
 219        char *buf = NULL, *line, *lineend;
 220        timestamp_t date;
 221        int result = 1;
 222
 223        if (revs->max_age == -1 && revs->min_age == -1)
 224                goto out;
 225
 226        buf = read_object_file(&tag->oid, &type, &size);
 227        if (!buf)
 228                goto out;
 229        line = memmem(buf, size, "\ntagger ", 8);
 230        if (!line++)
 231                goto out;
 232        lineend = memchr(line, '\n', buf + size - line);
 233        line = memchr(line, '>', lineend ? lineend - line : buf + size - line);
 234        if (!line++)
 235                goto out;
 236        date = parse_timestamp(line, NULL, 10);
 237        result = (revs->max_age == -1 || revs->max_age < date) &&
 238                (revs->min_age == -1 || revs->min_age > date);
 239out:
 240        free(buf);
 241        return result;
 242}
 243
 244
 245/* Write the pack data to bundle_fd, then close it if it is > 1. */
 246static int write_pack_data(int bundle_fd, struct rev_info *revs)
 247{
 248        struct child_process pack_objects = CHILD_PROCESS_INIT;
 249        int i;
 250
 251        argv_array_pushl(&pack_objects.args,
 252                         "pack-objects", "--all-progress-implied",
 253                         "--stdout", "--thin", "--delta-base-offset",
 254                         NULL);
 255        pack_objects.in = -1;
 256        pack_objects.out = bundle_fd;
 257        pack_objects.git_cmd = 1;
 258        if (start_command(&pack_objects))
 259                return error(_("Could not spawn pack-objects"));
 260
 261        for (i = 0; i < revs->pending.nr; i++) {
 262                struct object *object = revs->pending.objects[i].item;
 263                if (object->flags & UNINTERESTING)
 264                        write_or_die(pack_objects.in, "^", 1);
 265                write_or_die(pack_objects.in, oid_to_hex(&object->oid), GIT_SHA1_HEXSZ);
 266                write_or_die(pack_objects.in, "\n", 1);
 267        }
 268        close(pack_objects.in);
 269        if (finish_command(&pack_objects))
 270                return error(_("pack-objects died"));
 271        return 0;
 272}
 273
 274static int compute_and_write_prerequisites(int bundle_fd,
 275                                           struct rev_info *revs,
 276                                           int argc, const char **argv)
 277{
 278        struct child_process rls = CHILD_PROCESS_INIT;
 279        struct strbuf buf = STRBUF_INIT;
 280        FILE *rls_fout;
 281        int i;
 282
 283        argv_array_pushl(&rls.args,
 284                         "rev-list", "--boundary", "--pretty=oneline",
 285                         NULL);
 286        for (i = 1; i < argc; i++)
 287                argv_array_push(&rls.args, argv[i]);
 288        rls.out = -1;
 289        rls.git_cmd = 1;
 290        if (start_command(&rls))
 291                return -1;
 292        rls_fout = xfdopen(rls.out, "r");
 293        while (strbuf_getwholeline(&buf, rls_fout, '\n') != EOF) {
 294                struct object_id oid;
 295                if (buf.len > 0 && buf.buf[0] == '-') {
 296                        write_or_die(bundle_fd, buf.buf, buf.len);
 297                        if (!get_oid_hex(buf.buf + 1, &oid)) {
 298                                struct object *object = parse_object_or_die(&oid,
 299                                                                            buf.buf);
 300                                object->flags |= UNINTERESTING;
 301                                add_pending_object(revs, object, buf.buf);
 302                        }
 303                } else if (!get_oid_hex(buf.buf, &oid)) {
 304                        struct object *object = parse_object_or_die(&oid,
 305                                                                    buf.buf);
 306                        object->flags |= SHOWN;
 307                }
 308        }
 309        strbuf_release(&buf);
 310        fclose(rls_fout);
 311        if (finish_command(&rls))
 312                return error(_("rev-list died"));
 313        return 0;
 314}
 315
 316/*
 317 * Write out bundle refs based on the tips already
 318 * parsed into revs.pending. As a side effect, may
 319 * manipulate revs.pending to include additional
 320 * necessary objects (like tags).
 321 *
 322 * Returns the number of refs written, or negative
 323 * on error.
 324 */
 325static int write_bundle_refs(int bundle_fd, struct rev_info *revs)
 326{
 327        int i;
 328        int ref_count = 0;
 329
 330        for (i = 0; i < revs->pending.nr; i++) {
 331                struct object_array_entry *e = revs->pending.objects + i;
 332                struct object_id oid;
 333                char *ref;
 334                const char *display_ref;
 335                int flag;
 336
 337                if (e->item->flags & UNINTERESTING)
 338                        continue;
 339                if (dwim_ref(e->name, strlen(e->name), &oid, &ref) != 1)
 340                        goto skip_write_ref;
 341                if (read_ref_full(e->name, RESOLVE_REF_READING, &oid, &flag))
 342                        flag = 0;
 343                display_ref = (flag & REF_ISSYMREF) ? e->name : ref;
 344
 345                if (e->item->type == OBJ_TAG &&
 346                                !is_tag_in_date_range(e->item, revs)) {
 347                        e->item->flags |= UNINTERESTING;
 348                        goto skip_write_ref;
 349                }
 350
 351                /*
 352                 * Make sure the refs we wrote out is correct; --max-count and
 353                 * other limiting options could have prevented all the tips
 354                 * from getting output.
 355                 *
 356                 * Non commit objects such as tags and blobs do not have
 357                 * this issue as they are not affected by those extra
 358                 * constraints.
 359                 */
 360                if (!(e->item->flags & SHOWN) && e->item->type == OBJ_COMMIT) {
 361                        warning(_("ref '%s' is excluded by the rev-list options"),
 362                                e->name);
 363                        goto skip_write_ref;
 364                }
 365                /*
 366                 * If you run "git bundle create bndl v1.0..v2.0", the
 367                 * name of the positive ref is "v2.0" but that is the
 368                 * commit that is referenced by the tag, and not the tag
 369                 * itself.
 370                 */
 371                if (oidcmp(&oid, &e->item->oid)) {
 372                        /*
 373                         * Is this the positive end of a range expressed
 374                         * in terms of a tag (e.g. v2.0 from the range
 375                         * "v1.0..v2.0")?
 376                         */
 377                        struct commit *one = lookup_commit_reference(&oid);
 378                        struct object *obj;
 379
 380                        if (e->item == &(one->object)) {
 381                                /*
 382                                 * Need to include e->name as an
 383                                 * independent ref to the pack-objects
 384                                 * input, so that the tag is included
 385                                 * in the output; otherwise we would
 386                                 * end up triggering "empty bundle"
 387                                 * error.
 388                                 */
 389                                obj = parse_object_or_die(&oid, e->name);
 390                                obj->flags |= SHOWN;
 391                                add_pending_object(revs, obj, e->name);
 392                        }
 393                        goto skip_write_ref;
 394                }
 395
 396                ref_count++;
 397                write_or_die(bundle_fd, oid_to_hex(&e->item->oid), 40);
 398                write_or_die(bundle_fd, " ", 1);
 399                write_or_die(bundle_fd, display_ref, strlen(display_ref));
 400                write_or_die(bundle_fd, "\n", 1);
 401 skip_write_ref:
 402                free(ref);
 403        }
 404
 405        /* end header */
 406        write_or_die(bundle_fd, "\n", 1);
 407        return ref_count;
 408}
 409
 410int create_bundle(struct bundle_header *header, const char *path,
 411                  int argc, const char **argv)
 412{
 413        struct lock_file lock = LOCK_INIT;
 414        int bundle_fd = -1;
 415        int bundle_to_stdout;
 416        int ref_count = 0;
 417        struct rev_info revs;
 418
 419        bundle_to_stdout = !strcmp(path, "-");
 420        if (bundle_to_stdout)
 421                bundle_fd = 1;
 422        else {
 423                bundle_fd = hold_lock_file_for_update(&lock, path,
 424                                                      LOCK_DIE_ON_ERROR);
 425
 426                /*
 427                 * write_pack_data() will close the fd passed to it,
 428                 * but commit_lock_file() will also try to close the
 429                 * lockfile's fd. So make a copy of the file
 430                 * descriptor to avoid trying to close it twice.
 431                 */
 432                bundle_fd = dup(bundle_fd);
 433                if (bundle_fd < 0)
 434                        die_errno("unable to dup file descriptor");
 435        }
 436
 437        /* write signature */
 438        write_or_die(bundle_fd, bundle_signature, strlen(bundle_signature));
 439
 440        /* init revs to list objects for pack-objects later */
 441        save_commit_buffer = 0;
 442        init_revisions(&revs, NULL);
 443
 444        /* write prerequisites */
 445        if (compute_and_write_prerequisites(bundle_fd, &revs, argc, argv))
 446                goto err;
 447
 448        argc = setup_revisions(argc, argv, &revs, NULL);
 449
 450        if (argc > 1) {
 451                error(_("unrecognized argument: %s"), argv[1]);
 452                goto err;
 453        }
 454
 455        object_array_remove_duplicates(&revs.pending);
 456
 457        ref_count = write_bundle_refs(bundle_fd, &revs);
 458        if (!ref_count)
 459                die(_("Refusing to create empty bundle."));
 460        else if (ref_count < 0)
 461                goto err;
 462
 463        /* write pack */
 464        if (write_pack_data(bundle_fd, &revs)) {
 465                bundle_fd = -1; /* already closed by the above call */
 466                goto err;
 467        }
 468
 469        if (!bundle_to_stdout) {
 470                if (commit_lock_file(&lock))
 471                        die_errno(_("cannot create '%s'"), path);
 472        }
 473        return 0;
 474err:
 475        if (!bundle_to_stdout) {
 476                if (0 <= bundle_fd)
 477                        close(bundle_fd);
 478                rollback_lock_file(&lock);
 479        }
 480        return -1;
 481}
 482
 483int unbundle(struct bundle_header *header, int bundle_fd, int flags)
 484{
 485        const char *argv_index_pack[] = {"index-pack",
 486                                         "--fix-thin", "--stdin", NULL, NULL};
 487        struct child_process ip = CHILD_PROCESS_INIT;
 488
 489        if (flags & BUNDLE_VERBOSE)
 490                argv_index_pack[3] = "-v";
 491
 492        if (verify_bundle(header, 0))
 493                return -1;
 494        ip.argv = argv_index_pack;
 495        ip.in = bundle_fd;
 496        ip.no_stdout = 1;
 497        ip.git_cmd = 1;
 498        if (run_command(&ip))
 499                return error(_("index-pack died"));
 500        return 0;
 501}