builtin-diff-tree.con commit Merge branch 'jc/fmt-patch' into next (b9d301b)
   1#include "cache.h"
   2#include "diff.h"
   3#include "commit.h"
   4#include "log-tree.h"
   5#include "builtin.h"
   6
   7static struct rev_info log_tree_opt;
   8
   9static int diff_tree_commit_sha1(const unsigned char *sha1)
  10{
  11        struct commit *commit = lookup_commit_reference(sha1);
  12        if (!commit)
  13                return -1;
  14        return log_tree_commit(&log_tree_opt, commit);
  15}
  16
  17static int diff_tree_stdin(char *line)
  18{
  19        int len = strlen(line);
  20        unsigned char sha1[20];
  21        struct commit *commit;
  22
  23        if (!len || line[len-1] != '\n')
  24                return -1;
  25        line[len-1] = 0;
  26        if (get_sha1_hex(line, sha1))
  27                return -1;
  28        commit = lookup_commit(sha1);
  29        if (!commit || parse_commit(commit))
  30                return -1;
  31        if (isspace(line[40]) && !get_sha1_hex(line+41, sha1)) {
  32                /* Graft the fake parents locally to the commit */
  33                int pos = 41;
  34                struct commit_list **pptr, *parents;
  35
  36                /* Free the real parent list */
  37                for (parents = commit->parents; parents; ) {
  38                        struct commit_list *tmp = parents->next;
  39                        free(parents);
  40                        parents = tmp;
  41                }
  42                commit->parents = NULL;
  43                pptr = &(commit->parents);
  44                while (line[pos] && !get_sha1_hex(line + pos, sha1)) {
  45                        struct commit *parent = lookup_commit(sha1);
  46                        if (parent) {
  47                                pptr = &commit_list_insert(parent, pptr)->next;
  48                        }
  49                        pos += 41;
  50                }
  51        }
  52        return log_tree_commit(&log_tree_opt, commit);
  53}
  54
  55static const char diff_tree_usage[] =
  56"git-diff-tree [--stdin] [-m] [-c] [--cc] [-s] [-v] [--pretty] [-t] [-r] [--root] "
  57"[<common diff options>] <tree-ish> [<tree-ish>] [<path>...]\n"
  58"  -r            diff recursively\n"
  59"  --root        include the initial commit as diff against /dev/null\n"
  60COMMON_DIFF_OPTIONS_HELP;
  61
  62int cmd_diff_tree(int argc, const char **argv, char **envp)
  63{
  64        int nr_sha1;
  65        char line[1000];
  66        struct object *tree1, *tree2;
  67        static struct rev_info *opt = &log_tree_opt;
  68        struct object_list *list;
  69        int read_stdin = 0;
  70
  71        git_config(git_diff_config);
  72        nr_sha1 = 0;
  73        init_revisions(opt);
  74        opt->abbrev = 0;
  75        opt->diff = 1;
  76        argc = setup_revisions(argc, argv, opt, NULL);
  77
  78        while (--argc > 0) {
  79                const char *arg = *++argv;
  80
  81                if (!strcmp(arg, "--stdin")) {
  82                        read_stdin = 1;
  83                        continue;
  84                }
  85                usage(diff_tree_usage);
  86        }
  87
  88        /*
  89         * NOTE! "setup_revisions()" will have inserted the revisions
  90         * it parsed in reverse order. So if you do
  91         *
  92         *      git-diff-tree a b
  93         *
  94         * the commit list will be "b" -> "a" -> NULL, so we reverse
  95         * the order of the objects if the first one is not marked
  96         * UNINTERESTING.
  97         */
  98        nr_sha1 = 0;
  99        list = opt->pending_objects;
 100        if (list) {
 101                nr_sha1++;
 102                tree1 = list->item;
 103                list = list->next;
 104                if (list) {
 105                        nr_sha1++;
 106                        tree2 = tree1;
 107                        tree1 = list->item;
 108                        if (list->next)
 109                                usage(diff_tree_usage);
 110                        /* Switch them around if the second one was uninteresting.. */
 111                        if (tree2->flags & UNINTERESTING) {
 112                                struct object *tmp = tree2;
 113                                tree2 = tree1;
 114                                tree1 = tmp;
 115                        }
 116                }
 117        }
 118
 119        switch (nr_sha1) {
 120        case 0:
 121                if (!read_stdin)
 122                        usage(diff_tree_usage);
 123                break;
 124        case 1:
 125                diff_tree_commit_sha1(tree1->sha1);
 126                break;
 127        case 2:
 128                diff_tree_sha1(tree1->sha1,
 129                               tree2->sha1,
 130                               "", &opt->diffopt);
 131                log_tree_diff_flush(opt);
 132                break;
 133        }
 134
 135        if (!read_stdin)
 136                return 0;
 137
 138        if (opt->diffopt.detect_rename)
 139                opt->diffopt.setup |= (DIFF_SETUP_USE_SIZE_CACHE |
 140                                       DIFF_SETUP_USE_CACHE);
 141        while (fgets(line, sizeof(line), stdin)) {
 142                unsigned char sha1[20];
 143
 144                if (get_sha1_hex(line, sha1)) {
 145                        fputs(line, stdout);
 146                        fflush(stdout);
 147                }
 148                else
 149                        diff_tree_stdin(line);
 150        }
 151        return 0;
 152}