travis: add a test for everything
[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 (starts_with(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 (starts_with(buffer, "author "))
98                         author = buffer + 7;
99                 buffer = eol;
100         }
101         if (!author) {
102                 warning(_("Missing author: %s"),
103                     sha1_to_hex(commit->object.sha1));
104                 return;
105         }
106         if (log->user_format) {
107                 struct pretty_print_context ctx = {0};
108                 ctx.fmt = CMIT_FMT_USERFORMAT;
109                 ctx.abbrev = log->abbrev;
110                 ctx.subject = "";
111                 ctx.after_subject = "";
112                 ctx.date_mode = DATE_NORMAL;
113                 ctx.output_encoding = get_log_output_encoding();
114                 pretty_print_commit(&ctx, commit, &ufbuf);
115                 buffer = ufbuf.buf;
116         } else if (*buffer) {
117                 buffer++;
118         }
119         shortlog_insert_one_record(log, author, !*buffer ? "<none>" : buffer);
120         strbuf_release(&ufbuf);
121         strbuf_release(&buf);
122 }
123
124 static int compare_by_number(const void *a1, const void *a2)
125 {
126         const struct string_list_item *i1 = a1, *i2 = a2;
127         const struct string_list *l1 = i1->util, *l2 = i2->util;
128
129         if (l1->nr < l2->nr)
130                 return 1;
131         else if (l1->nr == l2->nr)
132                 return 0;
133         else
134                 return -1;
135 }
136
137 static void add_wrapped_shortlog_msg(struct strbuf *sb, const char *s,
138                                      const struct shortlog *log)
139 {
140         strbuf_add_wrapped_text(sb, s, log->in1, log->in2, log->wrap);
141         strbuf_addch(sb, '\n');
142 }
143
144 void shortlog_output(struct shortlog *log)
145 {
146         int i, j;
147         struct strbuf sb = STRBUF_INIT;
148
149         if (log->sort_by_number)
150                 qsort(log->list.items, log->list.nr, sizeof(struct string_list_item),
151                         compare_by_number);
152         for (i = 0; i < log->list.nr; i++) {
153                 struct string_list *onelines = log->list.items[i].util;
154
155                 if (log->summary) {
156                         printf("%6d\t%s\n", onelines->nr, log->list.items[i].string);
157                 } else {
158                         printf("%s (%d):\n", log->list.items[i].string, onelines->nr);
159                         for (j = onelines->nr - 1; j >= 0; j--) {
160                                 const char *msg = onelines->items[j].string;
161
162                                 if (log->wrap_lines) {
163                                         strbuf_reset(&sb);
164                                         add_wrapped_shortlog_msg(&sb, msg, log);
165                                         fwrite(sb.buf, sb.len, 1, stdout);
166                                 }
167                                 else
168                                         printf("      %s\n", msg);
169                         }
170                         putchar('\n');
171                 }
172
173                 onelines->strdup_strings = 1;
174                 string_list_clear(onelines, 0);
175                 free(onelines);
176                 log->list.items[i].util = NULL;
177         }
178
179         strbuf_release(&sb);
180         log->list.strdup_strings = 1;
181         string_list_clear(&log->list, 1);
182         clear_mailmap(&log->mailmap);
183 }