commit.con commit [PATCH] clone-pack and clone-script: documentation and add a missing parameter. (6ec311d)
   1#include <ctype.h>
   2#include "tag.h"
   3#include "commit.h"
   4#include "cache.h"
   5
   6struct sort_node
   7{
   8        /*
   9         * the number of children of the associated commit
  10         * that also occur in the list being sorted.
  11         */
  12        unsigned int indegree;
  13
  14        /*
  15         * reference to original list item that we will re-use
  16         * on output.
  17         */
  18        struct commit_list * list_item;
  19
  20};
  21
  22const char *commit_type = "commit";
  23
  24enum cmit_fmt get_commit_format(const char *arg)
  25{
  26        if (!*arg)
  27                return CMIT_FMT_DEFAULT;
  28        if (!strcmp(arg, "=raw"))
  29                return CMIT_FMT_RAW;
  30        if (!strcmp(arg, "=medium"))
  31                return CMIT_FMT_MEDIUM;
  32        if (!strcmp(arg, "=short"))
  33                return CMIT_FMT_SHORT;
  34        if (!strcmp(arg, "=full"))
  35                return CMIT_FMT_FULL;
  36        die("invalid --pretty format");
  37}
  38
  39static struct commit *check_commit(struct object *obj, const unsigned char *sha1)
  40{
  41        if (obj->type != commit_type) {
  42                error("Object %s is a %s, not a commit", 
  43                      sha1_to_hex(sha1), obj->type);
  44                return NULL;
  45        }
  46        return (struct commit *) obj;
  47}
  48
  49struct commit *lookup_commit_reference(const unsigned char *sha1)
  50{
  51        struct object *obj = parse_object(sha1);
  52
  53        if (!obj)
  54                return NULL;
  55        while (obj->type == tag_type)
  56                obj = parse_object(((struct tag *)obj)->tagged->sha1);
  57
  58        return check_commit(obj, sha1);
  59}
  60
  61struct commit *lookup_commit(const unsigned char *sha1)
  62{
  63        struct object *obj = lookup_object(sha1);
  64        if (!obj) {
  65                struct commit *ret = xmalloc(sizeof(struct commit));
  66                memset(ret, 0, sizeof(struct commit));
  67                created_object(sha1, &ret->object);
  68                ret->object.type = commit_type;
  69                return ret;
  70        }
  71        if (!obj->type)
  72                obj->type = commit_type;
  73        return check_commit(obj, sha1);
  74}
  75
  76static unsigned long parse_commit_date(const char *buf)
  77{
  78        unsigned long date;
  79
  80        if (memcmp(buf, "author", 6))
  81                return 0;
  82        while (*buf++ != '\n')
  83                /* nada */;
  84        if (memcmp(buf, "committer", 9))
  85                return 0;
  86        while (*buf++ != '>')
  87                /* nada */;
  88        date = strtoul(buf, NULL, 10);
  89        if (date == ULONG_MAX)
  90                date = 0;
  91        return date;
  92}
  93
  94int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size)
  95{
  96        void *bufptr = buffer;
  97        unsigned char parent[20];
  98        struct commit_list **pptr;
  99
 100        if (item->object.parsed)
 101                return 0;
 102        item->object.parsed = 1;
 103        get_sha1_hex(bufptr + 5, parent);
 104        item->tree = lookup_tree(parent);
 105        if (item->tree)
 106                add_ref(&item->object, &item->tree->object);
 107        bufptr += 46; /* "tree " + "hex sha1" + "\n" */
 108        pptr = &item->parents;
 109        while (!memcmp(bufptr, "parent ", 7) &&
 110               !get_sha1_hex(bufptr + 7, parent)) {
 111                struct commit *new_parent = lookup_commit(parent);
 112                if (new_parent) {
 113                        pptr = &commit_list_insert(new_parent, pptr)->next;
 114                        add_ref(&item->object, &new_parent->object);
 115                }
 116                bufptr += 48;
 117        }
 118        item->date = parse_commit_date(bufptr);
 119        return 0;
 120}
 121
 122int parse_commit(struct commit *item)
 123{
 124        char type[20];
 125        void *buffer;
 126        unsigned long size;
 127        int ret;
 128
 129        if (item->object.parsed)
 130                return 0;
 131        buffer = read_sha1_file(item->object.sha1, type, &size);
 132        if (!buffer)
 133                return error("Could not read %s",
 134                             sha1_to_hex(item->object.sha1));
 135        if (strcmp(type, commit_type)) {
 136                free(buffer);
 137                return error("Object %s not a commit",
 138                             sha1_to_hex(item->object.sha1));
 139        }
 140        ret = parse_commit_buffer(item, buffer, size);
 141        if (!ret) {
 142                item->buffer = buffer;
 143                return 0;
 144        }
 145        free(buffer);
 146        return ret;
 147}
 148
 149struct commit_list *commit_list_insert(struct commit *item, struct commit_list **list_p)
 150{
 151        struct commit_list *new_list = xmalloc(sizeof(struct commit_list));
 152        new_list->item = item;
 153        new_list->next = *list_p;
 154        *list_p = new_list;
 155        return new_list;
 156}
 157
 158void free_commit_list(struct commit_list *list)
 159{
 160        while (list) {
 161                struct commit_list *temp = list;
 162                list = temp->next;
 163                free(temp);
 164        }
 165}
 166
 167struct commit_list * insert_by_date(struct commit *item, struct commit_list **list)
 168{
 169        struct commit_list **pp = list;
 170        struct commit_list *p;
 171        while ((p = *pp) != NULL) {
 172                if (p->item->date < item->date) {
 173                        break;
 174                }
 175                pp = &p->next;
 176        }
 177        return commit_list_insert(item, pp);
 178}
 179
 180        
 181void sort_by_date(struct commit_list **list)
 182{
 183        struct commit_list *ret = NULL;
 184        while (*list) {
 185                insert_by_date((*list)->item, &ret);
 186                *list = (*list)->next;
 187        }
 188        *list = ret;
 189}
 190
 191struct commit *pop_most_recent_commit(struct commit_list **list,
 192                                      unsigned int mark)
 193{
 194        struct commit *ret = (*list)->item;
 195        struct commit_list *parents = ret->parents;
 196        struct commit_list *old = *list;
 197
 198        *list = (*list)->next;
 199        free(old);
 200
 201        while (parents) {
 202                struct commit *commit = parents->item;
 203                parse_commit(commit);
 204                if (!(commit->object.flags & mark)) {
 205                        commit->object.flags |= mark;
 206                        insert_by_date(commit, list);
 207                }
 208                parents = parents->next;
 209        }
 210        return ret;
 211}
 212
 213/*
 214 * Generic support for pretty-printing the header
 215 */
 216static int get_one_line(const char *msg, unsigned long len)
 217{
 218        int ret = 0;
 219
 220        while (len--) {
 221                char c = *msg++;
 222                ret++;
 223                if (c == '\n')
 224                        break;
 225                if (!c)
 226                        return 0;
 227        }
 228        return ret;
 229}
 230
 231static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const char *line)
 232{
 233        char *date;
 234        unsigned int namelen;
 235        unsigned long time;
 236        int tz, ret;
 237
 238        date = strchr(line, '>');
 239        if (!date)
 240                return 0;
 241        namelen = ++date - line;
 242        time = strtoul(date, &date, 10);
 243        tz = strtol(date, NULL, 10);
 244
 245        ret = sprintf(buf, "%s: %.*s\n", what, namelen, line);
 246        if (fmt == CMIT_FMT_MEDIUM)
 247                ret += sprintf(buf + ret, "Date:   %s\n", show_date(time, tz));
 248        return ret;
 249}
 250
 251static int is_empty_line(const char *line, int len)
 252{
 253        while (len && isspace(line[len-1]))
 254                len--;
 255        return !len;
 256}
 257
 258static int add_parent_info(enum cmit_fmt fmt, char *buf, const char *line, int parents)
 259{
 260        int offset = 0;
 261        switch (parents) {
 262        case 1:
 263                break;
 264        case 2:
 265                /* Go back to the previous line: 40 characters of previous parent, and one '\n' */
 266                offset = sprintf(buf, "Merge: %.40s\n", line-41);
 267                /* Fallthrough */
 268        default:
 269                /* Replace the previous '\n' with a space */
 270                buf[offset-1] = ' ';
 271                offset += sprintf(buf + offset, "%.40s\n", line+7);
 272        }
 273        return offset;
 274}
 275
 276unsigned long pretty_print_commit(enum cmit_fmt fmt, const char *msg, unsigned long len, char *buf, unsigned long space)
 277{
 278        int hdr = 1, body = 0;
 279        unsigned long offset = 0;
 280        int parents = 0;
 281
 282        for (;;) {
 283                const char *line = msg;
 284                int linelen = get_one_line(msg, len);
 285
 286                if (!linelen)
 287                        break;
 288
 289                /*
 290                 * We want some slop for indentation and a possible
 291                 * final "...". Thus the "+ 20".
 292                 */
 293                if (offset + linelen + 20 > space) {
 294                        memcpy(buf + offset, "    ...\n", 8);
 295                        offset += 8;
 296                        break;
 297                }
 298
 299                msg += linelen;
 300                len -= linelen;
 301                if (hdr) {
 302                        if (linelen == 1) {
 303                                hdr = 0;
 304                                buf[offset++] = '\n';
 305                                continue;
 306                        }
 307                        if (fmt == CMIT_FMT_RAW) {
 308                                memcpy(buf + offset, line, linelen);
 309                                offset += linelen;
 310                                continue;
 311                        }
 312                        if (!memcmp(line, "parent ", 7)) {
 313                                if (linelen != 48)
 314                                        die("bad parent line in commit");
 315                                offset += add_parent_info(fmt, buf + offset, line, ++parents);
 316                        }
 317                        if (!memcmp(line, "author ", 7))
 318                                offset += add_user_info("Author", fmt, buf + offset, line + 7);
 319                        if (fmt == CMIT_FMT_FULL) {
 320                                if (!memcmp(line, "committer ", 10))
 321                                        offset += add_user_info("Commit", fmt, buf + offset, line + 10);
 322                        }
 323                        continue;
 324                }
 325
 326                if (is_empty_line(line, linelen)) {
 327                        if (!body)
 328                                continue;
 329                        if (fmt == CMIT_FMT_SHORT)
 330                                break;
 331                } else {
 332                        body = 1;
 333                }
 334                memset(buf + offset, ' ', 4);
 335                memcpy(buf + offset + 4, line, linelen);
 336                offset += linelen + 4;
 337        }
 338        /* Make sure there is an EOLN */
 339        if (buf[offset - 1] != '\n')
 340                buf[offset++] = '\n';
 341        buf[offset] = '\0';
 342        return offset;
 343}
 344
 345struct commit *pop_commit(struct commit_list **stack)
 346{
 347        struct commit_list *top = *stack;
 348        struct commit *item = top ? top->item : NULL;
 349
 350        if (top) {
 351                *stack = top->next;
 352                free(top);
 353        }
 354        return item;
 355}
 356
 357int count_parents(struct commit * commit)
 358{
 359        int count = 0;
 360        struct commit_list * parents = commit->parents;
 361        for (count=0;parents; parents=parents->next,count++)
 362          ;
 363        return count;
 364}
 365
 366/*
 367 * Performs an in-place topological sort on the list supplied.
 368 */
 369void sort_in_topological_order(struct commit_list ** list)
 370{
 371        struct commit_list * next = *list;
 372        struct commit_list * work = NULL;
 373        struct commit_list ** pptr = list;
 374        struct sort_node * nodes;
 375        struct sort_node * next_nodes;
 376        int count = 0;
 377
 378        /* determine the size of the list */
 379        while (next) {
 380                next = next->next;
 381                count++;
 382        }
 383        /* allocate an array to help sort the list */
 384        nodes = xcalloc(count, sizeof(*nodes));
 385        /* link the list to the array */
 386        next_nodes = nodes;
 387        next=*list;
 388        while (next) {
 389                next_nodes->list_item = next;
 390                next->item->object.util = next_nodes;
 391                next_nodes++;
 392                next = next->next;
 393        }
 394        /* update the indegree */
 395        next=*list;
 396        while (next) {
 397                struct commit_list * parents = next->item->parents;
 398                while (parents) {
 399                        struct commit * parent=parents->item;
 400                        struct sort_node * pn = (struct sort_node *)parent->object.util;
 401                        
 402                        if (pn)
 403                                pn->indegree++;
 404                        parents=parents->next;
 405                }
 406                next=next->next;
 407        }
 408        /* 
 409         * find the tips
 410         *
 411         * tips are nodes not reachable from any other node in the list 
 412         * 
 413         * the tips serve as a starting set for the work queue.
 414         */
 415        next=*list;
 416        while (next) {
 417                struct sort_node * node = (struct sort_node *)next->item->object.util;
 418
 419                if (node->indegree == 0) {
 420                        commit_list_insert(next->item, &work);
 421                }
 422                next=next->next;
 423        }
 424        /* process the list in topological order */
 425        while (work) {
 426                struct commit * work_item = pop_commit(&work);
 427                struct sort_node * work_node = (struct sort_node *)work_item->object.util;
 428                struct commit_list * parents = work_item->parents;
 429
 430                while (parents) {
 431                        struct commit * parent=parents->item;
 432                        struct sort_node * pn = (struct sort_node *)parent->object.util;
 433                        
 434                        if (pn) {
 435                                /* 
 436                                 * parents are only enqueued for emission 
 437                                 * when all their children have been emitted thereby
 438                                 * guaranteeing topological order.
 439                                 */
 440                                pn->indegree--;
 441                                if (!pn->indegree) 
 442                                        commit_list_insert(parent, &work);
 443                        }
 444                        parents=parents->next;
 445                }
 446                /*
 447                 * work_item is a commit all of whose children
 448                 * have already been emitted. we can emit it now.
 449                 */
 450                *pptr = work_node->list_item;
 451                pptr = &(*pptr)->next;
 452                *pptr = NULL;
 453                work_item->object.util = NULL;
 454        }
 455        free(nodes);
 456}