@@ -254,6 +254,49 @@ static int ensure_leading_directories(char *path)
254
254
}
255
255
}
256
256
257
+ /*
258
+ * Unconditional writing of a plain regular file is what
259
+ * "git difftool --dir-diff" wants to do for symlinks. We are preparing two
260
+ * temporary directories to be fed to a Git-unaware tool that knows how to
261
+ * show a diff of two directories (e.g. "diff -r A B").
262
+ *
263
+ * Because the tool is Git-unaware, if a symbolic link appears in either of
264
+ * these temporary directories, it will try to dereference and show the
265
+ * difference of the target of the symbolic link, which is not what we want,
266
+ * as the goal of the dir-diff mode is to produce an output that is logically
267
+ * equivalent to what "git diff" produces.
268
+ *
269
+ * Most importantly, we want to get textual comparison of the result of the
270
+ * readlink(2). get_symlink() provides that---it returns the contents of
271
+ * the symlink that gets written to a regular file to force the external tool
272
+ * to compare the readlink(2) result as text, even on a filesystem that is
273
+ * capable of doing a symbolic link.
274
+ */
275
+ static char * get_symlink (const struct object_id * oid , const char * path )
276
+ {
277
+ char * data ;
278
+ if (is_null_oid (oid )) {
279
+ /* The symlink is unknown to Git so read from the filesystem */
280
+ struct strbuf link = STRBUF_INIT ;
281
+ if (has_symlinks ) {
282
+ if (strbuf_readlink (& link , path , strlen (path )))
283
+ die (_ ("could not read symlink %s" ), path );
284
+ } else if (strbuf_read_file (& link , path , 128 ))
285
+ die (_ ("could not read symlink file %s" ), path );
286
+
287
+ data = strbuf_detach (& link , NULL );
288
+ } else {
289
+ enum object_type type ;
290
+ unsigned long size ;
291
+ data = read_sha1_file (oid -> hash , & type , & size );
292
+ if (!data )
293
+ die (_ ("could not read object %s for symlink %s" ),
294
+ oid_to_hex (oid ), path );
295
+ }
296
+
297
+ return data ;
298
+ }
299
+
257
300
static int run_dir_diff (const char * extcmd , int symlinks , const char * prefix ,
258
301
int argc , const char * * argv )
259
302
{
@@ -270,8 +313,6 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
270
313
struct hashmap working_tree_dups , submodules , symlinks2 ;
271
314
struct hashmap_iter iter ;
272
315
struct pair_entry * entry ;
273
- enum object_type type ;
274
- unsigned long size ;
275
316
struct index_state wtindex ;
276
317
struct checkout lstate , rstate ;
277
318
int rc , flags = RUN_GIT_CMD , err = 0 ;
@@ -377,13 +418,13 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
377
418
}
378
419
379
420
if (S_ISLNK (lmode )) {
380
- char * content = read_sha1_file ( loid . hash , & type , & size );
421
+ char * content = get_symlink ( & loid , src_path );
381
422
add_left_or_right (& symlinks2 , src_path , content , 0 );
382
423
free (content );
383
424
}
384
425
385
426
if (S_ISLNK (rmode )) {
386
- char * content = read_sha1_file ( roid . hash , & type , & size );
427
+ char * content = get_symlink ( & roid , dst_path );
387
428
add_left_or_right (& symlinks2 , dst_path , content , 1 );
388
429
free (content );
389
430
}
@@ -397,7 +438,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
397
438
return error ("could not write '%s'" , src_path );
398
439
}
399
440
400
- if (rmode ) {
441
+ if (rmode && ! S_ISLNK ( rmode ) ) {
401
442
struct working_tree_entry * entry ;
402
443
403
444
/* Avoid duplicate working_tree entries */
0 commit comments