@@ -252,16 +252,6 @@ static void changed_files(struct hashmap *result, const char *index_path,
252252 strbuf_release (& buf );
253253}
254254
255- static NORETURN void exit_cleanup (const char * tmpdir , int exit_code )
256- {
257- struct strbuf buf = STRBUF_INIT ;
258- strbuf_addstr (& buf , tmpdir );
259- remove_dir_recursively (& buf , 0 );
260- if (exit_code )
261- warning (_ ("failed: %d" ), exit_code );
262- exit (exit_code );
263- }
264-
265255static int ensure_leading_directories (char * path )
266256{
267257 switch (safe_create_leading_directories (path )) {
@@ -330,19 +320,44 @@ static int checkout_path(unsigned mode, struct object_id *oid,
330320 return ret ;
331321}
332322
323+ static void write_file_in_directory (struct strbuf * dir , size_t dir_len ,
324+ const char * path , const char * content )
325+ {
326+ add_path (dir , dir_len , path );
327+ ensure_leading_directories (dir -> buf );
328+ unlink (dir -> buf );
329+ write_file (dir -> buf , "%s" , content );
330+ }
331+
332+ /* Write the file contents for the left and right sides of the difftool
333+ * dir-diff representation for submodules and symlinks. Symlinks and submodules
334+ * are written as regular text files so that external diff tools can diff them
335+ * as text files, resulting in behavior that is analogous to to what "git diff"
336+ * displays for symlink and submodule diffs.
337+ */
338+ static void write_standin_files (struct pair_entry * entry ,
339+ struct strbuf * ldir , size_t ldir_len ,
340+ struct strbuf * rdir , size_t rdir_len )
341+ {
342+ if (* entry -> left )
343+ write_file_in_directory (ldir , ldir_len , entry -> path , entry -> left );
344+ if (* entry -> right )
345+ write_file_in_directory (rdir , rdir_len , entry -> path , entry -> right );
346+ }
347+
333348static int run_dir_diff (const char * extcmd , int symlinks , const char * prefix ,
334349 struct child_process * child )
335350{
336- char tmpdir [PATH_MAX ];
337351 struct strbuf info = STRBUF_INIT , lpath = STRBUF_INIT ;
338352 struct strbuf rpath = STRBUF_INIT , buf = STRBUF_INIT ;
339353 struct strbuf ldir = STRBUF_INIT , rdir = STRBUF_INIT ;
340354 struct strbuf wtdir = STRBUF_INIT ;
341- char * lbase_dir , * rbase_dir ;
355+ struct strbuf tmpdir = STRBUF_INIT ;
356+ char * lbase_dir = NULL , * rbase_dir = NULL ;
342357 size_t ldir_len , rdir_len , wtdir_len ;
343358 const char * workdir , * tmp ;
344359 int ret = 0 , i ;
345- FILE * fp ;
360+ FILE * fp = NULL ;
346361 struct hashmap working_tree_dups = HASHMAP_INIT (working_tree_entry_cmp ,
347362 NULL );
348363 struct hashmap submodules = HASHMAP_INIT (pair_cmp , NULL );
@@ -351,7 +366,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
351366 struct pair_entry * entry ;
352367 struct index_state wtindex ;
353368 struct checkout lstate , rstate ;
354- int rc , flags = RUN_GIT_CMD , err = 0 ;
369+ int flags = RUN_GIT_CMD , err = 0 ;
355370 const char * helper_argv [] = { "difftool--helper" , NULL , NULL , NULL };
356371 struct hashmap wt_modified , tmp_modified ;
357372 int indices_loaded = 0 ;
@@ -360,11 +375,15 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
360375
361376 /* Setup temp directories */
362377 tmp = getenv ("TMPDIR" );
363- xsnprintf (tmpdir , sizeof (tmpdir ), "%s/git-difftool.XXXXXX" , tmp ? tmp : "/tmp" );
364- if (!mkdtemp (tmpdir ))
365- return error ("could not create '%s'" , tmpdir );
366- strbuf_addf (& ldir , "%s/left/" , tmpdir );
367- strbuf_addf (& rdir , "%s/right/" , tmpdir );
378+ strbuf_add_absolute_path (& tmpdir , tmp ? tmp : "/tmp" );
379+ strbuf_trim_trailing_dir_sep (& tmpdir );
380+ strbuf_addstr (& tmpdir , "/git-difftool.XXXXXX" );
381+ if (!mkdtemp (tmpdir .buf )) {
382+ ret = error ("could not create '%s'" , tmpdir .buf );
383+ goto finish ;
384+ }
385+ strbuf_addf (& ldir , "%s/left/" , tmpdir .buf );
386+ strbuf_addf (& rdir , "%s/right/" , tmpdir .buf );
368387 strbuf_addstr (& wtdir , workdir );
369388 if (!wtdir .len || !is_dir_sep (wtdir .buf [wtdir .len - 1 ]))
370389 strbuf_addch (& wtdir , '/' );
@@ -535,40 +554,19 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
535554 */
536555 hashmap_for_each_entry (& submodules , & iter , entry ,
537556 entry /* member name */ ) {
538- if (* entry -> left ) {
539- add_path (& ldir , ldir_len , entry -> path );
540- ensure_leading_directories (ldir .buf );
541- write_file (ldir .buf , "%s" , entry -> left );
542- }
543- if (* entry -> right ) {
544- add_path (& rdir , rdir_len , entry -> path );
545- ensure_leading_directories (rdir .buf );
546- write_file (rdir .buf , "%s" , entry -> right );
547- }
557+ write_standin_files (entry , & ldir , ldir_len , & rdir , rdir_len );
548558 }
549559
550560 /*
551- * Symbolic links require special treatment.The standard "git diff"
561+ * Symbolic links require special treatment. The standard "git diff"
552562 * shows only the link itself, not the contents of the link target.
553563 * This loop replicates that behavior.
554564 */
555565 hashmap_for_each_entry (& symlinks2 , & iter , entry ,
556566 entry /* member name */ ) {
557- if (* entry -> left ) {
558- add_path (& ldir , ldir_len , entry -> path );
559- ensure_leading_directories (ldir .buf );
560- unlink (ldir .buf );
561- write_file (ldir .buf , "%s" , entry -> left );
562- }
563- if (* entry -> right ) {
564- add_path (& rdir , rdir_len , entry -> path );
565- ensure_leading_directories (rdir .buf );
566- unlink (rdir .buf );
567- write_file (rdir .buf , "%s" , entry -> right );
568- }
569- }
570567
571- strbuf_release (& buf );
568+ write_standin_files (entry , & ldir , ldir_len , & rdir , rdir_len );
569+ }
572570
573571 strbuf_setlen (& ldir , ldir_len );
574572 helper_argv [1 ] = ldir .buf ;
@@ -580,7 +578,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
580578 flags = 0 ;
581579 } else
582580 setenv ("GIT_DIFFTOOL_DIRDIFF" , "true" , 1 );
583- rc = run_command_v_opt (helper_argv , flags );
581+ ret = run_command_v_opt (helper_argv , flags );
584582
585583 /* TODO: audit for interaction with sparse-index. */
586584 ensure_full_index (& wtindex );
@@ -614,7 +612,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
614612 if (!indices_loaded ) {
615613 struct lock_file lock = LOCK_INIT ;
616614 strbuf_reset (& buf );
617- strbuf_addf (& buf , "%s/wtindex" , tmpdir );
615+ strbuf_addf (& buf , "%s/wtindex" , tmpdir . buf );
618616 if (hold_lock_file_for_update (& lock , buf .buf , 0 ) < 0 ||
619617 write_locked_index (& wtindex , & lock , COMMIT_LOCK )) {
620618 ret = error ("could not write %s" , buf .buf );
@@ -644,11 +642,14 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
644642 }
645643
646644 if (err ) {
647- warning (_ ("temporary files exist in '%s'." ), tmpdir );
645+ warning (_ ("temporary files exist in '%s'." ), tmpdir . buf );
648646 warning (_ ("you may want to cleanup or recover these." ));
649- exit (1 );
650- } else
651- exit_cleanup (tmpdir , rc );
647+ ret = 1 ;
648+ } else {
649+ remove_dir_recursively (& tmpdir , 0 );
650+ if (ret )
651+ warning (_ ("failed: %d" ), ret );
652+ }
652653
653654finish :
654655 if (fp )
@@ -660,8 +661,9 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
660661 strbuf_release (& rdir );
661662 strbuf_release (& wtdir );
662663 strbuf_release (& buf );
664+ strbuf_release (& tmpdir );
663665
664- return ret ;
666+ return ( ret < 0 ) ? 1 : ret ;
665667}
666668
667669static int run_file_diff (int prompt , const char * prefix ,
0 commit comments