]>
Commit | Line | Data |
---|---|---|
9174026c | 1 | #include "cache.h" |
3ebfd4aa | 2 | #include "diff.h" |
9174026c | 3 | |
73134b6d | 4 | static int recursive = 0; |
6cbd72f8 | 5 | static int line_termination = '\n'; |
3ebfd4aa | 6 | static int generate_patch = 0; |
9174026c | 7 | |
c5b42386 LT |
8 | // What paths are we interested in? |
9 | static int nr_paths = 0; | |
10 | static char **paths = NULL; | |
11 | static int *pathlens = NULL; | |
12 | ||
eeb79916 | 13 | static int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const char *base); |
73134b6d LT |
14 | |
15 | static void update_tree_entry(void **bufp, unsigned long *sizep) | |
9174026c LT |
16 | { |
17 | void *buf = *bufp; | |
18 | unsigned long size = *sizep; | |
19 | int len = strlen(buf) + 1 + 20; | |
20 | ||
21 | if (size < len) | |
2de381f9 | 22 | die("corrupt tree file"); |
9174026c LT |
23 | *bufp = buf + len; |
24 | *sizep = size - len; | |
9174026c LT |
25 | } |
26 | ||
27 | static const unsigned char *extract(void *tree, unsigned long size, const char **pathp, unsigned int *modep) | |
28 | { | |
29 | int len = strlen(tree)+1; | |
30 | const unsigned char *sha1 = tree + len; | |
31 | const char *path = strchr(tree, ' '); | |
32 | ||
33 | if (!path || size < len + 20 || sscanf(tree, "%o", modep) != 1) | |
2de381f9 | 34 | die("corrupt tree file"); |
9174026c LT |
35 | *pathp = path+1; |
36 | return sha1; | |
37 | } | |
38 | ||
262e82b4 LT |
39 | static char *malloc_base(const char *base, const char *path, int pathlen) |
40 | { | |
41 | int baselen = strlen(base); | |
812666c8 | 42 | char *newbase = xmalloc(baselen + pathlen + 2); |
262e82b4 LT |
43 | memcpy(newbase, base, baselen); |
44 | memcpy(newbase + baselen, path, pathlen); | |
45 | memcpy(newbase + baselen + pathlen, "/", 2); | |
46 | return newbase; | |
47 | } | |
48 | ||
49 | static void show_file(const char *prefix, void *tree, unsigned long size, const char *base); | |
50 | ||
51 | /* A whole sub-tree went away or appeared */ | |
52 | static void show_tree(const char *prefix, void *tree, unsigned long size, const char *base) | |
53 | { | |
54 | while (size) { | |
55 | show_file(prefix, tree, size, base); | |
56 | update_tree_entry(&tree, &size); | |
57 | } | |
58 | } | |
59 | ||
60 | /* A file entry went away or appeared */ | |
73134b6d | 61 | static void show_file(const char *prefix, void *tree, unsigned long size, const char *base) |
9174026c LT |
62 | { |
63 | unsigned mode; | |
64 | const char *path; | |
65 | const unsigned char *sha1 = extract(tree, size, &path, &mode); | |
262e82b4 LT |
66 | |
67 | if (recursive && S_ISDIR(mode)) { | |
68 | char type[20]; | |
69 | unsigned long size; | |
70 | char *newbase = malloc_base(base, path, strlen(path)); | |
71 | void *tree; | |
72 | ||
73 | tree = read_sha1_file(sha1, type, &size); | |
74 | if (!tree || strcmp(type, "tree")) | |
2de381f9 | 75 | die("corrupt tree sha %s", sha1_to_hex(sha1)); |
262e82b4 LT |
76 | |
77 | show_tree(prefix, tree, size, newbase); | |
78 | ||
79 | free(tree); | |
80 | free(newbase); | |
81 | return; | |
82 | } | |
83 | ||
3ebfd4aa JH |
84 | if (generate_patch) { |
85 | if (!S_ISDIR(mode)) | |
86 | diff_addremove(prefix[0], mode, sha1, base, path); | |
87 | } | |
88 | else | |
89 | printf("%s%06o\t%s\t%s\t%s%s%c", prefix, mode, | |
90 | S_ISDIR(mode) ? "tree" : "blob", | |
91 | sha1_to_hex(sha1), base, path, | |
92 | line_termination); | |
9174026c LT |
93 | } |
94 | ||
eeb79916 | 95 | static int compare_tree_entry(void *tree1, unsigned long size1, void *tree2, unsigned long size2, const char *base) |
9174026c LT |
96 | { |
97 | unsigned mode1, mode2; | |
98 | const char *path1, *path2; | |
99 | const unsigned char *sha1, *sha2; | |
73134b6d | 100 | int cmp, pathlen1, pathlen2; |
eeb79916 | 101 | char old_sha1_hex[50]; |
9174026c LT |
102 | |
103 | sha1 = extract(tree1, size1, &path1, &mode1); | |
104 | sha2 = extract(tree2, size2, &path2, &mode2); | |
105 | ||
73134b6d LT |
106 | pathlen1 = strlen(path1); |
107 | pathlen2 = strlen(path2); | |
108 | cmp = cache_name_compare(path1, pathlen1, path2, pathlen2); | |
9174026c | 109 | if (cmp < 0) { |
eeb79916 | 110 | show_file("-", tree1, size1, base); |
9174026c LT |
111 | return -1; |
112 | } | |
113 | if (cmp > 0) { | |
eeb79916 | 114 | show_file("+", tree2, size2, base); |
9174026c LT |
115 | return 1; |
116 | } | |
117 | if (!memcmp(sha1, sha2, 20) && mode1 == mode2) | |
118 | return 0; | |
262e82b4 LT |
119 | |
120 | /* | |
121 | * If the filemode has changed to/from a directory from/to a regular | |
aebb2679 | 122 | * file, we need to consider it a remove and an add. |
262e82b4 LT |
123 | */ |
124 | if (S_ISDIR(mode1) != S_ISDIR(mode2)) { | |
125 | show_file("-", tree1, size1, base); | |
126 | show_file("+", tree2, size2, base); | |
127 | return 0; | |
128 | } | |
129 | ||
130 | if (recursive && S_ISDIR(mode1)) { | |
eeb79916 | 131 | int retval; |
262e82b4 | 132 | char *newbase = malloc_base(base, path1, pathlen1); |
eeb79916 LT |
133 | retval = diff_tree_sha1(sha1, sha2, newbase); |
134 | free(newbase); | |
135 | return retval; | |
73134b6d LT |
136 | } |
137 | ||
3ebfd4aa JH |
138 | if (generate_patch) { |
139 | if (!S_ISDIR(mode1)) | |
140 | diff_change(mode1, mode2, sha1, sha2, base, path1); | |
141 | } | |
142 | else { | |
143 | strcpy(old_sha1_hex, sha1_to_hex(sha1)); | |
144 | printf("*%06o->%06o\t%s\t%s->%s\t%s%s%c", mode1, mode2, | |
145 | S_ISDIR(mode1) ? "tree" : "blob", | |
146 | old_sha1_hex, sha1_to_hex(sha2), base, path1, | |
147 | line_termination); | |
148 | } | |
9174026c LT |
149 | return 0; |
150 | } | |
151 | ||
c5b42386 LT |
152 | static int interesting(void *tree, unsigned long size, const char *base) |
153 | { | |
154 | const char *path; | |
155 | unsigned mode; | |
156 | int i; | |
157 | int baselen, pathlen; | |
158 | ||
159 | if (!nr_paths) | |
160 | return 1; | |
161 | ||
162 | (void)extract(tree, size, &path, &mode); | |
163 | ||
164 | pathlen = strlen(path); | |
165 | baselen = strlen(base); | |
166 | ||
167 | for (i=0; i < nr_paths; i++) { | |
168 | const char *match = paths[i]; | |
169 | int matchlen = pathlens[i]; | |
170 | ||
171 | if (baselen >= matchlen) { | |
172 | /* If it doesn't match, move along... */ | |
173 | if (strncmp(base, match, matchlen)) | |
174 | continue; | |
175 | ||
176 | /* The base is a subdirectory of a path which was specified. */ | |
177 | return 1; | |
178 | } | |
179 | ||
180 | /* Does the base match? */ | |
181 | if (strncmp(base, match, baselen)) | |
182 | continue; | |
183 | ||
184 | match += baselen; | |
185 | matchlen -= baselen; | |
186 | ||
187 | if (pathlen > matchlen) | |
188 | continue; | |
189 | ||
190 | if (strncmp(path, match, pathlen)) | |
191 | continue; | |
192 | ||
193 | return 1; | |
194 | } | |
195 | return 0; /* No matches */ | |
196 | } | |
197 | ||
eeb79916 | 198 | static int diff_tree(void *tree1, unsigned long size1, void *tree2, unsigned long size2, const char *base) |
9174026c LT |
199 | { |
200 | while (size1 | size2) { | |
c5b42386 LT |
201 | if (nr_paths && size1 && !interesting(tree1, size1, base)) { |
202 | update_tree_entry(&tree1, &size1); | |
203 | continue; | |
204 | } | |
205 | if (nr_paths && size2 && !interesting(tree2, size2, base)) { | |
206 | update_tree_entry(&tree2, &size2); | |
207 | continue; | |
208 | } | |
9174026c | 209 | if (!size1) { |
eeb79916 | 210 | show_file("+", tree2, size2, base); |
9174026c LT |
211 | update_tree_entry(&tree2, &size2); |
212 | continue; | |
213 | } | |
214 | if (!size2) { | |
eeb79916 | 215 | show_file("-", tree1, size1, base); |
9174026c LT |
216 | update_tree_entry(&tree1, &size1); |
217 | continue; | |
218 | } | |
eeb79916 | 219 | switch (compare_tree_entry(tree1, size1, tree2, size2, base)) { |
9174026c LT |
220 | case -1: |
221 | update_tree_entry(&tree1, &size1); | |
222 | continue; | |
223 | case 0: | |
224 | update_tree_entry(&tree1, &size1); | |
225 | /* Fallthrough */ | |
226 | case 1: | |
227 | update_tree_entry(&tree2, &size2); | |
228 | continue; | |
229 | } | |
2de381f9 | 230 | die("diff-tree: internal error"); |
9174026c LT |
231 | } |
232 | return 0; | |
233 | } | |
234 | ||
eeb79916 | 235 | static int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const char *base) |
9174026c | 236 | { |
9174026c LT |
237 | void *tree1, *tree2; |
238 | unsigned long size1, size2; | |
73134b6d | 239 | int retval; |
9174026c | 240 | |
40469ee9 | 241 | tree1 = read_object_with_reference(old, "tree", &size1, 0); |
c1fdf2a6 | 242 | if (!tree1) |
2de381f9 | 243 | die("unable to read source tree (%s)", sha1_to_hex(old)); |
40469ee9 | 244 | tree2 = read_object_with_reference(new, "tree", &size2, 0); |
c1fdf2a6 | 245 | if (!tree2) |
2de381f9 | 246 | die("unable to read destination tree (%s)", sha1_to_hex(new)); |
eeb79916 | 247 | retval = diff_tree(tree1, size1, tree2, size2, base); |
73134b6d LT |
248 | free(tree1); |
249 | free(tree2); | |
250 | return retval; | |
251 | } | |
252 | ||
c5bac17a JH |
253 | static char *diff_tree_usage = "diff-tree [-r] [-z] <tree sha1> <tree sha1>"; |
254 | ||
73134b6d LT |
255 | int main(int argc, char **argv) |
256 | { | |
257 | unsigned char old[20], new[20]; | |
258 | ||
c5b42386 | 259 | for (;;) { |
73134b6d | 260 | char *arg = argv[1]; |
c5b42386 LT |
261 | |
262 | if (!arg || *arg != '-') | |
263 | break; | |
264 | ||
73134b6d LT |
265 | argv++; |
266 | argc--; | |
bf16c71e | 267 | if (!strcmp(arg, "-r")) { |
73134b6d LT |
268 | recursive = 1; |
269 | continue; | |
270 | } | |
3ebfd4aa | 271 | if (!strcmp(arg, "-p")) { |
3a663fd9 | 272 | recursive = generate_patch = 1; |
3ebfd4aa JH |
273 | continue; |
274 | } | |
6cbd72f8 LT |
275 | if (!strcmp(arg, "-z")) { |
276 | line_termination = '\0'; | |
277 | continue; | |
278 | } | |
c5bac17a | 279 | usage(diff_tree_usage); |
73134b6d LT |
280 | } |
281 | ||
3c249c95 | 282 | if (argc < 3 || get_sha1(argv[1], old) || get_sha1(argv[2], new)) |
c5bac17a | 283 | usage(diff_tree_usage); |
c5b42386 LT |
284 | |
285 | if (argc > 3) { | |
286 | int i; | |
287 | ||
288 | paths = &argv[3]; | |
289 | nr_paths = argc - 3; | |
812666c8 | 290 | pathlens = xmalloc(nr_paths * sizeof(int)); |
c5b42386 LT |
291 | for (i=0; i<nr_paths; i++) |
292 | pathlens[i] = strlen(paths[i]); | |
293 | } | |
294 | ||
eeb79916 | 295 | return diff_tree_sha1(old, new, ""); |
9174026c | 296 | } |