7 #include "reflog-walk.h"
9 #include "string-list.h"
12 struct decoration name_decoration = { "object names" };
14 enum decoration_type {
17 DECORATION_REF_REMOTE,
24 static char decoration_colors[][COLOR_MAXLEN] = {
26 GIT_COLOR_BOLD_GREEN, /* REF_LOCAL */
27 GIT_COLOR_BOLD_RED, /* REF_REMOTE */
28 GIT_COLOR_BOLD_YELLOW, /* REF_TAG */
29 GIT_COLOR_BOLD_MAGENTA, /* REF_STASH */
30 GIT_COLOR_BOLD_CYAN, /* REF_HEAD */
31 GIT_COLOR_BOLD_BLUE, /* GRAFTED */
34 static const char *decorate_get_color(int decorate_use_color, enum decoration_type ix)
36 if (want_color(decorate_use_color))
37 return decoration_colors[ix];
41 static int parse_decorate_color_slot(const char *slot)
44 * We're comparing with 'ignore-case' on
45 * (because config.c sets them all tolower),
46 * but let's match the letters in the literal
47 * string values here with how they are
48 * documented in Documentation/config.txt, for
51 * We love being consistent, don't we?
53 if (!strcasecmp(slot, "branch"))
54 return DECORATION_REF_LOCAL;
55 if (!strcasecmp(slot, "remoteBranch"))
56 return DECORATION_REF_REMOTE;
57 if (!strcasecmp(slot, "tag"))
58 return DECORATION_REF_TAG;
59 if (!strcasecmp(slot, "stash"))
60 return DECORATION_REF_STASH;
61 if (!strcasecmp(slot, "HEAD"))
62 return DECORATION_REF_HEAD;
66 int parse_decorate_color_config(const char *var, const int ofs, const char *value)
68 int slot = parse_decorate_color_slot(var + ofs);
72 return config_error_nonbool(var);
73 color_parse(value, var, decoration_colors[slot]);
78 * log-tree.c uses DIFF_OPT_TST for determining whether to use color
79 * for showing the commit sha1, use the same check for --decorate
81 #define decorate_get_color_opt(o, ix) \
82 decorate_get_color((o)->use_color, ix)
84 static void add_name_decoration(enum decoration_type type, const char *name, struct object *obj)
86 int nlen = strlen(name);
87 struct name_decoration *res = xmalloc(sizeof(struct name_decoration) + nlen);
88 memcpy(res->name, name, nlen + 1);
90 res->next = add_decoration(&name_decoration, obj, res);
93 static int add_ref_decoration(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
96 enum decoration_type type = DECORATION_NONE;
98 if (!prefixcmp(refname, "refs/replace/")) {
99 unsigned char original_sha1[20];
100 if (get_sha1_hex(refname + 13, original_sha1)) {
101 warning("invalid replace ref %s", refname);
104 obj = parse_object(original_sha1);
106 add_name_decoration(DECORATION_GRAFTED, "replaced", obj);
110 obj = parse_object(sha1);
114 if (!prefixcmp(refname, "refs/heads/"))
115 type = DECORATION_REF_LOCAL;
116 else if (!prefixcmp(refname, "refs/remotes/"))
117 type = DECORATION_REF_REMOTE;
118 else if (!prefixcmp(refname, "refs/tags/"))
119 type = DECORATION_REF_TAG;
120 else if (!prefixcmp(refname, "refs/stash"))
121 type = DECORATION_REF_STASH;
122 else if (!prefixcmp(refname, "HEAD"))
123 type = DECORATION_REF_HEAD;
125 if (!cb_data || *(int *)cb_data == DECORATE_SHORT_REFS)
126 refname = prettify_refname(refname);
127 add_name_decoration(type, refname, obj);
128 while (obj->type == OBJ_TAG) {
129 obj = ((struct tag *)obj)->tagged;
132 add_name_decoration(DECORATION_REF_TAG, refname, obj);
137 static int add_graft_decoration(const struct commit_graft *graft, void *cb_data)
139 struct commit *commit = lookup_commit(graft->sha1);
142 add_name_decoration(DECORATION_GRAFTED, "grafted", &commit->object);
146 void load_ref_decorations(int flags)
151 for_each_ref(add_ref_decoration, &flags);
152 head_ref(add_ref_decoration, &flags);
153 for_each_commit_graft(add_graft_decoration, NULL);
157 static void show_parents(struct commit *commit, int abbrev)
159 struct commit_list *p;
160 for (p = commit->parents; p ; p = p->next) {
161 struct commit *parent = p->item;
162 printf(" %s", find_unique_abbrev(parent->object.sha1, abbrev));
166 void show_decorations(struct rev_info *opt, struct commit *commit)
169 struct name_decoration *decoration;
170 const char *color_commit =
171 diff_get_color_opt(&opt->diffopt, DIFF_COMMIT);
172 const char *color_reset =
173 decorate_get_color_opt(&opt->diffopt, DECORATION_NONE);
175 if (opt->show_source && commit->util)
176 printf("\t%s", (char *) commit->util);
177 if (!opt->show_decorations)
179 decoration = lookup_decoration(&name_decoration, &commit->object);
184 printf("%s", prefix);
185 fputs(decorate_get_color_opt(&opt->diffopt, decoration->type),
187 if (decoration->type == DECORATION_REF_TAG)
188 fputs("tag: ", stdout);
189 printf("%s", decoration->name);
190 fputs(color_reset, stdout);
191 fputs(color_commit, stdout);
193 decoration = decoration->next;
199 * Search for "^[-A-Za-z]+: [^@]+@" pattern. It usually matches
200 * Signed-off-by: and Acked-by: lines.
202 static int detect_any_signoff(char *letter, int size)
211 while (letter <= --cp && *cp == '\n')
214 while (letter <= cp) {
233 if (('A' <= ch && ch <= 'Z') ||
234 ('a' <= ch && ch <= 'z') ||
239 /* no empty last line doesn't match */
242 return seen_head && seen_name;
245 static void append_signoff(struct strbuf *sb, const char *signoff)
247 static const char signed_off_by[] = "Signed-off-by: ";
248 size_t signoff_len = strlen(signoff);
254 /* First see if we already have the sign-off by the signer */
255 while ((cp = strstr(cp, signed_off_by))) {
259 cp += strlen(signed_off_by);
260 if (cp + signoff_len >= sb->buf + sb->len)
262 if (strncmp(cp, signoff, signoff_len))
264 if (!isspace(cp[signoff_len]))
266 /* we already have him */
271 has_signoff = detect_any_signoff(sb->buf, sb->len);
274 strbuf_addch(sb, '\n');
276 strbuf_addstr(sb, signed_off_by);
277 strbuf_add(sb, signoff, signoff_len);
278 strbuf_addch(sb, '\n');
281 static unsigned int digits_in_number(unsigned int number)
283 unsigned int i = 10, result = 1;
284 while (i <= number) {
291 void get_patch_filename(struct commit *commit, int nr, const char *suffix,
294 int suffix_len = strlen(suffix) + 1;
295 int start_len = buf->len;
297 strbuf_addf(buf, commit ? "%04d-" : "%d", nr);
299 int max_len = start_len + FORMAT_PATCH_NAME_MAX - suffix_len;
300 struct pretty_print_context ctx = {0};
301 ctx.date_mode = DATE_NORMAL;
303 format_commit_message(commit, "%f", buf, &ctx);
304 if (max_len < buf->len)
305 strbuf_setlen(buf, max_len);
306 strbuf_addstr(buf, suffix);
310 void log_write_email_headers(struct rev_info *opt, struct commit *commit,
311 const char **subject_p,
312 const char **extra_headers_p,
313 int *need_8bit_cte_p)
315 const char *subject = NULL;
316 const char *extra_headers = opt->extra_headers;
317 const char *name = sha1_to_hex(commit->object.sha1);
319 *need_8bit_cte_p = 0; /* unknown */
320 if (opt->total > 0) {
321 static char buffer[64];
322 snprintf(buffer, sizeof(buffer),
323 "Subject: [%s%s%0*d/%d] ",
325 *opt->subject_prefix ? " " : "",
326 digits_in_number(opt->total),
327 opt->nr, opt->total);
329 } else if (opt->total == 0 && opt->subject_prefix && *opt->subject_prefix) {
330 static char buffer[256];
331 snprintf(buffer, sizeof(buffer),
333 opt->subject_prefix);
336 subject = "Subject: ";
339 printf("From %s Mon Sep 17 00:00:00 2001\n", name);
340 graph_show_oneline(opt->graph);
341 if (opt->message_id) {
342 printf("Message-Id: <%s>\n", opt->message_id);
343 graph_show_oneline(opt->graph);
345 if (opt->ref_message_ids && opt->ref_message_ids->nr > 0) {
347 n = opt->ref_message_ids->nr;
348 printf("In-Reply-To: <%s>\n", opt->ref_message_ids->items[n-1].string);
349 for (i = 0; i < n; i++)
350 printf("%s<%s>\n", (i > 0 ? "\t" : "References: "),
351 opt->ref_message_ids->items[i].string);
352 graph_show_oneline(opt->graph);
354 if (opt->mime_boundary) {
355 static char subject_buffer[1024];
356 static char buffer[1024];
357 struct strbuf filename = STRBUF_INIT;
358 *need_8bit_cte_p = -1; /* NEVER */
359 snprintf(subject_buffer, sizeof(subject_buffer) - 1,
361 "MIME-Version: 1.0\n"
362 "Content-Type: multipart/mixed;"
363 " boundary=\"%s%s\"\n"
365 "This is a multi-part message in MIME "
368 "Content-Type: text/plain; "
369 "charset=UTF-8; format=fixed\n"
370 "Content-Transfer-Encoding: 8bit\n\n",
371 extra_headers ? extra_headers : "",
372 mime_boundary_leader, opt->mime_boundary,
373 mime_boundary_leader, opt->mime_boundary);
374 extra_headers = subject_buffer;
376 get_patch_filename(opt->numbered_files ? NULL : commit, opt->nr,
377 opt->patch_suffix, &filename);
378 snprintf(buffer, sizeof(buffer) - 1,
380 "Content-Type: text/x-patch;"
382 "Content-Transfer-Encoding: 8bit\n"
383 "Content-Disposition: %s;"
384 " filename=\"%s\"\n\n",
385 mime_boundary_leader, opt->mime_boundary,
387 opt->no_inline ? "attachment" : "inline",
389 opt->diffopt.stat_sep = buffer;
390 strbuf_release(&filename);
392 *subject_p = subject;
393 *extra_headers_p = extra_headers;
396 void show_log(struct rev_info *opt)
398 struct strbuf msgbuf = STRBUF_INIT;
399 struct log_info *log = opt->loginfo;
400 struct commit *commit = log->commit, *parent = log->parent;
401 int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40;
402 const char *extra_headers = opt->extra_headers;
403 struct pretty_print_context ctx = {0};
406 ctx.show_notes = opt->show_notes;
407 if (!opt->verbose_header) {
408 graph_show_commit(opt->graph);
411 put_revision_mark(opt, commit);
412 fputs(find_unique_abbrev(commit->object.sha1, abbrev_commit), stdout);
413 if (opt->print_parents)
414 show_parents(commit, abbrev_commit);
415 show_decorations(opt, commit);
416 if (opt->graph && !graph_is_commit_finished(opt->graph)) {
418 graph_show_remainder(opt->graph);
420 putchar(opt->diffopt.line_termination);
425 * If use_terminator is set, we already handled any record termination
426 * at the end of the last record.
427 * Otherwise, add a diffopt.line_termination character before all
428 * entries but the first. (IOW, as a separator between entries)
430 if (opt->shown_one && !opt->use_terminator) {
432 * If entries are separated by a newline, the output
433 * should look human-readable. If the last entry ended
434 * with a newline, print the graph output before this
435 * newline. Otherwise it will end up as a completely blank
436 * line and will look like a gap in the graph.
438 * If the entry separator is not a newline, the output is
439 * primarily intended for programmatic consumption, and we
440 * never want the extra graph output before the entry
443 if (opt->diffopt.line_termination == '\n' &&
444 !opt->missing_newline)
445 graph_show_padding(opt->graph);
446 putchar(opt->diffopt.line_termination);
451 * If the history graph was requested,
452 * print the graph, up to this commit's line
454 graph_show_commit(opt->graph);
457 * Print header line of header..
460 if (opt->commit_format == CMIT_FMT_EMAIL) {
461 log_write_email_headers(opt, commit, &ctx.subject, &extra_headers,
463 } else if (opt->commit_format != CMIT_FMT_USERFORMAT) {
464 fputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), stdout);
465 if (opt->commit_format != CMIT_FMT_ONELINE)
466 fputs("commit ", stdout);
469 put_revision_mark(opt, commit);
470 fputs(find_unique_abbrev(commit->object.sha1, abbrev_commit),
472 if (opt->print_parents)
473 show_parents(commit, abbrev_commit);
476 find_unique_abbrev(parent->object.sha1,
478 show_decorations(opt, commit);
479 printf("%s", diff_get_color_opt(&opt->diffopt, DIFF_RESET));
480 if (opt->commit_format == CMIT_FMT_ONELINE) {
484 graph_show_oneline(opt->graph);
486 if (opt->reflog_info) {
488 * setup_revisions() ensures that opt->reflog_info
489 * and opt->graph cannot both be set,
490 * so we don't need to worry about printing the
493 show_reflog_message(opt->reflog_info,
494 opt->commit_format == CMIT_FMT_ONELINE,
495 opt->date_mode_explicit ?
498 if (opt->commit_format == CMIT_FMT_ONELINE)
507 * And then the pretty-printed message itself
509 if (ctx.need_8bit_cte >= 0)
510 ctx.need_8bit_cte = has_non_ascii(opt->add_signoff);
511 ctx.date_mode = opt->date_mode;
512 ctx.abbrev = opt->diffopt.abbrev;
513 ctx.after_subject = extra_headers;
514 ctx.preserve_subject = opt->preserve_subject;
515 ctx.reflog_info = opt->reflog_info;
516 ctx.fmt = opt->commit_format;
517 pretty_print_commit(&ctx, commit, &msgbuf);
519 if (opt->add_signoff)
520 append_signoff(&msgbuf, opt->add_signoff);
521 if (opt->show_log_size) {
522 printf("log size %i\n", (int)msgbuf.len);
523 graph_show_oneline(opt->graph);
527 * Set opt->missing_newline if msgbuf doesn't
528 * end in a newline (including if it is empty)
530 if (!msgbuf.len || msgbuf.buf[msgbuf.len - 1] != '\n')
531 opt->missing_newline = 1;
533 opt->missing_newline = 0;
536 graph_show_commit_msg(opt->graph, &msgbuf);
538 fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);
539 if (opt->use_terminator) {
540 if (!opt->missing_newline)
541 graph_show_padding(opt->graph);
545 strbuf_release(&msgbuf);
548 int log_tree_diff_flush(struct rev_info *opt)
550 diffcore_std(&opt->diffopt);
552 if (diff_queue_is_empty()) {
553 int saved_fmt = opt->diffopt.output_format;
554 opt->diffopt.output_format = DIFF_FORMAT_NO_OUTPUT;
555 diff_flush(&opt->diffopt);
556 opt->diffopt.output_format = saved_fmt;
560 if (opt->loginfo && !opt->no_commit_id) {
561 /* When showing a verbose header (i.e. log message),
562 * and not in --pretty=oneline format, we would want
563 * an extra newline between the end of log and the
564 * output for readability.
567 if ((opt->diffopt.output_format & ~DIFF_FORMAT_NO_OUTPUT) &&
568 opt->verbose_header &&
569 opt->commit_format != CMIT_FMT_ONELINE) {
570 int pch = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_PATCH;
571 if ((pch & opt->diffopt.output_format) == pch)
573 if (opt->diffopt.output_prefix) {
574 struct strbuf *msg = NULL;
575 msg = opt->diffopt.output_prefix(&opt->diffopt,
576 opt->diffopt.output_prefix_data);
577 fwrite(msg->buf, msg->len, 1, stdout);
582 diff_flush(&opt->diffopt);
586 static int do_diff_combined(struct rev_info *opt, struct commit *commit)
588 unsigned const char *sha1 = commit->object.sha1;
590 diff_tree_combined_merge(sha1, opt->dense_combined_merges, opt);
591 return !opt->loginfo;
595 * Show the diff of a commit.
597 * Return true if we printed any log info messages
599 static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log_info *log)
602 struct commit_list *parents;
603 unsigned const char *sha1 = commit->object.sha1;
605 if (!opt->diff && !DIFF_OPT_TST(&opt->diffopt, EXIT_WITH_STATUS))
609 parents = commit->parents;
611 if (opt->show_root_diff) {
612 diff_root_tree_sha1(sha1, "", &opt->diffopt);
613 log_tree_diff_flush(opt);
615 return !opt->loginfo;
618 /* More than one parent? */
619 if (parents && parents->next) {
620 if (opt->ignore_merges)
622 else if (opt->combine_merges)
623 return do_diff_combined(opt, commit);
624 else if (opt->first_parent_only) {
626 * Generate merge log entry only for the first
627 * parent, showing summary diff of the others
630 diff_tree_sha1(parents->item->object.sha1, sha1, "", &opt->diffopt);
631 log_tree_diff_flush(opt);
632 return !opt->loginfo;
635 /* If we show individual diffs, show the parent info */
636 log->parent = parents->item;
641 struct commit *parent = parents->item;
643 diff_tree_sha1(parent->object.sha1, sha1, "", &opt->diffopt);
644 log_tree_diff_flush(opt);
646 showed_log |= !opt->loginfo;
648 /* Set up the log info for the next parent, if any.. */
649 parents = parents->next;
652 log->parent = parents->item;
658 int log_tree_commit(struct rev_info *opt, struct commit *commit)
667 shown = log_tree_diff(opt, commit, &log);
668 if (!shown && opt->loginfo && opt->always_show_header) {
674 maybe_flush_or_die(stdout, "stdout");