X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=commit.c;h=512b5d74d71dba46831192b740680243913166ba;hb=698ce6f87e0d6db380f7306e190e8586da184577;hp=3ac421ac349e64f89cdd2f319b732cbb6d10cc44;hpb=37fde874c2448ae2cd98abe24df2bd2a50aa2cda;p=git.git diff --git a/commit.c b/commit.c index 3ac421ac..0b163d48 100644 --- a/commit.c +++ b/commit.c @@ -1,7 +1,8 @@ -#include +#include "cache.h" #include "tag.h" #include "commit.h" -#include "cache.h" + +int save_commit_buffer = 1; struct sort_node { @@ -21,53 +22,76 @@ struct sort_node const char *commit_type = "commit"; +struct cmt_fmt_map { + const char *n; + size_t cmp_len; + enum cmit_fmt v; +} cmt_fmts[] = { + { "raw", 1, CMIT_FMT_RAW }, + { "medium", 1, CMIT_FMT_MEDIUM }, + { "short", 1, CMIT_FMT_SHORT }, + { "email", 1, CMIT_FMT_EMAIL }, + { "full", 5, CMIT_FMT_FULL }, + { "fuller", 5, CMIT_FMT_FULLER }, + { "oneline", 1, CMIT_FMT_ONELINE }, +}; + enum cmit_fmt get_commit_format(const char *arg) { - if (!*arg) + int i; + + if (!arg || !*arg) return CMIT_FMT_DEFAULT; - if (!strcmp(arg, "=raw")) - return CMIT_FMT_RAW; - if (!strcmp(arg, "=medium")) - return CMIT_FMT_MEDIUM; - if (!strcmp(arg, "=short")) - return CMIT_FMT_SHORT; - if (!strcmp(arg, "=full")) - return CMIT_FMT_FULL; - die("invalid --pretty format"); + if (*arg == '=') + arg++; + for (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) { + if (!strncmp(arg, cmt_fmts[i].n, cmt_fmts[i].cmp_len)) + return cmt_fmts[i].v; + } + + die("invalid --pretty format: %s", arg); } -static struct commit *check_commit(struct object *obj, const unsigned char *sha1) +static struct commit *check_commit(struct object *obj, + const unsigned char *sha1, + int quiet) { if (obj->type != commit_type) { - error("Object %s is a %s, not a commit", - sha1_to_hex(sha1), obj->type); + if (!quiet) + error("Object %s is a %s, not a commit", + sha1_to_hex(sha1), obj->type); return NULL; } return (struct commit *) obj; } -struct commit *lookup_commit_reference(const unsigned char *sha1) +struct commit *lookup_commit_reference_gently(const unsigned char *sha1, + int quiet) { - struct object *obj = deref_tag(parse_object(sha1)); + struct object *obj = deref_tag(parse_object(sha1), NULL, 0); if (!obj) return NULL; - return check_commit(obj, sha1); + return check_commit(obj, sha1, quiet); +} + +struct commit *lookup_commit_reference(const unsigned char *sha1) +{ + return lookup_commit_reference_gently(sha1, 0); } struct commit *lookup_commit(const unsigned char *sha1) { struct object *obj = lookup_object(sha1); if (!obj) { - struct commit *ret = xmalloc(sizeof(struct commit)); - memset(ret, 0, sizeof(struct commit)); + struct commit *ret = xcalloc(1, sizeof(struct commit)); created_object(sha1, &ret->object); ret->object.type = commit_type; return ret; } if (!obj->type) obj->type = commit_type; - return check_commit(obj, sha1); + return check_commit(obj, sha1, 0); } static unsigned long parse_commit_date(const char *buf) @@ -88,11 +112,7 @@ static unsigned long parse_commit_date(const char *buf) return date; } -static struct commit_graft { - unsigned char sha1[20]; - int nr_parent; - unsigned char parent[0][20]; /* more */ -} **commit_graft; +static struct commit_graft **commit_graft; static int commit_graft_alloc, commit_graft_nr; static int commit_graft_pos(const unsigned char *sha1) @@ -114,70 +134,100 @@ static int commit_graft_pos(const unsigned char *sha1) return -lo - 1; } -static void prepare_commit_graft(void) +int register_commit_graft(struct commit_graft *graft, int ignore_dups) +{ + int pos = commit_graft_pos(graft->sha1); + + if (0 <= pos) { + if (ignore_dups) + free(graft); + else { + free(commit_graft[pos]); + commit_graft[pos] = graft; + } + return 1; + } + pos = -pos - 1; + if (commit_graft_alloc <= ++commit_graft_nr) { + commit_graft_alloc = alloc_nr(commit_graft_alloc); + commit_graft = xrealloc(commit_graft, + sizeof(*commit_graft) * + commit_graft_alloc); + } + if (pos < commit_graft_nr) + memmove(commit_graft + pos + 1, + commit_graft + pos, + (commit_graft_nr - pos - 1) * + sizeof(*commit_graft)); + commit_graft[pos] = graft; + return 0; +} + +struct commit_graft *read_graft_line(char *buf, int len) +{ + /* The format is just "Commit Parent1 Parent2 ...\n" */ + int i; + struct commit_graft *graft = NULL; + + if (buf[len-1] == '\n') + buf[--len] = 0; + if (buf[0] == '#' || buf[0] == '\0') + return NULL; + if ((len + 1) % 41) { + bad_graft_data: + error("bad graft data: %s", buf); + free(graft); + return NULL; + } + i = (len + 1) / 41 - 1; + graft = xmalloc(sizeof(*graft) + 20 * i); + graft->nr_parent = i; + if (get_sha1_hex(buf, graft->sha1)) + goto bad_graft_data; + for (i = 40; i < len; i += 41) { + if (buf[i] != ' ') + goto bad_graft_data; + if (get_sha1_hex(buf + i + 1, graft->parent[i/41])) + goto bad_graft_data; + } + return graft; +} + +int read_graft_file(const char *graft_file) { - char *graft_file = get_graft_file(); FILE *fp = fopen(graft_file, "r"); char buf[1024]; - if (!fp) { - commit_graft = (struct commit_graft **) "hack"; - return; - } + if (!fp) + return -1; while (fgets(buf, sizeof(buf), fp)) { /* The format is just "Commit Parent1 Parent2 ...\n" */ int len = strlen(buf); - int i; - struct commit_graft *graft = NULL; - - if (buf[len-1] == '\n') - buf[--len] = 0; - if (buf[0] == '#') + struct commit_graft *graft = read_graft_line(buf, len); + if (!graft) continue; - if ((len + 1) % 41) { - bad_graft_data: - error("bad graft data: %s", buf); - free(graft); - continue; - } - i = (len + 1) / 41 - 1; - graft = xmalloc(sizeof(*graft) + 20 * i); - graft->nr_parent = i; - if (get_sha1_hex(buf, graft->sha1)) - goto bad_graft_data; - for (i = 40; i < len; i += 41) { - if (buf[i] != ' ') - goto bad_graft_data; - if (get_sha1_hex(buf + i + 1, graft->parent[i/41])) - goto bad_graft_data; - } - i = commit_graft_pos(graft->sha1); - if (0 <= i) { + if (register_commit_graft(graft, 1)) error("duplicate graft data: %s", buf); - free(graft); - continue; - } - i = -i - 1; - if (commit_graft_alloc <= ++commit_graft_nr) { - commit_graft_alloc = alloc_nr(commit_graft_alloc); - commit_graft = xrealloc(commit_graft, - sizeof(*commit_graft) * - commit_graft_alloc); - } - if (i < commit_graft_nr) - memmove(commit_graft + i + 1, - commit_graft + i, - (commit_graft_nr - i - 1) * - sizeof(*commit_graft)); - commit_graft[i] = graft; } fclose(fp); + return 0; +} + +static void prepare_commit_graft(void) +{ + static int commit_graft_prepared; + char *graft_file; + + if (commit_graft_prepared) + return; + graft_file = get_graft_file(); + read_graft_file(graft_file); + commit_graft_prepared = 1; } static struct commit_graft *lookup_commit_graft(const unsigned char *sha1) { int pos; - if (!commit_graft) - prepare_commit_graft(); + prepare_commit_graft(); pos = commit_graft_pos(sha1); if (pos < 0) return NULL; @@ -190,6 +240,7 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size) unsigned char parent[20]; struct commit_list **pptr; struct commit_graft *graft; + unsigned n_refs = 0; if (item->object.parsed) return 0; @@ -197,10 +248,11 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size) if (memcmp(bufptr, "tree ", 5)) return error("bogus commit object %s", sha1_to_hex(item->object.sha1)); if (get_sha1_hex(bufptr + 5, parent) < 0) - return error("bad tree pointer in commit %s\n", sha1_to_hex(item->object.sha1)); + return error("bad tree pointer in commit %s", + sha1_to_hex(item->object.sha1)); item->tree = lookup_tree(parent); if (item->tree) - add_ref(&item->object, &item->tree->object); + n_refs++; bufptr += 46; /* "tree " + "hex sha1" + "\n" */ pptr = &item->parents; @@ -216,7 +268,7 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size) new_parent = lookup_commit(parent); if (new_parent) { pptr = &commit_list_insert(new_parent, pptr)->next; - add_ref(&item->object, &new_parent->object); + n_refs++; } } if (graft) { @@ -227,10 +279,22 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size) if (!new_parent) continue; pptr = &commit_list_insert(new_parent, pptr)->next; - add_ref(&item->object, &new_parent->object); + n_refs++; } } item->date = parse_commit_date(bufptr); + + if (track_object_refs) { + unsigned i = 0; + struct commit_list *p; + struct object_refs *refs = alloc_object_refs(n_refs); + if (item->tree) + refs->ref[i++] = &item->tree->object; + for (p = item->parents; p; p = p->next) + refs->ref[i++] = &p->item->object; + set_object_refs(&item->object, refs); + } + return 0; } @@ -253,7 +317,7 @@ int parse_commit(struct commit *item) sha1_to_hex(item->object.sha1)); } ret = parse_commit_buffer(item, buffer, size); - if (!ret) { + if (save_commit_buffer && !ret) { item->buffer = buffer; return 0; } @@ -325,6 +389,21 @@ struct commit *pop_most_recent_commit(struct commit_list **list, return ret; } +void clear_commit_marks(struct commit *commit, unsigned int mark) +{ + struct commit_list *parents; + + parents = commit->parents; + commit->object.flags &= ~mark; + while (parents) { + struct commit *parent = parents->item; + if (parent && parent->object.parsed && + (parent->object.flags & mark)) + clear_commit_marks(parent, mark); + parents = parents->next; + } +} + /* * Generic support for pretty-printing the header */ @@ -334,11 +413,11 @@ static int get_one_line(const char *msg, unsigned long len) while (len--) { char c = *msg++; + if (!c) + break; ret++; if (c == '\n') break; - if (!c) - return 0; } return ret; } @@ -346,10 +425,13 @@ static int get_one_line(const char *msg, unsigned long len) static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const char *line) { char *date; - unsigned int namelen; + int namelen; unsigned long time; int tz, ret; + const char *filler = " "; + if (fmt == CMIT_FMT_ONELINE) + return 0; date = strchr(line, '>'); if (!date) return 0; @@ -357,42 +439,75 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c time = strtoul(date, &date, 10); tz = strtol(date, NULL, 10); - ret = sprintf(buf, "%s: %.*s\n", what, namelen, line); - if (fmt == CMIT_FMT_MEDIUM) + if (fmt == CMIT_FMT_EMAIL) { + what = "From"; + filler = ""; + } + ret = sprintf(buf, "%s: %.*s%.*s\n", what, + (fmt == CMIT_FMT_FULLER) ? 4 : 0, + filler, namelen, line); + switch (fmt) { + case CMIT_FMT_MEDIUM: ret += sprintf(buf + ret, "Date: %s\n", show_date(time, tz)); + break; + case CMIT_FMT_EMAIL: + ret += sprintf(buf + ret, "Date: %s\n", + show_rfc2822_date(time, tz)); + break; + case CMIT_FMT_FULLER: + ret += sprintf(buf + ret, "%sDate: %s\n", what, show_date(time, tz)); + break; + default: + /* notin' */ + break; + } return ret; } -static int is_empty_line(const char *line, int len) +static int is_empty_line(const char *line, int *len_p) { + int len = *len_p; while (len && isspace(line[len-1])) len--; + *len_p = len; return !len; } -static int add_parent_info(enum cmit_fmt fmt, char *buf, const char *line, int parents) +static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *commit, int abbrev) { - int offset = 0; - switch (parents) { - case 1: - break; - case 2: - /* Go back to the previous line: 40 characters of previous parent, and one '\n' */ - offset = sprintf(buf, "Merge: %.40s\n", line-41); - /* Fallthrough */ - default: - /* Replace the previous '\n' with a space */ - buf[offset-1] = ' '; - offset += sprintf(buf + offset, "%.40s\n", line+7); + struct commit_list *parent = commit->parents; + int offset; + + if ((fmt == CMIT_FMT_ONELINE) || (fmt == CMIT_FMT_EMAIL) || + !parent || !parent->next) + return 0; + + offset = sprintf(buf, "Merge:"); + + while (parent) { + struct commit *p = parent->item; + const char *hex = abbrev + ? find_unique_abbrev(p->object.sha1, abbrev) + : sha1_to_hex(p->object.sha1); + char *dots = (abbrev && strlen(hex) != 40) ? "..." : ""; + parent = parent->next; + + offset += sprintf(buf + offset, " %s%s", hex, dots); } + buf[offset++] = '\n'; return offset; } -unsigned long pretty_print_commit(enum cmit_fmt fmt, const char *msg, unsigned long len, char *buf, unsigned long space) +unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject, const char *after_subject) { int hdr = 1, body = 0; unsigned long offset = 0; - int parents = 0; + int indent = 4; + int parents_shown = 0; + const char *msg = commit->buffer; + + if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) + indent = 0; for (;;) { const char *line = msg; @@ -416,7 +531,8 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const char *msg, unsigned l if (hdr) { if (linelen == 1) { hdr = 0; - buf[offset++] = '\n'; + if ((fmt != CMIT_FMT_ONELINE) && !subject) + buf[offset++] = '\n'; continue; } if (fmt == CMIT_FMT_RAW) { @@ -427,31 +543,68 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const char *msg, unsigned l if (!memcmp(line, "parent ", 7)) { if (linelen != 48) die("bad parent line in commit"); - offset += add_parent_info(fmt, buf + offset, line, ++parents); + continue; } - if (!memcmp(line, "author ", 7)) - offset += add_user_info("Author", fmt, buf + offset, line + 7); - if (fmt == CMIT_FMT_FULL) { - if (!memcmp(line, "committer ", 10)) - offset += add_user_info("Commit", fmt, buf + offset, line + 10); + + if (!parents_shown) { + offset += add_merge_info(fmt, buf + offset, + commit, abbrev); + parents_shown = 1; + continue; } + /* + * MEDIUM == DEFAULT shows only author with dates. + * FULL shows both authors but not dates. + * FULLER shows both authors and dates. + */ + if (!memcmp(line, "author ", 7)) + offset += add_user_info("Author", fmt, + buf + offset, + line + 7); + if (!memcmp(line, "committer ", 10) && + (fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) + offset += add_user_info("Commit", fmt, + buf + offset, + line + 10); continue; } - if (is_empty_line(line, linelen)) { + if (is_empty_line(line, &linelen)) { if (!body) continue; + if (subject) + continue; if (fmt == CMIT_FMT_SHORT) break; } else { body = 1; } - memset(buf + offset, ' ', 4); - memcpy(buf + offset + 4, line, linelen); - offset += linelen + 4; + + if (subject) { + int slen = strlen(subject); + memcpy(buf + offset, subject, slen); + offset += slen; + } + memset(buf + offset, ' ', indent); + memcpy(buf + offset + indent, line, linelen); + offset += linelen + indent; + buf[offset++] = '\n'; + if (fmt == CMIT_FMT_ONELINE) + break; + if (after_subject) { + int slen = strlen(after_subject); + if (slen > space - offset - 1) + slen = space - offset - 1; + memcpy(buf + offset, after_subject, slen); + offset += slen; + after_subject = NULL; + } + subject = NULL; } - /* Make sure there is an EOLN */ - if (buf[offset - 1] != '\n') + while (offset && isspace(buf[offset-1])) + offset--; + /* Make sure there is an EOLN for the non-oneline case */ + if (fmt != CMIT_FMT_ONELINE) buf[offset++] = '\n'; buf[offset] = '\0'; return offset; @@ -478,13 +631,31 @@ int count_parents(struct commit * commit) return count; } +void topo_sort_default_setter(struct commit *c, void *data) +{ + c->object.util = data; +} + +void *topo_sort_default_getter(struct commit *c) +{ + return c->object.util; +} + /* * Performs an in-place topological sort on the list supplied. */ -void sort_in_topological_order(struct commit_list ** list) +void sort_in_topological_order(struct commit_list ** list, int lifo) +{ + sort_in_topological_order_fn(list, lifo, topo_sort_default_setter, + topo_sort_default_getter); +} + +void sort_in_topological_order_fn(struct commit_list ** list, int lifo, + topo_sort_set_fn_t setter, + topo_sort_get_fn_t getter) { struct commit_list * next = *list; - struct commit_list * work = NULL; + struct commit_list * work = NULL, **insert; struct commit_list ** pptr = list; struct sort_node * nodes; struct sort_node * next_nodes; @@ -495,6 +666,9 @@ void sort_in_topological_order(struct commit_list ** list) next = next->next; count++; } + + if (!count) + return; /* allocate an array to help sort the list */ nodes = xcalloc(count, sizeof(*nodes)); /* link the list to the array */ @@ -502,7 +676,7 @@ void sort_in_topological_order(struct commit_list ** list) next=*list; while (next) { next_nodes->list_item = next; - next->item->object.util = next_nodes; + setter(next->item, next_nodes); next_nodes++; next = next->next; } @@ -512,8 +686,8 @@ void sort_in_topological_order(struct commit_list ** list) struct commit_list * parents = next->item->parents; while (parents) { struct commit * parent=parents->item; - struct sort_node * pn = (struct sort_node *)parent->object.util; - + struct sort_node * pn = (struct sort_node *) getter(parent); + if (pn) pn->indegree++; parents=parents->next; @@ -528,33 +702,41 @@ void sort_in_topological_order(struct commit_list ** list) * the tips serve as a starting set for the work queue. */ next=*list; + insert = &work; while (next) { - struct sort_node * node = (struct sort_node *)next->item->object.util; + struct sort_node * node = (struct sort_node *) getter(next->item); if (node->indegree == 0) { - commit_list_insert(next->item, &work); + insert = &commit_list_insert(next->item, insert)->next; } next=next->next; } + /* process the list in topological order */ + if (!lifo) + sort_by_date(&work); while (work) { struct commit * work_item = pop_commit(&work); - struct sort_node * work_node = (struct sort_node *)work_item->object.util; + struct sort_node * work_node = (struct sort_node *) getter(work_item); struct commit_list * parents = work_item->parents; while (parents) { struct commit * parent=parents->item; - struct sort_node * pn = (struct sort_node *)parent->object.util; - + struct sort_node * pn = (struct sort_node *) getter(parent); + if (pn) { - /* + /* * parents are only enqueued for emission * when all their children have been emitted thereby * guaranteeing topological order. */ pn->indegree--; - if (!pn->indegree) - commit_list_insert(parent, &work); + if (!pn->indegree) { + if (!lifo) + insert_by_date(parent, &work); + else + commit_list_insert(parent, &work); + } } parents=parents->next; } @@ -565,7 +747,7 @@ void sort_in_topological_order(struct commit_list ** list) *pptr = work_node->list_item; pptr = &(*pptr)->next; *pptr = NULL; - work_item->object.util = NULL; + setter(work_item, NULL); } free(nodes); }