diff-tree.con commit GIT 1.0.0 (c2f3bf0)
   1#include "cache.h"
   2#include "diff.h"
   3#include "commit.h"
   4
   5static int show_root_diff = 0;
   6static int no_commit_id = 0;
   7static int verbose_header = 0;
   8static int ignore_merges = 1;
   9static int read_stdin = 0;
  10
  11static const char *header = NULL;
  12static const char *header_prefix = "";
  13static enum cmit_fmt commit_format = CMIT_FMT_RAW;
  14
  15static struct diff_options diff_options;
  16
  17static int call_diff_flush(void)
  18{
  19        diffcore_std(&diff_options);
  20        if (diff_queue_is_empty()) {
  21                int saved_fmt = diff_options.output_format;
  22                diff_options.output_format = DIFF_FORMAT_NO_OUTPUT;
  23                diff_flush(&diff_options);
  24                diff_options.output_format = saved_fmt;
  25                return 0;
  26        }
  27        if (header) {
  28                if (!no_commit_id)
  29                        printf("%s%c", header, diff_options.line_termination);
  30                header = NULL;
  31        }
  32        diff_flush(&diff_options);
  33        return 1;
  34}
  35
  36static int diff_tree_sha1_top(const unsigned char *old,
  37                              const unsigned char *new, const char *base)
  38{
  39        int ret;
  40
  41        ret = diff_tree_sha1(old, new, base, &diff_options);
  42        call_diff_flush();
  43        return ret;
  44}
  45
  46static int diff_root_tree(const unsigned char *new, const char *base)
  47{
  48        int retval;
  49        void *tree;
  50        struct tree_desc empty, real;
  51
  52        tree = read_object_with_reference(new, "tree", &real.size, NULL);
  53        if (!tree)
  54                die("unable to read root tree (%s)", sha1_to_hex(new));
  55        real.buf = tree;
  56
  57        empty.buf = "";
  58        empty.size = 0;
  59        retval = diff_tree(&empty, &real, base, &diff_options);
  60        free(tree);
  61        call_diff_flush();
  62        return retval;
  63}
  64
  65static const char *generate_header(const unsigned char *commit_sha1,
  66                                   const unsigned char *parent_sha1,
  67                                   const char *msg)
  68{
  69        static char this_header[16384];
  70        int offset;
  71        unsigned long len;
  72        int abbrev = diff_options.abbrev;
  73
  74        if (!verbose_header)
  75                return sha1_to_hex(commit_sha1);
  76
  77        len = strlen(msg);
  78
  79        offset = sprintf(this_header, "%s%s ",
  80                         header_prefix,
  81                         diff_unique_abbrev(commit_sha1, abbrev));
  82        offset += sprintf(this_header + offset, "(from %s)\n",
  83                         parent_sha1 ?
  84                         diff_unique_abbrev(parent_sha1, abbrev) : "root");
  85        offset += pretty_print_commit(commit_format, msg, len,
  86                                      this_header + offset,
  87                                      sizeof(this_header) - offset);
  88        return this_header;
  89}
  90
  91static int diff_tree_commit(const unsigned char *commit_sha1)
  92{
  93        struct commit *commit;
  94        struct commit_list *parents;
  95        char name[50];
  96        unsigned char sha1[20];
  97
  98        sprintf(name, "%s^0", sha1_to_hex(commit_sha1));
  99        if (get_sha1(name, sha1))
 100                return -1;
 101        name[40] = 0;
 102        commit = lookup_commit(sha1);
 103        
 104        /* Root commit? */
 105        if (show_root_diff && !commit->parents) {
 106                header = generate_header(sha1, NULL, commit->buffer);
 107                diff_root_tree(commit_sha1, "");
 108        }
 109
 110        /* More than one parent? */
 111        if (ignore_merges && commit->parents && commit->parents->next)
 112                return 0;
 113
 114        for (parents = commit->parents; parents; parents = parents->next) {
 115                struct commit *parent = parents->item;
 116                header = generate_header(sha1,
 117                                         parent->object.sha1,
 118                                         commit->buffer);
 119                diff_tree_sha1_top(parent->object.sha1, commit_sha1, "");
 120                if (!header && verbose_header) {
 121                        header_prefix = "\ndiff-tree ";
 122                        /*
 123                         * Don't print multiple merge entries if we
 124                         * don't print the diffs.
 125                         */
 126                }
 127        }
 128        return 0;
 129}
 130
 131static int diff_tree_stdin(char *line)
 132{
 133        int len = strlen(line);
 134        unsigned char commit[20], parent[20];
 135        static char this_header[1000];
 136        int abbrev = diff_options.abbrev;
 137
 138        if (!len || line[len-1] != '\n')
 139                return -1;
 140        line[len-1] = 0;
 141        if (get_sha1_hex(line, commit))
 142                return -1;
 143        if (isspace(line[40]) && !get_sha1_hex(line+41, parent)) {
 144                line[40] = 0;
 145                line[81] = 0;
 146                sprintf(this_header, "%s (from %s)\n",
 147                        diff_unique_abbrev(commit, abbrev),
 148                        diff_unique_abbrev(parent, abbrev));
 149                header = this_header;
 150                return diff_tree_sha1_top(parent, commit, "");
 151        }
 152        line[40] = 0;
 153        return diff_tree_commit(commit);
 154}
 155
 156static const char diff_tree_usage[] =
 157"git-diff-tree [--stdin] [-m] [-s] [-v] [--pretty] [-t] [-r] [--root] "
 158"[<common diff options>] <tree-ish> [<tree-ish>] [<path>...]\n"
 159"  -r            diff recursively\n"
 160"  --root        include the initial commit as diff against /dev/null\n"
 161COMMON_DIFF_OPTIONS_HELP;
 162
 163int main(int argc, const char **argv)
 164{
 165        int nr_sha1;
 166        char line[1000];
 167        unsigned char sha1[2][20];
 168        const char *prefix = setup_git_directory();
 169
 170        git_config(git_diff_config);
 171        nr_sha1 = 0;
 172        diff_setup(&diff_options);
 173
 174        for (;;) {
 175                int diff_opt_cnt;
 176                const char *arg;
 177
 178                argv++;
 179                argc--;
 180                arg = *argv;
 181                if (!arg)
 182                        break;
 183
 184                if (*arg != '-') {
 185                        if (nr_sha1 < 2 && !get_sha1(arg, sha1[nr_sha1])) {
 186                                nr_sha1++;
 187                                continue;
 188                        }
 189                        break;
 190                }
 191
 192                diff_opt_cnt = diff_opt_parse(&diff_options, argv, argc);
 193                if (diff_opt_cnt < 0)
 194                        usage(diff_tree_usage);
 195                else if (diff_opt_cnt) {
 196                        argv += diff_opt_cnt - 1;
 197                        argc -= diff_opt_cnt - 1;
 198                        continue;
 199                }
 200
 201
 202                if (!strcmp(arg, "--")) {
 203                        argv++;
 204                        argc--;
 205                        break;
 206                }
 207                if (!strcmp(arg, "-r")) {
 208                        diff_options.recursive = 1;
 209                        continue;
 210                }
 211                if (!strcmp(arg, "-t")) {
 212                        diff_options.recursive = 1;
 213                        diff_options.tree_in_recursive = 1;
 214                        continue;
 215                }
 216                if (!strcmp(arg, "-m")) {
 217                        ignore_merges = 0;
 218                        continue;
 219                }
 220                if (!strcmp(arg, "-v")) {
 221                        verbose_header = 1;
 222                        header_prefix = "diff-tree ";
 223                        continue;
 224                }
 225                if (!strncmp(arg, "--pretty", 8)) {
 226                        verbose_header = 1;
 227                        header_prefix = "diff-tree ";
 228                        commit_format = get_commit_format(arg+8);
 229                        continue;
 230                }
 231                if (!strcmp(arg, "--stdin")) {
 232                        read_stdin = 1;
 233                        continue;
 234                }
 235                if (!strcmp(arg, "--root")) {
 236                        show_root_diff = 1;
 237                        continue;
 238                }
 239                if (!strcmp(arg, "--no-commit-id")) {
 240                        no_commit_id = 1;
 241                        continue;
 242                }
 243                usage(diff_tree_usage);
 244        }
 245        if (diff_options.output_format == DIFF_FORMAT_PATCH)
 246                diff_options.recursive = 1;
 247
 248        diff_tree_setup_paths(get_pathspec(prefix, argv));
 249        diff_setup_done(&diff_options);
 250
 251        switch (nr_sha1) {
 252        case 0:
 253                if (!read_stdin)
 254                        usage(diff_tree_usage);
 255                break;
 256        case 1:
 257                diff_tree_commit(sha1[0]);
 258                break;
 259        case 2:
 260                diff_tree_sha1_top(sha1[0], sha1[1], "");
 261                break;
 262        }
 263
 264        if (!read_stdin)
 265                return 0;
 266
 267        if (diff_options.detect_rename)
 268                diff_options.setup |= (DIFF_SETUP_USE_SIZE_CACHE |
 269                                       DIFF_SETUP_USE_CACHE);
 270        while (fgets(line, sizeof(line), stdin))
 271                diff_tree_stdin(line);
 272
 273        return 0;
 274}