@@ -61,6 +61,13 @@ static enum ws_error_action {
61
61
static int whitespace_error ;
62
62
static int squelch_whitespace_errors = 5 ;
63
63
static int applied_after_fixing_ws ;
64
+
65
+ static enum ws_ignore {
66
+ ignore_ws_none ,
67
+ ignore_ws_change ,
68
+ } ws_ignore_action = ignore_ws_none ;
69
+
70
+
64
71
static const char * patch_input_file ;
65
72
static const char * root ;
66
73
static int root_len ;
@@ -97,6 +104,21 @@ static void parse_whitespace_option(const char *option)
97
104
die ("unrecognized whitespace option '%s'" , option );
98
105
}
99
106
107
+ static void parse_ignorewhitespace_option (const char * option )
108
+ {
109
+ if (!option || !strcmp (option , "no" ) ||
110
+ !strcmp (option , "false" ) || !strcmp (option , "never" ) ||
111
+ !strcmp (option , "none" )) {
112
+ ws_ignore_action = ignore_ws_none ;
113
+ return ;
114
+ }
115
+ if (!strcmp (option , "change" )) {
116
+ ws_ignore_action = ignore_ws_change ;
117
+ return ;
118
+ }
119
+ die ("unrecognized whitespace ignore option '%s'" , option );
120
+ }
121
+
100
122
static void set_default_whitespace_mode (const char * whitespace_option )
101
123
{
102
124
if (!whitespace_option && !apply_default_whitespace )
@@ -214,6 +236,62 @@ static uint32_t hash_line(const char *cp, size_t len)
214
236
return h ;
215
237
}
216
238
239
+ /*
240
+ * Compare lines s1 of length n1 and s2 of length n2, ignoring
241
+ * whitespace difference. Returns 1 if they match, 0 otherwise
242
+ */
243
+ static int fuzzy_matchlines (const char * s1 , size_t n1 ,
244
+ const char * s2 , size_t n2 )
245
+ {
246
+ const char * last1 = s1 + n1 - 1 ;
247
+ const char * last2 = s2 + n2 - 1 ;
248
+ int result = 0 ;
249
+
250
+ if (n1 < 0 || n2 < 0 )
251
+ return 0 ;
252
+
253
+ /* ignore line endings */
254
+ while ((* last1 == '\r' ) || (* last1 == '\n' ))
255
+ last1 -- ;
256
+ while ((* last2 == '\r' ) || (* last2 == '\n' ))
257
+ last2 -- ;
258
+
259
+ /* skip leading whitespace */
260
+ while (isspace (* s1 ) && (s1 <= last1 ))
261
+ s1 ++ ;
262
+ while (isspace (* s2 ) && (s2 <= last2 ))
263
+ s2 ++ ;
264
+ /* early return if both lines are empty */
265
+ if ((s1 > last1 ) && (s2 > last2 ))
266
+ return 1 ;
267
+ while (!result ) {
268
+ result = * s1 ++ - * s2 ++ ;
269
+ /*
270
+ * Skip whitespace inside. We check for whitespace on
271
+ * both buffers because we don't want "a b" to match
272
+ * "ab"
273
+ */
274
+ if (isspace (* s1 ) && isspace (* s2 )) {
275
+ while (isspace (* s1 ) && s1 <= last1 )
276
+ s1 ++ ;
277
+ while (isspace (* s2 ) && s2 <= last2 )
278
+ s2 ++ ;
279
+ }
280
+ /*
281
+ * If we reached the end on one side only,
282
+ * lines don't match
283
+ */
284
+ if (
285
+ ((s2 > last2 ) && (s1 <= last1 )) ||
286
+ ((s1 > last1 ) && (s2 <= last2 )))
287
+ return 0 ;
288
+ if ((s1 > last1 ) && (s2 > last2 ))
289
+ break ;
290
+ }
291
+
292
+ return !result ;
293
+ }
294
+
217
295
static void add_line_info (struct image * img , const char * bol , size_t len , unsigned flag )
218
296
{
219
297
ALLOC_GROW (img -> line_allocated , img -> nr + 1 , img -> alloc );
@@ -1592,10 +1670,17 @@ static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)
1592
1670
}
1593
1671
}
1594
1672
1673
+ /*
1674
+ * Update the preimage, and the common lines in postimage,
1675
+ * from buffer buf of length len. If postlen is 0 the postimage
1676
+ * is updated in place, otherwise it's updated on a new buffer
1677
+ * of length postlen
1678
+ */
1679
+
1595
1680
static void update_pre_post_images (struct image * preimage ,
1596
1681
struct image * postimage ,
1597
1682
char * buf ,
1598
- size_t len )
1683
+ size_t len , size_t postlen )
1599
1684
{
1600
1685
int i , ctx ;
1601
1686
char * new , * old , * fixed ;
@@ -1614,11 +1699,19 @@ static void update_pre_post_images(struct image *preimage,
1614
1699
* preimage = fixed_preimage ;
1615
1700
1616
1701
/*
1617
- * Adjust the common context lines in postimage, in place.
1618
- * This is possible because whitespace fixing does not make
1619
- * the string grow.
1702
+ * Adjust the common context lines in postimage. This can be
1703
+ * done in-place when we are just doing whitespace fixing,
1704
+ * which does not make the string grow, but needs a new buffer
1705
+ * when ignoring whitespace causes the update, since in this case
1706
+ * we could have e.g. tabs converted to multiple spaces.
1707
+ * We trust the caller to tell us if the update can be done
1708
+ * in place (postlen==0) or not.
1620
1709
*/
1621
- new = old = postimage -> buf ;
1710
+ old = postimage -> buf ;
1711
+ if (postlen )
1712
+ new = postimage -> buf = xmalloc (postlen );
1713
+ else
1714
+ new = old ;
1622
1715
fixed = preimage -> buf ;
1623
1716
for (i = ctx = 0 ; i < postimage -> nr ; i ++ ) {
1624
1717
size_t len = postimage -> line [i ].len ;
@@ -1693,12 +1786,58 @@ static int match_fragment(struct image *img,
1693
1786
!memcmp (img -> buf + try , preimage -> buf , preimage -> len ))
1694
1787
return 1 ;
1695
1788
1789
+ /*
1790
+ * No exact match. If we are ignoring whitespace, run a line-by-line
1791
+ * fuzzy matching. We collect all the line length information because
1792
+ * we need it to adjust whitespace if we match.
1793
+ */
1794
+ if (ws_ignore_action == ignore_ws_change ) {
1795
+ size_t imgoff = 0 ;
1796
+ size_t preoff = 0 ;
1797
+ size_t postlen = postimage -> len ;
1798
+ size_t imglen [preimage -> nr ];
1799
+ for (i = 0 ; i < preimage -> nr ; i ++ ) {
1800
+ size_t prelen = preimage -> line [i ].len ;
1801
+
1802
+ imglen [i ] = img -> line [try_lno + i ].len ;
1803
+ if (!fuzzy_matchlines (
1804
+ img -> buf + try + imgoff , imglen [i ],
1805
+ preimage -> buf + preoff , prelen ))
1806
+ return 0 ;
1807
+ if (preimage -> line [i ].flag & LINE_COMMON )
1808
+ postlen += imglen [i ] - prelen ;
1809
+ imgoff += imglen [i ];
1810
+ preoff += prelen ;
1811
+ }
1812
+
1813
+ /*
1814
+ * Ok, the preimage matches with whitespace fuzz. Update it and
1815
+ * the common postimage lines to use the same whitespace as the
1816
+ * target. imgoff now holds the true length of the target that
1817
+ * matches the preimage, and we need to update the line lengths
1818
+ * of the preimage to match the target ones.
1819
+ */
1820
+ fixed_buf = xmalloc (imgoff );
1821
+ memcpy (fixed_buf , img -> buf + try , imgoff );
1822
+ for (i = 0 ; i < preimage -> nr ; i ++ )
1823
+ preimage -> line [i ].len = imglen [i ];
1824
+
1825
+ /*
1826
+ * Update the preimage buffer and the postimage context lines.
1827
+ */
1828
+ update_pre_post_images (preimage , postimage ,
1829
+ fixed_buf , imgoff , postlen );
1830
+ return 1 ;
1831
+ }
1832
+
1696
1833
if (ws_error_action != correct_ws_error )
1697
1834
return 0 ;
1698
1835
1699
1836
/*
1700
1837
* The hunk does not apply byte-by-byte, but the hash says
1701
- * it might with whitespace fuzz.
1838
+ * it might with whitespace fuzz. We haven't been asked to
1839
+ * ignore whitespace, we were asked to correct whitespace
1840
+ * errors, so let's try matching after whitespace correction.
1702
1841
*/
1703
1842
fixed_buf = xmalloc (preimage -> len + 1 );
1704
1843
buf = fixed_buf ;
@@ -1750,7 +1889,7 @@ static int match_fragment(struct image *img,
1750
1889
* hunk match. Update the context lines in the postimage.
1751
1890
*/
1752
1891
update_pre_post_images (preimage , postimage ,
1753
- fixed_buf , buf - fixed_buf );
1892
+ fixed_buf , buf - fixed_buf , 0 );
1754
1893
return 1 ;
1755
1894
1756
1895
unmatch_exit :
@@ -3192,6 +3331,8 @@ static int git_apply_config(const char *var, const char *value, void *cb)
3192
3331
{
3193
3332
if (!strcmp (var , "apply.whitespace" ))
3194
3333
return git_config_string (& apply_default_whitespace , var , value );
3334
+ else if (!strcmp (var , "apply.ignorewhitespace" ))
3335
+ return git_config_string (& apply_default_ignorewhitespace , var , value );
3195
3336
return git_default_config (var , value , cb );
3196
3337
}
3197
3338
@@ -3228,6 +3369,16 @@ static int option_parse_z(const struct option *opt,
3228
3369
return 0 ;
3229
3370
}
3230
3371
3372
+ static int option_parse_space_change (const struct option * opt ,
3373
+ const char * arg , int unset )
3374
+ {
3375
+ if (unset )
3376
+ ws_ignore_action = ignore_ws_none ;
3377
+ else
3378
+ ws_ignore_action = ignore_ws_change ;
3379
+ return 0 ;
3380
+ }
3381
+
3231
3382
static int option_parse_whitespace (const struct option * opt ,
3232
3383
const char * arg , int unset )
3233
3384
{
@@ -3304,6 +3455,12 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
3304
3455
{ OPTION_CALLBACK , 0 , "whitespace" , & whitespace_option , "action" ,
3305
3456
"detect new or modified lines that have whitespace errors" ,
3306
3457
0 , option_parse_whitespace },
3458
+ { OPTION_CALLBACK , 0 , "ignore-space-change" , NULL , NULL ,
3459
+ "ignore changes in whitespace when finding context" ,
3460
+ PARSE_OPT_NOARG , option_parse_space_change },
3461
+ { OPTION_CALLBACK , 0 , "ignore-whitespace" , NULL , NULL ,
3462
+ "ignore changes in whitespace when finding context" ,
3463
+ PARSE_OPT_NOARG , option_parse_space_change },
3307
3464
OPT_BOOLEAN ('R' , "reverse" , & apply_in_reverse ,
3308
3465
"apply the patch in reverse" ),
3309
3466
OPT_BOOLEAN (0 , "unidiff-zero" , & unidiff_zero ,
@@ -3328,6 +3485,8 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
3328
3485
git_config (git_apply_config , NULL );
3329
3486
if (apply_default_whitespace )
3330
3487
parse_whitespace_option (apply_default_whitespace );
3488
+ if (apply_default_ignorewhitespace )
3489
+ parse_ignorewhitespace_option (apply_default_ignorewhitespace );
3331
3490
3332
3491
argc = parse_options (argc , argv , prefix , builtin_apply_options ,
3333
3492
apply_usage , 0 );
0 commit comments