зеркало из https://github.com/microsoft/git.git
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
This commit is contained in:
Коммит
648a50a08a
41
apply.c
41
apply.c
|
@ -220,6 +220,7 @@ struct patch {
|
||||||
unsigned int recount:1;
|
unsigned int recount:1;
|
||||||
unsigned int conflicted_threeway:1;
|
unsigned int conflicted_threeway:1;
|
||||||
unsigned int direct_to_threeway:1;
|
unsigned int direct_to_threeway:1;
|
||||||
|
unsigned int crlf_in_old:1;
|
||||||
struct fragment *fragments;
|
struct fragment *fragments;
|
||||||
char *result;
|
char *result;
|
||||||
size_t resultsize;
|
size_t resultsize;
|
||||||
|
@ -1662,6 +1663,19 @@ static void check_whitespace(struct apply_state *state,
|
||||||
record_ws_error(state, result, line + 1, len - 2, state->linenr);
|
record_ws_error(state, result, line + 1, len - 2, state->linenr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if the patch has context lines with CRLF or
|
||||||
|
* the patch wants to remove lines with CRLF.
|
||||||
|
*/
|
||||||
|
static void check_old_for_crlf(struct patch *patch, const char *line, int len)
|
||||||
|
{
|
||||||
|
if (len >= 2 && line[len-1] == '\n' && line[len-2] == '\r') {
|
||||||
|
patch->ws_rule |= WS_CR_AT_EOL;
|
||||||
|
patch->crlf_in_old = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Parse a unified diff. Note that this really needs to parse each
|
* Parse a unified diff. Note that this really needs to parse each
|
||||||
* fragment separately, since the only way to know the difference
|
* fragment separately, since the only way to know the difference
|
||||||
|
@ -1712,11 +1726,14 @@ static int parse_fragment(struct apply_state *state,
|
||||||
if (!deleted && !added)
|
if (!deleted && !added)
|
||||||
leading++;
|
leading++;
|
||||||
trailing++;
|
trailing++;
|
||||||
|
check_old_for_crlf(patch, line, len);
|
||||||
if (!state->apply_in_reverse &&
|
if (!state->apply_in_reverse &&
|
||||||
state->ws_error_action == correct_ws_error)
|
state->ws_error_action == correct_ws_error)
|
||||||
check_whitespace(state, line, len, patch->ws_rule);
|
check_whitespace(state, line, len, patch->ws_rule);
|
||||||
break;
|
break;
|
||||||
case '-':
|
case '-':
|
||||||
|
if (!state->apply_in_reverse)
|
||||||
|
check_old_for_crlf(patch, line, len);
|
||||||
if (state->apply_in_reverse &&
|
if (state->apply_in_reverse &&
|
||||||
state->ws_error_action != nowarn_ws_error)
|
state->ws_error_action != nowarn_ws_error)
|
||||||
check_whitespace(state, line, len, patch->ws_rule);
|
check_whitespace(state, line, len, patch->ws_rule);
|
||||||
|
@ -1725,6 +1742,8 @@ static int parse_fragment(struct apply_state *state,
|
||||||
trailing = 0;
|
trailing = 0;
|
||||||
break;
|
break;
|
||||||
case '+':
|
case '+':
|
||||||
|
if (state->apply_in_reverse)
|
||||||
|
check_old_for_crlf(patch, line, len);
|
||||||
if (!state->apply_in_reverse &&
|
if (!state->apply_in_reverse &&
|
||||||
state->ws_error_action != nowarn_ws_error)
|
state->ws_error_action != nowarn_ws_error)
|
||||||
check_whitespace(state, line, len, patch->ws_rule);
|
check_whitespace(state, line, len, patch->ws_rule);
|
||||||
|
@ -2268,8 +2287,11 @@ static void show_stats(struct apply_state *state, struct patch *patch)
|
||||||
add, pluses, del, minuses);
|
add, pluses, del, minuses);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)
|
static int read_old_data(struct stat *st, struct patch *patch,
|
||||||
|
const char *path, struct strbuf *buf)
|
||||||
{
|
{
|
||||||
|
enum safe_crlf safe_crlf = patch->crlf_in_old ?
|
||||||
|
SAFE_CRLF_KEEP_CRLF : SAFE_CRLF_RENORMALIZE;
|
||||||
switch (st->st_mode & S_IFMT) {
|
switch (st->st_mode & S_IFMT) {
|
||||||
case S_IFLNK:
|
case S_IFLNK:
|
||||||
if (strbuf_readlink(buf, path, st->st_size) < 0)
|
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)
|
||||||
case S_IFREG:
|
case S_IFREG:
|
||||||
if (strbuf_read_file(buf, path, st->st_size) != st->st_size)
|
if (strbuf_read_file(buf, path, st->st_size) != st->st_size)
|
||||||
return error(_("unable to open or read %s"), path);
|
return error(_("unable to open or read %s"), path);
|
||||||
convert_to_git(&the_index, path, buf->buf, buf->len, buf, 0);
|
/*
|
||||||
|
* "git apply" without "--index/--cached" should never look
|
||||||
|
* at the index; the target file may not have been added to
|
||||||
|
* the index yet, and we may not even be in any Git repository.
|
||||||
|
* Pass NULL to convert_to_git() to stress this; the function
|
||||||
|
* should never look at the index when explicit crlf option
|
||||||
|
* is given.
|
||||||
|
*/
|
||||||
|
convert_to_git(NULL, path, buf->buf, buf->len, buf, safe_crlf);
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -3381,6 +3411,7 @@ static int load_patch_target(struct apply_state *state,
|
||||||
struct strbuf *buf,
|
struct strbuf *buf,
|
||||||
const struct cache_entry *ce,
|
const struct cache_entry *ce,
|
||||||
struct stat *st,
|
struct stat *st,
|
||||||
|
struct patch *patch,
|
||||||
const char *name,
|
const char *name,
|
||||||
unsigned expected_mode)
|
unsigned expected_mode)
|
||||||
{
|
{
|
||||||
|
@ -3396,7 +3427,7 @@ static int load_patch_target(struct apply_state *state,
|
||||||
} else if (has_symlink_leading_path(name, strlen(name))) {
|
} else if (has_symlink_leading_path(name, strlen(name))) {
|
||||||
return error(_("reading from '%s' beyond a symbolic link"), name);
|
return error(_("reading from '%s' beyond a symbolic link"), name);
|
||||||
} else {
|
} else {
|
||||||
if (read_old_data(st, name, buf))
|
if (read_old_data(st, patch, name, buf))
|
||||||
return error(_("failed to read %s"), name);
|
return error(_("failed to read %s"), name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3429,7 +3460,7 @@ static int load_preimage(struct apply_state *state,
|
||||||
/* We have a patched copy in memory; use that. */
|
/* We have a patched copy in memory; use that. */
|
||||||
strbuf_add(&buf, previous->result, previous->resultsize);
|
strbuf_add(&buf, previous->result, previous->resultsize);
|
||||||
} else {
|
} else {
|
||||||
status = load_patch_target(state, &buf, ce, st,
|
status = load_patch_target(state, &buf, ce, st, patch,
|
||||||
patch->old_name, patch->old_mode);
|
patch->old_name, patch->old_mode);
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
return status;
|
return status;
|
||||||
|
@ -3517,7 +3548,7 @@ static int load_current(struct apply_state *state,
|
||||||
if (verify_index_match(ce, &st))
|
if (verify_index_match(ce, &st))
|
||||||
return error(_("%s: does not match index"), name);
|
return error(_("%s: does not match index"), name);
|
||||||
|
|
||||||
status = load_patch_target(state, &buf, ce, &st, name, mode);
|
status = load_patch_target(state, &buf, ce, &st, patch, name, mode);
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
return status;
|
return status;
|
||||||
else if (status)
|
else if (status)
|
||||||
|
|
10
convert.c
10
convert.c
|
@ -1133,10 +1133,12 @@ int convert_to_git(const struct index_state *istate,
|
||||||
src = dst->buf;
|
src = dst->buf;
|
||||||
len = dst->len;
|
len = dst->len;
|
||||||
}
|
}
|
||||||
ret |= crlf_to_git(istate, path, src, len, dst, ca.crlf_action, checksafe);
|
if (checksafe != SAFE_CRLF_KEEP_CRLF) {
|
||||||
if (ret && dst) {
|
ret |= crlf_to_git(istate, path, src, len, dst, ca.crlf_action, checksafe);
|
||||||
src = dst->buf;
|
if (ret && dst) {
|
||||||
len = dst->len;
|
src = dst->buf;
|
||||||
|
len = dst->len;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return ret | ident_to_git(path, src, len, dst, ca.ident);
|
return ret | ident_to_git(path, src, len, dst, ca.ident);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,8 @@ enum safe_crlf {
|
||||||
SAFE_CRLF_FALSE = 0,
|
SAFE_CRLF_FALSE = 0,
|
||||||
SAFE_CRLF_FAIL = 1,
|
SAFE_CRLF_FAIL = 1,
|
||||||
SAFE_CRLF_WARN = 2,
|
SAFE_CRLF_WARN = 2,
|
||||||
SAFE_CRLF_RENORMALIZE = 3
|
SAFE_CRLF_RENORMALIZE = 3,
|
||||||
|
SAFE_CRLF_KEEP_CRLF = 4
|
||||||
};
|
};
|
||||||
|
|
||||||
extern enum safe_crlf safe_crlf;
|
extern enum safe_crlf safe_crlf;
|
||||||
|
|
|
@ -467,21 +467,42 @@ test_expect_success 'same, but with CR-LF line endings && cr-at-eol set' '
|
||||||
test_cmp one expect
|
test_cmp one expect
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success 'same, but with CR-LF line endings && cr-at-eol unset' '
|
test_expect_success 'CR-LF line endings && add line && text=auto' '
|
||||||
git config --unset core.whitespace &&
|
git config --unset core.whitespace &&
|
||||||
printf "a\r\n" >one &&
|
printf "a\r\n" >one &&
|
||||||
printf "b\r\n" >>one &&
|
|
||||||
printf "c\r\n" >>one &&
|
|
||||||
cp one save-one &&
|
cp one save-one &&
|
||||||
printf " \r\n" >>one &&
|
|
||||||
git add one &&
|
git add one &&
|
||||||
|
printf "b\r\n" >>one &&
|
||||||
cp one expect &&
|
cp one expect &&
|
||||||
printf "d\r\n" >>one &&
|
|
||||||
git diff -- one >patch &&
|
git diff -- one >patch &&
|
||||||
mv save-one one &&
|
mv save-one one &&
|
||||||
echo d >>expect &&
|
echo "one text=auto" >.gitattributes &&
|
||||||
|
git apply patch &&
|
||||||
|
test_cmp one expect
|
||||||
|
'
|
||||||
|
|
||||||
git apply --ignore-space-change --whitespace=fix patch &&
|
test_expect_success 'CR-LF line endings && change line && text=auto' '
|
||||||
|
printf "a\r\n" >one &&
|
||||||
|
cp one save-one &&
|
||||||
|
git add one &&
|
||||||
|
printf "b\r\n" >one &&
|
||||||
|
cp one expect &&
|
||||||
|
git diff -- one >patch &&
|
||||||
|
mv save-one one &&
|
||||||
|
echo "one text=auto" >.gitattributes &&
|
||||||
|
git apply patch &&
|
||||||
|
test_cmp one expect
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'LF in repo, CRLF in worktree && change line && text=auto' '
|
||||||
|
printf "a\n" >one &&
|
||||||
|
git add one &&
|
||||||
|
printf "b\r\n" >one &&
|
||||||
|
git diff -- one >patch &&
|
||||||
|
printf "a\r\n" >one &&
|
||||||
|
echo "one text=auto" >.gitattributes &&
|
||||||
|
git -c core.eol=CRLF apply patch &&
|
||||||
|
printf "b\r\n" >expect &&
|
||||||
test_cmp one expect
|
test_cmp one expect
|
||||||
'
|
'
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче