8 static const char shortlog_usage
[] =
9 "git-shortlog [-n] [-s] [<commit-id>... ]";
11 static char *common_repo_prefix
;
13 static int compare_by_number(const void *a1
, const void *a2
)
15 const struct path_list_item
*i1
= a1
, *i2
= a2
;
16 const struct path_list
*l1
= i1
->util
, *l2
= i2
->util
;
20 else if (l1
->nr
== l2
->nr
)
26 static struct path_list mailmap
= {NULL
, 0, 0, 0};
28 static int read_mailmap(const char *filename
)
31 FILE *f
= fopen(filename
, "r");
35 while (fgets(buffer
, sizeof(buffer
), f
) != NULL
) {
36 char *end_of_name
, *left_bracket
, *right_bracket
;
39 if (buffer
[0] == '#') {
40 static const char abbrev
[] = "# repo-abbrev:";
41 int abblen
= sizeof(abbrev
) - 1;
42 int len
= strlen(buffer
);
44 if (len
&& buffer
[len
- 1] == '\n')
46 if (!strncmp(buffer
, abbrev
, abblen
)) {
49 if (common_repo_prefix
)
50 free(common_repo_prefix
);
51 common_repo_prefix
= xmalloc(len
);
53 for (cp
= buffer
+ abblen
; isspace(*cp
); cp
++)
55 strcpy(common_repo_prefix
, cp
);
59 if ((left_bracket
= strchr(buffer
, '<')) == NULL
)
61 if ((right_bracket
= strchr(left_bracket
+ 1, '>')) == NULL
)
63 if (right_bracket
== left_bracket
+ 1)
65 for (end_of_name
= left_bracket
; end_of_name
!= buffer
66 && isspace(end_of_name
[-1]); end_of_name
--)
68 if (end_of_name
== buffer
)
70 name
= xmalloc(end_of_name
- buffer
+ 1);
71 strlcpy(name
, buffer
, end_of_name
- buffer
+ 1);
72 email
= xmalloc(right_bracket
- left_bracket
);
73 for (i
= 0; i
< right_bracket
- left_bracket
- 1; i
++)
74 email
[i
] = tolower(left_bracket
[i
+ 1]);
75 email
[right_bracket
- left_bracket
- 1] = '\0';
76 path_list_insert(email
, &mailmap
)->util
= name
;
82 static int map_email(char *email
, char *name
, int maxlen
)
85 struct path_list_item
*item
;
87 /* autocomplete common developers */
88 p
= strchr(email
, '>');
93 /* downcase the email address */
94 for (p
= email
; *p
; p
++)
96 item
= path_list_lookup(email
, &mailmap
);
98 const char *realname
= (const char *)item
->util
;
99 strncpy(name
, realname
, maxlen
);
105 static void insert_author_oneline(struct path_list
*list
,
106 const char *author
, int authorlen
,
107 const char *oneline
, int onelinelen
)
109 const char *dot3
= common_repo_prefix
;
111 struct path_list_item
*item
;
112 struct path_list
*onelines
;
114 while (authorlen
> 0 && isspace(author
[authorlen
- 1]))
117 buffer
= xmalloc(authorlen
+ 1);
118 memcpy(buffer
, author
, authorlen
);
119 buffer
[authorlen
] = '\0';
121 item
= path_list_insert(buffer
, list
);
122 if (item
->util
== NULL
)
123 item
->util
= xcalloc(1, sizeof(struct path_list
));
127 if (!prefixcmp(oneline
, "[PATCH")) {
128 char *eob
= strchr(oneline
, ']');
131 while (isspace(eob
[1]) && eob
[1] != '\n')
133 if (eob
- oneline
< onelinelen
) {
134 onelinelen
-= eob
- oneline
;
140 while (onelinelen
> 0 && isspace(oneline
[0])) {
145 while (onelinelen
> 0 && isspace(oneline
[onelinelen
- 1]))
148 buffer
= xmalloc(onelinelen
+ 1);
149 memcpy(buffer
, oneline
, onelinelen
);
150 buffer
[onelinelen
] = '\0';
153 int dot3len
= strlen(dot3
);
155 while ((p
= strstr(buffer
, dot3
)) != NULL
) {
156 int taillen
= strlen(p
) - dot3len
;
157 memcpy(p
, "/.../", 5);
158 memmove(p
+ 5, p
+ dot3len
, taillen
+ 1);
163 onelines
= item
->util
;
164 if (onelines
->nr
>= onelines
->alloc
) {
165 onelines
->alloc
= alloc_nr(onelines
->nr
);
166 onelines
->items
= xrealloc(onelines
->items
,
168 * sizeof(struct path_list_item
));
171 onelines
->items
[onelines
->nr
].util
= NULL
;
172 onelines
->items
[onelines
->nr
++].path
= buffer
;
175 static void read_from_stdin(struct path_list
*list
)
179 while (fgets(buffer
, sizeof(buffer
), stdin
) != NULL
) {
181 if ((buffer
[0] == 'A' || buffer
[0] == 'a') &&
182 !prefixcmp(buffer
+ 1, "uthor: ") &&
183 (bob
= strchr(buffer
+ 7, '<')) != NULL
) {
184 char buffer2
[1024], offset
= 0;
186 if (map_email(bob
+ 1, buffer
, sizeof(buffer
)))
187 bob
= buffer
+ strlen(buffer
);
190 while (buffer
+ offset
< bob
&&
195 while (fgets(buffer2
, sizeof(buffer2
), stdin
) &&
198 if (fgets(buffer2
, sizeof(buffer2
), stdin
)) {
199 int l2
= strlen(buffer2
);
201 for (i
= 0; i
< l2
; i
++)
202 if (!isspace(buffer2
[i
]))
204 insert_author_oneline(list
,
206 bob
- buffer
- offset
,
207 buffer2
+ i
, l2
- i
);
213 static void get_from_rev(struct rev_info
*rev
, struct path_list
*list
)
216 struct commit
*commit
;
218 prepare_revision_walk(rev
);
219 while ((commit
= get_revision(rev
)) != NULL
) {
220 char *author
= NULL
, *oneline
, *buffer
;
221 int authorlen
= authorlen
, onelinelen
;
223 /* get author and oneline */
224 for (buffer
= commit
->buffer
; buffer
&& *buffer
!= '\0' &&
226 char *eol
= strchr(buffer
, '\n');
229 eol
= buffer
+ strlen(buffer
);
233 if (!prefixcmp(buffer
, "author ")) {
234 char *bracket
= strchr(buffer
, '<');
236 if (bracket
== NULL
|| bracket
> eol
)
237 die("Invalid commit buffer: %s",
238 sha1_to_hex(commit
->object
.sha1
));
240 if (map_email(bracket
+ 1, scratch
,
243 authorlen
= strlen(scratch
);
245 if (bracket
[-1] == ' ')
249 authorlen
= bracket
- buffer
- 7;
256 die ("Missing author: %s",
257 sha1_to_hex(commit
->object
.sha1
));
259 if (buffer
== NULL
|| *buffer
== '\0') {
261 onelinelen
= sizeof(oneline
) + 1;
265 oneline
= buffer
+ 1;
266 eol
= strchr(oneline
, '\n');
268 onelinelen
= strlen(oneline
);
270 onelinelen
= eol
- oneline
;
273 insert_author_oneline(list
,
274 author
, authorlen
, oneline
, onelinelen
);
279 int cmd_shortlog(int argc
, const char **argv
, const char *prefix
)
282 struct path_list list
= { NULL
, 0, 0, 1 };
283 int i
, j
, sort_by_number
= 0, summary
= 0;
285 /* since -n is a shadowed rev argument, parse our args first */
287 if (!strcmp(argv
[1], "-n") || !strcmp(argv
[1], "--numbered"))
289 else if (!strcmp(argv
[1], "-s") ||
290 !strcmp(argv
[1], "--summary"))
292 else if (!strcmp(argv
[1], "-h") || !strcmp(argv
[1], "--help"))
293 usage(shortlog_usage
);
299 init_revisions(&rev
, prefix
);
300 argc
= setup_revisions(argc
, argv
, &rev
, NULL
);
302 die ("unrecognized argument: %s", argv
[1]);
304 if (!access(".mailmap", R_OK
))
305 read_mailmap(".mailmap");
307 if (rev
.pending
.nr
== 0)
308 read_from_stdin(&list
);
310 get_from_rev(&rev
, &list
);
313 qsort(list
.items
, list
.nr
, sizeof(struct path_list_item
),
316 for (i
= 0; i
< list
.nr
; i
++) {
317 struct path_list
*onelines
= list
.items
[i
].util
;
320 printf("%s: %d\n", list
.items
[i
].path
, onelines
->nr
);
322 printf("%s (%d):\n", list
.items
[i
].path
, onelines
->nr
);
323 for (j
= onelines
->nr
- 1; j
>= 0; j
--)
324 printf(" %s\n", onelines
->items
[j
].path
);
328 onelines
->strdup_paths
= 1;
329 path_list_clear(onelines
, 1);
331 list
.items
[i
].util
= NULL
;
334 list
.strdup_paths
= 1;
335 path_list_clear(&list
, 1);
336 mailmap
.strdup_paths
= 1;
337 path_list_clear(&mailmap
, 1);