commit.con commit [PATCH] Add git-diff-stages command. (22f77b7)
   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
  67        if (item->object.parsed)
  68                return 0;
  69        item->object.parsed = 1;
  70        get_sha1_hex(bufptr + 5, parent);
  71        item->tree = lookup_tree(parent);
  72        if (item->tree)
  73                add_ref(&item->object, &item->tree->object);
  74        bufptr += 46; /* "tree " + "hex sha1" + "\n" */
  75        while (!memcmp(bufptr, "parent ", 7) &&
  76               !get_sha1_hex(bufptr + 7, parent)) {
  77                struct commit *new_parent = lookup_commit(parent);
  78                if (new_parent) {
  79                        commit_list_insert(new_parent, &item->parents);
  80                        add_ref(&item->object, &new_parent->object);
  81                }
  82                bufptr += 48;
  83        }
  84        item->date = parse_commit_date(bufptr);
  85        return 0;
  86}
  87
  88int parse_commit(struct commit *item)
  89{
  90        char type[20];
  91        void *buffer;
  92        unsigned long size;
  93        int ret;
  94
  95        if (item->object.parsed)
  96                return 0;
  97        buffer = read_sha1_file(item->object.sha1, type, &size);
  98        if (!buffer)
  99                return error("Could not read %s",
 100                             sha1_to_hex(item->object.sha1));
 101        if (strcmp(type, commit_type)) {
 102                free(buffer);
 103                return error("Object %s not a commit",
 104                             sha1_to_hex(item->object.sha1));
 105        }
 106        ret = parse_commit_buffer(item, buffer, size);
 107        if (!ret) {
 108                item->buffer = buffer;
 109                return 0;
 110        }
 111        free(buffer);
 112        return ret;
 113}
 114
 115struct commit_list *commit_list_insert(struct commit *item, struct commit_list **list_p)
 116{
 117        struct commit_list *new_list = xmalloc(sizeof(struct commit_list));
 118        new_list->item = item;
 119        new_list->next = *list_p;
 120        *list_p = new_list;
 121        return new_list;
 122}
 123
 124void free_commit_list(struct commit_list *list)
 125{
 126        while (list) {
 127                struct commit_list *temp = list;
 128                list = temp->next;
 129                free(temp);
 130        }
 131}
 132
 133void insert_by_date(struct commit_list **list, struct commit *item)
 134{
 135        struct commit_list **pp = list;
 136        struct commit_list *p;
 137        while ((p = *pp) != NULL) {
 138                if (p->item->date < item->date) {
 139                        break;
 140                }
 141                pp = &p->next;
 142        }
 143        commit_list_insert(item, pp);
 144}
 145
 146        
 147void sort_by_date(struct commit_list **list)
 148{
 149        struct commit_list *ret = NULL;
 150        while (*list) {
 151                insert_by_date(&ret, (*list)->item);
 152                *list = (*list)->next;
 153        }
 154        *list = ret;
 155}
 156
 157struct commit *pop_most_recent_commit(struct commit_list **list,
 158                                      unsigned int mark)
 159{
 160        struct commit *ret = (*list)->item;
 161        struct commit_list *parents = ret->parents;
 162        struct commit_list *old = *list;
 163
 164        *list = (*list)->next;
 165        free(old);
 166
 167        while (parents) {
 168                struct commit *commit = parents->item;
 169                parse_commit(commit);
 170                if (!(commit->object.flags & mark)) {
 171                        commit->object.flags |= mark;
 172                        insert_by_date(list, commit);
 173                }
 174                parents = parents->next;
 175        }
 176        return ret;
 177}
 178
 179/*
 180 * Generic support for pretty-printing the header
 181 */
 182static int get_one_line(const char *msg, unsigned long len)
 183{
 184        int ret = 0;
 185
 186        while (len--) {
 187                char c = *msg++;
 188                ret++;
 189                if (c == '\n')
 190                        break;
 191                if (!c)
 192                        return 0;
 193        }
 194        return ret;
 195}
 196
 197static int add_author_info(enum cmit_fmt fmt, char *buf, const char *line, int len)
 198{
 199        char *date;
 200        unsigned int namelen;
 201        unsigned long time;
 202        int tz, ret;
 203
 204        line += strlen("author ");
 205        date = strchr(line, '>');
 206        if (!date)
 207                return 0;
 208        namelen = ++date - line;
 209        time = strtoul(date, &date, 10);
 210        tz = strtol(date, NULL, 10);
 211
 212        ret = sprintf(buf, "Author: %.*s\n", namelen, line);
 213        if (fmt == CMIT_FMT_MEDIUM)
 214                ret += sprintf(buf + ret, "Date:   %s\n", show_date(time, tz));
 215        return ret;
 216}
 217
 218static int is_empty_line(const char *line, int len)
 219{
 220        while (len && isspace(line[len-1]))
 221                len--;
 222        return !len;
 223}
 224
 225unsigned long pretty_print_commit(enum cmit_fmt fmt, const char *msg, unsigned long len, char *buf, unsigned long space)
 226{
 227        int hdr = 1, body = 0;
 228        unsigned long offset = 0;
 229
 230        for (;;) {
 231                const char *line = msg;
 232                int linelen = get_one_line(msg, len);
 233
 234                if (!linelen)
 235                        break;
 236
 237                /*
 238                 * We want some slop for indentation and a possible
 239                 * final "...". Thus the "+ 20".
 240                 */
 241                if (offset + linelen + 20 > space) {
 242                        memcpy(buf + offset, "    ...\n", 8);
 243                        offset += 8;
 244                        break;
 245                }
 246
 247                msg += linelen;
 248                len -= linelen;
 249                if (hdr) {
 250                        if (linelen == 1) {
 251                                hdr = 0;
 252                                buf[offset++] = '\n';
 253                                continue;
 254                        }
 255                        if (fmt == CMIT_FMT_RAW) {
 256                                memcpy(buf + offset, line, linelen);
 257                                offset += linelen;
 258                                continue;
 259                        }
 260                        if (!memcmp(line, "author ", 7))
 261                                offset += add_author_info(fmt, buf + offset, line, linelen);
 262                        continue;
 263                }
 264
 265                if (is_empty_line(line, linelen)) {
 266                        if (!body)
 267                                continue;
 268                        if (fmt == CMIT_FMT_SHORT)
 269                                break;
 270                } else {
 271                        body = 1;
 272                }
 273                memset(buf + offset, ' ', 4);
 274                memcpy(buf + offset + 4, line, linelen);
 275                offset += linelen + 4;
 276        }
 277        /* Make sure there is an EOLN */
 278        if (buf[offset - 1] != '\n')
 279                buf[offset++] = '\n';
 280        buf[offset] = '\0';
 281        return offset;
 282}
 283
 284struct commit *pop_commit(struct commit_list **stack)
 285{
 286        struct commit_list *top = *stack;
 287        struct commit *item = top ? top->item : NULL;
 288
 289        if (top) {
 290                *stack = top->next;
 291                free(top);
 292        }
 293        return item;
 294}
 295
 296int count_parents(struct commit * commit)
 297{
 298        int count = 0;
 299        struct commit_list * parents = commit->parents;
 300        for (count=0;parents; parents=parents->next,count++)
 301          ;
 302        return count;
 303}
 304