|
@@ -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.
|
|
|
- for line in patch:
|
|
|
- if line.startswith('Subject: '):
|
|
|
- return munge_subject_to_filename(line[len('Subject: '):])
|
|
|
+ if not file_name:
|
|
|
+ for line in patch:
|
|
|
+ if line.startswith('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')
|
|
|
|