git-cvsimport: Handle "Removed" from pserver
[git.git] / diff-tree.c
1 #include "cache.h"
2 #include "diff.h"
3 #include "commit.h"
4 #include "log-tree.h"
5
6 static struct log_tree_opt log_tree_opt;
7
8 static int diff_tree_commit_sha1(const unsigned char *sha1)
9 {
10         struct commit *commit = lookup_commit_reference(sha1);
11         if (!commit)
12                 return -1;
13         return log_tree_commit(&log_tree_opt, commit);
14 }
15
16 static int diff_tree_stdin(char *line)
17 {
18         int len = strlen(line);
19         unsigned char sha1[20];
20         struct commit *commit;
21
22         if (!len || line[len-1] != '\n')
23                 return -1;
24         line[len-1] = 0;
25         if (get_sha1_hex(line, sha1))
26                 return -1;
27         commit = lookup_commit(sha1);
28         if (!commit || parse_commit(commit))
29                 return -1;
30         if (isspace(line[40]) && !get_sha1_hex(line+41, sha1)) {
31                 /* Graft the fake parents locally to the commit */
32                 int pos = 41;
33                 struct commit_list **pptr, *parents;
34
35                 /* Free the real parent list */
36                 for (parents = commit->parents; parents; ) {
37                         struct commit_list *tmp = parents->next;
38                         free(parents);
39                         parents = tmp;
40                 }
41                 commit->parents = NULL;
42                 pptr = &(commit->parents);
43                 while (line[pos] && !get_sha1_hex(line + pos, sha1)) {
44                         struct commit *parent = lookup_commit(sha1);
45                         if (parent) {
46                                 pptr = &commit_list_insert(parent, pptr)->next;
47                         }
48                         pos += 41;
49                 }
50         }
51         return log_tree_commit(&log_tree_opt, commit);
52 }
53
54 static const char diff_tree_usage[] =
55 "git-diff-tree [--stdin] [-m] [-c] [--cc] [-s] [-v] [--pretty] [-t] [-r] [--root] "
56 "[<common diff options>] <tree-ish> [<tree-ish>] [<path>...]\n"
57 "  -r            diff recursively\n"
58 "  --root        include the initial commit as diff against /dev/null\n"
59 COMMON_DIFF_OPTIONS_HELP;
60
61 int main(int argc, const char **argv)
62 {
63         int nr_sha1;
64         char line[1000];
65         unsigned char sha1[2][20];
66         const char *prefix = setup_git_directory();
67         static struct log_tree_opt *opt = &log_tree_opt;
68         int read_stdin = 0;
69
70         git_config(git_diff_config);
71         nr_sha1 = 0;
72         init_log_tree_opt(opt);
73
74         for (;;) {
75                 int opt_cnt;
76                 const char *arg;
77
78                 argv++;
79                 argc--;
80                 arg = *argv;
81                 if (!arg)
82                         break;
83
84                 if (*arg != '-') {
85                         if (nr_sha1 < 2 && !get_sha1(arg, sha1[nr_sha1])) {
86                                 nr_sha1++;
87                                 continue;
88                         }
89                         break;
90                 }
91
92                 opt_cnt = log_tree_opt_parse(opt, argv, argc);
93                 if (opt_cnt < 0)
94                         usage(diff_tree_usage);
95                 else if (opt_cnt) {
96                         argv += opt_cnt - 1;
97                         argc -= opt_cnt - 1;
98                         continue;
99                 }
100
101                 if (!strcmp(arg, "--")) {
102                         argv++;
103                         argc--;
104                         break;
105                 }
106                 if (!strcmp(arg, "--stdin")) {
107                         read_stdin = 1;
108                         continue;
109                 }
110                 usage(diff_tree_usage);
111         }
112
113         if (opt->combine_merges)
114                 opt->ignore_merges = 0;
115
116         /* We can only do dense combined merges with diff output */
117         if (opt->dense_combined_merges)
118                 opt->diffopt.output_format = DIFF_FORMAT_PATCH;
119
120         diff_tree_setup_paths(get_pathspec(prefix, argv), &opt->diffopt);
121         diff_setup_done(&opt->diffopt);
122
123         switch (nr_sha1) {
124         case 0:
125                 if (!read_stdin)
126                         usage(diff_tree_usage);
127                 break;
128         case 1:
129                 diff_tree_commit_sha1(sha1[0]);
130                 break;
131         case 2:
132                 diff_tree_sha1(sha1[0], sha1[1], "", &opt->diffopt);
133                 log_tree_diff_flush(opt);
134                 break;
135         }
136
137         if (!read_stdin)
138                 return 0;
139
140         if (opt->diffopt.detect_rename)
141                 opt->diffopt.setup |= (DIFF_SETUP_USE_SIZE_CACHE |
142                                        DIFF_SETUP_USE_CACHE);
143         while (fgets(line, sizeof(line), stdin))
144                 diff_tree_stdin(line);
145
146         return 0;
147 }