/src/git/builtin/shortlog.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "builtin.h" |
2 | | #include "config.h" |
3 | | #include "commit.h" |
4 | | #include "diff.h" |
5 | | #include "environment.h" |
6 | | #include "gettext.h" |
7 | | #include "string-list.h" |
8 | | #include "repository.h" |
9 | | #include "revision.h" |
10 | | #include "utf8.h" |
11 | | #include "mailmap.h" |
12 | | #include "setup.h" |
13 | | #include "shortlog.h" |
14 | | #include "parse-options.h" |
15 | | #include "trailer.h" |
16 | | #include "strmap.h" |
17 | | |
18 | | static char const * const shortlog_usage[] = { |
19 | | N_("git shortlog [<options>] [<revision-range>] [[--] <path>...]"), |
20 | | N_("git log --pretty=short | git shortlog [<options>]"), |
21 | | NULL |
22 | | }; |
23 | | |
24 | | /* |
25 | | * The util field of our string_list_items will contain one of two things: |
26 | | * |
27 | | * - if --summary is not in use, it will point to a string list of the |
28 | | * oneline subjects assigned to this author |
29 | | * |
30 | | * - if --summary is in use, we don't need that list; we only need to know |
31 | | * its size. So we abuse the pointer slot to store our integer counter. |
32 | | * |
33 | | * This macro accesses the latter. |
34 | | */ |
35 | 0 | #define UTIL_TO_INT(x) ((intptr_t)(x)->util) |
36 | | |
37 | | static int compare_by_counter(const void *a1, const void *a2) |
38 | 0 | { |
39 | 0 | const struct string_list_item *i1 = a1, *i2 = a2; |
40 | 0 | return UTIL_TO_INT(i2) - UTIL_TO_INT(i1); |
41 | 0 | } |
42 | | |
43 | | static int compare_by_list(const void *a1, const void *a2) |
44 | 0 | { |
45 | 0 | const struct string_list_item *i1 = a1, *i2 = a2; |
46 | 0 | const struct string_list *l1 = i1->util, *l2 = i2->util; |
47 | |
|
48 | 0 | if (l1->nr < l2->nr) |
49 | 0 | return 1; |
50 | 0 | else if (l1->nr == l2->nr) |
51 | 0 | return 0; |
52 | 0 | else |
53 | 0 | return -1; |
54 | 0 | } |
55 | | |
56 | | static void insert_one_record(struct shortlog *log, |
57 | | const char *ident, |
58 | | const char *oneline) |
59 | 0 | { |
60 | 0 | struct string_list_item *item; |
61 | |
|
62 | 0 | item = string_list_insert(&log->list, ident); |
63 | |
|
64 | 0 | if (log->summary) |
65 | 0 | item->util = (void *)(UTIL_TO_INT(item) + 1); |
66 | 0 | else { |
67 | 0 | char *buffer; |
68 | 0 | struct strbuf subject = STRBUF_INIT; |
69 | 0 | const char *eol; |
70 | | |
71 | | /* Skip any leading whitespace, including any blank lines. */ |
72 | 0 | while (*oneline && isspace(*oneline)) |
73 | 0 | oneline++; |
74 | 0 | eol = strchr(oneline, '\n'); |
75 | 0 | if (!eol) |
76 | 0 | eol = oneline + strlen(oneline); |
77 | 0 | if (starts_with(oneline, "[PATCH")) { |
78 | 0 | char *eob = strchr(oneline, ']'); |
79 | 0 | if (eob && (!eol || eob < eol)) |
80 | 0 | oneline = eob + 1; |
81 | 0 | } |
82 | 0 | while (*oneline && isspace(*oneline) && *oneline != '\n') |
83 | 0 | oneline++; |
84 | 0 | format_subject(&subject, oneline, " "); |
85 | 0 | buffer = strbuf_detach(&subject, NULL); |
86 | |
|
87 | 0 | if (!item->util) { |
88 | 0 | item->util = xmalloc(sizeof(struct string_list)); |
89 | 0 | string_list_init_nodup(item->util); |
90 | 0 | } |
91 | 0 | string_list_append(item->util, buffer); |
92 | 0 | } |
93 | 0 | } |
94 | | |
95 | | static int parse_ident(struct shortlog *log, |
96 | | struct strbuf *out, const char *in) |
97 | 0 | { |
98 | 0 | const char *mailbuf, *namebuf; |
99 | 0 | size_t namelen, maillen; |
100 | 0 | struct ident_split ident; |
101 | |
|
102 | 0 | if (split_ident_line(&ident, in, strlen(in))) |
103 | 0 | return -1; |
104 | | |
105 | 0 | namebuf = ident.name_begin; |
106 | 0 | mailbuf = ident.mail_begin; |
107 | 0 | namelen = ident.name_end - ident.name_begin; |
108 | 0 | maillen = ident.mail_end - ident.mail_begin; |
109 | |
|
110 | 0 | map_user(&log->mailmap, &mailbuf, &maillen, &namebuf, &namelen); |
111 | 0 | strbuf_add(out, namebuf, namelen); |
112 | 0 | if (log->email) |
113 | 0 | strbuf_addf(out, " <%.*s>", (int)maillen, mailbuf); |
114 | |
|
115 | 0 | return 0; |
116 | 0 | } |
117 | | |
118 | | static void read_from_stdin(struct shortlog *log) |
119 | 0 | { |
120 | 0 | struct strbuf ident = STRBUF_INIT; |
121 | 0 | struct strbuf mapped_ident = STRBUF_INIT; |
122 | 0 | struct strbuf oneline = STRBUF_INIT; |
123 | 0 | static const char *author_match[2] = { "Author: ", "author " }; |
124 | 0 | static const char *committer_match[2] = { "Commit: ", "committer " }; |
125 | 0 | const char **match; |
126 | |
|
127 | 0 | if (HAS_MULTI_BITS(log->groups)) |
128 | 0 | die(_("using multiple --group options with stdin is not supported")); |
129 | | |
130 | 0 | switch (log->groups) { |
131 | 0 | case SHORTLOG_GROUP_AUTHOR: |
132 | 0 | match = author_match; |
133 | 0 | break; |
134 | 0 | case SHORTLOG_GROUP_COMMITTER: |
135 | 0 | match = committer_match; |
136 | 0 | break; |
137 | 0 | case SHORTLOG_GROUP_TRAILER: |
138 | 0 | die(_("using %s with stdin is not supported"), "--group=trailer"); |
139 | 0 | case SHORTLOG_GROUP_FORMAT: |
140 | 0 | die(_("using %s with stdin is not supported"), "--group=format"); |
141 | 0 | default: |
142 | 0 | BUG("unhandled shortlog group"); |
143 | 0 | } |
144 | | |
145 | 0 | while (strbuf_getline_lf(&ident, stdin) != EOF) { |
146 | 0 | const char *v; |
147 | 0 | if (!skip_prefix(ident.buf, match[0], &v) && |
148 | 0 | !skip_prefix(ident.buf, match[1], &v)) |
149 | 0 | continue; |
150 | 0 | while (strbuf_getline_lf(&oneline, stdin) != EOF && |
151 | 0 | oneline.len) |
152 | 0 | ; /* discard headers */ |
153 | 0 | while (strbuf_getline_lf(&oneline, stdin) != EOF && |
154 | 0 | !oneline.len) |
155 | 0 | ; /* discard blanks */ |
156 | |
|
157 | 0 | strbuf_reset(&mapped_ident); |
158 | 0 | if (parse_ident(log, &mapped_ident, v) < 0) |
159 | 0 | continue; |
160 | | |
161 | 0 | insert_one_record(log, mapped_ident.buf, oneline.buf); |
162 | 0 | } |
163 | 0 | strbuf_release(&ident); |
164 | 0 | strbuf_release(&mapped_ident); |
165 | 0 | strbuf_release(&oneline); |
166 | 0 | } |
167 | | |
168 | | static void insert_records_from_trailers(struct shortlog *log, |
169 | | struct strset *dups, |
170 | | struct commit *commit, |
171 | | struct pretty_print_context *ctx, |
172 | | const char *oneline) |
173 | 0 | { |
174 | 0 | struct trailer_iterator iter; |
175 | 0 | const char *commit_buffer, *body; |
176 | 0 | struct strbuf ident = STRBUF_INIT; |
177 | |
|
178 | 0 | if (!log->trailers.nr) |
179 | 0 | return; |
180 | | |
181 | | /* |
182 | | * Using repo_format_commit_message("%B") would be simpler here, but |
183 | | * this saves us copying the message. |
184 | | */ |
185 | 0 | commit_buffer = repo_logmsg_reencode(the_repository, commit, NULL, |
186 | 0 | ctx->output_encoding); |
187 | 0 | body = strstr(commit_buffer, "\n\n"); |
188 | 0 | if (!body) |
189 | 0 | return; |
190 | | |
191 | 0 | trailer_iterator_init(&iter, body); |
192 | 0 | while (trailer_iterator_advance(&iter)) { |
193 | 0 | const char *value = iter.val.buf; |
194 | |
|
195 | 0 | if (!string_list_has_string(&log->trailers, iter.key.buf)) |
196 | 0 | continue; |
197 | | |
198 | 0 | strbuf_reset(&ident); |
199 | 0 | if (!parse_ident(log, &ident, value)) |
200 | 0 | value = ident.buf; |
201 | |
|
202 | 0 | if (!strset_add(dups, value)) |
203 | 0 | continue; |
204 | 0 | insert_one_record(log, value, oneline); |
205 | 0 | } |
206 | 0 | trailer_iterator_release(&iter); |
207 | |
|
208 | 0 | strbuf_release(&ident); |
209 | 0 | repo_unuse_commit_buffer(the_repository, commit, commit_buffer); |
210 | 0 | } |
211 | | |
212 | | static int shortlog_needs_dedup(const struct shortlog *log) |
213 | 0 | { |
214 | 0 | return HAS_MULTI_BITS(log->groups) || log->format.nr > 1 || log->trailers.nr; |
215 | 0 | } |
216 | | |
217 | | static void insert_records_from_format(struct shortlog *log, |
218 | | struct strset *dups, |
219 | | struct commit *commit, |
220 | | struct pretty_print_context *ctx, |
221 | | const char *oneline) |
222 | 0 | { |
223 | 0 | struct strbuf buf = STRBUF_INIT; |
224 | 0 | struct string_list_item *item; |
225 | |
|
226 | 0 | for_each_string_list_item(item, &log->format) { |
227 | 0 | strbuf_reset(&buf); |
228 | |
|
229 | 0 | repo_format_commit_message(the_repository, commit, |
230 | 0 | item->string, &buf, ctx); |
231 | |
|
232 | 0 | if (!shortlog_needs_dedup(log) || strset_add(dups, buf.buf)) |
233 | 0 | insert_one_record(log, buf.buf, oneline); |
234 | 0 | } |
235 | |
|
236 | 0 | strbuf_release(&buf); |
237 | 0 | } |
238 | | |
239 | | void shortlog_add_commit(struct shortlog *log, struct commit *commit) |
240 | 0 | { |
241 | 0 | struct strbuf oneline = STRBUF_INIT; |
242 | 0 | struct strset dups = STRSET_INIT; |
243 | 0 | struct pretty_print_context ctx = {0}; |
244 | 0 | const char *oneline_str; |
245 | |
|
246 | 0 | ctx.fmt = CMIT_FMT_USERFORMAT; |
247 | 0 | ctx.abbrev = log->abbrev; |
248 | 0 | ctx.date_mode = log->date_mode; |
249 | 0 | ctx.output_encoding = get_log_output_encoding(); |
250 | |
|
251 | 0 | if (!log->summary) { |
252 | 0 | if (log->user_format) |
253 | 0 | pretty_print_commit(&ctx, commit, &oneline); |
254 | 0 | else |
255 | 0 | repo_format_commit_message(the_repository, commit, |
256 | 0 | "%s", &oneline, &ctx); |
257 | 0 | } |
258 | 0 | oneline_str = oneline.len ? oneline.buf : "<none>"; |
259 | |
|
260 | 0 | insert_records_from_trailers(log, &dups, commit, &ctx, oneline_str); |
261 | 0 | insert_records_from_format(log, &dups, commit, &ctx, oneline_str); |
262 | |
|
263 | 0 | strset_clear(&dups); |
264 | 0 | strbuf_release(&oneline); |
265 | 0 | } |
266 | | |
267 | | static void get_from_rev(struct rev_info *rev, struct shortlog *log) |
268 | 0 | { |
269 | 0 | struct commit *commit; |
270 | |
|
271 | 0 | if (prepare_revision_walk(rev)) |
272 | 0 | die(_("revision walk setup failed")); |
273 | 0 | while ((commit = get_revision(rev)) != NULL) |
274 | 0 | shortlog_add_commit(log, commit); |
275 | 0 | } |
276 | | |
277 | | static int parse_uint(char const **arg, int comma, int defval) |
278 | 0 | { |
279 | 0 | unsigned long ul; |
280 | 0 | int ret; |
281 | 0 | char *endp; |
282 | |
|
283 | 0 | ul = strtoul(*arg, &endp, 10); |
284 | 0 | if (*endp && *endp != comma) |
285 | 0 | return -1; |
286 | 0 | if (ul > INT_MAX) |
287 | 0 | return -1; |
288 | 0 | ret = *arg == endp ? defval : (int)ul; |
289 | 0 | *arg = *endp ? endp + 1 : endp; |
290 | 0 | return ret; |
291 | 0 | } |
292 | | |
293 | | static const char wrap_arg_usage[] = "-w[<width>[,<indent1>[,<indent2>]]]"; |
294 | 0 | #define DEFAULT_WRAPLEN 76 |
295 | 0 | #define DEFAULT_INDENT1 6 |
296 | 0 | #define DEFAULT_INDENT2 9 |
297 | | |
298 | | static int parse_wrap_args(const struct option *opt, const char *arg, int unset) |
299 | 0 | { |
300 | 0 | struct shortlog *log = opt->value; |
301 | |
|
302 | 0 | log->wrap_lines = !unset; |
303 | 0 | if (unset) |
304 | 0 | return 0; |
305 | 0 | if (!arg) { |
306 | 0 | log->wrap = DEFAULT_WRAPLEN; |
307 | 0 | log->in1 = DEFAULT_INDENT1; |
308 | 0 | log->in2 = DEFAULT_INDENT2; |
309 | 0 | return 0; |
310 | 0 | } |
311 | | |
312 | 0 | log->wrap = parse_uint(&arg, ',', DEFAULT_WRAPLEN); |
313 | 0 | log->in1 = parse_uint(&arg, ',', DEFAULT_INDENT1); |
314 | 0 | log->in2 = parse_uint(&arg, '\0', DEFAULT_INDENT2); |
315 | 0 | if (log->wrap < 0 || log->in1 < 0 || log->in2 < 0) |
316 | 0 | return error(wrap_arg_usage); |
317 | 0 | if (log->wrap && |
318 | 0 | ((log->in1 && log->wrap <= log->in1) || |
319 | 0 | (log->in2 && log->wrap <= log->in2))) |
320 | 0 | return error(wrap_arg_usage); |
321 | 0 | return 0; |
322 | 0 | } |
323 | | |
324 | | static int parse_group_option(const struct option *opt, const char *arg, int unset) |
325 | 0 | { |
326 | 0 | struct shortlog *log = opt->value; |
327 | 0 | const char *field; |
328 | |
|
329 | 0 | if (unset) { |
330 | 0 | log->groups = 0; |
331 | 0 | string_list_clear(&log->trailers, 0); |
332 | 0 | string_list_clear(&log->format, 0); |
333 | 0 | } else if (!strcasecmp(arg, "author")) |
334 | 0 | log->groups |= SHORTLOG_GROUP_AUTHOR; |
335 | 0 | else if (!strcasecmp(arg, "committer")) |
336 | 0 | log->groups |= SHORTLOG_GROUP_COMMITTER; |
337 | 0 | else if (skip_prefix(arg, "trailer:", &field)) { |
338 | 0 | log->groups |= SHORTLOG_GROUP_TRAILER; |
339 | 0 | string_list_append(&log->trailers, field); |
340 | 0 | } else if (skip_prefix(arg, "format:", &field)) { |
341 | 0 | log->groups |= SHORTLOG_GROUP_FORMAT; |
342 | 0 | string_list_append(&log->format, field); |
343 | 0 | } else if (strchr(arg, '%')) { |
344 | 0 | log->groups |= SHORTLOG_GROUP_FORMAT; |
345 | 0 | string_list_append(&log->format, arg); |
346 | 0 | } else { |
347 | 0 | return error(_("unknown group type: %s"), arg); |
348 | 0 | } |
349 | | |
350 | 0 | return 0; |
351 | 0 | } |
352 | | |
353 | | |
354 | | void shortlog_init(struct shortlog *log) |
355 | 0 | { |
356 | 0 | memset(log, 0, sizeof(*log)); |
357 | |
|
358 | 0 | read_mailmap(&log->mailmap); |
359 | |
|
360 | 0 | log->list.strdup_strings = 1; |
361 | 0 | log->wrap = DEFAULT_WRAPLEN; |
362 | 0 | log->in1 = DEFAULT_INDENT1; |
363 | 0 | log->in2 = DEFAULT_INDENT2; |
364 | 0 | log->trailers.strdup_strings = 1; |
365 | 0 | log->trailers.cmp = strcasecmp; |
366 | 0 | log->format.strdup_strings = 1; |
367 | 0 | } |
368 | | |
369 | | void shortlog_finish_setup(struct shortlog *log) |
370 | 0 | { |
371 | 0 | if (log->groups & SHORTLOG_GROUP_AUTHOR) |
372 | 0 | string_list_append(&log->format, |
373 | 0 | log->email ? "%aN <%aE>" : "%aN"); |
374 | 0 | if (log->groups & SHORTLOG_GROUP_COMMITTER) |
375 | 0 | string_list_append(&log->format, |
376 | 0 | log->email ? "%cN <%cE>" : "%cN"); |
377 | |
|
378 | 0 | string_list_sort(&log->trailers); |
379 | 0 | } |
380 | | |
381 | | int cmd_shortlog(int argc, const char **argv, const char *prefix) |
382 | 0 | { |
383 | 0 | struct shortlog log = { STRING_LIST_INIT_NODUP }; |
384 | 0 | struct rev_info rev; |
385 | 0 | int nongit = !startup_info->have_repository; |
386 | |
|
387 | 0 | const struct option options[] = { |
388 | 0 | OPT_BIT('c', "committer", &log.groups, |
389 | 0 | N_("group by committer rather than author"), |
390 | 0 | SHORTLOG_GROUP_COMMITTER), |
391 | 0 | OPT_BOOL('n', "numbered", &log.sort_by_number, |
392 | 0 | N_("sort output according to the number of commits per author")), |
393 | 0 | OPT_BOOL('s', "summary", &log.summary, |
394 | 0 | N_("suppress commit descriptions, only provides commit count")), |
395 | 0 | OPT_BOOL('e', "email", &log.email, |
396 | 0 | N_("show the email address of each author")), |
397 | 0 | OPT_CALLBACK_F('w', NULL, &log, N_("<w>[,<i1>[,<i2>]]"), |
398 | 0 | N_("linewrap output"), PARSE_OPT_OPTARG, |
399 | 0 | &parse_wrap_args), |
400 | 0 | OPT_CALLBACK(0, "group", &log, N_("field"), |
401 | 0 | N_("group by field"), parse_group_option), |
402 | 0 | OPT_END(), |
403 | 0 | }; |
404 | |
|
405 | 0 | struct parse_opt_ctx_t ctx; |
406 | |
|
407 | 0 | git_config(git_default_config, NULL); |
408 | 0 | shortlog_init(&log); |
409 | 0 | repo_init_revisions(the_repository, &rev, prefix); |
410 | 0 | parse_options_start(&ctx, argc, argv, prefix, options, |
411 | 0 | PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0); |
412 | |
|
413 | 0 | for (;;) { |
414 | 0 | switch (parse_options_step(&ctx, options, shortlog_usage)) { |
415 | 0 | case PARSE_OPT_NON_OPTION: |
416 | 0 | case PARSE_OPT_UNKNOWN: |
417 | 0 | break; |
418 | 0 | case PARSE_OPT_HELP: |
419 | 0 | case PARSE_OPT_ERROR: |
420 | 0 | case PARSE_OPT_SUBCOMMAND: |
421 | 0 | exit(129); |
422 | 0 | case PARSE_OPT_COMPLETE: |
423 | 0 | exit(0); |
424 | 0 | case PARSE_OPT_DONE: |
425 | 0 | goto parse_done; |
426 | 0 | } |
427 | 0 | parse_revision_opt(&rev, &ctx, options, shortlog_usage); |
428 | 0 | } |
429 | 0 | parse_done: |
430 | 0 | revision_opts_finish(&rev); |
431 | 0 | argc = parse_options_end(&ctx); |
432 | |
|
433 | 0 | if (nongit && argc > 1) { |
434 | 0 | error(_("too many arguments given outside repository")); |
435 | 0 | usage_with_options(shortlog_usage, options); |
436 | 0 | } |
437 | | |
438 | 0 | if (!nongit && setup_revisions(argc, argv, &rev, NULL) != 1) { |
439 | 0 | error(_("unrecognized argument: %s"), argv[1]); |
440 | 0 | usage_with_options(shortlog_usage, options); |
441 | 0 | } |
442 | | |
443 | 0 | log.user_format = rev.commit_format == CMIT_FMT_USERFORMAT; |
444 | 0 | log.abbrev = rev.abbrev; |
445 | 0 | log.file = rev.diffopt.file; |
446 | 0 | log.date_mode = rev.date_mode; |
447 | |
|
448 | 0 | if (!log.groups) |
449 | 0 | log.groups = SHORTLOG_GROUP_AUTHOR; |
450 | 0 | shortlog_finish_setup(&log); |
451 | | |
452 | | /* assume HEAD if from a tty */ |
453 | 0 | if (!nongit && !rev.pending.nr && isatty(0)) |
454 | 0 | add_head_to_pending(&rev); |
455 | 0 | if (rev.pending.nr == 0) { |
456 | 0 | if (isatty(0)) |
457 | 0 | fprintf(stderr, _("(reading log message from standard input)\n")); |
458 | 0 | read_from_stdin(&log); |
459 | 0 | } |
460 | 0 | else |
461 | 0 | get_from_rev(&rev, &log); |
462 | |
|
463 | 0 | shortlog_output(&log); |
464 | 0 | release_revisions(&rev); |
465 | 0 | return 0; |
466 | 0 | } |
467 | | |
468 | | static void add_wrapped_shortlog_msg(struct strbuf *sb, const char *s, |
469 | | const struct shortlog *log) |
470 | 0 | { |
471 | 0 | strbuf_add_wrapped_text(sb, s, log->in1, log->in2, log->wrap); |
472 | 0 | strbuf_addch(sb, '\n'); |
473 | 0 | } |
474 | | |
475 | | void shortlog_output(struct shortlog *log) |
476 | 0 | { |
477 | 0 | size_t i, j; |
478 | 0 | struct strbuf sb = STRBUF_INIT; |
479 | |
|
480 | 0 | if (log->sort_by_number) |
481 | 0 | STABLE_QSORT(log->list.items, log->list.nr, |
482 | 0 | log->summary ? compare_by_counter : compare_by_list); |
483 | 0 | for (i = 0; i < log->list.nr; i++) { |
484 | 0 | const struct string_list_item *item = &log->list.items[i]; |
485 | 0 | if (log->summary) { |
486 | 0 | fprintf(log->file, "%6d\t%s\n", |
487 | 0 | (int)UTIL_TO_INT(item), item->string); |
488 | 0 | } else { |
489 | 0 | struct string_list *onelines = item->util; |
490 | 0 | fprintf(log->file, "%s (%"PRIuMAX"):\n", |
491 | 0 | item->string, (uintmax_t)onelines->nr); |
492 | 0 | for (j = onelines->nr; j >= 1; j--) { |
493 | 0 | const char *msg = onelines->items[j - 1].string; |
494 | |
|
495 | 0 | if (log->wrap_lines) { |
496 | 0 | strbuf_reset(&sb); |
497 | 0 | add_wrapped_shortlog_msg(&sb, msg, log); |
498 | 0 | fwrite(sb.buf, sb.len, 1, log->file); |
499 | 0 | } |
500 | 0 | else |
501 | 0 | fprintf(log->file, " %s\n", msg); |
502 | 0 | } |
503 | 0 | putc('\n', log->file); |
504 | 0 | onelines->strdup_strings = 1; |
505 | 0 | string_list_clear(onelines, 0); |
506 | 0 | free(onelines); |
507 | 0 | } |
508 | |
|
509 | 0 | log->list.items[i].util = NULL; |
510 | 0 | } |
511 | |
|
512 | 0 | strbuf_release(&sb); |
513 | 0 | log->list.strdup_strings = 1; |
514 | 0 | string_list_clear(&log->list, 1); |
515 | 0 | clear_mailmap(&log->mailmap); |
516 | 0 | string_list_clear(&log->format, 0); |
517 | 0 | string_list_clear(&log->trailers, 0); |
518 | 0 | } |