| OLD | NEW |
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 # Copyright (c) 2010 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2010 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 optparse | 6 import optparse |
| 7 import os | 7 import os |
| 8 import re | 8 import re |
| 9 import subprocess | 9 import subprocess |
| 10 import sys | 10 import sys |
| (...skipping 11 matching lines...) Expand all Loading... |
| 22 | 22 |
| 23 [Merge from trunk to branch] | 23 [Merge from trunk to branch] |
| 24 --merge <revision> --branch <branch_num> | 24 --merge <revision> --branch <branch_num> |
| 25 Example: %(app)s --merge 12345 --branch 187 | 25 Example: %(app)s --merge 12345 --branch 187 |
| 26 | 26 |
| 27 [Merge from trunk to local copy] | 27 [Merge from trunk to local copy] |
| 28 --merge <revision> --local | 28 --merge <revision> --local |
| 29 Example: %(app)s --merge 12345 --local | 29 Example: %(app)s --merge 12345 --local |
| 30 | 30 |
| 31 [Merge from branch to branch] | 31 [Merge from branch to branch] |
| 32 --merge <revision> --sbranch <branch_num> --branch <branch_num> | 32 --merge <revision> --sbranch <branch_num> --branch <branch_num> |
| 33 Example: %(app)s --merge 12345 --sbranch 248 --branch 249 | 33 Example: %(app)s --merge 12345 --sbranch 248 --branch 249 |
| 34 | 34 |
| 35 [Revert from trunk] | 35 [Revert from trunk] |
| 36 --revert <revision> | 36 --revert <revision> |
| 37 Example: %(app)s --revert 12345 | 37 Example: %(app)s --revert 12345 |
| 38 | 38 |
| 39 [Revert from branch] | 39 [Revert from branch] |
| 40 --revert <revision> --branch <branch_num> | 40 --revert <revision> --branch <branch_num> |
| 41 Example: %(app)s --revert 12345 --branch 187 | 41 Example: %(app)s --revert 12345 --branch 187 |
| 42 """ | 42 """ |
| (...skipping 12 matching lines...) Expand all Loading... |
| 55 os.system('rmdir /S /Q ' + root.replace('/','\\')) | 55 os.system('rmdir /S /Q ' + root.replace('/','\\')) |
| 56 else: | 56 else: |
| 57 for name in os.listdir(root): | 57 for name in os.listdir(root): |
| 58 path = os.path.join(root, name) | 58 path = os.path.join(root, name) |
| 59 if os.path.isdir(path): | 59 if os.path.isdir(path): |
| 60 deltree(path) | 60 deltree(path) |
| 61 else: | 61 else: |
| 62 os.unlink(path) | 62 os.unlink(path) |
| 63 os.rmdir(root) | 63 os.rmdir(root) |
| 64 | 64 |
| 65 def clobberDir(dir): | 65 def clobberDir(dirname): |
| 66 """Removes a given directory""" | 66 """Removes a given directory""" |
| 67 | 67 |
| 68 if (os.path.exists(dir)): | 68 if (os.path.exists(dirname)): |
| 69 print dir + " directory found, deleting" | 69 print dir + " directory found, deleting" |
| 70 # The following line was removed due to access controls in Windows | 70 # The following line was removed due to access controls in Windows |
| 71 # which make os.unlink(path) calls impossible. | 71 # which make os.unlink(path) calls impossible. |
| 72 #TODO(laforge) : Is this correct? | 72 #TODO(laforge) : Is this correct? |
| 73 deltree(dir) | 73 deltree(dirname) |
| 74 | 74 |
| 75 def runGcl(subcommand): | 75 def runGcl(subcommand): |
| 76 gcl_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "gcl") | 76 gcl_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "gcl") |
| 77 if not os.path.exists(gcl_path): | 77 if not os.path.exists(gcl_path): |
| 78 print "WARNING: gcl not found beside drover.py. Using system gcl instead..." | 78 print "WARNING: gcl not found beside drover.py. Using system gcl instead..." |
| 79 gcl_path = 'gcl' | 79 gcl_path = 'gcl' |
| 80 | 80 |
| 81 command = "%s %s" % (gcl_path, subcommand) | 81 command = "%s %s" % (gcl_path, subcommand) |
| 82 return os.system(command) | 82 return os.system(command) |
| 83 | 83 |
| 84 def gclUpload(revision, author): | 84 def gclUpload(revision, author): |
| 85 command = ("upload " + str(revision) + | 85 command = ("upload " + str(revision) + |
| 86 " --send_mail --no_try --no_presubmit --reviewers=" + author) | 86 " --send_mail --no_try --no_presubmit --reviewers=" + author) |
| 87 return runGcl(command) | 87 return runGcl(command) |
| 88 | 88 |
| 89 def getSVNInfo(url, revision): | 89 def getSVNInfo(url, revision): |
| 90 command = 'svn info ' + url + "@"+str(revision) | 90 command = 'svn info ' + url + "@"+str(revision) |
| 91 svn_info = subprocess.Popen(command, | 91 svn_info = subprocess.Popen(command, |
| 92 shell=True, | 92 shell=True, |
| 93 stdout=subprocess.PIPE, | 93 stdout=subprocess.PIPE, |
| 94 stderr=subprocess.PIPE).stdout.readlines() | 94 stderr=subprocess.PIPE).stdout.readlines() |
| 95 info = {} | 95 info = {} |
| 96 for line in svn_info: | 96 for line in svn_info: |
| 97 match = re.search(r"(.*?):(.*)", line) | 97 match = re.search(r"(.*?):(.*)", line) |
| 98 if match: | 98 if match: |
| 99 » info[match.group(1).strip()]=match.group(2).strip() | 99 info[match.group(1).strip()]=match.group(2).strip() |
| 100 | 100 |
| 101 return info | 101 return info |
| 102 | 102 |
| 103 def isSVNDirty(): | 103 def isSVNDirty(): |
| 104 command = 'svn status' | 104 command = 'svn status' |
| 105 svn_status = subprocess.Popen(command, | 105 svn_status = subprocess.Popen(command, |
| 106 shell=True, | 106 shell=True, |
| 107 stdout=subprocess.PIPE, | 107 stdout=subprocess.PIPE, |
| 108 stderr=subprocess.PIPE).stdout.readlines() | 108 stderr=subprocess.PIPE).stdout.readlines() |
| 109 for line in svn_status: | 109 for line in svn_status: |
| (...skipping 20 matching lines...) Expand all Loading... |
| 130 info = getSVNInfo(url, revision) | 130 info = getSVNInfo(url, revision) |
| 131 if (info.has_key("Node Kind")): | 131 if (info.has_key("Node Kind")): |
| 132 if (info["Node Kind"] == "directory"): | 132 if (info["Node Kind"] == "directory"): |
| 133 return True | 133 return True |
| 134 return False | 134 return False |
| 135 | 135 |
| 136 def inCheckoutRoot(path): | 136 def inCheckoutRoot(path): |
| 137 info = getSVNInfo(path, "HEAD") | 137 info = getSVNInfo(path, "HEAD") |
| 138 if (not info.has_key("Repository Root")): | 138 if (not info.has_key("Repository Root")): |
| 139 return False | 139 return False |
| 140 repo_root = info["Repository Root"]; | 140 repo_root = info["Repository Root"] |
| 141 info = getSVNInfo(os.path.dirname(os.path.abspath(path)), "HEAD") | 141 info = getSVNInfo(os.path.dirname(os.path.abspath(path)), "HEAD") |
| 142 if (info.get("Repository Root", None) != repo_root): | 142 if (info.get("Repository Root", None) != repo_root): |
| 143 return True | 143 return True |
| 144 return False | 144 return False |
| 145 | 145 |
| 146 def getRevisionLog(url, revision): | 146 def getRevisionLog(url, revision): |
| 147 """Takes an svn url and gets the associated revision.""" | 147 """Takes an svn url and gets the associated revision.""" |
| 148 command = 'svn log ' + url + " -r"+str(revision) | 148 command = 'svn log ' + url + " -r"+str(revision) |
| 149 svn_log = subprocess.Popen(command, | 149 svn_log = subprocess.Popen(command, |
| 150 shell=True, | 150 shell=True, |
| 151 stdout=subprocess.PIPE, | 151 stdout=subprocess.PIPE, |
| 152 stderr=subprocess.PIPE).stdout.readlines() | 152 stderr=subprocess.PIPE).stdout.readlines() |
| 153 # Don't include the header lines and the trailing "---..." line and eliminate | 153 # Don't include the header lines and the trailing "---..." line and eliminate |
| 154 # any '\r's. | 154 # any '\r's. |
| 155 return ''.join([l.replace('\r','') for l in svn_log[3:-1]]) | 155 return ''.join([l.replace('\r','') for l in svn_log[3:-1]]) |
| 156 | 156 |
| 157 def getSVNVersionInfo(): | 157 def getSVNVersionInfo(): |
| 158 """Extract version information from SVN""" | 158 """Extract version information from SVN""" |
| 159 command = 'svn --version' | 159 command = 'svn --version' |
| 160 svn_info = subprocess.Popen(command, | 160 svn_info = subprocess.Popen(command, |
| 161 shell=True, | 161 shell=True, |
| 162 stdout=subprocess.PIPE, | 162 stdout=subprocess.PIPE, |
| 163 stderr=subprocess.PIPE).stdout.readlines() | 163 stderr=subprocess.PIPE).stdout.readlines() |
| 164 info = {} | 164 info = {} |
| 165 for line in svn_info: | 165 for line in svn_info: |
| 166 match = re.search(r"svn, version ((\d+)\.(\d+)\.(\d+)) \(r(\d+)\)", line) | 166 match = re.search(r"svn, version ((\d+)\.(\d+)\.(\d+)) \(r(\d+)\)", line) |
| 167 if match: | 167 if match: |
| 168 » info['version']=match.group(1) | 168 info['version'] = match.group(1) |
| 169 » info['major']=int(match.group(2)) | 169 info['major'] = int(match.group(2)) |
| 170 » info['minor']=int(match.group(3)) | 170 info['minor'] = int(match.group(3)) |
| 171 » info['patch']=int(match.group(4)) | 171 info['patch'] = int(match.group(4)) |
| 172 » info['revision']=match.group(5) | 172 info['revision'] = match.group(5) |
| 173 » return info | 173 return info |
| 174 | 174 |
| 175 return None | 175 return None |
| 176 | 176 |
| 177 def isMinimumSVNVersion(major, minor, patch=0): | 177 def isMinimumSVNVersion(major, minor, patch=0): |
| 178 """Test for minimum SVN version""" | 178 """Test for minimum SVN version""" |
| 179 return _isMinimumSVNVersion(getSVNVersionInfo(), major, minor, patch) | 179 return _isMinimumSVNVersion(getSVNVersionInfo(), major, minor, patch) |
| 180 | 180 |
| 181 def _isMinimumSVNVersion(version, major, minor, patch=0): | 181 def _isMinimumSVNVersion(version, major, minor, patch=0): |
| 182 """Test for minimum SVN version, internal method""" | 182 """Test for minimum SVN version, internal method""" |
| 183 if not version: | 183 if not version: |
| (...skipping 26 matching lines...) Expand all Loading... |
| 210 match = re.search(r"svn://.*/(.*)", branch_url) | 210 match = re.search(r"svn://.*/(.*)", branch_url) |
| 211 | 211 |
| 212 if match: | 212 if match: |
| 213 os.chdir(match.group(1)) | 213 os.chdir(match.group(1)) |
| 214 | 214 |
| 215 # This line is extremely important due to the way svn behaves in the | 215 # This line is extremely important due to the way svn behaves in the |
| 216 # set-depths action. If parents aren't handled before children, the child | 216 # set-depths action. If parents aren't handled before children, the child |
| 217 # directories get clobbered and the merge step fails. | 217 # directories get clobbered and the merge step fails. |
| 218 paths.sort() | 218 paths.sort() |
| 219 | 219 |
| 220 # Checkout the directories that already exist | 220 # Checkout the directories that already exist |
| 221 for path in paths: | 221 for path in paths: |
| 222 if (export_map.has_key(path) and not revert): | 222 if (export_map.has_key(path) and not revert): |
| 223 print "Exclude new directory " + path | 223 print "Exclude new directory " + path |
| 224 continue | 224 continue |
| 225 subpaths = path.split('/') | 225 subpaths = path.split('/') |
| 226 subpaths.pop(0) | 226 subpaths.pop(0) |
| 227 base = '' | 227 base = '' |
| 228 for subpath in subpaths: | 228 for subpath in subpaths: |
| 229 base += '/' + subpath | 229 base += '/' + subpath |
| 230 # This logic ensures that you don't empty out any directories | 230 # This logic ensures that you don't empty out any directories |
| 231 if not os.path.exists("." + base): | 231 if not os.path.exists("." + base): |
| 232 command = ('svn update --depth empty ' + "." + base) | 232 command = ('svn update --depth empty ' + "." + base) |
| 233 print command | 233 print command |
| 234 os.system(command) | 234 os.system(command) |
| 235 | 235 |
| 236 if (revert): | 236 if (revert): |
| 237 files = getAllFilesInRevision(files_info) | 237 files = getAllFilesInRevision(files_info) |
| 238 else: | 238 else: |
| 239 files = getExistingFilesInRevision(files_info) | 239 files = getExistingFilesInRevision(files_info) |
| 240 | 240 |
| 241 for file in files: | 241 for f in files: |
| 242 # Prevent the tool from clobbering the src directory | 242 # Prevent the tool from clobbering the src directory |
| 243 if (file == ""): | 243 if (f == ""): |
| 244 continue | 244 continue |
| 245 command = ('svn up ".' + file + '"') | 245 command = ('svn up ".' + f + '"') |
| 246 print command | 246 print command |
| 247 os.system(command) | 247 os.system(command) |
| 248 | 248 |
| 249 def mergeRevision(url, revision): | 249 def mergeRevision(url, revision): |
| 250 paths = getBestMergePaths(url, revision) | 250 paths = getBestMergePaths(url, revision) |
| 251 export_map = getBestExportPathsMap(url, revision) | 251 export_map = getBestExportPathsMap(url, revision) |
| 252 | 252 |
| 253 for path in paths: | 253 for path in paths: |
| 254 if export_map.has_key(path): | 254 if export_map.has_key(path): |
| 255 continue | 255 continue |
| 256 command = ('svn merge -N -r ' + str(revision-1) + ":" + str(revision) + " ") | 256 command = ('svn merge -N -r ' + str(revision-1) + ":" + str(revision) + " ") |
| 257 command += " --ignore-ancestry " | 257 command += " --ignore-ancestry " |
| 258 command += " -x --ignore-eol-style " | 258 command += " -x --ignore-eol-style " |
| 259 command += url + path + "@" + str(revision) + " ." + path | 259 command += url + path + "@" + str(revision) + " ." + path |
| 260 | 260 |
| 261 print command | 261 print command |
| 262 os.system(command) | 262 os.system(command) |
| 263 | 263 |
| 264 def exportRevision(url, revision): | 264 def exportRevision(url, revision): |
| 265 paths = getBestExportPathsMap(url, revision).keys() | 265 paths = getBestExportPathsMap(url, revision).keys() |
| 266 paths.sort() | 266 paths.sort() |
| 267 | 267 |
| 268 for path in paths: | 268 for path in paths: |
| 269 command = ('svn export -N ' + url + path + "@" + str(revision) + " ." + | 269 command = ('svn export -N ' + url + path + "@" + str(revision) + " ." + |
| 270 path) | 270 path) |
| 271 print command | 271 print command |
| 272 os.system(command) | 272 os.system(command) |
| 273 | 273 |
| 274 command = 'svn add .' + path | 274 command = 'svn add .' + path |
| 275 print command | 275 print command |
| 276 os.system(command) | 276 os.system(command) |
| 277 | 277 |
| 278 def deleteRevision(url, revision): | 278 def deleteRevision(url, revision): |
| 279 paths = getBestDeletePathsMap(url, revision).keys() | 279 paths = getBestDeletePathsMap(url, revision).keys() |
| 280 paths.sort() | 280 paths.sort() |
| 281 paths.reverse() | 281 paths.reverse() |
| 282 | 282 |
| 283 for path in paths: | 283 for path in paths: |
| 284 command = "svn delete ." + path | 284 command = "svn delete ." + path |
| 285 print command | 285 print command |
| 286 os.system(command) | 286 os.system(command) |
| 287 | 287 |
| 288 def revertExportRevision(url, revision): | 288 def revertExportRevision(url, revision): |
| 289 paths = getBestExportPathsMap(url, revision).keys() | 289 paths = getBestExportPathsMap(url, revision).keys() |
| 290 paths.sort() | 290 paths.sort() |
| 291 paths.reverse() | 291 paths.reverse() |
| 292 | 292 |
| 293 for path in paths: | 293 for path in paths: |
| 294 command = "svn delete ." + path | 294 command = "svn delete ." + path |
| 295 print command | 295 print command |
| 296 os.system(command) | 296 os.system(command) |
| 297 | 297 |
| 298 def revertRevision(url, revision): | 298 def revertRevision(url, revision): |
| 299 paths = getBestMergePaths(url, revision) | 299 paths = getBestMergePaths(url, revision) |
| 300 for path in paths: | 300 for path in paths: |
| 301 command = ('svn merge -N -r ' + str(revision) + ":" + str(revision-1) + | 301 command = ('svn merge -N -r ' + str(revision) + ":" + str(revision-1) + |
| 302 " " + url + path + " ." + path) | 302 " " + url + path + " ." + path) |
| 303 print command | 303 print command |
| 304 os.system(command) | 304 os.system(command) |
| 305 | 305 |
| 306 def getFileInfo(url, revision): | 306 def getFileInfo(url, revision): |
| 307 global files_info_, file_pattern_ | 307 global files_info_, file_pattern_ |
| 308 | 308 |
| 309 if (files_info_ != None): | 309 if (files_info_ != None): |
| 310 return files_info_ | 310 return files_info_ |
| 311 | 311 |
| (...skipping 17 matching lines...) Expand all Loading... |
| 329 | 329 |
| 330 files_info_ = info | 330 files_info_ = info |
| 331 return info | 331 return info |
| 332 | 332 |
| 333 def getBestMergePaths(url, revision): | 333 def getBestMergePaths(url, revision): |
| 334 """Takes an svn url and gets the associated revision.""" | 334 """Takes an svn url and gets the associated revision.""" |
| 335 return getBestMergePaths2(getFileInfo(url, revision), revision) | 335 return getBestMergePaths2(getFileInfo(url, revision), revision) |
| 336 | 336 |
| 337 def getBestMergePaths2(files_info, revision): | 337 def getBestMergePaths2(files_info, revision): |
| 338 """Takes an svn url and gets the associated revision.""" | 338 """Takes an svn url and gets the associated revision.""" |
| 339 | 339 return list(set([f[2] for f in files_info])) |
| 340 map = dict() | |
| 341 for file_info in files_info: | |
| 342 map[file_info[2]] = file_info[2] | |
| 343 | |
| 344 return map.keys() | |
| 345 | 340 |
| 346 def getBestExportPathsMap(url, revision): | 341 def getBestExportPathsMap(url, revision): |
| 347 return getBestExportPathsMap2(getFileInfo(url, revision), revision) | 342 return getBestExportPathsMap2(getFileInfo(url, revision), revision) |
| 348 | 343 |
| 349 def getBestExportPathsMap2(files_info, revision): | 344 def getBestExportPathsMap2(files_info, revision): |
| 350 """Takes an svn url and gets the associated revision.""" | 345 """Takes an svn url and gets the associated revision.""" |
| 351 global export_map_ | 346 global export_map_ |
| 352 | 347 |
| 353 if export_map_: | 348 if export_map_: |
| 354 return export_map_ | 349 return export_map_ |
| 355 | 350 |
| 356 map = dict() | 351 result = {} |
| 357 for file_info in files_info: | 352 for file_info in files_info: |
| 358 if (file_info[0] == "A"): | 353 if (file_info[0] == "A"): |
| 359 if(isSVNDirectory("svn://svn.chromium.org/chrome/" + file_info[1], | 354 if(isSVNDirectory("svn://svn.chromium.org/chrome/" + file_info[1], |
| 360 revision)): | 355 revision)): |
| 361 map[file_info[2] + "/" + file_info[3]] = "" | 356 result[file_info[2] + "/" + file_info[3]] = "" |
| 362 | 357 |
| 363 export_map_ = map | 358 export_map_ = result |
| 364 return map | 359 return result |
| 365 | 360 |
| 366 def getBestDeletePathsMap(url, revision): | 361 def getBestDeletePathsMap(url, revision): |
| 367 return getBestDeletePathsMap2(getFileInfo(url, revision), revision) | 362 return getBestDeletePathsMap2(getFileInfo(url, revision), revision) |
| 368 | 363 |
| 369 def getBestDeletePathsMap2(files_info, revision): | 364 def getBestDeletePathsMap2(files_info, revision): |
| 370 """Takes an svn url and gets the associated revision.""" | 365 """Takes an svn url and gets the associated revision.""" |
| 371 global delete_map_ | 366 global delete_map_ |
| 372 | 367 |
| 373 if delete_map_: | 368 if delete_map_: |
| 374 return delete_map_ | 369 return delete_map_ |
| 375 | 370 |
| 376 map = dict() | 371 result = {} |
| 377 for file_info in files_info: | 372 for file_info in files_info: |
| 378 if (file_info[0] == "D"): | 373 if (file_info[0] == "D"): |
| 379 if(isSVNDirectory("svn://svn.chromium.org/chrome/" + file_info[1], | 374 if(isSVNDirectory("svn://svn.chromium.org/chrome/" + file_info[1], |
| 380 revision)): | 375 revision)): |
| 381 map[file_info[2] + "/" + file_info[3]] = "" | 376 result[file_info[2] + "/" + file_info[3]] = "" |
| 382 | 377 |
| 383 delete_map_ = map | 378 delete_map_ = result |
| 384 return map | 379 return result |
| 380 |
| 385 | 381 |
| 386 def getExistingFilesInRevision(files_info): | 382 def getExistingFilesInRevision(files_info): |
| 387 """Checks for existing files in the revision. | 383 """Checks for existing files in the revision. |
| 388 | 384 |
| 389 Anything that's A will require special treatment (either a merge or an | 385 Anything that's A will require special treatment (either a merge or an |
| 390 export + add) | 386 export + add) |
| 391 """ | 387 """ |
| 392 map = [] | 388 return ['%s/%s' % (f[2], f[3]) for f in files_info if f[0] != 'A'] |
| 393 for file_info in files_info: | |
| 394 if file_info[0] != "A": | |
| 395 map.append(file_info[2] + "/" + file_info[3]) | |
| 396 | 389 |
| 397 return map | |
| 398 | 390 |
| 399 def getAllFilesInRevision(files_info): | 391 def getAllFilesInRevision(files_info): |
| 400 """Checks for existing files in the revision. | 392 """Checks for existing files in the revision. |
| 401 | 393 |
| 402 Anything that's A will require special treatment (either a merge or an | 394 Anything that's A will require special treatment (either a merge or an |
| 403 export + add) | 395 export + add) |
| 404 """ | 396 """ |
| 405 map = [] | 397 return ['%s/%s' % (f[2], f[3]) for f in files_info] |
| 406 for file_info in files_info: | |
| 407 map.append(file_info[2] + "/" + file_info[3]) | |
| 408 return map | |
| 409 | 398 |
| 410 def prompt(question): | 399 def prompt(question): |
| 411 answer = None | 400 while True: |
| 412 | |
| 413 while not answer: | |
| 414 print question + " [y|n]:", | 401 print question + " [y|n]:", |
| 415 answer = sys.stdin.readline() | 402 answer = sys.stdin.readline() |
| 416 if answer.lower().startswith('n'): | 403 if answer.lower().startswith('n'): |
| 417 return False | 404 return False |
| 418 elif answer.lower().startswith('y'): | 405 elif answer.lower().startswith('y'): |
| 419 return True | 406 return True |
| 420 else: | 407 |
| 421 answer = None | |
| 422 | 408 |
| 423 def text_prompt(question, default): | 409 def text_prompt(question, default): |
| 424 print question + " [" + default + "]:" | 410 print question + " [" + default + "]:" |
| 425 answer = sys.stdin.readline() | 411 answer = sys.stdin.readline() |
| 426 if answer.strip() == "": | 412 if answer.strip() == "": |
| 427 return default | 413 return default |
| 428 return answer | 414 return answer |
| 429 | 415 |
| 430 def main(options, args): | 416 |
| 417 def drover(options, args): |
| 431 revision = options.revert or options.merge | 418 revision = options.revert or options.merge |
| 432 | 419 |
| 433 # Initialize some variables used below. They can be overwritten by | 420 # Initialize some variables used below. They can be overwritten by |
| 434 # the drover.properties file. | 421 # the drover.properties file. |
| 435 BASE_URL = "svn://svn.chromium.org/chrome" | 422 BASE_URL = "svn://svn.chromium.org/chrome" |
| 436 TRUNK_URL = BASE_URL + "/trunk/src" | 423 TRUNK_URL = BASE_URL + "/trunk/src" |
| 437 BRANCH_URL = BASE_URL + "/branches/$branch/src" | 424 BRANCH_URL = BASE_URL + "/branches/$branch/src" |
| 438 SKIP_CHECK_WORKING = True | 425 SKIP_CHECK_WORKING = True |
| 439 PROMPT_FOR_AUTHOR = False | 426 PROMPT_FOR_AUTHOR = False |
| 440 | 427 |
| 441 DEFAULT_WORKING = "drover_" + str(revision) | 428 DEFAULT_WORKING = "drover_" + str(revision) |
| 442 if options.branch: | 429 if options.branch: |
| 443 DEFAULT_WORKING += ("_" + options.branch) | 430 DEFAULT_WORKING += ("_" + options.branch) |
| 444 | 431 |
| 445 if not isMinimumSVNVersion(1,5): | 432 if not isMinimumSVNVersion(1, 5): |
| 446 print "You need to use at least SVN version 1.5.x" | 433 print "You need to use at least SVN version 1.5.x" |
| 447 sys.exit(1) | 434 return 1 |
| 448 | 435 |
| 449 # Override the default properties if there is a drover.properties file. | 436 # Override the default properties if there is a drover.properties file. |
| 450 global file_pattern_ | 437 global file_pattern_ |
| 451 if os.path.exists("drover.properties"): | 438 if os.path.exists("drover.properties"): |
| 452 file = open("drover.properties") | 439 f = open("drover.properties") |
| 453 exec(file) | 440 exec(f) |
| 454 file.close() | 441 f.close() |
| 455 if FILE_PATTERN: | 442 if FILE_PATTERN: |
| 456 file_pattern_ = FILE_PATTERN | 443 file_pattern_ = FILE_PATTERN |
| 457 | 444 |
| 458 if options.revert and options.branch: | 445 if options.revert and options.branch: |
| 459 url = BRANCH_URL.replace("$branch", options.branch) | 446 url = BRANCH_URL.replace("$branch", options.branch) |
| 460 elif options.merge and options.sbranch: | 447 elif options.merge and options.sbranch: |
| 461 url = BRANCH_URL.replace("$branch", options.sbranch) | 448 url = BRANCH_URL.replace("$branch", options.sbranch) |
| 462 else: | 449 else: |
| 463 url = TRUNK_URL | 450 url = TRUNK_URL |
| 464 | 451 |
| 465 working = options.workdir or DEFAULT_WORKING | 452 working = options.workdir or DEFAULT_WORKING |
| 466 | 453 |
| 467 if options.local: | 454 if options.local: |
| 468 working = os.getcwd() | 455 working = os.getcwd() |
| 469 if not inCheckoutRoot(working): | 456 if not inCheckoutRoot(working): |
| 470 print "'%s' appears not to be the root of a working copy" % working | 457 print "'%s' appears not to be the root of a working copy" % working |
| 471 sys.exit(1) | 458 return 1 |
| 472 if isSVNDirty(): | 459 if isSVNDirty(): |
| 473 print "Working copy contains uncommitted files" | 460 print "Working copy contains uncommitted files" |
| 474 sys.exit(1) | 461 return 1 |
| 475 | 462 |
| 476 command = 'svn log ' + url + " -r "+str(revision) + " -v" | 463 command = 'svn log ' + url + " -r "+str(revision) + " -v" |
| 477 os.system(command) | 464 os.system(command) |
| 478 | 465 |
| 479 if not (options.revertbot or prompt("Is this the correct revision?")): | 466 if not (options.revertbot or prompt("Is this the correct revision?")): |
| 480 sys.exit(0) | 467 return 0 |
| 481 | 468 |
| 482 if (os.path.exists(working)) and not options.local: | 469 if (os.path.exists(working)) and not options.local: |
| 483 if not (options.revertbot or SKIP_CHECK_WORKING or | 470 if not (options.revertbot or SKIP_CHECK_WORKING or |
| 484 prompt("Working directory: '%s' already exists, clobber?" % working)): | 471 prompt("Working directory: '%s' already exists, clobber?" % working)): |
| 485 sys.exit(0) | 472 return 0 |
| 486 deltree(working) | 473 deltree(working) |
| 487 | 474 |
| 488 if not options.local: | 475 if not options.local: |
| 489 os.makedirs(working) | 476 os.makedirs(working) |
| 490 os.chdir(working) | 477 os.chdir(working) |
| 491 | 478 |
| 492 if options.merge: | 479 if options.merge: |
| 493 action = "Merge" | 480 action = "Merge" |
| 494 if not options.local: | 481 if not options.local: |
| 495 branch_url = BRANCH_URL.replace("$branch", options.branch) | 482 branch_url = BRANCH_URL.replace("$branch", options.branch) |
| (...skipping 30 matching lines...) Expand all Loading... |
| 526 out.write("\nTBR=" + author) | 513 out.write("\nTBR=" + author) |
| 527 out.close() | 514 out.close() |
| 528 | 515 |
| 529 change_cmd = 'change ' + str(revision) + " " + filename | 516 change_cmd = 'change ' + str(revision) + " " + filename |
| 530 if options.revertbot: | 517 if options.revertbot: |
| 531 change_cmd += ' --silent' | 518 change_cmd += ' --silent' |
| 532 runGcl(change_cmd) | 519 runGcl(change_cmd) |
| 533 os.unlink(filename) | 520 os.unlink(filename) |
| 534 | 521 |
| 535 if options.local: | 522 if options.local: |
| 536 sys.exit(0) | 523 return 0 |
| 537 | 524 |
| 538 print author | 525 print author |
| 539 print revision | 526 print revision |
| 540 print ("gcl upload " + str(revision) + | 527 print ("gcl upload " + str(revision) + |
| 541 " --send_mail --no_try --no_presubmit --reviewers=" + author) | 528 " --send_mail --no_try --no_presubmit --reviewers=" + author) |
| 542 | 529 |
| 543 if options.revertbot or prompt("Would you like to upload?"): | 530 if options.revertbot or prompt("Would you like to upload?"): |
| 544 if PROMPT_FOR_AUTHOR: | 531 if PROMPT_FOR_AUTHOR: |
| 545 author = text_prompt("Enter new author or press enter to accept default", | 532 author = text_prompt("Enter new author or press enter to accept default", |
| 546 author) | 533 author) |
| 547 if options.revertbot and options.revertbot_reviewers: | 534 if options.revertbot and options.revertbot_reviewers: |
| 548 author += "," | 535 author += "," |
| 549 author += options.revertbot_reviewers | 536 author += options.revertbot_reviewers |
| 550 gclUpload(revision, author) | 537 gclUpload(revision, author) |
| 551 else: | 538 else: |
| 552 print "Deleting the changelist." | 539 print "Deleting the changelist." |
| 553 print "gcl delete " + str(revision) | 540 print "gcl delete " + str(revision) |
| 554 runGcl("delete " + str(revision)) | 541 runGcl("delete " + str(revision)) |
| 555 sys.exit(0) | 542 return 0 |
| 556 | 543 |
| 557 # We commit if the reverbot is set to commit automatically, or if this is | 544 # We commit if the reverbot is set to commit automatically, or if this is |
| 558 # not the revertbot and the user agrees. | 545 # not the revertbot and the user agrees. |
| 559 if options.revertbot_commit or (not options.revertbot and | 546 if options.revertbot_commit or (not options.revertbot and |
| 560 prompt("Would you like to commit?")): | 547 prompt("Would you like to commit?")): |
| 561 print "gcl commit " + str(revision) + " --no_presubmit --force" | 548 print "gcl commit " + str(revision) + " --no_presubmit --force" |
| 562 runGcl("commit " + str(revision) + " --no_presubmit --force") | 549 return runGcl("commit " + str(revision) + " --no_presubmit --force") |
| 563 else: | 550 else: |
| 564 sys.exit(0) | 551 return 0 |
| 565 | 552 |
| 566 if __name__ == "__main__": | 553 |
| 554 def main(): |
| 567 option_parser = optparse.OptionParser(usage=USAGE % {"app": sys.argv[0]}) | 555 option_parser = optparse.OptionParser(usage=USAGE % {"app": sys.argv[0]}) |
| 568 option_parser.add_option('-m', '--merge', type="int", | 556 option_parser.add_option('-m', '--merge', type="int", |
| 569 help='Revision to merge from trunk to branch') | 557 help='Revision to merge from trunk to branch') |
| 570 option_parser.add_option('-b', '--branch', | 558 option_parser.add_option('-b', '--branch', |
| 571 help='Branch to revert or merge from') | 559 help='Branch to revert or merge from') |
| 572 option_parser.add_option('-l', '--local', action='store_true', | 560 option_parser.add_option('-l', '--local', action='store_true', |
| 573 help='Local working copy to merge to') | 561 help='Local working copy to merge to') |
| 574 option_parser.add_option('-s', '--sbranch', | 562 option_parser.add_option('-s', '--sbranch', |
| 575 help='Source branch for merge') | 563 help='Source branch for merge') |
| 576 option_parser.add_option('-r', '--revert', type="int", | 564 option_parser.add_option('-r', '--revert', type="int", |
| 577 help='Revision to revert') | 565 help='Revision to revert') |
| 578 option_parser.add_option('-w', '--workdir', | 566 option_parser.add_option('-w', '--workdir', |
| 579 help='subdir to use for the revert') | 567 help='subdir to use for the revert') |
| 580 option_parser.add_option('-a', '--auditor', | 568 option_parser.add_option('-a', '--auditor', |
| 581 help='overrides the author for reviewer')
| 569 help='overrides the author for reviewer') |
| 582 option_parser.add_option('', '--revertbot', action='store_true', | 570 option_parser.add_option('', '--revertbot', action='store_true', |
| 583 default=False) | 571 default=False) |
| 584 option_parser.add_option('', '--revertbot-commit', action='store_true', | 572 option_parser.add_option('', '--revertbot-commit', action='store_true', |
| 585 default=False) | 573 default=False) |
| 586 option_parser.add_option('', '--revertbot-reviewers') | 574 option_parser.add_option('', '--revertbot-reviewers') |
| 587 options, args = option_parser.parse_args() | 575 options, args = option_parser.parse_args() |
| 588 | 576 |
| 589 if not options.merge and not options.revert: | 577 if not options.merge and not options.revert: |
| 590 option_parser.error("You need at least --merge or --revert") | 578 option_parser.error("You need at least --merge or --revert") |
| 591 sys.exit(1) | 579 return 1 |
| 592 | 580 |
| 593 if options.merge and not options.branch and not options.local: | 581 if options.merge and not options.branch and not options.local: |
| 594 option_parser.error("--merge requires either --branch or --local") | 582 option_parser.error("--merge requires either --branch or --local") |
| 595 sys.exit(1) | 583 return 1 |
| 596 | 584 |
| 597 if options.local and (options.revert or options.branch): | 585 if options.local and (options.revert or options.branch): |
| 598 option_parser.error("--local cannot be used with --revert or --branch") | 586 option_parser.error("--local cannot be used with --revert or --branch") |
| 599 sys.exit(1) | 587 return 1 |
| 600 | 588 |
| 601 sys.exit(main(options, args)) | 589 return drover(options, args) |
| 590 |
| 591 |
| 592 if __name__ == "__main__": |
| 593 sys.exit(main()) |
| OLD | NEW |