| OLD | NEW |
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 | 2 |
| 3 # Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 3 # Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 4 # for details. All rights reserved. Use of this source code is governed by a | 4 # for details. All rights reserved. Use of this source code is governed by a |
| 5 # BSD-style license that can be found in the LICENSE file. | 5 # BSD-style license that can be found in the LICENSE file. |
| 6 | 6 |
| 7 import optparse | 7 import optparse |
| 8 import os | 8 import os |
| 9 import platform | 9 import platform |
| 10 import subprocess | 10 import subprocess |
| 11 import sys | 11 import sys |
| 12 | 12 |
| 13 """A script used to revert one or a sequence of consecutive CLs, for svn and | 13 """A script used to revert one or a sequence of consecutive CLs, for svn and |
| 14 git-svn users. | 14 git-svn users. |
| 15 """ | 15 """ |
| 16 | 16 |
| 17 def parse_args(): | 17 def parse_args(): |
| 18 parser = optparse.OptionParser() | 18 parser = optparse.OptionParser() |
| 19 parser.add_option('--revisions', '-r', dest='rev_range', action='store', | 19 parser.add_option('--revisions', '-r', dest='rev_range', action='store', |
| 20 default=None, help='The revision number(s) of the commits ' | 20 default=None, help='The revision number(s) of the commits ' |
| 21 'you wish to undo. An individual number, or a range (8-10, ' | 21 'you wish to undo. An individual number, or a range (8-10, ' |
| 22 '8..10, or 8:10).') | 22 '8..10, or 8:10).') |
| 23 args, _ = parser.parse_args() | 23 args, _ = parser.parse_args() |
| 24 revision_range = args.rev_range | 24 revision_range = args.rev_range |
| 25 if revision_range == None: | 25 if revision_range is None: |
| 26 maybe_fail('You must specify at least one revision number to revert.') | 26 maybe_fail('You must specify at least one revision number to revert.') |
| 27 if revision_range.find('-') > -1 or revision_range.find(':') > -1 or \ | 27 if revision_range.find('-') > -1 or revision_range.find(':') > -1 or \ |
| 28 revision_range.find('..') > -1: | 28 revision_range.find('..') > -1: |
| 29 # We have a range of commits to revert. | 29 # We have a range of commits to revert. |
| 30 split = revision_range.split('-') | 30 split = revision_range.split('-') |
| 31 if len(split) == 1: | 31 if len(split) == 1: |
| 32 split = revision_range.split(':') | 32 split = revision_range.split(':') |
| 33 if len(split) == 1: | 33 if len(split) == 1: |
| 34 split = revision_range.split('..') | 34 split = revision_range.split('..') |
| 35 start = int(split[0]) | 35 start = int(split[0]) |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 75 for line in results.split('\n'): | 75 for line in results.split('\n'): |
| 76 if not is_git and (not line.strip().startswith('?') and line != ''): | 76 if not is_git and (not line.strip().startswith('?') and line != ''): |
| 77 return True | 77 return True |
| 78 elif is_git and ('Changes to be committed' in line or | 78 elif is_git and ('Changes to be committed' in line or |
| 79 'Changes not staged for commit:' in line): | 79 'Changes not staged for commit:' in line): |
| 80 return True | 80 return True |
| 81 if is_git: | 81 if is_git: |
| 82 p = subprocess.Popen(['git', 'log', '-1'], stdout=subprocess.PIPE, | 82 p = subprocess.Popen(['git', 'log', '-1'], stdout=subprocess.PIPE, |
| 83 shell=(platform.system()=='Windows')) | 83 shell=(platform.system()=='Windows')) |
| 84 output, _ = p.communicate() | 84 output, _ = p.communicate() |
| 85 if find_git_info(output) == None: | 85 if find_git_info(output) is None: |
| 86 return True | 86 return True |
| 87 return False | 87 return False |
| 88 | 88 |
| 89 def run_cmd(cmd_list, suppress_output=False, std_in=''): | 89 def run_cmd(cmd_list, suppress_output=False, std_in=''): |
| 90 """Run the specified command and print out any output to stdout.""" | 90 """Run the specified command and print out any output to stdout.""" |
| 91 print ' '.join(cmd_list) | 91 print ' '.join(cmd_list) |
| 92 p = subprocess.Popen(cmd_list, stdout=subprocess.PIPE, stderr=subprocess.PIPE, | 92 p = subprocess.Popen(cmd_list, stdout=subprocess.PIPE, stderr=subprocess.PIPE, |
| 93 stdin=subprocess.PIPE, | 93 stdin=subprocess.PIPE, |
| 94 shell=(platform.system()=='Windows')) | 94 shell=(platform.system()=='Windows')) |
| 95 output, stderr = p.communicate(std_in) | 95 output, stderr = p.communicate(std_in) |
| 96 if output and not suppress_output: | 96 if output and not suppress_output: |
| 97 print output | 97 print output |
| 98 if stderr and not suppress_output: | 98 if stderr and not suppress_output: |
| 99 print stderr | 99 print stderr |
| 100 return (output, stderr) | 100 return output, stderr |
| 101 | 101 |
| 102 def runs_git(): | 102 def runs_git(): |
| 103 """Returns True if we're standing in an svn-git repository.""" | 103 """Returns True if we're standing in an svn-git repository.""" |
| 104 p = subprocess.Popen(['svn', 'info'], stdout=subprocess.PIPE, | 104 p = subprocess.Popen(['svn', 'info'], stdout=subprocess.PIPE, |
| 105 stderr=subprocess.PIPE, | 105 stderr=subprocess.PIPE, |
| 106 shell=(platform.system()=='Windows')) | 106 shell=(platform.system()=='Windows')) |
| 107 output, err = p.communicate() | 107 output, err = p.communicate() |
| 108 if err != None and 'is not a working copy' in err: | 108 if err is not None and 'is not a working copy' in err: |
| 109 p = subprocess.Popen(['git', 'status'], stdout=subprocess.PIPE, | 109 p = subprocess.Popen(['git', 'status'], stdout=subprocess.PIPE, |
| 110 shell=(platform.system()=='Windows')) | 110 shell=(platform.system()=='Windows')) |
| 111 output, _ = p.communicate() | 111 output, _ = p.communicate() |
| 112 if 'fatal: Not a git repository' in output: | 112 if 'fatal: Not a git repository' in output: |
| 113 maybe_fail('Error: not running git or svn.') | 113 maybe_fail('Error: not running git or svn.') |
| 114 else: | 114 else: |
| 115 return True | 115 return True |
| 116 return False | 116 return False |
| 117 | 117 |
| 118 def find_git_info(git_log, rev_num=None): | 118 def find_git_info(git_log, rev_num=None): |
| 119 """Determine the latest svn revision number if rev_num = None, or find the | 119 """Determine the latest svn revision number if rev_num = None, or find the |
| 120 git commit_id that corresponds to a particular svn revision number. | 120 git commit_id that corresponds to a particular svn revision number. |
| 121 """ | 121 """ |
| 122 for line in git_log.split('\n'): | 122 for line in git_log.split('\n'): |
| 123 tokens = line.split() | 123 tokens = line.split() |
| 124 if len(tokens) == 2 and tokens[0] == 'commit': | 124 if len(tokens) == 2 and tokens[0] == 'commit': |
| 125 current_commit_id = tokens[1] | 125 current_commit_id = tokens[1] |
| 126 elif len(tokens) > 0 and tokens[0] == 'git-svn-id:': | 126 elif len(tokens) > 0 and tokens[0] == 'git-svn-id:': |
| 127 revision_number = int(tokens[1].split('@')[1]) | 127 revision_number = int(tokens[1].split('@')[1]) |
| 128 if revision_number == rev_num: | 128 if revision_number == rev_num: |
| 129 return current_commit_id | 129 return current_commit_id |
| 130 if rev_num == None: | 130 if rev_num is None: |
| 131 return revision_number | 131 return revision_number |
| 132 | 132 |
| 133 def revert(start, end, is_git): | 133 def revert(start, end, is_git): |
| 134 """Revert the sequence of CLs. | 134 """Revert the sequence of CLs. |
| 135 Args: | 135 Args: |
| 136 - start: The first CL to revert. | 136 - start: The first CL to revert. |
| 137 - end: The last CL to revert. | 137 - end: The last CL to revert. |
| 138 - is_git: True if we are in a git-svn checkout. | 138 - is_git: True if we are in a git-svn checkout. |
| 139 """ | 139 """ |
| 140 if not is_git: | 140 if not is_git: |
| 141 _, err = run_cmd(['svn', 'merge', '-r', '%d:%d' % (end, start-1), '.'], | 141 _, err = run_cmd(['svn', 'merge', '-r', '%d:%d' % (end, start-1), '.'], |
| 142 std_in='p') | 142 std_in='p') |
| 143 if 'Conflict discovered' in err: | 143 if 'Conflict discovered' in err: |
| 144 maybe_fail('Please fix the above conflicts before submitting. Then create' | 144 maybe_fail('Please fix the above conflicts before submitting. Then create' |
| 145 ' a CL and submit your changes to complete the revert.') | 145 ' a CL and submit your changes to complete the revert.') |
| 146 | 146 |
| 147 else: | 147 else: |
| 148 # If we're running git, we have to use the log feature to find the commit | 148 # If we're running git, we have to use the log feature to find the commit |
| 149 # id(s) that correspond to the particular revision number(s). | 149 # id(s) that correspond to the particular revision number(s). |
| 150 output, _ = run_cmd(['git', 'log', '-1'], suppress_output=True) | 150 output, _ = run_cmd(['git', 'log', '-1'], suppress_output=True) |
| 151 current_revision = find_git_info(output) | 151 current_revision = find_git_info(output) |
| 152 distance = (current_revision-start) + 1 | 152 distance = (current_revision-start) + 1 |
| 153 output, _ = run_cmd(['git', 'log', '-%d' % distance], suppress_output=True) | 153 output, _ = run_cmd(['git', 'log', '-%d' % distance], suppress_output=True) |
| 154 reverts = [start] | 154 reverts = [start] |
| 155 commit_msg = '"Reverting %d"' % start | 155 commit_msg = '"Reverting %d"' % start |
| 156 if end != start: | 156 if end != start: |
| 157 reverts = range(start, end + 1) | 157 reverts = range(start, end + 1) |
| 158 reverts.reverse() | 158 reverts.reverse() |
| 159 commit_msg = '%s-%d"' % (commit_msg[:-1], end) | 159 commit_msg = '%s-%d"' % (commit_msg[:-1], end) |
| 160 for revert in reverts: | 160 for the_revert in reverts: |
| 161 git_commit_id = find_git_info(output, revert) | 161 git_commit_id = find_git_info(output, the_revert) |
| 162 if git_commit_id == None: | 162 if git_commit_id is None: |
| 163 maybe_fail('Error: Revision number not found. Is this earlier than your' | 163 maybe_fail('Error: Revision number not found. Is this earlier than your' |
| 164 ' git checkout history?') | 164 ' git checkout history?') |
| 165 _, err = run_cmd(['git', 'revert', '-n', git_commit_id]) | 165 _, err = run_cmd(['git', 'revert', '-n', git_commit_id]) |
| 166 if 'error: could not revert' in err or 'unmerged' in err: | 166 if 'error: could not revert' in err or 'unmerged' in err: |
| 167 command_sequence = '' | 167 command_sequence = '' |
| 168 for revert in reverts: | 168 for a_revert in reverts: |
| 169 git_commit_id = find_git_info(output, revert) | 169 git_commit_id = find_git_info(output, a_revert) |
| 170 command_sequence += 'git revert -n %s\n' % git_commit_id | 170 command_sequence += 'git revert -n %s\n' % git_commit_id |
| 171 maybe_fail('There are conflicts while reverting. Please resolve these ' | 171 maybe_fail('There are conflicts while reverting. Please resolve these ' |
| 172 'after manually running:\n' + command_sequence + 'and then ' | 172 'after manually running:\n' + command_sequence + 'and then ' |
| 173 'create a CL and submit to complete the revert.') | 173 'create a CL and submit to complete the revert.') |
| 174 run_cmd(['git', 'commit', '-m', commit_msg]) | 174 run_cmd(['git', 'commit', '-m', commit_msg]) |
| 175 | 175 |
| 176 def main(): | 176 def main(): |
| 177 revisions = parse_args() | 177 revisions = parse_args() |
| 178 git_user = runs_git() | 178 git_user = runs_git() |
| 179 if has_new_code(git_user): | 179 if has_new_code(git_user): |
| 180 maybe_fail('WARNING: This checkout has local modifications!! This could ' | 180 maybe_fail('WARNING: This checkout has local modifications!! This could ' |
| 181 'result in a CL that is not just a revert and/or you could lose your' | 181 'result in a CL that is not just a revert and/or you could lose your' |
| 182 ' local changes! Are you **SURE** you want to continue? ', | 182 ' local changes! Are you **SURE** you want to continue? ', |
| 183 user_input=True) | 183 user_input=True) |
| 184 if git_user: | 184 if git_user: |
| 185 run_cmd(['git', 'cl', 'rebase']) | 185 run_cmd(['git', 'cl', 'rebase']) |
| 186 run_cmd(['gclient', 'sync']) | 186 run_cmd(['gclient', 'sync']) |
| 187 revert(revisions[0], revisions[1], git_user) | 187 revert(revisions[0], revisions[1], git_user) |
| 188 print ('Now, create a CL and submit! The buildbots and your teammates thank ' | 188 print ('Now, create a CL and submit! The buildbots and your teammates thank ' |
| 189 'you!') | 189 'you!') |
| 190 | 190 |
| 191 if __name__ == '__main__': | 191 if __name__ == '__main__': |
| 192 main() | 192 main() |
| OLD | NEW |