rev-list.con commit Remove insane overlapping bit ranges from epoch.c (bce6286)
   1#include "cache.h"
   2#include "tag.h"
   3#include "commit.h"
   4#include "tree.h"
   5#include "blob.h"
   6#include "epoch.h"
   7
   8#define SEEN            (1u << 0)
   9#define INTERESTING     (1u << 1)
  10#define COUNTED         (1u << 2)
  11#define SHOWN           (1u << 3)
  12#define DUPCHECK        (1u << 4)
  13
  14static const char rev_list_usage[] =
  15        "usage: git-rev-list [OPTION] commit-id <commit-id>\n"
  16                      "  --max-count=nr\n"
  17                      "  --max-age=epoch\n"
  18                      "  --min-age=epoch\n"
  19                      "  --bisect\n"
  20                      "  --objects\n"
  21                      "  --unpacked\n"
  22                      "  --header\n"
  23                      "  --pretty\n"
  24                      "  --merge-order [ --show-breaks ]";
  25
  26static int unpacked = 0;
  27static int bisect_list = 0;
  28static int tag_objects = 0;
  29static int tree_objects = 0;
  30static int blob_objects = 0;
  31static int verbose_header = 0;
  32static int show_parents = 0;
  33static int hdr_termination = 0;
  34static const char *prefix = "";
  35static unsigned long max_age = -1;
  36static unsigned long min_age = -1;
  37static int max_count = -1;
  38static enum cmit_fmt commit_format = CMIT_FMT_RAW;
  39static int merge_order = 0;
  40static int show_breaks = 0;
  41static int stop_traversal = 0;
  42
  43static void show_commit(struct commit *commit)
  44{
  45        commit->object.flags |= SHOWN;
  46        if (show_breaks) {
  47                prefix = "| ";
  48                if (commit->object.flags & DISCONTINUITY) {
  49                        prefix = "^ ";     
  50                } else if (commit->object.flags & BOUNDARY) {
  51                        prefix = "= ";
  52                } 
  53        }                       
  54        printf("%s%s", prefix, sha1_to_hex(commit->object.sha1));
  55        if (show_parents) {
  56                struct commit_list *parents = commit->parents;
  57                while (parents) {
  58                        printf(" %s", sha1_to_hex(parents->item->object.sha1));
  59                        parents = parents->next;
  60                }
  61        }
  62        putchar('\n');
  63        if (verbose_header) {
  64                static char pretty_header[16384];
  65                pretty_print_commit(commit_format, commit->buffer, ~0, pretty_header, sizeof(pretty_header));
  66                printf("%s%c", pretty_header, hdr_termination);
  67        }
  68        fflush(stdout);
  69}
  70
  71static int filter_commit(struct commit * commit)
  72{
  73        if (merge_order && stop_traversal && commit->object.flags & BOUNDARY)
  74                return STOP;
  75        if (commit->object.flags & (UNINTERESTING|SHOWN))
  76                return CONTINUE;
  77        if (min_age != -1 && (commit->date > min_age))
  78                return CONTINUE;
  79        if (max_age != -1 && (commit->date < max_age)) {
  80                if (!merge_order)
  81                        return STOP;
  82                else {
  83                        stop_traversal = 1;
  84                        return CONTINUE;
  85                }
  86        }
  87        if (max_count != -1 && !max_count--)
  88                return STOP;
  89        return DO;
  90}
  91
  92static int process_commit(struct commit * commit)
  93{
  94        int action=filter_commit(commit);
  95
  96        if (action == STOP) {
  97                return STOP;
  98        }
  99
 100        if (action == CONTINUE) {
 101                return CONTINUE;
 102        }
 103
 104        show_commit(commit);
 105
 106        return CONTINUE;
 107}
 108
 109static struct object_list **add_object(struct object *obj, struct object_list **p, const char *name)
 110{
 111        struct object_list *entry = xmalloc(sizeof(*entry));
 112        entry->item = obj;
 113        entry->next = *p;
 114        entry->name = name;
 115        *p = entry;
 116        return &entry->next;
 117}
 118
 119static struct object_list **process_blob(struct blob *blob, struct object_list **p, const char *name)
 120{
 121        struct object *obj = &blob->object;
 122
 123        if (!blob_objects)
 124                return p;
 125        if (obj->flags & (UNINTERESTING | SEEN))
 126                return p;
 127        obj->flags |= SEEN;
 128        return add_object(obj, p, name);
 129}
 130
 131static struct object_list **process_tree(struct tree *tree, struct object_list **p, const char *name)
 132{
 133        struct object *obj = &tree->object;
 134        struct tree_entry_list *entry;
 135
 136        if (!tree_objects)
 137                return p;
 138        if (obj->flags & (UNINTERESTING | SEEN))
 139                return p;
 140        if (parse_tree(tree) < 0)
 141                die("bad tree object %s", sha1_to_hex(obj->sha1));
 142        obj->flags |= SEEN;
 143        p = add_object(obj, p, name);
 144        for (entry = tree->entries ; entry ; entry = entry->next) {
 145                if (entry->directory)
 146                        p = process_tree(entry->item.tree, p, entry->name);
 147                else
 148                        p = process_blob(entry->item.blob, p, entry->name);
 149        }
 150        return p;
 151}
 152
 153static struct object_list *pending_objects = NULL;
 154
 155static void show_commit_list(struct commit_list *list)
 156{
 157        struct object_list *objects = NULL, **p = &objects, *pending;
 158        while (list) {
 159                struct commit *commit = pop_most_recent_commit(&list, SEEN);
 160
 161                p = process_tree(commit->tree, p, "");
 162                if (process_commit(commit) == STOP)
 163                        break;
 164        }
 165        for (pending = pending_objects; pending; pending = pending->next) {
 166                struct object *obj = pending->item;
 167                const char *name = pending->name;
 168                if (obj->flags & (UNINTERESTING | SEEN))
 169                        continue;
 170                if (obj->type == tag_type) {
 171                        obj->flags |= SEEN;
 172                        p = add_object(obj, p, name);
 173                        continue;
 174                }
 175                if (obj->type == tree_type) {
 176                        p = process_tree((struct tree *)obj, p, name);
 177                        continue;
 178                }
 179                if (obj->type == blob_type) {
 180                        p = process_blob((struct blob *)obj, p, name);
 181                        continue;
 182                }
 183                die("unknown pending object %s (%s)", sha1_to_hex(obj->sha1), name);
 184        }
 185        while (objects) {
 186                printf("%s %s\n", sha1_to_hex(objects->item->sha1), objects->name);
 187                objects = objects->next;
 188        }
 189}
 190
 191static void mark_blob_uninteresting(struct blob *blob)
 192{
 193        if (!blob_objects)
 194                return;
 195        if (blob->object.flags & UNINTERESTING)
 196                return;
 197        blob->object.flags |= UNINTERESTING;
 198}
 199
 200static void mark_tree_uninteresting(struct tree *tree)
 201{
 202        struct object *obj = &tree->object;
 203        struct tree_entry_list *entry;
 204
 205        if (!tree_objects)
 206                return;
 207        if (obj->flags & UNINTERESTING)
 208                return;
 209        obj->flags |= UNINTERESTING;
 210        if (parse_tree(tree) < 0)
 211                die("bad tree %s", sha1_to_hex(obj->sha1));
 212        entry = tree->entries;
 213        while (entry) {
 214                if (entry->directory)
 215                        mark_tree_uninteresting(entry->item.tree);
 216                else
 217                        mark_blob_uninteresting(entry->item.blob);
 218                entry = entry->next;
 219        }
 220}
 221
 222static void mark_parents_uninteresting(struct commit *commit)
 223{
 224        struct commit_list *parents = commit->parents;
 225
 226        if (tree_objects)
 227                mark_tree_uninteresting(commit->tree);
 228        while (parents) {
 229                struct commit *commit = parents->item;
 230                commit->object.flags |= UNINTERESTING;
 231                parents = parents->next;
 232        }
 233}
 234
 235static int everybody_uninteresting(struct commit_list *list)
 236{
 237        while (list) {
 238                struct commit *commit = list->item;
 239                list = list->next;
 240                if (commit->object.flags & UNINTERESTING)
 241                        continue;
 242                return 0;
 243        }
 244        return 1;
 245}
 246
 247/*
 248 * This is a truly stupid algorithm, but it's only
 249 * used for bisection, and we just don't care enough.
 250 *
 251 * We care just barely enough to avoid recursing for
 252 * non-merge entries.
 253 */
 254static int count_distance(struct commit_list *entry)
 255{
 256        int nr = 0;
 257
 258        while (entry) {
 259                struct commit *commit = entry->item;
 260                struct commit_list *p;
 261
 262                if (commit->object.flags & (UNINTERESTING | COUNTED))
 263                        break;
 264                nr++;
 265                commit->object.flags |= COUNTED;
 266                p = commit->parents;
 267                entry = p;
 268                if (p) {
 269                        p = p->next;
 270                        while (p) {
 271                                nr += count_distance(p);
 272                                p = p->next;
 273                        }
 274                }
 275        }
 276        return nr;
 277}
 278
 279static void clear_distance(struct commit_list *list)
 280{
 281        while (list) {
 282                struct commit *commit = list->item;
 283                commit->object.flags &= ~COUNTED;
 284                list = list->next;
 285        }
 286}
 287
 288static struct commit_list *find_bisection(struct commit_list *list)
 289{
 290        int nr, closest;
 291        struct commit_list *p, *best;
 292
 293        nr = 0;
 294        p = list;
 295        while (p) {
 296                nr++;
 297                p = p->next;
 298        }
 299        closest = 0;
 300        best = list;
 301
 302        p = list;
 303        while (p) {
 304                int distance = count_distance(p);
 305                clear_distance(list);
 306                if (nr - distance < distance)
 307                        distance = nr - distance;
 308                if (distance > closest) {
 309                        best = p;
 310                        closest = distance;
 311                }
 312                p = p->next;
 313        }
 314        if (best)
 315                best->next = NULL;
 316        return best;
 317}
 318
 319static struct commit_list *limit_list(struct commit_list *list)
 320{
 321        struct commit_list *newlist = NULL;
 322        struct commit_list **p = &newlist;
 323        while (list) {
 324                struct commit *commit = pop_most_recent_commit(&list, SEEN);
 325                struct object *obj = &commit->object;
 326
 327                if (unpacked && has_sha1_pack(obj->sha1))
 328                        obj->flags |= UNINTERESTING;
 329                if (obj->flags & UNINTERESTING) {
 330                        mark_parents_uninteresting(commit);
 331                        if (everybody_uninteresting(list))
 332                                break;
 333                        continue;
 334                }
 335                p = &commit_list_insert(commit, p)->next;
 336        }
 337        if (bisect_list)
 338                newlist = find_bisection(newlist);
 339        return newlist;
 340}
 341
 342static void add_pending_object(struct object *obj, const char *name)
 343{
 344        add_object(obj, &pending_objects, name);
 345}
 346
 347static struct commit *get_commit_reference(const char *name, unsigned int flags)
 348{
 349        unsigned char sha1[20];
 350        struct object *object;
 351
 352        if (get_sha1(name, sha1))
 353                usage(rev_list_usage);
 354        object = parse_object(sha1);
 355        if (!object)
 356                die("bad object %s", name);
 357
 358        /*
 359         * Tag object? Look what it points to..
 360         */
 361        if (object->type == tag_type) {
 362                struct tag *tag = (struct tag *) object;
 363                object->flags |= flags;
 364                if (tag_objects && !(object->flags & UNINTERESTING))
 365                        add_pending_object(object, tag->tag);
 366                object = tag->tagged;
 367        }
 368
 369        /*
 370         * Commit object? Just return it, we'll do all the complex
 371         * reachability crud.
 372         */
 373        if (object->type == commit_type) {
 374                struct commit *commit = (struct commit *)object;
 375                object->flags |= flags;
 376                if (parse_commit(commit) < 0)
 377                        die("unable to parse commit %s", name);
 378                return commit;
 379        }
 380
 381        /*
 382         * Tree object? Either mark it uniniteresting, or add it
 383         * to the list of objects to look at later..
 384         */
 385        if (object->type == tree_type) {
 386                struct tree *tree = (struct tree *)object;
 387                if (!tree_objects)
 388                        return NULL;
 389                if (flags & UNINTERESTING) {
 390                        mark_tree_uninteresting(tree);
 391                        return NULL;
 392                }
 393                add_pending_object(object, "");
 394                return NULL;
 395        }
 396
 397        /*
 398         * Blob object? You know the drill by now..
 399         */
 400        if (object->type == blob_type) {
 401                struct blob *blob = (struct blob *)object;
 402                if (!blob_objects)
 403                        return NULL;
 404                if (flags & UNINTERESTING) {
 405                        mark_blob_uninteresting(blob);
 406                        return NULL;
 407                }
 408                add_pending_object(object, "");
 409                return NULL;
 410        }
 411        die("%s is unknown object", name);
 412}
 413
 414int main(int argc, char **argv)
 415{
 416        struct commit_list *list = NULL;
 417        struct commit_list *(*insert)(struct commit *, struct commit_list **);
 418        int i, limited = 0;
 419
 420        insert = insert_by_date;
 421        for (i = 1 ; i < argc; i++) {
 422                int flags;
 423                char *arg = argv[i];
 424                struct commit *commit;
 425
 426                if (!strncmp(arg, "--max-count=", 12)) {
 427                        max_count = atoi(arg + 12);
 428                        continue;
 429                }
 430                if (!strncmp(arg, "--max-age=", 10)) {
 431                        max_age = atoi(arg + 10);
 432                        continue;
 433                }
 434                if (!strncmp(arg, "--min-age=", 10)) {
 435                        min_age = atoi(arg + 10);
 436                        continue;
 437                }
 438                if (!strcmp(arg, "--header")) {
 439                        verbose_header = 1;
 440                        continue;
 441                }
 442                if (!strncmp(arg, "--pretty", 8)) {
 443                        commit_format = get_commit_format(arg+8);
 444                        verbose_header = 1;
 445                        hdr_termination = '\n';
 446                        prefix = "commit ";
 447                        continue;
 448                }
 449                if (!strcmp(arg, "--parents")) {
 450                        show_parents = 1;
 451                        continue;
 452                }
 453                if (!strcmp(arg, "--bisect")) {
 454                        bisect_list = 1;
 455                        continue;
 456                }
 457                if (!strcmp(arg, "--objects")) {
 458                        tag_objects = 1;
 459                        tree_objects = 1;
 460                        blob_objects = 1;
 461                        continue;
 462                }
 463                if (!strcmp(arg, "--unpacked")) {
 464                        unpacked = 1;
 465                        limited = 1;
 466                        continue;
 467                }
 468                if (!strcmp(arg, "--merge-order")) {
 469                        merge_order = 1;
 470                        insert = commit_list_insert;
 471                        continue;
 472                }
 473                if (!strcmp(arg, "--show-breaks")) {
 474                        show_breaks = 1;
 475                        continue;
 476                }
 477
 478                flags = 0;
 479                if (*arg == '^') {
 480                        flags = UNINTERESTING;
 481                        arg++;
 482                        limited = 1;
 483                }
 484                if (show_breaks && !merge_order)
 485                        usage(rev_list_usage);
 486                commit = get_commit_reference(arg, flags);
 487                if (!commit)
 488                        continue;
 489                if (commit->object.flags & DUPCHECK)
 490                        continue;
 491                commit->object.flags |= DUPCHECK;
 492                insert(commit, &list);
 493        }
 494
 495        if (!merge_order) {             
 496                if (limited)
 497                        list = limit_list(list);
 498                show_commit_list(list);
 499        } else {
 500                if (sort_list_in_merge_order(list, &process_commit)) {
 501                          die("merge order sort failed\n");
 502                }
 503        }
 504
 505        return 0;
 506}