зеркало из 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 conflicted_threeway:1;
|
||||
unsigned int direct_to_threeway:1;
|
||||
unsigned int crlf_in_old:1;
|
||||
struct fragment *fragments;
|
||||
char *result;
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
* 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)
|
||||
leading++;
|
||||
trailing++;
|
||||
check_old_for_crlf(patch, line, len);
|
||||
if (!state->apply_in_reverse &&
|
||||
state->ws_error_action == correct_ws_error)
|
||||
check_whitespace(state, line, len, patch->ws_rule);
|
||||
break;
|
||||
case '-':
|
||||
if (!state->apply_in_reverse)
|
||||
check_old_for_crlf(patch, line, len);
|
||||
if (state->apply_in_reverse &&
|
||||
state->ws_error_action != nowarn_ws_error)
|
||||
check_whitespace(state, line, len, patch->ws_rule);
|
||||
|
@ -1725,6 +1742,8 @@ static int parse_fragment(struct apply_state *state,
|
|||
trailing = 0;
|
||||
break;
|
||||
case '+':
|
||||
if (state->apply_in_reverse)
|
||||
check_old_for_crlf(patch, line, len);
|
||||
if (!state->apply_in_reverse &&
|
||||
state->ws_error_action != nowarn_ws_error)
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
case S_IFLNK:
|
||||
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:
|
||||
if (strbuf_read_file(buf, path, st->st_size) != st->st_size)
|
||||
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;
|
||||
default:
|
||||
return -1;
|
||||
|
@ -3381,6 +3411,7 @@ static int load_patch_target(struct apply_state *state,
|
|||
struct strbuf *buf,
|
||||
const struct cache_entry *ce,
|
||||
struct stat *st,
|
||||
struct patch *patch,
|
||||
const char *name,
|
||||
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))) {
|
||||
return error(_("reading from '%s' beyond a symbolic link"), name);
|
||||
} else {
|
||||
if (read_old_data(st, name, buf))
|
||||
if (read_old_data(st, patch, name, buf))
|
||||
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. */
|
||||
strbuf_add(&buf, previous->result, previous->resultsize);
|
||||
} else {
|
||||
status = load_patch_target(state, &buf, ce, st,
|
||||
status = load_patch_target(state, &buf, ce, st, patch,
|
||||
patch->old_name, patch->old_mode);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
@ -3517,7 +3548,7 @@ static int load_current(struct apply_state *state,
|
|||
if (verify_index_match(ce, &st))
|
||||
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)
|
||||
return 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;
|
||||
len = dst->len;
|
||||
}
|
||||
ret |= crlf_to_git(istate, path, src, len, dst, ca.crlf_action, checksafe);
|
||||
if (ret && dst) {
|
||||
src = dst->buf;
|
||||
len = dst->len;
|
||||
if (checksafe != SAFE_CRLF_KEEP_CRLF) {
|
||||
ret |= crlf_to_git(istate, path, src, len, dst, ca.crlf_action, checksafe);
|
||||
if (ret && dst) {
|
||||
src = dst->buf;
|
||||
len = dst->len;
|
||||
}
|
||||
}
|
||||
return ret | ident_to_git(path, src, len, dst, ca.ident);
|
||||
}
|
||||
|
|
|
@ -12,7 +12,8 @@ enum safe_crlf {
|
|||
SAFE_CRLF_FALSE = 0,
|
||||
SAFE_CRLF_FAIL = 1,
|
||||
SAFE_CRLF_WARN = 2,
|
||||
SAFE_CRLF_RENORMALIZE = 3
|
||||
SAFE_CRLF_RENORMALIZE = 3,
|
||||
SAFE_CRLF_KEEP_CRLF = 4
|
||||
};
|
||||
|
||||
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_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 &&
|
||||
printf "a\r\n" >one &&
|
||||
printf "b\r\n" >>one &&
|
||||
printf "c\r\n" >>one &&
|
||||
cp one save-one &&
|
||||
printf " \r\n" >>one &&
|
||||
git add one &&
|
||||
printf "b\r\n" >>one &&
|
||||
cp one expect &&
|
||||
printf "d\r\n" >>one &&
|
||||
git diff -- one >patch &&
|
||||
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
|
||||
'
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче