Chromium Code Reviews| 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 |