Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1)

Side by Side Diff: tools/revert.py

Issue 10388020: Add a revert script. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/python
2
3 # Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
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.
6
7 import optparse
8 import os
9 import platform
10 import subprocess
11 import sys
12
13 """A script used to revert one or a sequence of consecutive CLs, for svn and
14 git-svn users.
15 """
16
17 def parse_args():
18 parser = optparse.OptionParser()
19 parser.add_option('--revisions', '-r', dest='rev_range', action='store',
20 default=None, help='The revision number(s) of the commits '
21 'you wish to undo. An individual number, or a range (8-10, '
22 '8..10, or 8:10).')
23 args, _ = parser.parse_args()
24 revision_range = args.rev_range
25 if revision_range == None:
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 \
28 revision_range.find('..') > -1:
29 # We have a range of commits to revert.
30 split = revision_range.split('-')
31 if len(split) == 1:
32 split = revision_range.split(':')
33 if len(split) == 1:
34 split = revision_range.split('..')
35 start = int(split[0])
36 end = int(split[1])
37 if start > end:
38 temp = start
39 start = end
40 end = temp
41 if start != end:
42 maybe_fail('Warning: Are you sure you want to revert a range of '
43 'revisions? If you just want to revert one CL, only specify '
44 'one revision number.', user_input=True)
45 else:
46 start = end = int(revision_range)
47 return start, end
48
49 def maybe_fail(msg, user_input=False):
50 """Determine if we have encountered a condition upon which our script cannot
51 continue, and abort if so.
52 Args:
53 - msg: The error or user prompt message to print.
54 - user_input: True if we require user confirmation to continue. We assume
55 that the user must enter y to proceed.
56 """
57 if user_input:
58 force = raw_input(msg + ' (y/N) ')
59 if force != 'y':
60 sys.exit(0)
61 else:
62 print msg
63 sys.exit(1)
64
65 def has_new_code(is_git):
66 """Tests if there are any newer versions of files on the server.
67 Args:
68 - is_git: True if we are working in a git repository.
69 """
70 os.chdir(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
71 if not is_git:
72 results, _ = run_cmd(['svn', 'st'])
73 else:
74 results, _ = run_cmd(['git', 'status'])
75 for line in results.split('\n'):
76 if not is_git and (not line.strip().startswith('?') and line != ''):
77 return True
78 elif is_git and ('Changes to be committed' in line or
79 'Changes not staged for commit:' in line):
80 return True
81 if is_git:
82 p = subprocess.Popen(['git', 'log', '-1'], stdout=subprocess.PIPE)
83 output, _ = p.communicate()
84 if find_git_info(output) == None:
85 return True
86 return False
87
88 def run_cmd(cmd_list, suppress_output=False, std_in=''):
89 """Run the specified command and print out any output to stdout."""
90 print ' '.join(cmd_list)
91 p = subprocess.Popen(cmd_list, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
92 stdin=subprocess.PIPE,
93 shell=(platform.system()=='Windows'))
94 output, stderr = p.communicate(std_in)
95 if output and not suppress_output:
96 print output
97 if stderr and not suppress_output:
98 print stderr
99 return (output, stderr)
100
101 def runs_git():
102 """Returns True if we're standing in an svn-git repository."""
103 p = subprocess.Popen(['svn', 'info'], stdout=subprocess.PIPE,
104 stderr=subprocess.PIPE)
105 output, err = p.communicate()
106 if err != None and 'is not a working copy' in err:
107 p = subprocess.Popen(['git', 'status'], stdout=subprocess.PIPE)
108 output, _ = p.communicate()
109 if 'fatal: Not a git repository' in output:
110 maybe_fail('Error: not running git or svn.')
111 else:
112 return True
113 return False
114
115 def find_git_info(git_log, rev_num=None):
116 """Determine the latest svn revision number if rev_num = None, or find the
117 git commit_id that corresponds to a particular svn revision number.
118 """
119 for line in git_log.split('\n'):
120 tokens = line.split()
121 if len(tokens) == 2 and tokens[0] == 'commit':
122 current_commit_id = tokens[1]
123 elif len(tokens) > 0 and tokens[0] == 'git-svn-id:':
124 revision_number = int(tokens[1].split('@')[1])
125 if revision_number == rev_num:
126 return current_commit_id
127 if rev_num == None:
128 return revision_number
129
130 def revert(start, end, is_git):
131 """Revert the sequence of CLs.
132 Args:
133 - start: The first CL to revert.
134 - end: The last CL to revert.
135 - is_git: True if we are in a git-svn checkout.
136 """
137 if not is_git:
138 _, err = run_cmd(['svn', 'merge', '-r', '%d:%d' % (end, start-1), '.'],
139 std_in='p')
140 if 'Conflict discovered' in err:
141 maybe_fail('Please fix the above conflicts before submitting. Then create'
142 ' a CL and submit your changes to complete the revert.')
143
144 else:
145 # If we're running git, we have to use the log feature to find the commit
146 # id(s) that correspond to the particular revision number(s).
147 output, _ = run_cmd(['git', 'log', '-1'], suppress_output=True)
148 current_revision = find_git_info(output)
149 distance = (current_revision-start) + 1
150 output, _ = run_cmd(['git', 'log', '-%d' % distance], suppress_output=True)
151 reverts = [start]
152 commit_msg = '"Reverting %d"' % start
153 if end != start:
154 reverts = range(start, end + 1)
155 reverts.reverse()
156 commit_msg = '%s-%d"' % (commit_msg[:-1], end)
157 for revert in reverts:
158 git_commit_id = find_git_info(output, revert)
159 if git_commit_id == None:
160 maybe_fail('Error: Revision number not found. Is this earlier than your'
161 ' git checkout history?')
162 _, err = run_cmd(['git', 'revert', '-n', git_commit_id])
163 if 'error: could not revert' in err or 'unmerged' in err:
164 command_sequence = ''
165 for revert in reverts:
166 git_commit_id = find_git_info(output, revert)
167 command_sequence += 'git revert -n %s\n' % git_commit_id
168 maybe_fail('There are conflicts while reverting. Please resolve these '
169 'after manually running:\n' + command_sequence + 'and then '
170 'create a CL and submit to complete the revert.')
171 run_cmd(['git', 'commit', '-m', commit_msg])
172
173 def main():
174 revisions = parse_args()
175 git_user = runs_git()
176 if has_new_code(git_user):
177 if git_user:
178 maybe_fail('Your tree has local modifications! Move to a clean tree and '
179 'try running this script again.')
180 else:
181 maybe_fail('WARNING: This checkout has local modifications!! This could '
182 'result in a CL that is not just a revert and/or you could lose your'
183 ' local changes! Are you **SURE** you want to continue? ',
184 user_input=True)
185 if git_user:
186 run_cmd(['git', 'cl', 'rebase'])
187 run_cmd(['gclient', 'sync'])
188 revert(revisions[0], revisions[1], git_user)
189 print ('Now, create a CL and submit! The buildbots and your teammates thank '
190 'you!')
191
192 if __name__ == '__main__':
193 main()
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698