10 static const char shortlog_usage[] =
11 "git-shortlog [-n] [-s] [<commit-id>... ]";
13 static char *common_repo_prefix;
15 static int compare_by_number(const void *a1, const void *a2)
17 const struct path_list_item *i1 = a1, *i2 = a2;
18 const struct path_list *l1 = i1->util, *l2 = i2->util;
22 else if (l1->nr == l2->nr)
28 static struct path_list mailmap = {NULL, 0, 0, 0};
30 static void insert_one_record(struct path_list *list,
34 const char *dot3 = common_repo_prefix;
36 struct path_list_item *item;
37 struct path_list *onelines;
41 const char *boemail, *eoemail;
43 boemail = strchr(author, '<');
46 eoemail = strchr(boemail, '>');
49 if (!map_email(&mailmap, boemail+1, namebuf, sizeof(namebuf))) {
50 while (author < boemail && isspace(*author))
53 len < sizeof(namebuf) - 1 && author + len < boemail;
55 namebuf[len] = author[len];
56 while (0 < len && isspace(namebuf[len-1]))
61 buffer = xstrdup(namebuf);
62 item = path_list_insert(buffer, list);
63 if (item->util == NULL)
64 item->util = xcalloc(1, sizeof(struct path_list));
68 eol = strchr(oneline, '\n');
70 eol = oneline + strlen(oneline);
71 while (*oneline && isspace(*oneline) && *oneline != '\n')
73 if (!prefixcmp(oneline, "[PATCH")) {
74 char *eob = strchr(oneline, ']');
75 if (eob && (!eol || eob < eol))
78 while (*oneline && isspace(*oneline) && *oneline != '\n')
81 while (len && isspace(oneline[len-1]))
83 buffer = xmemdupz(oneline, len);
86 int dot3len = strlen(dot3);
88 while ((p = strstr(buffer, dot3)) != NULL) {
89 int taillen = strlen(p) - dot3len;
90 memcpy(p, "/.../", 5);
91 memmove(p + 5, p + dot3len, taillen + 1);
96 onelines = item->util;
97 if (onelines->nr >= onelines->alloc) {
98 onelines->alloc = alloc_nr(onelines->nr);
99 onelines->items = xrealloc(onelines->items,
101 * sizeof(struct path_list_item));
104 onelines->items[onelines->nr].util = NULL;
105 onelines->items[onelines->nr++].path = buffer;
108 static void read_from_stdin(struct path_list *list)
110 char author[1024], oneline[1024];
112 while (fgets(author, sizeof(author), stdin) != NULL) {
113 if (!(author[0] == 'A' || author[0] == 'a') ||
114 prefixcmp(author + 1, "uthor: "))
116 while (fgets(oneline, sizeof(oneline), stdin) &&
118 ; /* discard headers */
119 while (fgets(oneline, sizeof(oneline), stdin) &&
121 ; /* discard blanks */
122 insert_one_record(list, author + 8, oneline);
126 static void get_from_rev(struct rev_info *rev, struct path_list *list)
128 struct commit *commit;
130 prepare_revision_walk(rev);
131 while ((commit = get_revision(rev)) != NULL) {
132 const char *author = NULL, *buffer;
134 buffer = commit->buffer;
135 while (*buffer && *buffer != '\n') {
136 const char *eol = strchr(buffer, '\n');
139 eol = buffer + strlen(buffer);
143 if (!prefixcmp(buffer, "author "))
148 die("Missing author: %s",
149 sha1_to_hex(commit->object.sha1));
152 insert_one_record(list, author, !*buffer ? "<none>" : buffer);
156 static int parse_uint(char const **arg, int comma)
162 ul = strtoul(*arg, &endp, 10);
163 if (endp != *arg && *endp && *endp != comma)
174 static const char wrap_arg_usage[] = "-w[<width>[,<indent1>[,<indent2>]]]";
175 #define DEFAULT_WRAPLEN 76
176 #define DEFAULT_INDENT1 6
177 #define DEFAULT_INDENT2 9
179 static void parse_wrap_args(const char *arg, int *in1, int *in2, int *wrap)
181 arg += 2; /* skip -w */
183 *wrap = parse_uint(&arg, ',');
186 *in1 = parse_uint(&arg, ',');
189 *in2 = parse_uint(&arg, '\0');
194 *wrap = DEFAULT_WRAPLEN;
196 *in1 = DEFAULT_INDENT1;
198 *in2 = DEFAULT_INDENT2;
200 ((*in1 && *wrap <= *in1) ||
201 (*in2 && *wrap <= *in2)))
205 int cmd_shortlog(int argc, const char **argv, const char *prefix)
208 struct path_list list = { NULL, 0, 0, 1 };
209 int i, j, sort_by_number = 0, summary = 0;
211 int wrap = DEFAULT_WRAPLEN;
212 int in1 = DEFAULT_INDENT1;
213 int in2 = DEFAULT_INDENT2;
215 /* since -n is a shadowed rev argument, parse our args first */
217 if (!strcmp(argv[1], "-n") || !strcmp(argv[1], "--numbered"))
219 else if (!strcmp(argv[1], "-s") ||
220 !strcmp(argv[1], "--summary"))
222 else if (!prefixcmp(argv[1], "-w")) {
224 parse_wrap_args(argv[1], &in1, &in2, &wrap);
226 else if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
227 usage(shortlog_usage);
233 init_revisions(&rev, prefix);
234 argc = setup_revisions(argc, argv, &rev, NULL);
236 die ("unrecognized argument: %s", argv[1]);
238 read_mailmap(&mailmap, ".mailmap", &common_repo_prefix);
240 if (rev.pending.nr == 0) {
242 fprintf(stderr, "(reading log to summarize from standard input)\n");
243 read_from_stdin(&list);
246 get_from_rev(&rev, &list);
249 qsort(list.items, list.nr, sizeof(struct path_list_item),
252 for (i = 0; i < list.nr; i++) {
253 struct path_list *onelines = list.items[i].util;
256 printf("%s: %d\n", list.items[i].path, onelines->nr);
258 printf("%s (%d):\n", list.items[i].path, onelines->nr);
259 for (j = onelines->nr - 1; j >= 0; j--) {
260 const char *msg = onelines->items[j].path;
263 int col = print_wrapped_text(msg, in1, in2, wrap);
268 printf(" %s\n", msg);
273 onelines->strdup_paths = 1;
274 path_list_clear(onelines, 1);
276 list.items[i].util = NULL;
279 list.strdup_paths = 1;
280 path_list_clear(&list, 1);
281 mailmap.strdup_paths = 1;
282 path_list_clear(&mailmap, 1);