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 |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 61 parser.add_argument( | 61 parser.add_argument( |
| 62 '--fetch-ref', | 62 '--fetch-ref', |
| 63 default='HEAD', | 63 default='HEAD', |
| 64 help='The remote ref to fetch', | 64 help='The remote ref to fetch', |
| 65 metavar='REF') | 65 metavar='REF') |
| 66 parser.add_argument( | 66 parser.add_argument( |
| 67 '--readme', | 67 '--readme', |
| 68 help='The README.chromium file describing the imported project', | 68 help='The README.chromium file describing the imported project', |
| 69 metavar='FILE', | 69 metavar='FILE', |
| 70 dest='readme_path') | 70 dest='readme_path') |
| 71 parser.add_argument( | |
| 72 '--exclude', | |
| 73 default=['codereview.settings'], | |
| 74 action='append', | |
| 75 help='Files to exclude from the imported copy', | |
| 76 metavar='PATH') | |
| 71 parsed = parser.parse_args(args) | 77 parsed = parser.parse_args(args) |
| 72 | 78 |
| 73 original_head = ( | 79 original_head = ( |
| 74 subprocess.check_output(['git', 'rev-parse', 'HEAD'], | 80 subprocess.check_output(['git', 'rev-parse', 'HEAD'], |
| 75 shell=IS_WINDOWS).rstrip()) | 81 shell=IS_WINDOWS).rstrip()) |
| 76 | 82 |
| 77 # Read the README, because that’s what it’s for. Extract some things from | 83 # Read the README, because that’s what it’s for. Extract some things from |
| 78 # it, and save it to be able to update it later. | 84 # it, and save it to be able to update it later. |
| 79 readme_path = (parsed.readme_path or | 85 readme_path = (parsed.readme_path or |
| 80 os.path.join(os.path.dirname(__file__ or '.'), | 86 os.path.join(os.path.dirname(__file__ or '.'), |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 100 # the update operation. | 106 # the update operation. |
| 101 if not GitMergeBaseIsAncestor(parsed.update_to, 'FETCH_HEAD'): | 107 if not GitMergeBaseIsAncestor(parsed.update_to, 'FETCH_HEAD'): |
| 102 raise Exception('update_to is not an ancestor of FETCH_HEAD', | 108 raise Exception('update_to is not an ancestor of FETCH_HEAD', |
| 103 parsed.update_to, | 109 parsed.update_to, |
| 104 'FETCH_HEAD') | 110 'FETCH_HEAD') |
| 105 if not GitMergeBaseIsAncestor(revision_old, parsed.update_to): | 111 if not GitMergeBaseIsAncestor(revision_old, parsed.update_to): |
| 106 raise Exception('revision_old is not an ancestor of update_to', | 112 raise Exception('revision_old is not an ancestor of update_to', |
| 107 revision_old, | 113 revision_old, |
| 108 parsed.update_to) | 114 parsed.update_to) |
| 109 | 115 |
| 110 update_range = revision_old + '..' + parsed.update_to | 116 # git-filter-branch needs a ref to update. It’s not enough to just tell it |
| 117 # to operate on a range of commits ending at parsed.update_to, because | |
| 118 # parsed.update_to is a commit hash that can’t be updated. | |
| 119 subprocess.check_call(['git', 'update-ref', 'UPDATE_TO', parsed.update_to], | |
| 120 shell=IS_WINDOWS) | |
| 111 | 121 |
| 112 # This cherry-picks each change in the window from the upstream project into | 122 # Filter the range being updated over to exclude files that ought to be |
| 113 # the current branch. | 123 # missing. This points UPDATE_TO to the rewritten (filtered) version. |
| 124 # git-filter-branch inists on running from the top level of the working | |
| 125 # tree. | |
| 126 toplevel = subprocess.check_output(['git', 'rev-parse', '--show-toplevel'], | |
| 127 shell=IS_WINDOWS).rstrip() | |
| 128 subprocess.check_call( | |
| 129 ['git', | |
| 130 'filter-branch', | |
| 131 '--force', | |
| 132 '--index-filter', | |
| 133 'git rm --cached --ignore-unmatch ' + ' '.join(parsed.exclude), | |
|
Mark Mentovai
2017/03/02 02:38:05
This line bothers me. How does git-filter-branch i
| |
| 134 revision_old + '..UPDATE_TO'], | |
| 135 cwd=toplevel, | |
| 136 shell=IS_WINDOWS) | |
|
Mark Mentovai
2017/03/02 02:38:05
I’m concerned that the approach might not work on
Mark Mentovai
2017/03/02 02:40:46
I wrote:
scottmg
2017/03/02 03:13:24
Yeah, it's because depot_tools wraps git.exe in gi
| |
| 137 | |
| 138 # git-filter-branch saved a copy of the original UPDATE_TO at | |
| 139 # original/UPDATE_TO, but this isn’t useful because it refers to the same | |
| 140 # thing as parsed.update_to, which is already known. | |
| 141 subprocess.check_call( | |
| 142 ['git', 'update-ref', '-d', 'refs/original/UPDATE_TO'], | |
| 143 shell=IS_WINDOWS) | |
| 144 | |
| 145 filtered_update_range = revision_old + '..UPDATE_TO' | |
| 146 unfiltered_update_range = revision_old + '..' + parsed.update_to | |
| 147 | |
| 148 # This cherry-picks each change in the window from the filtered view of the | |
| 149 # upstream project into the current branch. | |
| 114 assisted_cherry_pick = False | 150 assisted_cherry_pick = False |
| 115 try: | 151 try: |
| 116 if not SubprocessCheckCall0Or1(['git', | 152 if not SubprocessCheckCall0Or1(['git', |
| 117 'cherry-pick', | 153 'cherry-pick', |
| 118 '--keep-redundant-commits', | 154 '--keep-redundant-commits', |
| 119 '--strategy=subtree', | 155 '--strategy=subtree', |
| 120 '-Xsubtree=' + parsed.subtree, | 156 '-Xsubtree=' + parsed.subtree, |
| 121 '-x', | 157 '-x', |
| 122 update_range]): | 158 filtered_update_range]): |
| 123 assisted_cherry_pick = True | 159 assisted_cherry_pick = True |
| 124 print >>sys.stderr, (""" | 160 print >>sys.stderr, (""" |
| 125 Please fix the errors above and run "git cherry-pick --continue". | 161 Please fix the errors above and run "git cherry-pick --continue". |
| 126 Press Enter when "git cherry-pick" completes. | 162 Press Enter when "git cherry-pick" completes. |
| 127 You may use a new shell for this, or ^Z if job control is available. | 163 You may use a new shell for this, or ^Z if job control is available. |
| 128 Press ^C to abort. | 164 Press ^C to abort. |
| 129 """) | 165 """) |
| 130 raw_input() | 166 raw_input() |
| 131 except: | 167 except: |
| 132 # ^C, signal, or something else. | 168 # ^C, signal, or something else. |
| 133 print >>sys.stderr, 'Aborting...' | 169 print >>sys.stderr, 'Aborting...' |
| 134 subprocess.call(['git', 'cherry-pick', '--abort'], shell=IS_WINDOWS) | 170 subprocess.call(['git', 'cherry-pick', '--abort'], shell=IS_WINDOWS) |
| 135 raise | 171 raise |
| 136 | 172 |
| 137 # Get an abbreviated hash and subject line for each commit in the window, | 173 # Get an abbreviated hash and subject line for each commit in the window, |
| 138 # sorted in chronological order. | 174 # sorted in chronological order. Use the unfiltered view so that the commit |
| 175 # hashes are recognizable. | |
| 139 log_lines = subprocess.check_output(['git', | 176 log_lines = subprocess.check_output(['git', |
| 140 '-c', | 177 '-c', |
| 141 'core.abbrev=12', | 178 'core.abbrev=12', |
| 142 'log', | 179 'log', |
| 143 '--abbrev-commit', | 180 '--abbrev-commit', |
| 144 '--pretty=oneline', | 181 '--pretty=oneline', |
| 145 '--reverse', | 182 '--reverse', |
| 146 update_range], | 183 unfiltered_update_range], |
| 147 shell=IS_WINDOWS).splitlines(False) | 184 shell=IS_WINDOWS).splitlines(False) |
| 148 | 185 |
| 149 if assisted_cherry_pick: | 186 if assisted_cherry_pick: |
| 150 # If the user had to help, count the number of cherry-picked commits, | 187 # If the user had to help, count the number of cherry-picked commits, |
| 151 # expecting it to match. | 188 # expecting it to match. |
| 152 cherry_picked_commits = int(subprocess.check_output( | 189 cherry_picked_commits = int(subprocess.check_output( |
| 153 ['git', 'rev-list', '--count', original_head + '..HEAD'])) | 190 ['git', 'rev-list', '--count', original_head + '..HEAD'], |
| 191 shell=IS_WINDOWS)) | |
| 154 if cherry_picked_commits != len(log_lines): | 192 if cherry_picked_commits != len(log_lines): |
| 155 print >>sys.stderr, 'Something smells fishy, aborting anyway...' | 193 print >>sys.stderr, 'Something smells fishy, aborting anyway...' |
| 156 subprocess.call(['git', 'cherry-pick', '--abort'], shell=IS_WINDOWS) | 194 subprocess.call(['git', 'cherry-pick', '--abort'], shell=IS_WINDOWS) |
| 157 raise Exception('not all commits were cherry-picked', | 195 raise Exception('not all commits were cherry-picked', |
| 158 len(log_lines), | 196 len(log_lines), |
| 159 cherry_picked_commits) | 197 cherry_picked_commits) |
| 160 | 198 |
| 161 # Make a nice commit message. Start with the full commit hash. | 199 # Make a nice commit message. Start with the full commit hash. |
| 162 revision_new = subprocess.check_output( | 200 revision_new = subprocess.check_output( |
| 163 ['git', 'rev-parse', parsed.update_to], shell=IS_WINDOWS).rstrip() | 201 ['git', 'rev-parse', parsed.update_to], shell=IS_WINDOWS).rstrip() |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 185 readme_content_old, | 223 readme_content_old, |
| 186 1, | 224 1, |
| 187 re.MULTILINE) | 225 re.MULTILINE) |
| 188 | 226 |
| 189 # If the in-tree copy has no changes relative to the upstream, clear the | 227 # If the in-tree copy has no changes relative to the upstream, clear the |
| 190 # “Local Modifications” section of the README. | 228 # “Local Modifications” section of the README. |
| 191 has_local_modifications = True | 229 has_local_modifications = True |
| 192 if SubprocessCheckCall0Or1(['git', | 230 if SubprocessCheckCall0Or1(['git', |
| 193 'diff-tree', | 231 'diff-tree', |
| 194 '--quiet', | 232 '--quiet', |
| 195 parsed.update_to, | 233 'UPDATE_TO', |
| 196 'HEAD:' + parsed.subtree]): | 234 'HEAD:' + parsed.subtree]): |
| 197 has_local_modifications = False | 235 has_local_modifications = False |
| 198 | 236 |
| 237 if not parsed.exclude: | |
| 238 modifications = 'None.\n' | |
| 239 elif len(parsed.exclude) == 1: | |
| 240 modifications = \ | |
| 241 ' - %s has been excluded.\n' % parsed.exclude[0] | |
| 242 else: | |
| 243 modifications =\ | |
| 244 ' - The following files have been excluded:\n' | |
| 245 for excluded in sorted(parsed.exclude): | |
| 246 modifications += ' - ' + excluded + '\n' | |
| 199 readme_content_new = re.sub(r'\nLocal Modifications:\n.*$', | 247 readme_content_new = re.sub(r'\nLocal Modifications:\n.*$', |
| 200 '\nLocal Modifications:\nNone.', | 248 '\nLocal Modifications:\n' + modifications, |
| 201 readme_content_new, | 249 readme_content_new, |
| 202 1) | 250 1, |
| 251 re.DOTALL) | |
| 203 | 252 |
| 204 # This soft-reset causes all of the cherry-picks to show up as staged, | 253 # The UPDATE_TO ref is no longer useful. |
| 205 # which will have the effect of squashing them along with the README update | 254 subprocess.check_call(['git', 'update-ref', '-d', 'UPDATE_TO'], |
| 206 # when committed below. | 255 shell=IS_WINDOWS) |
| 256 | |
| 257 # This soft-reset causes all of the cherry-picks to show up as staged, which | |
| 258 # will have the effect of squashing them along with the README update when | |
| 259 # committed 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 |