Skip to content

Commit 648a50a

Browse files
committed
Merge branch 'tb/apply-with-crlf' into maint
"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 27015b4 + c24f3ab commit 648a50a

4 files changed

Lines changed: 71 additions & 16 deletions

File tree

apply.c

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

1666+
/*
1667+
* Check if the patch has context lines with CRLF or
1668+
* the patch wants to remove lines with CRLF.
1669+
*/
1670+
static void check_old_for_crlf(struct patch *patch, const char *line, int len)
1671+
{
1672+
if (len >= 2 && line[len-1] == '\n' && line[len-2] == '\r') {
1673+
patch->ws_rule |= WS_CR_AT_EOL;
1674+
patch->crlf_in_old = 1;
1675+
}
1676+
}
1677+
1678+
16651679
/*
16661680
* Parse a unified diff. Note that this really needs to parse each
16671681
* fragment separately, since the only way to know the difference
@@ -1712,11 +1726,14 @@ static int parse_fragment(struct apply_state *state,
17121726
if (!deleted && !added)
17131727
leading++;
17141728
trailing++;
1729+
check_old_for_crlf(patch, line, len);
17151730
if (!state->apply_in_reverse &&
17161731
state->ws_error_action == correct_ws_error)
17171732
check_whitespace(state, line, len, patch->ws_rule);
17181733
break;
17191734
case '-':
1735+
if (!state->apply_in_reverse)
1736+
check_old_for_crlf(patch, line, len);
17201737
if (state->apply_in_reverse &&
17211738
state->ws_error_action != nowarn_ws_error)
17221739
check_whitespace(state, line, len, patch->ws_rule);
@@ -1725,6 +1742,8 @@ static int parse_fragment(struct apply_state *state,
17251742
trailing = 0;
17261743
break;
17271744
case '+':
1745+
if (state->apply_in_reverse)
1746+
check_old_for_crlf(patch, line, len);
17281747
if (!state->apply_in_reverse &&
17291748
state->ws_error_action != nowarn_ws_error)
17301749
check_whitespace(state, line, len, patch->ws_rule);
@@ -2268,8 +2287,11 @@ static void show_stats(struct apply_state *state, struct patch *patch)
22682287
add, pluses, del, minuses);
22692288
}
22702289

2271-
static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)
2290+
static int read_old_data(struct stat *st, struct patch *patch,
2291+
const char *path, struct strbuf *buf)
22722292
{
2293+
enum safe_crlf safe_crlf = patch->crlf_in_old ?
2294+
SAFE_CRLF_KEEP_CRLF : SAFE_CRLF_RENORMALIZE;
22732295
switch (st->st_mode & S_IFMT) {
22742296
case S_IFLNK:
22752297
if (strbuf_readlink(buf, path, st->st_size) < 0)
@@ -2278,7 +2300,15 @@ static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)
22782300
case S_IFREG:
22792301
if (strbuf_read_file(buf, path, st->st_size) != st->st_size)
22802302
return error(_("unable to open or read %s"), path);
2281-
convert_to_git(&the_index, path, buf->buf, buf->len, buf, 0);
2303+
/*
2304+
* "git apply" without "--index/--cached" should never look
2305+
* at the index; the target file may not have been added to
2306+
* the index yet, and we may not even be in any Git repository.
2307+
* Pass NULL to convert_to_git() to stress this; the function
2308+
* should never look at the index when explicit crlf option
2309+
* is given.
2310+
*/
2311+
convert_to_git(NULL, path, buf->buf, buf->len, buf, safe_crlf);
22822312
return 0;
22832313
default:
22842314
return -1;
@@ -3381,6 +3411,7 @@ static int load_patch_target(struct apply_state *state,
33813411
struct strbuf *buf,
33823412
const struct cache_entry *ce,
33833413
struct stat *st,
3414+
struct patch *patch,
33843415
const char *name,
33853416
unsigned expected_mode)
33863417
{
@@ -3396,7 +3427,7 @@ static int load_patch_target(struct apply_state *state,
33963427
} else if (has_symlink_leading_path(name, strlen(name))) {
33973428
return error(_("reading from '%s' beyond a symbolic link"), name);
33983429
} else {
3399-
if (read_old_data(st, name, buf))
3430+
if (read_old_data(st, patch, name, buf))
34003431
return error(_("failed to read %s"), name);
34013432
}
34023433
}
@@ -3429,7 +3460,7 @@ static int load_preimage(struct apply_state *state,
34293460
/* We have a patched copy in memory; use that. */
34303461
strbuf_add(&buf, previous->result, previous->resultsize);
34313462
} else {
3432-
status = load_patch_target(state, &buf, ce, st,
3463+
status = load_patch_target(state, &buf, ce, st, patch,
34333464
patch->old_name, patch->old_mode);
34343465
if (status < 0)
34353466
return status;
@@ -3517,7 +3548,7 @@ static int load_current(struct apply_state *state,
35173548
if (verify_index_match(ce, &st))
35183549
return error(_("%s: does not match index"), name);
35193550

3520-
status = load_patch_target(state, &buf, ce, &st, name, mode);
3551+
status = load_patch_target(state, &buf, ce, &st, patch, name, mode);
35213552
if (status < 0)
35223553
return status;
35233554
else if (status)

convert.c

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

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)