Skip to content

Commit a1c5405

Browse files
committed
Merge branch 'jk/shortlog'
"git shortlog" used to accumulate various pieces of information regardless of what was asked to be shown in the final output. It has been optimized by noticing what need not to be collected (e.g. there is no need to collect the log messages when showing only the number of changes). * jk/shortlog: shortlog: don't warn on empty author shortlog: optimize out useless string list shortlog: optimize out useless "<none>" normalization shortlog: optimize "--summary" mode shortlog: replace hand-parsing of author with pretty-printer shortlog: use strbufs to read from stdin shortlog: match both "Author:" and "author" on stdin
2 parents b62624b + d6b16ce commit a1c5405

File tree

2 files changed

+105
-103
lines changed

2 files changed

+105
-103
lines changed

builtin/shortlog.c

Lines changed: 99 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,26 @@ static char const * const shortlog_usage[] = {
1414
NULL
1515
};
1616

17-
static int compare_by_number(const void *a1, const void *a2)
17+
/*
18+
* The util field of our string_list_items will contain one of two things:
19+
*
20+
* - if --summary is not in use, it will point to a string list of the
21+
* oneline subjects assigned to this author
22+
*
23+
* - if --summary is in use, we don't need that list; we only need to know
24+
* its size. So we abuse the pointer slot to store our integer counter.
25+
*
26+
* This macro accesses the latter.
27+
*/
28+
#define UTIL_TO_INT(x) ((intptr_t)(x)->util)
29+
30+
static int compare_by_counter(const void *a1, const void *a2)
31+
{
32+
const struct string_list_item *i1 = a1, *i2 = a2;
33+
return UTIL_TO_INT(i2) - UTIL_TO_INT(i1);
34+
}
35+
36+
static int compare_by_list(const void *a1, const void *a2)
1837
{
1938
const struct string_list_item *i1 = a1, *i2 = a2;
2039
const struct string_list *l1 = i1->util, *l2 = i2->util;
@@ -31,13 +50,9 @@ static void insert_one_record(struct shortlog *log,
3150
const char *author,
3251
const char *oneline)
3352
{
34-
const char *dot3 = log->common_repo_prefix;
35-
char *buffer, *p;
3653
struct string_list_item *item;
3754
const char *mailbuf, *namebuf;
3855
size_t namelen, maillen;
39-
const char *eol;
40-
struct strbuf subject = STRBUF_INIT;
4156
struct strbuf namemailbuf = STRBUF_INIT;
4257
struct ident_split ident;
4358

@@ -56,98 +71,95 @@ static void insert_one_record(struct shortlog *log,
5671
strbuf_addf(&namemailbuf, " <%.*s>", (int)maillen, mailbuf);
5772

5873
item = string_list_insert(&log->list, namemailbuf.buf);
59-
if (item->util == NULL)
60-
item->util = xcalloc(1, sizeof(struct string_list));
61-
62-
/* Skip any leading whitespace, including any blank lines. */
63-
while (*oneline && isspace(*oneline))
64-
oneline++;
65-
eol = strchr(oneline, '\n');
66-
if (!eol)
67-
eol = oneline + strlen(oneline);
68-
if (starts_with(oneline, "[PATCH")) {
69-
char *eob = strchr(oneline, ']');
70-
if (eob && (!eol || eob < eol))
71-
oneline = eob + 1;
72-
}
73-
while (*oneline && isspace(*oneline) && *oneline != '\n')
74-
oneline++;
75-
format_subject(&subject, oneline, " ");
76-
buffer = strbuf_detach(&subject, NULL);
77-
78-
if (dot3) {
79-
int dot3len = strlen(dot3);
80-
if (dot3len > 5) {
81-
while ((p = strstr(buffer, dot3)) != NULL) {
82-
int taillen = strlen(p) - dot3len;
83-
memcpy(p, "/.../", 5);
84-
memmove(p + 5, p + dot3len, taillen + 1);
74+
75+
if (log->summary)
76+
item->util = (void *)(UTIL_TO_INT(item) + 1);
77+
else {
78+
const char *dot3 = log->common_repo_prefix;
79+
char *buffer, *p;
80+
struct strbuf subject = STRBUF_INIT;
81+
const char *eol;
82+
83+
/* Skip any leading whitespace, including any blank lines. */
84+
while (*oneline && isspace(*oneline))
85+
oneline++;
86+
eol = strchr(oneline, '\n');
87+
if (!eol)
88+
eol = oneline + strlen(oneline);
89+
if (starts_with(oneline, "[PATCH")) {
90+
char *eob = strchr(oneline, ']');
91+
if (eob && (!eol || eob < eol))
92+
oneline = eob + 1;
93+
}
94+
while (*oneline && isspace(*oneline) && *oneline != '\n')
95+
oneline++;
96+
format_subject(&subject, oneline, " ");
97+
buffer = strbuf_detach(&subject, NULL);
98+
99+
if (dot3) {
100+
int dot3len = strlen(dot3);
101+
if (dot3len > 5) {
102+
while ((p = strstr(buffer, dot3)) != NULL) {
103+
int taillen = strlen(p) - dot3len;
104+
memcpy(p, "/.../", 5);
105+
memmove(p + 5, p + dot3len, taillen + 1);
106+
}
85107
}
86108
}
87-
}
88109

89-
string_list_append(item->util, buffer);
110+
if (item->util == NULL)
111+
item->util = xcalloc(1, sizeof(struct string_list));
112+
string_list_append(item->util, buffer);
113+
}
90114
}
91115

92116
static void read_from_stdin(struct shortlog *log)
93117
{
94-
char author[1024], oneline[1024];
118+
struct strbuf author = STRBUF_INIT;
119+
struct strbuf oneline = STRBUF_INIT;
95120

96-
while (fgets(author, sizeof(author), stdin) != NULL) {
97-
if (!(author[0] == 'A' || author[0] == 'a') ||
98-
!starts_with(author + 1, "uthor: "))
121+
while (strbuf_getline_lf(&author, stdin) != EOF) {
122+
const char *v;
123+
if (!skip_prefix(author.buf, "Author: ", &v) &&
124+
!skip_prefix(author.buf, "author ", &v))
99125
continue;
100-
while (fgets(oneline, sizeof(oneline), stdin) &&
101-
oneline[0] != '\n')
126+
while (strbuf_getline_lf(&oneline, stdin) != EOF &&
127+
oneline.len)
102128
; /* discard headers */
103-
while (fgets(oneline, sizeof(oneline), stdin) &&
104-
oneline[0] == '\n')
129+
while (strbuf_getline_lf(&oneline, stdin) != EOF &&
130+
!oneline.len)
105131
; /* discard blanks */
106-
insert_one_record(log, author + 8, oneline);
132+
insert_one_record(log, v, oneline.buf);
107133
}
134+
strbuf_release(&author);
135+
strbuf_release(&oneline);
108136
}
109137

110138
void shortlog_add_commit(struct shortlog *log, struct commit *commit)
111139
{
112-
const char *author = NULL, *buffer;
113-
struct strbuf buf = STRBUF_INIT;
114-
struct strbuf ufbuf = STRBUF_INIT;
115-
116-
pp_commit_easy(CMIT_FMT_RAW, commit, &buf);
117-
buffer = buf.buf;
118-
while (*buffer && *buffer != '\n') {
119-
const char *eol = strchr(buffer, '\n');
120-
121-
if (eol == NULL)
122-
eol = buffer + strlen(buffer);
140+
struct strbuf author = STRBUF_INIT;
141+
struct strbuf oneline = STRBUF_INIT;
142+
struct pretty_print_context ctx = {0};
143+
144+
ctx.fmt = CMIT_FMT_USERFORMAT;
145+
ctx.abbrev = log->abbrev;
146+
ctx.subject = "";
147+
ctx.after_subject = "";
148+
ctx.date_mode.type = DATE_NORMAL;
149+
ctx.output_encoding = get_log_output_encoding();
150+
151+
format_commit_message(commit, "%an <%ae>", &author, &ctx);
152+
if (!log->summary) {
153+
if (log->user_format)
154+
pretty_print_commit(&ctx, commit, &oneline);
123155
else
124-
eol++;
125-
126-
if (starts_with(buffer, "author "))
127-
author = buffer + 7;
128-
buffer = eol;
129-
}
130-
if (!author) {
131-
warning(_("Missing author: %s"),
132-
oid_to_hex(&commit->object.oid));
133-
return;
156+
format_commit_message(commit, "%s", &oneline, &ctx);
134157
}
135-
if (log->user_format) {
136-
struct pretty_print_context ctx = {0};
137-
ctx.fmt = CMIT_FMT_USERFORMAT;
138-
ctx.abbrev = log->abbrev;
139-
ctx.subject = "";
140-
ctx.after_subject = "";
141-
ctx.date_mode.type = DATE_NORMAL;
142-
ctx.output_encoding = get_log_output_encoding();
143-
pretty_print_commit(&ctx, commit, &ufbuf);
144-
buffer = ufbuf.buf;
145-
} else if (*buffer) {
146-
buffer++;
147-
}
148-
insert_one_record(log, author, !*buffer ? "<none>" : buffer);
149-
strbuf_release(&ufbuf);
150-
strbuf_release(&buf);
158+
159+
insert_one_record(log, author.buf, oneline.len ? oneline.buf : "<none>");
160+
161+
strbuf_release(&author);
162+
strbuf_release(&oneline);
151163
}
152164

153165
static void get_from_rev(struct rev_info *rev, struct shortlog *log)
@@ -294,14 +306,14 @@ void shortlog_output(struct shortlog *log)
294306

295307
if (log->sort_by_number)
296308
qsort(log->list.items, log->list.nr, sizeof(struct string_list_item),
297-
compare_by_number);
309+
log->summary ? compare_by_counter : compare_by_list);
298310
for (i = 0; i < log->list.nr; i++) {
299-
struct string_list *onelines = log->list.items[i].util;
300-
311+
const struct string_list_item *item = &log->list.items[i];
301312
if (log->summary) {
302-
printf("%6d\t%s\n", onelines->nr, log->list.items[i].string);
313+
printf("%6d\t%s\n", (int)UTIL_TO_INT(item), item->string);
303314
} else {
304-
printf("%s (%d):\n", log->list.items[i].string, onelines->nr);
315+
struct string_list *onelines = item->util;
316+
printf("%s (%d):\n", item->string, onelines->nr);
305317
for (j = onelines->nr - 1; j >= 0; j--) {
306318
const char *msg = onelines->items[j].string;
307319

@@ -314,11 +326,11 @@ void shortlog_output(struct shortlog *log)
314326
printf(" %s\n", msg);
315327
}
316328
putchar('\n');
329+
onelines->strdup_strings = 1;
330+
string_list_clear(onelines, 0);
331+
free(onelines);
317332
}
318333

319-
onelines->strdup_strings = 1;
320-
string_list_clear(onelines, 0);
321-
free(onelines);
322334
log->list.items[i].util = NULL;
323335
}
324336

t/t4201-shortlog.sh

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,12 @@ test_expect_success !MINGW 'shortlog from non-git directory' '
120120
test_cmp expect out
121121
'
122122

123+
test_expect_success !MINGW 'shortlog can read --format=raw output' '
124+
git log --format=raw HEAD >log &&
125+
GIT_DIR=non-existing git shortlog -w <log >out &&
126+
test_cmp expect out
127+
'
128+
123129
test_expect_success 'shortlog should add newline when input line matches wraplen' '
124130
cat >expect <<\EOF &&
125131
A U Thor (2):
@@ -172,22 +178,6 @@ test_expect_success !MINGW 'shortlog encoding' '
172178
git shortlog HEAD~2.. > out &&
173179
test_cmp expect out'
174180

175-
test_expect_success 'shortlog ignores commits with missing authors' '
176-
git commit --allow-empty -m normal &&
177-
git commit --allow-empty -m soon-to-be-broken &&
178-
git cat-file commit HEAD >commit.tmp &&
179-
sed "/^author/d" commit.tmp >broken.tmp &&
180-
commit=$(git hash-object -w -t commit --stdin <broken.tmp) &&
181-
git update-ref HEAD $commit &&
182-
cat >expect <<-\EOF &&
183-
A U Thor (1):
184-
normal
185-
186-
EOF
187-
git shortlog HEAD~2.. >actual &&
188-
test_cmp expect actual
189-
'
190-
191181
test_expect_success 'shortlog with revision pseudo options' '
192182
git shortlog --all &&
193183
git shortlog --branches &&

0 commit comments

Comments
 (0)