rev-list.con commit rev-list split: minimum fixup. (d9cfb96)
   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 void show_commit_list(struct commit_list *list)
 218{
 219        struct object_list *objects = NULL, **p = &objects, *pending;
 220        while (list) {
 221                struct commit *commit = pop_most_recent_commit(&list, SEEN);
 222
 223                p = process_tree(commit->tree, p, NULL, "");
 224                if (process_commit(commit) == STOP)
 225                        break;
 226        }
 227        for (pending = revs.pending_objects; pending; pending = pending->next) {
 228                struct object *obj = pending->item;
 229                const char *name = pending->name;
 230                if (obj->flags & (UNINTERESTING | SEEN))
 231                        continue;
 232                if (obj->type == tag_type) {
 233                        obj->flags |= SEEN;
 234                        p = add_object(obj, p, NULL, name);
 235                        continue;
 236                }
 237                if (obj->type == tree_type) {
 238                        p = process_tree((struct tree *)obj, p, NULL, name);
 239                        continue;
 240                }
 241                if (obj->type == blob_type) {
 242                        p = process_blob((struct blob *)obj, p, NULL, name);
 243                        continue;
 244                }
 245                die("unknown pending object %s (%s)", sha1_to_hex(obj->sha1), name);
 246        }
 247        while (objects) {
 248                /* An object with name "foo\n0000000..." can be used to
 249                 * confuse downstream git-pack-objects very badly.
 250                 */
 251                const char *ep = strchr(objects->name, '\n');
 252                if (ep) {
 253                        printf("%s %.*s\n", sha1_to_hex(objects->item->sha1),
 254                               (int) (ep - objects->name),
 255                               objects->name);
 256                }
 257                else
 258                        printf("%s %s\n", sha1_to_hex(objects->item->sha1), objects->name);
 259                objects = objects->next;
 260        }
 261}
 262
 263static int everybody_uninteresting(struct commit_list *orig)
 264{
 265        struct commit_list *list = orig;
 266        while (list) {
 267                struct commit *commit = list->item;
 268                list = list->next;
 269                if (commit->object.flags & UNINTERESTING)
 270                        continue;
 271                return 0;
 272        }
 273        return 1;
 274}
 275
 276/*
 277 * This is a truly stupid algorithm, but it's only
 278 * used for bisection, and we just don't care enough.
 279 *
 280 * We care just barely enough to avoid recursing for
 281 * non-merge entries.
 282 */
 283static int count_distance(struct commit_list *entry)
 284{
 285        int nr = 0;
 286
 287        while (entry) {
 288                struct commit *commit = entry->item;
 289                struct commit_list *p;
 290
 291                if (commit->object.flags & (UNINTERESTING | COUNTED))
 292                        break;
 293                if (!revs.paths || (commit->object.flags & TREECHANGE))
 294                        nr++;
 295                commit->object.flags |= COUNTED;
 296                p = commit->parents;
 297                entry = p;
 298                if (p) {
 299                        p = p->next;
 300                        while (p) {
 301                                nr += count_distance(p);
 302                                p = p->next;
 303                        }
 304                }
 305        }
 306
 307        return nr;
 308}
 309
 310static void clear_distance(struct commit_list *list)
 311{
 312        while (list) {
 313                struct commit *commit = list->item;
 314                commit->object.flags &= ~COUNTED;
 315                list = list->next;
 316        }
 317}
 318
 319static struct commit_list *find_bisection(struct commit_list *list)
 320{
 321        int nr, closest;
 322        struct commit_list *p, *best;
 323
 324        nr = 0;
 325        p = list;
 326        while (p) {
 327                if (!revs.paths || (p->item->object.flags & TREECHANGE))
 328                        nr++;
 329                p = p->next;
 330        }
 331        closest = 0;
 332        best = list;
 333
 334        for (p = list; p; p = p->next) {
 335                int distance;
 336
 337                if (revs.paths && !(p->item->object.flags & TREECHANGE))
 338                        continue;
 339
 340                distance = count_distance(p);
 341                clear_distance(list);
 342                if (nr - distance < distance)
 343                        distance = nr - distance;
 344                if (distance > closest) {
 345                        best = p;
 346                        closest = distance;
 347                }
 348        }
 349        if (best)
 350                best->next = NULL;
 351        return best;
 352}
 353
 354static void mark_edge_parents_uninteresting(struct commit *commit)
 355{
 356        struct commit_list *parents;
 357
 358        for (parents = commit->parents; parents; parents = parents->next) {
 359                struct commit *parent = parents->item;
 360                if (!(parent->object.flags & UNINTERESTING))
 361                        continue;
 362                mark_tree_uninteresting(parent->tree);
 363                if (revs.edge_hint && !(parent->object.flags & SHOWN)) {
 364                        parent->object.flags |= SHOWN;
 365                        printf("-%s\n", sha1_to_hex(parent->object.sha1));
 366                }
 367        }
 368}
 369
 370static void mark_edges_uninteresting(struct commit_list *list)
 371{
 372        for ( ; list; list = list->next) {
 373                struct commit *commit = list->item;
 374
 375                if (commit->object.flags & UNINTERESTING) {
 376                        mark_tree_uninteresting(commit->tree);
 377                        continue;
 378                }
 379                mark_edge_parents_uninteresting(commit);
 380        }
 381}
 382
 383#define TREE_SAME       0
 384#define TREE_NEW        1
 385#define TREE_DIFFERENT  2
 386static int tree_difference = TREE_SAME;
 387
 388static void file_add_remove(struct diff_options *options,
 389                    int addremove, unsigned mode,
 390                    const unsigned char *sha1,
 391                    const char *base, const char *path)
 392{
 393        int diff = TREE_DIFFERENT;
 394
 395        /*
 396         * Is it an add of a new file? It means that
 397         * the old tree didn't have it at all, so we
 398         * will turn "TREE_SAME" -> "TREE_NEW", but
 399         * leave any "TREE_DIFFERENT" alone (and if
 400         * it already was "TREE_NEW", we'll keep it
 401         * "TREE_NEW" of course).
 402         */
 403        if (addremove == '+') {
 404                diff = tree_difference;
 405                if (diff != TREE_SAME)
 406                        return;
 407                diff = TREE_NEW;
 408        }
 409        tree_difference = diff;
 410}
 411
 412static void file_change(struct diff_options *options,
 413                 unsigned old_mode, unsigned new_mode,
 414                 const unsigned char *old_sha1,
 415                 const unsigned char *new_sha1,
 416                 const char *base, const char *path)
 417{
 418        tree_difference = TREE_DIFFERENT;
 419}
 420
 421static struct diff_options diff_opt = {
 422        .recursive = 1,
 423        .add_remove = file_add_remove,
 424        .change = file_change,
 425};
 426
 427static int compare_tree(struct tree *t1, struct tree *t2)
 428{
 429        if (!t1)
 430                return TREE_NEW;
 431        if (!t2)
 432                return TREE_DIFFERENT;
 433        tree_difference = TREE_SAME;
 434        if (diff_tree_sha1(t1->object.sha1, t2->object.sha1, "", &diff_opt) < 0)
 435                return TREE_DIFFERENT;
 436        return tree_difference;
 437}
 438
 439static int same_tree_as_empty(struct tree *t1)
 440{
 441        int retval;
 442        void *tree;
 443        struct tree_desc empty, real;
 444
 445        if (!t1)
 446                return 0;
 447
 448        tree = read_object_with_reference(t1->object.sha1, "tree", &real.size, NULL);
 449        if (!tree)
 450                return 0;
 451        real.buf = tree;
 452
 453        empty.buf = "";
 454        empty.size = 0;
 455
 456        tree_difference = 0;
 457        retval = diff_tree(&empty, &real, "", &diff_opt);
 458        free(tree);
 459
 460        return retval >= 0 && !tree_difference;
 461}
 462
 463static void try_to_simplify_commit(struct commit *commit)
 464{
 465        struct commit_list **pp, *parent;
 466
 467        if (!commit->tree)
 468                return;
 469
 470        if (!commit->parents) {
 471                if (!same_tree_as_empty(commit->tree))
 472                        commit->object.flags |= TREECHANGE;
 473                return;
 474        }
 475
 476        pp = &commit->parents;
 477        while ((parent = *pp) != NULL) {
 478                struct commit *p = parent->item;
 479
 480                if (p->object.flags & UNINTERESTING) {
 481                        pp = &parent->next;
 482                        continue;
 483                }
 484
 485                parse_commit(p);
 486                switch (compare_tree(p->tree, commit->tree)) {
 487                case TREE_SAME:
 488                        parent->next = NULL;
 489                        commit->parents = parent;
 490                        return;
 491
 492                case TREE_NEW:
 493                        if (revs.remove_empty_trees && same_tree_as_empty(p->tree)) {
 494                                *pp = parent->next;
 495                                continue;
 496                        }
 497                /* fallthrough */
 498                case TREE_DIFFERENT:
 499                        pp = &parent->next;
 500                        continue;
 501                }
 502                die("bad tree compare for commit %s", sha1_to_hex(commit->object.sha1));
 503        }
 504        commit->object.flags |= TREECHANGE;
 505}
 506
 507static void add_parents_to_list(struct commit *commit, struct commit_list **list)
 508{
 509        struct commit_list *parent = commit->parents;
 510
 511        /*
 512         * If the commit is uninteresting, don't try to
 513         * prune parents - we want the maximal uninteresting
 514         * set.
 515         *
 516         * Normally we haven't parsed the parent
 517         * yet, so we won't have a parent of a parent
 518         * here. However, it may turn out that we've
 519         * reached this commit some other way (where it
 520         * wasn't uninteresting), in which case we need
 521         * to mark its parents recursively too..
 522         */
 523        if (commit->object.flags & UNINTERESTING) {
 524                while (parent) {
 525                        struct commit *p = parent->item;
 526                        parent = parent->next;
 527                        parse_commit(p);
 528                        p->object.flags |= UNINTERESTING;
 529                        if (p->parents)
 530                                mark_parents_uninteresting(p);
 531                        if (p->object.flags & SEEN)
 532                                continue;
 533                        p->object.flags |= SEEN;
 534                        insert_by_date(p, list);
 535                }
 536                return;
 537        }
 538
 539        /*
 540         * Ok, the commit wasn't uninteresting. Try to
 541         * simplify the commit history and find the parent
 542         * that has no differences in the path set if one exists.
 543         */
 544        if (revs.paths)
 545                try_to_simplify_commit(commit);
 546
 547        parent = commit->parents;
 548        while (parent) {
 549                struct commit *p = parent->item;
 550
 551                parent = parent->next;
 552
 553                parse_commit(p);
 554                if (p->object.flags & SEEN)
 555                        continue;
 556                p->object.flags |= SEEN;
 557                insert_by_date(p, list);
 558        }
 559}
 560
 561static struct commit_list *limit_list(struct commit_list *list)
 562{
 563        struct commit_list *newlist = NULL;
 564        struct commit_list **p = &newlist;
 565        while (list) {
 566                struct commit_list *entry = list;
 567                struct commit *commit = list->item;
 568                struct object *obj = &commit->object;
 569
 570                list = list->next;
 571                free(entry);
 572
 573                if (revs.max_age != -1 && (commit->date < revs.max_age))
 574                        obj->flags |= UNINTERESTING;
 575                if (unpacked && has_sha1_pack(obj->sha1))
 576                        obj->flags |= UNINTERESTING;
 577                add_parents_to_list(commit, &list);
 578                if (obj->flags & UNINTERESTING) {
 579                        mark_parents_uninteresting(commit);
 580                        if (everybody_uninteresting(list))
 581                                break;
 582                        continue;
 583                }
 584                if (revs.min_age != -1 && (commit->date > revs.min_age))
 585                        continue;
 586                p = &commit_list_insert(commit, p)->next;
 587        }
 588        if (revs.tree_objects)
 589                mark_edges_uninteresting(newlist);
 590        if (bisect_list)
 591                newlist = find_bisection(newlist);
 592        return newlist;
 593}
 594
 595int main(int argc, const char **argv)
 596{
 597        struct commit_list *list;
 598        int i, limited = 0;
 599
 600        argc = setup_revisions(argc, argv, &revs);
 601
 602        for (i = 1 ; i < argc; i++) {
 603                const char *arg = argv[i];
 604
 605                /* accept -<digit>, like traditilnal "head" */
 606                if ((*arg == '-') && isdigit(arg[1])) {
 607                        revs.max_count = atoi(arg + 1);
 608                        continue;
 609                }
 610                if (!strcmp(arg, "-n")) {
 611                        if (++i >= argc)
 612                                die("-n requires an argument");
 613                        revs.max_count = atoi(argv[i]);
 614                        continue;
 615                }
 616                if (!strncmp(arg,"-n",2)) {
 617                        revs.max_count = atoi(arg + 2);
 618                        continue;
 619                }
 620                if (!strcmp(arg, "--header")) {
 621                        verbose_header = 1;
 622                        continue;
 623                }
 624                if (!strcmp(arg, "--no-abbrev")) {
 625                        abbrev = 0;
 626                        continue;
 627                }
 628                if (!strncmp(arg, "--abbrev=", 9)) {
 629                        abbrev = strtoul(arg + 9, NULL, 10);
 630                        if (abbrev && abbrev < MINIMUM_ABBREV)
 631                                abbrev = MINIMUM_ABBREV;
 632                        else if (40 < abbrev)
 633                                abbrev = 40;
 634                        continue;
 635                }
 636                if (!strncmp(arg, "--pretty", 8)) {
 637                        commit_format = get_commit_format(arg+8);
 638                        verbose_header = 1;
 639                        hdr_termination = '\n';
 640                        if (commit_format == CMIT_FMT_ONELINE)
 641                                commit_prefix = "";
 642                        else
 643                                commit_prefix = "commit ";
 644                        continue;
 645                }
 646                if (!strncmp(arg, "--no-merges", 11)) {
 647                        no_merges = 1;
 648                        continue;
 649                }
 650                if (!strcmp(arg, "--parents")) {
 651                        show_parents = 1;
 652                        continue;
 653                }
 654                if (!strcmp(arg, "--bisect")) {
 655                        bisect_list = 1;
 656                        continue;
 657                }
 658                if (!strcmp(arg, "--unpacked")) {
 659                        unpacked = 1;
 660                        limited = 1;
 661                        continue;
 662                }
 663                if (!strcmp(arg, "--merge-order")) {
 664                        merge_order = 1;
 665                        continue;
 666                }
 667                if (!strcmp(arg, "--show-breaks")) {
 668                        show_breaks = 1;
 669                        continue;
 670                }
 671                usage(rev_list_usage);
 672
 673        }
 674
 675        list = revs.commits;
 676        if (list)
 677                limited = 1;
 678
 679        if (revs.topo_order)
 680                limited = 1;
 681
 682        if (!list &&
 683            (!(revs.tag_objects||revs.tree_objects||revs.blob_objects) && !revs.pending_objects))
 684                usage(rev_list_usage);
 685
 686        if (revs.paths) {
 687                limited = 1;
 688                diff_tree_setup_paths(revs.paths);
 689        }
 690        if (revs.max_age != -1 || revs.min_age != -1)
 691                limited = 1;
 692
 693        save_commit_buffer = verbose_header;
 694        track_object_refs = 0;
 695
 696        if (!merge_order) {             
 697                sort_by_date(&list);
 698                if (list && !limited && revs.max_count == 1 &&
 699                    !revs.tag_objects && !revs.tree_objects && !revs.blob_objects) {
 700                        show_commit(list->item);
 701                        return 0;
 702                }
 703                if (limited)
 704                        list = limit_list(list);
 705                if (revs.topo_order)
 706                        sort_in_topological_order(&list, revs.lifo);
 707                show_commit_list(list);
 708        } else {
 709#ifndef NO_OPENSSL
 710                if (sort_list_in_merge_order(list, &process_commit)) {
 711                        die("merge order sort failed\n");
 712                }
 713#else
 714                die("merge order sort unsupported, OpenSSL not linked");
 715#endif
 716        }
 717
 718        return 0;
 719}