2 * Helper functions for tree diff generation
10 * Compare two tree entries, taking into account only path/S_ISDIR(mode),
11 * but not their sha1's.
13 * NOTE files and directories *always* compare differently, even when having
14 * the same name - thanks to base_name_compare().
16 static int tree_entry_pathcmp(struct tree_desc
*t1
, struct tree_desc
*t2
)
18 unsigned mode1
, mode2
;
19 const char *path1
, *path2
;
20 const unsigned char *sha1
, *sha2
;
21 int cmp
, pathlen1
, pathlen2
;
23 sha1
= tree_entry_extract(t1
, &path1
, &mode1
);
24 sha2
= tree_entry_extract(t2
, &path2
, &mode2
);
26 pathlen1
= tree_entry_len(&t1
->entry
);
27 pathlen2
= tree_entry_len(&t2
->entry
);
29 cmp
= base_name_compare(path1
, pathlen1
, mode1
, path2
, pathlen2
, mode2
);
34 /* convert path, t1/t2 -> opt->diff_*() callbacks */
35 static void emit_diff(struct diff_options
*opt
, struct strbuf
*path
,
36 struct tree_desc
*t1
, struct tree_desc
*t2
)
38 unsigned int mode1
= t1
? t1
->entry
.mode
: 0;
39 unsigned int mode2
= t2
? t2
->entry
.mode
: 0;
42 opt
->change(opt
, mode1
, mode2
, t1
->entry
.sha1
, t2
->entry
.sha1
,
43 1, 1, path
->buf
, 0, 0);
46 const unsigned char *sha1
;
52 sha1
= t2
->entry
.sha1
;
56 sha1
= t1
->entry
.sha1
;
60 opt
->add_remove(opt
, addremove
, mode
, sha1
, 1, path
->buf
, 0);
65 /* new path should be added to diff
67 * 3 cases on how/when it should be called and behaves:
69 * !t1, t2 -> path added, parent lacks it
70 * t1, !t2 -> path removed from parent
71 * t1, t2 -> path modified
73 static void show_path(struct strbuf
*base
, struct diff_options
*opt
,
74 struct tree_desc
*t1
, struct tree_desc
*t2
)
79 int old_baselen
= base
->len
;
80 int isdir
, recurse
= 0, emitthis
= 1;
82 /* at least something has to be valid */
86 /* path present in resulting tree */
87 tree_entry_extract(t2
, &path
, &mode
);
88 pathlen
= tree_entry_len(&t2
->entry
);
89 isdir
= S_ISDIR(mode
);
92 * a path was removed - take path from parent. Also take
93 * mode from parent, to decide on recursion.
95 tree_entry_extract(t1
, &path
, &mode
);
96 pathlen
= tree_entry_len(&t1
->entry
);
98 isdir
= S_ISDIR(mode
);
102 if (DIFF_OPT_TST(opt
, RECURSIVE
) && isdir
) {
104 emitthis
= DIFF_OPT_TST(opt
, TREE_IN_RECURSIVE
);
107 strbuf_add(base
, path
, pathlen
);
110 emit_diff(opt
, base
, t1
, t2
);
113 strbuf_addch(base
, '/');
114 diff_tree_sha1(t1
? t1
->entry
.sha1
: NULL
,
115 t2
? t2
->entry
.sha1
: NULL
, base
->buf
, opt
);
118 strbuf_setlen(base
, old_baselen
);
121 static void skip_uninteresting(struct tree_desc
*t
, struct strbuf
*base
,
122 struct diff_options
*opt
)
124 enum interesting match
;
127 match
= tree_entry_interesting(&t
->entry
, base
, 0, &opt
->pathspec
);
129 if (match
== all_entries_not_interesting
)
133 update_tree_entry(t
);
137 int diff_tree(struct tree_desc
*t1
, struct tree_desc
*t2
,
138 const char *base_str
, struct diff_options
*opt
)
141 int baselen
= strlen(base_str
);
143 /* Enable recursion indefinitely */
144 opt
->pathspec
.recursive
= DIFF_OPT_TST(opt
, RECURSIVE
);
146 strbuf_init(&base
, PATH_MAX
);
147 strbuf_add(&base
, base_str
, baselen
);
152 if (diff_can_quit_early(opt
))
154 if (opt
->pathspec
.nr
) {
155 skip_uninteresting(t1
, &base
, opt
);
156 skip_uninteresting(t2
, &base
, opt
);
161 show_path(&base
, opt
, /*t1=*/NULL
, t2
);
162 update_tree_entry(t2
);
166 show_path(&base
, opt
, t1
, /*t2=*/NULL
);
167 update_tree_entry(t1
);
171 cmp
= tree_entry_pathcmp(t1
, t2
);
175 if (DIFF_OPT_TST(opt
, FIND_COPIES_HARDER
) ||
176 hashcmp(t1
->entry
.sha1
, t2
->entry
.sha1
) ||
177 (t1
->entry
.mode
!= t2
->entry
.mode
))
178 show_path(&base
, opt
, t1
, t2
);
180 update_tree_entry(t1
);
181 update_tree_entry(t2
);
186 show_path(&base
, opt
, t1
, /*t2=*/NULL
);
187 update_tree_entry(t1
);
192 show_path(&base
, opt
, /*t1=*/NULL
, t2
);
193 update_tree_entry(t2
);
197 strbuf_release(&base
);
202 * Does it look like the resulting diff might be due to a rename?
204 * - not a valid previous file
206 static inline int diff_might_be_rename(void)
208 return diff_queued_diff
.nr
== 1 &&
209 !DIFF_FILE_VALID(diff_queued_diff
.queue
[0]->one
);
212 static void try_to_follow_renames(struct tree_desc
*t1
, struct tree_desc
*t2
, const char *base
, struct diff_options
*opt
)
214 struct diff_options diff_opts
;
215 struct diff_queue_struct
*q
= &diff_queued_diff
;
216 struct diff_filepair
*choice
;
220 * follow-rename code is very specific, we need exactly one
221 * path. Magic that matches more than one path is not
224 GUARD_PATHSPEC(&opt
->pathspec
, PATHSPEC_FROMTOP
| PATHSPEC_LITERAL
);
227 * We should reject wildcards as well. Unfortunately we
228 * haven't got a reliable way to detect that 'foo\*bar' in
229 * fact has no wildcards. nowildcard_len is merely a hint for
230 * optimization. Let it slip for now until wildmatch is taught
231 * about dry-run mode and returns wildcard info.
233 if (opt
->pathspec
.has_wildcard
)
234 die("BUG:%s:%d: wildcards are not supported",
238 /* Remove the file creation entry from the diff queue, and remember it */
239 choice
= q
->queue
[0];
242 diff_setup(&diff_opts
);
243 DIFF_OPT_SET(&diff_opts
, RECURSIVE
);
244 DIFF_OPT_SET(&diff_opts
, FIND_COPIES_HARDER
);
245 diff_opts
.output_format
= DIFF_FORMAT_NO_OUTPUT
;
246 diff_opts
.single_follow
= opt
->pathspec
.items
[0].match
;
247 diff_opts
.break_opt
= opt
->break_opt
;
248 diff_opts
.rename_score
= opt
->rename_score
;
249 diff_setup_done(&diff_opts
);
250 diff_tree(t1
, t2
, base
, &diff_opts
);
251 diffcore_std(&diff_opts
);
252 free_pathspec(&diff_opts
.pathspec
);
254 /* Go through the new set of filepairing, and see if we find a more interesting one */
255 opt
->found_follow
= 0;
256 for (i
= 0; i
< q
->nr
; i
++) {
257 struct diff_filepair
*p
= q
->queue
[i
];
260 * Found a source? Not only do we use that for the new
261 * diff_queued_diff, we will also use that as the path in
264 if ((p
->status
== 'R' || p
->status
== 'C') &&
265 !strcmp(p
->two
->path
, opt
->pathspec
.items
[0].match
)) {
268 /* Switch the file-pairs around */
269 q
->queue
[i
] = choice
;
272 /* Update the path we use from now on.. */
273 path
[0] = p
->one
->path
;
275 free_pathspec(&opt
->pathspec
);
276 parse_pathspec(&opt
->pathspec
,
277 PATHSPEC_ALL_MAGIC
& ~PATHSPEC_LITERAL
,
278 PATHSPEC_LITERAL_PATH
, "", path
);
281 * The caller expects us to return a set of vanilla
282 * filepairs to let a later call to diffcore_std()
283 * it makes to sort the renames out (among other
284 * things), but we already have found renames
285 * ourselves; signal diffcore_std() not to muck with
286 * rename information.
288 opt
->found_follow
= 1;
294 * Then, discard all the non-relevant file pairs...
296 for (i
= 0; i
< q
->nr
; i
++) {
297 struct diff_filepair
*p
= q
->queue
[i
];
298 diff_free_filepair(p
);
302 * .. and re-instate the one we want (which might be either the
303 * original one, or the rename/copy we found)
305 q
->queue
[0] = choice
;
309 int diff_tree_sha1(const unsigned char *old
, const unsigned char *new, const char *base
, struct diff_options
*opt
)
312 struct tree_desc t1
, t2
;
313 unsigned long size1
, size2
;
316 tree1
= fill_tree_descriptor(&t1
, old
);
317 tree2
= fill_tree_descriptor(&t2
, new);
320 retval
= diff_tree(&t1
, &t2
, base
, opt
);
321 if (!*base
&& DIFF_OPT_TST(opt
, FOLLOW_RENAMES
) && diff_might_be_rename()) {
322 init_tree_desc(&t1
, tree1
, size1
);
323 init_tree_desc(&t2
, tree2
, size2
);
324 try_to_follow_renames(&t1
, &t2
, base
, opt
);
331 int diff_root_tree_sha1(const unsigned char *new, const char *base
, struct diff_options
*opt
)
333 return diff_tree_sha1(NULL
, new, base
, opt
);