Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # coding: utf-8 | 2 # coding: utf-8 |
| 3 | 3 |
| 4 # Copyright 2015 The Chromium Authors. All rights reserved. | 4 # Copyright 2015 The Chromium Authors. All rights reserved. |
| 5 # Use of this source code is governed by a BSD-style license that can be | 5 # Use of this source code is governed by a BSD-style license that can be |
| 6 # found in the LICENSE file. | 6 # found in the LICENSE file. |
| 7 | 7 |
| 8 import argparse | 8 import argparse |
| 9 import os | 9 import os |
| 10 import re | 10 import re |
| 11 import subprocess | 11 import subprocess |
| 12 import sys | 12 import sys |
| 13 import tempfile | 13 import tempfile |
| 14 import textwrap | 14 import textwrap |
| 15 | 15 |
| 16 | 16 |
| 17 EXCLUDED_FILES = ['codereview.settings'] | |
| 18 | |
| 17 IS_WINDOWS = sys.platform.startswith('win') | 19 IS_WINDOWS = sys.platform.startswith('win') |
| 18 | 20 |
| 19 | 21 |
| 20 def SubprocessCheckCall0Or1(args): | 22 def SubprocessCheckCall0Or1(args): |
| 21 """Like subprocss.check_call(), but allows a return code of 1. | 23 """Like subprocss.check_call(), but allows a return code of 1. |
| 22 | 24 |
| 23 Returns True if the subprocess exits with code 0, False if it exits with | 25 Returns True if the subprocess exits with code 0, False if it exits with |
| 24 code 1, and re-raises the subprocess.check_call() exception otherwise. | 26 code 1, and re-raises the subprocess.check_call() exception otherwise. |
| 25 """ | 27 """ |
| 26 try: | 28 try: |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 100 # the update operation. | 102 # the update operation. |
| 101 if not GitMergeBaseIsAncestor(parsed.update_to, 'FETCH_HEAD'): | 103 if not GitMergeBaseIsAncestor(parsed.update_to, 'FETCH_HEAD'): |
| 102 raise Exception('update_to is not an ancestor of FETCH_HEAD', | 104 raise Exception('update_to is not an ancestor of FETCH_HEAD', |
| 103 parsed.update_to, | 105 parsed.update_to, |
| 104 'FETCH_HEAD') | 106 'FETCH_HEAD') |
| 105 if not GitMergeBaseIsAncestor(revision_old, parsed.update_to): | 107 if not GitMergeBaseIsAncestor(revision_old, parsed.update_to): |
| 106 raise Exception('revision_old is not an ancestor of update_to', | 108 raise Exception('revision_old is not an ancestor of update_to', |
| 107 revision_old, | 109 revision_old, |
| 108 parsed.update_to) | 110 parsed.update_to) |
| 109 | 111 |
| 112 cherry_pick_start_head = original_head | |
| 113 | |
| 114 # Put excluded files back at the old revision, so that any changes to them | |
| 115 # can be made during the update. They’ll be deleted again before the update | |
| 116 # is committed. | |
| 117 git_root = subprocess.check_output(['git', 'rev-parse', '--show-toplevel'], | |
| 118 shell=IS_WINDOWS).rstrip() | |
| 119 excluded_full_paths = [] | |
| 120 for excluded_file in EXCLUDED_FILES: | |
| 121 contents = subprocess.check_output( | |
| 122 ['git', 'cat-file', 'blob', revision_old + ':' + excluded_file], | |
|
scottmg
2017/03/01 18:38:15
I can imagine this mucking up newlines on Windows,
| |
| 123 shell=IS_WINDOWS) | |
| 124 excluded_full_path = \ | |
|
agable
2017/03/01 20:50:54
Don't use \ for line continuations, use parenthese
| |
| 125 os.path.join(git_root, parsed.subtree, excluded_file) | |
| 126 excluded_full_paths.append(excluded_full_path) | |
| 127 with open(excluded_full_path, 'wbx') as file: | |
|
scottmg
2017/03/01 18:38:15
I've never used 'x' before, why is it useful here?
Mark Mentovai
2017/03/01 21:00:36
scottmg wrote:
| |
| 128 file.write(contents) | |
| 129 if excluded_full_paths: | |
| 130 add_cmd = ['git', 'add'] | |
| 131 add_cmd.extend(excluded_full_paths) | |
| 132 subprocess.check_call(add_cmd, shell=IS_WINDOWS) | |
| 133 commit_cmd = ['git', 'commit', '--message', 'Restore excluded files'] | |
| 134 commit_cmd.extend(excluded_full_paths) | |
| 135 subprocess.check_call(commit_cmd, shell=IS_WINDOWS) | |
| 136 | |
| 137 # Cherry-picking will begin from here. | |
| 138 cherry_pick_start_head = ( | |
| 139 subprocess.check_output(['git', 'rev-parse', 'HEAD'], | |
| 140 shell=IS_WINDOWS).rstrip()) | |
| 141 | |
| 110 update_range = revision_old + '..' + parsed.update_to | 142 update_range = revision_old + '..' + parsed.update_to |
| 111 | 143 |
| 112 # This cherry-picks each change in the window from the upstream project into | 144 # This cherry-picks each change in the window from the upstream project into |
| 113 # the current branch. | 145 # the current branch. |
| 114 assisted_cherry_pick = False | 146 assisted_cherry_pick = False |
| 115 try: | 147 try: |
| 116 if not SubprocessCheckCall0Or1(['git', | 148 if not SubprocessCheckCall0Or1(['git', |
| 117 'cherry-pick', | 149 'cherry-pick', |
| 118 '--keep-redundant-commits', | 150 '--keep-redundant-commits', |
| 119 '--strategy=subtree', | 151 '--strategy=subtree', |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 143 '--abbrev-commit', | 175 '--abbrev-commit', |
| 144 '--pretty=oneline', | 176 '--pretty=oneline', |
| 145 '--reverse', | 177 '--reverse', |
| 146 update_range], | 178 update_range], |
| 147 shell=IS_WINDOWS).splitlines(False) | 179 shell=IS_WINDOWS).splitlines(False) |
| 148 | 180 |
| 149 if assisted_cherry_pick: | 181 if assisted_cherry_pick: |
| 150 # If the user had to help, count the number of cherry-picked commits, | 182 # If the user had to help, count the number of cherry-picked commits, |
| 151 # expecting it to match. | 183 # expecting it to match. |
| 152 cherry_picked_commits = int(subprocess.check_output( | 184 cherry_picked_commits = int(subprocess.check_output( |
| 153 ['git', 'rev-list', '--count', original_head + '..HEAD'])) | 185 ['git', 'rev-list', '--count', cherry_pick_start + '..HEAD'], |
| 186 shell=IS_WINDOWS)) | |
| 154 if cherry_picked_commits != len(log_lines): | 187 if cherry_picked_commits != len(log_lines): |
| 155 print >>sys.stderr, 'Something smells fishy, aborting anyway...' | 188 print >>sys.stderr, 'Something smells fishy, aborting anyway...' |
| 156 subprocess.call(['git', 'cherry-pick', '--abort'], shell=IS_WINDOWS) | 189 subprocess.call(['git', 'cherry-pick', '--abort'], shell=IS_WINDOWS) |
| 157 raise Exception('not all commits were cherry-picked', | 190 raise Exception('not all commits were cherry-picked', |
| 158 len(log_lines), | 191 len(log_lines), |
| 159 cherry_picked_commits) | 192 cherry_picked_commits) |
| 160 | 193 |
| 161 # Make a nice commit message. Start with the full commit hash. | 194 # Make a nice commit message. Start with the full commit hash. |
| 162 revision_new = subprocess.check_output( | 195 revision_new = subprocess.check_output( |
| 163 ['git', 'rev-parse', parsed.update_to], shell=IS_WINDOWS).rstrip() | 196 ['git', 'rev-parse', parsed.update_to], shell=IS_WINDOWS).rstrip() |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 189 # If the in-tree copy has no changes relative to the upstream, clear the | 222 # If the in-tree copy has no changes relative to the upstream, clear the |
| 190 # “Local Modifications” section of the README. | 223 # “Local Modifications” section of the README. |
| 191 has_local_modifications = True | 224 has_local_modifications = True |
| 192 if SubprocessCheckCall0Or1(['git', | 225 if SubprocessCheckCall0Or1(['git', |
| 193 'diff-tree', | 226 'diff-tree', |
| 194 '--quiet', | 227 '--quiet', |
| 195 parsed.update_to, | 228 parsed.update_to, |
| 196 'HEAD:' + parsed.subtree]): | 229 'HEAD:' + parsed.subtree]): |
| 197 has_local_modifications = False | 230 has_local_modifications = False |
| 198 | 231 |
| 232 if not EXCLUDED_FILES: | |
| 233 modifications = 'None.' | |
| 234 elif len(EXCLUDED_FILES) == 1: | |
| 235 modifications = \ | |
| 236 ' - %s has been excluded.' % EXCLUDED_FILES[0] | |
| 237 else: | |
| 238 modifications =\ | |
| 239 ' - The following files have been excluded:' | |
| 240 for excluded_file in sorted(EXCLUDED_FILES): | |
| 241 modifications += '\n - ' + excluded_file | |
| 199 readme_content_new = re.sub(r'\nLocal Modifications:\n.*$', | 242 readme_content_new = re.sub(r'\nLocal Modifications:\n.*$', |
| 200 '\nLocal Modifications:\nNone.', | 243 '\nLocal Modifications:\n' + modifications, |
| 201 readme_content_new, | 244 readme_content_new, |
| 202 1) | 245 1) |
| 203 | 246 |
| 204 # This soft-reset causes all of the cherry-picks to show up as staged, | 247 # Remove excluded files as promised. |
| 205 # which will have the effect of squashing them along with the README update | 248 if excluded_full_paths: |
| 206 # when committed below. | 249 rm_cmd = ['git', 'rm'] |
| 250 rm_cmd.extend(excluded_full_paths) | |
| 251 subprocess.check_call(rm_cmd, shell=IS_WINDOWS) | |
| 252 commit_cmd = ['git', 'commit', '--message', 'Remove excluded files'] | |
| 253 commit_cmd.extend(excluded_full_paths) | |
| 254 subprocess.check_call(commit_cmd, shell=IS_WINDOWS) | |
| 255 | |
| 256 # This soft-reset causes all of the cherry-picks and any additions and | |
| 257 # removals of excluded files to show up as staged, which will have the | |
| 258 # effect of squashing them along with the README update when committed | |
| 259 # below. | |
| 207 subprocess.check_call(['git', 'reset', '--soft', original_head], | 260 subprocess.check_call(['git', 'reset', '--soft', original_head], |
| 208 shell=IS_WINDOWS) | 261 shell=IS_WINDOWS) |
| 209 | 262 |
| 210 # Write the new README. | 263 # Write the new README. |
| 211 open(readme_path, 'wb').write(readme_content_new) | 264 open(readme_path, 'wb').write(readme_content_new) |
| 212 | 265 |
| 213 # Commit everything. | 266 # Commit everything. |
| 214 subprocess.check_call(['git', 'add', readme_path], shell=IS_WINDOWS) | 267 subprocess.check_call(['git', 'add', readme_path], shell=IS_WINDOWS) |
| 215 | 268 |
| 216 try: | 269 try: |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 228 if has_local_modifications: | 281 if has_local_modifications: |
| 229 print >>sys.stderr, ( | 282 print >>sys.stderr, ( |
| 230 'Remember to check the Local Modifications section in ' + | 283 'Remember to check the Local Modifications section in ' + |
| 231 readme_path) | 284 readme_path) |
| 232 | 285 |
| 233 return 0 | 286 return 0 |
| 234 | 287 |
| 235 | 288 |
| 236 if __name__ == '__main__': | 289 if __name__ == '__main__': |
| 237 sys.exit(main(sys.argv[1:])) | 290 sys.exit(main(sys.argv[1:])) |
| OLD | NEW |