2 * Helper functions for tree diff generation
8 static char *malloc_base(const char *base
, int baselen
, const char *path
, int pathlen
)
10 char *newbase
= xmalloc(baselen
+ pathlen
+ 2);
11 memcpy(newbase
, base
, baselen
);
12 memcpy(newbase
+ baselen
, path
, pathlen
);
13 memcpy(newbase
+ baselen
+ pathlen
, "/", 2);
17 static void show_entry(struct diff_options
*opt
, const char *prefix
, struct tree_desc
*desc
,
18 const char *base
, int baselen
);
20 static int compare_tree_entry(struct tree_desc
*t1
, struct tree_desc
*t2
, const char *base
, int baselen
, struct diff_options
*opt
)
22 unsigned mode1
, mode2
;
23 const char *path1
, *path2
;
24 const unsigned char *sha1
, *sha2
;
25 int cmp
, pathlen1
, pathlen2
;
27 sha1
= tree_entry_extract(t1
, &path1
, &mode1
);
28 sha2
= tree_entry_extract(t2
, &path2
, &mode2
);
30 pathlen1
= tree_entry_len(path1
, sha1
);
31 pathlen2
= tree_entry_len(path2
, sha2
);
32 cmp
= base_name_compare(path1
, pathlen1
, mode1
, path2
, pathlen2
, mode2
);
34 show_entry(opt
, "-", t1
, base
, baselen
);
38 show_entry(opt
, "+", t2
, base
, baselen
);
41 if (!opt
->find_copies_harder
&& !hashcmp(sha1
, sha2
) && mode1
== mode2
)
45 * If the filemode has changed to/from a directory from/to a regular
46 * file, we need to consider it a remove and an add.
48 if (S_ISDIR(mode1
) != S_ISDIR(mode2
)) {
49 show_entry(opt
, "-", t1
, base
, baselen
);
50 show_entry(opt
, "+", t2
, base
, baselen
);
54 if (opt
->recursive
&& S_ISDIR(mode1
)) {
56 char *newbase
= malloc_base(base
, baselen
, path1
, pathlen1
);
57 if (opt
->tree_in_recursive
)
58 opt
->change(opt
, mode1
, mode2
,
59 sha1
, sha2
, base
, path1
);
60 retval
= diff_tree_sha1(sha1
, sha2
, newbase
, opt
);
65 opt
->change(opt
, mode1
, mode2
, sha1
, sha2
, base
, path1
);
70 * Is a tree entry interesting given the pathspec we have?
73 * - 2 for "yes, and all subsequent entries will be"
76 * - negative for "no, and no subsequent entries will be either"
78 static int tree_entry_interesting(struct tree_desc
*desc
, const char *base
, int baselen
, struct diff_options
*opt
)
81 const unsigned char *sha1
;
85 int never_interesting
= -1;
90 sha1
= tree_entry_extract(desc
, &path
, &mode
);
92 pathlen
= tree_entry_len(path
, sha1
);
94 for (i
= 0; i
< opt
->nr_paths
; i
++) {
95 const char *match
= opt
->paths
[i
];
96 int matchlen
= opt
->pathlens
[i
];
97 int m
= -1; /* signals that we haven't called strncmp() */
99 if (baselen
>= matchlen
) {
100 /* If it doesn't match, move along... */
101 if (strncmp(base
, match
, matchlen
))
105 * The base is a subdirectory of a path which
106 * was specified, so all of them are interesting.
111 /* Does the base match? */
112 if (strncmp(base
, match
, baselen
))
118 if (never_interesting
) {
120 * We have not seen any match that sorts later
121 * than the current path.
125 * Does match sort strictly earlier than path
126 * with their common parts?
128 m
= strncmp(match
, path
,
129 (matchlen
< pathlen
) ? matchlen
: pathlen
);
134 * If we come here even once, that means there is at
135 * least one pathspec that would sort equal to or
136 * later than the path we are currently looking at.
137 * In other words, if we have never reached this point
138 * after iterating all pathspecs, it means all
139 * pathspecs are either outside of base, or inside the
140 * base but sorts strictly earlier than the current
141 * one. In either case, they will never match the
142 * subsequent entries. In such a case, we initialized
143 * the variable to -1 and that is what will be
144 * returned, allowing the caller to terminate early.
146 never_interesting
= 0;
149 if (pathlen
> matchlen
)
152 if (matchlen
> pathlen
) {
153 if (match
[pathlen
] != '/')
161 * we cheated and did not do strncmp(), so we do
164 m
= strncmp(match
, path
, pathlen
);
167 * If common part matched earlier then it is a hit,
168 * because we rejected the case where path is not a
169 * leading directory and is shorter than match.
174 return never_interesting
; /* No matches */
177 /* A whole sub-tree went away or appeared */
178 static void show_tree(struct diff_options
*opt
, const char *prefix
, struct tree_desc
*desc
, const char *base
, int baselen
)
180 int all_interesting
= 0;
187 show
= tree_entry_interesting(desc
, base
, baselen
,
195 show_entry(opt
, prefix
, desc
, base
, baselen
);
196 update_tree_entry(desc
);
200 /* A file entry went away or appeared */
201 static void show_entry(struct diff_options
*opt
, const char *prefix
, struct tree_desc
*desc
,
202 const char *base
, int baselen
)
206 const unsigned char *sha1
= tree_entry_extract(desc
, &path
, &mode
);
208 if (opt
->recursive
&& S_ISDIR(mode
)) {
209 enum object_type type
;
210 int pathlen
= tree_entry_len(path
, sha1
);
211 char *newbase
= malloc_base(base
, baselen
, path
, pathlen
);
212 struct tree_desc inner
;
216 tree
= read_sha1_file(sha1
, &type
, &size
);
217 if (!tree
|| type
!= OBJ_TREE
)
218 die("corrupt tree sha %s", sha1_to_hex(sha1
));
220 init_tree_desc(&inner
, tree
, size
);
221 show_tree(opt
, prefix
, &inner
, newbase
, baselen
+ 1 + pathlen
);
226 opt
->add_remove(opt
, prefix
[0], mode
, sha1
, base
, path
);
230 static void skip_uninteresting(struct tree_desc
*t
, const char *base
, int baselen
, struct diff_options
*opt
)
232 int all_interesting
= 0;
239 show
= tree_entry_interesting(t
, base
, baselen
, opt
);
244 update_tree_entry(t
);
254 int diff_tree(struct tree_desc
*t1
, struct tree_desc
*t2
, const char *base
, struct diff_options
*opt
)
256 int baselen
= strlen(base
);
259 if (opt
->quiet
&& opt
->has_changes
)
262 skip_uninteresting(t1
, base
, baselen
, opt
);
263 skip_uninteresting(t2
, base
, baselen
, opt
);
268 show_entry(opt
, "+", t2
, base
, baselen
);
269 update_tree_entry(t2
);
273 show_entry(opt
, "-", t1
, base
, baselen
);
274 update_tree_entry(t1
);
277 switch (compare_tree_entry(t1
, t2
, base
, baselen
, opt
)) {
279 update_tree_entry(t1
);
282 update_tree_entry(t1
);
285 update_tree_entry(t2
);
288 die("git-diff-tree: internal error");
293 int diff_tree_sha1(const unsigned char *old
, const unsigned char *new, const char *base
, struct diff_options
*opt
)
296 struct tree_desc t1
, t2
;
297 unsigned long size1
, size2
;
300 tree1
= read_object_with_reference(old
, tree_type
, &size1
, NULL
);
302 die("unable to read source tree (%s)", sha1_to_hex(old
));
303 tree2
= read_object_with_reference(new, tree_type
, &size2
, NULL
);
305 die("unable to read destination tree (%s)", sha1_to_hex(new));
306 init_tree_desc(&t1
, tree1
, size1
);
307 init_tree_desc(&t2
, tree2
, size2
);
308 retval
= diff_tree(&t1
, &t2
, base
, opt
);
314 int diff_root_tree_sha1(const unsigned char *new, const char *base
, struct diff_options
*opt
)
319 struct tree_desc empty
, real
;
321 tree
= read_object_with_reference(new, tree_type
, &size
, NULL
);
323 die("unable to read root tree (%s)", sha1_to_hex(new));
324 init_tree_desc(&real
, tree
, size
);
326 init_tree_desc(&empty
, "", 0);
327 retval
= diff_tree(&empty
, &real
, base
, opt
);
332 static int count_paths(const char **paths
)
340 void diff_tree_release_paths(struct diff_options
*opt
)
345 void diff_tree_setup_paths(const char **p
, struct diff_options
*opt
)
348 opt
->pathlens
= NULL
;
355 opt
->nr_paths
= count_paths(p
);
356 if (opt
->nr_paths
== 0) {
357 opt
->pathlens
= NULL
;
360 opt
->pathlens
= xmalloc(opt
->nr_paths
* sizeof(int));
361 for (i
=0; i
< opt
->nr_paths
; i
++)
362 opt
->pathlens
[i
] = strlen(p
[i
]);