26212b009c6b1bb1a17dacc174d2e59d0dc54ef1
1#include "builtin.h"
2#include "cache.h"
3#include "commit.h"
4#include "diff.h"
5#include "path-list.h"
6#include "revision.h"
7#include <string.h>
8
9static const char shortlog_usage[] =
10"git-shortlog [-n] [-s] [<commit-id>... ]\n";
11
12static int compare_by_number(const void *a1, const void *a2)
13{
14 const struct path_list_item *i1 = a1, *i2 = a2;
15 const struct path_list *l1 = i1->util, *l2 = i2->util;
16
17 if (l1->nr < l2->nr)
18 return -1;
19 else if (l1->nr == l2->nr)
20 return 0;
21 else
22 return +1;
23}
24
25static struct path_list_item mailmap_list[] = {
26 { "R.Marek@sh.cvut.cz", (void*)"Rudolf Marek" },
27 { "Ralf.Wildenhues@gmx.de", (void*)"Ralf Wildenhues" },
28 { "aherrman@de.ibm.com", (void*)"Andreas Herrmann" },
29 { "akpm@osdl.org", (void*)"Andrew Morton" },
30 { "andrew.vasquez@qlogic.com", (void*)"Andrew Vasquez" },
31 { "aquynh@gmail.com", (void*)"Nguyen Anh Quynh" },
32 { "axboe@suse.de", (void*)"Jens Axboe" },
33 { "blaisorblade@yahoo.it", (void*)"Paolo 'Blaisorblade' Giarrusso" },
34 { "bunk@stusta.de", (void*)"Adrian Bunk" },
35 { "domen@coderock.org", (void*)"Domen Puncer" },
36 { "dougg@torque.net", (void*)"Douglas Gilbert" },
37 { "dwmw2@shinybook.infradead.org", (void*)"David Woodhouse" },
38 { "ecashin@coraid.com", (void*)"Ed L Cashin" },
39 { "felix@derklecks.de", (void*)"Felix Moeller" },
40 { "fzago@systemfabricworks.com", (void*)"Frank Zago" },
41 { "gregkh@suse.de", (void*)"Greg Kroah-Hartman" },
42 { "hch@lst.de", (void*)"Christoph Hellwig" },
43 { "htejun@gmail.com", (void*)"Tejun Heo" },
44 { "jejb@mulgrave.(none)", (void*)"James Bottomley" },
45 { "jejb@titanic.il.steeleye.com", (void*)"James Bottomley" },
46 { "jgarzik@pretzel.yyz.us", (void*)"Jeff Garzik" },
47 { "johnpol@2ka.mipt.ru", (void*)"Evgeniy Polyakov" },
48 { "kay.sievers@vrfy.org", (void*)"Kay Sievers" },
49 { "minyard@acm.org", (void*)"Corey Minyard" },
50 { "mshah@teja.com", (void*)"Mitesh shah" },
51 { "pj@ludd.ltu.se", (void*)"Peter A Jonsson" },
52 { "rmps@joel.ist.utl.pt", (void*)"Rui Saraiva" },
53 { "santtu.hyrkko@gmail.com", (void*)"Santtu Hyrkk\e,Av\e(B" },
54 { "simon@thekelleys.org.uk", (void*)"Simon Kelley" },
55 { "ssant@in.ibm.com", (void*)"Sachin P Sant" },
56 { "terra@gnome.org", (void*)"Morten Welinder" },
57 { "tony.luck@intel.com", (void*)"Tony Luck" },
58 { "welinder@anemone.rentec.com", (void*)"Morten Welinder" },
59 { "welinder@darter.rentec.com", (void*)"Morten Welinder" },
60 { "welinder@troll.com", (void*)"Morten Welinder" }
61};
62
63static struct path_list mailmap = {
64 mailmap_list,
65 sizeof(mailmap_list) / sizeof(struct path_list_item), 0, 0
66};
67
68static int map_email(char *email, char *name, int maxlen)
69{
70 char *p;
71 struct path_list_item *item;
72
73 /* autocomplete common developers */
74 p = strchr(email, '>');
75 if (!p)
76 return 0;
77
78 *p = '\0';
79 item = path_list_lookup(email, &mailmap);
80 if (item != NULL) {
81 const char *realname = (const char *)item->util;
82 strncpy(name, realname, maxlen);
83 return 1;
84 }
85 return 0;
86}
87
88static void insert_author_oneline(struct path_list *list,
89 const char *author, int authorlen,
90 const char *oneline, int onelinelen)
91{
92 const char *dot3 = "/pub/scm/linux/kernel/git/";
93 char *buffer, *p;
94 struct path_list_item *item;
95 struct path_list *onelines;
96
97 while (authorlen > 0 && isspace(author[authorlen - 1]))
98 authorlen--;
99
100 buffer = xmalloc(authorlen + 1);
101 memcpy(buffer, author, authorlen);
102 buffer[authorlen] = '\0';
103
104 item = path_list_insert(buffer, list);
105 if (item->util == NULL)
106 item->util = xcalloc(1, sizeof(struct path_list));
107 else
108 free(buffer);
109
110 if (!strncmp(oneline, "[PATCH", 6)) {
111 char *eob = strchr(oneline, ']');
112
113 if (eob) {
114 while (isspace(eob[1]) && eob[1] != '\n')
115 eob++;
116 if (eob - oneline < onelinelen) {
117 onelinelen -= eob - oneline;
118 oneline = eob;
119 }
120 }
121 }
122
123 while (onelinelen > 0 && isspace(oneline[0])) {
124 oneline++;
125 onelinelen--;
126 }
127
128 while (onelinelen > 0 && isspace(oneline[onelinelen - 1]))
129 onelinelen--;
130
131 buffer = xmalloc(onelinelen + 1);
132 memcpy(buffer, oneline, onelinelen);
133 buffer[onelinelen] = '\0';
134
135 while ((p = strstr(buffer, dot3)) != NULL) {
136 memcpy(p, "...", 3);
137 strcpy(p + 2, p + sizeof(dot3) - 1);
138 }
139
140
141 onelines = item->util;
142 if (onelines->nr >= onelines->alloc) {
143 onelines->alloc = alloc_nr(onelines->nr);
144 onelines->items = xrealloc(onelines->items,
145 onelines->alloc
146 * sizeof(struct path_list_item));
147 }
148
149 onelines->items[onelines->nr].util = NULL;
150 onelines->items[onelines->nr++].path = buffer;
151}
152
153static void read_from_stdin(struct path_list *list)
154{
155 char buffer[1024];
156
157 while (fgets(buffer, sizeof(buffer), stdin) != NULL) {
158 char *bob;
159 if ((buffer[0] == 'A' || buffer[0] == 'a') &&
160 !strncmp(buffer + 1, "uthor: ", 7) &&
161 (bob = strchr(buffer + 7, '<')) != NULL) {
162 char buffer2[1024], offset = 0;
163
164 if (map_email(bob + 1, buffer, sizeof(buffer)))
165 bob = buffer + strlen(buffer);
166 else {
167 offset = 8;
168 while (isspace(bob[-1]))
169 bob--;
170 }
171
172 while (fgets(buffer2, sizeof(buffer2), stdin) &&
173 buffer2[0] != '\n')
174 ; /* chomp input */
175 if (fgets(buffer2, sizeof(buffer2), stdin))
176 insert_author_oneline(list,
177 buffer + offset,
178 bob - buffer - offset,
179 buffer2, strlen(buffer2));
180 }
181 }
182}
183
184static void get_from_rev(struct rev_info *rev, struct path_list *list)
185{
186 char scratch[1024];
187 struct commit *commit;
188
189 prepare_revision_walk(rev);
190 while ((commit = get_revision(rev)) != NULL) {
191 char *author = NULL, *oneline, *buffer;
192 int authorlen = authorlen, onelinelen;
193
194 /* get author and oneline */
195 for (buffer = commit->buffer; buffer && *buffer != '\0' &&
196 *buffer != '\n'; ) {
197 char *eol = strchr(buffer, '\n');
198
199 if (eol == NULL)
200 eol = buffer + strlen(buffer);
201 else
202 eol++;
203
204 if (!strncmp(buffer, "author ", 7)) {
205 char *bracket = strchr(buffer, '<');
206
207 if (bracket == NULL || bracket > eol)
208 die("Invalid commit buffer: %s",
209 sha1_to_hex(commit->object.sha1));
210
211 if (map_email(bracket + 1, scratch,
212 sizeof(scratch))) {
213 author = scratch;
214 authorlen = strlen(scratch);
215 } else {
216 while (bracket[-1] == ' ')
217 bracket--;
218
219 author = buffer + 7;
220 authorlen = bracket - buffer - 7;
221 }
222 }
223 buffer = eol;
224 }
225
226 if (author == NULL)
227 die ("Missing author: %s",
228 sha1_to_hex(commit->object.sha1));
229
230 if (buffer == NULL || *buffer == '\0') {
231 oneline = "<none>";
232 onelinelen = sizeof(oneline) + 1;
233 } else {
234 char *eol;
235
236 oneline = buffer + 1;
237 eol = strchr(oneline, '\n');
238 if (eol == NULL)
239 onelinelen = strlen(oneline);
240 else
241 onelinelen = eol - oneline;
242 }
243
244 insert_author_oneline(list,
245 author, authorlen, oneline, onelinelen);
246 }
247
248}
249
250int cmd_shortlog(int argc, const char **argv, const char *prefix)
251{
252 struct rev_info rev;
253 struct path_list list = { NULL, 0, 0, 1 };
254 int i, j, sort_by_number = 0, summary = 0;
255
256 init_revisions(&rev, prefix);
257 argc = setup_revisions(argc, argv, &rev, NULL);
258 while (argc > 1) {
259 if (!strcmp(argv[1], "-n") || !strcmp(argv[1], "--numbered"))
260 sort_by_number = 1;
261 else if (!strcmp(argv[1], "-s") ||
262 !strcmp(argv[1], "--summary"))
263 summary = 1;
264 else if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
265 usage(shortlog_usage);
266 else
267 die ("unrecognized argument: %s", argv[1]);
268 argv++;
269 argc--;
270 }
271
272 if (rev.pending.nr == 1)
273 die ("Need a range!");
274 else if (rev.pending.nr == 0)
275 read_from_stdin(&list);
276 else
277 get_from_rev(&rev, &list);
278
279 if (sort_by_number)
280 qsort(list.items, sizeof(struct path_list_item), list.nr,
281 compare_by_number);
282
283 for (i = 0; i < list.nr; i++) {
284 struct path_list *onelines = list.items[i].util;
285
286 printf("%s (%d):\n", list.items[i].path, onelines->nr);
287 if (!summary) {
288 for (j = onelines->nr - 1; j >= 0; j--)
289 printf(" %s\n", onelines->items[j].path);
290 printf("\n");
291 }
292
293 onelines->strdup_paths = 1;
294 path_list_clear(onelines, 1);
295 free(onelines);
296 list.items[i].util = NULL;
297 }
298
299 list.strdup_paths = 1;
300 path_list_clear(&list, 1);
301
302 return 0;
303}
304