Update diff engine for symlinks stored in the cache.
[git.git] / diff-cache.c
1 #include "cache.h"
2 #include "diff.h"
3
4 static int cached_only = 0;
5 static int generate_patch = 0;
6 static int match_nonexisting = 0;
7 static int line_termination = '\n';
8
9 /* A file entry went away or appeared */
10 static void show_file(const char *prefix, struct cache_entry *ce, unsigned char *sha1, unsigned int mode)
11 {
12         if (generate_patch)
13                 diff_addremove(prefix[0], ntohl(mode), sha1, ce->name, NULL);
14         else
15                 printf("%s%06o\tblob\t%s\t%s%c", prefix, ntohl(mode),
16                        sha1_to_hex(sha1), ce->name, line_termination);
17 }
18
19 static int get_stat_data(struct cache_entry *ce, unsigned char **sha1p, unsigned int *modep)
20 {
21         unsigned char *sha1 = ce->sha1;
22         unsigned int mode = ce->ce_mode;
23
24         if (!cached_only) {
25                 static unsigned char no_sha1[20];
26                 int changed;
27                 struct stat st;
28                 if (lstat(ce->name, &st) < 0) {
29                         if (errno == ENOENT && match_nonexisting) {
30                                 *sha1p = sha1;
31                                 *modep = mode;
32                                 return 0;
33                         }
34                         return -1;
35                 }
36                 changed = cache_match_stat(ce, &st);
37                 if (changed) {
38                         mode = create_ce_mode(st.st_mode);
39                         sha1 = no_sha1;
40                 }
41         }
42
43         *sha1p = sha1;
44         *modep = mode;
45         return 0;
46 }
47
48 static void show_new_file(struct cache_entry *new)
49 {
50         unsigned char *sha1;
51         unsigned int mode;
52
53         /* New file in the index: it might actually be different in the working copy */
54         if (get_stat_data(new, &sha1, &mode) < 0)
55                 return;
56
57         show_file("+", new, sha1, mode);
58 }
59
60 static int show_modified(struct cache_entry *old, struct cache_entry *new)
61 {
62         unsigned int mode, oldmode;
63         unsigned char *sha1;
64         unsigned char old_sha1_hex[60];
65
66         if (get_stat_data(new, &sha1, &mode) < 0) {
67                 show_file("-", old, old->sha1, old->ce_mode);
68                 return -1;
69         }
70
71         oldmode = old->ce_mode;
72         if (mode == oldmode && !memcmp(sha1, old->sha1, 20))
73                 return 0;
74
75         mode = ntohl(mode);
76         oldmode = ntohl(oldmode);
77
78         if (generate_patch)
79                 diff_change(oldmode, mode,
80                             old->sha1, sha1, old->name, NULL);
81         else {
82                 strcpy(old_sha1_hex, sha1_to_hex(old->sha1));
83                 printf("*%06o->%06o\tblob\t%s->%s\t%s%c", oldmode, mode,
84                        old_sha1_hex, sha1_to_hex(sha1),
85                        old->name, line_termination);
86         }
87         return 0;
88 }
89
90 static int diff_cache(struct cache_entry **ac, int entries)
91 {
92         while (entries) {
93                 struct cache_entry *ce = *ac;
94                 int same = (entries > 1) && same_name(ce, ac[1]);
95
96                 switch (ce_stage(ce)) {
97                 case 0:
98                         /* No stage 1 entry? That means it's a new file */
99                         if (!same) {
100                                 show_new_file(ce);
101                                 break;
102                         }
103                         /* Show difference between old and new */
104                         show_modified(ac[1], ce);
105                         break;
106                 case 1:
107                         /* No stage 3 (merge) entry? That means it's been deleted */
108                         if (!same) {
109                                 show_file("-", ce, ce->sha1, ce->ce_mode);
110                                 break;
111                         }
112                         /* Otherwise we fall through to the "unmerged" case */
113                 case 3:
114                         if (generate_patch)
115                                 diff_unmerge(ce->name);
116                         else
117                                 printf("U %s%c", ce->name, line_termination);
118                         break;
119
120                 default:
121                         die("impossible cache entry stage");
122                 }
123
124                 /*
125                  * Ignore all the different stages for this file,
126                  * we've handled the relevant cases now.
127                  */
128                 do {
129                         ac++;
130                         entries--;
131                 } while (entries && same_name(ce, ac[0]));
132         }
133         return 0;
134 }
135
136 /*
137  * This turns all merge entries into "stage 3". That guarantees that
138  * when we read in the new tree (into "stage 1"), we won't lose sight
139  * of the fact that we had unmerged entries.
140  */
141 static void mark_merge_entries(void)
142 {
143         int i;
144         for (i = 0; i < active_nr; i++) {
145                 struct cache_entry *ce = active_cache[i];
146                 if (!ce_stage(ce))
147                         continue;
148                 ce->ce_flags |= htons(CE_STAGEMASK);
149         }
150 }
151
152 static char *diff_cache_usage =
153 "diff-cache [-r] [-z] [-p] [-i] [--cached] <tree sha1>";
154
155 int main(int argc, char **argv)
156 {
157         unsigned char tree_sha1[20];
158         void *tree;
159         unsigned long size;
160
161         read_cache();
162         while (argc > 2) {
163                 char *arg = argv[1];
164                 argv++;
165                 argc--;
166                 if (!strcmp(arg, "-r")) {
167                         /* We accept the -r flag just to look like diff-tree */
168                         continue;
169                 }
170                 if (!strcmp(arg, "-p")) {
171                         generate_patch = 1;
172                         continue;
173                 }
174                 if (!strcmp(arg, "-z")) {
175                         line_termination = '\0';
176                         continue;
177                 }
178                 if (!strcmp(arg, "-m")) {
179                         match_nonexisting = 1;
180                         continue;
181                 }
182                 if (!strcmp(arg, "--cached")) {
183                         cached_only = 1;
184                         continue;
185                 }
186                 usage(diff_cache_usage);
187         }
188
189         if (argc != 2 || get_sha1(argv[1], tree_sha1))
190                 usage(diff_cache_usage);
191
192         mark_merge_entries();
193
194         tree = read_object_with_reference(tree_sha1, "tree", &size, 0);
195         if (!tree)
196                 die("bad tree object %s", argv[1]);
197         if (read_tree(tree, size, 1))
198                 die("unable to read tree object %s", argv[1]);
199
200         return diff_cache(active_cache, active_nr);
201 }