diff options
Diffstat (limited to 'commit.c')
-rw-r--r-- | commit.c | 633 |
1 files changed, 361 insertions, 272 deletions
@@ -10,26 +10,13 @@ #include "mergesort.h" #include "commit-slab.h" #include "prio-queue.h" +#include "sha1-lookup.h" static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **); int save_commit_buffer = 1; const char *commit_type = "commit"; -static int commit_count; - -static struct commit *check_commit(struct object *obj, - const unsigned char *sha1, - int quiet) -{ - if (obj->type != OBJ_COMMIT) { - if (!quiet) - error("Object %s is a %s, not a commit", - sha1_to_hex(sha1), typename(obj->type)); - return NULL; - } - return (struct commit *) obj; -} struct commit *lookup_commit_reference_gently(const unsigned char *sha1, int quiet) @@ -38,7 +25,7 @@ struct commit *lookup_commit_reference_gently(const unsigned char *sha1, if (!obj) return NULL; - return check_commit(obj, sha1, quiet); + return object_as_type(obj, OBJ_COMMIT, quiet); } struct commit *lookup_commit_reference(const unsigned char *sha1) @@ -51,7 +38,7 @@ struct commit *lookup_commit_or_die(const unsigned char *sha1, const char *ref_n struct commit *c = lookup_commit_reference(sha1); if (!c) die(_("could not parse %s"), ref_name); - if (hashcmp(sha1, c->object.sha1)) { + if (hashcmp(sha1, c->object.oid.hash)) { warning(_("%s %s is not a commit!"), ref_name, sha1_to_hex(sha1)); } @@ -61,24 +48,19 @@ struct commit *lookup_commit_or_die(const unsigned char *sha1, const char *ref_n struct commit *lookup_commit(const unsigned char *sha1) { struct object *obj = lookup_object(sha1); - if (!obj) { - struct commit *c = alloc_commit_node(); - c->index = commit_count++; - return create_object(sha1, OBJ_COMMIT, c); - } - if (!obj->type) - obj->type = OBJ_COMMIT; - return check_commit(obj, sha1, 0); + if (!obj) + return create_object(sha1, alloc_commit_node()); + return object_as_type(obj, OBJ_COMMIT, 0); } struct commit *lookup_commit_reference_by_name(const char *name) { - unsigned char sha1[20]; + struct object_id oid; struct commit *commit; - if (get_sha1_committish(name, sha1)) + if (get_sha1_committish(name, oid.hash)) return NULL; - commit = lookup_commit_reference(sha1); + commit = lookup_commit_reference(oid.hash); if (parse_commit(commit)) return NULL; return commit; @@ -114,28 +96,21 @@ static unsigned long parse_commit_date(const char *buf, const char *tail) static struct commit_graft **commit_graft; static int commit_graft_alloc, commit_graft_nr; +static const unsigned char *commit_graft_sha1_access(size_t index, void *table) +{ + struct commit_graft **commit_graft_table = table; + return commit_graft_table[index]->oid.hash; +} + static int commit_graft_pos(const unsigned char *sha1) { - int lo, hi; - lo = 0; - hi = commit_graft_nr; - while (lo < hi) { - int mi = (lo + hi) / 2; - struct commit_graft *graft = commit_graft[mi]; - int cmp = hashcmp(sha1, graft->sha1); - if (!cmp) - return mi; - if (cmp < 0) - hi = mi; - else - lo = mi + 1; - } - return -lo - 1; + return sha1_pos(sha1, commit_graft, commit_graft_nr, + commit_graft_sha1_access); } int register_commit_graft(struct commit_graft *graft, int ignore_dups) { - int pos = commit_graft_pos(graft->sha1); + int pos = commit_graft_pos(graft->oid.hash); if (0 <= pos) { if (ignore_dups) @@ -147,12 +122,8 @@ int register_commit_graft(struct commit_graft *graft, int ignore_dups) 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); - } + ALLOC_GROW(commit_graft, commit_graft_nr + 1, commit_graft_alloc); + commit_graft_nr++; if (pos < commit_graft_nr) memmove(commit_graft + pos + 1, commit_graft + pos, @@ -167,22 +138,23 @@ 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; + const int entry_size = GIT_SHA1_HEXSZ + 1; while (len && isspace(buf[len-1])) buf[--len] = '\0'; if (buf[0] == '#' || buf[0] == '\0') return NULL; - if ((len + 1) % 41) + if ((len + 1) % entry_size) goto bad_graft_data; - i = (len + 1) / 41 - 1; - graft = xmalloc(sizeof(*graft) + 20 * i); + i = (len + 1) / entry_size - 1; + graft = xmalloc(st_add(sizeof(*graft), st_mult(GIT_SHA1_RAWSZ, i))); graft->nr_parent = i; - if (get_sha1_hex(buf, graft->sha1)) + if (get_oid_hex(buf, &graft->oid)) goto bad_graft_data; - for (i = 40; i < len; i += 41) { + for (i = GIT_SHA1_HEXSZ; i < len; i += entry_size) { if (buf[i] != ' ') goto bad_graft_data; - if (get_sha1_hex(buf + i + 1, graft->parent[i/41])) + if (get_sha1_hex(buf + i + 1, graft->parent[i/entry_size].hash)) goto bad_graft_data; } return graft; @@ -257,43 +229,128 @@ int unregister_shallow(const unsigned char *sha1) return 0; } +struct commit_buffer { + void *buffer; + unsigned long size; +}; +define_commit_slab(buffer_slab, struct commit_buffer); +static struct buffer_slab buffer_slab = COMMIT_SLAB_INIT(1, buffer_slab); + +void set_commit_buffer(struct commit *commit, void *buffer, unsigned long size) +{ + struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit); + v->buffer = buffer; + v->size = size; +} + +const void *get_cached_commit_buffer(const struct commit *commit, unsigned long *sizep) +{ + struct commit_buffer *v = buffer_slab_peek(&buffer_slab, commit); + if (!v) { + if (sizep) + *sizep = 0; + return NULL; + } + if (sizep) + *sizep = v->size; + return v->buffer; +} + +const void *get_commit_buffer(const struct commit *commit, unsigned long *sizep) +{ + const void *ret = get_cached_commit_buffer(commit, sizep); + if (!ret) { + enum object_type type; + unsigned long size; + ret = read_sha1_file(commit->object.oid.hash, &type, &size); + if (!ret) + die("cannot read commit object %s", + oid_to_hex(&commit->object.oid)); + if (type != OBJ_COMMIT) + die("expected commit for %s, got %s", + oid_to_hex(&commit->object.oid), typename(type)); + if (sizep) + *sizep = size; + } + return ret; +} + +void unuse_commit_buffer(const struct commit *commit, const void *buffer) +{ + struct commit_buffer *v = buffer_slab_peek(&buffer_slab, commit); + if (!(v && v->buffer == buffer)) + free((void *)buffer); +} + +void free_commit_buffer(struct commit *commit) +{ + struct commit_buffer *v = buffer_slab_peek(&buffer_slab, commit); + if (v) { + free(v->buffer); + v->buffer = NULL; + v->size = 0; + } +} + +const void *detach_commit_buffer(struct commit *commit, unsigned long *sizep) +{ + struct commit_buffer *v = buffer_slab_peek(&buffer_slab, commit); + void *ret; + + if (!v) { + if (sizep) + *sizep = 0; + return NULL; + } + ret = v->buffer; + if (sizep) + *sizep = v->size; + + v->buffer = NULL; + v->size = 0; + return ret; +} + int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long size) { const char *tail = buffer; const char *bufptr = buffer; - unsigned char parent[20]; + struct object_id parent; struct commit_list **pptr; struct commit_graft *graft; + const int tree_entry_len = GIT_SHA1_HEXSZ + 5; + const int parent_entry_len = GIT_SHA1_HEXSZ + 7; if (item->object.parsed) return 0; item->object.parsed = 1; tail += size; - if (tail <= bufptr + 46 || memcmp(bufptr, "tree ", 5) || bufptr[45] != '\n') - return error("bogus commit object %s", sha1_to_hex(item->object.sha1)); - if (get_sha1_hex(bufptr + 5, parent) < 0) + if (tail <= bufptr + tree_entry_len + 1 || memcmp(bufptr, "tree ", 5) || + bufptr[tree_entry_len] != '\n') + return error("bogus commit object %s", oid_to_hex(&item->object.oid)); + if (get_sha1_hex(bufptr + 5, parent.hash) < 0) return error("bad tree pointer in commit %s", - sha1_to_hex(item->object.sha1)); - item->tree = lookup_tree(parent); - bufptr += 46; /* "tree " + "hex sha1" + "\n" */ + oid_to_hex(&item->object.oid)); + item->tree = lookup_tree(parent.hash); + bufptr += tree_entry_len + 1; /* "tree " + "hex sha1" + "\n" */ pptr = &item->parents; - graft = lookup_commit_graft(item->object.sha1); - while (bufptr + 48 < tail && !memcmp(bufptr, "parent ", 7)) { + graft = lookup_commit_graft(item->object.oid.hash); + while (bufptr + parent_entry_len < tail && !memcmp(bufptr, "parent ", 7)) { struct commit *new_parent; - if (tail <= bufptr + 48 || - get_sha1_hex(bufptr + 7, parent) || - bufptr[47] != '\n') - return error("bad parents in commit %s", sha1_to_hex(item->object.sha1)); - bufptr += 48; + if (tail <= bufptr + parent_entry_len + 1 || + get_sha1_hex(bufptr + 7, parent.hash) || + bufptr[parent_entry_len] != '\n') + return error("bad parents in commit %s", oid_to_hex(&item->object.oid)); + bufptr += parent_entry_len + 1; /* * The clone is shallow if nr_parent < 0, and we must * not traverse its real parents even when we unhide them. */ if (graft && (graft->nr_parent < 0 || grafts_replace_parents)) continue; - new_parent = lookup_commit(parent); + new_parent = lookup_commit(parent.hash); if (new_parent) pptr = &commit_list_insert(new_parent, pptr)->next; } @@ -301,7 +358,7 @@ int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long s int i; struct commit *new_parent; for (i = 0; i < graft->nr_parent; i++) { - new_parent = lookup_commit(graft->parent[i]); + new_parent = lookup_commit(graft->parent[i].hash); if (!new_parent) continue; pptr = &commit_list_insert(new_parent, pptr)->next; @@ -312,7 +369,7 @@ int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long s return 0; } -int parse_commit(struct commit *item) +int parse_commit_gently(struct commit *item, int quiet_on_missing) { enum object_type type; void *buffer; @@ -323,18 +380,19 @@ int parse_commit(struct commit *item) return -1; if (item->object.parsed) return 0; - buffer = read_sha1_file(item->object.sha1, &type, &size); + buffer = read_sha1_file(item->object.oid.hash, &type, &size); if (!buffer) - return error("Could not read %s", - sha1_to_hex(item->object.sha1)); + return quiet_on_missing ? -1 : + error("Could not read %s", + oid_to_hex(&item->object.oid)); if (type != OBJ_COMMIT) { free(buffer); return error("Object %s not a commit", - sha1_to_hex(item->object.sha1)); + oid_to_hex(&item->object.oid)); } ret = parse_commit_buffer(item, buffer, size); if (save_commit_buffer && !ret) { - item->buffer = buffer; + set_commit_buffer(item, buffer, size); return 0; } free(buffer); @@ -345,7 +403,7 @@ void parse_commit_or_die(struct commit *item) { if (parse_commit(item)) die("unable to parse commit %s", - item ? sha1_to_hex(item->object.sha1) : "(null)"); + item ? oid_to_hex(&item->object.oid) : "(null)"); } int find_commit_subject(const char *commit_buffer, const char **subject) @@ -356,7 +414,7 @@ int find_commit_subject(const char *commit_buffer, const char **subject) while (*p && (*p != '\n' || p[1] != '\n')) p++; if (*p) { - p += 2; + p = skip_blank_lines(p + 2); for (eol = p; *eol && *eol != '\n'; eol++) ; /* do nothing */ } else @@ -389,12 +447,7 @@ struct commit_list *copy_commit_list(struct commit_list *list) struct commit_list *head = NULL; struct commit_list **pp = &head; while (list) { - struct commit_list *new; - new = xmalloc(sizeof(struct commit_list)); - new->item = list->item; - new->next = NULL; - *pp = new; - pp = &new->next; + pp = commit_list_append(list->item, pp); list = list->next; } return head; @@ -402,11 +455,8 @@ struct commit_list *copy_commit_list(struct commit_list *list) void free_commit_list(struct commit_list *list) { - while (list) { - struct commit_list *temp = list; - list = temp->next; - free(temp); - } + while (list) + pop_commit(&list); } struct commit_list * commit_list_insert_by_date(struct commit *item, struct commit_list **list) @@ -452,12 +502,8 @@ void commit_list_sort_by_date(struct commit_list **list) struct commit *pop_most_recent_commit(struct commit_list **list, unsigned int mark) { - struct commit *ret = (*list)->item; + struct commit *ret = pop_commit(list); struct commit_list *parents = ret->parents; - struct commit_list *old = *list; - - *list = (*list)->next; - free(old); while (parents) { struct commit *commit = parents->item; @@ -517,7 +563,7 @@ void clear_commit_marks_for_object_array(struct object_array *a, unsigned mark) for (i = 0; i < a->nr; i++) { object = a->objects[i].item; - commit = lookup_commit_reference_gently(object->sha1, 1); + commit = lookup_commit_reference_gently(object->oid.hash, 1); if (commit) clear_commit_marks(commit, mark); } @@ -548,36 +594,19 @@ define_commit_slab(author_date_slab, unsigned long); static void record_author_date(struct author_date_slab *author_date, struct commit *commit) { - const char *buf, *line_end; - char *buffer = NULL; + const char *buffer = get_commit_buffer(commit, NULL); struct ident_split ident; + const char *ident_line; + size_t ident_len; char *date_end; unsigned long date; - if (!commit->buffer) { - unsigned long size; - enum object_type type; - buffer = read_sha1_file(commit->object.sha1, &type, &size); - if (!buffer) - return; - } - - for (buf = commit->buffer ? commit->buffer : buffer; - buf; - buf = line_end + 1) { - line_end = strchrnul(buf, '\n'); - if (!starts_with(buf, "author ")) { - if (!line_end[0] || line_end[1] == '\n') - return; /* end of header */ - continue; - } - if (split_ident_line(&ident, - buf + strlen("author "), - line_end - (buf + strlen("author "))) || - !ident.date_begin || !ident.date_end) - goto fail_exit; /* malformed "author" line */ - break; - } + ident_line = find_commit_header(buffer, "author", &ident_len); + if (!ident_line) + goto fail_exit; /* no author line */ + if (split_ident_line(&ident, ident_line, ident_len) || + !ident.date_begin || !ident.date_end) + goto fail_exit; /* malformed "author" line */ date = strtoul(ident.date_begin, &date_end, 10); if (date_end != ident.date_end) @@ -585,7 +614,7 @@ static void record_author_date(struct author_date_slab *author_date, *(author_date_slab_at(author_date, commit)) = date; fail_exit: - free(buffer); + unuse_commit_buffer(commit, buffer); } static int compare_commits_by_author_date(const void *a_, const void *b_, @@ -731,7 +760,7 @@ void sort_in_topological_order(struct commit_list **list, enum rev_sort_order so /* merge-base stuff */ -/* bits #0..15 in revision.h */ +/* Remember to update object flag allocation in object.h */ #define PARENT1 (1u<<16) #define PARENT2 (1u<<17) #define STALE (1u<<18) @@ -739,45 +768,41 @@ void sort_in_topological_order(struct commit_list **list, enum rev_sort_order so static const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT); -static struct commit *interesting(struct commit_list *list) +static int queue_has_nonstale(struct prio_queue *queue) { - while (list) { - struct commit *commit = list->item; - list = list->next; - if (commit->object.flags & STALE) - continue; - return commit; + int i; + for (i = 0; i < queue->nr; i++) { + struct commit *commit = queue->array[i].data; + if (!(commit->object.flags & STALE)) + return 1; } - return NULL; + return 0; } /* all input commits in one and twos[] must have been parsed! */ static struct commit_list *paint_down_to_common(struct commit *one, int n, struct commit **twos) { - struct commit_list *list = NULL; + struct prio_queue queue = { compare_commits_by_commit_date }; struct commit_list *result = NULL; int i; one->object.flags |= PARENT1; - commit_list_insert_by_date(one, &list); - if (!n) - return list; + if (!n) { + commit_list_append(one, &result); + return result; + } + prio_queue_put(&queue, one); + for (i = 0; i < n; i++) { twos[i]->object.flags |= PARENT2; - commit_list_insert_by_date(twos[i], &list); + prio_queue_put(&queue, twos[i]); } - while (interesting(list)) { - struct commit *commit; + while (queue_has_nonstale(&queue)) { + struct commit *commit = prio_queue_get(&queue); struct commit_list *parents; - struct commit_list *next; int flags; - commit = list->item; - next = list->next; - free(list); - list = next; - flags = commit->object.flags & (PARENT1 | PARENT2 | STALE); if (flags == (PARENT1 | PARENT2)) { if (!(commit->object.flags & RESULT)) { @@ -796,11 +821,11 @@ static struct commit_list *paint_down_to_common(struct commit *one, int n, struc if (parse_commit(p)) return NULL; p->object.flags |= flags; - commit_list_insert_by_date(p, &list); + prio_queue_put(&queue, p); } } - free_commit_list(list); + clear_prio_queue(&queue); return result; } @@ -829,11 +854,9 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co list = paint_down_to_common(one, n, twos); while (list) { - struct commit_list *next = list->next; - if (!(list->item->object.flags & STALE)) - commit_list_insert_by_date(list->item, &result); - free(list); - list = next; + struct commit *commit = pop_commit(&list); + if (!(commit->object.flags & STALE)) + commit_list_insert_by_date(commit, &result); } return result; } @@ -852,7 +875,7 @@ struct commit_list *get_octopus_merge_bases(struct commit_list *in) for (j = ret; j; j = j->next) { struct commit_list *bases; - bases = get_merge_bases(i->item, j->item, 1); + bases = get_merge_bases(i->item, j->item); if (!new) new = bases; else @@ -880,7 +903,7 @@ static int remove_redundant(struct commit **array, int cnt) work = xcalloc(cnt, sizeof(*work)); redundant = xcalloc(cnt, 1); - filled_index = xmalloc(sizeof(*filled_index) * (cnt - 1)); + ALLOC_ARRAY(filled_index, cnt - 1); for (i = 0; i < cnt; i++) parse_commit(array[i]); @@ -908,7 +931,7 @@ static int remove_redundant(struct commit **array, int cnt) } /* Now collect the result */ - memcpy(work, array, sizeof(*array) * cnt); + COPY_ARRAY(work, array, cnt); for (i = filled = 0; i < cnt; i++) if (!redundant[i]) array[filled++] = work[i]; @@ -921,10 +944,10 @@ static int remove_redundant(struct commit **array, int cnt) return filled; } -struct commit_list *get_merge_bases_many(struct commit *one, - int n, - struct commit **twos, - int cleanup) +static struct commit_list *get_merge_bases_many_0(struct commit *one, + int n, + struct commit **twos, + int cleanup) { struct commit_list *list; struct commit **rslt; @@ -945,12 +968,7 @@ struct commit_list *get_merge_bases_many(struct commit *one, } /* There are more than one */ - cnt = 0; - list = result; - while (list) { - list = list->next; - cnt++; - } + cnt = commit_list_count(result); rslt = xcalloc(cnt, sizeof(*rslt)); for (list = result, i = 0; list; list = list->next) rslt[i++] = list->item; @@ -967,10 +985,23 @@ struct commit_list *get_merge_bases_many(struct commit *one, return result; } -struct commit_list *get_merge_bases(struct commit *one, struct commit *two, - int cleanup) +struct commit_list *get_merge_bases_many(struct commit *one, + int n, + struct commit **twos) +{ + return get_merge_bases_many_0(one, n, twos, 1); +} + +struct commit_list *get_merge_bases_many_dirty(struct commit *one, + int n, + struct commit **twos) +{ + return get_merge_bases_many_0(one, n, twos, 0); +} + +struct commit_list *get_merge_bases(struct commit *one, struct commit *two) { - return get_merge_bases_many(one, 1, &two, cleanup); + return get_merge_bases_many_0(one, 1, &two, 1); } /* @@ -1041,7 +1072,7 @@ struct commit_list *reduce_heads(struct commit_list *heads) p->item->object.flags |= STALE; num_head++; } - array = xcalloc(sizeof(*array), num_head); + array = xcalloc(num_head, sizeof(*array)); for (p = heads, i = 0; p; p = p->next) { if (p->item->object.flags & STALE) { array[i++] = p->item; @@ -1061,9 +1092,14 @@ static int do_sign_commit(struct strbuf *buf, const char *keyid) { struct strbuf sig = STRBUF_INIT; int inspos, copypos; + const char *eoh; /* find the end of the header */ - inspos = strstr(buf->buf, "\n\n") - buf->buf + 1; + eoh = strstr(buf->buf, "\n\n"); + if (!eoh) + inspos = buf->len; + else + inspos = eoh - buf->buf + 1; if (!keyid || !*keyid) keyid = get_signing_key(); @@ -1090,17 +1126,14 @@ static int do_sign_commit(struct strbuf *buf, const char *keyid) return 0; } -int parse_signed_commit(const unsigned char *sha1, +int parse_signed_commit(const struct commit *commit, struct strbuf *payload, struct strbuf *signature) { + unsigned long size; - enum object_type type; - char *buffer = read_sha1_file(sha1, &type, &size); + const char *buffer = get_commit_buffer(commit, &size); int in_signature, saw_signature = -1; - char *line, *tail; - - if (!buffer || type != OBJ_COMMIT) - goto cleanup; + const char *line, *tail; line = buffer; tail = buffer + size; @@ -1108,7 +1141,7 @@ int parse_signed_commit(const unsigned char *sha1, saw_signature = 0; while (line < tail) { const char *sig = NULL; - char *next = memchr(line, '\n', tail - line); + const char *next = memchr(line, '\n', tail - line); next = next ? next + 1 : tail; if (in_signature && line[0] == ' ') @@ -1129,11 +1162,44 @@ int parse_signed_commit(const unsigned char *sha1, } line = next; } - cleanup: - free(buffer); + unuse_commit_buffer(commit, buffer); return saw_signature; } +int remove_signature(struct strbuf *buf) +{ + const char *line = buf->buf; + const char *tail = buf->buf + buf->len; + int in_signature = 0; + const char *sig_start = NULL; + const char *sig_end = NULL; + + while (line < tail) { + const char *next = memchr(line, '\n', tail - line); + next = next ? next + 1 : tail; + + if (in_signature && line[0] == ' ') + sig_end = next; + else if (starts_with(line, gpg_sig_header) && + line[gpg_sig_header_len] == ' ') { + sig_start = line; + sig_end = next; + in_signature = 1; + } else { + if (*line == '\n') + /* dump the whole remainder of the buffer */ + next = tail; + in_signature = 0; + } + line = next; + } + + if (sig_start) + strbuf_remove(buf, sig_start - buf->buf, sig_end - sig_start); + + return sig_start != NULL; +} + static void handle_signed_tag(struct commit *parent, struct commit_extra_header ***tail) { struct merge_remote_desc *desc; @@ -1145,7 +1211,7 @@ static void handle_signed_tag(struct commit *parent, struct commit_extra_header desc = merge_remote_util(parent); if (!desc || !desc->obj) return; - buf = read_sha1_file(desc->obj->sha1, &type, &size); + buf = read_sha1_file(desc->obj->oid.hash, &type, &size); if (!buf || type != OBJ_TAG) goto free_return; len = parse_signature(buf, size); @@ -1174,72 +1240,24 @@ free_return: free(buf); } -static struct { - char result; - const char *check; -} sigcheck_gpg_status[] = { - { 'G', "\n[GNUPG:] GOODSIG " }, - { 'B', "\n[GNUPG:] BADSIG " }, - { 'U', "\n[GNUPG:] TRUST_NEVER" }, - { 'U', "\n[GNUPG:] TRUST_UNDEFINED" }, -}; - -static void parse_gpg_output(struct signature_check *sigc) -{ - const char *buf = sigc->gpg_status; - int i; - - /* Iterate over all search strings */ - for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) { - const char *found, *next; - - if (starts_with(buf, sigcheck_gpg_status[i].check + 1)) { - /* At the very beginning of the buffer */ - found = buf + strlen(sigcheck_gpg_status[i].check + 1); - } else { - found = strstr(buf, sigcheck_gpg_status[i].check); - if (!found) - continue; - found += strlen(sigcheck_gpg_status[i].check); - } - sigc->result = sigcheck_gpg_status[i].result; - /* The trust messages are not followed by key/signer information */ - if (sigc->result != 'U') { - sigc->key = xmemdupz(found, 16); - found += 17; - next = strchrnul(found, '\n'); - sigc->signer = xmemdupz(found, next - found); - } - } -} - -void check_commit_signature(const struct commit* commit, struct signature_check *sigc) +int check_commit_signature(const struct commit *commit, struct signature_check *sigc) { struct strbuf payload = STRBUF_INIT; struct strbuf signature = STRBUF_INIT; - struct strbuf gpg_output = STRBUF_INIT; - struct strbuf gpg_status = STRBUF_INIT; - int status; + int ret = 1; sigc->result = 'N'; - if (parse_signed_commit(commit->object.sha1, - &payload, &signature) <= 0) + if (parse_signed_commit(commit, &payload, &signature) <= 0) goto out; - status = verify_signed_buffer(payload.buf, payload.len, - signature.buf, signature.len, - &gpg_output, &gpg_status); - if (status && !gpg_output.len) - goto out; - sigc->gpg_output = strbuf_detach(&gpg_output, NULL); - sigc->gpg_status = strbuf_detach(&gpg_status, NULL); - parse_gpg_output(sigc); + ret = check_signature(payload.buf, payload.len, signature.buf, + signature.len, sigc); out: - strbuf_release(&gpg_status); - strbuf_release(&gpg_output); strbuf_release(&payload); strbuf_release(&signature); + + return ret; } @@ -1269,14 +1287,25 @@ struct commit_extra_header *read_commit_extra_headers(struct commit *commit, { struct commit_extra_header *extra = NULL; unsigned long size; - enum object_type type; - char *buffer = read_sha1_file(commit->object.sha1, &type, &size); - if (buffer && type == OBJ_COMMIT) - extra = read_commit_extra_header_lines(buffer, size, exclude); - free(buffer); + const char *buffer = get_commit_buffer(commit, &size); + extra = read_commit_extra_header_lines(buffer, size, exclude); + unuse_commit_buffer(commit, buffer); return extra; } +void for_each_mergetag(each_mergetag_fn fn, struct commit *commit, void *data) +{ + struct commit_extra_header *extra, *to_free; + + to_free = read_commit_extra_headers(commit, NULL); + for (extra = to_free; extra; extra = extra->next) { + if (strcmp(extra->key, "mergetag")) + continue; /* not a merge tag */ + fn(commit, extra, data); + } + free_commit_extra_headers(to_free); +} + static inline int standard_header_field(const char *field, size_t len) { return ((len == 4 && !memcmp(field, "tree ", 5)) || @@ -1356,7 +1385,8 @@ void free_commit_extra_headers(struct commit_extra_header *extra) } } -int commit_tree(const struct strbuf *msg, const unsigned char *tree, +int commit_tree(const char *msg, size_t msg_len, + const unsigned char *tree, struct commit_list *parents, unsigned char *ret, const char *author, const char *sign_commit) { @@ -1364,7 +1394,7 @@ int commit_tree(const struct strbuf *msg, const unsigned char *tree, int result; append_merge_tag_headers(parents, &tail); - result = commit_tree_extended(msg, tree, parents, ret, + result = commit_tree_extended(msg, msg_len, tree, parents, ret, author, sign_commit, extra); free_commit_extra_headers(extra); return result; @@ -1481,11 +1511,12 @@ static int verify_utf8(struct strbuf *buf) } static const char commit_utf8_warn[] = -"Warning: commit message did not conform to UTF-8.\n" -"You may want to amend it after fixing the message, or set the config\n" -"variable i18n.commitencoding to the encoding your project uses.\n"; +N_("Warning: commit message did not conform to UTF-8.\n" + "You may want to amend it after fixing the message, or set the config\n" + "variable i18n.commitencoding to the encoding your project uses.\n"); -int commit_tree_extended(const struct strbuf *msg, const unsigned char *tree, +int commit_tree_extended(const char *msg, size_t msg_len, + const unsigned char *tree, struct commit_list *parents, unsigned char *ret, const char *author, const char *sign_commit, struct commit_extra_header *extra) @@ -1496,7 +1527,7 @@ int commit_tree_extended(const struct strbuf *msg, const unsigned char *tree, assert_sha1_type(tree, OBJ_TREE); - if (memchr(msg->buf, '\0', msg->len)) + if (memchr(msg, '\0', msg_len)) return error("a NUL byte in commit log message not allowed."); /* Not having i18n.commitencoding is the same as having utf-8 */ @@ -1511,13 +1542,9 @@ int commit_tree_extended(const struct strbuf *msg, const unsigned char *tree, * if everything else stays the same. */ while (parents) { - struct commit_list *next = parents->next; - struct commit *parent = parents->item; - + struct commit *parent = pop_commit(&parents); strbuf_addf(&buffer, "parent %s\n", - sha1_to_hex(parent->object.sha1)); - free(parents); - parents = next; + oid_to_hex(&parent->object.oid)); } /* Person/date information */ @@ -1535,11 +1562,11 @@ int commit_tree_extended(const struct strbuf *msg, const unsigned char *tree, strbuf_addch(&buffer, '\n'); /* And add the comment */ - strbuf_addbuf(&buffer, msg); + strbuf_add(&buffer, msg, msg_len); /* And check the encoding */ if (encoding_is_utf8 && !verify_utf8(&buffer)) - fprintf(stderr, commit_utf8_warn); + fprintf(stderr, _(commit_utf8_warn)); if (sign_commit && do_sign_commit(&buffer, sign_commit)) return -1; @@ -1549,22 +1576,26 @@ int commit_tree_extended(const struct strbuf *msg, const unsigned char *tree, return result; } +void set_merge_remote_desc(struct commit *commit, + const char *name, struct object *obj) +{ + struct merge_remote_desc *desc; + FLEX_ALLOC_STR(desc, name, name); + desc->obj = obj; + commit->util = desc; +} + struct commit *get_merge_parent(const char *name) { struct object *obj; struct commit *commit; - unsigned char sha1[20]; - if (get_sha1(name, sha1)) + struct object_id oid; + if (get_sha1(name, oid.hash)) return NULL; - obj = parse_object(sha1); + obj = parse_object(oid.hash); commit = (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT); - if (commit && !commit->util) { - struct merge_remote_desc *desc; - desc = xmalloc(sizeof(*desc)); - desc->obj = obj; - desc->name = strdup(name); - commit->util = desc; - } + if (commit && !commit->util) + set_merge_remote_desc(commit, name, obj); return commit; } @@ -1595,12 +1626,70 @@ struct commit_list **commit_list_append(struct commit *commit, return &new->next; } -void print_commit_list(struct commit_list *list, - const char *format_cur, - const char *format_last) +const char *find_commit_header(const char *msg, const char *key, size_t *out_len) { - for ( ; list; list = list->next) { - const char *format = list->next ? format_cur : format_last; - printf(format, sha1_to_hex(list->item->object.sha1)); + int key_len = strlen(key); + const char *line = msg; + + while (line) { + const char *eol = strchrnul(line, '\n'); + + if (line == eol) + return NULL; + + if (eol - line > key_len && + !strncmp(line, key, key_len) && + line[key_len] == ' ') { + *out_len = eol - line - key_len - 1; + return line + key_len + 1; + } + line = *eol ? eol + 1 : NULL; + } + return NULL; +} + +/* + * Inspect the given string and determine the true "end" of the log message, in + * order to find where to put a new Signed-off-by: line. Ignored are + * trailing comment lines and blank lines, and also the traditional + * "Conflicts:" block that is not commented out, so that we can use + * "git commit -s --amend" on an existing commit that forgot to remove + * it. + * + * Returns the number of bytes from the tail to ignore, to be fed as + * the second parameter to append_signoff(). + */ +int ignore_non_trailer(const char *buf, size_t len) +{ + int boc = 0; + int bol = 0; + int in_old_conflicts_block = 0; + + while (bol < len) { + const char *next_line = memchr(buf + bol, '\n', len - bol); + + if (!next_line) + next_line = buf + len; + else + next_line++; + + if (buf[bol] == comment_line_char || buf[bol] == '\n') { + /* is this the first of the run of comments? */ + if (!boc) + boc = bol; + /* otherwise, it is just continuing */ + } else if (starts_with(buf + bol, "Conflicts:\n")) { + in_old_conflicts_block = 1; + if (!boc) + boc = bol; + } else if (in_old_conflicts_block && buf[bol] == '\t') { + ; /* a pathname in the conflicts block */ + } else if (boc) { + /* the previous was not trailing comment */ + boc = 0; + in_old_conflicts_block = 0; + } + bol = next_line - buf; } + return boc ? len - boc : 0; } |