diff options
author | Jiang Xin <worldhello.net@gmail.com> | 2012-04-28 15:54:37 +0800 |
---|---|---|
committer | Jiang Xin <worldhello.net@gmail.com> | 2012-04-28 15:54:37 +0800 |
commit | 69c835701b507e336fd5354f9f3b05e42d20c07d (patch) | |
tree | cc6b079231ddc1ab3e04654cbdf8937ef0d6632b /builtin/fmt-merge-msg.c | |
parent | l10n: Add the German translation team and initialize de.po (diff) | |
parent | The sixth batch of topics graduated to 'master' (diff) | |
download | tgif-69c835701b507e336fd5354f9f3b05e42d20c07d.tar.xz |
Merge master branch for tracking l10n updates of next release
Use master branch to track l10n updates for git next release, while
use maint branch to track l10n updates for git stable version.
Diffstat (limited to 'builtin/fmt-merge-msg.c')
-rw-r--r-- | builtin/fmt-merge-msg.c | 242 |
1 files changed, 229 insertions, 13 deletions
diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c index c81a7fef26..a517f1794a 100644 --- a/builtin/fmt-merge-msg.c +++ b/builtin/fmt-merge-msg.c @@ -27,6 +27,8 @@ int fmt_merge_msg_config(const char *key, const char *value, void *cb) merge_log_config = DEFAULT_MERGE_LOG_LEN; } else if (!strcmp(key, "merge.branchdesc")) { use_branch_desc = git_config_bool(key, value); + } else { + return git_default_config(key, value, cb); } return 0; } @@ -53,7 +55,48 @@ static void init_src_data(struct src_data *data) static struct string_list srcs = STRING_LIST_INIT_DUP; static struct string_list origins = STRING_LIST_INIT_DUP; -static int handle_line(char *line) +struct merge_parents { + int alloc, nr; + struct merge_parent { + unsigned char given[20]; + unsigned char commit[20]; + unsigned char used; + } *item; +}; + +/* + * I know, I know, this is inefficient, but you won't be pulling and merging + * hundreds of heads at a time anyway. + */ +static struct merge_parent *find_merge_parent(struct merge_parents *table, + unsigned char *given, + unsigned char *commit) +{ + int i; + for (i = 0; i < table->nr; i++) { + if (given && hashcmp(table->item[i].given, given)) + continue; + if (commit && hashcmp(table->item[i].commit, commit)) + continue; + return &table->item[i]; + } + return NULL; +} + +static void add_merge_parent(struct merge_parents *table, + unsigned char *given, + unsigned char *commit) +{ + if (table->nr && find_merge_parent(table, given, commit)) + return; + ALLOC_GROW(table->item, table->nr + 1, table->alloc); + hashcpy(table->item[table->nr].given, given); + hashcpy(table->item[table->nr].commit, commit); + table->item[table->nr].used = 0; + table->nr++; +} + +static int handle_line(char *line, struct merge_parents *merge_parents) { int i, len = strlen(line); struct origin_data *origin_data; @@ -61,6 +104,7 @@ static int handle_line(char *line) struct src_data *src_data; struct string_list_item *item; int pulling_head = 0; + unsigned char sha1[20]; if (len < 43 || line[40] != '\t') return 1; @@ -71,14 +115,15 @@ static int handle_line(char *line) if (line[41] != '\t') return 2; - line[40] = 0; - origin_data = xcalloc(1, sizeof(struct origin_data)); - i = get_sha1(line, origin_data->sha1); - line[40] = '\t'; - if (i) { - free(origin_data); + i = get_sha1_hex(line, sha1); + if (i) return 3; - } + + if (!find_merge_parent(merge_parents, sha1, NULL)) + return 0; /* subsumed by other parents */ + + origin_data = xcalloc(1, sizeof(struct origin_data)); + hashcpy(origin_data->sha1, sha1); if (line[len - 1] == '\n') line[len - 1] = 0; @@ -180,6 +225,101 @@ static void add_branch_desc(struct strbuf *out, const char *name) strbuf_release(&desc); } +#define util_as_integral(elem) ((intptr_t)((elem)->util)) + +static void record_person(int which, struct string_list *people, + struct commit *commit) +{ + char name_buf[MAX_GITNAME], *name, *name_end; + struct string_list_item *elem; + const char *field = (which == 'a') ? "\nauthor " : "\ncommitter "; + + name = strstr(commit->buffer, field); + if (!name) + return; + name += strlen(field); + name_end = strchrnul(name, '<'); + if (*name_end) + name_end--; + while (isspace(*name_end) && name <= name_end) + name_end--; + if (name_end < name || name + MAX_GITNAME <= name_end) + return; + memcpy(name_buf, name, name_end - name + 1); + name_buf[name_end - name + 1] = '\0'; + + elem = string_list_lookup(people, name_buf); + if (!elem) { + elem = string_list_insert(people, name_buf); + elem->util = (void *)0; + } + elem->util = (void*)(util_as_integral(elem) + 1); +} + +static int cmp_string_list_util_as_integral(const void *a_, const void *b_) +{ + const struct string_list_item *a = a_, *b = b_; + return util_as_integral(b) - util_as_integral(a); +} + +static void add_people_count(struct strbuf *out, struct string_list *people) +{ + if (people->nr == 1) + strbuf_addf(out, "%s", people->items[0].string); + else if (people->nr == 2) + strbuf_addf(out, "%s (%d) and %s (%d)", + people->items[0].string, + (int)util_as_integral(&people->items[0]), + people->items[1].string, + (int)util_as_integral(&people->items[1])); + else if (people->nr) + strbuf_addf(out, "%s (%d) and others", + people->items[0].string, + (int)util_as_integral(&people->items[0])); +} + +static void credit_people(struct strbuf *out, + struct string_list *them, + int kind) +{ + const char *label; + const char *me; + + if (kind == 'a') { + label = "\nBy "; + me = git_author_info(IDENT_NO_DATE); + } else { + label = "\nvia "; + me = git_committer_info(IDENT_NO_DATE); + } + + if (!them->nr || + (them->nr == 1 && + me && + (me = skip_prefix(me, them->items->string)) != NULL && + skip_prefix(me, " <"))) + return; + strbuf_addstr(out, label); + add_people_count(out, them); +} + +static void add_people_info(struct strbuf *out, + struct string_list *authors, + struct string_list *committers) +{ + if (authors->nr) + qsort(authors->items, + authors->nr, sizeof(authors->items[0]), + cmp_string_list_util_as_integral); + if (committers->nr) + qsort(committers->items, + committers->nr, sizeof(committers->items[0]), + cmp_string_list_util_as_integral); + + credit_people(out, authors, 'a'); + credit_people(out, committers, 'c'); +} + static void shortlog(const char *name, struct origin_data *origin_data, struct commit *head, @@ -190,6 +330,8 @@ static void shortlog(const char *name, struct commit *commit; struct object *branch; struct string_list subjects = STRING_LIST_INIT_DUP; + struct string_list authors = STRING_LIST_INIT_DUP; + struct string_list committers = STRING_LIST_INIT_DUP; int flags = UNINTERESTING | TREESAME | SEEN | SHOWN | ADDED; struct strbuf sb = STRBUF_INIT; const unsigned char *sha1 = origin_data->sha1; @@ -199,7 +341,6 @@ static void shortlog(const char *name, return; setup_revisions(0, NULL, rev, NULL); - rev->ignore_merges = 1; add_pending_object(rev, branch, name); add_pending_object(rev, &head->object, "^HEAD"); head->object.flags |= UNINTERESTING; @@ -208,10 +349,15 @@ static void shortlog(const char *name, while ((commit = get_revision(rev)) != NULL) { struct pretty_print_context ctx = {0}; - /* ignore merges */ - if (commit->parents && commit->parents->next) + if (commit->parents && commit->parents->next) { + /* do not list a merge but count committer */ + record_person('c', &committers, commit); continue; - + } + if (!count) + /* the 'tip' committer */ + record_person('c', &committers, commit); + record_person('a', &authors, commit); count++; if (subjects.nr > limit) continue; @@ -226,6 +372,7 @@ static void shortlog(const char *name, string_list_append(&subjects, strbuf_detach(&sb, NULL)); } + add_people_info(out, &authors, &committers); if (count > limit) strbuf_addf(out, "\n* %s: (%d commits)\n", name, count); else @@ -246,6 +393,8 @@ static void shortlog(const char *name, rev->commits = NULL; rev->pending.nr = 0; + string_list_clear(&authors, 0); + string_list_clear(&committers, 0); string_list_clear(&subjects, 0); } @@ -366,6 +515,67 @@ static void fmt_merge_msg_sigs(struct strbuf *out) strbuf_release(&tagbuf); } +static void find_merge_parents(struct merge_parents *result, + struct strbuf *in, unsigned char *head) +{ + struct commit_list *parents, *next; + struct commit *head_commit; + int pos = 0, i, j; + + parents = NULL; + while (pos < in->len) { + int len; + char *p = in->buf + pos; + char *newline = strchr(p, '\n'); + unsigned char sha1[20]; + struct commit *parent; + struct object *obj; + + len = newline ? newline - p : strlen(p); + pos += len + !!newline; + + if (len < 43 || + get_sha1_hex(p, sha1) || + p[40] != '\t' || + p[41] != '\t') + continue; /* skip not-for-merge */ + /* + * Do not use get_merge_parent() here; we do not have + * "name" here and we do not want to contaminate its + * util field yet. + */ + obj = parse_object(sha1); + parent = (struct commit *)peel_to_type(NULL, 0, obj, OBJ_COMMIT); + if (!parent) + continue; + commit_list_insert(parent, &parents); + add_merge_parent(result, obj->sha1, parent->object.sha1); + } + head_commit = lookup_commit(head); + if (head_commit) + commit_list_insert(head_commit, &parents); + parents = reduce_heads(parents); + + while (parents) { + for (i = 0; i < result->nr; i++) + if (!hashcmp(result->item[i].commit, + parents->item->object.sha1)) + result->item[i].used = 1; + next = parents->next; + free(parents); + parents = next; + } + + for (i = j = 0; i < result->nr; i++) { + if (result->item[i].used) { + if (i != j) + result->item[j] = result->item[i]; + j++; + } + } + result->nr = j; +} + int fmt_merge_msg(struct strbuf *in, struct strbuf *out, struct fmt_merge_msg_opts *opts) { @@ -373,6 +583,9 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out, unsigned char head_sha1[20]; const char *current_branch; void *current_branch_to_free; + struct merge_parents merge_parents; + + memset(&merge_parents, 0, sizeof(merge_parents)); /* get current branch */ current_branch = current_branch_to_free = @@ -382,6 +595,8 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out, if (!prefixcmp(current_branch, "refs/heads/")) current_branch += 11; + find_merge_parents(&merge_parents, in, head_sha1); + /* get a line */ while (pos < in->len) { int len; @@ -392,7 +607,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out, pos += len + !!newline; i++; p[len] = 0; - if (handle_line(p)) + if (handle_line(p, &merge_parents)) die ("Error in line %d: %.*s", i, len, p); } @@ -423,6 +638,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out, strbuf_complete_line(out); free(current_branch_to_free); + free(merge_parents.item); return 0; } |