Skip to content

Commit a17483f

Browse files
committed
Merge branch 'tb/apply-with-crlf'
"git apply" that is used as a better "patch -p1" failed to apply a taken from a file with CRLF line endings to a file with CRLF line endings. The root cause was because it misused convert_to_git() that tried to do "safe-crlf" processing by looking at the index entry at the same path, which is a nonsense---in that mode, "apply" is not working on the data in (or derived from) the index at all. This has been fixed. * tb/apply-with-crlf: apply: file commited with CRLF should roundtrip diff and apply convert: add SAFE_CRLF_KEEP_CRLF
2 parents f6a47f9 + c24f3ab commit a17483f

File tree

4 files changed

+71
-16
lines changed

4 files changed

+71
-16
lines changed

apply.c

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ struct patch {
219219
unsigned int recount:1;
220220
unsigned int conflicted_threeway:1;
221221
unsigned int direct_to_threeway:1;
222+
unsigned int crlf_in_old:1;
222223
struct fragment *fragments;
223224
char *result;
224225
size_t resultsize;
@@ -1661,6 +1662,19 @@ static void check_whitespace(struct apply_state *state,
16611662
record_ws_error(state, result, line + 1, len - 2, state->linenr);
16621663
}
16631664

1665+
/*
1666+
* Check if the patch has context lines with CRLF or
1667+
* the patch wants to remove lines with CRLF.
1668+
*/
1669+
static void check_old_for_crlf(struct patch *patch, const char *line, int len)
1670+
{
1671+
if (len >= 2 && line[len-1] == '\n' && line[len-2] == '\r') {
1672+
patch->ws_rule |= WS_CR_AT_EOL;
1673+
patch->crlf_in_old = 1;
1674+
}
1675+
}
1676+
1677+
16641678
/*
16651679
* Parse a unified diff. Note that this really needs to parse each
16661680
* fragment separately, since the only way to know the difference
@@ -1711,11 +1725,14 @@ static int parse_fragment(struct apply_state *state,
17111725
if (!deleted && !added)
17121726
leading++;
17131727
trailing++;
1728+
check_old_for_crlf(patch, line, len);
17141729
if (!state->apply_in_reverse &&
17151730
state->ws_error_action == correct_ws_error)
17161731
check_whitespace(state, line, len, patch->ws_rule);
17171732
break;
17181733
case '-':
1734+
if (!state->apply_in_reverse)
1735+
check_old_for_crlf(patch, line, len);
17191736
if (state->apply_in_reverse &&
17201737
state->ws_error_action != nowarn_ws_error)
17211738
check_whitespace(state, line, len, patch->ws_rule);
@@ -1724,6 +1741,8 @@ static int parse_fragment(struct apply_state *state,
17241741
trailing = 0;
17251742
break;
17261743
case '+':
1744+
if (state->apply_in_reverse)
1745+
check_old_for_crlf(patch, line, len);
17271746
if (!state->apply_in_reverse &&
17281747
state->ws_error_action != nowarn_ws_error)
17291748
check_whitespace(state, line, len, patch->ws_rule);
@@ -2266,8 +2285,11 @@ static void show_stats(struct apply_state *state, struct patch *patch)
22662285
add, pluses, del, minuses);
22672286
}
22682287

2269-
static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)
2288+
static int read_old_data(struct stat *st, struct patch *patch,
2289+
const char *path, struct strbuf *buf)
22702290
{
2291+
enum safe_crlf safe_crlf = patch->crlf_in_old ?
2292+
SAFE_CRLF_KEEP_CRLF : SAFE_CRLF_RENORMALIZE;
22712293
switch (st->st_mode & S_IFMT) {
22722294
case S_IFLNK:
22732295
if (strbuf_readlink(buf, path, st->st_size) < 0)
@@ -2276,7 +2298,15 @@ static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)
22762298
case S_IFREG:
22772299
if (strbuf_read_file(buf, path, st->st_size) != st->st_size)
22782300
return error(_("unable to open or read %s"), path);
2279-
convert_to_git(&the_index, path, buf->buf, buf->len, buf, 0);
2301+
/*
2302+
* "git apply" without "--index/--cached" should never look
2303+
* at the index; the target file may not have been added to
2304+
* the index yet, and we may not even be in any Git repository.
2305+
* Pass NULL to convert_to_git() to stress this; the function
2306+
* should never look at the index when explicit crlf option
2307+
* is given.
2308+
*/
2309+
convert_to_git(NULL, path, buf->buf, buf->len, buf, safe_crlf);
22802310
return 0;
22812311
default:
22822312
return -1;
@@ -3379,6 +3409,7 @@ static int load_patch_target(struct apply_state *state,
33793409
struct strbuf *buf,
33803410
const struct cache_entry *ce,
33813411
struct stat *st,
3412+
struct patch *patch,
33823413
const char *name,
33833414
unsigned expected_mode)
33843415
{
@@ -3394,7 +3425,7 @@ static int load_patch_target(struct apply_state *state,
33943425
} else if (has_symlink_leading_path(name, strlen(name))) {
33953426
return error(_("reading from '%s' beyond a symbolic link"), name);
33963427
} else {
3397-
if (read_old_data(st, name, buf))
3428+
if (read_old_data(st, patch, name, buf))
33983429
return error(_("failed to read %s"), name);
33993430
}
34003431
}
@@ -3427,7 +3458,7 @@ static int load_preimage(struct apply_state *state,
34273458
/* We have a patched copy in memory; use that. */
34283459
strbuf_add(&buf, previous->result, previous->resultsize);
34293460
} else {
3430-
status = load_patch_target(state, &buf, ce, st,
3461+
status = load_patch_target(state, &buf, ce, st, patch,
34313462
patch->old_name, patch->old_mode);
34323463
if (status < 0)
34333464
return status;
@@ -3515,7 +3546,7 @@ static int load_current(struct apply_state *state,
35153546
if (verify_index_match(ce, &st))
35163547
return error(_("%s: does not match index"), name);
35173548

3518-
status = load_patch_target(state, &buf, ce, &st, name, mode);
3549+
status = load_patch_target(state, &buf, ce, &st, patch, name, mode);
35193550
if (status < 0)
35203551
return status;
35213552
else if (status)

convert.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1132,10 +1132,12 @@ int convert_to_git(const struct index_state *istate,
11321132
src = dst->buf;
11331133
len = dst->len;
11341134
}
1135-
ret |= crlf_to_git(istate, path, src, len, dst, ca.crlf_action, checksafe);
1136-
if (ret && dst) {
1137-
src = dst->buf;
1138-
len = dst->len;
1135+
if (checksafe != SAFE_CRLF_KEEP_CRLF) {
1136+
ret |= crlf_to_git(istate, path, src, len, dst, ca.crlf_action, checksafe);
1137+
if (ret && dst) {
1138+
src = dst->buf;
1139+
len = dst->len;
1140+
}
11391141
}
11401142
return ret | ident_to_git(path, src, len, dst, ca.ident);
11411143
}

convert.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ enum safe_crlf {
1212
SAFE_CRLF_FALSE = 0,
1313
SAFE_CRLF_FAIL = 1,
1414
SAFE_CRLF_WARN = 2,
15-
SAFE_CRLF_RENORMALIZE = 3
15+
SAFE_CRLF_RENORMALIZE = 3,
16+
SAFE_CRLF_KEEP_CRLF = 4
1617
};
1718

1819
extern enum safe_crlf safe_crlf;

t/t4124-apply-ws-rule.sh

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -467,21 +467,42 @@ test_expect_success 'same, but with CR-LF line endings && cr-at-eol set' '
467467
test_cmp one expect
468468
'
469469

470-
test_expect_success 'same, but with CR-LF line endings && cr-at-eol unset' '
470+
test_expect_success 'CR-LF line endings && add line && text=auto' '
471471
git config --unset core.whitespace &&
472472
printf "a\r\n" >one &&
473+
cp one save-one &&
474+
git add one &&
473475
printf "b\r\n" >>one &&
474-
printf "c\r\n" >>one &&
476+
cp one expect &&
477+
git diff -- one >patch &&
478+
mv save-one one &&
479+
echo "one text=auto" >.gitattributes &&
480+
git apply patch &&
481+
test_cmp one expect
482+
'
483+
484+
test_expect_success 'CR-LF line endings && change line && text=auto' '
485+
printf "a\r\n" >one &&
475486
cp one save-one &&
476-
printf " \r\n" >>one &&
477487
git add one &&
488+
printf "b\r\n" >one &&
478489
cp one expect &&
479-
printf "d\r\n" >>one &&
480490
git diff -- one >patch &&
481491
mv save-one one &&
482-
echo d >>expect &&
492+
echo "one text=auto" >.gitattributes &&
493+
git apply patch &&
494+
test_cmp one expect
495+
'
483496

484-
git apply --ignore-space-change --whitespace=fix patch &&
497+
test_expect_success 'LF in repo, CRLF in worktree && change line && text=auto' '
498+
printf "a\n" >one &&
499+
git add one &&
500+
printf "b\r\n" >one &&
501+
git diff -- one >patch &&
502+
printf "a\r\n" >one &&
503+
echo "one text=auto" >.gitattributes &&
504+
git -c core.eol=CRLF apply patch &&
505+
printf "b\r\n" >expect &&
485506
test_cmp one expect
486507
'
487508

0 commit comments

Comments
 (0)