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