builtin-rev-list.con commit revision --simplify-merges: use decoration instead of commit->util field (faf0156)
   1#include "cache.h"
   2#include "refs.h"
   3#include "tag.h"
   4#include "commit.h"
   5#include "tree.h"
   6#include "blob.h"
   7#include "tree-walk.h"
   8#include "diff.h"
   9#include "revision.h"
  10#include "list-objects.h"
  11#include "builtin.h"
  12#include "log-tree.h"
  13#include "graph.h"
  14
  15/* bits #0-15 in revision.h */
  16
  17#define COUNTED         (1u<<16)
  18
  19static const char rev_list_usage[] =
  20"git rev-list [OPTION] <commit-id>... [ -- paths... ]\n"
  21"  limiting output:\n"
  22"    --max-count=nr\n"
  23"    --max-age=epoch\n"
  24"    --min-age=epoch\n"
  25"    --sparse\n"
  26"    --no-merges\n"
  27"    --remove-empty\n"
  28"    --all\n"
  29"    --branches\n"
  30"    --tags\n"
  31"    --remotes\n"
  32"    --stdin\n"
  33"    --quiet\n"
  34"  ordering output:\n"
  35"    --topo-order\n"
  36"    --date-order\n"
  37"    --reverse\n"
  38"  formatting output:\n"
  39"    --parents\n"
  40"    --children\n"
  41"    --objects | --objects-edge\n"
  42"    --unpacked\n"
  43"    --header | --pretty\n"
  44"    --abbrev=nr | --no-abbrev\n"
  45"    --abbrev-commit\n"
  46"    --left-right\n"
  47"  special purpose:\n"
  48"    --bisect\n"
  49"    --bisect-vars\n"
  50"    --bisect-all"
  51;
  52
  53static struct rev_info revs;
  54
  55static int bisect_list;
  56static int show_timestamp;
  57static int hdr_termination;
  58static const char *header_prefix;
  59
  60static void finish_commit(struct commit *commit);
  61static void show_commit(struct commit *commit)
  62{
  63        graph_show_commit(revs.graph);
  64
  65        if (show_timestamp)
  66                printf("%lu ", commit->date);
  67        if (header_prefix)
  68                fputs(header_prefix, stdout);
  69
  70        if (!revs.graph) {
  71                if (commit->object.flags & BOUNDARY)
  72                        putchar('-');
  73                else if (commit->object.flags & UNINTERESTING)
  74                        putchar('^');
  75                else if (revs.left_right) {
  76                        if (commit->object.flags & SYMMETRIC_LEFT)
  77                                putchar('<');
  78                        else
  79                                putchar('>');
  80                }
  81        }
  82        if (revs.abbrev_commit && revs.abbrev)
  83                fputs(find_unique_abbrev(commit->object.sha1, revs.abbrev),
  84                      stdout);
  85        else
  86                fputs(sha1_to_hex(commit->object.sha1), stdout);
  87        if (revs.print_parents) {
  88                struct commit_list *parents = commit->parents;
  89                while (parents) {
  90                        printf(" %s", sha1_to_hex(parents->item->object.sha1));
  91                        parents = parents->next;
  92                }
  93        }
  94        if (revs.children.name) {
  95                struct commit_list *children;
  96
  97                children = lookup_decoration(&revs.children, &commit->object);
  98                while (children) {
  99                        printf(" %s", sha1_to_hex(children->item->object.sha1));
 100                        children = children->next;
 101                }
 102        }
 103        show_decorations(commit);
 104        if (revs.commit_format == CMIT_FMT_ONELINE)
 105                putchar(' ');
 106        else
 107                putchar('\n');
 108
 109        if (revs.verbose_header && commit->buffer) {
 110                struct strbuf buf;
 111                strbuf_init(&buf, 0);
 112                pretty_print_commit(revs.commit_format, commit,
 113                                    &buf, revs.abbrev, NULL, NULL,
 114                                    revs.date_mode, 0);
 115                if (revs.graph) {
 116                        if (buf.len) {
 117                                if (revs.commit_format != CMIT_FMT_ONELINE)
 118                                        graph_show_oneline(revs.graph);
 119
 120                                graph_show_commit_msg(revs.graph, &buf);
 121
 122                                /*
 123                                 * Add a newline after the commit message.
 124                                 *
 125                                 * Usually, this newline produces a blank
 126                                 * padding line between entries, in which case
 127                                 * we need to add graph padding on this line.
 128                                 *
 129                                 * However, the commit message may not end in a
 130                                 * newline.  In this case the newline simply
 131                                 * ends the last line of the commit message,
 132                                 * and we don't need any graph output.  (This
 133                                 * always happens with CMIT_FMT_ONELINE, and it
 134                                 * happens with CMIT_FMT_USERFORMAT when the
 135                                 * format doesn't explicitly end in a newline.)
 136                                 */
 137                                if (buf.len && buf.buf[buf.len - 1] == '\n')
 138                                        graph_show_padding(revs.graph);
 139                                putchar('\n');
 140                        } else {
 141                                /*
 142                                 * If the message buffer is empty, just show
 143                                 * the rest of the graph output for this
 144                                 * commit.
 145                                 */
 146                                if (graph_show_remainder(revs.graph))
 147                                        putchar('\n');
 148                        }
 149                } else {
 150                        if (buf.len)
 151                                printf("%s%c", buf.buf, hdr_termination);
 152                }
 153                strbuf_release(&buf);
 154        } else {
 155                if (graph_show_remainder(revs.graph))
 156                        putchar('\n');
 157        }
 158        maybe_flush_or_die(stdout, "stdout");
 159        finish_commit(commit);
 160}
 161
 162static void finish_commit(struct commit *commit)
 163{
 164        if (commit->parents) {
 165                free_commit_list(commit->parents);
 166                commit->parents = NULL;
 167        }
 168        free(commit->buffer);
 169        commit->buffer = NULL;
 170}
 171
 172static void finish_object(struct object_array_entry *p)
 173{
 174        if (p->item->type == OBJ_BLOB && !has_sha1_file(p->item->sha1))
 175                die("missing blob object '%s'", sha1_to_hex(p->item->sha1));
 176}
 177
 178static void show_object(struct object_array_entry *p)
 179{
 180        /* An object with name "foo\n0000000..." can be used to
 181         * confuse downstream git-pack-objects very badly.
 182         */
 183        const char *ep = strchr(p->name, '\n');
 184
 185        finish_object(p);
 186        if (ep) {
 187                printf("%s %.*s\n", sha1_to_hex(p->item->sha1),
 188                       (int) (ep - p->name),
 189                       p->name);
 190        }
 191        else
 192                printf("%s %s\n", sha1_to_hex(p->item->sha1), p->name);
 193}
 194
 195static void show_edge(struct commit *commit)
 196{
 197        printf("-%s\n", sha1_to_hex(commit->object.sha1));
 198}
 199
 200/*
 201 * This is a truly stupid algorithm, but it's only
 202 * used for bisection, and we just don't care enough.
 203 *
 204 * We care just barely enough to avoid recursing for
 205 * non-merge entries.
 206 */
 207static int count_distance(struct commit_list *entry)
 208{
 209        int nr = 0;
 210
 211        while (entry) {
 212                struct commit *commit = entry->item;
 213                struct commit_list *p;
 214
 215                if (commit->object.flags & (UNINTERESTING | COUNTED))
 216                        break;
 217                if (!(commit->object.flags & TREESAME))
 218                        nr++;
 219                commit->object.flags |= COUNTED;
 220                p = commit->parents;
 221                entry = p;
 222                if (p) {
 223                        p = p->next;
 224                        while (p) {
 225                                nr += count_distance(p);
 226                                p = p->next;
 227                        }
 228                }
 229        }
 230
 231        return nr;
 232}
 233
 234static void clear_distance(struct commit_list *list)
 235{
 236        while (list) {
 237                struct commit *commit = list->item;
 238                commit->object.flags &= ~COUNTED;
 239                list = list->next;
 240        }
 241}
 242
 243#define DEBUG_BISECT 0
 244
 245static inline int weight(struct commit_list *elem)
 246{
 247        return *((int*)(elem->item->util));
 248}
 249
 250static inline void weight_set(struct commit_list *elem, int weight)
 251{
 252        *((int*)(elem->item->util)) = weight;
 253}
 254
 255static int count_interesting_parents(struct commit *commit)
 256{
 257        struct commit_list *p;
 258        int count;
 259
 260        for (count = 0, p = commit->parents; p; p = p->next) {
 261                if (p->item->object.flags & UNINTERESTING)
 262                        continue;
 263                count++;
 264        }
 265        return count;
 266}
 267
 268static inline int halfway(struct commit_list *p, int nr)
 269{
 270        /*
 271         * Don't short-cut something we are not going to return!
 272         */
 273        if (p->item->object.flags & TREESAME)
 274                return 0;
 275        if (DEBUG_BISECT)
 276                return 0;
 277        /*
 278         * 2 and 3 are halfway of 5.
 279         * 3 is halfway of 6 but 2 and 4 are not.
 280         */
 281        switch (2 * weight(p) - nr) {
 282        case -1: case 0: case 1:
 283                return 1;
 284        default:
 285                return 0;
 286        }
 287}
 288
 289#if !DEBUG_BISECT
 290#define show_list(a,b,c,d) do { ; } while (0)
 291#else
 292static void show_list(const char *debug, int counted, int nr,
 293                      struct commit_list *list)
 294{
 295        struct commit_list *p;
 296
 297        fprintf(stderr, "%s (%d/%d)\n", debug, counted, nr);
 298
 299        for (p = list; p; p = p->next) {
 300                struct commit_list *pp;
 301                struct commit *commit = p->item;
 302                unsigned flags = commit->object.flags;
 303                enum object_type type;
 304                unsigned long size;
 305                char *buf = read_sha1_file(commit->object.sha1, &type, &size);
 306                char *ep, *sp;
 307
 308                fprintf(stderr, "%c%c%c ",
 309                        (flags & TREESAME) ? ' ' : 'T',
 310                        (flags & UNINTERESTING) ? 'U' : ' ',
 311                        (flags & COUNTED) ? 'C' : ' ');
 312                if (commit->util)
 313                        fprintf(stderr, "%3d", weight(p));
 314                else
 315                        fprintf(stderr, "---");
 316                fprintf(stderr, " %.*s", 8, sha1_to_hex(commit->object.sha1));
 317                for (pp = commit->parents; pp; pp = pp->next)
 318                        fprintf(stderr, " %.*s", 8,
 319                                sha1_to_hex(pp->item->object.sha1));
 320
 321                sp = strstr(buf, "\n\n");
 322                if (sp) {
 323                        sp += 2;
 324                        for (ep = sp; *ep && *ep != '\n'; ep++)
 325                                ;
 326                        fprintf(stderr, " %.*s", (int)(ep - sp), sp);
 327                }
 328                fprintf(stderr, "\n");
 329        }
 330}
 331#endif /* DEBUG_BISECT */
 332
 333static struct commit_list *best_bisection(struct commit_list *list, int nr)
 334{
 335        struct commit_list *p, *best;
 336        int best_distance = -1;
 337
 338        best = list;
 339        for (p = list; p; p = p->next) {
 340                int distance;
 341                unsigned flags = p->item->object.flags;
 342
 343                if (flags & TREESAME)
 344                        continue;
 345                distance = weight(p);
 346                if (nr - distance < distance)
 347                        distance = nr - distance;
 348                if (distance > best_distance) {
 349                        best = p;
 350                        best_distance = distance;
 351                }
 352        }
 353
 354        return best;
 355}
 356
 357struct commit_dist {
 358        struct commit *commit;
 359        int distance;
 360};
 361
 362static int compare_commit_dist(const void *a_, const void *b_)
 363{
 364        struct commit_dist *a, *b;
 365
 366        a = (struct commit_dist *)a_;
 367        b = (struct commit_dist *)b_;
 368        if (a->distance != b->distance)
 369                return b->distance - a->distance; /* desc sort */
 370        return hashcmp(a->commit->object.sha1, b->commit->object.sha1);
 371}
 372
 373static struct commit_list *best_bisection_sorted(struct commit_list *list, int nr)
 374{
 375        struct commit_list *p;
 376        struct commit_dist *array = xcalloc(nr, sizeof(*array));
 377        int cnt, i;
 378
 379        for (p = list, cnt = 0; p; p = p->next) {
 380                int distance;
 381                unsigned flags = p->item->object.flags;
 382
 383                if (flags & TREESAME)
 384                        continue;
 385                distance = weight(p);
 386                if (nr - distance < distance)
 387                        distance = nr - distance;
 388                array[cnt].commit = p->item;
 389                array[cnt].distance = distance;
 390                cnt++;
 391        }
 392        qsort(array, cnt, sizeof(*array), compare_commit_dist);
 393        for (p = list, i = 0; i < cnt; i++) {
 394                struct name_decoration *r = xmalloc(sizeof(*r) + 100);
 395                struct object *obj = &(array[i].commit->object);
 396
 397                sprintf(r->name, "dist=%d", array[i].distance);
 398                r->next = add_decoration(&name_decoration, obj, r);
 399                p->item = array[i].commit;
 400                p = p->next;
 401        }
 402        if (p)
 403                p->next = NULL;
 404        free(array);
 405        return list;
 406}
 407
 408/*
 409 * zero or positive weight is the number of interesting commits it can
 410 * reach, including itself.  Especially, weight = 0 means it does not
 411 * reach any tree-changing commits (e.g. just above uninteresting one
 412 * but traversal is with pathspec).
 413 *
 414 * weight = -1 means it has one parent and its distance is yet to
 415 * be computed.
 416 *
 417 * weight = -2 means it has more than one parent and its distance is
 418 * unknown.  After running count_distance() first, they will get zero
 419 * or positive distance.
 420 */
 421static struct commit_list *do_find_bisection(struct commit_list *list,
 422                                             int nr, int *weights,
 423                                             int find_all)
 424{
 425        int n, counted;
 426        struct commit_list *p;
 427
 428        counted = 0;
 429
 430        for (n = 0, p = list; p; p = p->next) {
 431                struct commit *commit = p->item;
 432                unsigned flags = commit->object.flags;
 433
 434                p->item->util = &weights[n++];
 435                switch (count_interesting_parents(commit)) {
 436                case 0:
 437                        if (!(flags & TREESAME)) {
 438                                weight_set(p, 1);
 439                                counted++;
 440                                show_list("bisection 2 count one",
 441                                          counted, nr, list);
 442                        }
 443                        /*
 444                         * otherwise, it is known not to reach any
 445                         * tree-changing commit and gets weight 0.
 446                         */
 447                        break;
 448                case 1:
 449                        weight_set(p, -1);
 450                        break;
 451                default:
 452                        weight_set(p, -2);
 453                        break;
 454                }
 455        }
 456
 457        show_list("bisection 2 initialize", counted, nr, list);
 458
 459        /*
 460         * If you have only one parent in the resulting set
 461         * then you can reach one commit more than that parent
 462         * can reach.  So we do not have to run the expensive
 463         * count_distance() for single strand of pearls.
 464         *
 465         * However, if you have more than one parents, you cannot
 466         * just add their distance and one for yourself, since
 467         * they usually reach the same ancestor and you would
 468         * end up counting them twice that way.
 469         *
 470         * So we will first count distance of merges the usual
 471         * way, and then fill the blanks using cheaper algorithm.
 472         */
 473        for (p = list; p; p = p->next) {
 474                if (p->item->object.flags & UNINTERESTING)
 475                        continue;
 476                if (weight(p) != -2)
 477                        continue;
 478                weight_set(p, count_distance(p));
 479                clear_distance(list);
 480
 481                /* Does it happen to be at exactly half-way? */
 482                if (!find_all && halfway(p, nr))
 483                        return p;
 484                counted++;
 485        }
 486
 487        show_list("bisection 2 count_distance", counted, nr, list);
 488
 489        while (counted < nr) {
 490                for (p = list; p; p = p->next) {
 491                        struct commit_list *q;
 492                        unsigned flags = p->item->object.flags;
 493
 494                        if (0 <= weight(p))
 495                                continue;
 496                        for (q = p->item->parents; q; q = q->next) {
 497                                if (q->item->object.flags & UNINTERESTING)
 498                                        continue;
 499                                if (0 <= weight(q))
 500                                        break;
 501                        }
 502                        if (!q)
 503                                continue;
 504
 505                        /*
 506                         * weight for p is unknown but q is known.
 507                         * add one for p itself if p is to be counted,
 508                         * otherwise inherit it from q directly.
 509                         */
 510                        if (!(flags & TREESAME)) {
 511                                weight_set(p, weight(q)+1);
 512                                counted++;
 513                                show_list("bisection 2 count one",
 514                                          counted, nr, list);
 515                        }
 516                        else
 517                                weight_set(p, weight(q));
 518
 519                        /* Does it happen to be at exactly half-way? */
 520                        if (!find_all && halfway(p, nr))
 521                                return p;
 522                }
 523        }
 524
 525        show_list("bisection 2 counted all", counted, nr, list);
 526
 527        if (!find_all)
 528                return best_bisection(list, nr);
 529        else
 530                return best_bisection_sorted(list, nr);
 531}
 532
 533static struct commit_list *find_bisection(struct commit_list *list,
 534                                          int *reaches, int *all,
 535                                          int find_all)
 536{
 537        int nr, on_list;
 538        struct commit_list *p, *best, *next, *last;
 539        int *weights;
 540
 541        show_list("bisection 2 entry", 0, 0, list);
 542
 543        /*
 544         * Count the number of total and tree-changing items on the
 545         * list, while reversing the list.
 546         */
 547        for (nr = on_list = 0, last = NULL, p = list;
 548             p;
 549             p = next) {
 550                unsigned flags = p->item->object.flags;
 551
 552                next = p->next;
 553                if (flags & UNINTERESTING)
 554                        continue;
 555                p->next = last;
 556                last = p;
 557                if (!(flags & TREESAME))
 558                        nr++;
 559                on_list++;
 560        }
 561        list = last;
 562        show_list("bisection 2 sorted", 0, nr, list);
 563
 564        *all = nr;
 565        weights = xcalloc(on_list, sizeof(*weights));
 566
 567        /* Do the real work of finding bisection commit. */
 568        best = do_find_bisection(list, nr, weights, find_all);
 569        if (best) {
 570                if (!find_all)
 571                        best->next = NULL;
 572                *reaches = weight(best);
 573        }
 574        free(weights);
 575        return best;
 576}
 577
 578int cmd_rev_list(int argc, const char **argv, const char *prefix)
 579{
 580        struct commit_list *list;
 581        int i;
 582        int read_from_stdin = 0;
 583        int bisect_show_vars = 0;
 584        int bisect_find_all = 0;
 585        int quiet = 0;
 586
 587        git_config(git_default_config, NULL);
 588        init_revisions(&revs, prefix);
 589        revs.abbrev = 0;
 590        revs.commit_format = CMIT_FMT_UNSPECIFIED;
 591        argc = setup_revisions(argc, argv, &revs, NULL);
 592
 593        quiet = DIFF_OPT_TST(&revs.diffopt, QUIET);
 594        for (i = 1 ; i < argc; i++) {
 595                const char *arg = argv[i];
 596
 597                if (!strcmp(arg, "--header")) {
 598                        revs.verbose_header = 1;
 599                        continue;
 600                }
 601                if (!strcmp(arg, "--timestamp")) {
 602                        show_timestamp = 1;
 603                        continue;
 604                }
 605                if (!strcmp(arg, "--bisect")) {
 606                        bisect_list = 1;
 607                        continue;
 608                }
 609                if (!strcmp(arg, "--bisect-all")) {
 610                        bisect_list = 1;
 611                        bisect_find_all = 1;
 612                        continue;
 613                }
 614                if (!strcmp(arg, "--bisect-vars")) {
 615                        bisect_list = 1;
 616                        bisect_show_vars = 1;
 617                        continue;
 618                }
 619                if (!strcmp(arg, "--stdin")) {
 620                        if (read_from_stdin++)
 621                                die("--stdin given twice?");
 622                        read_revisions_from_stdin(&revs);
 623                        continue;
 624                }
 625                usage(rev_list_usage);
 626
 627        }
 628        if (revs.commit_format != CMIT_FMT_UNSPECIFIED) {
 629                /* The command line has a --pretty  */
 630                hdr_termination = '\n';
 631                if (revs.commit_format == CMIT_FMT_ONELINE)
 632                        header_prefix = "";
 633                else
 634                        header_prefix = "commit ";
 635        }
 636        else if (revs.verbose_header)
 637                /* Only --header was specified */
 638                revs.commit_format = CMIT_FMT_RAW;
 639
 640        list = revs.commits;
 641
 642        if ((!list &&
 643             (!(revs.tag_objects||revs.tree_objects||revs.blob_objects) &&
 644              !revs.pending.nr)) ||
 645            revs.diff)
 646                usage(rev_list_usage);
 647
 648        save_commit_buffer = revs.verbose_header || revs.grep_filter;
 649        if (bisect_list)
 650                revs.limited = 1;
 651
 652        if (prepare_revision_walk(&revs))
 653                die("revision walk setup failed");
 654        if (revs.tree_objects)
 655                mark_edges_uninteresting(revs.commits, &revs, show_edge);
 656
 657        if (bisect_list) {
 658                int reaches = reaches, all = all;
 659
 660                revs.commits = find_bisection(revs.commits, &reaches, &all,
 661                                              bisect_find_all);
 662                if (bisect_show_vars) {
 663                        int cnt;
 664                        char hex[41];
 665                        if (!revs.commits)
 666                                return 1;
 667                        /*
 668                         * revs.commits can reach "reaches" commits among
 669                         * "all" commits.  If it is good, then there are
 670                         * (all-reaches) commits left to be bisected.
 671                         * On the other hand, if it is bad, then the set
 672                         * to bisect is "reaches".
 673                         * A bisect set of size N has (N-1) commits further
 674                         * to test, as we already know one bad one.
 675                         */
 676                        cnt = all - reaches;
 677                        if (cnt < reaches)
 678                                cnt = reaches;
 679                        strcpy(hex, sha1_to_hex(revs.commits->item->object.sha1));
 680
 681                        if (bisect_find_all) {
 682                                traverse_commit_list(&revs, show_commit, show_object);
 683                                printf("------\n");
 684                        }
 685
 686                        printf("bisect_rev=%s\n"
 687                               "bisect_nr=%d\n"
 688                               "bisect_good=%d\n"
 689                               "bisect_bad=%d\n"
 690                               "bisect_all=%d\n",
 691                               hex,
 692                               cnt - 1,
 693                               all - reaches - 1,
 694                               reaches - 1,
 695                               all);
 696                        return 0;
 697                }
 698        }
 699
 700        traverse_commit_list(&revs,
 701                quiet ? finish_commit : show_commit,
 702                quiet ? finish_object : show_object);
 703
 704        return 0;
 705}