d1c52a6a9b599d6df62508394e6e8bb1cc10a126
   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 "epoch.h"
   8#include "diff.h"
   9#include "revision.h"
  10
  11/* bits #0 and #1 in revision.h */
  12
  13#define COUNTED         (1u << 2)
  14#define SHOWN           (1u << 3)
  15#define TREECHANGE      (1u << 4)
  16#define TMP_MARK        (1u << 5) /* for isolated cases; clean after use */
  17
  18static const char rev_list_usage[] =
  19"git-rev-list [OPTION] <commit-id>... [ -- paths... ]\n"
  20"  limiting output:\n"
  21"    --max-count=nr\n"
  22"    --max-age=epoch\n"
  23"    --min-age=epoch\n"
  24"    --sparse\n"
  25"    --no-merges\n"
  26"    --remove-empty\n"
  27"    --all\n"
  28"  ordering output:\n"
  29"    --merge-order [ --show-breaks ]\n"
  30"    --topo-order\n"
  31"    --date-order\n"
  32"  formatting output:\n"
  33"    --parents\n"
  34"    --objects | --objects-edge\n"
  35"    --unpacked\n"
  36"    --header | --pretty\n"
  37"    --abbrev=nr | --no-abbrev\n"
  38"  special purpose:\n"
  39"    --bisect"
  40;
  41
  42struct rev_info revs;
  43
  44static int unpacked = 0;
  45static int bisect_list = 0;
  46static int verbose_header = 0;
  47static int abbrev = DEFAULT_ABBREV;
  48static int show_parents = 0;
  49static int hdr_termination = 0;
  50static const char *commit_prefix = "";
  51static enum cmit_fmt commit_format = CMIT_FMT_RAW;
  52static int merge_order = 0;
  53static int show_breaks = 0;
  54static int stop_traversal = 0;
  55static int no_merges = 0;
  56
  57static void show_commit(struct commit *commit)
  58{
  59        commit->object.flags |= SHOWN;
  60        if (show_breaks) {
  61                commit_prefix = "| ";
  62                if (commit->object.flags & DISCONTINUITY) {
  63                        commit_prefix = "^ ";     
  64                } else if (commit->object.flags & BOUNDARY) {
  65                        commit_prefix = "= ";
  66                } 
  67        }                       
  68        printf("%s%s", commit_prefix, sha1_to_hex(commit->object.sha1));
  69        if (show_parents) {
  70                struct commit_list *parents = commit->parents;
  71                while (parents) {
  72                        struct object *o = &(parents->item->object);
  73                        parents = parents->next;
  74                        if (o->flags & TMP_MARK)
  75                                continue;
  76                        printf(" %s", sha1_to_hex(o->sha1));
  77                        o->flags |= TMP_MARK;
  78                }
  79                /* TMP_MARK is a general purpose flag that can
  80                 * be used locally, but the user should clean
  81                 * things up after it is done with them.
  82                 */
  83                for (parents = commit->parents;
  84                     parents;
  85                     parents = parents->next)
  86                        parents->item->object.flags &= ~TMP_MARK;
  87        }
  88        if (commit_format == CMIT_FMT_ONELINE)
  89                putchar(' ');
  90        else
  91                putchar('\n');
  92
  93        if (verbose_header) {
  94                static char pretty_header[16384];
  95                pretty_print_commit(commit_format, commit, ~0, pretty_header, sizeof(pretty_header), abbrev);
  96                printf("%s%c", pretty_header, hdr_termination);
  97        }
  98        fflush(stdout);
  99}
 100
 101static int rewrite_one(struct commit **pp)
 102{
 103        for (;;) {
 104                struct commit *p = *pp;
 105                if (p->object.flags & (TREECHANGE | UNINTERESTING))
 106                        return 0;
 107                if (!p->parents)
 108                        return -1;
 109                *pp = p->parents->item;
 110        }
 111}
 112
 113static void rewrite_parents(struct commit *commit)
 114{
 115        struct commit_list **pp = &commit->parents;
 116        while (*pp) {
 117                struct commit_list *parent = *pp;
 118                if (rewrite_one(&parent->item) < 0) {
 119                        *pp = parent->next;
 120                        continue;
 121                }
 122                pp = &parent->next;
 123        }
 124}
 125
 126static int filter_commit(struct commit * commit)
 127{
 128        if (stop_traversal && (commit->object.flags & BOUNDARY))
 129                return STOP;
 130        if (commit->object.flags & (UNINTERESTING|SHOWN))
 131                return CONTINUE;
 132        if (revs.min_age != -1 && (commit->date > revs.min_age))
 133                return CONTINUE;
 134        if (revs.max_age != -1 && (commit->date < revs.max_age)) {
 135                stop_traversal=1;
 136                return CONTINUE;
 137        }
 138        if (no_merges && (commit->parents && commit->parents->next))
 139                return CONTINUE;
 140        if (revs.paths && revs.dense) {
 141                if (!(commit->object.flags & TREECHANGE))
 142                        return CONTINUE;
 143                rewrite_parents(commit);
 144        }
 145        return DO;
 146}
 147
 148static int process_commit(struct commit * commit)
 149{
 150        int action=filter_commit(commit);
 151
 152        if (action == STOP) {
 153                return STOP;
 154        }
 155
 156        if (action == CONTINUE) {
 157                return CONTINUE;
 158        }
 159
 160        if (revs.max_count != -1 && !revs.max_count--)
 161                return STOP;
 162
 163        show_commit(commit);
 164
 165        return CONTINUE;
 166}
 167
 168static struct object_list **process_blob(struct blob *blob,
 169                                         struct object_list **p,
 170                                         struct name_path *path,
 171                                         const char *name)
 172{
 173        struct object *obj = &blob->object;
 174
 175        if (!revs.blob_objects)
 176                return p;
 177        if (obj->flags & (UNINTERESTING | SEEN))
 178                return p;
 179        obj->flags |= SEEN;
 180        return add_object(obj, p, path, name);
 181}
 182
 183static struct object_list **process_tree(struct tree *tree,
 184                                         struct object_list **p,
 185                                         struct name_path *path,
 186                                         const char *name)
 187{
 188        struct object *obj = &tree->object;
 189        struct tree_entry_list *entry;
 190        struct name_path me;
 191
 192        if (!revs.tree_objects)
 193                return p;
 194        if (obj->flags & (UNINTERESTING | SEEN))
 195                return p;
 196        if (parse_tree(tree) < 0)
 197                die("bad tree object %s", sha1_to_hex(obj->sha1));
 198        obj->flags |= SEEN;
 199        p = add_object(obj, p, path, name);
 200        me.up = path;
 201        me.elem = name;
 202        me.elem_len = strlen(name);
 203        entry = tree->entries;
 204        tree->entries = NULL;
 205        while (entry) {
 206                struct tree_entry_list *next = entry->next;
 207                if (entry->directory)
 208                        p = process_tree(entry->item.tree, p, &me, entry->name);
 209                else
 210                        p = process_blob(entry->item.blob, p, &me, entry->name);
 211                free(entry);
 212                entry = next;
 213        }
 214        return p;
 215}
 216
 217static struct object_list *pending_objects = NULL;
 218
 219static void show_commit_list(struct commit_list *list)
 220{
 221        struct object_list *objects = NULL, **p = &objects, *pending;
 222        while (list) {
 223                struct commit *commit = pop_most_recent_commit(&list, SEEN);
 224
 225                p = process_tree(commit->tree, p, NULL, "");
 226                if (process_commit(commit) == STOP)
 227                        break;
 228        }
 229        for (pending = pending_objects; pending; pending = pending->next) {
 230                struct object *obj = pending->item;
 231                const char *name = pending->name;
 232                if (obj->flags & (UNINTERESTING | SEEN))
 233                        continue;
 234                if (obj->type == tag_type) {
 235                        obj->flags |= SEEN;
 236                        p = add_object(obj, p, NULL, name);
 237                        continue;
 238                }
 239                if (obj->type == tree_type) {
 240                        p = process_tree((struct tree *)obj, p, NULL, name);
 241                        continue;
 242                }
 243                if (obj->type == blob_type) {
 244                        p = process_blob((struct blob *)obj, p, NULL, name);
 245                        continue;
 246                }
 247                die("unknown pending object %s (%s)", sha1_to_hex(obj->sha1), name);
 248        }
 249        while (objects) {
 250                /* An object with name "foo\n0000000..." can be used to
 251                 * confuse downstream git-pack-objects very badly.
 252                 */
 253                const char *ep = strchr(objects->name, '\n');
 254                if (ep) {
 255                        printf("%s %.*s\n", sha1_to_hex(objects->item->sha1),
 256                               (int) (ep - objects->name),
 257                               objects->name);
 258                }
 259                else
 260                        printf("%s %s\n", sha1_to_hex(objects->item->sha1), objects->name);
 261                objects = objects->next;
 262        }
 263}
 264
 265static int everybody_uninteresting(struct commit_list *orig)
 266{
 267        struct commit_list *list = orig;
 268        while (list) {
 269                struct commit *commit = list->item;
 270                list = list->next;
 271                if (commit->object.flags & UNINTERESTING)
 272                        continue;
 273                return 0;
 274        }
 275        return 1;
 276}
 277
 278/*
 279 * This is a truly stupid algorithm, but it's only
 280 * used for bisection, and we just don't care enough.
 281 *
 282 * We care just barely enough to avoid recursing for
 283 * non-merge entries.
 284 */
 285static int count_distance(struct commit_list *entry)
 286{
 287        int nr = 0;
 288
 289        while (entry) {
 290                struct commit *commit = entry->item;
 291                struct commit_list *p;
 292
 293                if (commit->object.flags & (UNINTERESTING | COUNTED))
 294                        break;
 295                if (!revs.paths || (commit->object.flags & TREECHANGE))
 296                        nr++;
 297                commit->object.flags |= COUNTED;
 298                p = commit->parents;
 299                entry = p;
 300                if (p) {
 301                        p = p->next;
 302                        while (p) {
 303                                nr += count_distance(p);
 304                                p = p->next;
 305                        }
 306                }
 307        }
 308
 309        return nr;
 310}
 311
 312static void clear_distance(struct commit_list *list)
 313{
 314        while (list) {
 315                struct commit *commit = list->item;
 316                commit->object.flags &= ~COUNTED;
 317                list = list->next;
 318        }
 319}
 320
 321static struct commit_list *find_bisection(struct commit_list *list)
 322{
 323        int nr, closest;
 324        struct commit_list *p, *best;
 325
 326        nr = 0;
 327        p = list;
 328        while (p) {
 329                if (!revs.paths || (p->item->object.flags & TREECHANGE))
 330                        nr++;
 331                p = p->next;
 332        }
 333        closest = 0;
 334        best = list;
 335
 336        for (p = list; p; p = p->next) {
 337                int distance;
 338
 339                if (revs.paths && !(p->item->object.flags & TREECHANGE))
 340                        continue;
 341
 342                distance = count_distance(p);
 343                clear_distance(list);
 344                if (nr - distance < distance)
 345                        distance = nr - distance;
 346                if (distance > closest) {
 347                        best = p;
 348                        closest = distance;
 349                }
 350        }
 351        if (best)
 352                best->next = NULL;
 353        return best;
 354}
 355
 356static void mark_edge_parents_uninteresting(struct commit *commit)
 357{
 358        struct commit_list *parents;
 359
 360        for (parents = commit->parents; parents; parents = parents->next) {
 361                struct commit *parent = parents->item;
 362                if (!(parent->object.flags & UNINTERESTING))
 363                        continue;
 364                mark_tree_uninteresting(parent->tree);
 365                if (revs.edge_hint && !(parent->object.flags & SHOWN)) {
 366                        parent->object.flags |= SHOWN;
 367                        printf("-%s\n", sha1_to_hex(parent->object.sha1));
 368                }
 369        }
 370}
 371
 372static void mark_edges_uninteresting(struct commit_list *list)
 373{
 374        for ( ; list; list = list->next) {
 375                struct commit *commit = list->item;
 376
 377                if (commit->object.flags & UNINTERESTING) {
 378                        mark_tree_uninteresting(commit->tree);
 379                        continue;
 380                }
 381                mark_edge_parents_uninteresting(commit);
 382        }
 383}
 384
 385#define TREE_SAME       0
 386#define TREE_NEW        1
 387#define TREE_DIFFERENT  2
 388static int tree_difference = TREE_SAME;
 389
 390static void file_add_remove(struct diff_options *options,
 391                    int addremove, unsigned mode,
 392                    const unsigned char *sha1,
 393                    const char *base, const char *path)
 394{
 395        int diff = TREE_DIFFERENT;
 396
 397        /*
 398         * Is it an add of a new file? It means that
 399         * the old tree didn't have it at all, so we
 400         * will turn "TREE_SAME" -> "TREE_NEW", but
 401         * leave any "TREE_DIFFERENT" alone (and if
 402         * it already was "TREE_NEW", we'll keep it
 403         * "TREE_NEW" of course).
 404         */
 405        if (addremove == '+') {
 406                diff = tree_difference;
 407                if (diff != TREE_SAME)
 408                        return;
 409                diff = TREE_NEW;
 410        }
 411        tree_difference = diff;
 412}
 413
 414static void file_change(struct diff_options *options,
 415                 unsigned old_mode, unsigned new_mode,
 416                 const unsigned char *old_sha1,
 417                 const unsigned char *new_sha1,
 418                 const char *base, const char *path)
 419{
 420        tree_difference = TREE_DIFFERENT;
 421}
 422
 423static struct diff_options diff_opt = {
 424        .recursive = 1,
 425        .add_remove = file_add_remove,
 426        .change = file_change,
 427};
 428
 429static int compare_tree(struct tree *t1, struct tree *t2)
 430{
 431        if (!t1)
 432                return TREE_NEW;
 433        if (!t2)
 434                return TREE_DIFFERENT;
 435        tree_difference = TREE_SAME;
 436        if (diff_tree_sha1(t1->object.sha1, t2->object.sha1, "", &diff_opt) < 0)
 437                return TREE_DIFFERENT;
 438        return tree_difference;
 439}
 440
 441static int same_tree_as_empty(struct tree *t1)
 442{
 443        int retval;
 444        void *tree;
 445        struct tree_desc empty, real;
 446
 447        if (!t1)
 448                return 0;
 449
 450        tree = read_object_with_reference(t1->object.sha1, "tree", &real.size, NULL);
 451        if (!tree)
 452                return 0;
 453        real.buf = tree;
 454
 455        empty.buf = "";
 456        empty.size = 0;
 457
 458        tree_difference = 0;
 459        retval = diff_tree(&empty, &real, "", &diff_opt);
 460        free(tree);
 461
 462        return retval >= 0 && !tree_difference;
 463}
 464
 465static void try_to_simplify_commit(struct commit *commit)
 466{
 467        struct commit_list **pp, *parent;
 468
 469        if (!commit->tree)
 470                return;
 471
 472        if (!commit->parents) {
 473                if (!same_tree_as_empty(commit->tree))
 474                        commit->object.flags |= TREECHANGE;
 475                return;
 476        }
 477
 478        pp = &commit->parents;
 479        while ((parent = *pp) != NULL) {
 480                struct commit *p = parent->item;
 481
 482                if (p->object.flags & UNINTERESTING) {
 483                        pp = &parent->next;
 484                        continue;
 485                }
 486
 487                parse_commit(p);
 488                switch (compare_tree(p->tree, commit->tree)) {
 489                case TREE_SAME:
 490                        parent->next = NULL;
 491                        commit->parents = parent;
 492                        return;
 493
 494                case TREE_NEW:
 495                        if (revs.remove_empty_trees && same_tree_as_empty(p->tree)) {
 496                                *pp = parent->next;
 497                                continue;
 498                        }
 499                /* fallthrough */
 500                case TREE_DIFFERENT:
 501                        pp = &parent->next;
 502                        continue;
 503                }
 504                die("bad tree compare for commit %s", sha1_to_hex(commit->object.sha1));
 505        }
 506        commit->object.flags |= TREECHANGE;
 507}
 508
 509static void add_parents_to_list(struct commit *commit, struct commit_list **list)
 510{
 511        struct commit_list *parent = commit->parents;
 512
 513        /*
 514         * If the commit is uninteresting, don't try to
 515         * prune parents - we want the maximal uninteresting
 516         * set.
 517         *
 518         * Normally we haven't parsed the parent
 519         * yet, so we won't have a parent of a parent
 520         * here. However, it may turn out that we've
 521         * reached this commit some other way (where it
 522         * wasn't uninteresting), in which case we need
 523         * to mark its parents recursively too..
 524         */
 525        if (commit->object.flags & UNINTERESTING) {
 526                while (parent) {
 527                        struct commit *p = parent->item;
 528                        parent = parent->next;
 529                        parse_commit(p);
 530                        p->object.flags |= UNINTERESTING;
 531                        if (p->parents)
 532                                mark_parents_uninteresting(p);
 533                        if (p->object.flags & SEEN)
 534                                continue;
 535                        p->object.flags |= SEEN;
 536                        insert_by_date(p, list);
 537                }
 538                return;
 539        }
 540
 541        /*
 542         * Ok, the commit wasn't uninteresting. Try to
 543         * simplify the commit history and find the parent
 544         * that has no differences in the path set if one exists.
 545         */
 546        if (revs.paths)
 547                try_to_simplify_commit(commit);
 548
 549        parent = commit->parents;
 550        while (parent) {
 551                struct commit *p = parent->item;
 552
 553                parent = parent->next;
 554
 555                parse_commit(p);
 556                if (p->object.flags & SEEN)
 557                        continue;
 558                p->object.flags |= SEEN;
 559                insert_by_date(p, list);
 560        }
 561}
 562
 563static struct commit_list *limit_list(struct commit_list *list)
 564{
 565        struct commit_list *newlist = NULL;
 566        struct commit_list **p = &newlist;
 567        while (list) {
 568                struct commit_list *entry = list;
 569                struct commit *commit = list->item;
 570                struct object *obj = &commit->object;
 571
 572                list = list->next;
 573                free(entry);
 574
 575                if (revs.max_age != -1 && (commit->date < revs.max_age))
 576                        obj->flags |= UNINTERESTING;
 577                if (unpacked && has_sha1_pack(obj->sha1))
 578                        obj->flags |= UNINTERESTING;
 579                add_parents_to_list(commit, &list);
 580                if (obj->flags & UNINTERESTING) {
 581                        mark_parents_uninteresting(commit);
 582                        if (everybody_uninteresting(list))
 583                                break;
 584                        continue;
 585                }
 586                if (revs.min_age != -1 && (commit->date > revs.min_age))
 587                        continue;
 588                p = &commit_list_insert(commit, p)->next;
 589        }
 590        if (revs.tree_objects)
 591                mark_edges_uninteresting(newlist);
 592        if (bisect_list)
 593                newlist = find_bisection(newlist);
 594        return newlist;
 595}
 596
 597int main(int argc, const char **argv)
 598{
 599        struct commit_list *list;
 600        int i, limited = 0;
 601
 602        argc = setup_revisions(argc, argv, &revs);
 603
 604        for (i = 1 ; i < argc; i++) {
 605                const char *arg = argv[i];
 606
 607                /* accept -<digit>, like traditilnal "head" */
 608                if ((*arg == '-') && isdigit(arg[1])) {
 609                        revs.max_count = atoi(arg + 1);
 610                        continue;
 611                }
 612                if (!strcmp(arg, "-n")) {
 613                        if (++i >= argc)
 614                                die("-n requires an argument");
 615                        revs.max_count = atoi(argv[i]);
 616                        continue;
 617                }
 618                if (!strncmp(arg,"-n",2)) {
 619                        revs.max_count = atoi(arg + 2);
 620                        continue;
 621                }
 622                if (!strcmp(arg, "--header")) {
 623                        verbose_header = 1;
 624                        continue;
 625                }
 626                if (!strcmp(arg, "--no-abbrev")) {
 627                        abbrev = 0;
 628                        continue;
 629                }
 630                if (!strncmp(arg, "--abbrev=", 9)) {
 631                        abbrev = strtoul(arg + 9, NULL, 10);
 632                        if (abbrev && abbrev < MINIMUM_ABBREV)
 633                                abbrev = MINIMUM_ABBREV;
 634                        else if (40 < abbrev)
 635                                abbrev = 40;
 636                        continue;
 637                }
 638                if (!strncmp(arg, "--pretty", 8)) {
 639                        commit_format = get_commit_format(arg+8);
 640                        verbose_header = 1;
 641                        hdr_termination = '\n';
 642                        if (commit_format == CMIT_FMT_ONELINE)
 643                                commit_prefix = "";
 644                        else
 645                                commit_prefix = "commit ";
 646                        continue;
 647                }
 648                if (!strncmp(arg, "--no-merges", 11)) {
 649                        no_merges = 1;
 650                        continue;
 651                }
 652                if (!strcmp(arg, "--parents")) {
 653                        show_parents = 1;
 654                        continue;
 655                }
 656                if (!strcmp(arg, "--bisect")) {
 657                        bisect_list = 1;
 658                        continue;
 659                }
 660                if (!strcmp(arg, "--unpacked")) {
 661                        unpacked = 1;
 662                        limited = 1;
 663                        continue;
 664                }
 665                if (!strcmp(arg, "--merge-order")) {
 666                        merge_order = 1;
 667                        continue;
 668                }
 669                if (!strcmp(arg, "--show-breaks")) {
 670                        show_breaks = 1;
 671                        continue;
 672                }
 673                usage(rev_list_usage);
 674
 675        }
 676
 677        list = revs.commits;
 678        if (list && list->next)
 679                limited = 1;
 680
 681        if (revs.topo_order)
 682                limited = 1;
 683
 684        if (!list &&
 685            (!(revs.tag_objects||revs.tree_objects||revs.blob_objects) && !revs.pending_objects))
 686                usage(rev_list_usage);
 687
 688        if (revs.paths) {
 689                limited = 1;
 690                diff_tree_setup_paths(revs.paths);
 691        }
 692        if (revs.max_age || revs.min_age)
 693                limited = 1;
 694
 695        save_commit_buffer = verbose_header;
 696        track_object_refs = 0;
 697
 698        if (!merge_order) {             
 699                sort_by_date(&list);
 700                if (list && !limited && revs.max_count == 1 &&
 701                    !revs.tag_objects && !revs.tree_objects && !revs.blob_objects) {
 702                        show_commit(list->item);
 703                        return 0;
 704                }
 705                if (limited)
 706                        list = limit_list(list);
 707                if (revs.topo_order)
 708                        sort_in_topological_order(&list, revs.lifo);
 709                show_commit_list(list);
 710        } else {
 711#ifndef NO_OPENSSL
 712                if (sort_list_in_merge_order(list, &process_commit)) {
 713                        die("merge order sort failed\n");
 714                }
 715#else
 716                die("merge order sort unsupported, OpenSSL not linked");
 717#endif
 718        }
 719
 720        return 0;
 721}