log-tree.c 7.7 KB
Newer Older
1 2 3 4 5
#include "cache.h"
#include "diff.h"
#include "commit.h"
#include "log-tree.h"

6 7 8 9 10 11 12 13 14
static void show_parents(struct commit *commit, int abbrev)
{
	struct commit_list *p;
	for (p = commit->parents; p ; p = p->next) {
		struct commit *parent = p->item;
		printf(" %s", diff_unique_abbrev(parent->object.sha1, abbrev));
	}
}

J
Junio C Hamano 已提交
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
static int append_signoff(char *buf, int buf_sz, int at, const char *signoff)
{
	int signoff_len = strlen(signoff);
	static const char signed_off_by[] = "Signed-off-by: ";
	char *cp = buf;

	/* Do we have enough space to add it? */
	if (buf_sz - at <= strlen(signed_off_by) + signoff_len + 2)
		return at;

	/* First see if we already have the sign-off by the signer */
	while (1) {
		cp = strstr(cp, signed_off_by);
		if (!cp)
			break;
		cp += strlen(signed_off_by);
		if ((cp + signoff_len < buf + at) &&
		    !strncmp(cp, signoff, signoff_len) &&
		    isspace(cp[signoff_len]))
			return at; /* we already have him */
	}

	strcpy(buf + at, signed_off_by);
	at += strlen(signed_off_by);
	strcpy(buf + at, signoff);
	at += signoff_len;
	buf[at++] = '\n';
	buf[at] = 0;
	return at;
}

T
Timo Hirvonen 已提交
46
void show_log(struct rev_info *opt, const char *sep)
L
Linus Torvalds 已提交
47 48
{
	static char this_header[16384];
T
Timo Hirvonen 已提交
49
	struct log_info *log = opt->loginfo;
L
Linus Torvalds 已提交
50 51 52
	struct commit *commit = log->commit, *parent = log->parent;
	int abbrev = opt->diffopt.abbrev;
	int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40;
53
	const char *extra;
L
Linus Torvalds 已提交
54
	int len;
55
	const char *subject = NULL, *extra_headers = opt->extra_headers;
L
Linus Torvalds 已提交
56 57 58

	opt->loginfo = NULL;
	if (!opt->verbose_header) {
59 60 61
		fputs(diff_unique_abbrev(commit->object.sha1, abbrev_commit), stdout);
		if (opt->parents)
			show_parents(commit, abbrev_commit);
62
		putchar(opt->diffopt.line_termination);
L
Linus Torvalds 已提交
63 64 65 66
		return;
	}

	/*
67 68 69 70 71 72 73
	 * The "oneline" format has several special cases:
	 *  - The pretty-printed commit lacks a newline at the end
	 *    of the buffer, but we do want to make sure that we
	 *    have a newline there. If the separator isn't already
	 *    a newline, add an extra one.
	 *  - unlike other log messages, the one-line format does
	 *    not have an empty line between entries.
L
Linus Torvalds 已提交
74
	 */
75 76 77
	extra = "";
	if (*sep != '\n' && opt->commit_format == CMIT_FMT_ONELINE)
		extra = "\n";
L
Linus Torvalds 已提交
78 79 80 81 82 83 84
	if (opt->shown_one && opt->commit_format != CMIT_FMT_ONELINE)
		putchar('\n');
	opt->shown_one = 1;

	/*
	 * Print header line of header..
	 */
85

86
	if (opt->commit_format == CMIT_FMT_EMAIL) {
87
		char *sha1 = sha1_to_hex(commit->object.sha1);
88 89 90 91 92 93
		if (opt->total > 0) {
			static char buffer[64];
			snprintf(buffer, sizeof(buffer),
					"Subject: [PATCH %d/%d] ",
					opt->nr, opt->total);
			subject = buffer;
94
		} else if (opt->total == 0)
95
			subject = "Subject: [PATCH] ";
96 97 98
		else
			subject = "Subject: ";

99
		printf("From %s Mon Sep 17 00:00:00 2001\n", sha1);
100 101 102 103 104
		if (opt->message_id)
			printf("Message-Id: <%s>\n", opt->message_id);
		if (opt->ref_message_id)
			printf("In-Reply-To: <%s>\nReferences: <%s>\n",
			       opt->ref_message_id, opt->ref_message_id);
105 106 107 108
		if (opt->mime_boundary) {
			static char subject_buffer[1024];
			static char buffer[1024];
			snprintf(subject_buffer, sizeof(subject_buffer) - 1,
109
				 "%s"
110 111 112 113 114 115 116 117 118 119
				 "MIME-Version: 1.0\n"
				 "Content-Type: multipart/mixed;\n"
				 " boundary=\"%s%s\"\n"
				 "\n"
				 "This is a multi-part message in MIME "
				 "format.\n"
				 "--%s%s\n"
				 "Content-Type: text/plain; "
				 "charset=UTF-8; format=fixed\n"
				 "Content-Transfer-Encoding: 8bit\n\n",
120
				 extra_headers ? extra_headers : "",
121 122
				 mime_boundary_leader, opt->mime_boundary,
				 mime_boundary_leader, opt->mime_boundary);
123
			extra_headers = subject_buffer;
124 125 126 127 128 129 130 131 132 133 134 135

			snprintf(buffer, sizeof(buffer) - 1,
				 "--%s%s\n"
				 "Content-Type: text/x-patch;\n"
				 " name=\"%s.diff\"\n"
				 "Content-Transfer-Encoding: 8bit\n"
				 "Content-Disposition: inline;\n"
				 " filename=\"%s.diff\"\n\n",
				 mime_boundary_leader, opt->mime_boundary,
				 sha1, sha1);
			opt->diffopt.stat_sep = buffer;
		}
136
	} else {
J
Jeff King 已提交
137 138
		printf("%s%s%s",
		       diff_get_color(opt->diffopt.color_diff, DIFF_COMMIT),
139 140
		       opt->commit_format == CMIT_FMT_ONELINE ? "" : "commit ",
		       diff_unique_abbrev(commit->object.sha1, abbrev_commit));
141 142
		if (opt->parents)
			show_parents(commit, abbrev_commit);
J
Junio C Hamano 已提交
143
		if (parent)
144 145 146
			printf(" (from %s)",
			       diff_unique_abbrev(parent->object.sha1,
						  abbrev_commit));
J
Jeff King 已提交
147 148
		printf("%s",
		       diff_get_color(opt->diffopt.color_diff, DIFF_RESET));
149
		putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n');
150
	}
L
Linus Torvalds 已提交
151 152 153 154

	/*
	 * And then the pretty-printed message itself
	 */
155
	len = pretty_print_commit(opt->commit_format, commit, ~0u, this_header, sizeof(this_header), abbrev, subject, extra_headers);
J
Junio C Hamano 已提交
156 157 158 159

	if (opt->add_signoff)
		len = append_signoff(this_header, sizeof(this_header), len,
				     opt->add_signoff);
160
	printf("%s%s%s", this_header, extra, sep);
L
Linus Torvalds 已提交
161 162
}

163
int log_tree_diff_flush(struct rev_info *opt)
164 165
{
	diffcore_std(&opt->diffopt);
L
Linus Torvalds 已提交
166

167 168 169 170 171 172 173
	if (diff_queue_is_empty()) {
		int saved_fmt = opt->diffopt.output_format;
		opt->diffopt.output_format = DIFF_FORMAT_NO_OUTPUT;
		diff_flush(&opt->diffopt);
		opt->diffopt.output_format = saved_fmt;
		return 0;
	}
L
Linus Torvalds 已提交
174

175 176 177 178 179 180
	if (opt->loginfo && !opt->no_commit_id) {
		/* When showing a verbose header (i.e. log message),
		 * and not in --pretty=oneline format, we would want
		 * an extra newline between the end of log and the
		 * output for readability.
		 */
T
Timo Hirvonen 已提交
181
		show_log(opt, opt->diffopt.msg_sep);
182 183 184 185 186 187 188 189 190
		if (opt->verbose_header &&
		    opt->commit_format != CMIT_FMT_ONELINE) {
			int pch = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_PATCH;
			if ((pch & opt->diffopt.output_format) == pch)
				printf("---%c", opt->diffopt.line_termination);
			else
				putchar(opt->diffopt.line_termination);
		}
	}
191 192 193 194
	diff_flush(&opt->diffopt);
	return 1;
}

195
static int diff_root_tree(struct rev_info *opt,
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
			  const unsigned char *new, const char *base)
{
	int retval;
	void *tree;
	struct tree_desc empty, real;

	tree = read_object_with_reference(new, tree_type, &real.size, NULL);
	if (!tree)
		die("unable to read root tree (%s)", sha1_to_hex(new));
	real.buf = tree;

	empty.buf = "";
	empty.size = 0;
	retval = diff_tree(&empty, &real, base, &opt->diffopt);
	free(tree);
	log_tree_diff_flush(opt);
	return retval;
}

215
static int do_diff_combined(struct rev_info *opt, struct commit *commit)
216 217 218
{
	unsigned const char *sha1 = commit->object.sha1;

L
Linus Torvalds 已提交
219 220
	diff_tree_combined_merge(sha1, opt->dense_combined_merges, opt);
	return !opt->loginfo;
221 222
}

L
Linus Torvalds 已提交
223 224 225 226 227 228
/*
 * Show the diff of a commit.
 *
 * Return true if we printed any log info messages
 */
static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log_info *log)
229
{
L
Linus Torvalds 已提交
230
	int showed_log;
231 232 233
	struct commit_list *parents;
	unsigned const char *sha1 = commit->object.sha1;

L
Linus Torvalds 已提交
234 235 236
	if (!opt->diff)
		return 0;

237
	/* Root commit? */
L
Linus Torvalds 已提交
238 239 240 241 242
	parents = commit->parents;
	if (!parents) {
		if (opt->show_root_diff)
			diff_root_tree(opt, sha1, "");
		return !opt->loginfo;
243 244 245
	}

	/* More than one parent? */
L
Linus Torvalds 已提交
246
	if (parents && parents->next) {
247 248 249 250
		if (opt->ignore_merges)
			return 0;
		else if (opt->combine_merges)
			return do_diff_combined(opt, commit);
L
Linus Torvalds 已提交
251 252 253

		/* If we show individual diffs, show the parent info */
		log->parent = parents->item;
254 255
	}

L
Linus Torvalds 已提交
256 257
	showed_log = 0;
	for (;;) {
258 259
		struct commit *parent = parents->item;

L
Linus Torvalds 已提交
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
		diff_tree_sha1(parent->object.sha1, sha1, "", &opt->diffopt);
		log_tree_diff_flush(opt);

		showed_log |= !opt->loginfo;

		/* Set up the log info for the next parent, if any.. */
		parents = parents->next;
		if (!parents)
			break;
		log->parent = parents->item;
		opt->loginfo = log;
	}
	return showed_log;
}

int log_tree_commit(struct rev_info *opt, struct commit *commit)
{
	struct log_info log;
278
	int shown;
L
Linus Torvalds 已提交
279 280 281 282 283

	log.commit = commit;
	log.parent = NULL;
	opt->loginfo = &log;

284 285
	shown = log_tree_diff(opt, commit, &log);
	if (!shown && opt->loginfo && opt->always_show_header) {
L
Linus Torvalds 已提交
286
		log.parent = NULL;
T
Timo Hirvonen 已提交
287
		show_log(opt, "");
288
		shown = 1;
289
	}
L
Linus Torvalds 已提交
290
	opt->loginfo = NULL;
291
	return shown;
292
}