chore: deprecate apply-patches in favour of git-{import,export}-patches (#15300)

This commit is contained in:
Jeremy Apthorp 2018-10-24 11:24:11 -07:00 committed by GitHub
parent 4185efa08f
commit 335e9f68b7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
123 changed files with 4368 additions and 4780 deletions

View file

@ -9,8 +9,6 @@ structure, or make assumptions about the passed arguments or calls' outcomes.
import os
import subprocess
from lib.util import scoped_cwd
def is_repo_root(path):
path_exists = os.path.exists(path)
@ -42,8 +40,28 @@ def get_repo_root(path):
return get_repo_root(parent_path)
def am(repo, patch_data, threeway=False,
committer_name=None, committer_email=None):
args = []
if threeway:
args += ['--3way']
root_args = ['-C', repo]
if committer_name is not None:
root_args += ['-c', 'user.name=' + committer_name]
if committer_email is not None:
root_args += ['-c', 'user.email=' + committer_email]
command = ['git'] + root_args + ['am'] + args
proc = subprocess.Popen(
command,
stdin=subprocess.PIPE)
proc.communicate(patch_data)
if proc.returncode != 0:
raise RuntimeError("Command {} returned {}".format(command,
proc.returncode))
def apply_patch(repo, patch_path, directory=None, index=False, reverse=False):
args = ['git', 'apply',
args = ['git', '-C', repo, 'apply',
'--ignore-space-change',
'--ignore-whitespace',
'--whitespace', 'fix'
@ -56,35 +74,31 @@ def apply_patch(repo, patch_path, directory=None, index=False, reverse=False):
args += ['--reverse']
args += ['--', patch_path]
with scoped_cwd(repo):
return_code = subprocess.call(args)
applied_successfully = (return_code == 0)
return applied_successfully
return_code = subprocess.call(args)
applied_successfully = (return_code == 0)
return applied_successfully
def get_patch(repo, commit_hash):
args = ['git', 'diff-tree',
args = ['git', '-C', repo, 'diff-tree',
'-p',
commit_hash,
'--' # Explicitly tell Git `commit_hash` is a revision, not a path.
]
with scoped_cwd(repo):
return subprocess.check_output(args)
return subprocess.check_output(args)
def get_head_commit(repo):
args = ['git', 'rev-parse', 'HEAD']
args = ['git', '-C', repo, 'rev-parse', 'HEAD']
with scoped_cwd(repo):
return subprocess.check_output(args).strip()
return subprocess.check_output(args).strip()
def reset(repo):
args = ['git', 'reset']
args = ['git', '-C', repo, 'reset']
with scoped_cwd(repo):
subprocess.check_call(args)
subprocess.check_call(args)
def commit(repo, author, message):
@ -96,12 +110,11 @@ def commit(repo, author, message):
env['GIT_COMMITTER_NAME'] = 'Anonymous Committer'
env['GIT_COMMITTER_EMAIL'] = 'anonymous@electronjs.org'
args = ['git', 'commit',
args = ['git', '-C', repo, 'commit',
'--author', author,
'--message', message
]
with scoped_cwd(repo):
return_code = subprocess.call(args, env=env)
committed_successfully = (return_code == 0)
return committed_successfully
return_code = subprocess.call(args, env=env)
committed_successfully = (return_code == 0)
return committed_successfully

View file

@ -1,177 +1,27 @@
#!/usr/bin/env python
import os
import sys
from lib import git
SOURCE_ROOT = os.path.abspath(os.path.dirname(
os.path.dirname(os.path.dirname(__file__))))
VENDOR_DIR = os.path.join(SOURCE_ROOT, 'vendor')
PYYAML_LIB_DIR = os.path.join(VENDOR_DIR, 'pyyaml', 'lib')
sys.path.append(PYYAML_LIB_DIR)
import yaml #pylint: disable=wrong-import-position,wrong-import-order
class Patch:
def __init__(self, file_path, repo_path, paths_prefix=None,
author='Anonymous <anonymous@electronjs.org>', description=None):
self.author = author
self.description = description
self.file_path = file_path
self.paths_prefix = paths_prefix
self.repo_path = repo_path
def apply(self, reverse=False, commit=False, index=False):
# Add the change to index only if we're going to commit it later.
add_to_index = index or commit
patch_applied = git.apply_patch(self.repo_path, self.file_path,
directory=self.paths_prefix,
index=add_to_index, reverse=reverse)
if not patch_applied:
return False
if commit:
message = self.__get_commit_message(reverse)
patch_committed = git.commit(self.repo_path, author=self.author,
message=message)
return patch_committed
return True
def __get_commit_message(self, reverse):
message = self.description
if message is None:
message = os.path.basename(self.file_path)
if reverse:
message = 'Revert: ' + message
return message
def reverse(self):
return self.apply(reverse=True)
def get_file_path(self):
return self.file_path
class PatchesList:
"""A list of patches for a specific git repo."""
def __init__(self, repo_path, patches):
# TODO(alexeykuzmin): Make sure that all patches have the same repo_path.
self.repo_path = repo_path
self.patches = patches
def __len__(self):
return len(self.patches)
def apply(self, reverse=False, stop_on_error=True, commit=False):
all_patches_applied = True
failed_patches = []
for patch in self.patches:
# Even if `commit` is True we're not going to commit
# individual patches, it takes too much time in the Chromium repo.
# Applying all commits takes about 10 minutes (!) on a fast dev machine.
# Instead of it we are going only to add all changes to the index
# and commit them all at once later.
applied_successfully = patch.apply(reverse=reverse, index=commit,
commit=False)
if not applied_successfully:
all_patches_applied = False
failed_patches.append(patch)
should_stop_now = not applied_successfully and stop_on_error
if should_stop_now:
break
if commit and not all_patches_applied:
git.reset(self.repo_path)
if commit and all_patches_applied:
author = 'Electron Build Process <build@electronjs.org>'
message = 'Apply Electron patches'
git.commit(self.repo_path, author=author, message=message)
return (all_patches_applied, failed_patches)
def reverse(self, stop_on_error=True, commit=False):
return self.apply(reverse=True, stop_on_error=stop_on_error, commit=commit)
def read_patch(patch_dir, patch_filename):
"""Read a patch from |patch_dir/filename| and amend the commit message with
metadata about the patch file it came from."""
ret = []
with open(os.path.join(patch_dir, patch_filename)) as f:
for l in f.readlines():
if l.startswith('diff -'):
ret.append('Patch-Filename: {}\n'.format(patch_filename))
ret.append(l)
return ''.join(ret)
class PatchesConfig:
@staticmethod
def from_directory(dir_path, project_root, config_name='.patches.yaml'):
config_path = os.path.join(dir_path, config_name)
return PatchesConfig(config_path, project_root)
def patch_from_dir(patch_dir):
"""Read a directory of patches into a format suitable for passing to
'git am'"""
with open(os.path.join(patch_dir, ".patches")) as f:
patch_list = [l.rstrip('\n') for l in f.readlines()]
def __init__(self, config_path, project_root):
self.path = config_path
self.patches_list = None
self.project_root = project_root
def __parse(self):
contents = None
if os.path.isfile(self.path):
with open(self.path, 'r') as stream:
try:
contents = yaml.load(stream)
except yaml.YAMLError as e:
print(e)
return contents
def __create_patch(self, raw_data, base_directory, repo_path, paths_prefix):
author = raw_data['author']
if author is None: # Shouldn't actually happen.
author = 'Anonymous <anonymous@electronjs.org>'
relative_file_path = raw_data['file']
absolute_file_path = os.path.join(base_directory, relative_file_path)
# Use a patch file path as a commit summary
# and optional description as a commit body.
description = relative_file_path
if raw_data['description'] is not None:
description += '\n\n' + raw_data['description']
return Patch(absolute_file_path, repo_path, paths_prefix=paths_prefix,
author=author, description=description)
def __create_patches_list(self):
config_contents = self.__parse()
if config_contents is None:
return None
relative_repo_path = os.path.normpath(config_contents['repo'])
absolute_repo_path = os.path.join(self.project_root, relative_repo_path)
# If the 'repo' path is not really a git repository,
# then use that path as a prefix for patched files.
paths_prefix = None
if not git.is_repo_root(absolute_repo_path):
assert(git.is_repo_root(self.project_root))
absolute_repo_path = self.project_root
paths_prefix = relative_repo_path
patches_data = config_contents['patches']
base_directory = os.path.abspath(os.path.dirname(self.path))
patches = [self.__create_patch(data, base_directory, absolute_repo_path,
paths_prefix) for data in patches_data]
patches_list = PatchesList(repo_path=absolute_repo_path, patches=patches)
return patches_list
def get_patches_list(self):
if self.patches_list is not None:
return self.patches_list
patches_list = self.__create_patches_list()
self.patches_list = patches_list
return patches_list
return ''.join([
read_patch(patch_dir, patch_filename)
for patch_filename in patch_list
])