2 * Helper functions for tree diff generation
9 static char *malloc_base(const char *base
, int baselen
, const char *path
, int pathlen
)
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 char *malloc_fullname(const char *base
, int baselen
, const char *path
, int pathlen
)
20 char *fullname
= xmalloc(baselen
+ pathlen
+ 1);
21 memcpy(fullname
, base
, baselen
);
22 memcpy(fullname
+ baselen
, path
, pathlen
);
23 fullname
[baselen
+ pathlen
] = 0;
27 static void show_entry(struct diff_options
*opt
, const char *prefix
, struct tree_desc
*desc
,
28 const char *base
, int baselen
);
30 static int compare_tree_entry(struct tree_desc
*t1
, struct tree_desc
*t2
, const char *base
, int baselen
, struct diff_options
*opt
)
32 unsigned mode1
, mode2
;
33 const char *path1
, *path2
;
34 const unsigned char *sha1
, *sha2
;
35 int cmp
, pathlen1
, pathlen2
;
38 sha1
= tree_entry_extract(t1
, &path1
, &mode1
);
39 sha2
= tree_entry_extract(t2
, &path2
, &mode2
);
41 pathlen1
= tree_entry_len(path1
, sha1
);
42 pathlen2
= tree_entry_len(path2
, sha2
);
43 cmp
= base_name_compare(path1
, pathlen1
, mode1
, path2
, pathlen2
, mode2
);
45 show_entry(opt
, "-", t1
, base
, baselen
);
49 show_entry(opt
, "+", t2
, base
, baselen
);
52 if (!DIFF_OPT_TST(opt
, FIND_COPIES_HARDER
) && !hashcmp(sha1
, sha2
) && mode1
== mode2
)
56 * If the filemode has changed to/from a directory from/to a regular
57 * file, we need to consider it a remove and an add.
59 if (S_ISDIR(mode1
) != S_ISDIR(mode2
)) {
60 show_entry(opt
, "-", t1
, base
, baselen
);
61 show_entry(opt
, "+", t2
, base
, baselen
);
65 if (DIFF_OPT_TST(opt
, RECURSIVE
) && S_ISDIR(mode1
)) {
67 char *newbase
= malloc_base(base
, baselen
, path1
, pathlen1
);
68 if (DIFF_OPT_TST(opt
, TREE_IN_RECURSIVE
)) {
69 newbase
[baselen
+ pathlen1
] = 0;
70 opt
->change(opt
, mode1
, mode2
,
71 sha1
, sha2
, newbase
, 0, 0);
72 newbase
[baselen
+ pathlen1
] = '/';
74 retval
= diff_tree_sha1(sha1
, sha2
, newbase
, opt
);
79 fullname
= malloc_fullname(base
, baselen
, path1
, pathlen1
);
80 opt
->change(opt
, mode1
, mode2
, sha1
, sha2
, fullname
, 0, 0);
86 * Is a tree entry interesting given the pathspec we have?
88 * Pre-condition: baselen == 0 || base[baselen-1] == '/'
91 * - 2 for "yes, and all subsequent entries will be"
94 * - negative for "no, and no subsequent entries will be either"
96 static int tree_entry_interesting(const struct name_entry
*entry
, const char *base
, int baselen
, const struct pathspec
*ps
)
100 int never_interesting
= -1;
105 pathlen
= tree_entry_len(entry
->path
, entry
->sha1
);
107 for (i
= 0; i
< ps
->nr
; i
++) {
108 const struct pathspec_item
*item
= ps
->items
+i
;
109 const char *match
= item
->match
;
110 int matchlen
= item
->len
;
111 int m
= -1; /* signals that we haven't called strncmp() */
113 if (baselen
>= matchlen
) {
114 /* If it doesn't match, move along... */
115 if (strncmp(base
, match
, matchlen
))
119 * If the base is a subdirectory of a path which
120 * was specified, all of them are interesting.
123 base
[matchlen
] == '/' ||
124 match
[matchlen
- 1] == '/')
127 /* Just a random prefix match */
131 /* Does the base match? */
132 if (strncmp(base
, match
, baselen
))
138 if (never_interesting
) {
140 * We have not seen any match that sorts later
141 * than the current path.
145 * Does match sort strictly earlier than path
146 * with their common parts?
148 m
= strncmp(match
, entry
->path
,
149 (matchlen
< pathlen
) ? matchlen
: pathlen
);
154 * If we come here even once, that means there is at
155 * least one pathspec that would sort equal to or
156 * later than the path we are currently looking at.
157 * In other words, if we have never reached this point
158 * after iterating all pathspecs, it means all
159 * pathspecs are either outside of base, or inside the
160 * base but sorts strictly earlier than the current
161 * one. In either case, they will never match the
162 * subsequent entries. In such a case, we initialized
163 * the variable to -1 and that is what will be
164 * returned, allowing the caller to terminate early.
166 never_interesting
= 0;
169 if (pathlen
> matchlen
)
172 if (matchlen
> pathlen
) {
173 if (match
[pathlen
] != '/')
175 if (!S_ISDIR(entry
->mode
))
181 * we cheated and did not do strncmp(), so we do
184 m
= strncmp(match
, entry
->path
, pathlen
);
187 * If common part matched earlier then it is a hit,
188 * because we rejected the case where path is not a
189 * leading directory and is shorter than match.
194 return never_interesting
; /* No matches */
197 /* A whole sub-tree went away or appeared */
198 static void show_tree(struct diff_options
*opt
, const char *prefix
, struct tree_desc
*desc
, const char *base
, int baselen
)
200 int all_interesting
= 0;
207 show
= tree_entry_interesting(&desc
->entry
, base
, baselen
, &opt
->pathspec
);
214 show_entry(opt
, prefix
, desc
, base
, baselen
);
215 update_tree_entry(desc
);
219 /* A file entry went away or appeared */
220 static void show_entry(struct diff_options
*opt
, const char *prefix
, struct tree_desc
*desc
,
221 const char *base
, int baselen
)
225 const unsigned char *sha1
= tree_entry_extract(desc
, &path
, &mode
);
226 int pathlen
= tree_entry_len(path
, sha1
);
228 if (DIFF_OPT_TST(opt
, RECURSIVE
) && S_ISDIR(mode
)) {
229 enum object_type type
;
230 char *newbase
= malloc_base(base
, baselen
, path
, pathlen
);
231 struct tree_desc inner
;
235 tree
= read_sha1_file(sha1
, &type
, &size
);
236 if (!tree
|| type
!= OBJ_TREE
)
237 die("corrupt tree sha %s", sha1_to_hex(sha1
));
239 if (DIFF_OPT_TST(opt
, TREE_IN_RECURSIVE
)) {
240 newbase
[baselen
+ pathlen
] = 0;
241 opt
->add_remove(opt
, *prefix
, mode
, sha1
, newbase
, 0);
242 newbase
[baselen
+ pathlen
] = '/';
245 init_tree_desc(&inner
, tree
, size
);
246 show_tree(opt
, prefix
, &inner
, newbase
, baselen
+ 1 + pathlen
);
251 char *fullname
= malloc_fullname(base
, baselen
, path
, pathlen
);
252 opt
->add_remove(opt
, prefix
[0], mode
, sha1
, fullname
, 0);
257 static void skip_uninteresting(struct tree_desc
*t
, const char *base
, int baselen
, struct diff_options
*opt
, int *all_interesting
)
260 int show
= tree_entry_interesting(&t
->entry
, base
, baselen
, &opt
->pathspec
);
262 *all_interesting
= 1;
264 update_tree_entry(t
);
274 int diff_tree(struct tree_desc
*t1
, struct tree_desc
*t2
, const char *base
, struct diff_options
*opt
)
276 int baselen
= strlen(base
);
277 int all_t1_interesting
= 0;
278 int all_t2_interesting
= 0;
281 if (DIFF_OPT_TST(opt
, QUICK
) &&
282 DIFF_OPT_TST(opt
, HAS_CHANGES
))
284 if (opt
->pathspec
.nr
) {
285 if (!all_t1_interesting
)
286 skip_uninteresting(t1
, base
, baselen
, opt
,
287 &all_t1_interesting
);
288 if (!all_t2_interesting
)
289 skip_uninteresting(t2
, base
, baselen
, opt
,
290 &all_t2_interesting
);
295 show_entry(opt
, "+", t2
, base
, baselen
);
296 update_tree_entry(t2
);
300 show_entry(opt
, "-", t1
, base
, baselen
);
301 update_tree_entry(t1
);
304 switch (compare_tree_entry(t1
, t2
, base
, baselen
, opt
)) {
306 update_tree_entry(t1
);
309 update_tree_entry(t1
);
312 update_tree_entry(t2
);
315 die("git diff-tree: internal error");
321 * Does it look like the resulting diff might be due to a rename?
323 * - not a valid previous file
325 static inline int diff_might_be_rename(void)
327 return diff_queued_diff
.nr
== 1 &&
328 !DIFF_FILE_VALID(diff_queued_diff
.queue
[0]->one
);
331 static void try_to_follow_renames(struct tree_desc
*t1
, struct tree_desc
*t2
, const char *base
, struct diff_options
*opt
)
333 struct diff_options diff_opts
;
334 struct diff_queue_struct
*q
= &diff_queued_diff
;
335 struct diff_filepair
*choice
;
336 const char *paths
[1];
339 /* Remove the file creation entry from the diff queue, and remember it */
340 choice
= q
->queue
[0];
343 diff_setup(&diff_opts
);
344 DIFF_OPT_SET(&diff_opts
, RECURSIVE
);
345 DIFF_OPT_SET(&diff_opts
, FIND_COPIES_HARDER
);
346 diff_opts
.output_format
= DIFF_FORMAT_NO_OUTPUT
;
347 diff_opts
.single_follow
= opt
->pathspec
.raw
[0];
348 diff_opts
.break_opt
= opt
->break_opt
;
350 diff_tree_setup_paths(paths
, &diff_opts
);
351 if (diff_setup_done(&diff_opts
) < 0)
352 die("unable to set up diff options to follow renames");
353 diff_tree(t1
, t2
, base
, &diff_opts
);
354 diffcore_std(&diff_opts
);
355 diff_tree_release_paths(&diff_opts
);
357 /* Go through the new set of filepairing, and see if we find a more interesting one */
358 opt
->found_follow
= 0;
359 for (i
= 0; i
< q
->nr
; i
++) {
360 struct diff_filepair
*p
= q
->queue
[i
];
363 * Found a source? Not only do we use that for the new
364 * diff_queued_diff, we will also use that as the path in
367 if ((p
->status
== 'R' || p
->status
== 'C') &&
368 !strcmp(p
->two
->path
, opt
->pathspec
.raw
[0])) {
369 /* Switch the file-pairs around */
370 q
->queue
[i
] = choice
;
373 /* Update the path we use from now on.. */
374 diff_tree_release_paths(opt
);
375 opt
->pathspec
.raw
[0] = xstrdup(p
->one
->path
);
376 diff_tree_setup_paths(opt
->pathspec
.raw
, opt
);
379 * The caller expects us to return a set of vanilla
380 * filepairs to let a later call to diffcore_std()
381 * it makes to sort the renames out (among other
382 * things), but we already have found renames
383 * ourselves; signal diffcore_std() not to muck with
384 * rename information.
386 opt
->found_follow
= 1;
392 * Then, discard all the non-relevant file pairs...
394 for (i
= 0; i
< q
->nr
; i
++) {
395 struct diff_filepair
*p
= q
->queue
[i
];
396 diff_free_filepair(p
);
400 * .. and re-instate the one we want (which might be either the
401 * original one, or the rename/copy we found)
403 q
->queue
[0] = choice
;
407 int diff_tree_sha1(const unsigned char *old
, const unsigned char *new, const char *base
, struct diff_options
*opt
)
410 struct tree_desc t1
, t2
;
411 unsigned long size1
, size2
;
414 tree1
= read_object_with_reference(old
, tree_type
, &size1
, NULL
);
416 die("unable to read source tree (%s)", sha1_to_hex(old
));
417 tree2
= read_object_with_reference(new, tree_type
, &size2
, NULL
);
419 die("unable to read destination tree (%s)", sha1_to_hex(new));
420 init_tree_desc(&t1
, tree1
, size1
);
421 init_tree_desc(&t2
, tree2
, size2
);
422 retval
= diff_tree(&t1
, &t2
, base
, opt
);
423 if (!*base
&& DIFF_OPT_TST(opt
, FOLLOW_RENAMES
) && diff_might_be_rename()) {
424 init_tree_desc(&t1
, tree1
, size1
);
425 init_tree_desc(&t2
, tree2
, size2
);
426 try_to_follow_renames(&t1
, &t2
, base
, opt
);
433 int diff_root_tree_sha1(const unsigned char *new, const char *base
, struct diff_options
*opt
)
438 struct tree_desc empty
, real
;
440 tree
= read_object_with_reference(new, tree_type
, &size
, NULL
);
442 die("unable to read root tree (%s)", sha1_to_hex(new));
443 init_tree_desc(&real
, tree
, size
);
445 init_tree_desc(&empty
, "", 0);
446 retval
= diff_tree(&empty
, &real
, base
, opt
);
451 void diff_tree_release_paths(struct diff_options
*opt
)
453 free_pathspec(&opt
->pathspec
);
456 void diff_tree_setup_paths(const char **p
, struct diff_options
*opt
)
458 init_pathspec(&opt
->pathspec
, p
);