2 * Helper functions for tree diff generation
9 static void show_entry(struct diff_options
*opt
, const char *prefix
,
10 struct tree_desc
*desc
, struct strbuf
*base
);
12 static int compare_tree_entry(struct tree_desc
*t1
, struct tree_desc
*t2
,
13 struct strbuf
*base
, struct diff_options
*opt
)
15 unsigned mode1
, mode2
;
16 const char *path1
, *path2
;
17 const unsigned char *sha1
, *sha2
;
18 int cmp
, pathlen1
, pathlen2
;
19 int old_baselen
= base
->len
;
22 sha1
= tree_entry_extract(t1
, &path1
, &mode1
);
23 sha2
= tree_entry_extract(t2
, &path2
, &mode2
);
25 pathlen1
= tree_entry_len(path1
, sha1
);
26 pathlen2
= tree_entry_len(path2
, sha2
);
27 cmp
= base_name_compare(path1
, pathlen1
, mode1
, path2
, pathlen2
, mode2
);
29 show_entry(opt
, "-", t1
, base
);
33 show_entry(opt
, "+", t2
, base
);
36 if (!DIFF_OPT_TST(opt
, FIND_COPIES_HARDER
) && !hashcmp(sha1
, sha2
) && mode1
== mode2
)
40 * If the filemode has changed to/from a directory from/to a regular
41 * file, we need to consider it a remove and an add.
43 if (S_ISDIR(mode1
) != S_ISDIR(mode2
)) {
44 show_entry(opt
, "-", t1
, base
);
45 show_entry(opt
, "+", t2
, base
);
49 strbuf_add(base
, path1
, pathlen1
);
50 if (DIFF_OPT_TST(opt
, RECURSIVE
) && S_ISDIR(mode1
)) {
51 if (DIFF_OPT_TST(opt
, TREE_IN_RECURSIVE
)) {
52 opt
->change(opt
, mode1
, mode2
,
53 sha1
, sha2
, base
->buf
, 0, 0);
55 strbuf_addch(base
, '/');
56 retval
= diff_tree_sha1(sha1
, sha2
, base
->buf
, opt
);
58 opt
->change(opt
, mode1
, mode2
, sha1
, sha2
, base
->buf
, 0, 0);
60 strbuf_setlen(base
, old_baselen
);
64 /* A whole sub-tree went away or appeared */
65 static void show_tree(struct diff_options
*opt
, const char *prefix
,
66 struct tree_desc
*desc
, struct strbuf
*base
)
68 int all_interesting
= 0;
75 show
= tree_entry_interesting(&desc
->entry
, base
, 0,
83 show_entry(opt
, prefix
, desc
, base
);
84 update_tree_entry(desc
);
88 /* A file entry went away or appeared */
89 static void show_entry(struct diff_options
*opt
, const char *prefix
,
90 struct tree_desc
*desc
, struct strbuf
*base
)
94 const unsigned char *sha1
= tree_entry_extract(desc
, &path
, &mode
);
95 int pathlen
= tree_entry_len(path
, sha1
);
96 int old_baselen
= base
->len
;
98 strbuf_add(base
, path
, pathlen
);
99 if (DIFF_OPT_TST(opt
, RECURSIVE
) && S_ISDIR(mode
)) {
100 enum object_type type
;
101 struct tree_desc inner
;
105 tree
= read_sha1_file(sha1
, &type
, &size
);
106 if (!tree
|| type
!= OBJ_TREE
)
107 die("corrupt tree sha %s", sha1_to_hex(sha1
));
109 if (DIFF_OPT_TST(opt
, TREE_IN_RECURSIVE
))
110 opt
->add_remove(opt
, *prefix
, mode
, sha1
, base
->buf
, 0);
112 strbuf_addch(base
, '/');
114 init_tree_desc(&inner
, tree
, size
);
115 show_tree(opt
, prefix
, &inner
, base
);
118 opt
->add_remove(opt
, prefix
[0], mode
, sha1
, base
->buf
, 0);
120 strbuf_setlen(base
, old_baselen
);
123 static void skip_uninteresting(struct tree_desc
*t
, struct strbuf
*base
,
124 struct diff_options
*opt
, int *all_interesting
)
127 int show
= tree_entry_interesting(&t
->entry
, base
, 0, &opt
->pathspec
);
129 *all_interesting
= 1;
131 update_tree_entry(t
);
141 int diff_tree(struct tree_desc
*t1
, struct tree_desc
*t2
,
142 const char *base_str
, struct diff_options
*opt
)
145 int baselen
= strlen(base_str
);
146 int all_t1_interesting
= 0;
147 int all_t2_interesting
= 0;
149 /* Enable recursion indefinitely */
150 opt
->pathspec
.recursive
= DIFF_OPT_TST(opt
, RECURSIVE
);
151 opt
->pathspec
.max_depth
= -1;
153 strbuf_init(&base
, PATH_MAX
);
154 strbuf_add(&base
, base_str
, baselen
);
157 if (DIFF_OPT_TST(opt
, QUICK
) &&
158 DIFF_OPT_TST(opt
, HAS_CHANGES
))
160 if (opt
->pathspec
.nr
) {
161 if (!all_t1_interesting
)
162 skip_uninteresting(t1
, &base
, opt
, &all_t1_interesting
);
163 if (!all_t2_interesting
)
164 skip_uninteresting(t2
, &base
, opt
, &all_t2_interesting
);
169 show_entry(opt
, "+", t2
, &base
);
170 update_tree_entry(t2
);
174 show_entry(opt
, "-", t1
, &base
);
175 update_tree_entry(t1
);
178 switch (compare_tree_entry(t1
, t2
, &base
, opt
)) {
180 update_tree_entry(t1
);
183 update_tree_entry(t1
);
186 update_tree_entry(t2
);
189 die("git diff-tree: internal error");
192 strbuf_release(&base
);
197 * Does it look like the resulting diff might be due to a rename?
199 * - not a valid previous file
201 static inline int diff_might_be_rename(void)
203 return diff_queued_diff
.nr
== 1 &&
204 !DIFF_FILE_VALID(diff_queued_diff
.queue
[0]->one
);
207 static void try_to_follow_renames(struct tree_desc
*t1
, struct tree_desc
*t2
, const char *base
, struct diff_options
*opt
)
209 struct diff_options diff_opts
;
210 struct diff_queue_struct
*q
= &diff_queued_diff
;
211 struct diff_filepair
*choice
;
212 const char *paths
[1];
215 /* Remove the file creation entry from the diff queue, and remember it */
216 choice
= q
->queue
[0];
219 diff_setup(&diff_opts
);
220 DIFF_OPT_SET(&diff_opts
, RECURSIVE
);
221 DIFF_OPT_SET(&diff_opts
, FIND_COPIES_HARDER
);
222 diff_opts
.output_format
= DIFF_FORMAT_NO_OUTPUT
;
223 diff_opts
.single_follow
= opt
->pathspec
.raw
[0];
224 diff_opts
.break_opt
= opt
->break_opt
;
226 diff_tree_setup_paths(paths
, &diff_opts
);
227 if (diff_setup_done(&diff_opts
) < 0)
228 die("unable to set up diff options to follow renames");
229 diff_tree(t1
, t2
, base
, &diff_opts
);
230 diffcore_std(&diff_opts
);
231 diff_tree_release_paths(&diff_opts
);
233 /* Go through the new set of filepairing, and see if we find a more interesting one */
234 opt
->found_follow
= 0;
235 for (i
= 0; i
< q
->nr
; i
++) {
236 struct diff_filepair
*p
= q
->queue
[i
];
239 * Found a source? Not only do we use that for the new
240 * diff_queued_diff, we will also use that as the path in
243 if ((p
->status
== 'R' || p
->status
== 'C') &&
244 !strcmp(p
->two
->path
, opt
->pathspec
.raw
[0])) {
245 /* Switch the file-pairs around */
246 q
->queue
[i
] = choice
;
249 /* Update the path we use from now on.. */
250 diff_tree_release_paths(opt
);
251 opt
->pathspec
.raw
[0] = xstrdup(p
->one
->path
);
252 diff_tree_setup_paths(opt
->pathspec
.raw
, opt
);
255 * The caller expects us to return a set of vanilla
256 * filepairs to let a later call to diffcore_std()
257 * it makes to sort the renames out (among other
258 * things), but we already have found renames
259 * ourselves; signal diffcore_std() not to muck with
260 * rename information.
262 opt
->found_follow
= 1;
268 * Then, discard all the non-relevant file pairs...
270 for (i
= 0; i
< q
->nr
; i
++) {
271 struct diff_filepair
*p
= q
->queue
[i
];
272 diff_free_filepair(p
);
276 * .. and re-instate the one we want (which might be either the
277 * original one, or the rename/copy we found)
279 q
->queue
[0] = choice
;
283 int diff_tree_sha1(const unsigned char *old
, const unsigned char *new, const char *base
, struct diff_options
*opt
)
286 struct tree_desc t1
, t2
;
287 unsigned long size1
, size2
;
290 tree1
= read_object_with_reference(old
, tree_type
, &size1
, NULL
);
292 die("unable to read source tree (%s)", sha1_to_hex(old
));
293 tree2
= read_object_with_reference(new, tree_type
, &size2
, NULL
);
295 die("unable to read destination tree (%s)", sha1_to_hex(new));
296 init_tree_desc(&t1
, tree1
, size1
);
297 init_tree_desc(&t2
, tree2
, size2
);
298 retval
= diff_tree(&t1
, &t2
, base
, opt
);
299 if (!*base
&& DIFF_OPT_TST(opt
, FOLLOW_RENAMES
) && diff_might_be_rename()) {
300 init_tree_desc(&t1
, tree1
, size1
);
301 init_tree_desc(&t2
, tree2
, size2
);
302 try_to_follow_renames(&t1
, &t2
, base
, opt
);
309 int diff_root_tree_sha1(const unsigned char *new, const char *base
, struct diff_options
*opt
)
314 struct tree_desc empty
, real
;
316 tree
= read_object_with_reference(new, tree_type
, &size
, NULL
);
318 die("unable to read root tree (%s)", sha1_to_hex(new));
319 init_tree_desc(&real
, tree
, size
);
321 init_tree_desc(&empty
, "", 0);
322 retval
= diff_tree(&empty
, &real
, base
, opt
);
327 void diff_tree_release_paths(struct diff_options
*opt
)
329 free_pathspec(&opt
->pathspec
);
332 void diff_tree_setup_paths(const char **p
, struct diff_options
*opt
)
334 init_pathspec(&opt
->pathspec
, p
);