1#include "cache.h"
2#include "diff.h"
34
static int cached_only = 0;
5static int diff_output_format = DIFF_FORMAT_HUMAN;
6static int match_nonexisting = 0;
7static int detect_rename = 0;
8static int find_copies_harder = 0;
9static int diff_setup_opt = 0;
10static int diff_score_opt = 0;
11static const char *pickaxe = NULL;
12static int pickaxe_opts = 0;
13static int diff_break_opt = -1;
14static const char *orderfile = NULL;
15static const char *diff_filter = NULL;
1617
/* A file entry went away or appeared */
18static void show_file(const char *prefix, struct cache_entry *ce, unsigned char *sha1, unsigned int mode)
19{
20diff_addremove(prefix[0], ntohl(mode), sha1, ce->name, NULL);
21}
2223
static int get_stat_data(struct cache_entry *ce, unsigned char **sha1p, unsigned int *modep)
24{
25unsigned char *sha1 = ce->sha1;
26unsigned int mode = ce->ce_mode;
2728
if (!cached_only) {
29static unsigned char no_sha1[20];
30int changed;
31struct stat st;
32if (lstat(ce->name, &st) < 0) {
33if (errno == ENOENT && match_nonexisting) {
34*sha1p = sha1;
35*modep = mode;
36return 0;
37}
38return -1;
39}
40changed = ce_match_stat(ce, &st);
41if (changed) {
42mode = create_ce_mode(st.st_mode);
43sha1 = no_sha1;
44}
45}
4647
*sha1p = sha1;
48*modep = mode;
49return 0;
50}
5152
static void show_new_file(struct cache_entry *new)
53{
54unsigned char *sha1;
55unsigned int mode;
5657
/* New file in the index: it might actually be different in the working copy */
58if (get_stat_data(new, &sha1, &mode) < 0)
59return;
6061
show_file("+", new, sha1, mode);
62}
6364
static int show_modified(struct cache_entry *old,
65struct cache_entry *new,
66int report_missing)
67{
68unsigned int mode, oldmode;
69unsigned char *sha1;
7071
if (get_stat_data(new, &sha1, &mode) < 0) {
72if (report_missing)
73show_file("-", old, old->sha1, old->ce_mode);
74return -1;
75}
7677
oldmode = old->ce_mode;
78if (mode == oldmode && !memcmp(sha1, old->sha1, 20) &&
79!find_copies_harder)
80return 0;
8182
mode = ntohl(mode);
83oldmode = ntohl(oldmode);
8485
diff_change(oldmode, mode,
86old->sha1, sha1, old->name, NULL);
87return 0;
88}
8990
static int diff_cache(struct cache_entry **ac, int entries)
91{
92while (entries) {
93struct cache_entry *ce = *ac;
94int same = (entries > 1) && ce_same_name(ce, ac[1]);
9596
switch (ce_stage(ce)) {
97case 0:
98/* No stage 1 entry? That means it's a new file */
99if (!same) {
100show_new_file(ce);
101break;
102}
103/* Show difference between old and new */
104show_modified(ac[1], ce, 1);
105break;
106case 1:
107/* No stage 3 (merge) entry? That means it's been deleted */
108if (!same) {
109show_file("-", ce, ce->sha1, ce->ce_mode);
110break;
111}
112/* We come here with ce pointing at stage 1
113* (original tree) and ac[1] pointing at stage
114* 3 (unmerged). show-modified with
115* report-mising set to false does not say the
116* file is deleted but reports true if work
117* tree does not have it, in which case we
118* fall through to report the unmerged state.
119* Otherwise, we show the differences between
120* the original tree and the work tree.
121*/
122if (!cached_only && !show_modified(ce, ac[1], 0))
123break;
124/* fallthru */
125case 3:
126diff_unmerge(ce->name);
127break;
128129
default:
130die("impossible cache entry stage");
131}
132133
/*
134* Ignore all the different stages for this file,
135* we've handled the relevant cases now.
136*/
137do {
138ac++;
139entries--;
140} while (entries && ce_same_name(ce, ac[0]));
141}
142return 0;
143}
144145
/*
146* This turns all merge entries into "stage 3". That guarantees that
147* when we read in the new tree (into "stage 1"), we won't lose sight
148* of the fact that we had unmerged entries.
149*/
150static void mark_merge_entries(void)
151{
152int i;
153for (i = 0; i < active_nr; i++) {
154struct cache_entry *ce = active_cache[i];
155if (!ce_stage(ce))
156continue;
157ce->ce_flags |= htons(CE_STAGEMASK);
158}
159}
160161
static char *diff_cache_usage =
162"git-diff-cache [-m] [--cached] "
163"[<common diff options>] <tree-ish> [<path>...]"
164COMMON_DIFF_OPTIONS_HELP;
165166
int main(int argc, const char **argv)
167{
168const char *tree_name = NULL;
169unsigned char sha1[20];
170const char **pathspec = NULL;
171void *tree;
172unsigned long size;
173int ret;
174int allow_options = 1;
175int i;
176177
read_cache();
178for (i = 1; i < argc; i++) {
179const char *arg = argv[i];
180181
if (!allow_options || *arg != '-') {
182if (tree_name) {
183pathspec = argv + i;
184break;
185}
186tree_name = arg;
187continue;
188}
189190
if (!strcmp(arg, "--")) {
191allow_options = 0;
192continue;
193}
194if (!strcmp(arg, "-r")) {
195/* We accept the -r flag just to look like git-diff-tree */
196continue;
197}
198/* We accept the -u flag as a synonym for "-p" */
199if (!strcmp(arg, "-p") || !strcmp(arg, "-u")) {
200diff_output_format = DIFF_FORMAT_PATCH;
201continue;
202}
203if (!strncmp(arg, "-B", 2)) {
204if ((diff_break_opt = diff_scoreopt_parse(arg)) == -1)
205usage(diff_cache_usage);
206continue;
207}
208if (!strncmp(arg, "-M", 2)) {
209detect_rename = DIFF_DETECT_RENAME;
210if ((diff_score_opt = diff_scoreopt_parse(arg)) == -1)
211usage(diff_cache_usage);
212continue;
213}
214if (!strncmp(arg, "-C", 2)) {
215detect_rename = DIFF_DETECT_COPY;
216if ((diff_score_opt = diff_scoreopt_parse(arg)) == -1)
217usage(diff_cache_usage);
218continue;
219}
220if (!strcmp(arg, "--find-copies-harder")) {
221find_copies_harder = 1;
222continue;
223}
224if (!strcmp(arg, "-z")) {
225diff_output_format = DIFF_FORMAT_MACHINE;
226continue;
227}
228if (!strcmp(arg, "--name-only")) {
229diff_output_format = DIFF_FORMAT_NAME;
230continue;
231}
232if (!strcmp(arg, "--name-only-z")) {
233diff_output_format = DIFF_FORMAT_NAME_Z;
234continue;
235}
236if (!strcmp(arg, "-R")) {
237diff_setup_opt |= DIFF_SETUP_REVERSE;
238continue;
239}
240if (!strncmp(arg, "-S", 2)) {
241pickaxe = arg + 2;
242continue;
243}
244if (!strncmp(arg, "--diff-filter=", 14)) {
245diff_filter = arg + 14;
246continue;
247}
248if (!strncmp(arg, "-O", 2)) {
249orderfile = arg + 2;
250continue;
251}
252if (!strcmp(arg, "--pickaxe-all")) {
253pickaxe_opts = DIFF_PICKAXE_ALL;
254continue;
255}
256if (!strcmp(arg, "-m")) {
257match_nonexisting = 1;
258continue;
259}
260if (!strcmp(arg, "--cached")) {
261cached_only = 1;
262continue;
263}
264usage(diff_cache_usage);
265}
266267
if (find_copies_harder && detect_rename != DIFF_DETECT_COPY)
268usage(diff_cache_usage);
269270
if (!tree_name || get_sha1(tree_name, sha1))
271usage(diff_cache_usage);
272273
/* The rest is for paths restriction. */
274diff_setup(diff_setup_opt);
275276
mark_merge_entries();
277278
tree = read_object_with_reference(sha1, "tree", &size, NULL);
279if (!tree)
280die("bad tree object %s", tree_name);
281if (read_tree(tree, size, 1, NULL))
282die("unable to read tree object %s", tree_name);
283284
ret = diff_cache(active_cache, active_nr);
285286
diffcore_std(pathspec ? : NULL,
287detect_rename, diff_score_opt,
288pickaxe, pickaxe_opts,
289diff_break_opt,
290orderfile, diff_filter);
291diff_flush(diff_output_format);
292return ret;
293}