blob: 6d2fb7d700a64985c0915865c9c9174e9eb142ed [file] [log] [blame]
[email protected]ba551772010-02-03 18:21:421#!/usr/bin/python
[email protected]0c6b52b2010-06-02 21:56:222# Copyright (c) 2010 The Chromium Authors. All rights reserved.
[email protected]817ba612009-09-23 21:00:533# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
[email protected]a4899042009-10-16 19:42:416import optparse
7import os
8import re
[email protected]d90ad9b2009-08-06 00:20:179import subprocess
10import sys
[email protected]d90ad9b2009-08-06 00:20:1711import webbrowser
12
[email protected]ada4c652009-12-03 15:32:0113import breakpad
14
[email protected]a4899042009-10-16 19:42:4115USAGE = """
16WARNING: Please use this tool in an empty directory
17(or at least one that you don't mind clobbering.)
18
19REQUIRES: SVN 1.5+
20NOTE: NO NEED TO CHECKOUT ANYTHING IN ADVANCE OF USING THIS TOOL."
21Valid parameters:
22
23[Merge from trunk to branch]
[email protected]9ce98222009-10-19 20:24:1724--merge <revision> --branch <branch_num>
25Example: %(app)s --merge 12345 --branch 187
[email protected]a4899042009-10-16 19:42:4126
[email protected]0b9e6932010-02-22 16:49:0627[Merge from trunk to local copy]
28--merge <revision> --local
29Example: %(app)s --merge 12345 --local
30
[email protected]2d6af5f2010-01-22 18:18:5131[Merge from branch to branch]
[email protected]6e29d572010-06-04 17:32:2032--merge <revision> --sbranch <branch_num> --branch <branch_num>
[email protected]2d6af5f2010-01-22 18:18:5133Example: %(app)s --merge 12345 --sbranch 248 --branch 249
34
[email protected]a4899042009-10-16 19:42:4135[Revert from trunk]
[email protected]9ce98222009-10-19 20:24:1736--revert <revision>
37Example: %(app)s --revert 12345
[email protected]a4899042009-10-16 19:42:4138
39[Revert from branch]
[email protected]9ce98222009-10-19 20:24:1740--revert <revision> --branch <branch_num>
41Example: %(app)s --revert 12345 --branch 187
[email protected]a4899042009-10-16 19:42:4142"""
43
[email protected]b97010c2009-10-15 23:50:5644export_map_ = None
45files_info_ = None
46delete_map_ = None
[email protected]35299542010-01-27 01:22:1647file_pattern_ = r"[ ]+([MADUC])[ ]+/((?:trunk|branches/.*?)/src(.*)/(.*))"
[email protected]817ba612009-09-23 21:00:5348
[email protected]d90ad9b2009-08-06 00:20:1749def deltree(root):
[email protected]a4899042009-10-16 19:42:4150 """Removes a given directory"""
[email protected]d90ad9b2009-08-06 00:20:1751 if (not os.path.exists(root)):
52 return
[email protected]a4899042009-10-16 19:42:4153
[email protected]d90ad9b2009-08-06 00:20:1754 if sys.platform == 'win32':
55 os.system('rmdir /S /Q ' + root.replace('/','\\'))
56 else:
57 for name in os.listdir(root):
[email protected]a4899042009-10-16 19:42:4158 path = os.path.join(root, name)
59 if os.path.isdir(path):
60 deltree(path)
61 else:
62 os.unlink(path)
[email protected]d90ad9b2009-08-06 00:20:1763 os.rmdir(root)
64
[email protected]6e29d572010-06-04 17:32:2065def clobberDir(dirname):
[email protected]a4899042009-10-16 19:42:4166 """Removes a given directory"""
67
[email protected]6e29d572010-06-04 17:32:2068 if (os.path.exists(dirname)):
[email protected]a4899042009-10-16 19:42:4169 print dir + " directory found, deleting"
70 # The following line was removed due to access controls in Windows
71 # which make os.unlink(path) calls impossible.
72 #TODO(laforge) : Is this correct?
[email protected]6e29d572010-06-04 17:32:2073 deltree(dirname)
[email protected]d90ad9b2009-08-06 00:20:1774
[email protected]363b4d42009-11-03 21:13:3675def runGcl(subcommand):
76 gcl_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "gcl")
77 if not os.path.exists(gcl_path):
78 print "WARNING: gcl not found beside drover.py. Using system gcl instead..."
79 gcl_path = 'gcl'
80
81 command = "%s %s" % (gcl_path, subcommand)
82 return os.system(command)
83
[email protected]d90ad9b2009-08-06 00:20:1784def gclUpload(revision, author):
[email protected]363b4d42009-11-03 21:13:3685 command = ("upload " + str(revision) +
[email protected]817ba612009-09-23 21:00:5386 " --send_mail --no_try --no_presubmit --reviewers=" + author)
[email protected]363b4d42009-11-03 21:13:3687 return runGcl(command)
[email protected]817ba612009-09-23 21:00:5388
[email protected]b97010c2009-10-15 23:50:5689def getSVNInfo(url, revision):
90 command = 'svn info ' + url + "@"+str(revision)
[email protected]a4899042009-10-16 19:42:4191 svn_info = subprocess.Popen(command,
92 shell=True,
93 stdout=subprocess.PIPE,
94 stderr=subprocess.PIPE).stdout.readlines()
95 info = {}
[email protected]b97010c2009-10-15 23:50:5696 for line in svn_info:
97 match = re.search(r"(.*?):(.*)", line)
98 if match:
[email protected]6e29d572010-06-04 17:32:2099 info[match.group(1).strip()]=match.group(2).strip()
[email protected]a4899042009-10-16 19:42:41100
101 return info
[email protected]d90ad9b2009-08-06 00:20:17102
[email protected]0b9e6932010-02-22 16:49:06103def isSVNDirty():
104 command = 'svn status'
105 svn_status = subprocess.Popen(command,
106 shell=True,
107 stdout=subprocess.PIPE,
108 stderr=subprocess.PIPE).stdout.readlines()
109 for line in svn_status:
110 match = re.search(r"^[^X?]", line)
111 if match:
112 return True
113
114 return False
115
[email protected]d90ad9b2009-08-06 00:20:17116def getAuthor(url, revision):
[email protected]b97010c2009-10-15 23:50:56117 info = getSVNInfo(url, revision)
[email protected]b97010c2009-10-15 23:50:56118 if (info.has_key("Last Changed Author")):
119 return info["Last Changed Author"]
[email protected]d90ad9b2009-08-06 00:20:17120 return None
121
[email protected]b97010c2009-10-15 23:50:56122def isSVNFile(url, revision):
123 info = getSVNInfo(url, revision)
[email protected]b97010c2009-10-15 23:50:56124 if (info.has_key("Node Kind")):
[email protected]a4899042009-10-16 19:42:41125 if (info["Node Kind"] == "file"):
126 return True
127 return False
[email protected]d90ad9b2009-08-06 00:20:17128
[email protected]b97010c2009-10-15 23:50:56129def isSVNDirectory(url, revision):
130 info = getSVNInfo(url, revision)
[email protected]b97010c2009-10-15 23:50:56131 if (info.has_key("Node Kind")):
[email protected]a4899042009-10-16 19:42:41132 if (info["Node Kind"] == "directory"):
133 return True
134 return False
135
[email protected]0b9e6932010-02-22 16:49:06136def inCheckoutRoot(path):
137 info = getSVNInfo(path, "HEAD")
138 if (not info.has_key("Repository Root")):
139 return False
[email protected]6e29d572010-06-04 17:32:20140 repo_root = info["Repository Root"]
[email protected]0b9e6932010-02-22 16:49:06141 info = getSVNInfo(os.path.dirname(os.path.abspath(path)), "HEAD")
142 if (info.get("Repository Root", None) != repo_root):
143 return True
144 return False
145
[email protected]d90ad9b2009-08-06 00:20:17146def getRevisionLog(url, revision):
[email protected]a4899042009-10-16 19:42:41147 """Takes an svn url and gets the associated revision."""
[email protected]d90ad9b2009-08-06 00:20:17148 command = 'svn log ' + url + " -r"+str(revision)
[email protected]6e29d572010-06-04 17:32:20149 svn_log = subprocess.Popen(command,
150 shell=True,
151 stdout=subprocess.PIPE,
[email protected]a4899042009-10-16 19:42:41152 stderr=subprocess.PIPE).stdout.readlines()
[email protected]0c6b52b2010-06-02 21:56:22153 # Don't include the header lines and the trailing "---..." line and eliminate
154 # any '\r's.
155 return ''.join([l.replace('\r','') for l in svn_log[3:-1]])
[email protected]d90ad9b2009-08-06 00:20:17156
[email protected]004d2ef2009-12-17 23:49:04157def getSVNVersionInfo():
158 """Extract version information from SVN"""
159 command = 'svn --version'
160 svn_info = subprocess.Popen(command,
161 shell=True,
162 stdout=subprocess.PIPE,
163 stderr=subprocess.PIPE).stdout.readlines()
164 info = {}
165 for line in svn_info:
166 match = re.search(r"svn, version ((\d+)\.(\d+)\.(\d+)) \(r(\d+)\)", line)
167 if match:
[email protected]6e29d572010-06-04 17:32:20168 info['version'] = match.group(1)
169 info['major'] = int(match.group(2))
170 info['minor'] = int(match.group(3))
171 info['patch'] = int(match.group(4))
172 info['revision'] = match.group(5)
173 return info
[email protected]004d2ef2009-12-17 23:49:04174
175 return None
176
177def isMinimumSVNVersion(major, minor, patch=0):
178 """Test for minimum SVN version"""
179 return _isMinimumSVNVersion(getSVNVersionInfo(), major, minor, patch)
180
181def _isMinimumSVNVersion(version, major, minor, patch=0):
182 """Test for minimum SVN version, internal method"""
183 if not version:
184 return False
185
186 if (version['major'] > major):
187 return True
188 elif (version['major'] < major):
189 return False
190
191 if (version['minor'] > minor):
192 return True
193 elif (version['minor'] < minor):
194 return False
195
196 if (version['patch'] >= patch):
197 return True
198 else:
199 return False
200
[email protected]b97010c2009-10-15 23:50:56201def checkoutRevision(url, revision, branch_url, revert=False):
[email protected]a4899042009-10-16 19:42:41202 files_info = getFileInfo(url, revision)
[email protected]b97010c2009-10-15 23:50:56203 paths = getBestMergePaths2(files_info, revision)
204 export_map = getBestExportPathsMap2(files_info, revision)
[email protected]a4899042009-10-16 19:42:41205
[email protected]b97010c2009-10-15 23:50:56206 command = 'svn checkout -N ' + branch_url
207 print command
208 os.system(command)
[email protected]817ba612009-09-23 21:00:53209
[email protected]b97010c2009-10-15 23:50:56210 match = re.search(r"svn://.*/(.*)", branch_url)
[email protected]a4899042009-10-16 19:42:41211
[email protected]b97010c2009-10-15 23:50:56212 if match:
213 os.chdir(match.group(1))
[email protected]d90ad9b2009-08-06 00:20:17214
[email protected]a4899042009-10-16 19:42:41215 # 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
217 # directories get clobbered and the merge step fails.
[email protected]d90ad9b2009-08-06 00:20:17218 paths.sort()
[email protected]817ba612009-09-23 21:00:53219
[email protected]6e29d572010-06-04 17:32:20220 # Checkout the directories that already exist
[email protected]b97010c2009-10-15 23:50:56221 for path in paths:
[email protected]6e29d572010-06-04 17:32:20222 if (export_map.has_key(path) and not revert):
223 print "Exclude new directory " + path
224 continue
225 subpaths = path.split('/')
226 subpaths.pop(0)
227 base = ''
228 for subpath in subpaths:
229 base += '/' + subpath
230 # This logic ensures that you don't empty out any directories
231 if not os.path.exists("." + base):
232 command = ('svn update --depth empty ' + "." + base)
233 print command
234 os.system(command)
[email protected]a4899042009-10-16 19:42:41235
[email protected]b97010c2009-10-15 23:50:56236 if (revert):
237 files = getAllFilesInRevision(files_info)
238 else:
239 files = getExistingFilesInRevision(files_info)
[email protected]6e29d572010-06-04 17:32:20240
241 for f in files:
242 # Prevent the tool from clobbering the src directory
243 if (f == ""):
[email protected]d90ad9b2009-08-06 00:20:17244 continue
[email protected]6e29d572010-06-04 17:32:20245 command = ('svn up ".' + f + '"')
[email protected]d90ad9b2009-08-06 00:20:17246 print command
247 os.system(command)
[email protected]a4899042009-10-16 19:42:41248
[email protected]b97010c2009-10-15 23:50:56249def mergeRevision(url, revision):
[email protected]d90ad9b2009-08-06 00:20:17250 paths = getBestMergePaths(url, revision)
[email protected]b97010c2009-10-15 23:50:56251 export_map = getBestExportPathsMap(url, revision)
[email protected]a4899042009-10-16 19:42:41252
[email protected]d90ad9b2009-08-06 00:20:17253 for path in paths:
[email protected]b97010c2009-10-15 23:50:56254 if export_map.has_key(path):
255 continue
[email protected]d90ad9b2009-08-06 00:20:17256 command = ('svn merge -N -r ' + str(revision-1) + ":" + str(revision) + " ")
[email protected]94ed5022009-10-21 20:28:53257 command += " --ignore-ancestry "
[email protected]f5a11632009-12-18 00:07:37258 command += " -x --ignore-eol-style "
[email protected]94ed5022009-10-21 20:28:53259 command += url + path + "@" + str(revision) + " ." + path
[email protected]a4899042009-10-16 19:42:41260
[email protected]d90ad9b2009-08-06 00:20:17261 print command
262 os.system(command)
263
[email protected]b97010c2009-10-15 23:50:56264def exportRevision(url, revision):
265 paths = getBestExportPathsMap(url, revision).keys()
[email protected]b97010c2009-10-15 23:50:56266 paths.sort()
[email protected]a4899042009-10-16 19:42:41267
[email protected]b97010c2009-10-15 23:50:56268 for path in paths:
[email protected]a4899042009-10-16 19:42:41269 command = ('svn export -N ' + url + path + "@" + str(revision) + " ." +
270 path)
[email protected]b97010c2009-10-15 23:50:56271 print command
272 os.system(command)
[email protected]a4899042009-10-16 19:42:41273
274 command = 'svn add .' + path
[email protected]6e29d572010-06-04 17:32:20275 print command
[email protected]b97010c2009-10-15 23:50:56276 os.system(command)
277
278def deleteRevision(url, revision):
279 paths = getBestDeletePathsMap(url, revision).keys()
280 paths.sort()
281 paths.reverse()
[email protected]a4899042009-10-16 19:42:41282
[email protected]b97010c2009-10-15 23:50:56283 for path in paths:
[email protected]a4899042009-10-16 19:42:41284 command = "svn delete ." + path
[email protected]b97010c2009-10-15 23:50:56285 print command
286 os.system(command)
287
[email protected]b97010c2009-10-15 23:50:56288def revertExportRevision(url, revision):
289 paths = getBestExportPathsMap(url, revision).keys()
290 paths.sort()
291 paths.reverse()
[email protected]a4899042009-10-16 19:42:41292
[email protected]b97010c2009-10-15 23:50:56293 for path in paths:
[email protected]a4899042009-10-16 19:42:41294 command = "svn delete ." + path
[email protected]b97010c2009-10-15 23:50:56295 print command
296 os.system(command)
[email protected]817ba612009-09-23 21:00:53297
[email protected]d90ad9b2009-08-06 00:20:17298def revertRevision(url, revision):
299 paths = getBestMergePaths(url, revision)
300 for path in paths:
[email protected]6e29d572010-06-04 17:32:20301 command = ('svn merge -N -r ' + str(revision) + ":" + str(revision-1) +
[email protected]a4899042009-10-16 19:42:41302 " " + url + path + " ." + path)
[email protected]d90ad9b2009-08-06 00:20:17303 print command
304 os.system(command)
[email protected]a4899042009-10-16 19:42:41305
[email protected]b97010c2009-10-15 23:50:56306def getFileInfo(url, revision):
307 global files_info_, file_pattern_
[email protected]a4899042009-10-16 19:42:41308
[email protected]b97010c2009-10-15 23:50:56309 if (files_info_ != None):
310 return files_info_
[email protected]d90ad9b2009-08-06 00:20:17311
[email protected]b97010c2009-10-15 23:50:56312 command = 'svn log ' + url + " -r " + str(revision) + " -v"
[email protected]a4899042009-10-16 19:42:41313 svn_log = subprocess.Popen(command,
314 shell=True,
315 stdout=subprocess.PIPE,
316 stderr=subprocess.PIPE).stdout.readlines()
317
318 info = []
[email protected]b97010c2009-10-15 23:50:56319 for line in svn_log:
[email protected]a4899042009-10-16 19:42:41320 # A workaround to dump the (from .*) stuff, regex not so friendly in the 2nd
321 # pass...
[email protected]b97010c2009-10-15 23:50:56322 match = re.search(r"(.*) \(from.*\)", line)
323 if match:
324 line = match.group(1)
325 match = re.search(file_pattern_, line)
326 if match:
[email protected]a4899042009-10-16 19:42:41327 info.append([match.group(1).strip(), match.group(2).strip(),
328 match.group(3).strip(),match.group(4).strip()])
[email protected]b97010c2009-10-15 23:50:56329
[email protected]a4899042009-10-16 19:42:41330 files_info_ = info
[email protected]9ce98222009-10-19 20:24:17331 return info
[email protected]817ba612009-09-23 21:00:53332
[email protected]d90ad9b2009-08-06 00:20:17333def getBestMergePaths(url, revision):
[email protected]a4899042009-10-16 19:42:41334 """Takes an svn url and gets the associated revision."""
[email protected]b97010c2009-10-15 23:50:56335 return getBestMergePaths2(getFileInfo(url, revision), revision)
336
337def getBestMergePaths2(files_info, revision):
[email protected]a4899042009-10-16 19:42:41338 """Takes an svn url and gets the associated revision."""
[email protected]6e29d572010-06-04 17:32:20339 return list(set([f[2] for f in files_info]))
[email protected]d90ad9b2009-08-06 00:20:17340
[email protected]b97010c2009-10-15 23:50:56341def getBestExportPathsMap(url, revision):
342 return getBestExportPathsMap2(getFileInfo(url, revision), revision)
[email protected]a4899042009-10-16 19:42:41343
[email protected]b97010c2009-10-15 23:50:56344def getBestExportPathsMap2(files_info, revision):
[email protected]a4899042009-10-16 19:42:41345 """Takes an svn url and gets the associated revision."""
[email protected]b97010c2009-10-15 23:50:56346 global export_map_
[email protected]a4899042009-10-16 19:42:41347
[email protected]b97010c2009-10-15 23:50:56348 if export_map_:
349 return export_map_
[email protected]817ba612009-09-23 21:00:53350
[email protected]6e29d572010-06-04 17:32:20351 result = {}
[email protected]b97010c2009-10-15 23:50:56352 for file_info in files_info:
353 if (file_info[0] == "A"):
[email protected]b2ab0692009-11-03 23:35:27354 if(isSVNDirectory("svn://svn.chromium.org/chrome/" + file_info[1],
355 revision)):
[email protected]6e29d572010-06-04 17:32:20356 result[file_info[2] + "/" + file_info[3]] = ""
[email protected]d90ad9b2009-08-06 00:20:17357
[email protected]6e29d572010-06-04 17:32:20358 export_map_ = result
359 return result
[email protected]d90ad9b2009-08-06 00:20:17360
[email protected]b97010c2009-10-15 23:50:56361def getBestDeletePathsMap(url, revision):
[email protected]a4899042009-10-16 19:42:41362 return getBestDeletePathsMap2(getFileInfo(url, revision), revision)
[email protected]817ba612009-09-23 21:00:53363
[email protected]b97010c2009-10-15 23:50:56364def getBestDeletePathsMap2(files_info, revision):
[email protected]a4899042009-10-16 19:42:41365 """Takes an svn url and gets the associated revision."""
[email protected]b97010c2009-10-15 23:50:56366 global delete_map_
[email protected]a4899042009-10-16 19:42:41367
[email protected]b97010c2009-10-15 23:50:56368 if delete_map_:
369 return delete_map_
[email protected]d90ad9b2009-08-06 00:20:17370
[email protected]6e29d572010-06-04 17:32:20371 result = {}
[email protected]b97010c2009-10-15 23:50:56372 for file_info in files_info:
373 if (file_info[0] == "D"):
[email protected]b2ab0692009-11-03 23:35:27374 if(isSVNDirectory("svn://svn.chromium.org/chrome/" + file_info[1],
375 revision)):
[email protected]6e29d572010-06-04 17:32:20376 result[file_info[2] + "/" + file_info[3]] = ""
[email protected]d90ad9b2009-08-06 00:20:17377
[email protected]6e29d572010-06-04 17:32:20378 delete_map_ = result
379 return result
380
[email protected]a4899042009-10-16 19:42:41381
[email protected]b97010c2009-10-15 23:50:56382def getExistingFilesInRevision(files_info):
[email protected]a4899042009-10-16 19:42:41383 """Checks for existing files in the revision.
[email protected]6e29d572010-06-04 17:32:20384
[email protected]a4899042009-10-16 19:42:41385 Anything that's A will require special treatment (either a merge or an
386 export + add)
[email protected]b97010c2009-10-15 23:50:56387 """
[email protected]6e29d572010-06-04 17:32:20388 return ['%s/%s' % (f[2], f[3]) for f in files_info if f[0] != 'A']
[email protected]817ba612009-09-23 21:00:53389
[email protected]817ba612009-09-23 21:00:53390
[email protected]b97010c2009-10-15 23:50:56391def getAllFilesInRevision(files_info):
[email protected]a4899042009-10-16 19:42:41392 """Checks for existing files in the revision.
393
394 Anything that's A will require special treatment (either a merge or an
395 export + add)
[email protected]b97010c2009-10-15 23:50:56396 """
[email protected]6e29d572010-06-04 17:32:20397 return ['%s/%s' % (f[2], f[3]) for f in files_info]
[email protected]817ba612009-09-23 21:00:53398
[email protected]d90ad9b2009-08-06 00:20:17399def prompt(question):
[email protected]6e29d572010-06-04 17:32:20400 while True:
[email protected]c4e7bb32009-11-20 23:58:27401 print question + " [y|n]:",
[email protected]a4899042009-10-16 19:42:41402 answer = sys.stdin.readline()
403 if answer.lower().startswith('n'):
[email protected]d90ad9b2009-08-06 00:20:17404 return False
[email protected]a4899042009-10-16 19:42:41405 elif answer.lower().startswith('y'):
[email protected]d90ad9b2009-08-06 00:20:17406 return True
[email protected]6e29d572010-06-04 17:32:20407
[email protected]817ba612009-09-23 21:00:53408
[email protected]b97010c2009-10-15 23:50:56409def text_prompt(question, default):
410 print question + " [" + default + "]:"
[email protected]a4899042009-10-16 19:42:41411 answer = sys.stdin.readline()
412 if answer.strip() == "":
[email protected]b97010c2009-10-15 23:50:56413 return default
[email protected]a4899042009-10-16 19:42:41414 return answer
415
[email protected]6e29d572010-06-04 17:32:20416
417def drover(options, args):
[email protected]5ebf1632009-10-23 22:33:47418 revision = options.revert or options.merge
419
420 # Initialize some variables used below. They can be overwritten by
421 # the drover.properties file.
[email protected]b2ab0692009-11-03 23:35:27422 BASE_URL = "svn://svn.chromium.org/chrome"
[email protected]d90ad9b2009-08-06 00:20:17423 TRUNK_URL = BASE_URL + "/trunk/src"
[email protected]b97010c2009-10-15 23:50:56424 BRANCH_URL = BASE_URL + "/branches/$branch/src"
[email protected]b97010c2009-10-15 23:50:56425 SKIP_CHECK_WORKING = True
426 PROMPT_FOR_AUTHOR = False
[email protected]a4899042009-10-16 19:42:41427
[email protected]5ebf1632009-10-23 22:33:47428 DEFAULT_WORKING = "drover_" + str(revision)
429 if options.branch:
430 DEFAULT_WORKING += ("_" + options.branch)
431
[email protected]6e29d572010-06-04 17:32:20432 if not isMinimumSVNVersion(1, 5):
[email protected]004d2ef2009-12-17 23:49:04433 print "You need to use at least SVN version 1.5.x"
[email protected]6e29d572010-06-04 17:32:20434 return 1
[email protected]004d2ef2009-12-17 23:49:04435
[email protected]a4899042009-10-16 19:42:41436 # Override the default properties if there is a drover.properties file.
[email protected]b97010c2009-10-15 23:50:56437 global file_pattern_
438 if os.path.exists("drover.properties"):
[email protected]6e29d572010-06-04 17:32:20439 f = open("drover.properties")
440 exec(f)
441 f.close()
[email protected]b97010c2009-10-15 23:50:56442 if FILE_PATTERN:
[email protected]6e29d572010-06-04 17:32:20443 file_pattern_ = FILE_PATTERN
[email protected]d90ad9b2009-08-06 00:20:17444
[email protected]9ce98222009-10-19 20:24:17445 if options.revert and options.branch:
[email protected]2d6af5f2010-01-22 18:18:51446 url = BRANCH_URL.replace("$branch", options.branch)
447 elif options.merge and options.sbranch:
448 url = BRANCH_URL.replace("$branch", options.sbranch)
[email protected]d90ad9b2009-08-06 00:20:17449 else:
450 url = TRUNK_URL
[email protected]a4899042009-10-16 19:42:41451
[email protected]864fbdd2009-10-23 20:11:40452 working = options.workdir or DEFAULT_WORKING
[email protected]a4899042009-10-16 19:42:41453
[email protected]0b9e6932010-02-22 16:49:06454 if options.local:
455 working = os.getcwd()
456 if not inCheckoutRoot(working):
457 print "'%s' appears not to be the root of a working copy" % working
[email protected]6e29d572010-06-04 17:32:20458 return 1
[email protected]0b9e6932010-02-22 16:49:06459 if isSVNDirty():
460 print "Working copy contains uncommitted files"
[email protected]6e29d572010-06-04 17:32:20461 return 1
[email protected]0b9e6932010-02-22 16:49:06462
[email protected]d90ad9b2009-08-06 00:20:17463 command = 'svn log ' + url + " -r "+str(revision) + " -v"
464 os.system(command)
[email protected]a4899042009-10-16 19:42:41465
[email protected]9ce98222009-10-19 20:24:17466 if not (options.revertbot or prompt("Is this the correct revision?")):
[email protected]6e29d572010-06-04 17:32:20467 return 0
[email protected]a4899042009-10-16 19:42:41468
[email protected]0b9e6932010-02-22 16:49:06469 if (os.path.exists(working)) and not options.local:
[email protected]9ce98222009-10-19 20:24:17470 if not (options.revertbot or SKIP_CHECK_WORKING or
471 prompt("Working directory: '%s' already exists, clobber?" % working)):
[email protected]6e29d572010-06-04 17:32:20472 return 0
[email protected]b97010c2009-10-15 23:50:56473 deltree(working)
[email protected]a4899042009-10-16 19:42:41474
[email protected]0b9e6932010-02-22 16:49:06475 if not options.local:
476 os.makedirs(working)
477 os.chdir(working)
[email protected]a4899042009-10-16 19:42:41478
[email protected]9ce98222009-10-19 20:24:17479 if options.merge:
480 action = "Merge"
[email protected]0b9e6932010-02-22 16:49:06481 if not options.local:
482 branch_url = BRANCH_URL.replace("$branch", options.branch)
483 # Checkout everything but stuff that got added into a new dir
484 checkoutRevision(url, revision, branch_url)
[email protected]9ce98222009-10-19 20:24:17485 # Merge everything that changed
486 mergeRevision(url, revision)
487 # "Export" files that were added from the source and add them to branch
488 exportRevision(url, revision)
489 # Delete directories that were deleted (file deletes are handled in the
490 # merge).
491 deleteRevision(url, revision)
492 elif options.revert:
493 action = "Revert"
494 if options.branch:
495 url = BRANCH_URL.replace("$branch", options.branch)
496 checkoutRevision(url, revision, url, True)
497 revertRevision(url, revision)
498 revertExportRevision(url, revision)
[email protected]a4899042009-10-16 19:42:41499
500 # Check the base url so we actually find the author who made the change
[email protected]638044d2010-01-25 18:52:41501 if options.auditor:
502 author = options.auditor
503 else:
504 author = getAuthor(url, revision)
505 if not author:
506 author = getAuthor(TRUNK_URL, revision)
[email protected]a4899042009-10-16 19:42:41507
[email protected]d90ad9b2009-08-06 00:20:17508 filename = str(revision)+".txt"
509 out = open(filename,"w")
510 out.write(action +" " + str(revision) + " - ")
511 out.write(getRevisionLog(url, revision))
512 if (author):
[email protected]0c6b52b2010-06-02 21:56:22513 out.write("\nTBR=" + author)
[email protected]d90ad9b2009-08-06 00:20:17514 out.close()
[email protected]a4899042009-10-16 19:42:41515
[email protected]363b4d42009-11-03 21:13:36516 change_cmd = 'change ' + str(revision) + " " + filename
[email protected]9ce98222009-10-19 20:24:17517 if options.revertbot:
518 change_cmd += ' --silent'
[email protected]363b4d42009-11-03 21:13:36519 runGcl(change_cmd)
[email protected]d90ad9b2009-08-06 00:20:17520 os.unlink(filename)
[email protected]0b9e6932010-02-22 16:49:06521
522 if options.local:
[email protected]6e29d572010-06-04 17:32:20523 return 0
[email protected]0b9e6932010-02-22 16:49:06524
[email protected]d90ad9b2009-08-06 00:20:17525 print author
526 print revision
[email protected]a4899042009-10-16 19:42:41527 print ("gcl upload " + str(revision) +
[email protected]b97010c2009-10-15 23:50:56528 " --send_mail --no_try --no_presubmit --reviewers=" + author)
[email protected]d90ad9b2009-08-06 00:20:17529
[email protected]9ce98222009-10-19 20:24:17530 if options.revertbot or prompt("Would you like to upload?"):
[email protected]b97010c2009-10-15 23:50:56531 if PROMPT_FOR_AUTHOR:
[email protected]9ce98222009-10-19 20:24:17532 author = text_prompt("Enter new author or press enter to accept default",
533 author)
534 if options.revertbot and options.revertbot_reviewers:
535 author += ","
536 author += options.revertbot_reviewers
[email protected]d90ad9b2009-08-06 00:20:17537 gclUpload(revision, author)
538 else:
539 print "Deleting the changelist."
[email protected]9ce98222009-10-19 20:24:17540 print "gcl delete " + str(revision)
[email protected]363b4d42009-11-03 21:13:36541 runGcl("delete " + str(revision))
[email protected]6e29d572010-06-04 17:32:20542 return 0
[email protected]d90ad9b2009-08-06 00:20:17543
[email protected]9ce98222009-10-19 20:24:17544 # We commit if the reverbot is set to commit automatically, or if this is
545 # not the revertbot and the user agrees.
546 if options.revertbot_commit or (not options.revertbot and
547 prompt("Would you like to commit?")):
548 print "gcl commit " + str(revision) + " --no_presubmit --force"
[email protected]6e29d572010-06-04 17:32:20549 return runGcl("commit " + str(revision) + " --no_presubmit --force")
[email protected]d90ad9b2009-08-06 00:20:17550 else:
[email protected]6e29d572010-06-04 17:32:20551 return 0
[email protected]a4899042009-10-16 19:42:41552
[email protected]6e29d572010-06-04 17:32:20553
554def main():
[email protected]9ce98222009-10-19 20:24:17555 option_parser = optparse.OptionParser(usage=USAGE % {"app": sys.argv[0]})
556 option_parser.add_option('-m', '--merge', type="int",
557 help='Revision to merge from trunk to branch')
558 option_parser.add_option('-b', '--branch',
559 help='Branch to revert or merge from')
[email protected]0b9e6932010-02-22 16:49:06560 option_parser.add_option('-l', '--local', action='store_true',
561 help='Local working copy to merge to')
[email protected]2d6af5f2010-01-22 18:18:51562 option_parser.add_option('-s', '--sbranch',
563 help='Source branch for merge')
[email protected]9ce98222009-10-19 20:24:17564 option_parser.add_option('-r', '--revert', type="int",
565 help='Revision to revert')
[email protected]864fbdd2009-10-23 20:11:40566 option_parser.add_option('-w', '--workdir',
567 help='subdir to use for the revert')
[email protected]638044d2010-01-25 18:52:41568 option_parser.add_option('-a', '--auditor',
[email protected]6e29d572010-06-04 17:32:20569 help='overrides the author for reviewer')
[email protected]9ce98222009-10-19 20:24:17570 option_parser.add_option('', '--revertbot', action='store_true',
571 default=False)
572 option_parser.add_option('', '--revertbot-commit', action='store_true',
573 default=False)
574 option_parser.add_option('', '--revertbot-reviewers')
575 options, args = option_parser.parse_args()
576
577 if not options.merge and not options.revert:
578 option_parser.error("You need at least --merge or --revert")
[email protected]6e29d572010-06-04 17:32:20579 return 1
[email protected]9ce98222009-10-19 20:24:17580
[email protected]0b9e6932010-02-22 16:49:06581 if options.merge and not options.branch and not options.local:
582 option_parser.error("--merge requires either --branch or --local")
[email protected]6e29d572010-06-04 17:32:20583 return 1
[email protected]0b9e6932010-02-22 16:49:06584
585 if options.local and (options.revert or options.branch):
586 option_parser.error("--local cannot be used with --revert or --branch")
[email protected]6e29d572010-06-04 17:32:20587 return 1
[email protected]9ce98222009-10-19 20:24:17588
[email protected]6e29d572010-06-04 17:32:20589 return drover(options, args)
590
591
592if __name__ == "__main__":
593 sys.exit(main())