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 |