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 |