]> git.ipfire.org Git - thirdparty/git.git/blame - diff-index.c
blame.c: fix completely broken ancestry traversal.
[thirdparty/git.git] / diff-index.c
CommitLineData
e74f8f6a 1#include "cache.h"
521698b1 2#include "tree.h"
f92a4465 3#include "diff.h"
e74f8f6a
LT
4
5static int cached_only = 0;
160c8433 6static int match_nonexisting = 0;
6b5ee137 7static struct diff_options diff_options;
e74f8f6a 8
e74f8f6a 9/* A file entry went away or appeared */
6b5ee137
JH
10static void show_file(const char *prefix,
11 struct cache_entry *ce,
12 unsigned char *sha1, unsigned int mode)
e74f8f6a 13{
6b5ee137
JH
14 diff_addremove(&diff_options, prefix[0], ntohl(mode),
15 sha1, ce->name, NULL);
e74f8f6a
LT
16}
17
6b5ee137 18static int get_stat_data(struct cache_entry *ce,
3e09cdfd 19 unsigned char ** sha1p, unsigned int *modep)
e74f8f6a 20{
c9cddabe
LT
21 unsigned char *sha1 = ce->sha1;
22 unsigned int mode = ce->ce_mode;
e74f8f6a
LT
23
24 if (!cached_only) {
25 static unsigned char no_sha1[20];
b5af9107 26 int changed;
e74f8f6a 27 struct stat st;
160c8433
LT
28 if (lstat(ce->name, &st) < 0) {
29 if (errno == ENOENT && match_nonexisting) {
30 *sha1p = sha1;
31 *modep = mode;
32 return 0;
33 }
e74f8f6a 34 return -1;
160c8433 35 }
5f73076c 36 changed = ce_match_stat(ce, &st, 0);
e74f8f6a 37 if (changed) {
c9cddabe 38 mode = create_ce_mode(st.st_mode);
e464f4c3 39 if (!trust_executable_bit && S_ISREG(st.st_mode))
3e09cdfd 40 mode = ce->ce_mode;
b5af9107 41 sha1 = no_sha1;
e74f8f6a
LT
42 }
43 }
44
c9cddabe
LT
45 *sha1p = sha1;
46 *modep = mode;
47 return 0;
48}
49
b836825b 50static void show_new_file(struct cache_entry *new)
c9cddabe
LT
51{
52 unsigned char *sha1;
53 unsigned int mode;
54
3e09cdfd
JH
55 /* New file in the index: it might actually be different in
56 * the working copy.
57 */
c9cddabe 58 if (get_stat_data(new, &sha1, &mode) < 0)
b836825b 59 return;
c9cddabe
LT
60
61 show_file("+", new, sha1, mode);
62}
63
66026590
JH
64static int show_modified(struct cache_entry *old,
65 struct cache_entry *new,
66 int report_missing)
c9cddabe
LT
67{
68 unsigned int mode, oldmode;
69 unsigned char *sha1;
c9cddabe
LT
70
71 if (get_stat_data(new, &sha1, &mode) < 0) {
66026590
JH
72 if (report_missing)
73 show_file("-", old, old->sha1, old->ce_mode);
c9cddabe
LT
74 return -1;
75 }
76
77 oldmode = old->ce_mode;
c3e7fbcb 78 if (mode == oldmode && !memcmp(sha1, old->sha1, 20) &&
6b5ee137 79 !diff_options.find_copies_harder)
e74f8f6a
LT
80 return 0;
81
c9cddabe
LT
82 mode = ntohl(mode);
83 oldmode = ntohl(oldmode);
84
6b5ee137 85 diff_change(&diff_options, oldmode, mode,
57fe64a4 86 old->sha1, sha1, old->name, NULL);
e74f8f6a
LT
87 return 0;
88}
89
fdee7d07 90static int diff_cache(struct cache_entry **ac, int entries, const char **pathspec)
e74f8f6a 91{
b5af9107
LT
92 while (entries) {
93 struct cache_entry *ce = *ac;
dbbce55b 94 int same = (entries > 1) && ce_same_name(ce, ac[1]);
e74f8f6a 95
fdee7d07
LT
96 if (!ce_path_match(ce, pathspec))
97 goto skip_entry;
98
b0fe89ca
LT
99 switch (ce_stage(ce)) {
100 case 0:
101 /* No stage 1 entry? That means it's a new file */
102 if (!same) {
c9cddabe 103 show_new_file(ce);
b0fe89ca
LT
104 break;
105 }
106 /* Show difference between old and new */
66026590 107 show_modified(ac[1], ce, 1);
b0fe89ca
LT
108 break;
109 case 1:
110 /* No stage 3 (merge) entry? That means it's been deleted */
111 if (!same) {
c9cddabe 112 show_file("-", ce, ce->sha1, ce->ce_mode);
b0fe89ca
LT
113 break;
114 }
66026590
JH
115 /* We come here with ce pointing at stage 1
116 * (original tree) and ac[1] pointing at stage
117 * 3 (unmerged). show-modified with
82f9d58a 118 * report-missing set to false does not say the
66026590
JH
119 * file is deleted but reports true if work
120 * tree does not have it, in which case we
121 * fall through to report the unmerged state.
122 * Otherwise, we show the differences between
123 * the original tree and the work tree.
124 */
125 if (!cached_only && !show_modified(ce, ac[1], 0))
126 break;
127 /* fallthru */
b0fe89ca 128 case 3:
6b5ee137 129 diff_unmerge(&diff_options, ce->name);
b0fe89ca
LT
130 break;
131
132 default:
133 die("impossible cache entry stage");
e74f8f6a 134 }
b0fe89ca 135
fdee7d07 136skip_entry:
b0fe89ca
LT
137 /*
138 * Ignore all the different stages for this file,
139 * we've handled the relevant cases now.
140 */
141 do {
e74f8f6a
LT
142 ac++;
143 entries--;
dbbce55b 144 } while (entries && ce_same_name(ce, ac[0]));
e74f8f6a
LT
145 }
146 return 0;
147}
148
b0fe89ca
LT
149/*
150 * This turns all merge entries into "stage 3". That guarantees that
151 * when we read in the new tree (into "stage 1"), we won't lose sight
152 * of the fact that we had unmerged entries.
153 */
154static void mark_merge_entries(void)
b5af9107
LT
155{
156 int i;
157 for (i = 0; i < active_nr; i++) {
158 struct cache_entry *ce = active_cache[i];
159 if (!ce_stage(ce))
5697ecc7 160 continue;
b0fe89ca 161 ce->ce_flags |= htons(CE_STAGEMASK);
b5af9107
LT
162 }
163}
164
4d1f1190 165static const char diff_cache_usage[] =
215a7ad1 166"git-diff-index [-m] [--cached] "
dda2d79a
JH
167"[<common diff options>] <tree-ish> [<path>...]"
168COMMON_DIFF_OPTIONS_HELP;
c5bac17a 169
6b5ee137 170int main(int argc, const char **argv)
e74f8f6a 171{
6c56c534
LT
172 const char *tree_name = NULL;
173 unsigned char sha1[20];
d288a700 174 const char *prefix = setup_git_directory();
6c56c534 175 const char **pathspec = NULL;
521698b1 176 struct tree *tree;
5c97558c 177 int ret;
e0f0e891 178 int allow_options = 1;
6c56c534 179 int i;
e74f8f6a 180
9ce392f4 181 git_config(git_diff_config);
6b5ee137 182 diff_setup(&diff_options);
6c56c534
LT
183 for (i = 1; i < argc; i++) {
184 const char *arg = argv[i];
6b5ee137 185 int diff_opt_cnt;
6c56c534 186
e0f0e891 187 if (!allow_options || *arg != '-') {
d288a700 188 if (tree_name)
6c56c534 189 break;
6c56c534
LT
190 tree_name = arg;
191 continue;
192 }
193
e0f0e891
JF
194 if (!strcmp(arg, "--")) {
195 allow_options = 0;
196 continue;
197 }
e74f8f6a 198 if (!strcmp(arg, "-r")) {
667bb59b 199 /* We accept the -r flag just to look like git-diff-tree */
e74f8f6a
LT
200 continue;
201 }
4abd8964
JH
202 if (!strcmp(arg, "--cc"))
203 /*
204 * I _think_ "diff-index --cached HEAD" with an
205 * unmerged index could show something else
206 * later, but pretend --cc is the same as -p for
207 * now. "git diff" uses --cc by default.
208 */
209 argv[i] = arg = "-p";
6b5ee137
JH
210 diff_opt_cnt = diff_opt_parse(&diff_options, argv + i,
211 argc - i);
212 if (diff_opt_cnt < 0)
213 usage(diff_cache_usage);
214 else if (diff_opt_cnt) {
215 i += diff_opt_cnt - 1;
367cec1c
JH
216 continue;
217 }
6b5ee137 218
160c8433
LT
219 if (!strcmp(arg, "-m")) {
220 match_nonexisting = 1;
221 continue;
222 }
e74f8f6a
LT
223 if (!strcmp(arg, "--cached")) {
224 cached_only = 1;
225 continue;
226 }
c5bac17a 227 usage(diff_cache_usage);
e74f8f6a
LT
228 }
229
d288a700
LT
230 pathspec = get_pathspec(prefix, argv + i);
231
6b5ee137 232 if (diff_setup_done(&diff_options) < 0)
4727f640
JH
233 usage(diff_cache_usage);
234
6c56c534 235 if (!tree_name || get_sha1(tree_name, sha1))
c5bac17a 236 usage(diff_cache_usage);
e74f8f6a 237
d288a700
LT
238 read_cache();
239
b0fe89ca 240 mark_merge_entries();
b5af9107 241
521698b1 242 tree = parse_tree_indirect(sha1);
e74f8f6a 243 if (!tree)
6c56c534 244 die("bad tree object %s", tree_name);
521698b1 245 if (read_tree(tree, 1, pathspec))
6c56c534 246 die("unable to read tree object %s", tree_name);
e74f8f6a 247
fdee7d07 248 ret = diff_cache(active_cache, active_nr, pathspec);
f345b0a0 249
6b5ee137
JH
250 diffcore_std(&diff_options);
251 diff_flush(&diff_options);
5c97558c 252 return ret;
e74f8f6a 253}