log-tree.con commit Merge git://git.kernel.org/pub/scm/gitk/gitk (0825de8)
   1#include "cache.h"
   2#include "diff.h"
   3#include "commit.h"
   4#include "log-tree.h"
   5
   6static 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
  15void show_log(struct rev_info *opt, struct log_info *log, const char *sep)
  16{
  17        static char this_header[16384];
  18        struct commit *commit = log->commit, *parent = log->parent;
  19        int abbrev = opt->diffopt.abbrev;
  20        int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40;
  21        const char *extra;
  22        int len;
  23        char *subject = NULL, *after_subject = NULL;
  24
  25        opt->loginfo = NULL;
  26        if (!opt->verbose_header) {
  27                fputs(diff_unique_abbrev(commit->object.sha1, abbrev_commit), stdout);
  28                if (opt->parents)
  29                        show_parents(commit, abbrev_commit);
  30                putchar('\n');
  31                return;
  32        }
  33
  34        /*
  35         * The "oneline" format has several special cases:
  36         *  - The pretty-printed commit lacks a newline at the end
  37         *    of the buffer, but we do want to make sure that we
  38         *    have a newline there. If the separator isn't already
  39         *    a newline, add an extra one.
  40         *  - unlike other log messages, the one-line format does
  41         *    not have an empty line between entries.
  42         */
  43        extra = "";
  44        if (*sep != '\n' && opt->commit_format == CMIT_FMT_ONELINE)
  45                extra = "\n";
  46        if (opt->shown_one && opt->commit_format != CMIT_FMT_ONELINE)
  47                putchar('\n');
  48        opt->shown_one = 1;
  49
  50        /*
  51         * Print header line of header..
  52         */
  53
  54        if (opt->commit_format == CMIT_FMT_EMAIL) {
  55                char *sha1 = sha1_to_hex(commit->object.sha1);
  56                if (opt->total > 0) {
  57                        static char buffer[64];
  58                        snprintf(buffer, sizeof(buffer),
  59                                        "Subject: [PATCH %d/%d] ",
  60                                        opt->nr, opt->total);
  61                        subject = buffer;
  62                } else if (opt->total == 0)
  63                        subject = "Subject: [PATCH] ";
  64                else
  65                        subject = "Subject: ";
  66
  67                printf("From %s Mon Sep 17 00:00:00 2001\n", sha1);
  68                if (opt->mime_boundary) {
  69                        static char subject_buffer[1024];
  70                        static char buffer[1024];
  71                        snprintf(subject_buffer, sizeof(subject_buffer) - 1,
  72                                 "MIME-Version: 1.0\n"
  73                                 "Content-Type: multipart/mixed;\n"
  74                                 " boundary=\"%s%s\"\n"
  75                                 "\n"
  76                                 "This is a multi-part message in MIME "
  77                                 "format.\n"
  78                                 "--%s%s\n"
  79                                 "Content-Type: text/plain; "
  80                                 "charset=UTF-8; format=fixed\n"
  81                                 "Content-Transfer-Encoding: 8bit\n\n",
  82                                 mime_boundary_leader, opt->mime_boundary,
  83                                 mime_boundary_leader, opt->mime_boundary);
  84                        after_subject = subject_buffer;
  85
  86                        snprintf(buffer, sizeof(buffer) - 1,
  87                                 "--%s%s\n"
  88                                 "Content-Type: text/x-patch;\n"
  89                                 " name=\"%s.diff\"\n"
  90                                 "Content-Transfer-Encoding: 8bit\n"
  91                                 "Content-Disposition: inline;\n"
  92                                 " filename=\"%s.diff\"\n\n",
  93                                 mime_boundary_leader, opt->mime_boundary,
  94                                 sha1, sha1);
  95                        opt->diffopt.stat_sep = buffer;
  96                }
  97        } else {
  98                printf("%s%s",
  99                       opt->commit_format == CMIT_FMT_ONELINE ? "" : "commit ",
 100                       diff_unique_abbrev(commit->object.sha1, abbrev_commit));
 101                if (opt->parents)
 102                        show_parents(commit, abbrev_commit);
 103                if (parent)
 104                        printf(" (from %s)",
 105                               diff_unique_abbrev(parent->object.sha1,
 106                                                  abbrev_commit));
 107                putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n');
 108        }
 109
 110        /*
 111         * And then the pretty-printed message itself
 112         */
 113        len = pretty_print_commit(opt->commit_format, commit, ~0u, this_header, sizeof(this_header), abbrev, subject, after_subject);
 114        printf("%s%s%s", this_header, extra, sep);
 115}
 116
 117int log_tree_diff_flush(struct rev_info *opt)
 118{
 119        diffcore_std(&opt->diffopt);
 120
 121        if (diff_queue_is_empty()) {
 122                int saved_fmt = opt->diffopt.output_format;
 123                opt->diffopt.output_format = DIFF_FORMAT_NO_OUTPUT;
 124                diff_flush(&opt->diffopt);
 125                opt->diffopt.output_format = saved_fmt;
 126                return 0;
 127        }
 128
 129        if (opt->loginfo && !opt->no_commit_id)
 130                show_log(opt, opt->loginfo, opt->diffopt.with_stat ? "---\n" : "\n");
 131        diff_flush(&opt->diffopt);
 132        return 1;
 133}
 134
 135static int diff_root_tree(struct rev_info *opt,
 136                          const unsigned char *new, const char *base)
 137{
 138        int retval;
 139        void *tree;
 140        struct tree_desc empty, real;
 141
 142        tree = read_object_with_reference(new, tree_type, &real.size, NULL);
 143        if (!tree)
 144                die("unable to read root tree (%s)", sha1_to_hex(new));
 145        real.buf = tree;
 146
 147        empty.buf = "";
 148        empty.size = 0;
 149        retval = diff_tree(&empty, &real, base, &opt->diffopt);
 150        free(tree);
 151        log_tree_diff_flush(opt);
 152        return retval;
 153}
 154
 155static int do_diff_combined(struct rev_info *opt, struct commit *commit)
 156{
 157        unsigned const char *sha1 = commit->object.sha1;
 158
 159        diff_tree_combined_merge(sha1, opt->dense_combined_merges, opt);
 160        return !opt->loginfo;
 161}
 162
 163/*
 164 * Show the diff of a commit.
 165 *
 166 * Return true if we printed any log info messages
 167 */
 168static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log_info *log)
 169{
 170        int showed_log;
 171        struct commit_list *parents;
 172        unsigned const char *sha1 = commit->object.sha1;
 173
 174        if (!opt->diff)
 175                return 0;
 176
 177        /* Root commit? */
 178        parents = commit->parents;
 179        if (!parents) {
 180                if (opt->show_root_diff)
 181                        diff_root_tree(opt, sha1, "");
 182                return !opt->loginfo;
 183        }
 184
 185        /* More than one parent? */
 186        if (parents && parents->next) {
 187                if (opt->ignore_merges)
 188                        return 0;
 189                else if (opt->combine_merges)
 190                        return do_diff_combined(opt, commit);
 191
 192                /* If we show individual diffs, show the parent info */
 193                log->parent = parents->item;
 194        }
 195
 196        showed_log = 0;
 197        for (;;) {
 198                struct commit *parent = parents->item;
 199
 200                diff_tree_sha1(parent->object.sha1, sha1, "", &opt->diffopt);
 201                log_tree_diff_flush(opt);
 202
 203                showed_log |= !opt->loginfo;
 204
 205                /* Set up the log info for the next parent, if any.. */
 206                parents = parents->next;
 207                if (!parents)
 208                        break;
 209                log->parent = parents->item;
 210                opt->loginfo = log;
 211        }
 212        return showed_log;
 213}
 214
 215int log_tree_commit(struct rev_info *opt, struct commit *commit)
 216{
 217        struct log_info log;
 218        int shown;
 219
 220        log.commit = commit;
 221        log.parent = NULL;
 222        opt->loginfo = &log;
 223
 224        shown = log_tree_diff(opt, commit, &log);
 225        if (!shown && opt->loginfo && opt->always_show_header) {
 226                log.parent = NULL;
 227                show_log(opt, opt->loginfo, "");
 228                shown = 1;
 229        }
 230        opt->loginfo = NULL;
 231        return shown;
 232}