@@ -31,6 +31,7 @@ static const char *external_diff_cmd_cfg;
31
31
int diff_auto_refresh_index = 1 ;
32
32
static int diff_mnemonic_prefix ;
33
33
static int diff_no_prefix ;
34
+ static int diff_dirstat_permille_default = 30 ;
34
35
static struct diff_options default_diff_options ;
35
36
36
37
static char diff_colors [][COLOR_MAXLEN ] = {
@@ -66,6 +67,58 @@ static int parse_diff_color_slot(const char *var, int ofs)
66
67
return -1 ;
67
68
}
68
69
70
+ static int parse_dirstat_params (struct diff_options * options , const char * params ,
71
+ struct strbuf * errmsg )
72
+ {
73
+ const char * p = params ;
74
+ int p_len , ret = 0 ;
75
+
76
+ while (* p ) {
77
+ p_len = strchrnul (p , ',' ) - p ;
78
+ if (!memcmp (p , "changes" , p_len )) {
79
+ DIFF_OPT_CLR (options , DIRSTAT_BY_LINE );
80
+ DIFF_OPT_CLR (options , DIRSTAT_BY_FILE );
81
+ } else if (!memcmp (p , "lines" , p_len )) {
82
+ DIFF_OPT_SET (options , DIRSTAT_BY_LINE );
83
+ DIFF_OPT_CLR (options , DIRSTAT_BY_FILE );
84
+ } else if (!memcmp (p , "files" , p_len )) {
85
+ DIFF_OPT_CLR (options , DIRSTAT_BY_LINE );
86
+ DIFF_OPT_SET (options , DIRSTAT_BY_FILE );
87
+ } else if (!memcmp (p , "noncumulative" , p_len )) {
88
+ DIFF_OPT_CLR (options , DIRSTAT_CUMULATIVE );
89
+ } else if (!memcmp (p , "cumulative" , p_len )) {
90
+ DIFF_OPT_SET (options , DIRSTAT_CUMULATIVE );
91
+ } else if (isdigit (* p )) {
92
+ char * end ;
93
+ int permille = strtoul (p , & end , 10 ) * 10 ;
94
+ if (* end == '.' && isdigit (* ++ end )) {
95
+ /* only use first digit */
96
+ permille += * end - '0' ;
97
+ /* .. and ignore any further digits */
98
+ while (isdigit (* ++ end ))
99
+ ; /* nothing */
100
+ }
101
+ if (end - p == p_len )
102
+ options -> dirstat_permille = permille ;
103
+ else {
104
+ strbuf_addf (errmsg , _ (" Failed to parse dirstat cut-off percentage '%.*s'\n" ),
105
+ p_len , p );
106
+ ret ++ ;
107
+ }
108
+ } else {
109
+ strbuf_addf (errmsg , _ (" Unknown dirstat parameter '%.*s'\n" ),
110
+ p_len , p );
111
+ ret ++ ;
112
+ }
113
+
114
+ p += p_len ;
115
+
116
+ if (* p )
117
+ p ++ ; /* more parameters, swallow separator */
118
+ }
119
+ return ret ;
120
+ }
121
+
69
122
static int git_config_rename (const char * var , const char * value )
70
123
{
71
124
if (!value )
@@ -145,6 +198,17 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
145
198
return 0 ;
146
199
}
147
200
201
+ if (!strcmp (var , "diff.dirstat" )) {
202
+ struct strbuf errmsg = STRBUF_INIT ;
203
+ default_diff_options .dirstat_permille = diff_dirstat_permille_default ;
204
+ if (parse_dirstat_params (& default_diff_options , value , & errmsg ))
205
+ warning (_ ("Found errors in 'diff.dirstat' config variable:\n%s" ),
206
+ errmsg .buf );
207
+ strbuf_release (& errmsg );
208
+ diff_dirstat_permille_default = default_diff_options .dirstat_permille ;
209
+ return 0 ;
210
+ }
211
+
148
212
if (!prefixcmp (var , "submodule." ))
149
213
return parse_submodule_config_option (var , value );
150
214
@@ -1455,7 +1519,7 @@ struct dirstat_file {
1455
1519
1456
1520
struct dirstat_dir {
1457
1521
struct dirstat_file * files ;
1458
- int alloc , nr , percent , cumulative ;
1522
+ int alloc , nr , permille , cumulative ;
1459
1523
};
1460
1524
1461
1525
static long gather_dirstat (struct diff_options * opt , struct dirstat_dir * dir ,
@@ -1502,12 +1566,11 @@ static long gather_dirstat(struct diff_options *opt, struct dirstat_dir *dir,
1502
1566
* under this directory (sources == 1).
1503
1567
*/
1504
1568
if (baselen && sources != 1 ) {
1505
- int permille = this_dir * 1000 / changed ;
1506
- if (permille ) {
1507
- int percent = permille / 10 ;
1508
- if (percent >= dir -> percent ) {
1569
+ if (this_dir ) {
1570
+ int permille = this_dir * 1000 / changed ;
1571
+ if (permille >= dir -> permille ) {
1509
1572
fprintf (opt -> file , "%s%4d.%01d%% %.*s\n" , line_prefix ,
1510
- percent , permille % 10 , baselen , base );
1573
+ permille / 10 , permille % 10 , baselen , base );
1511
1574
if (!dir -> cumulative )
1512
1575
return 0 ;
1513
1576
}
@@ -1533,7 +1596,7 @@ static void show_dirstat(struct diff_options *options)
1533
1596
dir .files = NULL ;
1534
1597
dir .alloc = 0 ;
1535
1598
dir .nr = 0 ;
1536
- dir .percent = options -> dirstat_percent ;
1599
+ dir .permille = options -> dirstat_permille ;
1537
1600
dir .cumulative = DIFF_OPT_TST (options , DIRSTAT_CUMULATIVE );
1538
1601
1539
1602
changed = 0 ;
@@ -1622,6 +1685,50 @@ static void show_dirstat(struct diff_options *options)
1622
1685
gather_dirstat (options , & dir , changed , "" , 0 );
1623
1686
}
1624
1687
1688
+ static void show_dirstat_by_line (struct diffstat_t * data , struct diff_options * options )
1689
+ {
1690
+ int i ;
1691
+ unsigned long changed ;
1692
+ struct dirstat_dir dir ;
1693
+
1694
+ if (data -> nr == 0 )
1695
+ return ;
1696
+
1697
+ dir .files = NULL ;
1698
+ dir .alloc = 0 ;
1699
+ dir .nr = 0 ;
1700
+ dir .permille = options -> dirstat_permille ;
1701
+ dir .cumulative = DIFF_OPT_TST (options , DIRSTAT_CUMULATIVE );
1702
+
1703
+ changed = 0 ;
1704
+ for (i = 0 ; i < data -> nr ; i ++ ) {
1705
+ struct diffstat_file * file = data -> files [i ];
1706
+ unsigned long damage = file -> added + file -> deleted ;
1707
+ if (file -> is_binary )
1708
+ /*
1709
+ * binary files counts bytes, not lines. Must find some
1710
+ * way to normalize binary bytes vs. textual lines.
1711
+ * The following heuristic assumes that there are 64
1712
+ * bytes per "line".
1713
+ * This is stupid and ugly, but very cheap...
1714
+ */
1715
+ damage = (damage + 63 ) / 64 ;
1716
+ ALLOC_GROW (dir .files , dir .nr + 1 , dir .alloc );
1717
+ dir .files [dir .nr ].name = file -> name ;
1718
+ dir .files [dir .nr ].changed = damage ;
1719
+ changed += damage ;
1720
+ dir .nr ++ ;
1721
+ }
1722
+
1723
+ /* This can happen even with many files, if everything was renames */
1724
+ if (!changed )
1725
+ return ;
1726
+
1727
+ /* Show all directories with more than x% of the changes */
1728
+ qsort (dir .files , dir .nr , sizeof (dir .files [0 ]), dirstat_compare );
1729
+ gather_dirstat (options , & dir , changed , "" , 0 );
1730
+ }
1731
+
1625
1732
static void free_diffstat_info (struct diffstat_t * diffstat )
1626
1733
{
1627
1734
int i ;
@@ -2891,7 +2998,7 @@ void diff_setup(struct diff_options *options)
2891
2998
options -> line_termination = '\n' ;
2892
2999
options -> break_opt = -1 ;
2893
3000
options -> rename_limit = -1 ;
2894
- options -> dirstat_percent = 3 ;
3001
+ options -> dirstat_permille = diff_dirstat_permille_default ;
2895
3002
options -> context = 3 ;
2896
3003
2897
3004
options -> change = diff_change ;
@@ -3149,6 +3256,21 @@ static int stat_opt(struct diff_options *options, const char **av)
3149
3256
return argcount ;
3150
3257
}
3151
3258
3259
+ static int parse_dirstat_opt (struct diff_options * options , const char * params )
3260
+ {
3261
+ struct strbuf errmsg = STRBUF_INIT ;
3262
+ if (parse_dirstat_params (options , params , & errmsg ))
3263
+ die (_ ("Failed to parse --dirstat/-X option parameter:\n%s" ),
3264
+ errmsg .buf );
3265
+ strbuf_release (& errmsg );
3266
+ /*
3267
+ * The caller knows a dirstat-related option is given from the command
3268
+ * line; allow it to say "return this_function();"
3269
+ */
3270
+ options -> output_format |= DIFF_FORMAT_DIRSTAT ;
3271
+ return 1 ;
3272
+ }
3273
+
3152
3274
int diff_opt_parse (struct diff_options * options , const char * * av , int ac )
3153
3275
{
3154
3276
const char * arg = av [0 ];
@@ -3168,15 +3290,19 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
3168
3290
options -> output_format |= DIFF_FORMAT_NUMSTAT ;
3169
3291
else if (!strcmp (arg , "--shortstat" ))
3170
3292
options -> output_format |= DIFF_FORMAT_SHORTSTAT ;
3171
- else if (opt_arg (arg , 'X' , "dirstat" , & options -> dirstat_percent ))
3172
- options -> output_format |= DIFF_FORMAT_DIRSTAT ;
3173
- else if (!strcmp (arg , "--cumulative" )) {
3174
- options -> output_format |= DIFF_FORMAT_DIRSTAT ;
3175
- DIFF_OPT_SET (options , DIRSTAT_CUMULATIVE );
3176
- } else if (opt_arg (arg , 0 , "dirstat-by-file" ,
3177
- & options -> dirstat_percent )) {
3178
- options -> output_format |= DIFF_FORMAT_DIRSTAT ;
3179
- DIFF_OPT_SET (options , DIRSTAT_BY_FILE );
3293
+ else if (!strcmp (arg , "-X" ) || !strcmp (arg , "--dirstat" ))
3294
+ return parse_dirstat_opt (options , "" );
3295
+ else if (!prefixcmp (arg , "-X" ))
3296
+ return parse_dirstat_opt (options , arg + 2 );
3297
+ else if (!prefixcmp (arg , "--dirstat=" ))
3298
+ return parse_dirstat_opt (options , arg + 10 );
3299
+ else if (!strcmp (arg , "--cumulative" ))
3300
+ return parse_dirstat_opt (options , "cumulative" );
3301
+ else if (!strcmp (arg , "--dirstat-by-file" ))
3302
+ return parse_dirstat_opt (options , "files" );
3303
+ else if (!prefixcmp (arg , "--dirstat-by-file=" )) {
3304
+ parse_dirstat_opt (options , "files" );
3305
+ return parse_dirstat_opt (options , arg + 18 );
3180
3306
}
3181
3307
else if (!strcmp (arg , "--check" ))
3182
3308
options -> output_format |= DIFF_FORMAT_CHECKDIFF ;
@@ -4023,6 +4149,7 @@ void diff_flush(struct diff_options *options)
4023
4149
struct diff_queue_struct * q = & diff_queued_diff ;
4024
4150
int i , output_format = options -> output_format ;
4025
4151
int separator = 0 ;
4152
+ int dirstat_by_line = 0 ;
4026
4153
4027
4154
/*
4028
4155
* Order: raw, stat, summary, patch
@@ -4043,7 +4170,11 @@ void diff_flush(struct diff_options *options)
4043
4170
separator ++ ;
4044
4171
}
4045
4172
4046
- if (output_format & (DIFF_FORMAT_DIFFSTAT |DIFF_FORMAT_SHORTSTAT |DIFF_FORMAT_NUMSTAT )) {
4173
+ if (output_format & DIFF_FORMAT_DIRSTAT && DIFF_OPT_TST (options , DIRSTAT_BY_LINE ))
4174
+ dirstat_by_line = 1 ;
4175
+
4176
+ if (output_format & (DIFF_FORMAT_DIFFSTAT |DIFF_FORMAT_SHORTSTAT |DIFF_FORMAT_NUMSTAT ) ||
4177
+ dirstat_by_line ) {
4047
4178
struct diffstat_t diffstat ;
4048
4179
4049
4180
memset (& diffstat , 0 , sizeof (struct diffstat_t ));
@@ -4058,10 +4189,12 @@ void diff_flush(struct diff_options *options)
4058
4189
show_stats (& diffstat , options );
4059
4190
if (output_format & DIFF_FORMAT_SHORTSTAT )
4060
4191
show_shortstats (& diffstat , options );
4192
+ if (output_format & DIFF_FORMAT_DIRSTAT )
4193
+ show_dirstat_by_line (& diffstat , options );
4061
4194
free_diffstat_info (& diffstat );
4062
4195
separator ++ ;
4063
4196
}
4064
- if (output_format & DIFF_FORMAT_DIRSTAT )
4197
+ if (( output_format & DIFF_FORMAT_DIRSTAT ) && ! dirstat_by_line )
4065
4198
show_dirstat (options );
4066
4199
4067
4200
if (output_format & DIFF_FORMAT_SUMMARY && !is_summary_empty (q )) {
0 commit comments