]>
Commit | Line | Data |
---|---|---|
d1df5743 JH |
1 | #include "cache.h" |
2 | #include "strbuf.h" | |
3 | #include "diff.h" | |
4 | ||
5 | static int matches_pathspec(const char *name, char **spec, int cnt) | |
6 | { | |
7 | int i; | |
8 | int namelen = strlen(name); | |
9 | for (i = 0; i < cnt; i++) { | |
10 | int speclen = strlen(spec[i]); | |
11 | if (! strncmp(spec[i], name, speclen) && | |
12 | speclen <= namelen && | |
13 | (name[speclen] == 0 || | |
14 | name[speclen] == '/')) | |
15 | return 1; | |
16 | } | |
17 | return 0; | |
18 | } | |
19 | ||
20 | static int parse_oneside_change(const char *cp, unsigned char *sha1, | |
21 | char *path) { | |
22 | int ch; | |
23 | while ((ch = *cp) && '0' <= ch && ch <= '7') | |
24 | cp++; /* skip mode bits */ | |
25 | if (strncmp(cp, "\tblob\t", 6)) | |
26 | return -1; | |
27 | cp += 6; | |
28 | if (get_sha1_hex(cp, sha1)) | |
29 | return -1; | |
30 | cp += 40; | |
31 | if (*cp++ != '\t') | |
32 | return -1; | |
33 | strcpy(path, cp); | |
34 | return 0; | |
35 | } | |
36 | ||
37 | #define STATUS_CACHED 0 /* cached and sha1 valid */ | |
38 | #define STATUS_ABSENT 1 /* diff-tree says old removed or new added */ | |
39 | #define STATUS_UNCACHED 2 /* diff-cache output: read from working tree */ | |
40 | ||
41 | static int parse_diff_tree_output(const char *buf, | |
42 | unsigned char *old_sha1, | |
43 | int *old_status, | |
44 | unsigned char *new_sha1, | |
45 | int *new_status, | |
46 | char *path) { | |
47 | const char *cp = buf; | |
48 | int ch; | |
49 | static unsigned char null_sha[20] = { 0, }; | |
50 | ||
51 | switch (*cp++) { | |
52 | case '+': | |
53 | *old_status = STATUS_ABSENT; | |
54 | *new_status = (memcmp(new_sha1, null_sha, sizeof(null_sha)) ? | |
55 | STATUS_CACHED : STATUS_UNCACHED); | |
56 | return parse_oneside_change(cp, new_sha1, path); | |
57 | case '-': | |
58 | *new_status = STATUS_ABSENT; | |
59 | *old_status = (memcmp(old_sha1, null_sha, sizeof(null_sha)) ? | |
60 | STATUS_CACHED : STATUS_UNCACHED); | |
61 | return parse_oneside_change(cp, old_sha1, path); | |
62 | case '*': | |
63 | break; | |
64 | default: | |
65 | return -1; | |
66 | } | |
67 | ||
68 | /* This is for '*' entries */ | |
69 | while ((ch = *cp) && ('0' <= ch && ch <= '7')) | |
70 | cp++; /* skip mode bits */ | |
71 | if (strncmp(cp, "->", 2)) | |
72 | return -1; | |
73 | cp += 2; | |
74 | while ((ch = *cp) && ('0' <= ch && ch <= '7')) | |
75 | cp++; /* skip mode bits */ | |
76 | if (strncmp(cp, "\tblob\t", 6)) | |
77 | return -1; | |
78 | cp += 6; | |
79 | if (get_sha1_hex(cp, old_sha1)) | |
80 | return -1; | |
81 | cp += 40; | |
82 | if (strncmp(cp, "->", 2)) | |
83 | return -1; | |
84 | cp += 2; | |
85 | if (get_sha1_hex(cp, new_sha1)) | |
86 | return -1; | |
87 | cp += 40; | |
88 | if (*cp++ != '\t') | |
89 | return -1; | |
90 | strcpy(path, cp); | |
91 | *old_status = (memcmp(old_sha1, null_sha, sizeof(null_sha)) ? | |
92 | STATUS_CACHED : STATUS_UNCACHED); | |
93 | *new_status = (memcmp(new_sha1, null_sha, sizeof(null_sha)) ? | |
94 | STATUS_CACHED : STATUS_UNCACHED); | |
95 | return 0; | |
96 | } | |
97 | ||
98 | static int sha1err(const char *path, const unsigned char *sha1) | |
99 | { | |
100 | return error("diff-tree-helper: unable to read sha1 file of %s (%s)", | |
101 | path, sha1_to_hex(sha1)); | |
102 | } | |
103 | ||
104 | static int fserr(const char *path) | |
105 | { | |
106 | return error("diff-tree-helper: unable to read file %s", path); | |
107 | } | |
108 | ||
109 | static char *map_whole_file(const char *path, unsigned long *size) { | |
110 | int fd; | |
111 | struct stat st; | |
112 | void *buf; | |
113 | ||
114 | if ((fd = open(path, O_RDONLY)) < 0) { | |
115 | error("diff-tree-helper: unable to read file %s", path); | |
116 | return 0; | |
117 | } | |
118 | if (fstat(fd, &st) < 0) { | |
119 | close(fd); | |
120 | error("diff-tree-helper: unable to stat file %s", path); | |
121 | return 0; | |
122 | } | |
123 | *size = st.st_size; | |
124 | buf = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); | |
125 | close(fd); | |
126 | return buf; | |
127 | } | |
128 | ||
129 | static int show_diff(const unsigned char *old_sha1, int old_status, | |
130 | const unsigned char *new_sha1, int new_status, | |
131 | const char *path, int reverse_diff) | |
132 | { | |
133 | char other[PATH_MAX]; | |
134 | unsigned long size; | |
135 | char type[20]; | |
136 | int fd; | |
137 | int reverse; | |
138 | void *blob = 0; | |
139 | const char *fs = 0; | |
140 | int need_unmap = 0; | |
141 | int need_unlink = 0; | |
142 | ||
143 | ||
144 | switch (old_status) { | |
145 | case STATUS_CACHED: | |
146 | blob = read_sha1_file(old_sha1, type, &size); | |
147 | if (! blob) | |
148 | return sha1err(path, old_sha1); | |
149 | ||
150 | switch (new_status) { | |
151 | case STATUS_CACHED: | |
152 | strcpy(other, ".diff_tree_helper_XXXXXX"); | |
153 | fd = mkstemp(other); | |
154 | if (fd < 0) | |
155 | die("unable to create temp-file"); | |
156 | if (write(fd, blob, size) != size) | |
157 | die("unable to write temp-file"); | |
158 | close(fd); | |
159 | free(blob); | |
160 | ||
161 | blob = read_sha1_file(new_sha1, type, &size); | |
162 | if (! blob) | |
163 | return sha1err(path, new_sha1); | |
164 | ||
165 | need_unlink = 1; | |
166 | /* new = blob, old = fs */ | |
167 | reverse = !reverse_diff; | |
168 | fs = other; | |
169 | break; | |
170 | ||
171 | case STATUS_ABSENT: | |
172 | case STATUS_UNCACHED: | |
173 | fs = ((new_status == STATUS_ABSENT) ? | |
174 | "/dev/null" : path); | |
175 | reverse = reverse_diff; | |
176 | break; | |
177 | ||
178 | default: | |
179 | reverse = reverse_diff; | |
180 | } | |
181 | break; | |
182 | ||
183 | case STATUS_ABSENT: | |
184 | switch (new_status) { | |
185 | case STATUS_CACHED: | |
186 | blob = read_sha1_file(new_sha1, type, &size); | |
187 | if (! blob) | |
188 | return sha1err(path, new_sha1); | |
189 | /* old = fs, new = blob */ | |
190 | fs = "/dev/null"; | |
191 | reverse = !reverse_diff; | |
192 | break; | |
193 | ||
194 | case STATUS_ABSENT: | |
195 | return error("diff-tree-helper: absent from both old and new?"); | |
196 | case STATUS_UNCACHED: | |
197 | fs = path; | |
198 | blob = strdup(""); | |
199 | size = 0; | |
200 | /* old = blob, new = fs */ | |
201 | reverse = reverse_diff; | |
202 | break; | |
203 | default: | |
204 | reverse = reverse_diff; | |
205 | } | |
206 | break; | |
207 | ||
208 | case STATUS_UNCACHED: | |
209 | fs = path; /* old = fs, new = blob */ | |
210 | reverse = !reverse_diff; | |
211 | ||
212 | switch (new_status) { | |
213 | case STATUS_CACHED: | |
214 | blob = read_sha1_file(new_sha1, type, &size); | |
215 | if (! blob) | |
216 | return sha1err(path, new_sha1); | |
217 | break; | |
218 | ||
219 | case STATUS_ABSENT: | |
220 | blob = strdup(""); | |
221 | size = 0; | |
222 | break; | |
223 | ||
224 | case STATUS_UNCACHED: | |
225 | /* old = fs */ | |
226 | blob = map_whole_file(path, &size); | |
227 | if (! blob) | |
228 | return fserr(path); | |
229 | need_unmap = 1; | |
230 | break; | |
231 | default: | |
232 | reverse = reverse_diff; | |
233 | } | |
234 | break; | |
235 | ||
236 | default: | |
237 | reverse = reverse_diff; | |
238 | } | |
239 | ||
240 | if (fs) | |
241 | show_differences(fs, | |
242 | path, /* label */ | |
243 | blob, | |
244 | size, | |
245 | reverse /* 0: diff blob fs | |
246 | 1: diff fs blob */); | |
247 | ||
248 | if (need_unlink) | |
249 | unlink(other); | |
250 | if (need_unmap && blob) | |
251 | munmap(blob, size); | |
252 | else | |
253 | free(blob); | |
254 | return 0; | |
255 | } | |
256 | ||
257 | static const char *diff_tree_helper_usage = | |
258 | "diff-tree-helper [-R] [-z] paths..."; | |
259 | ||
260 | int main(int ac, char **av) { | |
261 | struct strbuf sb; | |
262 | int reverse_diff = 0; | |
263 | int line_termination = '\n'; | |
264 | ||
265 | strbuf_init(&sb); | |
266 | ||
267 | while (1 < ac && av[1][0] == '-') { | |
268 | if (av[1][1] == 'R') | |
269 | reverse_diff = 1; | |
270 | else if (av[1][1] == 'z') | |
271 | line_termination = 0; | |
272 | else | |
273 | usage(diff_tree_helper_usage); | |
274 | ac--; av++; | |
275 | } | |
276 | /* the remaining parameters are paths patterns */ | |
277 | ||
278 | prepare_diff_cmd(); | |
279 | ||
280 | while (1) { | |
281 | int old_status, new_status; | |
282 | unsigned char old_sha1[20], new_sha1[20]; | |
283 | char path[PATH_MAX]; | |
284 | read_line(&sb, stdin, line_termination); | |
285 | if (sb.eof) | |
286 | break; | |
287 | if (parse_diff_tree_output(sb.buf, | |
288 | old_sha1, &old_status, | |
289 | new_sha1, &new_status, | |
290 | path)) { | |
291 | fprintf(stderr, "cannot parse %s\n", sb.buf); | |
292 | continue; | |
293 | } | |
294 | if (1 < ac && ! matches_pathspec(path, av+1, ac-1)) | |
295 | continue; | |
296 | ||
297 | show_diff(old_sha1, old_status, | |
298 | new_sha1, new_status, | |
299 | path, reverse_diff); | |
300 | } | |
301 | return 0; | |
302 | } |