commit.con commit [PATCH] Parse tags for absent objects (89e4202)
   1#include <ctype.h>
   2#include "tag.h"
   3#include "commit.h"
   4#include "cache.h"
   5
   6const char *commit_type = "commit";
   7
   8static struct commit *check_commit(struct object *obj, const unsigned char *sha1)
   9{
  10        if (obj->type != commit_type) {
  11                error("Object %s is a %s, not a commit", 
  12                      sha1_to_hex(sha1), obj->type);
  13                return NULL;
  14        }
  15        return (struct commit *) obj;
  16}
  17
  18struct commit *lookup_commit_reference(const unsigned char *sha1)
  19{
  20        struct object *obj = parse_object(sha1);
  21
  22        if (!obj)
  23                return NULL;
  24        if (obj->type == tag_type)
  25                obj = ((struct tag *)obj)->tagged;
  26        return check_commit(obj, sha1);
  27}
  28
  29struct commit *lookup_commit(const unsigned char *sha1)
  30{
  31        struct object *obj = lookup_object(sha1);
  32        if (!obj) {
  33                struct commit *ret = xmalloc(sizeof(struct commit));
  34                memset(ret, 0, sizeof(struct commit));
  35                created_object(sha1, &ret->object);
  36                ret->object.type = commit_type;
  37                return ret;
  38        }
  39        if (!obj->type)
  40                obj->type = commit_type;
  41        return check_commit(obj, sha1);
  42}
  43
  44static unsigned long parse_commit_date(const char *buf)
  45{
  46        unsigned long date;
  47
  48        if (memcmp(buf, "author", 6))
  49                return 0;
  50        while (*buf++ != '\n')
  51                /* nada */;
  52        if (memcmp(buf, "committer", 9))
  53                return 0;
  54        while (*buf++ != '>')
  55                /* nada */;
  56        date = strtoul(buf, NULL, 10);
  57        if (date == ULONG_MAX)
  58                date = 0;
  59        return date;
  60}
  61
  62int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size)
  63{
  64        void *bufptr = buffer;
  65        unsigned char parent[20];
  66        struct commit_list **pptr;
  67
  68        if (item->object.parsed)
  69                return 0;
  70        item->object.parsed = 1;
  71        get_sha1_hex(bufptr + 5, parent);
  72        item->tree = lookup_tree(parent);
  73        if (item->tree)
  74                add_ref(&item->object, &item->tree->object);
  75        bufptr += 46; /* "tree " + "hex sha1" + "\n" */
  76        pptr = &item->parents;
  77        while (!memcmp(bufptr, "parent ", 7) &&
  78               !get_sha1_hex(bufptr + 7, parent)) {
  79                struct commit *new_parent = lookup_commit(parent);
  80                if (new_parent) {
  81                        pptr = &commit_list_insert(new_parent, pptr)->next;
  82                        add_ref(&item->object, &new_parent->object);
  83                }
  84                bufptr += 48;
  85        }
  86        item->date = parse_commit_date(bufptr);
  87        return 0;
  88}
  89
  90int parse_commit(struct commit *item)
  91{
  92        char type[20];
  93        void *buffer;
  94        unsigned long size;
  95        int ret;
  96
  97        if (item->object.parsed)
  98                return 0;
  99        buffer = read_sha1_file(item->object.sha1, type, &size);
 100        if (!buffer)
 101                return error("Could not read %s",
 102                             sha1_to_hex(item->object.sha1));
 103        if (strcmp(type, commit_type)) {
 104                free(buffer);
 105                return error("Object %s not a commit",
 106                             sha1_to_hex(item->object.sha1));
 107        }
 108        ret = parse_commit_buffer(item, buffer, size);
 109        if (!ret) {
 110                item->buffer = buffer;
 111                return 0;
 112        }
 113        free(buffer);
 114        return ret;
 115}
 116
 117struct commit_list *commit_list_insert(struct commit *item, struct commit_list **list_p)
 118{
 119        struct commit_list *new_list = xmalloc(sizeof(struct commit_list));
 120        new_list->item = item;
 121        new_list->next = *list_p;
 122        *list_p = new_list;
 123        return new_list;
 124}
 125
 126void free_commit_list(struct commit_list *list)
 127{
 128        while (list) {
 129                struct commit_list *temp = list;
 130                list = temp->next;
 131                free(temp);
 132        }
 133}
 134
 135void insert_by_date(struct commit_list **list, struct commit *item)
 136{
 137        struct commit_list **pp = list;
 138        struct commit_list *p;
 139        while ((p = *pp) != NULL) {
 140                if (p->item->date < item->date) {
 141                        break;
 142                }
 143                pp = &p->next;
 144        }
 145        commit_list_insert(item, pp);
 146}
 147
 148        
 149void sort_by_date(struct commit_list **list)
 150{
 151        struct commit_list *ret = NULL;
 152        while (*list) {
 153                insert_by_date(&ret, (*list)->item);
 154                *list = (*list)->next;
 155        }
 156        *list = ret;
 157}
 158
 159struct commit *pop_most_recent_commit(struct commit_list **list,
 160                                      unsigned int mark)
 161{
 162        struct commit *ret = (*list)->item;
 163        struct commit_list *parents = ret->parents;
 164        struct commit_list *old = *list;
 165
 166        *list = (*list)->next;
 167        free(old);
 168
 169        while (parents) {
 170                struct commit *commit = parents->item;
 171                parse_commit(commit);
 172                if (!(commit->object.flags & mark)) {
 173                        commit->object.flags |= mark;
 174                        insert_by_date(list, commit);
 175                }
 176                parents = parents->next;
 177        }
 178        return ret;
 179}
 180
 181/*
 182 * Generic support for pretty-printing the header
 183 */
 184static int get_one_line(const char *msg, unsigned long len)
 185{
 186        int ret = 0;
 187
 188        while (len--) {
 189                char c = *msg++;
 190                ret++;
 191                if (c == '\n')
 192                        break;
 193                if (!c)
 194                        return 0;
 195        }
 196        return ret;
 197}
 198
 199static int add_author_info(enum cmit_fmt fmt, char *buf, const char *line, int len)
 200{
 201        char *date;
 202        unsigned int namelen;
 203        unsigned long time;
 204        int tz, ret;
 205
 206        line += strlen("author ");
 207        date = strchr(line, '>');
 208        if (!date)
 209                return 0;
 210        namelen = ++date - line;
 211        time = strtoul(date, &date, 10);
 212        tz = strtol(date, NULL, 10);
 213
 214        ret = sprintf(buf, "Author: %.*s\n", namelen, line);
 215        if (fmt == CMIT_FMT_MEDIUM)
 216                ret += sprintf(buf + ret, "Date:   %s\n", show_date(time, tz));
 217        return ret;
 218}
 219
 220static int is_empty_line(const char *line, int len)
 221{
 222        while (len && isspace(line[len-1]))
 223                len--;
 224        return !len;
 225}
 226
 227static int add_parent_info(enum cmit_fmt fmt, char *buf, const char *line, int parents)
 228{
 229        int offset = 0;
 230        switch (parents) {
 231        case 1:
 232                break;
 233        case 2:
 234                /* Go back to the previous line: 40 characters of previous parent, and one '\n' */
 235                offset = sprintf(buf, "Merge: %.40s\n", line-41);
 236                /* Fallthrough */
 237        default:
 238                /* Replace the previous '\n' with a space */
 239                buf[offset-1] = ' ';
 240                offset += sprintf(buf + offset, "%.40s\n", line+7);
 241        }
 242        return offset;
 243}
 244
 245unsigned long pretty_print_commit(enum cmit_fmt fmt, const char *msg, unsigned long len, char *buf, unsigned long space)
 246{
 247        int hdr = 1, body = 0;
 248        unsigned long offset = 0;
 249        int parents = 0;
 250
 251        for (;;) {
 252                const char *line = msg;
 253                int linelen = get_one_line(msg, len);
 254
 255                if (!linelen)
 256                        break;
 257
 258                /*
 259                 * We want some slop for indentation and a possible
 260                 * final "...". Thus the "+ 20".
 261                 */
 262                if (offset + linelen + 20 > space) {
 263                        memcpy(buf + offset, "    ...\n", 8);
 264                        offset += 8;
 265                        break;
 266                }
 267
 268                msg += linelen;
 269                len -= linelen;
 270                if (hdr) {
 271                        if (linelen == 1) {
 272                                hdr = 0;
 273                                buf[offset++] = '\n';
 274                                continue;
 275                        }
 276                        if (fmt == CMIT_FMT_RAW) {
 277                                memcpy(buf + offset, line, linelen);
 278                                offset += linelen;
 279                                continue;
 280                        }
 281                        if (!memcmp(line, "parent ", 7)) {
 282                                if (linelen != 48)
 283                                        die("bad parent line in commit");
 284                                offset += add_parent_info(fmt, buf + offset, line, ++parents);
 285                        }
 286                        if (!memcmp(line, "author ", 7))
 287                                offset += add_author_info(fmt, buf + offset, line, linelen);
 288                        continue;
 289                }
 290
 291                if (is_empty_line(line, linelen)) {
 292                        if (!body)
 293                                continue;
 294                        if (fmt == CMIT_FMT_SHORT)
 295                                break;
 296                } else {
 297                        body = 1;
 298                }
 299                memset(buf + offset, ' ', 4);
 300                memcpy(buf + offset + 4, line, linelen);
 301                offset += linelen + 4;
 302        }
 303        /* Make sure there is an EOLN */
 304        if (buf[offset - 1] != '\n')
 305                buf[offset++] = '\n';
 306        buf[offset] = '\0';
 307        return offset;
 308}
 309
 310struct commit *pop_commit(struct commit_list **stack)
 311{
 312        struct commit_list *top = *stack;
 313        struct commit *item = top ? top->item : NULL;
 314
 315        if (top) {
 316                *stack = top->next;
 317                free(top);
 318        }
 319        return item;
 320}
 321
 322int count_parents(struct commit * commit)
 323{
 324        int count = 0;
 325        struct commit_list * parents = commit->parents;
 326        for (count=0;parents; parents=parents->next,count++)
 327          ;
 328        return count;
 329}
 330