fix: export patches not retaining CRLF line endings (#28360)
When a patch targets a file using CRLF line endings, they need to be retained in the patch file. Otherwise the patch will fail to apply due to being unable to find surrounding lines with matching whitespace.
This commit is contained in:
parent
7918ddb026
commit
b9b734c9c4
2 changed files with 29 additions and 16 deletions
|
@ -47,7 +47,7 @@ def get_repo_root(path):
|
|||
|
||||
|
||||
def am(repo, patch_data, threeway=False, directory=None, exclude=None,
|
||||
committer_name=None, committer_email=None):
|
||||
committer_name=None, committer_email=None, keep_cr=True):
|
||||
args = []
|
||||
if threeway:
|
||||
args += ['--3way']
|
||||
|
@ -56,6 +56,10 @@ def am(repo, patch_data, threeway=False, directory=None, exclude=None,
|
|||
if exclude is not None:
|
||||
for path_pattern in exclude:
|
||||
args += ['--exclude', path_pattern]
|
||||
if keep_cr is True:
|
||||
# Keep the CR of CRLF in case any patches target files with Windows line
|
||||
# endings.
|
||||
args += ['--keep-cr']
|
||||
|
||||
root_args = ['-C', repo]
|
||||
if committer_name is not None:
|
||||
|
@ -230,7 +234,9 @@ def split_patches(patch_data):
|
|||
"""Split a concatenated series of patches into N separate patches"""
|
||||
patches = []
|
||||
patch_start = re.compile('^From [0-9a-f]+ ')
|
||||
for line in patch_data.splitlines():
|
||||
# Keep line endings in case any patches target files with CRLF.
|
||||
keep_line_endings = True
|
||||
for line in patch_data.splitlines(keep_line_endings):
|
||||
if patch_start.match(line):
|
||||
patches.append([])
|
||||
patches[-1].append(line)
|
||||
|
@ -246,13 +252,23 @@ def munge_subject_to_filename(subject):
|
|||
|
||||
def get_file_name(patch):
|
||||
"""Return the name of the file to which the patch should be written"""
|
||||
file_name = None
|
||||
for line in patch:
|
||||
if line.startswith('Patch-Filename: '):
|
||||
return line[len('Patch-Filename: '):]
|
||||
file_name = line[len('Patch-Filename: '):]
|
||||
break
|
||||
# If no patch-filename header, munge the subject.
|
||||
if not file_name:
|
||||
for line in patch:
|
||||
if line.startswith('Subject: '):
|
||||
return munge_subject_to_filename(line[len('Subject: '):])
|
||||
file_name = munge_subject_to_filename(line[len('Subject: '):])
|
||||
break
|
||||
return file_name.rstrip('\n')
|
||||
|
||||
|
||||
def join_patch(patch):
|
||||
"""Joins and formats patch contents"""
|
||||
return ''.join(remove_patch_filename(patch)).rstrip('\n') + '\n'
|
||||
|
||||
|
||||
def remove_patch_filename(patch):
|
||||
|
@ -294,10 +310,8 @@ def export_patches(repo, out_dir, patch_range=None, dry_run=False):
|
|||
for patch in patches:
|
||||
filename = get_file_name(patch)
|
||||
filepath = posixpath.join(out_dir, filename)
|
||||
existing_patch = io.open(filepath, 'r', encoding='utf-8').read()
|
||||
formatted_patch = (
|
||||
'\n'.join(remove_patch_filename(patch)).rstrip('\n') + '\n'
|
||||
)
|
||||
existing_patch = io.open(filepath, 'rb').read()
|
||||
formatted_patch = join_patch(patch)
|
||||
if formatted_patch != existing_patch:
|
||||
patch_count += 1
|
||||
if patch_count > 0:
|
||||
|
@ -322,12 +336,11 @@ def export_patches(repo, out_dir, patch_range=None, dry_run=False):
|
|||
for patch in patches:
|
||||
filename = get_file_name(patch)
|
||||
file_path = posixpath.join(out_dir, filename)
|
||||
formatted_patch = (
|
||||
'\n'.join(remove_patch_filename(patch)).rstrip('\n') + '\n'
|
||||
)
|
||||
formatted_patch = join_patch(patch)
|
||||
# Write in binary mode to retain mixed line endings on write.
|
||||
with io.open(
|
||||
file_path, 'w', newline='\n', encoding='utf-8'
|
||||
file_path, 'wb'
|
||||
) as f:
|
||||
f.write(formatted_patch)
|
||||
f.write(formatted_patch.encode('utf-8'))
|
||||
pl.write(filename + '\n')
|
||||
|
||||
|
|
|
@ -207,7 +207,7 @@ const LINTERS = [{
|
|||
console.warn(`Patch file '${f}' has no description. Every patch must contain a justification for why the patch exists and the plan for its removal.`);
|
||||
return false;
|
||||
}
|
||||
const trailingWhitespace = patchText.split('\n').filter(line => line.startsWith('+')).some(line => /\s+$/.test(line));
|
||||
const trailingWhitespace = patchText.split(/\r?\n/).some(line => line.startsWith('+') && /\s+$/.test(line));
|
||||
if (trailingWhitespace) {
|
||||
console.warn(`Patch file '${f}' has trailing whitespace on some lines.`);
|
||||
return false;
|
||||
|
|
Loading…
Reference in a new issue