| #!/usr/bin/env python |
| # Copyright (C) 2017 Apple Inc. All rights reserved. |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions |
| # are met: |
| # 1. Redistributions of source code must retain the above copyright |
| # notice, this list of conditions and the following disclaimer. |
| # 2. Redistributions in binary form must reproduce the above copyright |
| # notice, this list of conditions and the following disclaimer in the |
| # documentation and/or other materials provided with the distribution. |
| # |
| # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND |
| # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| # DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR |
| # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| # A webkitpy import needs to go first for autoinstaller to work with subsequent imports. |
| import webkitpy |
| |
| import os |
| import six |
| import sys |
| |
| REBASE_DICTIONARY = { |
| 'WebKit2/': 'WebKit/', |
| 'WebKit/': 'WebKitLegacy/' |
| } |
| class RebaseStatus: |
| DO_NOT_NEED = 0 |
| MAYBE_NEED = 1 |
| NEED = 2 |
| ALREADY = 3 |
| |
| |
| def append_source(path): |
| return os.path.join('Source', path) |
| |
| |
| def is_editable_line(line): |
| editable_prefixes = ('Index: ', '--- ', '+++ ', 'diff --git ') |
| return any(map(line.startswith, editable_prefixes)) |
| |
| |
| def needs_rebase(line): |
| if not is_editable_line(line): |
| return RebaseStatus.DO_NOT_NEED |
| |
| for current_name, rebased_name in six.iteritems(REBASE_DICTIONARY): |
| # Check if we've already rebased. We need to check if the rebased_name is already in the REBASE_DICTIONARY, |
| # in this case, we don't know if we've already been rebased. |
| if append_source(rebased_name) in line and rebased_name not in REBASE_DICTIONARY: |
| return RebaseStatus.ALREADY |
| |
| # Check if we need to rebase. We need to check if the current name is one of the potential rebase names. |
| # In this case, we don't know if we need the rebase. |
| if append_source(current_name) in line and current_name not in REBASE_DICTIONARY.values(): |
| return RebaseStatus.NEED |
| |
| # Check if we might need a rebase |
| for current_name, rebased_name in six.iteritems(REBASE_DICTIONARY): |
| if append_source(current_name) in line: |
| return RebaseStatus.MAYBE_NEED |
| |
| return RebaseStatus.DO_NOT_NEED |
| |
| |
| def rebase_line(line): |
| if not is_editable_line(line): |
| for current_name, rebased_name in six.iteritems(REBASE_DICTIONARY): |
| if current_name in line: |
| sys.stderr.write('Found an instance of {} in the patch. Did you mean to replace it with {}?\n'.format(current_name, rebased_name)) |
| return line |
| for current_name, rebased_name in six.iteritems(REBASE_DICTIONARY): |
| if append_source(current_name) in line: |
| return line.replace(append_source(current_name), append_source(rebased_name)) |
| return line |
| |
| |
| def rebase(patch_content, output_file, patch_name): |
| rebase_status = RebaseStatus.DO_NOT_NEED |
| for line in patch_content: |
| line_status = needs_rebase(line) |
| if (rebase_status == RebaseStatus.NEED and line_status == RebaseStatus.ALREADY) or (rebase_status == RebaseStatus.ALREADY and line_status == RebaseStatus.NEED): |
| sys.stderr.write('{} is a partially rebased patch\n'.format(patch_name)) |
| return -1 |
| rebase_status = max(rebase_status, line_status) |
| |
| if rebase_status == RebaseStatus.MAYBE_NEED and output_file == sys.stdout: |
| sys.stderr.write('Cannot determine if path from {} has already been rebased, rebasing anyways\n'.format(patch_name)) |
| elif rebase_status == RebaseStatus.MAYBE_NEED: |
| rebase_status = RebaseStatus.MAYBE_NEED |
| sys.stdout.write('{} may have already been rebased, are you sure you want to rebase it Y/n?\n'.format(patch_name)) |
| line = sys.stdin.readline().rstrip().upper() |
| if line == 'Y' or line == 'YES': |
| rebase_status = RebaseStatus.NEED |
| |
| if rebase_status == RebaseStatus.ALREADY or rebase_status == RebaseStatus.MAYBE_NEED: |
| # Output the provided patch if no re-base is required |
| for line in patch_content: |
| output_file.write(line) |
| return 0 |
| |
| if rebase_status == RebaseStatus.MAYBE_NEED: |
| sys.stderr.write('Cannot determine if path from {} has already been rebased, rebasing anyways\n'.format(patch_name)) |
| |
| for line in patch_content: |
| output_file.write(rebase_line(line)) |
| |
| return 0 |
| |
| |
| def parse_arguments(): |
| files_to_rebase = [] |
| if len(sys.argv) == 1: |
| sys.stderr.write('Reading patch from stdin\n') |
| return [] |
| elif sys.argv[1] == '-h' or sys.argv[1] == 'help': |
| print('rebase-patch-after-webkit-move usage:') |
| print('\trebase-patch-after-webkit-move -h, help') |
| print('\t\tPrint this message') |
| print('\trebase-patch-after-webkit-move <path to patch>') |
| print('\t\tReplace the patch at the provided path with a rebased patch') |
| print('\trebase-patch-after-webkit-move') |
| print('\t\tTreat stdin as the patch to be rebased') |
| exit(0) |
| |
| for path in sys.argv[1:]: |
| if not os.path.isfile(path): |
| sys.stderr.write('{} does not exist, cannot rebase patch\n'.format(path)) |
| exit(1) |
| files_to_rebase.append(path) |
| |
| return files_to_rebase |
| |
| |
| if __name__ == '__main__': |
| files_to_rebase = parse_arguments() |
| |
| status = 0 |
| for path in files_to_rebase: |
| if rebase(open(path, 'r').readlines(), open(path, 'w'), path) != 0: |
| status = 1 |
| if not files_to_rebase: |
| status = rebase(sys.stdin.readlines(), sys.stdout, 'STDIN') |
| exit(status) |