]> git.ipfire.org Git - thirdparty/git.git/blob - builtin-shortlog.c
Build in shortlog
[thirdparty/git.git] / builtin-shortlog.c
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
9 static const char shortlog_usage[] =
10 "git-shortlog [-n] [-s] [<commit-id>... ]\n";
11
12 static 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
25 static 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
63 static struct path_list mailmap = {
64 mailmap_list,
65 sizeof(mailmap_list) / sizeof(struct path_list_item), 0, 0
66 };
67
68 static 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
88 static 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(buffer, ']');
112
113 while (isspace(eob[1]) && eob[1] != '\n')
114 eob++;
115 if (eob - oneline < onelinelen) {
116 onelinelen -= eob - oneline;
117 oneline = eob;
118 }
119 }
120
121 while (onelinelen > 0 && isspace(oneline[0])) {
122 oneline++;
123 onelinelen--;
124 }
125
126 while (onelinelen > 0 && isspace(oneline[onelinelen - 1]))
127 onelinelen--;
128
129 buffer = xmalloc(onelinelen + 1);
130 memcpy(buffer, oneline, onelinelen);
131 buffer[onelinelen] = '\0';
132
133 while ((p = strstr(buffer, dot3)) != NULL) {
134 memcpy(p, "...", 3);
135 strcpy(p + 2, p + sizeof(dot3) - 1);
136 }
137
138
139 onelines = item->util;
140 if (onelines->nr >= onelines->alloc) {
141 onelines->alloc = alloc_nr(onelines->nr);
142 onelines->items = xrealloc(onelines->items,
143 onelines->alloc
144 * sizeof(struct path_list_item));
145 }
146
147 onelines->items[onelines->nr].util = NULL;
148 onelines->items[onelines->nr++].path = buffer;
149 }
150
151 static void read_from_stdin(struct path_list *list)
152 {
153 char buffer[1024];
154
155 while (fgets(buffer, sizeof(buffer), stdin) != NULL) {
156 char *bob;
157 if ((buffer[0] == 'A' || buffer[0] == 'a') &&
158 !strncmp(buffer + 1, "uthor: ", 7) &&
159 (bob = strchr(buffer + 7, '<')) != NULL) {
160 char buffer2[1024], offset = 0;
161
162 if (map_email(bob + 1, buffer, sizeof(buffer)))
163 bob = buffer + strlen(buffer);
164 else {
165 offset = 8;
166 while (isspace(bob[-1]))
167 bob--;
168 }
169
170 while (fgets(buffer2, sizeof(buffer2), stdin) &&
171 buffer2[0] != '\n')
172 ; /* chomp input */
173 if (fgets(buffer2, sizeof(buffer2), stdin))
174 insert_author_oneline(list,
175 buffer + offset,
176 bob - buffer - offset,
177 buffer2, strlen(buffer2));
178 }
179 }
180 }
181
182 static void get_from_rev(struct rev_info *rev, struct path_list *list)
183 {
184 char scratch[1024];
185 struct commit *commit;
186
187 prepare_revision_walk(rev);
188 while ((commit = get_revision(rev)) != NULL) {
189 char *author = NULL, *oneline, *buffer;
190 int authorlen = authorlen, onelinelen;
191
192 /* get author and oneline */
193 for (buffer = commit->buffer; buffer && *buffer != '\0' &&
194 *buffer != '\n'; ) {
195 char *eol = strchr(buffer, '\n');
196
197 if (eol == NULL)
198 eol = buffer + strlen(buffer);
199 else
200 eol++;
201
202 if (!strncmp(buffer, "author ", 7)) {
203 char *bracket = strchr(buffer, '<');
204
205 if (bracket == NULL || bracket > eol)
206 die("Invalid commit buffer: %s",
207 sha1_to_hex(commit->object.sha1));
208
209 if (map_email(bracket + 1, scratch,
210 sizeof(scratch))) {
211 author = scratch;
212 authorlen = strlen(scratch);
213 } else {
214 while (bracket[-1] == ' ')
215 bracket--;
216
217 author = buffer + 7;
218 authorlen = bracket - buffer - 7;
219 }
220 }
221 buffer = eol;
222 }
223
224 if (author == NULL)
225 die ("Missing author: %s",
226 sha1_to_hex(commit->object.sha1));
227
228 if (buffer == NULL || *buffer == '\0') {
229 oneline = "<none>";
230 onelinelen = sizeof(oneline) + 1;
231 } else {
232 char *eol;
233
234 oneline = buffer + 1;
235 eol = strchr(oneline, '\n');
236 if (eol == NULL)
237 onelinelen = strlen(oneline);
238 else
239 onelinelen = eol - oneline;
240 }
241
242 insert_author_oneline(list,
243 author, authorlen, oneline, onelinelen);
244 }
245
246 }
247
248 int cmd_shortlog(int argc, const char **argv, const char *prefix)
249 {
250 struct rev_info rev;
251 struct path_list list = { NULL, 0, 0, 1 };
252 int i, j, sort_by_number = 0, summary = 0;
253
254 init_revisions(&rev, prefix);
255 argc = setup_revisions(argc, argv, &rev, NULL);
256 while (argc > 1) {
257 if (!strcmp(argv[1], "-n") || !strcmp(argv[1], "--numbered"))
258 sort_by_number = 1;
259 else if (!strcmp(argv[1], "-s") ||
260 !strcmp(argv[1], "--summary"))
261 summary = 1;
262 else if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
263 usage(shortlog_usage);
264 else
265 die ("unrecognized argument: %s", argv[1]);
266 argv++;
267 argc--;
268 }
269
270 if (rev.pending.nr == 1)
271 die ("Need a range!");
272 else if (rev.pending.nr == 0)
273 read_from_stdin(&list);
274 else
275 get_from_rev(&rev, &list);
276
277 if (sort_by_number)
278 qsort(list.items, sizeof(struct path_list_item), list.nr,
279 compare_by_number);
280
281 for (i = 0; i < list.nr; i++) {
282 struct path_list *onelines = list.items[i].util;
283
284 printf("%s (%d):\n", list.items[i].path, onelines->nr);
285 if (!summary) {
286 for (j = onelines->nr - 1; j >= 0; j--)
287 printf(" %s\n", onelines->items[j].path);
288 printf("\n");
289 }
290
291 onelines->strdup_paths = 1;
292 path_list_clear(onelines, 1);
293 free(onelines);
294 list.items[i].util = NULL;
295 }
296
297 list.strdup_paths = 1;
298 path_list_clear(&list, 1);
299
300 return 0;
301 }
302