#include "commit.h"
#include "log-tree.h"
+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));
+ }
+}
+
+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;
+}
+
+void show_log(struct rev_info *opt, struct log_info *log, const char *sep)
+{
+ static char this_header[16384];
+ struct commit *commit = log->commit, *parent = log->parent;
+ int abbrev = opt->diffopt.abbrev;
+ int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40;
+ const char *extra;
+ int len;
+ const char *subject = NULL, *extra_headers = opt->extra_headers;
+
+ opt->loginfo = NULL;
+ if (!opt->verbose_header) {
+ fputs(diff_unique_abbrev(commit->object.sha1, abbrev_commit), stdout);
+ if (opt->parents)
+ show_parents(commit, abbrev_commit);
+ putchar('\n');
+ return;
+ }
+
+ /*
+ * 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.
+ */
+ extra = "";
+ if (*sep != '\n' && opt->commit_format == CMIT_FMT_ONELINE)
+ extra = "\n";
+ if (opt->shown_one && opt->commit_format != CMIT_FMT_ONELINE)
+ putchar('\n');
+ opt->shown_one = 1;
+
+ /*
+ * Print header line of header..
+ */
+
+ if (opt->commit_format == CMIT_FMT_EMAIL) {
+ char *sha1 = sha1_to_hex(commit->object.sha1);
+ if (opt->total > 0) {
+ static char buffer[64];
+ snprintf(buffer, sizeof(buffer),
+ "Subject: [PATCH %d/%d] ",
+ opt->nr, opt->total);
+ subject = buffer;
+ } else if (opt->total == 0)
+ subject = "Subject: [PATCH] ";
+ else
+ subject = "Subject: ";
+
+ printf("From %s Mon Sep 17 00:00:00 2001\n", sha1);
+ if (opt->mime_boundary) {
+ static char subject_buffer[1024];
+ static char buffer[1024];
+ snprintf(subject_buffer, sizeof(subject_buffer) - 1,
+ "%s"
+ "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",
+ extra_headers ? extra_headers : "",
+ mime_boundary_leader, opt->mime_boundary,
+ mime_boundary_leader, opt->mime_boundary);
+ extra_headers = subject_buffer;
+
+ 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;
+ }
+ } else {
+ printf("%s%s",
+ opt->commit_format == CMIT_FMT_ONELINE ? "" : "commit ",
+ diff_unique_abbrev(commit->object.sha1, abbrev_commit));
+ if (opt->parents)
+ show_parents(commit, abbrev_commit);
+ if (parent)
+ printf(" (from %s)",
+ diff_unique_abbrev(parent->object.sha1,
+ abbrev_commit));
+ putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n');
+ }
+
+ /*
+ * And then the pretty-printed message itself
+ */
+ len = pretty_print_commit(opt->commit_format, commit, ~0u, this_header, sizeof(this_header), abbrev, subject, extra_headers);
+
+ if (opt->add_signoff)
+ len = append_signoff(this_header, sizeof(this_header), len,
+ opt->add_signoff);
+ printf("%s%s%s", this_header, extra, sep);
+}
+
int log_tree_diff_flush(struct rev_info *opt)
{
diffcore_std(&opt->diffopt);
+
if (diff_queue_is_empty()) {
int saved_fmt = opt->diffopt.output_format;
opt->diffopt.output_format = DIFF_FORMAT_NO_OUTPUT;
opt->diffopt.output_format = saved_fmt;
return 0;
}
- if (opt->header) {
- if (!opt->no_commit_id)
- printf("%s%c", opt->header,
- opt->diffopt.line_termination);
- opt->header = NULL;
- }
+
+ if (opt->loginfo && !opt->no_commit_id)
+ show_log(opt, opt->loginfo, opt->diffopt.with_stat ? "---\n" : "\n");
diff_flush(&opt->diffopt);
return 1;
}
return retval;
}
-static const char *generate_header(struct rev_info *opt,
- const unsigned char *commit_sha1,
- const unsigned char *parent_sha1,
- const struct commit *commit)
-{
- static char this_header[16384];
- int offset;
- unsigned long len;
- int abbrev = opt->diffopt.abbrev;
- const char *msg = commit->buffer;
-
- if (opt->use_precomputed_header)
- return opt->use_precomputed_header;
-
- if (!opt->verbose_header)
- return sha1_to_hex(commit_sha1);
-
- len = strlen(msg);
-
- offset = sprintf(this_header, "%s%s ",
- opt->header_prefix,
- diff_unique_abbrev(commit_sha1, abbrev));
- if (commit_sha1 != parent_sha1)
- offset += sprintf(this_header + offset, "(from %s)\n",
- parent_sha1
- ? diff_unique_abbrev(parent_sha1, abbrev)
- : "root");
- else
- offset += sprintf(this_header + offset, "(from parents)\n");
- offset += pretty_print_commit(opt->commit_format, commit, len,
- this_header + offset,
- sizeof(this_header) - offset, abbrev);
- if (opt->always_show_header) {
- puts(this_header);
- return NULL;
- }
- return this_header;
-}
-
static int do_diff_combined(struct rev_info *opt, struct commit *commit)
{
unsigned const char *sha1 = commit->object.sha1;
- opt->header = generate_header(opt, sha1, sha1, commit);
- opt->header = diff_tree_combined_merge(sha1, opt->header,
- opt->dense_combined_merges,
- &opt->diffopt);
- if (!opt->header && opt->verbose_header)
- opt->header_prefix = "\ndiff-tree ";
- return 0;
+ diff_tree_combined_merge(sha1, opt->dense_combined_merges, opt);
+ return !opt->loginfo;
}
-int log_tree_commit(struct rev_info *opt, struct commit *commit)
+/*
+ * 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)
{
+ int showed_log;
struct commit_list *parents;
unsigned const char *sha1 = commit->object.sha1;
+ if (!opt->diff)
+ return 0;
+
/* Root commit? */
- if (opt->show_root_diff && !commit->parents) {
- opt->header = generate_header(opt, sha1, NULL, commit);
- diff_root_tree(opt, sha1, "");
+ parents = commit->parents;
+ if (!parents) {
+ if (opt->show_root_diff)
+ diff_root_tree(opt, sha1, "");
+ return !opt->loginfo;
}
/* More than one parent? */
- if (commit->parents && commit->parents->next) {
+ if (parents && parents->next) {
if (opt->ignore_merges)
return 0;
else if (opt->combine_merges)
return do_diff_combined(opt, commit);
+
+ /* If we show individual diffs, show the parent info */
+ log->parent = parents->item;
}
- for (parents = commit->parents; parents; parents = parents->next) {
+ showed_log = 0;
+ for (;;) {
struct commit *parent = parents->item;
- unsigned const char *psha1 = parent->object.sha1;
- opt->header = generate_header(opt, sha1, psha1, commit);
- diff_tree_sha1(psha1, sha1, "", &opt->diffopt);
- log_tree_diff_flush(opt);
- if (!opt->header && opt->verbose_header)
- opt->header_prefix = "\ndiff-tree ";
+ 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;
+ int shown;
+
+ log.commit = commit;
+ log.parent = NULL;
+ opt->loginfo = &log;
+
+ shown = log_tree_diff(opt, commit, &log);
+ if (!shown && opt->loginfo && opt->always_show_header) {
+ log.parent = NULL;
+ show_log(opt, opt->loginfo, "");
+ shown = 1;
}
- return 0;
+ opt->loginfo = NULL;
+ return shown;
}