OLD | NEW |
---|---|
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 # Copyright (c) 2009 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2009 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 import getpass | 6 import getpass |
7 import optparse | 7 import optparse |
8 import os | 8 import os |
9 import subprocess | 9 import subprocess |
10 import tempfile | 10 import tempfile |
11 import traceback | 11 import traceback |
12 import urllib | 12 import urllib |
13 import sys | 13 import sys |
14 import re | 14 import re |
15 import trychange | 15 import trychange |
16 | 16 |
17 | 17 |
18 def Backquote(cmd): | 18 def Backquote(cmd, cwd=None): |
19 """Like running `cmd` in a shell script.""" | 19 """Like running `cmd` in a shell script.""" |
20 return subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0].strip() | 20 return subprocess.Popen(cmd, cwd=cwd, stdout=subprocess.PIPE).communicate()[0] .strip() |
M-A Ruel
2009/09/22 01:26:23
80 cols
| |
21 | 21 |
22 | 22 |
23 def GetTryServerConfig(): | 23 def GetTryServerConfig(): |
24 """Returns the dictionary of try server options or None if they | 24 """Returns the dictionary of try server options or None if they |
25 cannot be found.""" | 25 cannot be found.""" |
26 script_path = 'tools/tryserver/tryserver.py' | 26 script_path = 'tools/tryserver/tryserver.py' |
27 root_dir = Backquote(['git', 'rev-parse', '--show-cdup']) | 27 root_dir = Backquote(['git', 'rev-parse', '--show-cdup']) |
28 try: | 28 try: |
29 script_file = open(os.path.join(root_dir, script_path)) | 29 script_file = open(os.path.join(root_dir, script_path)) |
30 except IOError: | 30 except IOError: |
31 return None | 31 return None |
32 locals = {} | 32 locals = {} |
33 try: | 33 try: |
34 exec(script_file, locals) | 34 exec(script_file, locals) |
35 except Exception, e: | 35 except Exception, e: |
36 return None | 36 return None |
37 return locals | 37 return locals |
38 | 38 |
39 | 39 |
40 def GetBranchName(): | 40 def GetBranchName(working_dir=None): |
41 """Return name of current git branch.""" | 41 """Return name of current git branch.""" |
42 branch = Backquote(['git', 'symbolic-ref', 'HEAD']) | 42 branch = Backquote(['git', 'symbolic-ref', 'HEAD'], working_dir) |
43 if not branch.startswith('refs/heads/'): | 43 if not branch.startswith('refs/heads/'): |
44 raise "Couldn't figure out branch name" | 44 raise "Couldn't figure out branch name" |
45 branch = branch[len('refs/heads/'):] | 45 branch = branch[len('refs/heads/'):] |
46 return branch | 46 return branch |
47 | 47 |
48 | 48 |
49 def GetPatchName(): | 49 def GetPatchName(working_dir=None): |
50 """Construct a name for this patch.""" | 50 """Construct a name for this patch.""" |
51 short_sha = Backquote(['git', 'rev-parse', '--short=4', 'HEAD']) | 51 short_sha = Backquote(['git', 'rev-parse', '--short=4', 'HEAD'], working_dir) |
52 return GetBranchName() + '-' + short_sha | 52 return GetBranchName() + '-' + short_sha |
53 | 53 |
54 | 54 |
55 def GetRietveldIssueNumber(): | 55 def GetRietveldIssueNumber(): |
56 return Backquote(['git', 'config', | 56 return Backquote(['git', 'config', |
57 'branch.%s.rietveldissue' % GetBranchName()]) | 57 'branch.%s.rietveldissue' % GetBranchName()]) |
58 | 58 |
59 | 59 |
60 def GetRietveldPatchsetNumber(): | 60 def GetRietveldPatchsetNumber(): |
61 return Backquote(['git', 'config', | 61 return Backquote(['git', 'config', |
62 'branch.%s.rietveldpatchset' % GetBranchName()]) | 62 'branch.%s.rietveldpatchset' % GetBranchName()]) |
63 | 63 |
64 def GetSubRepWorkingDir(sub_rep_path): | |
65 """Computes the path to the sub repository""" | |
66 if sub_rep_path: | |
67 root_dir = os.path.abspath(Backquote(['git', 'rev-parse', '--show-cdup'])) | |
68 return os.path.join(root_dir, sub_rep_path) | |
69 return None | |
64 | 70 |
65 def GetMungedDiff(branch, prefix='src/'): | 71 def GetMungedDiff(branch, prefix, sub_rep_path): |
66 """Get the diff we'll send to the try server. We munge paths to match svn.""" | 72 """Get the diff we'll send to the try server. We munge paths to match svn. |
73 We add the prefix that the try bot is expecting. If sub_rep_path is | |
74 provided, diff will be calculated in the sub repository.""" | |
67 # Make the following changes: | 75 # Make the following changes: |
68 # - Prepend "src/" (or some other prefix) to paths as svn is expecting | 76 # - Prepend "src/" (or some other prefix) to paths as svn is expecting |
69 # - In the case of added files, replace /dev/null with the path to the file | 77 # - In the case of added files, replace /dev/null with the path to the file |
70 # being added. | 78 # being added. |
79 | |
80 cwd = GetSubRepWorkingDir(sub_rep_path) | |
81 | |
71 output = [] | 82 output = [] |
72 if not branch: | 83 if not branch: |
73 # Try to guess the upstream branch. | 84 # Try to guess the upstream branch. |
74 branch = Backquote(['git', 'cl', 'upstream']) | 85 branch = Backquote(['git', 'cl', 'upstream'], cwd) |
75 diff = subprocess.Popen(['git', 'diff-tree', '-p', '--no-prefix', | 86 command = ['git', 'diff-tree', '-p'] |
76 branch, 'HEAD'], | 87 |
77 stdout=subprocess.PIPE).stdout.readlines() | 88 new_cwd = None |
89 if not sub_rep_path: | |
90 command.extend(['--no-prefix']) | |
91 else: | |
92 # Append / | |
93 sub_rep_path = os.path.join(sub_rep_path, '') | |
94 # Add the right prefix | |
95 command.extend(['--src-prefix=' + sub_rep_path]) | |
96 command.extend(['--dst-prefix=' + sub_rep_path]) | |
97 | |
98 command.extend([branch, 'HEAD']) | |
99 | |
100 # Run diff tree | |
101 diff = subprocess.Popen(command, | |
102 stdout=subprocess.PIPE, | |
103 cwd=cwd).stdout.readlines() | |
104 # Replace --- /dev/null with --- <new file name> | |
78 for i in range(len(diff)): | 105 for i in range(len(diff)): |
79 line = diff[i] | 106 line = diff[i] |
80 if line.startswith('--- /dev/null'): | 107 if line.startswith('--- /dev/null'): |
81 line = '--- %s' % prefix + diff[i+1][4:] | 108 line = '--- %s' % diff[i+1][4:] |
82 elif line.startswith('--- ') or line.startswith('+++ '): | 109 output.append(line) |
83 line = line[0:4] + prefix + line[4:] | 110 diff = output |
111 | |
112 # Add root prefix | |
113 output = [] | |
114 for i in range(len(diff)): | |
M-A Ruel
2009/09/22 01:26:23
for line in diff:
| |
115 line = diff[i] | |
116 if line.startswith('--- ') or line.startswith('+++ '): | |
117 line = line[0:4] + os.path.join(prefix,line[4:]) | |
84 output.append(line) | 118 output.append(line) |
85 | 119 |
86 munged_diff = ''.join(output) | 120 munged_diff = ''.join(output) |
87 if len(munged_diff.strip()) == 0: | 121 if len(munged_diff.strip()) == 0: |
88 raise Exception("Patch was empty, did you give the right remote branch?") | 122 raise Exception("Patch was empty, did you give the right remote branch?") |
89 | 123 |
90 return munged_diff | 124 return munged_diff |
91 | 125 |
126 def OneRepositoryDiff(diff_file, patch_names, branch, prefix, sub_rep_path): | |
127 """Computes a diff for one git repository at a given path against a given | |
128 branch. Writes the diff into diff_file and appends a name to the | |
129 patch_names list.""" | |
130 | |
131 diff = GetMungedDiff(branch, prefix, sub_rep_path) | |
132 | |
133 # Write the diff out | |
134 diff_file.write(diff) | |
135 | |
136 # Add patch name to list of patches | |
137 patch_name = GetPatchName(GetSubRepWorkingDir(sub_rep_path)) | |
138 patch_names.extend([patch_name]) | |
139 | |
92 | 140 |
93 def ValidEmail(email): | 141 def ValidEmail(email): |
94 return re.match(r"^[a-zA-Z0-9._%-+]+@[a-zA-Z0-9._%-]+.[a-zA-Z]{2,6}$", email) | 142 return re.match(r"^[a-zA-Z0-9._%-+]+@[a-zA-Z0-9._%-]+.[a-zA-Z]{2,6}$", email) |
95 | 143 |
96 | 144 |
97 def GetEmail(): | 145 def GetEmail(): |
98 email = Backquote(['git', 'config', 'user.email']) | 146 email = Backquote(['git', 'config', 'user.email']) |
99 runmsg = "Try: git config user.email <EMAIL>" | 147 runmsg = "Try: git config user.email <EMAIL>" |
100 assert ValidEmail(email), "Email '%s' is not valid. %s" % (email, runmsg) | 148 assert ValidEmail(email), "Email '%s' is not valid. %s" % (email, runmsg) |
101 return email | 149 return email |
(...skipping 10 matching lines...) Expand all Loading... | |
112 parser = optparse.OptionParser( | 160 parser = optparse.OptionParser( |
113 usage='git try [options] [branch]', | 161 usage='git try [options] [branch]', |
114 description='Upload the current diff of branch...HEAD to the try server.') | 162 description='Upload the current diff of branch...HEAD to the try server.') |
115 parser.add_option("-b", "--bot", | 163 parser.add_option("-b", "--bot", |
116 help="Force the use of a specific build slave (eg mac, " | 164 help="Force the use of a specific build slave (eg mac, " |
117 "win, or linux)") | 165 "win, or linux)") |
118 parser.add_option("-c", "--clobber", action="store_true", | 166 parser.add_option("-c", "--clobber", action="store_true", |
119 help="Make the try run use be a clobber build") | 167 help="Make the try run use be a clobber build") |
120 parser.add_option("-r", "--revision", | 168 parser.add_option("-r", "--revision", |
121 help="Specify the SVN base revision to use") | 169 help="Specify the SVN base revision to use") |
170 parser.add_option("--root", default="src", metavar="PATH", | |
171 help="Specify the root prefix that is appended to paths " | |
172 "in the patch") | |
173 parser.add_option("--dry_run", action="store_true", | |
174 help="Print the diff but don't send it to the try bots") | |
175 parser.add_option("--sub_rep", nargs=2, action="append", default=[], | |
176 metavar="PATH BRANCH", | |
177 help="Specify a path to a git sub-repository and a branch " | |
178 "to diff with in order to simultanously try changes " | |
179 "in multiple git repositories. Option may be " | |
180 "specified multiple times.") | |
181 parser.add_option("--webkit", metavar="BRANCH", | |
182 help="Specify webkit branch. Syntactic sugar for " | |
183 "--sub_rep third_party/WebKit/ <branch>") | |
184 | |
122 (options, args) = parser.parse_args(sys.argv) | 185 (options, args) = parser.parse_args(sys.argv) |
123 | 186 |
187 if options.webkit: | |
188 options.sub_rep = ('third_party/WebKit/', options.webkit) | |
189 | |
124 branch = None | 190 branch = None |
125 if len(args) > 1: | 191 if len(args) > 1: |
126 branch = args[1] | 192 branch = args[1] |
193 patch_names = [] | |
127 | 194 |
128 patch_name = GetPatchName() | 195 # Dump all diffs into one diff file. |
129 diff = GetMungedDiff(branch) | |
130 | |
131 # Write the diff out to a temporary file | |
132 diff_file = tempfile.NamedTemporaryFile() | 196 diff_file = tempfile.NamedTemporaryFile() |
133 diff_file.write(diff) | 197 |
198 # Calculate diff for main git repository. | |
199 OneRepositoryDiff(diff_file, patch_names, branch, options.root, None) | |
200 | |
201 # Calculate diff for each extra git repository. | |
202 if options.sub_rep: | |
M-A Ruel
2009/09/22 01:26:23
The condition is unnecessary.
| |
203 for path_and_branch in options.sub_rep: | |
204 OneRepositoryDiff(diff_file, | |
205 patch_names, | |
206 path_and_branch[1], | |
207 options.root, | |
208 path_and_branch[0]) | |
209 # Make diff file ready for reading. | |
134 diff_file.flush() | 210 diff_file.flush() |
135 | 211 |
212 # Concatenate patch names | |
213 # Prepare args for TryChange | |
136 email = GetEmail() | 214 email = GetEmail() |
137 user = email.partition('@')[0] | 215 user = email.partition('@')[0] |
138 args = [ | 216 args = [ |
139 '-u', user, | 217 '-u', user, |
140 '-e', email, | 218 '-e', email, |
141 '-n', patch_name, | 219 '-n', '-'.join(patch_names), |
142 '--diff', diff_file.name, | 220 '--diff', diff_file.name, |
143 ] | 221 ] |
144 | 222 |
145 # Send to try server via HTTP if we can parse the config, otherwise | 223 # Send to try server via HTTP if we can parse the config, otherwise |
146 # upload via SVN. | 224 # upload via SVN. |
147 config = GetTryServerConfig() | 225 config = GetTryServerConfig() |
148 if config is not None: | 226 if config is not None: |
149 sendmsg = "Sending %s using HTTP..." % patch_name | 227 sendmsg = "Sending %s using HTTP..." % '-'.join(patch_names) |
150 args.extend(['--use_http']) | 228 args.extend(['--use_http']) |
151 if config['try_server_http_host'] is not None: | 229 if config['try_server_http_host'] is not None: |
152 args.extend(['--host', config['try_server_http_host']]) | 230 args.extend(['--host', config['try_server_http_host']]) |
153 if config['try_server_http_port'] is not None: | 231 if config['try_server_http_port'] is not None: |
154 args.extend(['--port', config['try_server_http_port']]) | 232 args.extend(['--port', config['try_server_http_port']]) |
155 | 233 |
156 else: | 234 else: |
157 print "Could not get server config -- if you're within Google, " | 235 print "Could not get server config -- if you're within Google, " |
158 print "do you have have src-internal checked out?" | 236 print "do you have have src-internal checked out?" |
159 sendmsg = "Sending %s using SVN..." % patch_name | 237 sendmsg = "Sending %s using SVN..." % patch_name |
160 args.extend([ | 238 args.extend([ |
161 '--use_svn', '--svn_repo', | 239 '--use_svn', '--svn_repo', |
162 'svn://svn.chromium.org/chrome-try/try', | 240 'svn://svn.chromium.org/chrome-try/try', |
163 ]) | 241 ]) |
164 | 242 |
165 if options.bot: | 243 if options.bot: |
166 args.extend(['--bot', options.bot]) | 244 args.extend(['--bot', options.bot]) |
167 if options.clobber: | 245 if options.clobber: |
168 args.append('--clobber') | 246 args.append('--clobber') |
169 if options.revision: | 247 if options.revision: |
170 args.extend(['-r', options.revision]) | 248 args.extend(['-r', options.revision]) |
171 if GetRietveldPatchsetNumber(): | 249 if GetRietveldPatchsetNumber(): |
172 args.extend([ | 250 args.extend([ |
173 '--issue', GetRietveldIssueNumber(), | 251 '--issue', GetRietveldIssueNumber(), |
174 '--patchset', GetRietveldPatchsetNumber(), | 252 '--patchset', GetRietveldPatchsetNumber(), |
175 ]) | 253 ]) |
176 | 254 |
255 if options.dry_run: | |
256 print open(diff_file.name, 'r').read() | |
257 exit(0) | |
258 | |
177 print sendmsg | 259 print sendmsg |
178 TryChange(args=args) | 260 TryChange(args=args) |
OLD | NEW |