]>
Commit | Line | Data |
---|---|---|
9174026c LT |
1 | #include "cache.h" |
2 | ||
73134b6d | 3 | static int recursive = 0; |
6cbd72f8 | 4 | static int line_termination = '\n'; |
9174026c | 5 | |
eeb79916 | 6 | static int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const char *base); |
73134b6d LT |
7 | |
8 | static void update_tree_entry(void **bufp, unsigned long *sizep) | |
9174026c LT |
9 | { |
10 | void *buf = *bufp; | |
11 | unsigned long size = *sizep; | |
12 | int len = strlen(buf) + 1 + 20; | |
13 | ||
14 | if (size < len) | |
2de381f9 | 15 | die("corrupt tree file"); |
9174026c LT |
16 | *bufp = buf + len; |
17 | *sizep = size - len; | |
9174026c LT |
18 | } |
19 | ||
20 | static const unsigned char *extract(void *tree, unsigned long size, const char **pathp, unsigned int *modep) | |
21 | { | |
22 | int len = strlen(tree)+1; | |
23 | const unsigned char *sha1 = tree + len; | |
24 | const char *path = strchr(tree, ' '); | |
25 | ||
26 | if (!path || size < len + 20 || sscanf(tree, "%o", modep) != 1) | |
2de381f9 | 27 | die("corrupt tree file"); |
9174026c LT |
28 | *pathp = path+1; |
29 | return sha1; | |
30 | } | |
31 | ||
262e82b4 LT |
32 | static char *malloc_base(const char *base, const char *path, int pathlen) |
33 | { | |
34 | int baselen = strlen(base); | |
35 | char *newbase = malloc(baselen + pathlen + 2); | |
36 | memcpy(newbase, base, baselen); | |
37 | memcpy(newbase + baselen, path, pathlen); | |
38 | memcpy(newbase + baselen + pathlen, "/", 2); | |
39 | return newbase; | |
40 | } | |
41 | ||
42 | static void show_file(const char *prefix, void *tree, unsigned long size, const char *base); | |
43 | ||
44 | /* A whole sub-tree went away or appeared */ | |
45 | static void show_tree(const char *prefix, void *tree, unsigned long size, const char *base) | |
46 | { | |
47 | while (size) { | |
48 | show_file(prefix, tree, size, base); | |
49 | update_tree_entry(&tree, &size); | |
50 | } | |
51 | } | |
52 | ||
53 | /* A file entry went away or appeared */ | |
73134b6d | 54 | static void show_file(const char *prefix, void *tree, unsigned long size, const char *base) |
9174026c LT |
55 | { |
56 | unsigned mode; | |
57 | const char *path; | |
58 | const unsigned char *sha1 = extract(tree, size, &path, &mode); | |
262e82b4 LT |
59 | |
60 | if (recursive && S_ISDIR(mode)) { | |
61 | char type[20]; | |
62 | unsigned long size; | |
63 | char *newbase = malloc_base(base, path, strlen(path)); | |
64 | void *tree; | |
65 | ||
66 | tree = read_sha1_file(sha1, type, &size); | |
67 | if (!tree || strcmp(type, "tree")) | |
2de381f9 | 68 | die("corrupt tree sha %s", sha1_to_hex(sha1)); |
262e82b4 LT |
69 | |
70 | show_tree(prefix, tree, size, newbase); | |
71 | ||
72 | free(tree); | |
73 | free(newbase); | |
74 | return; | |
75 | } | |
76 | ||
33b238d1 PB |
77 | printf("%s%o\t%s\t%s\t%s%s%c", prefix, mode, |
78 | S_ISDIR(mode) ? "tree" : "blob", | |
6cbd72f8 LT |
79 | sha1_to_hex(sha1), base, path, |
80 | line_termination); | |
9174026c LT |
81 | } |
82 | ||
eeb79916 | 83 | static int compare_tree_entry(void *tree1, unsigned long size1, void *tree2, unsigned long size2, const char *base) |
9174026c LT |
84 | { |
85 | unsigned mode1, mode2; | |
86 | const char *path1, *path2; | |
87 | const unsigned char *sha1, *sha2; | |
73134b6d | 88 | int cmp, pathlen1, pathlen2; |
eeb79916 | 89 | char old_sha1_hex[50]; |
9174026c LT |
90 | |
91 | sha1 = extract(tree1, size1, &path1, &mode1); | |
92 | sha2 = extract(tree2, size2, &path2, &mode2); | |
93 | ||
73134b6d LT |
94 | pathlen1 = strlen(path1); |
95 | pathlen2 = strlen(path2); | |
96 | cmp = cache_name_compare(path1, pathlen1, path2, pathlen2); | |
9174026c | 97 | if (cmp < 0) { |
eeb79916 | 98 | show_file("-", tree1, size1, base); |
9174026c LT |
99 | return -1; |
100 | } | |
101 | if (cmp > 0) { | |
eeb79916 | 102 | show_file("+", tree2, size2, base); |
9174026c LT |
103 | return 1; |
104 | } | |
105 | if (!memcmp(sha1, sha2, 20) && mode1 == mode2) | |
106 | return 0; | |
262e82b4 LT |
107 | |
108 | /* | |
109 | * If the filemode has changed to/from a directory from/to a regular | |
aebb2679 | 110 | * file, we need to consider it a remove and an add. |
262e82b4 LT |
111 | */ |
112 | if (S_ISDIR(mode1) != S_ISDIR(mode2)) { | |
113 | show_file("-", tree1, size1, base); | |
114 | show_file("+", tree2, size2, base); | |
115 | return 0; | |
116 | } | |
117 | ||
118 | if (recursive && S_ISDIR(mode1)) { | |
eeb79916 | 119 | int retval; |
262e82b4 | 120 | char *newbase = malloc_base(base, path1, pathlen1); |
eeb79916 LT |
121 | retval = diff_tree_sha1(sha1, sha2, newbase); |
122 | free(newbase); | |
123 | return retval; | |
73134b6d LT |
124 | } |
125 | ||
eeb79916 | 126 | strcpy(old_sha1_hex, sha1_to_hex(sha1)); |
33b238d1 PB |
127 | printf("*%o->%o\t%s\t%s->%s\t%s%s%c", mode1, mode2, |
128 | S_ISDIR(mode1) ? "tree" : "blob", | |
6cbd72f8 LT |
129 | old_sha1_hex, sha1_to_hex(sha2), base, path1, |
130 | line_termination); | |
9174026c LT |
131 | return 0; |
132 | } | |
133 | ||
eeb79916 | 134 | static int diff_tree(void *tree1, unsigned long size1, void *tree2, unsigned long size2, const char *base) |
9174026c LT |
135 | { |
136 | while (size1 | size2) { | |
137 | if (!size1) { | |
eeb79916 | 138 | show_file("+", tree2, size2, base); |
9174026c LT |
139 | update_tree_entry(&tree2, &size2); |
140 | continue; | |
141 | } | |
142 | if (!size2) { | |
eeb79916 | 143 | show_file("-", tree1, size1, base); |
9174026c LT |
144 | update_tree_entry(&tree1, &size1); |
145 | continue; | |
146 | } | |
eeb79916 | 147 | switch (compare_tree_entry(tree1, size1, tree2, size2, base)) { |
9174026c LT |
148 | case -1: |
149 | update_tree_entry(&tree1, &size1); | |
150 | continue; | |
151 | case 0: | |
152 | update_tree_entry(&tree1, &size1); | |
153 | /* Fallthrough */ | |
154 | case 1: | |
155 | update_tree_entry(&tree2, &size2); | |
156 | continue; | |
157 | } | |
2de381f9 | 158 | die("diff-tree: internal error"); |
9174026c LT |
159 | } |
160 | return 0; | |
161 | } | |
162 | ||
eeb79916 | 163 | static int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const char *base) |
9174026c | 164 | { |
9174026c LT |
165 | void *tree1, *tree2; |
166 | unsigned long size1, size2; | |
73134b6d | 167 | int retval; |
9174026c | 168 | |
c1fdf2a6 JH |
169 | tree1 = read_tree_with_tree_or_commit_sha1(old, &size1, 0); |
170 | if (!tree1) | |
2de381f9 | 171 | die("unable to read source tree (%s)", sha1_to_hex(old)); |
c1fdf2a6 JH |
172 | tree2 = read_tree_with_tree_or_commit_sha1(new, &size2, 0); |
173 | if (!tree2) | |
2de381f9 | 174 | die("unable to read destination tree (%s)", sha1_to_hex(new)); |
eeb79916 | 175 | retval = diff_tree(tree1, size1, tree2, size2, base); |
73134b6d LT |
176 | free(tree1); |
177 | free(tree2); | |
178 | return retval; | |
179 | } | |
180 | ||
181 | int main(int argc, char **argv) | |
182 | { | |
183 | unsigned char old[20], new[20]; | |
184 | ||
185 | while (argc > 3) { | |
186 | char *arg = argv[1]; | |
187 | argv++; | |
188 | argc--; | |
bf16c71e | 189 | if (!strcmp(arg, "-r")) { |
73134b6d LT |
190 | recursive = 1; |
191 | continue; | |
192 | } | |
6cbd72f8 LT |
193 | if (!strcmp(arg, "-z")) { |
194 | line_termination = '\0'; | |
195 | continue; | |
196 | } | |
197 | usage("diff-tree [-r] [-z] <tree sha1> <tree sha1>"); | |
73134b6d LT |
198 | } |
199 | ||
200 | if (argc != 3 || get_sha1_hex(argv[1], old) || get_sha1_hex(argv[2], new)) | |
201 | usage("diff-tree <tree sha1> <tree sha1>"); | |
eeb79916 | 202 | return diff_tree_sha1(old, new, ""); |
9174026c | 203 | } |