Junio C Hamano | 5f1c3f0 | 2006-04-09 01:11:11 -0700 | [diff] [blame] | 1 | #include "cache.h" |
| 2 | #include "diff.h" |
| 3 | #include "commit.h" |
| 4 | #include "log-tree.h" |
| 5 | |
Linus Torvalds | c8c893c | 2006-05-03 07:59:00 -0700 | [diff] [blame] | 6 | static void show_parents(struct commit *commit, int abbrev) |
| 7 | { |
| 8 | struct commit_list *p; |
| 9 | for (p = commit->parents; p ; p = p->next) { |
| 10 | struct commit *parent = p->item; |
| 11 | printf(" %s", diff_unique_abbrev(parent->object.sha1, abbrev)); |
| 12 | } |
| 13 | } |
| 14 | |
Junio C Hamano | cf2251b | 2006-05-31 15:11:49 -0700 | [diff] [blame] | 15 | static int append_signoff(char *buf, int buf_sz, int at, const char *signoff) |
| 16 | { |
| 17 | int signoff_len = strlen(signoff); |
| 18 | static const char signed_off_by[] = "Signed-off-by: "; |
| 19 | char *cp = buf; |
| 20 | |
| 21 | /* Do we have enough space to add it? */ |
| 22 | if (buf_sz - at <= strlen(signed_off_by) + signoff_len + 2) |
| 23 | return at; |
| 24 | |
| 25 | /* First see if we already have the sign-off by the signer */ |
| 26 | while (1) { |
| 27 | cp = strstr(cp, signed_off_by); |
| 28 | if (!cp) |
| 29 | break; |
| 30 | cp += strlen(signed_off_by); |
| 31 | if ((cp + signoff_len < buf + at) && |
| 32 | !strncmp(cp, signoff, signoff_len) && |
| 33 | isspace(cp[signoff_len])) |
| 34 | return at; /* we already have him */ |
| 35 | } |
| 36 | |
| 37 | strcpy(buf + at, signed_off_by); |
| 38 | at += strlen(signed_off_by); |
| 39 | strcpy(buf + at, signoff); |
| 40 | at += signoff_len; |
| 41 | buf[at++] = '\n'; |
| 42 | buf[at] = 0; |
| 43 | return at; |
| 44 | } |
| 45 | |
Timo Hirvonen | 39bc9a6 | 2006-06-25 13:54:14 +0300 | [diff] [blame] | 46 | void show_log(struct rev_info *opt, const char *sep) |
Linus Torvalds | 9153983 | 2006-04-17 11:59:32 -0700 | [diff] [blame] | 47 | { |
| 48 | static char this_header[16384]; |
Timo Hirvonen | 39bc9a6 | 2006-06-25 13:54:14 +0300 | [diff] [blame] | 49 | struct log_info *log = opt->loginfo; |
Linus Torvalds | 9153983 | 2006-04-17 11:59:32 -0700 | [diff] [blame] | 50 | struct commit *commit = log->commit, *parent = log->parent; |
| 51 | int abbrev = opt->diffopt.abbrev; |
| 52 | int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40; |
Linus Torvalds | a4d34e2 | 2006-04-17 17:43:40 -0700 | [diff] [blame] | 53 | const char *extra; |
Linus Torvalds | 9153983 | 2006-04-17 11:59:32 -0700 | [diff] [blame] | 54 | int len; |
Johannes Schindelin | 20ff068 | 2006-06-02 15:21:17 +0200 | [diff] [blame] | 55 | const char *subject = NULL, *extra_headers = opt->extra_headers; |
Linus Torvalds | 9153983 | 2006-04-17 11:59:32 -0700 | [diff] [blame] | 56 | |
| 57 | opt->loginfo = NULL; |
| 58 | if (!opt->verbose_header) { |
Linus Torvalds | c8c893c | 2006-05-03 07:59:00 -0700 | [diff] [blame] | 59 | fputs(diff_unique_abbrev(commit->object.sha1, abbrev_commit), stdout); |
| 60 | if (opt->parents) |
| 61 | show_parents(commit, abbrev_commit); |
| 62 | putchar('\n'); |
Linus Torvalds | 9153983 | 2006-04-17 11:59:32 -0700 | [diff] [blame] | 63 | return; |
| 64 | } |
| 65 | |
| 66 | /* |
Linus Torvalds | a4d34e2 | 2006-04-17 17:43:40 -0700 | [diff] [blame] | 67 | * The "oneline" format has several special cases: |
| 68 | * - The pretty-printed commit lacks a newline at the end |
| 69 | * of the buffer, but we do want to make sure that we |
| 70 | * have a newline there. If the separator isn't already |
| 71 | * a newline, add an extra one. |
| 72 | * - unlike other log messages, the one-line format does |
| 73 | * not have an empty line between entries. |
Linus Torvalds | 9153983 | 2006-04-17 11:59:32 -0700 | [diff] [blame] | 74 | */ |
Linus Torvalds | a4d34e2 | 2006-04-17 17:43:40 -0700 | [diff] [blame] | 75 | extra = ""; |
| 76 | if (*sep != '\n' && opt->commit_format == CMIT_FMT_ONELINE) |
| 77 | extra = "\n"; |
Linus Torvalds | 9153983 | 2006-04-17 11:59:32 -0700 | [diff] [blame] | 78 | if (opt->shown_one && opt->commit_format != CMIT_FMT_ONELINE) |
| 79 | putchar('\n'); |
| 80 | opt->shown_one = 1; |
| 81 | |
| 82 | /* |
| 83 | * Print header line of header.. |
| 84 | */ |
Junio C Hamano | 3eefc18 | 2006-04-18 16:45:27 -0700 | [diff] [blame] | 85 | |
Johannes Schindelin | 596524b | 2006-05-05 04:30:52 +0200 | [diff] [blame] | 86 | if (opt->commit_format == CMIT_FMT_EMAIL) { |
Johannes Schindelin | 698ce6f | 2006-05-20 15:40:29 +0200 | [diff] [blame] | 87 | char *sha1 = sha1_to_hex(commit->object.sha1); |
Johannes Schindelin | 596524b | 2006-05-05 04:30:52 +0200 | [diff] [blame] | 88 | if (opt->total > 0) { |
| 89 | static char buffer[64]; |
| 90 | snprintf(buffer, sizeof(buffer), |
| 91 | "Subject: [PATCH %d/%d] ", |
| 92 | opt->nr, opt->total); |
| 93 | subject = buffer; |
Johannes Schindelin | 8ac80a5 | 2006-05-05 04:31:29 +0200 | [diff] [blame] | 94 | } else if (opt->total == 0) |
Johannes Schindelin | 596524b | 2006-05-05 04:30:52 +0200 | [diff] [blame] | 95 | subject = "Subject: [PATCH] "; |
Johannes Schindelin | 8ac80a5 | 2006-05-05 04:31:29 +0200 | [diff] [blame] | 96 | else |
| 97 | subject = "Subject: "; |
| 98 | |
Johannes Schindelin | 698ce6f | 2006-05-20 15:40:29 +0200 | [diff] [blame] | 99 | printf("From %s Mon Sep 17 00:00:00 2001\n", sha1); |
| 100 | if (opt->mime_boundary) { |
| 101 | static char subject_buffer[1024]; |
| 102 | static char buffer[1024]; |
| 103 | snprintf(subject_buffer, sizeof(subject_buffer) - 1, |
Johannes Schindelin | 20ff068 | 2006-06-02 15:21:17 +0200 | [diff] [blame] | 104 | "%s" |
Johannes Schindelin | 698ce6f | 2006-05-20 15:40:29 +0200 | [diff] [blame] | 105 | "MIME-Version: 1.0\n" |
| 106 | "Content-Type: multipart/mixed;\n" |
| 107 | " boundary=\"%s%s\"\n" |
| 108 | "\n" |
| 109 | "This is a multi-part message in MIME " |
| 110 | "format.\n" |
| 111 | "--%s%s\n" |
| 112 | "Content-Type: text/plain; " |
| 113 | "charset=UTF-8; format=fixed\n" |
| 114 | "Content-Transfer-Encoding: 8bit\n\n", |
Johannes Schindelin | 20ff068 | 2006-06-02 15:21:17 +0200 | [diff] [blame] | 115 | extra_headers ? extra_headers : "", |
Johannes Schindelin | 698ce6f | 2006-05-20 15:40:29 +0200 | [diff] [blame] | 116 | mime_boundary_leader, opt->mime_boundary, |
| 117 | mime_boundary_leader, opt->mime_boundary); |
Johannes Schindelin | 20ff068 | 2006-06-02 15:21:17 +0200 | [diff] [blame] | 118 | extra_headers = subject_buffer; |
Johannes Schindelin | 698ce6f | 2006-05-20 15:40:29 +0200 | [diff] [blame] | 119 | |
| 120 | snprintf(buffer, sizeof(buffer) - 1, |
| 121 | "--%s%s\n" |
| 122 | "Content-Type: text/x-patch;\n" |
| 123 | " name=\"%s.diff\"\n" |
| 124 | "Content-Transfer-Encoding: 8bit\n" |
| 125 | "Content-Disposition: inline;\n" |
| 126 | " filename=\"%s.diff\"\n\n", |
| 127 | mime_boundary_leader, opt->mime_boundary, |
| 128 | sha1, sha1); |
| 129 | opt->diffopt.stat_sep = buffer; |
| 130 | } |
Johannes Schindelin | 596524b | 2006-05-05 04:30:52 +0200 | [diff] [blame] | 131 | } else { |
Jeff King | ce43697 | 2006-07-23 05:24:18 -0400 | [diff] [blame] | 132 | printf("%s%s%s", |
| 133 | diff_get_color(opt->diffopt.color_diff, DIFF_COMMIT), |
Junio C Hamano | 3eefc18 | 2006-04-18 16:45:27 -0700 | [diff] [blame] | 134 | opt->commit_format == CMIT_FMT_ONELINE ? "" : "commit ", |
| 135 | diff_unique_abbrev(commit->object.sha1, abbrev_commit)); |
Junio C Hamano | c66b6c0 | 2006-05-06 14:42:08 -0700 | [diff] [blame] | 136 | if (opt->parents) |
| 137 | show_parents(commit, abbrev_commit); |
Junio C Hamano | 73f0a15 | 2006-05-24 12:19:47 -0700 | [diff] [blame] | 138 | if (parent) |
Junio C Hamano | 3eefc18 | 2006-04-18 16:45:27 -0700 | [diff] [blame] | 139 | printf(" (from %s)", |
| 140 | diff_unique_abbrev(parent->object.sha1, |
| 141 | abbrev_commit)); |
Jeff King | ce43697 | 2006-07-23 05:24:18 -0400 | [diff] [blame] | 142 | printf("%s", |
| 143 | diff_get_color(opt->diffopt.color_diff, DIFF_RESET)); |
Johannes Schindelin | e557667 | 2006-07-24 14:41:41 +0200 | [diff] [blame^] | 144 | putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n'); |
Junio C Hamano | 3eefc18 | 2006-04-18 16:45:27 -0700 | [diff] [blame] | 145 | } |
Linus Torvalds | 9153983 | 2006-04-17 11:59:32 -0700 | [diff] [blame] | 146 | |
| 147 | /* |
| 148 | * And then the pretty-printed message itself |
| 149 | */ |
Johannes Schindelin | 20ff068 | 2006-06-02 15:21:17 +0200 | [diff] [blame] | 150 | len = pretty_print_commit(opt->commit_format, commit, ~0u, this_header, sizeof(this_header), abbrev, subject, extra_headers); |
Junio C Hamano | cf2251b | 2006-05-31 15:11:49 -0700 | [diff] [blame] | 151 | |
| 152 | if (opt->add_signoff) |
| 153 | len = append_signoff(this_header, sizeof(this_header), len, |
| 154 | opt->add_signoff); |
Linus Torvalds | a4d34e2 | 2006-04-17 17:43:40 -0700 | [diff] [blame] | 155 | printf("%s%s%s", this_header, extra, sep); |
Linus Torvalds | 9153983 | 2006-04-17 11:59:32 -0700 | [diff] [blame] | 156 | } |
| 157 | |
Linus Torvalds | cd2bdc5 | 2006-04-14 16:52:13 -0700 | [diff] [blame] | 158 | int log_tree_diff_flush(struct rev_info *opt) |
Junio C Hamano | 5f1c3f0 | 2006-04-09 01:11:11 -0700 | [diff] [blame] | 159 | { |
| 160 | diffcore_std(&opt->diffopt); |
Linus Torvalds | 9153983 | 2006-04-17 11:59:32 -0700 | [diff] [blame] | 161 | |
Junio C Hamano | 5f1c3f0 | 2006-04-09 01:11:11 -0700 | [diff] [blame] | 162 | if (diff_queue_is_empty()) { |
| 163 | int saved_fmt = opt->diffopt.output_format; |
| 164 | opt->diffopt.output_format = DIFF_FORMAT_NO_OUTPUT; |
| 165 | diff_flush(&opt->diffopt); |
| 166 | opt->diffopt.output_format = saved_fmt; |
| 167 | return 0; |
| 168 | } |
Linus Torvalds | 9153983 | 2006-04-17 11:59:32 -0700 | [diff] [blame] | 169 | |
Junio C Hamano | 3969cf7 | 2006-06-27 15:08:19 -0700 | [diff] [blame] | 170 | if (opt->loginfo && !opt->no_commit_id) { |
| 171 | /* When showing a verbose header (i.e. log message), |
| 172 | * and not in --pretty=oneline format, we would want |
| 173 | * an extra newline between the end of log and the |
| 174 | * output for readability. |
| 175 | */ |
Timo Hirvonen | 39bc9a6 | 2006-06-25 13:54:14 +0300 | [diff] [blame] | 176 | show_log(opt, opt->diffopt.msg_sep); |
Junio C Hamano | 3969cf7 | 2006-06-27 15:08:19 -0700 | [diff] [blame] | 177 | if (opt->verbose_header && |
| 178 | opt->commit_format != CMIT_FMT_ONELINE) { |
| 179 | int pch = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_PATCH; |
| 180 | if ((pch & opt->diffopt.output_format) == pch) |
| 181 | printf("---%c", opt->diffopt.line_termination); |
| 182 | else |
| 183 | putchar(opt->diffopt.line_termination); |
| 184 | } |
| 185 | } |
Junio C Hamano | 5f1c3f0 | 2006-04-09 01:11:11 -0700 | [diff] [blame] | 186 | diff_flush(&opt->diffopt); |
| 187 | return 1; |
| 188 | } |
| 189 | |
Linus Torvalds | cd2bdc5 | 2006-04-14 16:52:13 -0700 | [diff] [blame] | 190 | static int diff_root_tree(struct rev_info *opt, |
Junio C Hamano | 5f1c3f0 | 2006-04-09 01:11:11 -0700 | [diff] [blame] | 191 | const unsigned char *new, const char *base) |
| 192 | { |
| 193 | int retval; |
| 194 | void *tree; |
| 195 | struct tree_desc empty, real; |
| 196 | |
| 197 | tree = read_object_with_reference(new, tree_type, &real.size, NULL); |
| 198 | if (!tree) |
| 199 | die("unable to read root tree (%s)", sha1_to_hex(new)); |
| 200 | real.buf = tree; |
| 201 | |
| 202 | empty.buf = ""; |
| 203 | empty.size = 0; |
| 204 | retval = diff_tree(&empty, &real, base, &opt->diffopt); |
| 205 | free(tree); |
| 206 | log_tree_diff_flush(opt); |
| 207 | return retval; |
| 208 | } |
| 209 | |
Linus Torvalds | cd2bdc5 | 2006-04-14 16:52:13 -0700 | [diff] [blame] | 210 | static int do_diff_combined(struct rev_info *opt, struct commit *commit) |
Junio C Hamano | 5f1c3f0 | 2006-04-09 01:11:11 -0700 | [diff] [blame] | 211 | { |
| 212 | unsigned const char *sha1 = commit->object.sha1; |
| 213 | |
Linus Torvalds | 9153983 | 2006-04-17 11:59:32 -0700 | [diff] [blame] | 214 | diff_tree_combined_merge(sha1, opt->dense_combined_merges, opt); |
| 215 | return !opt->loginfo; |
Junio C Hamano | 5f1c3f0 | 2006-04-09 01:11:11 -0700 | [diff] [blame] | 216 | } |
| 217 | |
Linus Torvalds | 9153983 | 2006-04-17 11:59:32 -0700 | [diff] [blame] | 218 | /* |
| 219 | * Show the diff of a commit. |
| 220 | * |
| 221 | * Return true if we printed any log info messages |
| 222 | */ |
| 223 | static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log_info *log) |
Junio C Hamano | 5f1c3f0 | 2006-04-09 01:11:11 -0700 | [diff] [blame] | 224 | { |
Linus Torvalds | 9153983 | 2006-04-17 11:59:32 -0700 | [diff] [blame] | 225 | int showed_log; |
Junio C Hamano | 5f1c3f0 | 2006-04-09 01:11:11 -0700 | [diff] [blame] | 226 | struct commit_list *parents; |
| 227 | unsigned const char *sha1 = commit->object.sha1; |
| 228 | |
Linus Torvalds | 9153983 | 2006-04-17 11:59:32 -0700 | [diff] [blame] | 229 | if (!opt->diff) |
| 230 | return 0; |
| 231 | |
Junio C Hamano | 5f1c3f0 | 2006-04-09 01:11:11 -0700 | [diff] [blame] | 232 | /* Root commit? */ |
Linus Torvalds | 9153983 | 2006-04-17 11:59:32 -0700 | [diff] [blame] | 233 | parents = commit->parents; |
| 234 | if (!parents) { |
| 235 | if (opt->show_root_diff) |
| 236 | diff_root_tree(opt, sha1, ""); |
| 237 | return !opt->loginfo; |
Junio C Hamano | 5f1c3f0 | 2006-04-09 01:11:11 -0700 | [diff] [blame] | 238 | } |
| 239 | |
| 240 | /* More than one parent? */ |
Linus Torvalds | 9153983 | 2006-04-17 11:59:32 -0700 | [diff] [blame] | 241 | if (parents && parents->next) { |
Junio C Hamano | 5f1c3f0 | 2006-04-09 01:11:11 -0700 | [diff] [blame] | 242 | if (opt->ignore_merges) |
| 243 | return 0; |
| 244 | else if (opt->combine_merges) |
| 245 | return do_diff_combined(opt, commit); |
Linus Torvalds | 9153983 | 2006-04-17 11:59:32 -0700 | [diff] [blame] | 246 | |
| 247 | /* If we show individual diffs, show the parent info */ |
| 248 | log->parent = parents->item; |
Junio C Hamano | 5f1c3f0 | 2006-04-09 01:11:11 -0700 | [diff] [blame] | 249 | } |
| 250 | |
Linus Torvalds | 9153983 | 2006-04-17 11:59:32 -0700 | [diff] [blame] | 251 | showed_log = 0; |
| 252 | for (;;) { |
Junio C Hamano | 5f1c3f0 | 2006-04-09 01:11:11 -0700 | [diff] [blame] | 253 | struct commit *parent = parents->item; |
Junio C Hamano | 5f1c3f0 | 2006-04-09 01:11:11 -0700 | [diff] [blame] | 254 | |
Linus Torvalds | 9153983 | 2006-04-17 11:59:32 -0700 | [diff] [blame] | 255 | diff_tree_sha1(parent->object.sha1, sha1, "", &opt->diffopt); |
| 256 | log_tree_diff_flush(opt); |
| 257 | |
| 258 | showed_log |= !opt->loginfo; |
| 259 | |
| 260 | /* Set up the log info for the next parent, if any.. */ |
| 261 | parents = parents->next; |
| 262 | if (!parents) |
| 263 | break; |
| 264 | log->parent = parents->item; |
| 265 | opt->loginfo = log; |
Junio C Hamano | 5f1c3f0 | 2006-04-09 01:11:11 -0700 | [diff] [blame] | 266 | } |
Linus Torvalds | 9153983 | 2006-04-17 11:59:32 -0700 | [diff] [blame] | 267 | return showed_log; |
| 268 | } |
| 269 | |
| 270 | int log_tree_commit(struct rev_info *opt, struct commit *commit) |
| 271 | { |
| 272 | struct log_info log; |
Junio C Hamano | 3eefc18 | 2006-04-18 16:45:27 -0700 | [diff] [blame] | 273 | int shown; |
Linus Torvalds | 9153983 | 2006-04-17 11:59:32 -0700 | [diff] [blame] | 274 | |
| 275 | log.commit = commit; |
| 276 | log.parent = NULL; |
| 277 | opt->loginfo = &log; |
| 278 | |
Junio C Hamano | 3eefc18 | 2006-04-18 16:45:27 -0700 | [diff] [blame] | 279 | shown = log_tree_diff(opt, commit, &log); |
| 280 | if (!shown && opt->loginfo && opt->always_show_header) { |
Linus Torvalds | 9153983 | 2006-04-17 11:59:32 -0700 | [diff] [blame] | 281 | log.parent = NULL; |
Timo Hirvonen | 39bc9a6 | 2006-06-25 13:54:14 +0300 | [diff] [blame] | 282 | show_log(opt, ""); |
Junio C Hamano | 3eefc18 | 2006-04-18 16:45:27 -0700 | [diff] [blame] | 283 | shown = 1; |
Linus Torvalds | 9153983 | 2006-04-17 11:59:32 -0700 | [diff] [blame] | 284 | } |
| 285 | opt->loginfo = NULL; |
Junio C Hamano | 3eefc18 | 2006-04-18 16:45:27 -0700 | [diff] [blame] | 286 | return shown; |
Junio C Hamano | 5f1c3f0 | 2006-04-09 01:11:11 -0700 | [diff] [blame] | 287 | } |