Split "git-pull-script" into two parts
[git/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 }