2 * Helper functions for tree diff generation
7 // What paths are we interested in?
8 static int nr_paths
= 0;
9 static const char **paths
= NULL
;
10 static int *pathlens
= NULL
;
12 void update_tree_entry(struct tree_desc
*desc
)
14 void *buf
= desc
->buf
;
15 unsigned long size
= desc
->size
;
16 int len
= strlen(buf
) + 1 + 20;
19 die("corrupt tree file");
20 desc
->buf
= buf
+ len
;
21 desc
->size
= size
- len
;
24 const unsigned char *tree_entry_extract(struct tree_desc
*desc
, const char **pathp
, unsigned int *modep
)
26 void *tree
= desc
->buf
;
27 unsigned long size
= desc
->size
;
28 int len
= strlen(tree
)+1;
29 const unsigned char *sha1
= tree
+ len
;
30 const char *path
= strchr(tree
, ' ');
33 if (!path
|| size
< len
+ 20 || sscanf(tree
, "%o", &mode
) != 1)
34 die("corrupt tree file");
36 *modep
= DIFF_FILE_CANON_MODE(mode
);
40 static char *malloc_base(const char *base
, const char *path
, int pathlen
)
42 int baselen
= strlen(base
);
43 char *newbase
= xmalloc(baselen
+ pathlen
+ 2);
44 memcpy(newbase
, base
, baselen
);
45 memcpy(newbase
+ baselen
, path
, pathlen
);
46 memcpy(newbase
+ baselen
+ pathlen
, "/", 2);
50 static int show_entry(struct diff_options
*opt
, const char *prefix
, struct tree_desc
*desc
, const char *base
);
52 static int compare_tree_entry(struct tree_desc
*t1
, struct tree_desc
*t2
, const char *base
, struct diff_options
*opt
)
54 unsigned mode1
, mode2
;
55 const char *path1
, *path2
;
56 const unsigned char *sha1
, *sha2
;
57 int cmp
, pathlen1
, pathlen2
;
59 sha1
= tree_entry_extract(t1
, &path1
, &mode1
);
60 sha2
= tree_entry_extract(t2
, &path2
, &mode2
);
62 pathlen1
= strlen(path1
);
63 pathlen2
= strlen(path2
);
64 cmp
= base_name_compare(path1
, pathlen1
, mode1
, path2
, pathlen2
, mode2
);
66 show_entry(opt
, "-", t1
, base
);
70 show_entry(opt
, "+", t2
, base
);
73 if (!opt
->find_copies_harder
&&
74 !memcmp(sha1
, sha2
, 20) && mode1
== mode2
)
78 * If the filemode has changed to/from a directory from/to a regular
79 * file, we need to consider it a remove and an add.
81 if (S_ISDIR(mode1
) != S_ISDIR(mode2
)) {
82 show_entry(opt
, "-", t1
, base
);
83 show_entry(opt
, "+", t2
, base
);
87 if (opt
->recursive
&& S_ISDIR(mode1
)) {
89 char *newbase
= malloc_base(base
, path1
, pathlen1
);
90 if (opt
->tree_in_recursive
)
91 opt
->change(opt
, mode1
, mode2
,
92 sha1
, sha2
, base
, path1
);
93 retval
= diff_tree_sha1(sha1
, sha2
, newbase
, opt
);
98 opt
->change(opt
, mode1
, mode2
, sha1
, sha2
, base
, path1
);
102 static int interesting(struct tree_desc
*desc
, const char *base
)
107 int baselen
, pathlen
;
112 (void)tree_entry_extract(desc
, &path
, &mode
);
114 pathlen
= strlen(path
);
115 baselen
= strlen(base
);
117 for (i
=0; i
< nr_paths
; i
++) {
118 const char *match
= paths
[i
];
119 int matchlen
= pathlens
[i
];
121 if (baselen
>= matchlen
) {
122 /* If it doesn't match, move along... */
123 if (strncmp(base
, match
, matchlen
))
126 /* The base is a subdirectory of a path which was specified. */
130 /* Does the base match? */
131 if (strncmp(base
, match
, baselen
))
137 if (pathlen
> matchlen
)
140 if (matchlen
> pathlen
) {
141 if (match
[pathlen
] != '/')
147 if (strncmp(path
, match
, pathlen
))
152 return 0; /* No matches */
155 /* A whole sub-tree went away or appeared */
156 static void show_tree(struct diff_options
*opt
, const char *prefix
, struct tree_desc
*desc
, const char *base
)
159 if (interesting(desc
, base
))
160 show_entry(opt
, prefix
, desc
, base
);
161 update_tree_entry(desc
);
165 /* A file entry went away or appeared */
166 static int show_entry(struct diff_options
*opt
, const char *prefix
, struct tree_desc
*desc
, const char *base
)
170 const unsigned char *sha1
= tree_entry_extract(desc
, &path
, &mode
);
172 if (opt
->recursive
&& S_ISDIR(mode
)) {
174 char *newbase
= malloc_base(base
, path
, strlen(path
));
175 struct tree_desc inner
;
178 tree
= read_sha1_file(sha1
, type
, &inner
.size
);
179 if (!tree
|| strcmp(type
, "tree"))
180 die("corrupt tree sha %s", sha1_to_hex(sha1
));
183 show_tree(opt
, prefix
, &inner
, newbase
);
190 opt
->add_remove(opt
, prefix
[0], mode
, sha1
, base
, path
);
194 int diff_tree(struct tree_desc
*t1
, struct tree_desc
*t2
, const char *base
, struct diff_options
*opt
)
196 while (t1
->size
| t2
->size
) {
197 if (nr_paths
&& t1
->size
&& !interesting(t1
, base
)) {
198 update_tree_entry(t1
);
201 if (nr_paths
&& t2
->size
&& !interesting(t2
, base
)) {
202 update_tree_entry(t2
);
206 show_entry(opt
, "+", t2
, base
);
207 update_tree_entry(t2
);
211 show_entry(opt
, "-", t1
, base
);
212 update_tree_entry(t1
);
215 switch (compare_tree_entry(t1
, t2
, base
, opt
)) {
217 update_tree_entry(t1
);
220 update_tree_entry(t1
);
223 update_tree_entry(t2
);
226 die("git-diff-tree: internal error");
231 int diff_tree_sha1(const unsigned char *old
, const unsigned char *new, const char *base
, struct diff_options
*opt
)
234 struct tree_desc t1
, t2
;
237 tree1
= read_object_with_reference(old
, "tree", &t1
.size
, NULL
);
239 die("unable to read source tree (%s)", sha1_to_hex(old
));
240 tree2
= read_object_with_reference(new, "tree", &t2
.size
, NULL
);
242 die("unable to read destination tree (%s)", sha1_to_hex(new));
245 retval
= diff_tree(&t1
, &t2
, base
, opt
);
251 static int count_paths(const char **paths
)
259 void diff_tree_setup_paths(const char **p
)
265 nr_paths
= count_paths(paths
);
270 pathlens
= xmalloc(nr_paths
* sizeof(int));
271 for (i
=0; i
<nr_paths
; i
++)
272 pathlens
[i
] = strlen(paths
[i
]);