1 #include "cache.h"
2 #include "config.h"
3 #include "diff.h"
4 #include "object-store.h"
5 #include "repository.h"
6 #include "commit.h"
7 #include "tag.h"
8 #include "graph.h"
9 #include "log-tree.h"
10 #include "reflog-walk.h"
11 #include "refs.h"
12 #include "string-list.h"
13 #include "color.h"
14 #include "gpg-interface.h"
15 #include "sequencer.h"
16 #include "line-log.h"
17 #include "help.h"
18 #include "range-diff.h"
19 
20 static struct decoration name_decoration = { "object names" };
21 static int decoration_loaded;
22 static int decoration_flags;
23 
24 static char decoration_colors[][COLOR_MAXLEN] = {
25 	GIT_COLOR_RESET,
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 */
32 };
33 
34 static const char *color_decorate_slots[] = {
35 	[DECORATION_REF_LOCAL]	= "branch",
36 	[DECORATION_REF_REMOTE] = "remoteBranch",
37 	[DECORATION_REF_TAG]	= "tag",
38 	[DECORATION_REF_STASH]	= "stash",
39 	[DECORATION_REF_HEAD]	= "HEAD",
40 	[DECORATION_GRAFTED]	= "grafted",
41 };
42 
decorate_get_color(int decorate_use_color,enum decoration_type ix)43 static const char *decorate_get_color(int decorate_use_color, enum decoration_type ix)
44 {
45 	if (want_color(decorate_use_color))
46 		return decoration_colors[ix];
47 	return "";
48 }
49 
50 define_list_config_array(color_decorate_slots);
51 
parse_decorate_color_config(const char * var,const char * slot_name,const char * value)52 int parse_decorate_color_config(const char *var, const char *slot_name, const char *value)
53 {
54 	int slot = LOOKUP_CONFIG(color_decorate_slots, slot_name);
55 	if (slot < 0)
56 		return 0;
57 	if (!value)
58 		return config_error_nonbool(var);
59 	return color_parse(value, decoration_colors[slot]);
60 }
61 
62 /*
63  * log-tree.c uses DIFF_OPT_TST for determining whether to use color
64  * for showing the commit sha1, use the same check for --decorate
65  */
66 #define decorate_get_color_opt(o, ix) \
67 	decorate_get_color((o)->use_color, ix)
68 
add_name_decoration(enum decoration_type type,const char * name,struct object * obj)69 void add_name_decoration(enum decoration_type type, const char *name, struct object *obj)
70 {
71 	struct name_decoration *res;
72 	FLEX_ALLOC_STR(res, name, name);
73 	res->type = type;
74 	res->next = add_decoration(&name_decoration, obj, res);
75 }
76 
get_name_decoration(const struct object * obj)77 const struct name_decoration *get_name_decoration(const struct object *obj)
78 {
79 	load_ref_decorations(NULL, DECORATE_SHORT_REFS);
80 	return lookup_decoration(&name_decoration, obj);
81 }
82 
match_ref_pattern(const char * refname,const struct string_list_item * item)83 static int match_ref_pattern(const char *refname,
84 			     const struct string_list_item *item)
85 {
86 	int matched = 0;
87 	if (item->util == NULL) {
88 		if (!wildmatch(item->string, refname, 0))
89 			matched = 1;
90 	} else {
91 		const char *rest;
92 		if (skip_prefix(refname, item->string, &rest) &&
93 		    (!*rest || *rest == '/'))
94 			matched = 1;
95 	}
96 	return matched;
97 }
98 
ref_filter_match(const char * refname,const struct decoration_filter * filter)99 static int ref_filter_match(const char *refname,
100 			    const struct decoration_filter *filter)
101 {
102 	struct string_list_item *item;
103 	const struct string_list *exclude_patterns = filter->exclude_ref_pattern;
104 	const struct string_list *include_patterns = filter->include_ref_pattern;
105 	const struct string_list *exclude_patterns_config =
106 				filter->exclude_ref_config_pattern;
107 
108 	if (exclude_patterns && exclude_patterns->nr) {
109 		for_each_string_list_item(item, exclude_patterns) {
110 			if (match_ref_pattern(refname, item))
111 				return 0;
112 		}
113 	}
114 
115 	if (include_patterns && include_patterns->nr) {
116 		for_each_string_list_item(item, include_patterns) {
117 			if (match_ref_pattern(refname, item))
118 				return 1;
119 		}
120 		return 0;
121 	}
122 
123 	if (exclude_patterns_config && exclude_patterns_config->nr) {
124 		for_each_string_list_item(item, exclude_patterns_config) {
125 			if (match_ref_pattern(refname, item))
126 				return 0;
127 		}
128 	}
129 
130 	return 1;
131 }
132 
add_ref_decoration(const char * refname,const struct object_id * oid,int flags,void * cb_data)133 static int add_ref_decoration(const char *refname, const struct object_id *oid,
134 			      int flags, void *cb_data)
135 {
136 	struct object *obj;
137 	enum object_type objtype;
138 	enum decoration_type deco_type = DECORATION_NONE;
139 	struct decoration_filter *filter = (struct decoration_filter *)cb_data;
140 
141 	if (filter && !ref_filter_match(refname, filter))
142 		return 0;
143 
144 	if (starts_with(refname, git_replace_ref_base)) {
145 		struct object_id original_oid;
146 		if (!read_replace_refs)
147 			return 0;
148 		if (get_oid_hex(refname + strlen(git_replace_ref_base),
149 				&original_oid)) {
150 			warning("invalid replace ref %s", refname);
151 			return 0;
152 		}
153 		obj = parse_object(the_repository, &original_oid);
154 		if (obj)
155 			add_name_decoration(DECORATION_GRAFTED, "replaced", obj);
156 		return 0;
157 	}
158 
159 	objtype = oid_object_info(the_repository, oid, NULL);
160 	if (objtype < 0)
161 		return 0;
162 	obj = lookup_object_by_type(the_repository, oid, objtype);
163 
164 	if (starts_with(refname, "refs/heads/"))
165 		deco_type = DECORATION_REF_LOCAL;
166 	else if (starts_with(refname, "refs/remotes/"))
167 		deco_type = DECORATION_REF_REMOTE;
168 	else if (starts_with(refname, "refs/tags/"))
169 		deco_type = DECORATION_REF_TAG;
170 	else if (!strcmp(refname, "refs/stash"))
171 		deco_type = DECORATION_REF_STASH;
172 	else if (!strcmp(refname, "HEAD"))
173 		deco_type = DECORATION_REF_HEAD;
174 
175 	add_name_decoration(deco_type, refname, obj);
176 	while (obj->type == OBJ_TAG) {
177 		if (!obj->parsed)
178 			parse_object(the_repository, &obj->oid);
179 		obj = ((struct tag *)obj)->tagged;
180 		if (!obj)
181 			break;
182 		add_name_decoration(DECORATION_REF_TAG, refname, obj);
183 	}
184 	return 0;
185 }
186 
add_graft_decoration(const struct commit_graft * graft,void * cb_data)187 static int add_graft_decoration(const struct commit_graft *graft, void *cb_data)
188 {
189 	struct commit *commit = lookup_commit(the_repository, &graft->oid);
190 	if (!commit)
191 		return 0;
192 	add_name_decoration(DECORATION_GRAFTED, "grafted", &commit->object);
193 	return 0;
194 }
195 
load_ref_decorations(struct decoration_filter * filter,int flags)196 void load_ref_decorations(struct decoration_filter *filter, int flags)
197 {
198 	if (!decoration_loaded) {
199 		if (filter) {
200 			struct string_list_item *item;
201 			for_each_string_list_item(item, filter->exclude_ref_pattern) {
202 				normalize_glob_ref(item, NULL, item->string);
203 			}
204 			for_each_string_list_item(item, filter->include_ref_pattern) {
205 				normalize_glob_ref(item, NULL, item->string);
206 			}
207 			for_each_string_list_item(item, filter->exclude_ref_config_pattern) {
208 				normalize_glob_ref(item, NULL, item->string);
209 			}
210 		}
211 		decoration_loaded = 1;
212 		decoration_flags = flags;
213 		for_each_ref(add_ref_decoration, filter);
214 		head_ref(add_ref_decoration, filter);
215 		for_each_commit_graft(add_graft_decoration, filter);
216 	}
217 }
218 
show_parents(struct commit * commit,int abbrev,FILE * file)219 static void show_parents(struct commit *commit, int abbrev, FILE *file)
220 {
221 	struct commit_list *p;
222 	for (p = commit->parents; p ; p = p->next) {
223 		struct commit *parent = p->item;
224 		fprintf(file, " %s", find_unique_abbrev(&parent->object.oid, abbrev));
225 	}
226 }
227 
show_children(struct rev_info * opt,struct commit * commit,int abbrev)228 static void show_children(struct rev_info *opt, struct commit *commit, int abbrev)
229 {
230 	struct commit_list *p = lookup_decoration(&opt->children, &commit->object);
231 	for ( ; p; p = p->next) {
232 		fprintf(opt->diffopt.file, " %s", find_unique_abbrev(&p->item->object.oid, abbrev));
233 	}
234 }
235 
236 /*
237  * Do we have HEAD in the output, and also the branch it points at?
238  * If so, find that decoration entry for that current branch.
239  */
current_pointed_by_HEAD(const struct name_decoration * decoration)240 static const struct name_decoration *current_pointed_by_HEAD(const struct name_decoration *decoration)
241 {
242 	const struct name_decoration *list, *head = NULL;
243 	const char *branch_name = NULL;
244 	int rru_flags;
245 
246 	/* First find HEAD */
247 	for (list = decoration; list; list = list->next)
248 		if (list->type == DECORATION_REF_HEAD) {
249 			head = list;
250 			break;
251 		}
252 	if (!head)
253 		return NULL;
254 
255 	/* Now resolve and find the matching current branch */
256 	branch_name = resolve_ref_unsafe("HEAD", 0, NULL, &rru_flags);
257 	if (!branch_name || !(rru_flags & REF_ISSYMREF))
258 		return NULL;
259 
260 	if (!starts_with(branch_name, "refs/"))
261 		return NULL;
262 
263 	/* OK, do we have that ref in the list? */
264 	for (list = decoration; list; list = list->next)
265 		if ((list->type == DECORATION_REF_LOCAL) &&
266 		    !strcmp(branch_name, list->name)) {
267 			return list;
268 		}
269 
270 	return NULL;
271 }
272 
show_name(struct strbuf * sb,const struct name_decoration * decoration)273 static void show_name(struct strbuf *sb, const struct name_decoration *decoration)
274 {
275 	if (decoration_flags == DECORATE_SHORT_REFS)
276 		strbuf_addstr(sb, prettify_refname(decoration->name));
277 	else
278 		strbuf_addstr(sb, decoration->name);
279 }
280 
281 /*
282  * The caller makes sure there is no funny color before calling.
283  * format_decorations_extended makes sure the same after return.
284  */
format_decorations_extended(struct strbuf * sb,const struct commit * commit,int use_color,const char * prefix,const char * separator,const char * suffix)285 void format_decorations_extended(struct strbuf *sb,
286 			const struct commit *commit,
287 			int use_color,
288 			const char *prefix,
289 			const char *separator,
290 			const char *suffix)
291 {
292 	const struct name_decoration *decoration;
293 	const struct name_decoration *current_and_HEAD;
294 	const char *color_commit =
295 		diff_get_color(use_color, DIFF_COMMIT);
296 	const char *color_reset =
297 		decorate_get_color(use_color, DECORATION_NONE);
298 
299 	decoration = get_name_decoration(&commit->object);
300 	if (!decoration)
301 		return;
302 
303 	current_and_HEAD = current_pointed_by_HEAD(decoration);
304 	while (decoration) {
305 		/*
306 		 * When both current and HEAD are there, only
307 		 * show HEAD->current where HEAD would have
308 		 * appeared, skipping the entry for current.
309 		 */
310 		if (decoration != current_and_HEAD) {
311 			strbuf_addstr(sb, color_commit);
312 			strbuf_addstr(sb, prefix);
313 			strbuf_addstr(sb, color_reset);
314 			strbuf_addstr(sb, decorate_get_color(use_color, decoration->type));
315 			if (decoration->type == DECORATION_REF_TAG)
316 				strbuf_addstr(sb, "tag: ");
317 
318 			show_name(sb, decoration);
319 
320 			if (current_and_HEAD &&
321 			    decoration->type == DECORATION_REF_HEAD) {
322 				strbuf_addstr(sb, " -> ");
323 				strbuf_addstr(sb, color_reset);
324 				strbuf_addstr(sb, decorate_get_color(use_color, current_and_HEAD->type));
325 				show_name(sb, current_and_HEAD);
326 			}
327 			strbuf_addstr(sb, color_reset);
328 
329 			prefix = separator;
330 		}
331 		decoration = decoration->next;
332 	}
333 	strbuf_addstr(sb, color_commit);
334 	strbuf_addstr(sb, suffix);
335 	strbuf_addstr(sb, color_reset);
336 }
337 
show_decorations(struct rev_info * opt,struct commit * commit)338 void show_decorations(struct rev_info *opt, struct commit *commit)
339 {
340 	struct strbuf sb = STRBUF_INIT;
341 
342 	if (opt->sources) {
343 		char **slot = revision_sources_peek(opt->sources, commit);
344 
345 		if (slot && *slot)
346 			fprintf(opt->diffopt.file, "\t%s", *slot);
347 	}
348 	if (!opt->show_decorations)
349 		return;
350 	format_decorations(&sb, commit, opt->diffopt.use_color);
351 	fputs(sb.buf, opt->diffopt.file);
352 	strbuf_release(&sb);
353 }
354 
digits_in_number(unsigned int number)355 static unsigned int digits_in_number(unsigned int number)
356 {
357 	unsigned int i = 10, result = 1;
358 	while (i <= number) {
359 		i *= 10;
360 		result++;
361 	}
362 	return result;
363 }
364 
fmt_output_subject(struct strbuf * filename,const char * subject,struct rev_info * info)365 void fmt_output_subject(struct strbuf *filename,
366 			const char *subject,
367 			struct rev_info *info)
368 {
369 	const char *suffix = info->patch_suffix;
370 	int nr = info->nr;
371 	int start_len = filename->len;
372 	int max_len = start_len + info->patch_name_max - (strlen(suffix) + 1);
373 
374 	if (info->reroll_count) {
375 		struct strbuf temp = STRBUF_INIT;
376 
377 		strbuf_addf(&temp, "v%s", info->reroll_count);
378 		format_sanitized_subject(filename, temp.buf, temp.len);
379 		strbuf_addstr(filename, "-");
380 		strbuf_release(&temp);
381 	}
382 	strbuf_addf(filename, "%04d-%s", nr, subject);
383 
384 	if (max_len < filename->len)
385 		strbuf_setlen(filename, max_len);
386 	strbuf_addstr(filename, suffix);
387 }
388 
fmt_output_commit(struct strbuf * filename,struct commit * commit,struct rev_info * info)389 void fmt_output_commit(struct strbuf *filename,
390 		       struct commit *commit,
391 		       struct rev_info *info)
392 {
393 	struct pretty_print_context ctx = {0};
394 	struct strbuf subject = STRBUF_INIT;
395 
396 	format_commit_message(commit, "%f", &subject, &ctx);
397 	fmt_output_subject(filename, subject.buf, info);
398 	strbuf_release(&subject);
399 }
400 
fmt_output_email_subject(struct strbuf * sb,struct rev_info * opt)401 void fmt_output_email_subject(struct strbuf *sb, struct rev_info *opt)
402 {
403 	if (opt->total > 0) {
404 		strbuf_addf(sb, "Subject: [%s%s%0*d/%d] ",
405 			    opt->subject_prefix,
406 			    *opt->subject_prefix ? " " : "",
407 			    digits_in_number(opt->total),
408 			    opt->nr, opt->total);
409 	} else if (opt->total == 0 && opt->subject_prefix && *opt->subject_prefix) {
410 		strbuf_addf(sb, "Subject: [%s] ",
411 			    opt->subject_prefix);
412 	} else {
413 		strbuf_addstr(sb, "Subject: ");
414 	}
415 }
416 
log_write_email_headers(struct rev_info * opt,struct commit * commit,const char ** extra_headers_p,int * need_8bit_cte_p,int maybe_multipart)417 void log_write_email_headers(struct rev_info *opt, struct commit *commit,
418 			     const char **extra_headers_p,
419 			     int *need_8bit_cte_p,
420 			     int maybe_multipart)
421 {
422 	const char *extra_headers = opt->extra_headers;
423 	const char *name = oid_to_hex(opt->zero_commit ?
424 				      null_oid() : &commit->object.oid);
425 
426 	*need_8bit_cte_p = 0; /* unknown */
427 
428 	fprintf(opt->diffopt.file, "From %s Mon Sep 17 00:00:00 2001\n", name);
429 	graph_show_oneline(opt->graph);
430 	if (opt->message_id) {
431 		fprintf(opt->diffopt.file, "Message-Id: <%s>\n", opt->message_id);
432 		graph_show_oneline(opt->graph);
433 	}
434 	if (opt->ref_message_ids && opt->ref_message_ids->nr > 0) {
435 		int i, n;
436 		n = opt->ref_message_ids->nr;
437 		fprintf(opt->diffopt.file, "In-Reply-To: <%s>\n", opt->ref_message_ids->items[n-1].string);
438 		for (i = 0; i < n; i++)
439 			fprintf(opt->diffopt.file, "%s<%s>\n", (i > 0 ? "\t" : "References: "),
440 			       opt->ref_message_ids->items[i].string);
441 		graph_show_oneline(opt->graph);
442 	}
443 	if (opt->mime_boundary && maybe_multipart) {
444 		static struct strbuf subject_buffer = STRBUF_INIT;
445 		static struct strbuf buffer = STRBUF_INIT;
446 		struct strbuf filename =  STRBUF_INIT;
447 		*need_8bit_cte_p = -1; /* NEVER */
448 
449 		strbuf_reset(&subject_buffer);
450 		strbuf_reset(&buffer);
451 
452 		strbuf_addf(&subject_buffer,
453 			 "%s"
454 			 "MIME-Version: 1.0\n"
455 			 "Content-Type: multipart/mixed;"
456 			 " boundary=\"%s%s\"\n"
457 			 "\n"
458 			 "This is a multi-part message in MIME "
459 			 "format.\n"
460 			 "--%s%s\n"
461 			 "Content-Type: text/plain; "
462 			 "charset=UTF-8; format=fixed\n"
463 			 "Content-Transfer-Encoding: 8bit\n\n",
464 			 extra_headers ? extra_headers : "",
465 			 mime_boundary_leader, opt->mime_boundary,
466 			 mime_boundary_leader, opt->mime_boundary);
467 		extra_headers = subject_buffer.buf;
468 
469 		if (opt->numbered_files)
470 			strbuf_addf(&filename, "%d", opt->nr);
471 		else
472 			fmt_output_commit(&filename, commit, opt);
473 		strbuf_addf(&buffer,
474 			 "\n--%s%s\n"
475 			 "Content-Type: text/x-patch;"
476 			 " name=\"%s\"\n"
477 			 "Content-Transfer-Encoding: 8bit\n"
478 			 "Content-Disposition: %s;"
479 			 " filename=\"%s\"\n\n",
480 			 mime_boundary_leader, opt->mime_boundary,
481 			 filename.buf,
482 			 opt->no_inline ? "attachment" : "inline",
483 			 filename.buf);
484 		opt->diffopt.stat_sep = buffer.buf;
485 		strbuf_release(&filename);
486 	}
487 	*extra_headers_p = extra_headers;
488 }
489 
show_sig_lines(struct rev_info * opt,int status,const char * bol)490 static void show_sig_lines(struct rev_info *opt, int status, const char *bol)
491 {
492 	const char *color, *reset, *eol;
493 
494 	color = diff_get_color_opt(&opt->diffopt,
495 				   status ? DIFF_WHITESPACE : DIFF_FRAGINFO);
496 	reset = diff_get_color_opt(&opt->diffopt, DIFF_RESET);
497 	while (*bol) {
498 		eol = strchrnul(bol, '\n');
499 		fprintf(opt->diffopt.file, "%s%.*s%s%s", color, (int)(eol - bol), bol, reset,
500 		       *eol ? "\n" : "");
501 		graph_show_oneline(opt->graph);
502 		bol = (*eol) ? (eol + 1) : eol;
503 	}
504 }
505 
show_signature(struct rev_info * opt,struct commit * commit)506 static void show_signature(struct rev_info *opt, struct commit *commit)
507 {
508 	struct strbuf payload = STRBUF_INIT;
509 	struct strbuf signature = STRBUF_INIT;
510 	struct signature_check sigc = { 0 };
511 	int status;
512 
513 	if (parse_signed_commit(commit, &payload, &signature, the_hash_algo) <= 0)
514 		goto out;
515 
516 	status = check_signature(payload.buf, payload.len, signature.buf,
517 				 signature.len, &sigc);
518 	if (status && !sigc.output)
519 		show_sig_lines(opt, status, "No signature\n");
520 	else
521 		show_sig_lines(opt, status, sigc.output);
522 	signature_check_clear(&sigc);
523 
524  out:
525 	strbuf_release(&payload);
526 	strbuf_release(&signature);
527 }
528 
which_parent(const struct object_id * oid,const struct commit * commit)529 static int which_parent(const struct object_id *oid, const struct commit *commit)
530 {
531 	int nth;
532 	const struct commit_list *parent;
533 
534 	for (nth = 0, parent = commit->parents; parent; parent = parent->next) {
535 		if (oideq(&parent->item->object.oid, oid))
536 			return nth;
537 		nth++;
538 	}
539 	return -1;
540 }
541 
is_common_merge(const struct commit * commit)542 static int is_common_merge(const struct commit *commit)
543 {
544 	return (commit->parents
545 		&& commit->parents->next
546 		&& !commit->parents->next->next);
547 }
548 
show_one_mergetag(struct commit * commit,struct commit_extra_header * extra,void * data)549 static int show_one_mergetag(struct commit *commit,
550 			     struct commit_extra_header *extra,
551 			     void *data)
552 {
553 	struct rev_info *opt = (struct rev_info *)data;
554 	struct object_id oid;
555 	struct tag *tag;
556 	struct strbuf verify_message;
557 	struct signature_check sigc = { 0 };
558 	int status, nth;
559 	struct strbuf payload = STRBUF_INIT;
560 	struct strbuf signature = STRBUF_INIT;
561 
562 	hash_object_file(the_hash_algo, extra->value, extra->len,
563 			 type_name(OBJ_TAG), &oid);
564 	tag = lookup_tag(the_repository, &oid);
565 	if (!tag)
566 		return -1; /* error message already given */
567 
568 	strbuf_init(&verify_message, 256);
569 	if (parse_tag_buffer(the_repository, tag, extra->value, extra->len))
570 		strbuf_addstr(&verify_message, "malformed mergetag\n");
571 	else if (is_common_merge(commit) &&
572 		 oideq(&tag->tagged->oid,
573 		       &commit->parents->next->item->object.oid))
574 		strbuf_addf(&verify_message,
575 			    "merged tag '%s'\n", tag->tag);
576 	else if ((nth = which_parent(&tag->tagged->oid, commit)) < 0)
577 		strbuf_addf(&verify_message, "tag %s names a non-parent %s\n",
578 				    tag->tag, oid_to_hex(&tag->tagged->oid));
579 	else
580 		strbuf_addf(&verify_message,
581 			    "parent #%d, tagged '%s'\n", nth + 1, tag->tag);
582 
583 	status = -1;
584 	if (parse_signature(extra->value, extra->len, &payload, &signature)) {
585 		/* could have a good signature */
586 		status = check_signature(payload.buf, payload.len,
587 					 signature.buf, signature.len, &sigc);
588 		if (sigc.output)
589 			strbuf_addstr(&verify_message, sigc.output);
590 		else
591 			strbuf_addstr(&verify_message, "No signature\n");
592 		signature_check_clear(&sigc);
593 		/* otherwise we couldn't verify, which is shown as bad */
594 	}
595 
596 	show_sig_lines(opt, status, verify_message.buf);
597 	strbuf_release(&verify_message);
598 	strbuf_release(&payload);
599 	strbuf_release(&signature);
600 	return 0;
601 }
602 
show_mergetag(struct rev_info * opt,struct commit * commit)603 static int show_mergetag(struct rev_info *opt, struct commit *commit)
604 {
605 	return for_each_mergetag(show_one_mergetag, commit, opt);
606 }
607 
next_commentary_block(struct rev_info * opt,struct strbuf * sb)608 static void next_commentary_block(struct rev_info *opt, struct strbuf *sb)
609 {
610 	const char *x = opt->shown_dashes ? "\n" : "---\n";
611 	if (sb)
612 		strbuf_addstr(sb, x);
613 	else
614 		fputs(x, opt->diffopt.file);
615 	opt->shown_dashes = 1;
616 }
617 
show_log(struct rev_info * opt)618 void show_log(struct rev_info *opt)
619 {
620 	struct strbuf msgbuf = STRBUF_INIT;
621 	struct log_info *log = opt->loginfo;
622 	struct commit *commit = log->commit, *parent = log->parent;
623 	int abbrev_commit = opt->abbrev_commit ? opt->abbrev : the_hash_algo->hexsz;
624 	const char *extra_headers = opt->extra_headers;
625 	struct pretty_print_context ctx = {0};
626 
627 	opt->loginfo = NULL;
628 	if (!opt->verbose_header) {
629 		graph_show_commit(opt->graph);
630 
631 		if (!opt->graph)
632 			put_revision_mark(opt, commit);
633 		fputs(find_unique_abbrev(&commit->object.oid, abbrev_commit), opt->diffopt.file);
634 		if (opt->print_parents)
635 			show_parents(commit, abbrev_commit, opt->diffopt.file);
636 		if (opt->children.name)
637 			show_children(opt, commit, abbrev_commit);
638 		show_decorations(opt, commit);
639 		if (opt->graph && !graph_is_commit_finished(opt->graph)) {
640 			putc('\n', opt->diffopt.file);
641 			graph_show_remainder(opt->graph);
642 		}
643 		putc(opt->diffopt.line_termination, opt->diffopt.file);
644 		return;
645 	}
646 
647 	/*
648 	 * If use_terminator is set, we already handled any record termination
649 	 * at the end of the last record.
650 	 * Otherwise, add a diffopt.line_termination character before all
651 	 * entries but the first.  (IOW, as a separator between entries)
652 	 */
653 	if (opt->shown_one && !opt->use_terminator) {
654 		/*
655 		 * If entries are separated by a newline, the output
656 		 * should look human-readable.  If the last entry ended
657 		 * with a newline, print the graph output before this
658 		 * newline.  Otherwise it will end up as a completely blank
659 		 * line and will look like a gap in the graph.
660 		 *
661 		 * If the entry separator is not a newline, the output is
662 		 * primarily intended for programmatic consumption, and we
663 		 * never want the extra graph output before the entry
664 		 * separator.
665 		 */
666 		if (opt->diffopt.line_termination == '\n' &&
667 		    !opt->missing_newline)
668 			graph_show_padding(opt->graph);
669 		putc(opt->diffopt.line_termination, opt->diffopt.file);
670 	}
671 	opt->shown_one = 1;
672 
673 	/*
674 	 * If the history graph was requested,
675 	 * print the graph, up to this commit's line
676 	 */
677 	graph_show_commit(opt->graph);
678 
679 	/*
680 	 * Print header line of header..
681 	 */
682 
683 	if (cmit_fmt_is_mail(opt->commit_format)) {
684 		log_write_email_headers(opt, commit, &extra_headers,
685 					&ctx.need_8bit_cte, 1);
686 		ctx.rev = opt;
687 		ctx.print_email_subject = 1;
688 	} else if (opt->commit_format != CMIT_FMT_USERFORMAT) {
689 		fputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), opt->diffopt.file);
690 		if (opt->commit_format != CMIT_FMT_ONELINE)
691 			fputs("commit ", opt->diffopt.file);
692 
693 		if (!opt->graph)
694 			put_revision_mark(opt, commit);
695 		fputs(find_unique_abbrev(&commit->object.oid,
696 					 abbrev_commit),
697 		      opt->diffopt.file);
698 		if (opt->print_parents)
699 			show_parents(commit, abbrev_commit, opt->diffopt.file);
700 		if (opt->children.name)
701 			show_children(opt, commit, abbrev_commit);
702 		if (parent)
703 			fprintf(opt->diffopt.file, " (from %s)",
704 			       find_unique_abbrev(&parent->object.oid, abbrev_commit));
705 		fputs(diff_get_color_opt(&opt->diffopt, DIFF_RESET), opt->diffopt.file);
706 		show_decorations(opt, commit);
707 		if (opt->commit_format == CMIT_FMT_ONELINE) {
708 			putc(' ', opt->diffopt.file);
709 		} else {
710 			putc('\n', opt->diffopt.file);
711 			graph_show_oneline(opt->graph);
712 		}
713 		if (opt->reflog_info) {
714 			/*
715 			 * setup_revisions() ensures that opt->reflog_info
716 			 * and opt->graph cannot both be set,
717 			 * so we don't need to worry about printing the
718 			 * graph info here.
719 			 */
720 			show_reflog_message(opt->reflog_info,
721 					    opt->commit_format == CMIT_FMT_ONELINE,
722 					    &opt->date_mode,
723 					    opt->date_mode_explicit);
724 			if (opt->commit_format == CMIT_FMT_ONELINE)
725 				return;
726 		}
727 	}
728 
729 	if (opt->show_signature) {
730 		show_signature(opt, commit);
731 		show_mergetag(opt, commit);
732 	}
733 
734 	if (opt->show_notes) {
735 		int raw;
736 		struct strbuf notebuf = STRBUF_INIT;
737 
738 		raw = (opt->commit_format == CMIT_FMT_USERFORMAT);
739 		format_display_notes(&commit->object.oid, &notebuf,
740 				     get_log_output_encoding(), raw);
741 		ctx.notes_message = strbuf_detach(&notebuf, NULL);
742 	}
743 
744 	/*
745 	 * And then the pretty-printed message itself
746 	 */
747 	if (ctx.need_8bit_cte >= 0 && opt->add_signoff)
748 		ctx.need_8bit_cte =
749 			has_non_ascii(fmt_name(WANT_COMMITTER_IDENT));
750 	ctx.date_mode = opt->date_mode;
751 	ctx.date_mode_explicit = opt->date_mode_explicit;
752 	ctx.abbrev = opt->diffopt.abbrev;
753 	ctx.after_subject = extra_headers;
754 	ctx.preserve_subject = opt->preserve_subject;
755 	ctx.encode_email_headers = opt->encode_email_headers;
756 	ctx.reflog_info = opt->reflog_info;
757 	ctx.fmt = opt->commit_format;
758 	ctx.mailmap = opt->mailmap;
759 	ctx.color = opt->diffopt.use_color;
760 	ctx.expand_tabs_in_log = opt->expand_tabs_in_log;
761 	ctx.output_encoding = get_log_output_encoding();
762 	ctx.rev = opt;
763 	if (opt->from_ident.mail_begin && opt->from_ident.name_begin)
764 		ctx.from_ident = &opt->from_ident;
765 	if (opt->graph)
766 		ctx.graph_width = graph_width(opt->graph);
767 	pretty_print_commit(&ctx, commit, &msgbuf);
768 
769 	if (opt->add_signoff)
770 		append_signoff(&msgbuf, 0, APPEND_SIGNOFF_DEDUP);
771 
772 	if ((ctx.fmt != CMIT_FMT_USERFORMAT) &&
773 	    ctx.notes_message && *ctx.notes_message) {
774 		if (cmit_fmt_is_mail(ctx.fmt))
775 			next_commentary_block(opt, &msgbuf);
776 		strbuf_addstr(&msgbuf, ctx.notes_message);
777 	}
778 
779 	if (opt->show_log_size) {
780 		fprintf(opt->diffopt.file, "log size %i\n", (int)msgbuf.len);
781 		graph_show_oneline(opt->graph);
782 	}
783 
784 	/*
785 	 * Set opt->missing_newline if msgbuf doesn't
786 	 * end in a newline (including if it is empty)
787 	 */
788 	if (!msgbuf.len || msgbuf.buf[msgbuf.len - 1] != '\n')
789 		opt->missing_newline = 1;
790 	else
791 		opt->missing_newline = 0;
792 
793 	graph_show_commit_msg(opt->graph, opt->diffopt.file, &msgbuf);
794 	if (opt->use_terminator && !commit_format_is_empty(opt->commit_format)) {
795 		if (!opt->missing_newline)
796 			graph_show_padding(opt->graph);
797 		putc(opt->diffopt.line_termination, opt->diffopt.file);
798 	}
799 
800 	strbuf_release(&msgbuf);
801 	free(ctx.notes_message);
802 
803 	if (cmit_fmt_is_mail(ctx.fmt) && opt->idiff_oid1) {
804 		struct diff_queue_struct dq;
805 
806 		memcpy(&dq, &diff_queued_diff, sizeof(diff_queued_diff));
807 		DIFF_QUEUE_CLEAR(&diff_queued_diff);
808 
809 		next_commentary_block(opt, NULL);
810 		fprintf_ln(opt->diffopt.file, "%s", opt->idiff_title);
811 		show_interdiff(opt->idiff_oid1, opt->idiff_oid2, 2,
812 			       &opt->diffopt);
813 
814 		memcpy(&diff_queued_diff, &dq, sizeof(diff_queued_diff));
815 	}
816 
817 	if (cmit_fmt_is_mail(ctx.fmt) && opt->rdiff1) {
818 		struct diff_queue_struct dq;
819 		struct diff_options opts;
820 		struct range_diff_options range_diff_opts = {
821 			.creation_factor = opt->creation_factor,
822 			.dual_color = 1,
823 			.diffopt = &opts
824 		};
825 
826 		memcpy(&dq, &diff_queued_diff, sizeof(diff_queued_diff));
827 		DIFF_QUEUE_CLEAR(&diff_queued_diff);
828 
829 		next_commentary_block(opt, NULL);
830 		fprintf_ln(opt->diffopt.file, "%s", opt->rdiff_title);
831 		/*
832 		 * Pass minimum required diff-options to range-diff; others
833 		 * can be added later if deemed desirable.
834 		 */
835 		diff_setup(&opts);
836 		opts.file = opt->diffopt.file;
837 		opts.use_color = opt->diffopt.use_color;
838 		diff_setup_done(&opts);
839 		show_range_diff(opt->rdiff1, opt->rdiff2, &range_diff_opts);
840 
841 		memcpy(&diff_queued_diff, &dq, sizeof(diff_queued_diff));
842 	}
843 }
844 
log_tree_diff_flush(struct rev_info * opt)845 int log_tree_diff_flush(struct rev_info *opt)
846 {
847 	opt->shown_dashes = 0;
848 	diffcore_std(&opt->diffopt);
849 
850 	if (diff_queue_is_empty()) {
851 		int saved_fmt = opt->diffopt.output_format;
852 		opt->diffopt.output_format = DIFF_FORMAT_NO_OUTPUT;
853 		diff_flush(&opt->diffopt);
854 		opt->diffopt.output_format = saved_fmt;
855 		return 0;
856 	}
857 
858 	if (opt->loginfo && !opt->no_commit_id) {
859 		show_log(opt);
860 		if ((opt->diffopt.output_format & ~DIFF_FORMAT_NO_OUTPUT) &&
861 		    opt->verbose_header &&
862 		    opt->commit_format != CMIT_FMT_ONELINE &&
863 		    !commit_format_is_empty(opt->commit_format)) {
864 			/*
865 			 * When showing a verbose header (i.e. log message),
866 			 * and not in --pretty=oneline format, we would want
867 			 * an extra newline between the end of log and the
868 			 * diff/diffstat output for readability.
869 			 */
870 			int pch = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_PATCH;
871 			if (opt->diffopt.output_prefix) {
872 				struct strbuf *msg = NULL;
873 				msg = opt->diffopt.output_prefix(&opt->diffopt,
874 					opt->diffopt.output_prefix_data);
875 				fwrite(msg->buf, msg->len, 1, opt->diffopt.file);
876 			}
877 
878 			/*
879 			 * We may have shown three-dashes line early
880 			 * between generated commentary (notes, etc.)
881 			 * and the log message, in which case we only
882 			 * want a blank line after the commentary
883 			 * without (an extra) three-dashes line.
884 			 * Otherwise, we show the three-dashes line if
885 			 * we are showing the patch with diffstat, but
886 			 * in that case, there is no extra blank line
887 			 * after the three-dashes line.
888 			 */
889 			if (!opt->shown_dashes &&
890 			    (pch & opt->diffopt.output_format) == pch)
891 				fprintf(opt->diffopt.file, "---");
892 			putc('\n', opt->diffopt.file);
893 		}
894 	}
895 	diff_flush(&opt->diffopt);
896 	return 1;
897 }
898 
do_diff_combined(struct rev_info * opt,struct commit * commit)899 static int do_diff_combined(struct rev_info *opt, struct commit *commit)
900 {
901 	diff_tree_combined_merge(commit, opt);
902 	return !opt->loginfo;
903 }
904 
905 /*
906  * Show the diff of a commit.
907  *
908  * Return true if we printed any log info messages
909  */
log_tree_diff(struct rev_info * opt,struct commit * commit,struct log_info * log)910 static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log_info *log)
911 {
912 	int showed_log;
913 	struct commit_list *parents;
914 	struct object_id *oid;
915 	int is_merge;
916 	int all_need_diff = opt->diff || opt->diffopt.flags.exit_with_status;
917 
918 	if (!all_need_diff && !opt->merges_need_diff)
919 		return 0;
920 
921 	parse_commit_or_die(commit);
922 	oid = get_commit_tree_oid(commit);
923 
924 	parents = get_saved_parents(opt, commit);
925 	is_merge = parents && parents->next;
926 	if (!is_merge && !all_need_diff)
927 		return 0;
928 
929 	/* Root commit? */
930 	if (!parents) {
931 		if (opt->show_root_diff) {
932 			diff_root_tree_oid(oid, "", &opt->diffopt);
933 			log_tree_diff_flush(opt);
934 		}
935 		return !opt->loginfo;
936 	}
937 
938 	if (is_merge) {
939 		if (opt->combine_merges)
940 			return do_diff_combined(opt, commit);
941 		if (opt->separate_merges) {
942 			if (!opt->first_parent_merges) {
943 				/* Show parent info for multiple diffs */
944 				log->parent = parents->item;
945 			}
946 		} else
947 			return 0;
948 	}
949 
950 	showed_log = 0;
951 	for (;;) {
952 		struct commit *parent = parents->item;
953 
954 		parse_commit_or_die(parent);
955 		diff_tree_oid(get_commit_tree_oid(parent),
956 			      oid, "", &opt->diffopt);
957 		log_tree_diff_flush(opt);
958 
959 		showed_log |= !opt->loginfo;
960 
961 		/* Set up the log info for the next parent, if any.. */
962 		parents = parents->next;
963 		if (!parents || opt->first_parent_merges)
964 			break;
965 		log->parent = parents->item;
966 		opt->loginfo = log;
967 	}
968 	return showed_log;
969 }
970 
log_tree_commit(struct rev_info * opt,struct commit * commit)971 int log_tree_commit(struct rev_info *opt, struct commit *commit)
972 {
973 	struct log_info log;
974 	int shown;
975 	/* maybe called by e.g. cmd_log_walk(), maybe stand-alone */
976 	int no_free = opt->diffopt.no_free;
977 
978 	log.commit = commit;
979 	log.parent = NULL;
980 	opt->loginfo = &log;
981 	opt->diffopt.no_free = 1;
982 
983 	if (opt->line_level_traverse)
984 		return line_log_print(opt, commit);
985 
986 	if (opt->track_linear && !opt->linear && !opt->reverse_output_stage)
987 		fprintf(opt->diffopt.file, "\n%s\n", opt->break_bar);
988 	shown = log_tree_diff(opt, commit, &log);
989 	if (!shown && opt->loginfo && opt->always_show_header) {
990 		log.parent = NULL;
991 		show_log(opt);
992 		shown = 1;
993 	}
994 	if (opt->track_linear && !opt->linear && opt->reverse_output_stage)
995 		fprintf(opt->diffopt.file, "\n%s\n", opt->break_bar);
996 	opt->loginfo = NULL;
997 	maybe_flush_or_die(opt->diffopt.file, "stdout");
998 	opt->diffopt.no_free = no_free;
999 	diff_free(&opt->diffopt);
1000 	return shown;
1001 }
1002