c7ebd2f03cd578ea0f21c3e4037e955a11b2c165
   1#include "cache.h"
   2#include "commit.h"
   3#include "epoch.h"
   4
   5#define SEEN            (1u << 0)
   6#define INTERESTING     (1u << 1)
   7#define COUNTED         (1u << 2)
   8
   9static const char rev_list_usage[] =
  10        "usage: git-rev-list [OPTION] commit-id <commit-id>\n"
  11                      "  --max-count=nr\n"
  12                      "  --max-age=epoch\n"
  13                      "  --min-age=epoch\n"
  14                      "  --header\n"
  15                      "  --pretty\n"
  16                      "  --merge-order [ --show-breaks ]";
  17
  18static int bisect_list = 0;
  19static int verbose_header = 0;
  20static int show_parents = 0;
  21static int hdr_termination = 0;
  22static const char *prefix = "";
  23static unsigned long max_age = -1;
  24static unsigned long min_age = -1;
  25static int max_count = -1;
  26static enum cmit_fmt commit_format = CMIT_FMT_RAW;
  27static int merge_order = 0;
  28static int show_breaks = 0;
  29
  30static void show_commit(struct commit *commit)
  31{
  32        if (show_breaks) {
  33                prefix = "| ";
  34                if (commit->object.flags & DISCONTINUITY) {
  35                        prefix = "^ ";     
  36                } else if (commit->object.flags & BOUNDARY) {
  37                        prefix = "= ";
  38                } 
  39        }                       
  40        printf("%s%s", prefix, sha1_to_hex(commit->object.sha1));
  41        if (show_parents) {
  42                struct commit_list *parents = commit->parents;
  43                while (parents) {
  44                        printf(" %s", sha1_to_hex(parents->item->object.sha1));
  45                        parents = parents->next;
  46                }
  47        }
  48        putchar('\n');
  49        if (verbose_header) {
  50                static char pretty_header[16384];
  51                pretty_print_commit(commit_format, commit->buffer, ~0, pretty_header, sizeof(pretty_header));
  52                printf("%s%c", pretty_header, hdr_termination);
  53        }       
  54}
  55
  56static int filter_commit(struct commit * commit)
  57{
  58        if (commit->object.flags & UNINTERESTING)
  59                return CONTINUE;
  60        if (min_age != -1 && (commit->date > min_age))
  61                return CONTINUE;
  62        if (max_age != -1 && (commit->date < max_age))
  63                return STOP;
  64        if (max_count != -1 && !max_count--)
  65                return STOP;
  66
  67        return DO;
  68}
  69
  70static int process_commit(struct commit * commit)
  71{
  72        int action=filter_commit(commit);
  73
  74        if (action == STOP) {
  75                return STOP;
  76        }
  77
  78        if (action == CONTINUE) {
  79                return CONTINUE;
  80        }
  81
  82        show_commit(commit);
  83
  84        return CONTINUE;
  85}
  86
  87static void show_commit_list(struct commit_list *list)
  88{
  89        while (list) {
  90                struct commit *commit = pop_most_recent_commit(&list, SEEN);
  91
  92                if (process_commit(commit) == STOP)
  93                        break;
  94        }
  95}
  96
  97static void mark_parents_uninteresting(struct commit *commit)
  98{
  99        struct commit_list *parents = commit->parents;
 100
 101        while (parents) {
 102                struct commit *commit = parents->item;
 103                commit->object.flags |= UNINTERESTING;
 104                parents = parents->next;
 105        }
 106}
 107
 108static int everybody_uninteresting(struct commit_list *list)
 109{
 110        while (list) {
 111                struct commit *commit = list->item;
 112                list = list->next;
 113                if (commit->object.flags & UNINTERESTING)
 114                        continue;
 115                return 0;
 116        }
 117        return 1;
 118}
 119
 120/*
 121 * This is a truly stupid algorithm, but it's only
 122 * used for bisection, and we just don't care enough.
 123 *
 124 * We care just barely enough to avoid recursing for
 125 * non-merge entries.
 126 */
 127static int count_distance(struct commit_list *entry)
 128{
 129        int nr = 0;
 130
 131        while (entry) {
 132                struct commit *commit = entry->item;
 133                struct commit_list *p;
 134
 135                if (commit->object.flags & (UNINTERESTING | COUNTED))
 136                        break;
 137                nr++;
 138                commit->object.flags |= COUNTED;
 139                p = commit->parents;
 140                entry = p;
 141                if (p) {
 142                        p = p->next;
 143                        while (p) {
 144                                nr += count_distance(p);
 145                                p = p->next;
 146                        }
 147                }
 148        }
 149        return nr;
 150}
 151
 152static int clear_distance(struct commit_list *list)
 153{
 154        while (list) {
 155                struct commit *commit = list->item;
 156                commit->object.flags &= ~COUNTED;
 157                list = list->next;
 158        }
 159}
 160
 161static struct commit_list *find_bisection(struct commit_list *list)
 162{
 163        int nr, closest;
 164        struct commit_list *p, *best;
 165
 166        nr = 0;
 167        p = list;
 168        while (p) {
 169                nr++;
 170                p = p->next;
 171        }
 172        closest = 0;
 173        best = list;
 174
 175        p = list;
 176        while (p) {
 177                int distance = count_distance(p);
 178                clear_distance(list);
 179                if (nr - distance < distance)
 180                        distance = nr - distance;
 181                if (distance > closest) {
 182                        best = p;
 183                        closest = distance;
 184                }
 185                p = p->next;
 186        }
 187        if (best)
 188                best->next = NULL;
 189        return best;
 190}
 191
 192struct commit_list *limit_list(struct commit_list *list)
 193{
 194        struct commit_list *newlist = NULL;
 195        struct commit_list **p = &newlist;
 196        do {
 197                struct commit *commit = pop_most_recent_commit(&list, SEEN);
 198                struct object *obj = &commit->object;
 199
 200                if (obj->flags & UNINTERESTING) {
 201                        mark_parents_uninteresting(commit);
 202                        if (everybody_uninteresting(list))
 203                                break;
 204                        continue;
 205                }
 206                p = &commit_list_insert(commit, p)->next;
 207        } while (list);
 208        if (bisect_list)
 209                newlist = find_bisection(newlist);
 210        return newlist;
 211}
 212
 213static enum cmit_fmt get_commit_format(const char *arg)
 214{
 215        if (!*arg)
 216                return CMIT_FMT_DEFAULT;
 217        if (!strcmp(arg, "=raw"))
 218                return CMIT_FMT_RAW;
 219        if (!strcmp(arg, "=medium"))
 220                return CMIT_FMT_MEDIUM;
 221        if (!strcmp(arg, "=short"))
 222                return CMIT_FMT_SHORT;
 223        usage(rev_list_usage);  
 224}                       
 225
 226
 227int main(int argc, char **argv)
 228{
 229        struct commit_list *list = NULL;
 230        int i, limited = 0;
 231
 232        for (i = 1 ; i < argc; i++) {
 233                int flags;
 234                char *arg = argv[i];
 235                unsigned char sha1[20];
 236                struct commit *commit;
 237
 238                if (!strncmp(arg, "--max-count=", 12)) {
 239                        max_count = atoi(arg + 12);
 240                        continue;
 241                }
 242                if (!strncmp(arg, "--max-age=", 10)) {
 243                        max_age = atoi(arg + 10);
 244                        continue;
 245                }
 246                if (!strncmp(arg, "--min-age=", 10)) {
 247                        min_age = atoi(arg + 10);
 248                        continue;
 249                }
 250                if (!strcmp(arg, "--header")) {
 251                        verbose_header = 1;
 252                        continue;
 253                }
 254                if (!strncmp(arg, "--pretty", 8)) {
 255                        commit_format = get_commit_format(arg+8);
 256                        verbose_header = 1;
 257                        hdr_termination = '\n';
 258                        prefix = "commit ";
 259                        continue;
 260                }
 261                if (!strcmp(arg, "--parents")) {
 262                        show_parents = 1;
 263                        continue;
 264                }
 265                if (!strcmp(arg, "--bisect")) {
 266                        bisect_list = 1;
 267                        continue;
 268                }
 269                if (!strncmp(arg, "--merge-order", 13)) {
 270                        merge_order = 1;
 271                        continue;
 272                }
 273                if (!strncmp(arg, "--show-breaks", 13)) {
 274                        show_breaks = 1;
 275                        continue;
 276                }
 277
 278                flags = 0;
 279                if (*arg == '^') {
 280                        flags = UNINTERESTING;
 281                        arg++;
 282                        limited = 1;
 283                }
 284                if (get_sha1(arg, sha1) || (show_breaks && !merge_order))
 285                        usage(rev_list_usage);
 286                commit = lookup_commit_reference(sha1);
 287                if (!commit || parse_commit(commit) < 0)
 288                        die("bad commit object %s", arg);
 289                commit->object.flags |= flags;
 290                commit_list_insert(commit, &list);
 291        }
 292
 293        if (!list)
 294                usage(rev_list_usage);
 295
 296        if (!merge_order) {             
 297                if (limited)
 298                        list = limit_list(list);
 299                show_commit_list(list);
 300        } else {
 301                if (sort_list_in_merge_order(list, &process_commit)) {
 302                          die("merge order sort failed\n");
 303                }
 304        }
 305
 306        return 0;
 307}