request-pull: rewrite to C
[git] / shortlog.c
1 #include "cache.h"
2 #include "shortlog.h"
3 #include "commit.h"
4 #include "mailmap.h"
5 #include "utf8.h"
6
7 void shortlog_init(struct shortlog *log)
8 {
9         memset(log, 0, sizeof(*log));
10
11         read_mailmap(&log->mailmap, &log->common_repo_prefix);
12
13         log->list.strdup_strings = 1;
14         log->wrap = DEFAULT_WRAPLEN;
15         log->in1 = DEFAULT_INDENT1;
16         log->in2 = DEFAULT_INDENT2;
17 }
18
19 void shortlog_insert_one_record(struct shortlog *log,
20                 const char *author,
21                 const char *oneline)
22 {
23         const char *dot3 = log->common_repo_prefix;
24         char *buffer, *p;
25         struct string_list_item *item;
26         const char *mailbuf, *namebuf;
27         size_t namelen, maillen;
28         const char *eol;
29         struct strbuf subject = STRBUF_INIT;
30         struct strbuf namemailbuf = STRBUF_INIT;
31         struct ident_split ident;
32
33         if (split_ident_line(&ident, author, strlen(author)))
34                 return;
35
36         namebuf = ident.name_begin;
37         mailbuf = ident.mail_begin;
38         namelen = ident.name_end - ident.name_begin;
39         maillen = ident.mail_end - ident.mail_begin;
40
41         map_user(&log->mailmap, &mailbuf, &maillen, &namebuf, &namelen);
42         strbuf_add(&namemailbuf, namebuf, namelen);
43
44         if (log->email)
45                 strbuf_addf(&namemailbuf, " <%.*s>", (int)maillen, mailbuf);
46
47         item = string_list_insert(&log->list, namemailbuf.buf);
48         if (item->util == NULL)
49                 item->util = xcalloc(1, sizeof(struct string_list));
50
51         /* Skip any leading whitespace, including any blank lines. */
52         while (*oneline && isspace(*oneline))
53                 oneline++;
54         eol = strchr(oneline, '\n');
55         if (!eol)
56                 eol = oneline + strlen(oneline);
57         if (!prefixcmp(oneline, "[PATCH")) {
58                 char *eob = strchr(oneline, ']');
59                 if (eob && (!eol || eob < eol))
60                         oneline = eob + 1;
61         }
62         while (*oneline && isspace(*oneline) && *oneline != '\n')
63                 oneline++;
64         format_subject(&subject, oneline, " ");
65         buffer = strbuf_detach(&subject, NULL);
66
67         if (dot3) {
68                 int dot3len = strlen(dot3);
69                 if (dot3len > 5) {
70                         while ((p = strstr(buffer, dot3)) != NULL) {
71                                 int taillen = strlen(p) - dot3len;
72                                 memcpy(p, "/.../", 5);
73                                 memmove(p + 5, p + dot3len, taillen + 1);
74                         }
75                 }
76         }
77
78         string_list_append(item->util, buffer);
79 }
80
81 void shortlog_add_commit(struct shortlog *log, struct commit *commit)
82 {
83         const char *author = NULL, *buffer;
84         struct strbuf buf = STRBUF_INIT;
85         struct strbuf ufbuf = STRBUF_INIT;
86
87         pp_commit_easy(CMIT_FMT_RAW, commit, &buf);
88         buffer = buf.buf;
89         while (*buffer && *buffer != '\n') {
90                 const char *eol = strchr(buffer, '\n');
91
92                 if (eol == NULL)
93                         eol = buffer + strlen(buffer);
94                 else
95                         eol++;
96
97                 if (!prefixcmp(buffer, "author "))
98                         author = buffer + 7;
99                 buffer = eol;
100         }
101         if (!author)
102                 die(_("Missing author: %s"),
103                     sha1_to_hex(commit->object.sha1));
104         if (log->user_format) {
105                 struct pretty_print_context ctx = {0};
106                 ctx.fmt = CMIT_FMT_USERFORMAT;
107                 ctx.abbrev = log->abbrev;
108                 ctx.subject = "";
109                 ctx.after_subject = "";
110                 ctx.date_mode = DATE_NORMAL;
111                 ctx.output_encoding = get_log_output_encoding();
112                 pretty_print_commit(&ctx, commit, &ufbuf);
113                 buffer = ufbuf.buf;
114         } else if (*buffer) {
115                 buffer++;
116         }
117         shortlog_insert_one_record(log, author, !*buffer ? "<none>" : buffer);
118         strbuf_release(&ufbuf);
119         strbuf_release(&buf);
120 }
121
122 static int compare_by_number(const void *a1, const void *a2)
123 {
124         const struct string_list_item *i1 = a1, *i2 = a2;
125         const struct string_list *l1 = i1->util, *l2 = i2->util;
126
127         if (l1->nr < l2->nr)
128                 return 1;
129         else if (l1->nr == l2->nr)
130                 return 0;
131         else
132                 return -1;
133 }
134
135 static void add_wrapped_shortlog_msg(struct strbuf *sb, const char *s,
136                                      const struct shortlog *log)
137 {
138         strbuf_add_wrapped_text(sb, s, log->in1, log->in2, log->wrap);
139         strbuf_addch(sb, '\n');
140 }
141
142 void shortlog_output(struct shortlog *log)
143 {
144         int i, j;
145         struct strbuf sb = STRBUF_INIT;
146
147         if (log->sort_by_number)
148                 qsort(log->list.items, log->list.nr, sizeof(struct string_list_item),
149                         compare_by_number);
150         for (i = 0; i < log->list.nr; i++) {
151                 struct string_list *onelines = log->list.items[i].util;
152
153                 if (log->summary) {
154                         printf("%6d\t%s\n", onelines->nr, log->list.items[i].string);
155                 } else {
156                         printf("%s (%d):\n", log->list.items[i].string, onelines->nr);
157                         for (j = onelines->nr - 1; j >= 0; j--) {
158                                 const char *msg = onelines->items[j].string;
159
160                                 if (log->wrap_lines) {
161                                         strbuf_reset(&sb);
162                                         add_wrapped_shortlog_msg(&sb, msg, log);
163                                         fwrite(sb.buf, sb.len, 1, stdout);
164                                 }
165                                 else
166                                         printf("      %s\n", msg);
167                         }
168                         putchar('\n');
169                 }
170
171                 onelines->strdup_strings = 1;
172                 string_list_clear(onelines, 0);
173                 free(onelines);
174                 log->list.items[i].util = NULL;
175         }
176
177         strbuf_release(&sb);
178         log->list.strdup_strings = 1;
179         string_list_clear(&log->list, 1);
180         clear_mailmap(&log->mailmap);
181 }