t3600-rm: skip failed-remove test when we cannot make an unremovable file.
[git.git] / log-tree.c
1 #include "cache.h"
2 #include "diff.h"
3 #include "commit.h"
4 #include "log-tree.h"
5
6 void init_log_tree_opt(struct log_tree_opt *opt)
7 {
8         memset(opt, 0, sizeof *opt);
9         opt->ignore_merges = 1;
10         opt->header_prefix = "";
11         opt->commit_format = CMIT_FMT_RAW;
12         diff_setup(&opt->diffopt);
13 }
14
15 int log_tree_opt_parse(struct log_tree_opt *opt, const char **av, int ac)
16 {
17         const char *arg;
18         int cnt = diff_opt_parse(&opt->diffopt, av, ac);
19         if (0 < cnt)
20                 return cnt;
21         arg = *av;
22         if (!strcmp(arg, "-r"))
23                 opt->diffopt.recursive = 1;
24         else if (!strcmp(arg, "-t")) {
25                 opt->diffopt.recursive = 1;
26                 opt->diffopt.tree_in_recursive = 1;
27         }
28         else if (!strcmp(arg, "-m"))
29                 opt->ignore_merges = 0;
30         else if (!strcmp(arg, "-c"))
31                 opt->combine_merges = 1;
32         else if (!strcmp(arg, "--cc")) {
33                 opt->dense_combined_merges = 1;
34                 opt->combine_merges = 1;
35         }
36         else if (!strcmp(arg, "-v")) {
37                 opt->verbose_header = 1;
38                 opt->header_prefix = "diff-tree ";
39         }
40         else if (!strncmp(arg, "--pretty", 8)) {
41                 opt->verbose_header = 1;
42                 opt->header_prefix = "diff-tree ";
43                 opt->commit_format = get_commit_format(arg+8);
44         }
45         else if (!strcmp(arg, "--root"))
46                 opt->show_root_diff = 1;
47         else if (!strcmp(arg, "--no-commit-id"))
48                 opt->no_commit_id = 1;
49         else if (!strcmp(arg, "--always"))
50                 opt->always_show_header = 1;
51         else
52                 return 0;
53         return 1;
54 }
55
56 int log_tree_diff_flush(struct log_tree_opt *opt)
57 {
58         diffcore_std(&opt->diffopt);
59         if (diff_queue_is_empty()) {
60                 int saved_fmt = opt->diffopt.output_format;
61                 opt->diffopt.output_format = DIFF_FORMAT_NO_OUTPUT;
62                 diff_flush(&opt->diffopt);
63                 opt->diffopt.output_format = saved_fmt;
64                 return 0;
65         }
66         if (opt->header) {
67                 if (!opt->no_commit_id)
68                         printf("%s%c", opt->header,
69                                opt->diffopt.line_termination);
70                 opt->header = NULL;
71         }
72         diff_flush(&opt->diffopt);
73         return 1;
74 }
75
76 static int diff_root_tree(struct log_tree_opt *opt,
77                           const unsigned char *new, const char *base)
78 {
79         int retval;
80         void *tree;
81         struct tree_desc empty, real;
82
83         tree = read_object_with_reference(new, tree_type, &real.size, NULL);
84         if (!tree)
85                 die("unable to read root tree (%s)", sha1_to_hex(new));
86         real.buf = tree;
87
88         empty.buf = "";
89         empty.size = 0;
90         retval = diff_tree(&empty, &real, base, &opt->diffopt);
91         free(tree);
92         log_tree_diff_flush(opt);
93         return retval;
94 }
95
96 static const char *generate_header(struct log_tree_opt *opt,
97                                    const unsigned char *commit_sha1,
98                                    const unsigned char *parent_sha1,
99                                    const struct commit *commit)
100 {
101         static char this_header[16384];
102         int offset;
103         unsigned long len;
104         int abbrev = opt->diffopt.abbrev;
105         const char *msg = commit->buffer;
106
107         if (!opt->verbose_header)
108                 return sha1_to_hex(commit_sha1);
109
110         len = strlen(msg);
111
112         offset = sprintf(this_header, "%s%s ",
113                          opt->header_prefix,
114                          diff_unique_abbrev(commit_sha1, abbrev));
115         if (commit_sha1 != parent_sha1)
116                 offset += sprintf(this_header + offset, "(from %s)\n",
117                                   parent_sha1
118                                   ? diff_unique_abbrev(parent_sha1, abbrev)
119                                   : "root");
120         else
121                 offset += sprintf(this_header + offset, "(from parents)\n");
122         offset += pretty_print_commit(opt->commit_format, commit, len,
123                                       this_header + offset,
124                                       sizeof(this_header) - offset, abbrev);
125         if (opt->always_show_header) {
126                 puts(this_header);
127                 return NULL;
128         }
129         return this_header;
130 }
131
132 static int do_diff_combined(struct log_tree_opt *opt, struct commit *commit)
133 {
134         unsigned const char *sha1 = commit->object.sha1;
135
136         opt->header = generate_header(opt, sha1, sha1, commit);
137         opt->header = diff_tree_combined_merge(sha1, opt->header,
138                                                 opt->dense_combined_merges,
139                                                 &opt->diffopt);
140         if (!opt->header && opt->verbose_header)
141                 opt->header_prefix = "\ndiff-tree ";
142         return 0;
143 }
144
145 int log_tree_commit(struct log_tree_opt *opt, struct commit *commit)
146 {
147         struct commit_list *parents;
148         unsigned const char *sha1 = commit->object.sha1;
149
150         /* Root commit? */
151         if (opt->show_root_diff && !commit->parents) {
152                 opt->header = generate_header(opt, sha1, NULL, commit);
153                 diff_root_tree(opt, sha1, "");
154         }
155
156         /* More than one parent? */
157         if (commit->parents && commit->parents->next) {
158                 if (opt->ignore_merges)
159                         return 0;
160                 else if (opt->combine_merges)
161                         return do_diff_combined(opt, commit);
162         }
163
164         for (parents = commit->parents; parents; parents = parents->next) {
165                 struct commit *parent = parents->item;
166                 unsigned const char *psha1 = parent->object.sha1;
167                 opt->header = generate_header(opt, sha1, psha1, commit);
168                 diff_tree_sha1(psha1, sha1, "", &opt->diffopt);
169                 log_tree_diff_flush(opt);               
170
171                 if (!opt->header && opt->verbose_header)
172                         opt->header_prefix = "\ndiff-tree ";
173         }
174         return 0;
175 }