commit.con commit Retire git-log.sh (9173080)
   1#include "cache.h"
   2#include "tag.h"
   3#include "commit.h"
   4
   5int save_commit_buffer = 1;
   6
   7struct sort_node
   8{
   9        /*
  10         * the number of children of the associated commit
  11         * that also occur in the list being sorted.
  12         */
  13        unsigned int indegree;
  14
  15        /*
  16         * reference to original list item that we will re-use
  17         * on output.
  18         */
  19        struct commit_list * list_item;
  20
  21};
  22
  23const char *commit_type = "commit";
  24
  25enum cmit_fmt get_commit_format(const char *arg)
  26{
  27        if (!*arg)
  28                return CMIT_FMT_DEFAULT;
  29        if (!strcmp(arg, "=raw"))
  30                return CMIT_FMT_RAW;
  31        if (!strcmp(arg, "=medium"))
  32                return CMIT_FMT_MEDIUM;
  33        if (!strcmp(arg, "=short"))
  34                return CMIT_FMT_SHORT;
  35        if (!strcmp(arg, "=full"))
  36                return CMIT_FMT_FULL;
  37        if (!strcmp(arg, "=fuller"))
  38                return CMIT_FMT_FULLER;
  39        if (!strcmp(arg, "=oneline"))
  40                return CMIT_FMT_ONELINE;
  41        die("invalid --pretty format");
  42}
  43
  44static struct commit *check_commit(struct object *obj,
  45                                   const unsigned char *sha1,
  46                                   int quiet)
  47{
  48        if (obj->type != commit_type) {
  49                if (!quiet)
  50                        error("Object %s is a %s, not a commit",
  51                              sha1_to_hex(sha1), obj->type);
  52                return NULL;
  53        }
  54        return (struct commit *) obj;
  55}
  56
  57struct commit *lookup_commit_reference_gently(const unsigned char *sha1,
  58                                              int quiet)
  59{
  60        struct object *obj = deref_tag(parse_object(sha1), NULL, 0);
  61
  62        if (!obj)
  63                return NULL;
  64        return check_commit(obj, sha1, quiet);
  65}
  66
  67struct commit *lookup_commit_reference(const unsigned char *sha1)
  68{
  69        return lookup_commit_reference_gently(sha1, 0);
  70}
  71
  72struct commit *lookup_commit(const unsigned char *sha1)
  73{
  74        struct object *obj = lookup_object(sha1);
  75        if (!obj) {
  76                struct commit *ret = xcalloc(1, sizeof(struct commit));
  77                created_object(sha1, &ret->object);
  78                ret->object.type = commit_type;
  79                return ret;
  80        }
  81        if (!obj->type)
  82                obj->type = commit_type;
  83        return check_commit(obj, sha1, 0);
  84}
  85
  86static unsigned long parse_commit_date(const char *buf)
  87{
  88        unsigned long date;
  89
  90        if (memcmp(buf, "author", 6))
  91                return 0;
  92        while (*buf++ != '\n')
  93                /* nada */;
  94        if (memcmp(buf, "committer", 9))
  95                return 0;
  96        while (*buf++ != '>')
  97                /* nada */;
  98        date = strtoul(buf, NULL, 10);
  99        if (date == ULONG_MAX)
 100                date = 0;
 101        return date;
 102}
 103
 104static struct commit_graft **commit_graft;
 105static int commit_graft_alloc, commit_graft_nr;
 106
 107static int commit_graft_pos(const unsigned char *sha1)
 108{
 109        int lo, hi;
 110        lo = 0;
 111        hi = commit_graft_nr;
 112        while (lo < hi) {
 113                int mi = (lo + hi) / 2;
 114                struct commit_graft *graft = commit_graft[mi];
 115                int cmp = memcmp(sha1, graft->sha1, 20);
 116                if (!cmp)
 117                        return mi;
 118                if (cmp < 0)
 119                        hi = mi;
 120                else
 121                        lo = mi + 1;
 122        }
 123        return -lo - 1;
 124}
 125
 126int register_commit_graft(struct commit_graft *graft, int ignore_dups)
 127{
 128        int pos = commit_graft_pos(graft->sha1);
 129        
 130        if (0 <= pos) {
 131                if (ignore_dups)
 132                        free(graft);
 133                else {
 134                        free(commit_graft[pos]);
 135                        commit_graft[pos] = graft;
 136                }
 137                return 1;
 138        }
 139        pos = -pos - 1;
 140        if (commit_graft_alloc <= ++commit_graft_nr) {
 141                commit_graft_alloc = alloc_nr(commit_graft_alloc);
 142                commit_graft = xrealloc(commit_graft,
 143                                        sizeof(*commit_graft) *
 144                                        commit_graft_alloc);
 145        }
 146        if (pos < commit_graft_nr)
 147                memmove(commit_graft + pos + 1,
 148                        commit_graft + pos,
 149                        (commit_graft_nr - pos - 1) *
 150                        sizeof(*commit_graft));
 151        commit_graft[pos] = graft;
 152        return 0;
 153}
 154
 155struct commit_graft *read_graft_line(char *buf, int len)
 156{
 157        /* The format is just "Commit Parent1 Parent2 ...\n" */
 158        int i;
 159        struct commit_graft *graft = NULL;
 160
 161        if (buf[len-1] == '\n')
 162                buf[--len] = 0;
 163        if (buf[0] == '#')
 164                return 0;
 165        if ((len + 1) % 41) {
 166        bad_graft_data:
 167                error("bad graft data: %s", buf);
 168                free(graft);
 169                return NULL;
 170        }
 171        i = (len + 1) / 41 - 1;
 172        graft = xmalloc(sizeof(*graft) + 20 * i);
 173        graft->nr_parent = i;
 174        if (get_sha1_hex(buf, graft->sha1))
 175                goto bad_graft_data;
 176        for (i = 40; i < len; i += 41) {
 177                if (buf[i] != ' ')
 178                        goto bad_graft_data;
 179                if (get_sha1_hex(buf + i + 1, graft->parent[i/41]))
 180                        goto bad_graft_data;
 181        }
 182        return graft;
 183}
 184
 185int read_graft_file(const char *graft_file)
 186{
 187        FILE *fp = fopen(graft_file, "r");
 188        char buf[1024];
 189        if (!fp)
 190                return -1;
 191        while (fgets(buf, sizeof(buf), fp)) {
 192                /* The format is just "Commit Parent1 Parent2 ...\n" */
 193                int len = strlen(buf);
 194                struct commit_graft *graft = read_graft_line(buf, len);
 195                if (register_commit_graft(graft, 1))
 196                        error("duplicate graft data: %s", buf);
 197        }
 198        fclose(fp);
 199        return 0;
 200}
 201
 202static void prepare_commit_graft(void)
 203{
 204        static int commit_graft_prepared;
 205        char *graft_file;
 206
 207        if (commit_graft_prepared)
 208                return;
 209        graft_file = get_graft_file();
 210        read_graft_file(graft_file);
 211        commit_graft_prepared = 1;
 212}
 213
 214static struct commit_graft *lookup_commit_graft(const unsigned char *sha1)
 215{
 216        int pos;
 217        prepare_commit_graft();
 218        pos = commit_graft_pos(sha1);
 219        if (pos < 0)
 220                return NULL;
 221        return commit_graft[pos];
 222}
 223
 224int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size)
 225{
 226        char *bufptr = buffer;
 227        unsigned char parent[20];
 228        struct commit_list **pptr;
 229        struct commit_graft *graft;
 230        unsigned n_refs = 0;
 231
 232        if (item->object.parsed)
 233                return 0;
 234        item->object.parsed = 1;
 235        if (memcmp(bufptr, "tree ", 5))
 236                return error("bogus commit object %s", sha1_to_hex(item->object.sha1));
 237        if (get_sha1_hex(bufptr + 5, parent) < 0)
 238                return error("bad tree pointer in commit %s",
 239                             sha1_to_hex(item->object.sha1));
 240        item->tree = lookup_tree(parent);
 241        if (item->tree)
 242                n_refs++;
 243        bufptr += 46; /* "tree " + "hex sha1" + "\n" */
 244        pptr = &item->parents;
 245
 246        graft = lookup_commit_graft(item->object.sha1);
 247        while (!memcmp(bufptr, "parent ", 7)) {
 248                struct commit *new_parent;
 249
 250                if (get_sha1_hex(bufptr + 7, parent) || bufptr[47] != '\n')
 251                        return error("bad parents in commit %s", sha1_to_hex(item->object.sha1));
 252                bufptr += 48;
 253                if (graft)
 254                        continue;
 255                new_parent = lookup_commit(parent);
 256                if (new_parent) {
 257                        pptr = &commit_list_insert(new_parent, pptr)->next;
 258                        n_refs++;
 259                }
 260        }
 261        if (graft) {
 262                int i;
 263                struct commit *new_parent;
 264                for (i = 0; i < graft->nr_parent; i++) {
 265                        new_parent = lookup_commit(graft->parent[i]);
 266                        if (!new_parent)
 267                                continue;
 268                        pptr = &commit_list_insert(new_parent, pptr)->next;
 269                        n_refs++;
 270                }
 271        }
 272        item->date = parse_commit_date(bufptr);
 273
 274        if (track_object_refs) {
 275                unsigned i = 0;
 276                struct commit_list *p;
 277                struct object_refs *refs = alloc_object_refs(n_refs);
 278                if (item->tree)
 279                        refs->ref[i++] = &item->tree->object;
 280                for (p = item->parents; p; p = p->next)
 281                        refs->ref[i++] = &p->item->object;
 282                set_object_refs(&item->object, refs);
 283        }
 284
 285        return 0;
 286}
 287
 288int parse_commit(struct commit *item)
 289{
 290        char type[20];
 291        void *buffer;
 292        unsigned long size;
 293        int ret;
 294
 295        if (item->object.parsed)
 296                return 0;
 297        buffer = read_sha1_file(item->object.sha1, type, &size);
 298        if (!buffer)
 299                return error("Could not read %s",
 300                             sha1_to_hex(item->object.sha1));
 301        if (strcmp(type, commit_type)) {
 302                free(buffer);
 303                return error("Object %s not a commit",
 304                             sha1_to_hex(item->object.sha1));
 305        }
 306        ret = parse_commit_buffer(item, buffer, size);
 307        if (save_commit_buffer && !ret) {
 308                item->buffer = buffer;
 309                return 0;
 310        }
 311        free(buffer);
 312        return ret;
 313}
 314
 315struct commit_list *commit_list_insert(struct commit *item, struct commit_list **list_p)
 316{
 317        struct commit_list *new_list = xmalloc(sizeof(struct commit_list));
 318        new_list->item = item;
 319        new_list->next = *list_p;
 320        *list_p = new_list;
 321        return new_list;
 322}
 323
 324void free_commit_list(struct commit_list *list)
 325{
 326        while (list) {
 327                struct commit_list *temp = list;
 328                list = temp->next;
 329                free(temp);
 330        }
 331}
 332
 333struct commit_list * insert_by_date(struct commit *item, struct commit_list **list)
 334{
 335        struct commit_list **pp = list;
 336        struct commit_list *p;
 337        while ((p = *pp) != NULL) {
 338                if (p->item->date < item->date) {
 339                        break;
 340                }
 341                pp = &p->next;
 342        }
 343        return commit_list_insert(item, pp);
 344}
 345
 346        
 347void sort_by_date(struct commit_list **list)
 348{
 349        struct commit_list *ret = NULL;
 350        while (*list) {
 351                insert_by_date((*list)->item, &ret);
 352                *list = (*list)->next;
 353        }
 354        *list = ret;
 355}
 356
 357struct commit *pop_most_recent_commit(struct commit_list **list,
 358                                      unsigned int mark)
 359{
 360        struct commit *ret = (*list)->item;
 361        struct commit_list *parents = ret->parents;
 362        struct commit_list *old = *list;
 363
 364        *list = (*list)->next;
 365        free(old);
 366
 367        while (parents) {
 368                struct commit *commit = parents->item;
 369                parse_commit(commit);
 370                if (!(commit->object.flags & mark)) {
 371                        commit->object.flags |= mark;
 372                        insert_by_date(commit, list);
 373                }
 374                parents = parents->next;
 375        }
 376        return ret;
 377}
 378
 379void clear_commit_marks(struct commit *commit, unsigned int mark)
 380{
 381        struct commit_list *parents;
 382
 383        parents = commit->parents;
 384        commit->object.flags &= ~mark;
 385        while (parents) {
 386                struct commit *parent = parents->item;
 387                if (parent && parent->object.parsed &&
 388                    (parent->object.flags & mark))
 389                        clear_commit_marks(parent, mark);
 390                parents = parents->next;
 391        }
 392}
 393
 394/*
 395 * Generic support for pretty-printing the header
 396 */
 397static int get_one_line(const char *msg, unsigned long len)
 398{
 399        int ret = 0;
 400
 401        while (len--) {
 402                char c = *msg++;
 403                ret++;
 404                if (c == '\n')
 405                        break;
 406                if (!c)
 407                        return 0;
 408        }
 409        return ret;
 410}
 411
 412static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const char *line)
 413{
 414        char *date;
 415        int namelen;
 416        unsigned long time;
 417        int tz, ret;
 418        const char *filler = "    ";
 419
 420        if (fmt == CMIT_FMT_ONELINE)
 421                return 0;
 422        date = strchr(line, '>');
 423        if (!date)
 424                return 0;
 425        namelen = ++date - line;
 426        time = strtoul(date, &date, 10);
 427        tz = strtol(date, NULL, 10);
 428
 429        ret = sprintf(buf, "%s: %.*s%.*s\n", what,
 430                      (fmt == CMIT_FMT_FULLER) ? 4 : 0,
 431                      filler, namelen, line);
 432        switch (fmt) {
 433        case CMIT_FMT_MEDIUM:
 434                ret += sprintf(buf + ret, "Date:   %s\n", show_date(time, tz));
 435                break;
 436        case CMIT_FMT_FULLER:
 437                ret += sprintf(buf + ret, "%sDate: %s\n", what, show_date(time, tz));
 438                break;
 439        default:
 440                /* notin' */
 441                break;
 442        }
 443        return ret;
 444}
 445
 446static int is_empty_line(const char *line, int len)
 447{
 448        while (len && isspace(line[len-1]))
 449                len--;
 450        return !len;
 451}
 452
 453static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *commit, int abbrev)
 454{
 455        struct commit_list *parent = commit->parents;
 456        int offset;
 457
 458        if ((fmt == CMIT_FMT_ONELINE) || !parent || !parent->next)
 459                return 0;
 460
 461        offset = sprintf(buf, "Merge:");
 462
 463        while (parent) {
 464                struct commit *p = parent->item;
 465                const char *hex = abbrev
 466                        ? find_unique_abbrev(p->object.sha1, abbrev)
 467                        : sha1_to_hex(p->object.sha1);
 468                char *dots = (abbrev && strlen(hex) != 40) ? "..." : "";
 469                parent = parent->next;
 470
 471                offset += sprintf(buf + offset, " %s%s", hex, dots);
 472        }
 473        buf[offset++] = '\n';
 474        return offset;
 475}
 476
 477unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, unsigned long len, char *buf, unsigned long space, int abbrev)
 478{
 479        int hdr = 1, body = 0;
 480        unsigned long offset = 0;
 481        int indent = (fmt == CMIT_FMT_ONELINE) ? 0 : 4;
 482        int parents_shown = 0;
 483        const char *msg = commit->buffer;
 484
 485        for (;;) {
 486                const char *line = msg;
 487                int linelen = get_one_line(msg, len);
 488
 489                if (!linelen)
 490                        break;
 491
 492                /*
 493                 * We want some slop for indentation and a possible
 494                 * final "...". Thus the "+ 20".
 495                 */
 496                if (offset + linelen + 20 > space) {
 497                        memcpy(buf + offset, "    ...\n", 8);
 498                        offset += 8;
 499                        break;
 500                }
 501
 502                msg += linelen;
 503                len -= linelen;
 504                if (hdr) {
 505                        if (linelen == 1) {
 506                                hdr = 0;
 507                                if (fmt != CMIT_FMT_ONELINE)
 508                                        buf[offset++] = '\n';
 509                                continue;
 510                        }
 511                        if (fmt == CMIT_FMT_RAW) {
 512                                memcpy(buf + offset, line, linelen);
 513                                offset += linelen;
 514                                continue;
 515                        }
 516                        if (!memcmp(line, "parent ", 7)) {
 517                                if (linelen != 48)
 518                                        die("bad parent line in commit");
 519                                continue;
 520                        }
 521
 522                        if (!parents_shown) {
 523                                offset += add_merge_info(fmt, buf + offset,
 524                                                         commit, abbrev);
 525                                parents_shown = 1;
 526                                continue;
 527                        }
 528                        /*
 529                         * MEDIUM == DEFAULT shows only author with dates.
 530                         * FULL shows both authors but not dates.
 531                         * FULLER shows both authors and dates.
 532                         */
 533                        if (!memcmp(line, "author ", 7))
 534                                offset += add_user_info("Author", fmt,
 535                                                        buf + offset,
 536                                                        line + 7);
 537                        if (!memcmp(line, "committer ", 10) &&
 538                            (fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER))
 539                                offset += add_user_info("Commit", fmt,
 540                                                        buf + offset,
 541                                                        line + 10);
 542                        continue;
 543                }
 544
 545                if (is_empty_line(line, linelen)) {
 546                        if (!body)
 547                                continue;
 548                        if (fmt == CMIT_FMT_SHORT)
 549                                break;
 550                } else {
 551                        body = 1;
 552                }
 553
 554                memset(buf + offset, ' ', indent);
 555                memcpy(buf + offset + indent, line, linelen);
 556                offset += linelen + indent;
 557                if (fmt == CMIT_FMT_ONELINE)
 558                        break;
 559        }
 560        if (fmt == CMIT_FMT_ONELINE) {
 561                /* We do not want the terminating newline */
 562                if (buf[offset - 1] == '\n')
 563                        offset--;
 564        }
 565        else {
 566                /* Make sure there is an EOLN */
 567                if (buf[offset - 1] != '\n')
 568                        buf[offset++] = '\n';
 569        }
 570        buf[offset] = '\0';
 571        return offset;
 572}
 573
 574struct commit *pop_commit(struct commit_list **stack)
 575{
 576        struct commit_list *top = *stack;
 577        struct commit *item = top ? top->item : NULL;
 578
 579        if (top) {
 580                *stack = top->next;
 581                free(top);
 582        }
 583        return item;
 584}
 585
 586int count_parents(struct commit * commit)
 587{
 588        int count = 0;
 589        struct commit_list * parents = commit->parents;
 590        for (count=0;parents; parents=parents->next,count++)
 591          ;
 592        return count;
 593}
 594
 595void topo_sort_default_setter(struct commit *c, void *data)
 596{
 597        c->object.util = data;
 598}
 599
 600void *topo_sort_default_getter(struct commit *c)
 601{
 602        return c->object.util;
 603}
 604
 605/*
 606 * Performs an in-place topological sort on the list supplied.
 607 */
 608void sort_in_topological_order(struct commit_list ** list, int lifo)
 609{
 610        sort_in_topological_order_fn(list, lifo, topo_sort_default_setter,
 611                                     topo_sort_default_getter);
 612}
 613
 614void sort_in_topological_order_fn(struct commit_list ** list, int lifo,
 615                                  topo_sort_set_fn_t setter,
 616                                  topo_sort_get_fn_t getter)
 617{
 618        struct commit_list * next = *list;
 619        struct commit_list * work = NULL, **insert;
 620        struct commit_list ** pptr = list;
 621        struct sort_node * nodes;
 622        struct sort_node * next_nodes;
 623        int count = 0;
 624
 625        /* determine the size of the list */
 626        while (next) {
 627                next = next->next;
 628                count++;
 629        }
 630        
 631        if (!count)
 632                return;
 633        /* allocate an array to help sort the list */
 634        nodes = xcalloc(count, sizeof(*nodes));
 635        /* link the list to the array */
 636        next_nodes = nodes;
 637        next=*list;
 638        while (next) {
 639                next_nodes->list_item = next;
 640                setter(next->item, next_nodes);
 641                next_nodes++;
 642                next = next->next;
 643        }
 644        /* update the indegree */
 645        next=*list;
 646        while (next) {
 647                struct commit_list * parents = next->item->parents;
 648                while (parents) {
 649                        struct commit * parent=parents->item;
 650                        struct sort_node * pn = (struct sort_node *) getter(parent);
 651
 652                        if (pn)
 653                                pn->indegree++;
 654                        parents=parents->next;
 655                }
 656                next=next->next;
 657        }
 658        /* 
 659         * find the tips
 660         *
 661         * tips are nodes not reachable from any other node in the list 
 662         * 
 663         * the tips serve as a starting set for the work queue.
 664         */
 665        next=*list;
 666        insert = &work;
 667        while (next) {
 668                struct sort_node * node = (struct sort_node *) getter(next->item);
 669
 670                if (node->indegree == 0) {
 671                        insert = &commit_list_insert(next->item, insert)->next;
 672                }
 673                next=next->next;
 674        }
 675
 676        /* process the list in topological order */
 677        if (!lifo)
 678                sort_by_date(&work);
 679        while (work) {
 680                struct commit * work_item = pop_commit(&work);
 681                struct sort_node * work_node = (struct sort_node *) getter(work_item);
 682                struct commit_list * parents = work_item->parents;
 683
 684                while (parents) {
 685                        struct commit * parent=parents->item;
 686                        struct sort_node * pn = (struct sort_node *) getter(parent);
 687
 688                        if (pn) {
 689                                /*
 690                                 * parents are only enqueued for emission 
 691                                 * when all their children have been emitted thereby
 692                                 * guaranteeing topological order.
 693                                 */
 694                                pn->indegree--;
 695                                if (!pn->indegree) {
 696                                        if (!lifo)
 697                                                insert_by_date(parent, &work);
 698                                        else
 699                                                commit_list_insert(parent, &work);
 700                                }
 701                        }
 702                        parents=parents->next;
 703                }
 704                /*
 705                 * work_item is a commit all of whose children
 706                 * have already been emitted. we can emit it now.
 707                 */
 708                *pptr = work_node->list_item;
 709                pptr = &(*pptr)->next;
 710                *pptr = NULL;
 711                setter(work_item, NULL);
 712        }
 713        free(nodes);
 714}