@@ -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 );
@@ -1672,10 +1750,17 @@ static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)
1672
1750
}
1673
1751
}
1674
1752
1753
+ /*
1754
+ * Update the preimage, and the common lines in postimage,
1755
+ * from buffer buf of length len. If postlen is 0 the postimage
1756
+ * is updated in place, otherwise it's updated on a new buffer
1757
+ * of length postlen
1758
+ */
1759
+
1675
1760
static void update_pre_post_images (struct image * preimage ,
1676
1761
struct image * postimage ,
1677
1762
char * buf ,
1678
- size_t len )
1763
+ size_t len , size_t postlen )
1679
1764
{
1680
1765
int i , ctx ;
1681
1766
char * new , * old , * fixed ;
@@ -1694,11 +1779,19 @@ static void update_pre_post_images(struct image *preimage,
1694
1779
* preimage = fixed_preimage ;
1695
1780
1696
1781
/*
1697
- * Adjust the common context lines in postimage, in place.
1698
- * This is possible because whitespace fixing does not make
1699
- * the string grow.
1782
+ * Adjust the common context lines in postimage. This can be
1783
+ * done in-place when we are just doing whitespace fixing,
1784
+ * which does not make the string grow, but needs a new buffer
1785
+ * when ignoring whitespace causes the update, since in this case
1786
+ * we could have e.g. tabs converted to multiple spaces.
1787
+ * We trust the caller to tell us if the update can be done
1788
+ * in place (postlen==0) or not.
1700
1789
*/
1701
- new = old = postimage -> buf ;
1790
+ old = postimage -> buf ;
1791
+ if (postlen )
1792
+ new = postimage -> buf = xmalloc (postlen );
1793
+ else
1794
+ new = old ;
1702
1795
fixed = preimage -> buf ;
1703
1796
for (i = ctx = 0 ; i < postimage -> nr ; i ++ ) {
1704
1797
size_t len = postimage -> line [i ].len ;
@@ -1773,12 +1866,58 @@ static int match_fragment(struct image *img,
1773
1866
!memcmp (img -> buf + try , preimage -> buf , preimage -> len ))
1774
1867
return 1 ;
1775
1868
1869
+ /*
1870
+ * No exact match. If we are ignoring whitespace, run a line-by-line
1871
+ * fuzzy matching. We collect all the line length information because
1872
+ * we need it to adjust whitespace if we match.
1873
+ */
1874
+ if (ws_ignore_action == ignore_ws_change ) {
1875
+ size_t imgoff = 0 ;
1876
+ size_t preoff = 0 ;
1877
+ size_t postlen = postimage -> len ;
1878
+ size_t imglen [preimage -> nr ];
1879
+ for (i = 0 ; i < preimage -> nr ; i ++ ) {
1880
+ size_t prelen = preimage -> line [i ].len ;
1881
+
1882
+ imglen [i ] = img -> line [try_lno + i ].len ;
1883
+ if (!fuzzy_matchlines (
1884
+ img -> buf + try + imgoff , imglen [i ],
1885
+ preimage -> buf + preoff , prelen ))
1886
+ return 0 ;
1887
+ if (preimage -> line [i ].flag & LINE_COMMON )
1888
+ postlen += imglen [i ] - prelen ;
1889
+ imgoff += imglen [i ];
1890
+ preoff += prelen ;
1891
+ }
1892
+
1893
+ /*
1894
+ * Ok, the preimage matches with whitespace fuzz. Update it and
1895
+ * the common postimage lines to use the same whitespace as the
1896
+ * target. imgoff now holds the true length of the target that
1897
+ * matches the preimage, and we need to update the line lengths
1898
+ * of the preimage to match the target ones.
1899
+ */
1900
+ fixed_buf = xmalloc (imgoff );
1901
+ memcpy (fixed_buf , img -> buf + try , imgoff );
1902
+ for (i = 0 ; i < preimage -> nr ; i ++ )
1903
+ preimage -> line [i ].len = imglen [i ];
1904
+
1905
+ /*
1906
+ * Update the preimage buffer and the postimage context lines.
1907
+ */
1908
+ update_pre_post_images (preimage , postimage ,
1909
+ fixed_buf , imgoff , postlen );
1910
+ return 1 ;
1911
+ }
1912
+
1776
1913
if (ws_error_action != correct_ws_error )
1777
1914
return 0 ;
1778
1915
1779
1916
/*
1780
1917
* The hunk does not apply byte-by-byte, but the hash says
1781
- * it might with whitespace fuzz.
1918
+ * it might with whitespace fuzz. We haven't been asked to
1919
+ * ignore whitespace, we were asked to correct whitespace
1920
+ * errors, so let's try matching after whitespace correction.
1782
1921
*/
1783
1922
fixed_buf = xmalloc (preimage -> len + 1 );
1784
1923
buf = fixed_buf ;
@@ -1830,7 +1969,7 @@ static int match_fragment(struct image *img,
1830
1969
* hunk match. Update the context lines in the postimage.
1831
1970
*/
1832
1971
update_pre_post_images (preimage , postimage ,
1833
- fixed_buf , buf - fixed_buf );
1972
+ fixed_buf , buf - fixed_buf , 0 );
1834
1973
return 1 ;
1835
1974
1836
1975
unmatch_exit :
@@ -3272,6 +3411,8 @@ static int git_apply_config(const char *var, const char *value, void *cb)
3272
3411
{
3273
3412
if (!strcmp (var , "apply.whitespace" ))
3274
3413
return git_config_string (& apply_default_whitespace , var , value );
3414
+ else if (!strcmp (var , "apply.ignorewhitespace" ))
3415
+ return git_config_string (& apply_default_ignorewhitespace , var , value );
3275
3416
return git_default_config (var , value , cb );
3276
3417
}
3277
3418
@@ -3308,6 +3449,16 @@ static int option_parse_z(const struct option *opt,
3308
3449
return 0 ;
3309
3450
}
3310
3451
3452
+ static int option_parse_space_change (const struct option * opt ,
3453
+ const char * arg , int unset )
3454
+ {
3455
+ if (unset )
3456
+ ws_ignore_action = ignore_ws_none ;
3457
+ else
3458
+ ws_ignore_action = ignore_ws_change ;
3459
+ return 0 ;
3460
+ }
3461
+
3311
3462
static int option_parse_whitespace (const struct option * opt ,
3312
3463
const char * arg , int unset )
3313
3464
{
@@ -3384,6 +3535,12 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
3384
3535
{ OPTION_CALLBACK , 0 , "whitespace" , & whitespace_option , "action" ,
3385
3536
"detect new or modified lines that have whitespace errors" ,
3386
3537
0 , option_parse_whitespace },
3538
+ { OPTION_CALLBACK , 0 , "ignore-space-change" , NULL , NULL ,
3539
+ "ignore changes in whitespace when finding context" ,
3540
+ PARSE_OPT_NOARG , option_parse_space_change },
3541
+ { OPTION_CALLBACK , 0 , "ignore-whitespace" , NULL , NULL ,
3542
+ "ignore changes in whitespace when finding context" ,
3543
+ PARSE_OPT_NOARG , option_parse_space_change },
3387
3544
OPT_BOOLEAN ('R' , "reverse" , & apply_in_reverse ,
3388
3545
"apply the patch in reverse" ),
3389
3546
OPT_BOOLEAN (0 , "unidiff-zero" , & unidiff_zero ,
@@ -3408,6 +3565,8 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
3408
3565
git_config (git_apply_config , NULL );
3409
3566
if (apply_default_whitespace )
3410
3567
parse_whitespace_option (apply_default_whitespace );
3568
+ if (apply_default_ignorewhitespace )
3569
+ parse_ignorewhitespace_option (apply_default_ignorewhitespace );
3411
3570
3412
3571
argc = parse_options (argc , argv , prefix , builtin_apply_options ,
3413
3572
apply_usage , 0 );
0 commit comments