@@ -178,11 +178,12 @@ static void emit_rewrite_diff(const char *name_a,
178178 copy_file ('+' , temp [1 ].name );
179179}
180180
181- static void builtin_diff (const char * name_a ,
181+ static const char * builtin_diff (const char * name_a ,
182182 const char * name_b ,
183183 struct diff_tempfile * temp ,
184184 const char * xfrm_msg ,
185- int complete_rewrite )
185+ int complete_rewrite ,
186+ const char * * args )
186187{
187188 int i , next_at , cmd_size ;
188189 const char * const diff_cmd = "diff -L%s -L%s" ;
@@ -242,19 +243,24 @@ static void builtin_diff(const char *name_a,
242243 }
243244 if (xfrm_msg && xfrm_msg [0 ])
244245 puts (xfrm_msg );
246+ /*
247+ * we do not run diff between different kind
248+ * of objects.
249+ */
245250 if (strncmp (temp [0 ].mode , temp [1 ].mode , 3 ))
246- /* we do not run diff between different kind
247- * of objects.
248- */
249- exit (0 );
251+ return NULL ;
250252 if (complete_rewrite ) {
251- fflush (NULL );
252253 emit_rewrite_diff (name_a , name_b , temp );
253- exit ( 0 ) ;
254+ return NULL ;
254255 }
255256 }
256- fflush (NULL );
257- execlp ("/bin/sh" ,"sh" , "-c" , cmd , NULL );
257+
258+ /* This is disgusting */
259+ * args ++ = "sh" ;
260+ * args ++ = "-c" ;
261+ * args ++ = cmd ;
262+ * args = NULL ;
263+ return "/bin/sh" ;
258264}
259265
260266struct diff_filespec * alloc_filespec (const char * path )
@@ -559,6 +565,40 @@ static void remove_tempfile_on_signal(int signo)
559565 raise (signo );
560566}
561567
568+ static int spawn_prog (const char * pgm , const char * * arg )
569+ {
570+ pid_t pid ;
571+ int status ;
572+
573+ fflush (NULL );
574+ pid = fork ();
575+ if (pid < 0 )
576+ die ("unable to fork" );
577+ if (!pid ) {
578+ execvp (pgm , (char * const * ) arg );
579+ exit (255 );
580+ }
581+
582+ while (waitpid (pid , & status , 0 ) < 0 ) {
583+ if (errno == EINTR )
584+ continue ;
585+ return -1 ;
586+ }
587+
588+ /* Earlier we did not check the exit status because
589+ * diff exits non-zero if files are different, and
590+ * we are not interested in knowing that. It was a
591+ * mistake which made it harder to quit a diff-*
592+ * session that uses the git-apply-patch-script as
593+ * the GIT_EXTERNAL_DIFF. A custom GIT_EXTERNAL_DIFF
594+ * should also exit non-zero only when it wants to
595+ * abort the entire diff-* session.
596+ */
597+ if (WIFEXITED (status ) && !WEXITSTATUS (status ))
598+ return 0 ;
599+ return -1 ;
600+ }
601+
562602/* An external diff command takes:
563603 *
564604 * diff-cmd name infile1 infile1-sha1 infile1-mode \
@@ -573,9 +613,9 @@ static void run_external_diff(const char *pgm,
573613 const char * xfrm_msg ,
574614 int complete_rewrite )
575615{
616+ const char * spawn_arg [10 ];
576617 struct diff_tempfile * temp = diff_temp ;
577- pid_t pid ;
578- int status ;
618+ int retval ;
579619 static int atexit_asked = 0 ;
580620 const char * othername ;
581621
@@ -592,59 +632,41 @@ static void run_external_diff(const char *pgm,
592632 signal (SIGINT , remove_tempfile_on_signal );
593633 }
594634
595- fflush (NULL );
596- pid = fork ();
597- if (pid < 0 )
598- die ("unable to fork" );
599- if (!pid ) {
600- if (pgm ) {
601- if (one && two ) {
602- const char * exec_arg [10 ];
603- const char * * arg = & exec_arg [0 ];
604- * arg ++ = pgm ;
605- * arg ++ = name ;
606- * arg ++ = temp [0 ].name ;
607- * arg ++ = temp [0 ].hex ;
608- * arg ++ = temp [0 ].mode ;
609- * arg ++ = temp [1 ].name ;
610- * arg ++ = temp [1 ].hex ;
611- * arg ++ = temp [1 ].mode ;
612- if (other ) {
613- * arg ++ = other ;
614- * arg ++ = xfrm_msg ;
615- }
616- * arg = NULL ;
617- execvp (pgm , (char * const * ) exec_arg );
635+ if (pgm ) {
636+ const char * * arg = & spawn_arg [0 ];
637+ if (one && two ) {
638+ * arg ++ = pgm ;
639+ * arg ++ = name ;
640+ * arg ++ = temp [0 ].name ;
641+ * arg ++ = temp [0 ].hex ;
642+ * arg ++ = temp [0 ].mode ;
643+ * arg ++ = temp [1 ].name ;
644+ * arg ++ = temp [1 ].hex ;
645+ * arg ++ = temp [1 ].mode ;
646+ if (other ) {
647+ * arg ++ = other ;
648+ * arg ++ = xfrm_msg ;
618649 }
619- else
620- execlp (pgm , pgm , name , NULL );
650+ } else {
651+ * arg ++ = pgm ;
652+ * arg ++ = name ;
621653 }
622- /*
623- * otherwise we use the built-in one.
624- */
625- if (one && two )
626- builtin_diff (name , othername , temp , xfrm_msg ,
627- complete_rewrite );
628- else
654+ * arg = NULL ;
655+ } else {
656+ if (one && two ) {
657+ pgm = builtin_diff (name , othername , temp , xfrm_msg , complete_rewrite , spawn_arg );
658+ } else
629659 printf ("* Unmerged path %s\n" , name );
630- exit (0 );
631660 }
632- if (waitpid (pid , & status , 0 ) < 0 ||
633- !WIFEXITED (status ) || WEXITSTATUS (status )) {
634- /* Earlier we did not check the exit status because
635- * diff exits non-zero if files are different, and
636- * we are not interested in knowing that. It was a
637- * mistake which made it harder to quit a diff-*
638- * session that uses the git-apply-patch-script as
639- * the GIT_EXTERNAL_DIFF. A custom GIT_EXTERNAL_DIFF
640- * should also exit non-zero only when it wants to
641- * abort the entire diff-* session.
642- */
643- remove_tempfile ();
661+
662+ retval = 0 ;
663+ if (pgm )
664+ retval = spawn_prog (pgm , spawn_arg );
665+ remove_tempfile ();
666+ if (retval ) {
644667 fprintf (stderr , "external diff died, stopping at %s.\n" , name );
645668 exit (1 );
646669 }
647- remove_tempfile ();
648670}
649671
650672static void diff_fill_sha1_info (struct diff_filespec * one )
0 commit comments