88#include "quote.h"
99#include "diff.h"
1010#include "diffcore.h"
11- #include "xdiff/xdiff .h"
11+ #include "xdiff-interface .h"
1212
1313static int use_size_cache ;
1414
@@ -195,6 +195,137 @@ static int fn_out(void *priv, mmbuffer_t *mb, int nbuf)
195195 return 0 ;
196196}
197197
198+ struct diffstat_t {
199+ struct xdiff_emit_state xm ;
200+
201+ int nr ;
202+ int alloc ;
203+ struct diffstat_file {
204+ char * name ;
205+ unsigned int added , deleted ;
206+ } * * files ;
207+ };
208+
209+ static struct diffstat_file * diffstat_add (struct diffstat_t * diffstat ,
210+ const char * name )
211+ {
212+ struct diffstat_file * x ;
213+ x = xcalloc (sizeof (* x ), 1 );
214+ if (diffstat -> nr == diffstat -> alloc ) {
215+ diffstat -> alloc = alloc_nr (diffstat -> alloc );
216+ diffstat -> files = xrealloc (diffstat -> files ,
217+ diffstat -> alloc * sizeof (x ));
218+ }
219+ diffstat -> files [diffstat -> nr ++ ] = x ;
220+ x -> name = strdup (name );
221+ return x ;
222+ }
223+
224+ static void diffstat_consume (void * priv , char * line , unsigned long len )
225+ {
226+ struct diffstat_t * diffstat = priv ;
227+ struct diffstat_file * x = diffstat -> files [diffstat -> nr - 1 ];
228+
229+ if (line [0 ] == '+' )
230+ x -> added ++ ;
231+ else if (line [0 ] == '-' )
232+ x -> deleted ++ ;
233+ }
234+
235+ static const char pluses [] = "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" ;
236+ static const char minuses []= "----------------------------------------------------------------------" ;
237+
238+ static void show_stats (struct diffstat_t * data )
239+ {
240+ char * prefix = "" ;
241+ int i , len , add , del , total , adds = 0 , dels = 0 ;
242+ int max , max_change = 0 , max_len = 0 ;
243+ int total_files = data -> nr ;
244+
245+ if (data -> nr == 0 )
246+ return ;
247+
248+ printf ("---\n" );
249+
250+ for (i = 0 ; i < data -> nr ; i ++ ) {
251+ struct diffstat_file * file = data -> files [i ];
252+
253+ if (max_change < file -> added + file -> deleted )
254+ max_change = file -> added + file -> deleted ;
255+ len = strlen (file -> name );
256+ if (max_len < len )
257+ max_len = len ;
258+ }
259+
260+ for (i = 0 ; i < data -> nr ; i ++ ) {
261+ char * name = data -> files [i ]-> name ;
262+ int added = data -> files [i ]-> added ;
263+ int deleted = data -> files [i ]-> deleted ;
264+
265+ if (0 < (len = quote_c_style (name , NULL , NULL , 0 ))) {
266+ char * qname = xmalloc (len + 1 );
267+ quote_c_style (name , qname , NULL , 0 );
268+ free (name );
269+ name = qname ;
270+ }
271+
272+ /*
273+ * "scale" the filename
274+ */
275+ len = strlen (name );
276+ max = max_len ;
277+ if (max > 50 )
278+ max = 50 ;
279+ if (len > max ) {
280+ char * slash ;
281+ prefix = "..." ;
282+ max -= 3 ;
283+ name += len - max ;
284+ slash = strchr (name , '/' );
285+ if (slash )
286+ name = slash ;
287+ }
288+ len = max ;
289+
290+ /*
291+ * scale the add/delete
292+ */
293+ max = max_change ;
294+ if (max + len > 70 )
295+ max = 70 - len ;
296+
297+ if (added < 0 ) {
298+ /* binary file */
299+ printf (" %s%-*s | Bin\n" , prefix , len , name );
300+ continue ;
301+ } else if (added + deleted == 0 ) {
302+ total_files -- ;
303+ continue ;
304+ }
305+
306+ add = added ;
307+ del = deleted ;
308+ total = add + del ;
309+ adds += add ;
310+ dels += del ;
311+
312+ if (max_change > 0 ) {
313+ total = (total * max + max_change / 2 ) / max_change ;
314+ add = (add * max + max_change / 2 ) / max_change ;
315+ del = total - add ;
316+ }
317+ /* TODO: binary */
318+ printf (" %s%-*s |%5d %.*s%.*s\n" , prefix ,
319+ len , name , added + deleted ,
320+ add , pluses , del , minuses );
321+ free (name );
322+ free (data -> files [i ]);
323+ }
324+ free (data -> files );
325+ printf (" %d files changed, %d insertions(+), %d deletions(-)\n" ,
326+ total_files , adds , dels );
327+ }
328+
198329#define FIRST_FEW_BYTES 8000
199330static int mmfile_is_binary (mmfile_t * mf )
200331{
@@ -286,6 +417,35 @@ static void builtin_diff(const char *name_a,
286417 return ;
287418}
288419
420+ static void builtin_diffstat (const char * name_a , const char * name_b ,
421+ struct diff_filespec * one , struct diff_filespec * two ,
422+ struct diffstat_t * diffstat )
423+ {
424+ mmfile_t mf1 , mf2 ;
425+ struct diffstat_file * data ;
426+
427+ data = diffstat_add (diffstat , name_a ? name_a : name_b );
428+
429+ if (fill_mmfile (& mf1 , one ) < 0 || fill_mmfile (& mf2 , two ) < 0 )
430+ die ("unable to read files to diff" );
431+
432+ if (mmfile_is_binary (& mf1 ) || mmfile_is_binary (& mf2 ))
433+ data -> added = -1 ;
434+ else {
435+ /* Crazy xdl interfaces.. */
436+ xpparam_t xpp ;
437+ xdemitconf_t xecfg ;
438+ xdemitcb_t ecb ;
439+
440+ xpp .flags = XDF_NEED_MINIMAL ;
441+ xecfg .ctxlen = 3 ;
442+ xecfg .flags = XDL_EMIT_FUNCNAMES ;
443+ ecb .outf = xdiff_outf ;
444+ ecb .priv = diffstat ;
445+ xdl_diff (& mf1 , & mf2 , & xpp , & xecfg , & ecb );
446+ }
447+ }
448+
289449struct diff_filespec * alloc_filespec (const char * path )
290450{
291451 int namelen = strlen (path );
@@ -819,6 +979,27 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
819979 free (other_munged );
820980}
821981
982+ static void run_diffstat (struct diff_filepair * p , struct diff_options * o ,
983+ struct diffstat_t * diffstat )
984+ {
985+ const char * name ;
986+ const char * other ;
987+
988+ if (DIFF_PAIR_UNMERGED (p )) {
989+ /* unmerged */
990+ builtin_diffstat (p -> one -> path , NULL , NULL , NULL , diffstat );
991+ return ;
992+ }
993+
994+ name = p -> one -> path ;
995+ other = (strcmp (name , p -> two -> path ) ? p -> two -> path : NULL );
996+
997+ diff_fill_sha1_info (p -> one );
998+ diff_fill_sha1_info (p -> two );
999+
1000+ builtin_diffstat (name , other , p -> one , p -> two , diffstat );
1001+ }
1002+
8221003void diff_setup (struct diff_options * options )
8231004{
8241005 memset (options , 0 , sizeof (* options ));
@@ -866,6 +1047,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
8661047 options -> output_format = DIFF_FORMAT_PATCH ;
8671048 options -> with_raw = 1 ;
8681049 }
1050+ else if (!strcmp (arg , "--stat" ))
1051+ options -> output_format = DIFF_FORMAT_DIFFSTAT ;
8691052 else if (!strcmp (arg , "-z" ))
8701053 options -> line_termination = 0 ;
8711054 else if (!strncmp (arg , "-l" , 2 ))
@@ -1160,11 +1343,24 @@ static void diff_flush_patch(struct diff_filepair *p, struct diff_options *o)
11601343
11611344 if ((DIFF_FILE_VALID (p -> one ) && S_ISDIR (p -> one -> mode )) ||
11621345 (DIFF_FILE_VALID (p -> two ) && S_ISDIR (p -> two -> mode )))
1163- return ; /* no tree diffs in patch format */
1346+ return ; /* no tree diffs in patch format */
11641347
11651348 run_diff (p , o );
11661349}
11671350
1351+ static void diff_flush_stat (struct diff_filepair * p , struct diff_options * o ,
1352+ struct diffstat_t * diffstat )
1353+ {
1354+ if (diff_unmodified_pair (p ))
1355+ return ;
1356+
1357+ if ((DIFF_FILE_VALID (p -> one ) && S_ISDIR (p -> one -> mode )) ||
1358+ (DIFF_FILE_VALID (p -> two ) && S_ISDIR (p -> two -> mode )))
1359+ return ; /* no tree diffs in patch format */
1360+
1361+ run_diffstat (p , o , diffstat );
1362+ }
1363+
11681364int diff_queue_is_empty (void )
11691365{
11701366 struct diff_queue_struct * q = & diff_queued_diff ;
@@ -1276,7 +1472,8 @@ static void diff_resolve_rename_copy(void)
12761472
12771473static void flush_one_pair (struct diff_filepair * p ,
12781474 int diff_output_format ,
1279- struct diff_options * options )
1475+ struct diff_options * options ,
1476+ struct diffstat_t * diffstat )
12801477{
12811478 int inter_name_termination = '\t' ;
12821479 int line_termination = options -> line_termination ;
@@ -1291,6 +1488,9 @@ static void flush_one_pair(struct diff_filepair *p,
12911488 break ;
12921489 default :
12931490 switch (diff_output_format ) {
1491+ case DIFF_FORMAT_DIFFSTAT :
1492+ diff_flush_stat (p , options , diffstat );
1493+ break ;
12941494 case DIFF_FORMAT_PATCH :
12951495 diff_flush_patch (p , options );
12961496 break ;
@@ -1316,19 +1516,31 @@ void diff_flush(struct diff_options *options)
13161516 struct diff_queue_struct * q = & diff_queued_diff ;
13171517 int i ;
13181518 int diff_output_format = options -> output_format ;
1519+ struct diffstat_t * diffstat = NULL ;
1520+
1521+ if (diff_output_format == DIFF_FORMAT_DIFFSTAT ) {
1522+ diffstat = xcalloc (sizeof (struct diffstat_t ), 1 );
1523+ diffstat -> xm .consume = diffstat_consume ;
1524+ }
13191525
13201526 if (options -> with_raw ) {
13211527 for (i = 0 ; i < q -> nr ; i ++ ) {
13221528 struct diff_filepair * p = q -> queue [i ];
1323- flush_one_pair (p , DIFF_FORMAT_RAW , options );
1529+ flush_one_pair (p , DIFF_FORMAT_RAW , options , NULL );
13241530 }
13251531 putchar (options -> line_termination );
13261532 }
13271533 for (i = 0 ; i < q -> nr ; i ++ ) {
13281534 struct diff_filepair * p = q -> queue [i ];
1329- flush_one_pair (p , diff_output_format , options );
1535+ flush_one_pair (p , diff_output_format , options , diffstat );
13301536 diff_free_filepair (p );
13311537 }
1538+
1539+ if (diffstat ) {
1540+ show_stats (diffstat );
1541+ free (diffstat );
1542+ }
1543+
13321544 free (q -> queue );
13331545 q -> queue = NULL ;
13341546 q -> nr = q -> alloc = 0 ;
0 commit comments