2 * Helper functions for tree diff generation
8 static char *malloc_base(const char *base
, const char *path
, int pathlen
)
10 int baselen
= strlen(base
);
11 char *newbase
= xmalloc(baselen
+ pathlen
+ 2);
12 memcpy(newbase
, base
, baselen
);
13 memcpy(newbase
+ baselen
, path
, pathlen
);
14 memcpy(newbase
+ baselen
+ pathlen
, "/", 2);
18 static void show_entry(struct diff_options
*opt
, const char *prefix
, struct tree_desc
*desc
,
21 static int compare_tree_entry(struct tree_desc
*t1
, struct tree_desc
*t2
, const char *base
, struct diff_options
*opt
)
23 unsigned mode1
, mode2
;
24 const char *path1
, *path2
;
25 const unsigned char *sha1
, *sha2
;
26 int cmp
, pathlen1
, pathlen2
;
28 sha1
= tree_entry_extract(t1
, &path1
, &mode1
);
29 sha2
= tree_entry_extract(t2
, &path2
, &mode2
);
31 pathlen1
= strlen(path1
);
32 pathlen2
= strlen(path2
);
33 cmp
= base_name_compare(path1
, pathlen1
, mode1
, path2
, pathlen2
, mode2
);
35 show_entry(opt
, "-", t1
, base
);
39 show_entry(opt
, "+", t2
, base
);
42 if (!opt
->find_copies_harder
&&
43 !memcmp(sha1
, sha2
, 20) && mode1
== mode2
)
47 * If the filemode has changed to/from a directory from/to a regular
48 * file, we need to consider it a remove and an add.
50 if (S_ISDIR(mode1
) != S_ISDIR(mode2
)) {
51 show_entry(opt
, "-", t1
, base
);
52 show_entry(opt
, "+", t2
, base
);
56 if (opt
->recursive
&& S_ISDIR(mode1
)) {
58 char *newbase
= malloc_base(base
, path1
, pathlen1
);
59 if (opt
->tree_in_recursive
)
60 opt
->change(opt
, mode1
, mode2
,
61 sha1
, sha2
, base
, path1
);
62 retval
= diff_tree_sha1(sha1
, sha2
, newbase
, opt
);
67 opt
->change(opt
, mode1
, mode2
, sha1
, sha2
, base
, path1
);
71 static int interesting(struct tree_desc
*desc
, const char *base
, struct diff_options
*opt
)
81 (void)tree_entry_extract(desc
, &path
, &mode
);
83 pathlen
= strlen(path
);
84 baselen
= strlen(base
);
86 for (i
=0; i
< opt
->nr_paths
; i
++) {
87 const char *match
= opt
->paths
[i
];
88 int matchlen
= opt
->pathlens
[i
];
90 if (baselen
>= matchlen
) {
91 /* If it doesn't match, move along... */
92 if (strncmp(base
, match
, matchlen
))
95 /* The base is a subdirectory of a path which was specified. */
99 /* Does the base match? */
100 if (strncmp(base
, match
, baselen
))
106 if (pathlen
> matchlen
)
109 if (matchlen
> pathlen
) {
110 if (match
[pathlen
] != '/')
116 if (strncmp(path
, match
, pathlen
))
121 return 0; /* No matches */
124 /* A whole sub-tree went away or appeared */
125 static void show_tree(struct diff_options
*opt
, const char *prefix
, struct tree_desc
*desc
, const char *base
)
128 if (interesting(desc
, base
, opt
))
129 show_entry(opt
, prefix
, desc
, base
);
130 update_tree_entry(desc
);
134 /* A file entry went away or appeared */
135 static void show_entry(struct diff_options
*opt
, const char *prefix
, struct tree_desc
*desc
,
140 const unsigned char *sha1
= tree_entry_extract(desc
, &path
, &mode
);
142 if (opt
->recursive
&& S_ISDIR(mode
)) {
144 char *newbase
= malloc_base(base
, path
, strlen(path
));
145 struct tree_desc inner
;
148 tree
= read_sha1_file(sha1
, type
, &inner
.size
);
149 if (!tree
|| strcmp(type
, tree_type
))
150 die("corrupt tree sha %s", sha1_to_hex(sha1
));
153 show_tree(opt
, prefix
, &inner
, newbase
);
158 opt
->add_remove(opt
, prefix
[0], mode
, sha1
, base
, path
);
162 int diff_tree(struct tree_desc
*t1
, struct tree_desc
*t2
, const char *base
, struct diff_options
*opt
)
164 while (t1
->size
| t2
->size
) {
165 if (opt
->nr_paths
&& t1
->size
&& !interesting(t1
, base
, opt
)) {
166 update_tree_entry(t1
);
169 if (opt
->nr_paths
&& t2
->size
&& !interesting(t2
, base
, opt
)) {
170 update_tree_entry(t2
);
174 show_entry(opt
, "+", t2
, base
);
175 update_tree_entry(t2
);
179 show_entry(opt
, "-", t1
, base
);
180 update_tree_entry(t1
);
183 switch (compare_tree_entry(t1
, t2
, base
, opt
)) {
185 update_tree_entry(t1
);
188 update_tree_entry(t1
);
191 update_tree_entry(t2
);
194 die("git-diff-tree: internal error");
199 int diff_tree_sha1(const unsigned char *old
, const unsigned char *new, const char *base
, struct diff_options
*opt
)
202 struct tree_desc t1
, t2
;
205 tree1
= read_object_with_reference(old
, tree_type
, &t1
.size
, NULL
);
207 die("unable to read source tree (%s)", sha1_to_hex(old
));
208 tree2
= read_object_with_reference(new, tree_type
, &t2
.size
, NULL
);
210 die("unable to read destination tree (%s)", sha1_to_hex(new));
213 retval
= diff_tree(&t1
, &t2
, base
, opt
);
219 static int count_paths(const char **paths
)
227 void diff_tree_release_paths(struct diff_options
*opt
)
232 void diff_tree_setup_paths(const char **p
, struct diff_options
*opt
)
235 opt
->pathlens
= NULL
;
242 opt
->nr_paths
= count_paths(p
);
243 if (opt
->nr_paths
== 0) {
244 opt
->pathlens
= NULL
;
247 opt
->pathlens
= xmalloc(opt
->nr_paths
* sizeof(int));
248 for (i
=0; i
< opt
->nr_paths
; i
++)
249 opt
->pathlens
[i
] = strlen(p
[i
]);