Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 import optparse | |
| 6 import os | |
| 7 import re | |
| 5 import subprocess | 8 import subprocess |
| 6 import sys | 9 import sys |
| 7 import re | |
| 8 import os | |
| 9 import webbrowser | 10 import webbrowser |
| 10 | 11 |
| 12 USAGE = """ | |
| 13 WARNING: Please use this tool in an empty directory | |
| 14 (or at least one that you don't mind clobbering.) | |
| 15 | |
| 16 REQUIRES: SVN 1.5+ | |
| 17 NOTE: NO NEED TO CHECKOUT ANYTHING IN ADVANCE OF USING THIS TOOL." | |
| 18 Valid parameters: | |
| 19 | |
| 20 [Merge from trunk to branch] | |
| 21 <revision> --merge <branch_num> | |
| 22 Example: %(app)s 12345 --merge 187 | |
| 23 | |
| 24 [Merge from trunk to branch, ignoring revision history] | |
| 25 <revision> --mplus <branch_num> | |
| 26 Example: %(app)s 12345 --mplus 187 | |
| 27 | |
| 28 [Revert from trunk] | |
| 29 <revision> --revert | |
| 30 Example: %(app)s 12345 --revert | |
| 31 | |
| 32 | |
| 33 [Revert from branch] | |
| 34 <revision> --revert <branch_num> | |
| 35 Example: %(app)s 12345 --revert 187 | |
| 36 """ | |
| 37 | |
| 11 export_map_ = None | 38 export_map_ = None |
| 12 files_info_ = None | 39 files_info_ = None |
| 13 delete_map_ = None | 40 delete_map_ = None |
| 14 file_pattern_ = r"[ ]+([MADUC])[ ]+/((?:trunk|branches/\d+)/src(.*)/(.*))" | 41 file_pattern_ = r"[ ]+([MADUC])[ ]+/((?:trunk|branches/\d+)/src(.*)/(.*))" |
| 15 | 42 |
| 16 def deltree(root): | 43 def deltree(root): |
| 17 """ | 44 """Removes a given directory""" |
| 18 Removes a given directory | |
| 19 """ | |
| 20 if (not os.path.exists(root)): | 45 if (not os.path.exists(root)): |
| 21 return | 46 return |
| 22 | 47 |
| 23 if sys.platform == 'win32': | 48 if sys.platform == 'win32': |
| 24 os.system('rmdir /S /Q ' + root.replace('/','\\')) | 49 os.system('rmdir /S /Q ' + root.replace('/','\\')) |
| 25 else: | 50 else: |
| 26 for name in os.listdir(root): | 51 for name in os.listdir(root): |
| 27 path = os.path.join(root, name) | 52 path = os.path.join(root, name) |
| 28 if os.path.isdir(path): | 53 if os.path.isdir(path): |
| 29 deltree(path) | 54 deltree(path) |
| 30 else: | 55 else: |
| 31 os.unlink(path) | 56 os.unlink(path) |
| 32 os.rmdir(root) | 57 os.rmdir(root) |
| 33 | 58 |
| 34 def clobberDir(dir): | 59 def clobberDir(dir): |
| 35 """ | 60 """Removes a given directory""" |
| 36 Removes a given directory | 61 |
| 37 """ | |
| 38 | |
| 39 if (os.path.exists(dir)): | 62 if (os.path.exists(dir)): |
| 40 print dir + " directory found, deleting" | 63 print dir + " directory found, deleting" |
| 41 #The following line was removed due to access controls in Windows | 64 # The following line was removed due to access controls in Windows |
| 42 #which make os.unlink(path) calls impossible. | 65 # which make os.unlink(path) calls impossible. |
| 43 #deltree(dir) | 66 #TODO(laforge) : Is this correct? |
|
laforge
2009/10/16 14:57:25
That's correct
| |
| 44 os.system('rmdir /S /Q ' + dir.replace('/','\\')) | 67 deltree(dir) |
| 45 | 68 |
| 46 def gclUpload(revision, author): | 69 def gclUpload(revision, author): |
| 47 command = ("gcl upload " + str(revision) + | 70 command = ("gcl upload " + str(revision) + |
| 48 " --send_mail --no_try --no_presubmit --reviewers=" + author) | 71 " --send_mail --no_try --no_presubmit --reviewers=" + author) |
| 49 os.system(command) | 72 os.system(command) |
| 50 | 73 |
| 51 def getSVNInfo(url, revision): | 74 def getSVNInfo(url, revision): |
| 52 command = 'svn info ' + url + "@"+str(revision) | 75 command = 'svn info ' + url + "@"+str(revision) |
| 53 svn_info = subprocess.Popen(command, | 76 svn_info = subprocess.Popen(command, |
| 54 shell=True, | 77 shell=True, |
| 55 stdout=subprocess.PIPE, | 78 stdout=subprocess.PIPE, |
| 56 stderr=subprocess.PIPE).stdout.readlines() | 79 stderr=subprocess.PIPE).stdout.readlines() |
| 57 rtn = {} | 80 info = {} |
| 58 for line in svn_info: | 81 for line in svn_info: |
| 59 match = re.search(r"(.*?):(.*)", line) | 82 match = re.search(r"(.*?):(.*)", line) |
| 60 if match: | 83 if match: |
| 61 » rtn[match.group(1).strip()]=match.group(2).strip() | 84 » info[match.group(1).strip()]=match.group(2).strip() |
| 62 | 85 |
| 63 return rtn | 86 return info |
| 64 | 87 |
| 65 def getAuthor(url, revision): | 88 def getAuthor(url, revision): |
| 66 info = getSVNInfo(url, revision) | 89 info = getSVNInfo(url, revision) |
| 67 | |
| 68 if (info.has_key("Last Changed Author")): | 90 if (info.has_key("Last Changed Author")): |
| 69 return info["Last Changed Author"] | 91 return info["Last Changed Author"] |
| 70 | |
| 71 return None | 92 return None |
| 72 | 93 |
| 73 def isSVNFile(url, revision): | 94 def isSVNFile(url, revision): |
| 74 info = getSVNInfo(url, revision) | 95 info = getSVNInfo(url, revision) |
| 75 | |
| 76 if (info.has_key("Node Kind")): | 96 if (info.has_key("Node Kind")): |
| 77 if (info["Node Kind"] == "file"): return True | 97 if (info["Node Kind"] == "file"): |
| 78 | 98 return True |
| 79 return False | 99 return False |
| 80 | 100 |
| 81 def isSVNDirectory(url, revision): | 101 def isSVNDirectory(url, revision): |
| 82 info = getSVNInfo(url, revision) | 102 info = getSVNInfo(url, revision) |
| 83 | |
| 84 if (info.has_key("Node Kind")): | 103 if (info.has_key("Node Kind")): |
| 85 if (info["Node Kind"] == "directory"): return True | 104 if (info["Node Kind"] == "directory"): |
| 86 | 105 return True |
| 87 return False | 106 return False |
| 88 | 107 |
| 89 def getRevisionLog(url, revision): | 108 def getRevisionLog(url, revision): |
| 90 """ | 109 """Takes an svn url and gets the associated revision.""" |
| 91 Takes an svn url and gets the associated revision. | |
| 92 """ | |
| 93 command = 'svn log ' + url + " -r"+str(revision) | 110 command = 'svn log ' + url + " -r"+str(revision) |
| 94 svn_info = subprocess.Popen(command, | 111 svn_log = subprocess.Popen(command, |
| 95 shell=True, | 112 shell=True, |
| 96 stdout=subprocess.PIPE, | 113 stdout=subprocess.PIPE, |
| 97 stderr=subprocess.PIPE).stdout.readlines() | 114 stderr=subprocess.PIPE).stdout.readlines() |
| 98 rtn= "" | 115 log = "" |
| 99 pos = 0 | 116 pos = 0 |
| 100 for line in svn_info: | 117 for line in svn_log: |
| 101 if (pos > 2): | 118 if (pos > 2): |
| 102 rtn += line.replace('-','').replace('\r','') | 119 log += line.replace('-','').replace('\r','') |
| 103 else: | 120 else: |
| 104 pos = pos + 1 | 121 pos = pos + 1 |
| 105 | 122 return log |
| 106 return rtn | |
| 107 | 123 |
| 108 def checkoutRevision(url, revision, branch_url, revert=False): | 124 def checkoutRevision(url, revision, branch_url, revert=False): |
| 109 files_info = getFileInfo(url, revision) | 125 files_info = getFileInfo(url, revision) |
| 110 paths = getBestMergePaths2(files_info, revision) | 126 paths = getBestMergePaths2(files_info, revision) |
| 111 export_map = getBestExportPathsMap2(files_info, revision) | 127 export_map = getBestExportPathsMap2(files_info, revision) |
| 112 | 128 |
| 113 command = 'svn checkout -N ' + branch_url | 129 command = 'svn checkout -N ' + branch_url |
| 114 print command | 130 print command |
| 115 os.system(command) | 131 os.system(command) |
| 116 | 132 |
| 117 match = re.search(r"svn://.*/(.*)", branch_url) | 133 match = re.search(r"svn://.*/(.*)", branch_url) |
| 118 | 134 |
| 119 if match: | 135 if match: |
| 120 os.chdir(match.group(1)) | 136 os.chdir(match.group(1)) |
| 121 | 137 |
| 122 #This line is extremely important due to the way svn behaves in the set-depths | 138 # This line is extremely important due to the way svn behaves in the |
| 123 #action. If parents aren't handled before children, the child directories get | 139 # set-depths action. If parents aren't handled before children, the child |
| 124 #clobbered and the merge step fails. | 140 # directories get clobbered and the merge step fails. |
| 125 paths.sort() | 141 paths.sort() |
| 126 | 142 |
| 127 #Checkout the directories that already exist | 143 # Checkout the directories that already exist |
| 128 for path in paths: | 144 for path in paths: |
| 129 if (export_map.has_key(path) and not revert): | 145 if (export_map.has_key(path) and not revert): |
| 130 print "Exclude new directory " + path | 146 print "Exclude new directory " + path |
| 131 continue | 147 continue |
| 132 subpaths = path.split('/') | 148 subpaths = path.split('/') |
| 133 subpaths.pop(0) | 149 subpaths.pop(0) |
| 134 base = '' | 150 base = '' |
| 135 for subpath in subpaths: | 151 for subpath in subpaths: |
| 136 base += '/' + subpath | 152 base += '/' + subpath |
| 137 #This logic ensures that you don't empty out any directories | 153 # This logic ensures that you don't empty out any directories |
| 138 if not os.path.exists("." + base): | 154 if not os.path.exists("." + base): |
| 139 command = ('svn update --depth empty ' + "." + base) | 155 command = ('svn update --depth empty ' + "." + base) |
| 140 print command | 156 print command |
| 141 os.system(command) | 157 os.system(command) |
| 142 | 158 |
| 143 if (revert): | 159 if (revert): |
| 144 files = getAllFilesInRevision(files_info) | 160 files = getAllFilesInRevision(files_info) |
| 145 else: | 161 else: |
| 146 files = getExistingFilesInRevision(files_info) | 162 files = getExistingFilesInRevision(files_info) |
| 147 | 163 |
| 148 for file in files: | 164 for file in files: |
| 149 #Prevent the tool from clobbering the src directory | 165 # Prevent the tool from clobbering the src directory |
| 150 if (file == ""): | 166 if (file == ""): |
| 151 continue | 167 continue |
| 152 command = ('svn up ".' + file + '"') | 168 command = ('svn up ".' + file + '"') |
| 153 print command | 169 print command |
| 154 os.system(command) | 170 os.system(command) |
| 155 | 171 |
| 156 def mergeRevision(url, revision): | 172 def mergeRevision(url, revision): |
| 157 paths = getBestMergePaths(url, revision) | 173 paths = getBestMergePaths(url, revision) |
| 158 export_map = getBestExportPathsMap(url, revision) | 174 export_map = getBestExportPathsMap(url, revision) |
| 159 | 175 |
| 160 for path in paths: | 176 for path in paths: |
| 161 if export_map.has_key(path): | 177 if export_map.has_key(path): |
| 162 continue | 178 continue |
| 163 command = ('svn merge -N -r ' + str(revision-1) + ":" + str(revision) + " ") | 179 command = ('svn merge -N -r ' + str(revision-1) + ":" + str(revision) + " ") |
| 164 command = command + url + path + "@" + str(revision) + " ." + path | 180 command = command + url + path + "@" + str(revision) + " ." + path |
| 165 | 181 |
| 166 print command | 182 print command |
| 167 os.system(command) | 183 os.system(command) |
| 168 | 184 |
| 169 def exportRevision(url, revision): | 185 def exportRevision(url, revision): |
| 170 paths = getBestExportPathsMap(url, revision).keys() | 186 paths = getBestExportPathsMap(url, revision).keys() |
| 171 | |
| 172 paths.sort() | 187 paths.sort() |
| 173 | 188 |
| 174 for path in paths: | 189 for path in paths: |
| 175 command = ('svn export -N ' + url + path + "@" + str(revision) + " ." | 190 command = ('svn export -N ' + url + path + "@" + str(revision) + " ." + |
| 176 + path) | 191 path) |
| 177 print command | 192 print command |
| 178 os.system(command) | 193 os.system(command) |
| 179 | 194 |
| 180 command = ('svn add .' + path) | 195 command = 'svn add .' + path |
| 181 print command | 196 print command |
| 182 os.system(command) | 197 os.system(command) |
| 183 | 198 |
| 184 def deleteRevision(url, revision): | 199 def deleteRevision(url, revision): |
| 185 paths = getBestDeletePathsMap(url, revision).keys() | 200 paths = getBestDeletePathsMap(url, revision).keys() |
| 186 paths.sort() | 201 paths.sort() |
| 187 paths.reverse() | 202 paths.reverse() |
| 188 | 203 |
| 189 for path in paths: | 204 for path in paths: |
| 190 command = ("svn delete ." + path) | 205 command = "svn delete ." + path |
| 191 print command | 206 print command |
| 192 os.system(command) | 207 os.system(command) |
| 193 | 208 |
| 194 | |
| 195 def revertExportRevision(url, revision): | 209 def revertExportRevision(url, revision): |
| 196 paths = getBestExportPathsMap(url, revision).keys() | 210 paths = getBestExportPathsMap(url, revision).keys() |
| 197 paths.sort() | 211 paths.sort() |
| 198 paths.reverse() | 212 paths.reverse() |
| 199 | 213 |
| 200 for path in paths: | 214 for path in paths: |
| 201 command = ("svn delete ." + path) | 215 command = "svn delete ." + path |
| 202 print command | 216 print command |
| 203 os.system(command) | 217 os.system(command) |
| 204 | 218 |
| 205 def revertRevision(url, revision): | 219 def revertRevision(url, revision): |
| 206 paths = getBestMergePaths(url, revision) | 220 paths = getBestMergePaths(url, revision) |
| 207 for path in paths: | 221 for path in paths: |
| 208 command = ('svn merge -N -r ' + str(revision) + ":" + str(revision-1) + | 222 command = ('svn merge -N -r ' + str(revision) + ":" + str(revision-1) + |
| 209 " " + url + path + " ." + path) | 223 " " + url + path + " ." + path) |
| 210 print command | 224 print command |
| 211 os.system(command) | 225 os.system(command) |
| 212 | 226 |
| 213 def getFileInfo(url, revision): | 227 def getFileInfo(url, revision): |
| 214 global files_info_, file_pattern_ | 228 global files_info_, file_pattern_ |
| 215 | 229 |
| 216 if (files_info_ != None): | 230 if (files_info_ != None): |
| 217 return files_info_ | 231 return files_info_ |
| 218 | 232 |
| 219 command = 'svn log ' + url + " -r " + str(revision) + " -v" | 233 command = 'svn log ' + url + " -r " + str(revision) + " -v" |
| 220 svn_log = subprocess.Popen(command, | 234 svn_log = subprocess.Popen(command, |
| 221 shell=True, | 235 shell=True, |
| 222 stdout=subprocess.PIPE, | 236 stdout=subprocess.PIPE, |
| 223 stderr=subprocess.PIPE).stdout.readlines() | 237 stderr=subprocess.PIPE).stdout.readlines() |
| 224 | 238 |
| 225 rtn = [] | 239 info = [] |
| 226 for line in svn_log: | 240 for line in svn_log: |
| 227 #A workaround to dump the (from .*) stuff, regex not so friendly in the 2nd | 241 # A workaround to dump the (from .*) stuff, regex not so friendly in the 2nd |
| 228 #pass... | 242 # pass... |
| 229 match = re.search(r"(.*) \(from.*\)", line) | 243 match = re.search(r"(.*) \(from.*\)", line) |
| 230 if match: | 244 if match: |
| 231 line = match.group(1) | 245 line = match.group(1) |
| 232 match = re.search(file_pattern_, line) | 246 match = re.search(file_pattern_, line) |
| 233 if match: | 247 if match: |
| 234 rtn.append([match.group(1).strip(), match.group(2).strip(), | 248 info.append([match.group(1).strip(), match.group(2).strip(), |
| 235 match.group(3).strip(),match.group(4).strip()]) | 249 match.group(3).strip(),match.group(4).strip()]) |
| 236 | 250 |
| 237 files_info_ = rtn | 251 files_info_ = info |
| 238 return rtn | 252 return rtn |
| 239 | 253 |
| 240 def getBestMergePaths(url, revision): | 254 def getBestMergePaths(url, revision): |
| 241 """ | 255 """Takes an svn url and gets the associated revision.""" |
| 242 Takes an svn url and gets the associated revision. | |
| 243 """ | |
| 244 return getBestMergePaths2(getFileInfo(url, revision), revision) | 256 return getBestMergePaths2(getFileInfo(url, revision), revision) |
| 245 | 257 |
| 246 def getBestMergePaths2(files_info, revision): | 258 def getBestMergePaths2(files_info, revision): |
| 247 """ | 259 """Takes an svn url and gets the associated revision.""" |
| 248 Takes an svn url and gets the associated revision. | |
| 249 """ | |
| 250 | 260 |
| 251 map = dict() | 261 map = dict() |
| 252 for file_info in files_info: | 262 for file_info in files_info: |
| 253 map[file_info[2]] = file_info[2] | 263 map[file_info[2]] = file_info[2] |
| 254 | 264 |
| 255 return map.keys() | 265 return map.keys() |
| 256 | 266 |
| 257 def getBestExportPathsMap(url, revision): | 267 def getBestExportPathsMap(url, revision): |
| 258 return getBestExportPathsMap2(getFileInfo(url, revision), revision) | 268 return getBestExportPathsMap2(getFileInfo(url, revision), revision) |
| 259 | 269 |
| 260 def getBestExportPathsMap2(files_info, revision): | 270 def getBestExportPathsMap2(files_info, revision): |
| 261 """ | 271 """Takes an svn url and gets the associated revision.""" |
| 262 Takes an svn url and gets the associated revision. | |
| 263 """ | |
| 264 global export_map_ | 272 global export_map_ |
| 265 | 273 |
| 266 if export_map_: | 274 if export_map_: |
| 267 return export_map_ | 275 return export_map_ |
| 268 | 276 |
| 269 map = dict() | 277 map = dict() |
| 270 for file_info in files_info: | 278 for file_info in files_info: |
| 271 if (file_info[0] == "A"): | 279 if (file_info[0] == "A"): |
| 272 if(isSVNDirectory("svn://chrome-svn/chrome/" + file_info[1], revision)): | 280 if(isSVNDirectory("svn://chrome-svn/chrome/" + file_info[1], revision)): |
| 273 map[file_info[2] + "/" + file_info[3]] = "" | 281 map[file_info[2] + "/" + file_info[3]] = "" |
| 274 | 282 |
| 275 export_map_ = map | 283 export_map_ = map |
| 276 | |
| 277 return map | 284 return map |
| 278 | 285 |
| 279 def getBestDeletePathsMap(url, revision): | 286 def getBestDeletePathsMap(url, revision): |
| 280 return getBestDeletePathsMap2(getFileInfo(url, revision), revision) | 287 return getBestDeletePathsMap2(getFileInfo(url, revision), revision) |
| 281 | 288 |
| 282 def getBestDeletePathsMap2(files_info, revision): | 289 def getBestDeletePathsMap2(files_info, revision): |
| 283 """ | 290 """Takes an svn url and gets the associated revision.""" |
| 284 Takes an svn url and gets the associated revision. | |
| 285 """ | |
| 286 global delete_map_ | 291 global delete_map_ |
| 287 | 292 |
| 288 if delete_map_: | 293 if delete_map_: |
| 289 return delete_map_ | 294 return delete_map_ |
| 290 | 295 |
| 291 map = dict() | 296 map = dict() |
| 292 for file_info in files_info: | 297 for file_info in files_info: |
| 293 if (file_info[0] == "D"): | 298 if (file_info[0] == "D"): |
| 294 if(isSVNDirectory("svn://chrome-svn/chrome/" + file_info[1], revision)): | 299 if(isSVNDirectory("svn://chrome-svn/chrome/" + file_info[1], revision)): |
| 295 map[file_info[2] + "/" + file_info[3]] = "" | 300 map[file_info[2] + "/" + file_info[3]] = "" |
| 296 | 301 |
| 297 delete_map_ = map | 302 delete_map_ = map |
| 298 | |
| 299 return map | 303 return map |
| 300 | 304 |
| 301 def getExistingFilesInRevision(files_info): | 305 def getExistingFilesInRevision(files_info): |
| 302 """ | 306 """Checks for existing files in the revision. |
| 303 Checks for existing files in the revision, anything that's A will require | 307 |
| 304 special treatment (either a merge or an export + add) | 308 Anything that's A will require special treatment (either a merge or an |
| 309 export + add) | |
| 305 """ | 310 """ |
| 306 map = [] | 311 map = [] |
| 307 for file_info in files_info: | 312 for file_info in files_info: |
| 308 if file_info[0] != "A": | 313 if file_info[0] != "A": |
| 309 map.append(file_info[2] + "/" + file_info[3]) | 314 map.append(file_info[2] + "/" + file_info[3]) |
| 310 | 315 |
| 311 return map | 316 return map |
| 312 | 317 |
| 313 def getAllFilesInRevision(files_info): | 318 def getAllFilesInRevision(files_info): |
| 314 """ | 319 """Checks for existing files in the revision. |
| 315 Checks for existing files in the revision, anything that's A will require | 320 |
| 316 special treatment (either a merge or an export + add) | 321 Anything that's A will require special treatment (either a merge or an |
| 322 export + add) | |
| 317 """ | 323 """ |
| 318 map = [] | 324 map = [] |
| 319 for file_info in files_info: | 325 for file_info in files_info: |
| 320 map.append(file_info[2] + "/" + file_info[3]) | 326 map.append(file_info[2] + "/" + file_info[3]) |
| 321 | |
| 322 return map | 327 return map |
| 323 | 328 |
| 324 def prompt(question): | 329 def prompt(question): |
| 325 p = None | 330 answer = None |
| 326 | 331 |
| 327 while not p: | 332 while not p: |
| 328 print question + " [y|n]:" | 333 print question + " [y|n]:" |
| 329 p = sys.stdin.readline() | 334 answer = sys.stdin.readline() |
| 330 if p.lower().startswith('n'): | 335 if answer.lower().startswith('n'): |
| 331 return False | 336 return False |
| 332 elif p.lower().startswith('y'): | 337 elif answer.lower().startswith('y'): |
| 333 return True | 338 return True |
| 334 else: | 339 else: |
| 335 p = None | 340 answer = None |
| 336 | 341 |
| 337 def text_prompt(question, default): | 342 def text_prompt(question, default): |
| 338 print question + " [" + default + "]:" | 343 print question + " [" + default + "]:" |
| 339 p = sys.stdin.readline() | 344 answer = sys.stdin.readline() |
| 340 if p.strip() == "": | 345 if answer.strip() == "": |
| 341 return default | 346 return default |
| 342 return p | 347 return answer |
| 343 | 348 |
| 344 def main(argv=None): | 349 def main(argv=None): |
| 345 BASE_URL = "svn://chrome-svn/chrome" | 350 BASE_URL = "svn://chrome-svn/chrome" |
| 346 TRUNK_URL = BASE_URL + "/trunk/src" | 351 TRUNK_URL = BASE_URL + "/trunk/src" |
| 347 BRANCH_URL = BASE_URL + "/branches/$branch/src" | 352 BRANCH_URL = BASE_URL + "/branches/$branch/src" |
| 348 DEFAULT_WORKING = "working" | 353 DEFAULT_WORKING = "working" |
| 349 SKIP_CHECK_WORKING = True | 354 SKIP_CHECK_WORKING = True |
| 350 PROMPT_FOR_AUTHOR = False | 355 PROMPT_FOR_AUTHOR = False |
| 351 | 356 |
| 357 # Override the default properties if there is a drover.properties file. | |
| 352 global file_pattern_ | 358 global file_pattern_ |
| 353 if os.path.exists("drover.properties"): | 359 if os.path.exists("drover.properties"): |
| 354 file = open("drover.properties") | 360 file = open("drover.properties") |
| 355 exec(file) | 361 exec(file) |
| 356 file.close() | 362 file.close() |
| 357 if FILE_PATTERN: | 363 if FILE_PATTERN: |
| 358 file_pattern_ = FILE_PATTERN | 364 file_pattern_ = FILE_PATTERN |
| 359 | 365 |
| 360 if (len(sys.argv) == 1): | 366 if (len(sys.argv) == 1): |
| 361 print "WARNING: Please use this tool in an empty directory (or at least one" | 367 print USAGE % {"app": sys.argv[0]} |
| 362 print "that you don't mind clobbering." | |
| 363 print "REQUIRES: SVN 1.5+" | |
| 364 print "NOTE: NO NEED TO CHECKOUT ANYTHING IN ADVANCE OF USING THIS TOOL." | |
| 365 print "\nValid parameters:" | |
| 366 print "\n[Merge from trunk to branch]" | |
| 367 print "<revision> --merge <branch_num>" | |
| 368 print "Example " + sys.argv[0] + " 12345 --merge 187" | |
| 369 print "\n[Merge from trunk to branch, ignoring revision history]" | |
| 370 print "<revision> --mplus <branch_num>" | |
| 371 print "Example " + sys.argv[0] + " 12345 --mplus 187" | |
| 372 print "\n[Revert from trunk]" | |
| 373 print " <revision> --revert" | |
| 374 print "Example " + sys.argv[0] + " 12345 --revert" | |
| 375 print "\n[Revert from branch]" | |
| 376 print " <revision> --revert <branch_num>" | |
| 377 print "Example " + sys.argv[0] + " 12345 --revert 187" | |
| 378 sys.exit(0) | 368 sys.exit(0) |
| 379 | 369 |
| 380 revision = int(sys.argv[1]) | 370 revision = int(sys.argv[1]) |
| 381 if ((len(sys.argv) >= 4) and (sys.argv[2] in ['--revert','-r'])): | 371 if ((len(sys.argv) >= 4) and (sys.argv[2] in ['--revert','-r'])): |
| 382 url = BRANCH_URL.replace("$branch", sys.argv[3]) | 372 url = BRANCH_URL.replace("$branch", sys.argv[3]) |
| 383 else: | 373 else: |
| 384 url = TRUNK_URL | 374 url = TRUNK_URL |
| 385 action = "Merge" | 375 action = "Merge" |
| 386 | 376 |
| 387 working = DEFAULT_WORKING | 377 working = DEFAULT_WORKING |
| 388 | 378 |
| 389 command = 'svn log ' + url + " -r "+str(revision) + " -v" | 379 command = 'svn log ' + url + " -r "+str(revision) + " -v" |
| 390 os.system(command) | 380 os.system(command) |
| 391 | 381 |
| 392 if not prompt("Is this the correct revision?"): | 382 if not prompt("Is this the correct revision?"): |
| 393 sys.exit(0) | 383 sys.exit(0) |
| 394 | 384 |
| 395 if (os.path.exists(working)): | 385 if (os.path.exists(working)): |
| 396 if not (SKIP_CHECK_WORKING or prompt("Working directory: '" + working + "' a lready exists, clobber?")): | 386 if not (SKIP_CHECK_WORKING or prompt("Working directory: '" + working + "' a lready exists, clobber?")): |
| 397 sys.exit(0) | 387 sys.exit(0) |
| 398 deltree(working) | 388 deltree(working) |
| 399 | 389 |
| 400 os.makedirs(working) | 390 os.makedirs(working) |
| 401 os.chdir(working) | 391 os.chdir(working) |
| 402 | 392 |
| 403 if (len(sys.argv) > 1): | 393 if (len(sys.argv) > 1): |
| 404 if sys.argv[2] in ['--merge','-m']: | 394 if sys.argv[2] in ['--merge','-m']: |
| 405 if (len(sys.argv) != 4): | 395 if (len(sys.argv) != 4): |
| 406 print "Please specify the branch # you want (i.e. 182) after --merge" | 396 print "Please specify the branch # you want (i.e. 182) after --merge" |
| 407 sys.exit(0) | 397 sys.exit(0) |
| 408 | 398 |
| 409 branch_url = BRANCH_URL.replace("$branch", sys.argv[3]) | 399 branch_url = BRANCH_URL.replace("$branch", sys.argv[3]) |
| 410 #Checkout everything but stuff that got added into a new dir | 400 # Checkout everything but stuff that got added into a new dir |
| 411 checkoutRevision(url, revision, branch_url) | 401 checkoutRevision(url, revision, branch_url) |
| 412 #Merge everything that changed | 402 # Merge everything that changed |
| 413 mergeRevision(url, revision) | 403 mergeRevision(url, revision) |
| 414 #"Export" files that were added from the source and add them to branch | 404 # "Export" files that were added from the source and add them to branch |
| 415 exportRevision(url, revision) | 405 exportRevision(url, revision) |
| 416 #Delete directories that were deleted (file deletes are handled in the | 406 # Delete directories that were deleted (file deletes are handled in the |
| 417 #merge). | 407 # merge). |
| 418 deleteRevision(url, revision) | 408 deleteRevision(url, revision) |
| 419 elif sys.argv[2] in ['--revert','-r']: | 409 elif sys.argv[2] in ['--revert','-r']: |
| 420 if (len(sys.argv) == 4): | 410 if (len(sys.argv) == 4): |
| 421 url = BRANCH_URL.replace("$branch", sys.argv[3]) | 411 url = BRANCH_URL.replace("$branch", sys.argv[3]) |
| 422 checkoutRevision(url, revision, url, True) | 412 checkoutRevision(url, revision, url, True) |
| 423 revertRevision(url, revision) | 413 revertRevision(url, revision) |
| 424 revertExportRevision(url, revision) | 414 revertExportRevision(url, revision) |
| 425 action = "Revert" | 415 action = "Revert" |
| 426 else: | 416 else: |
| 427 print "Unknown parameter " + sys.argv[2] | 417 print "Unknown parameter " + sys.argv[2] |
| 428 sys.exit(0) | 418 sys.exit(0) |
| 429 | 419 |
| 430 #Check the base url so we actually find the author who made the change | 420 # Check the base url so we actually find the author who made the change |
| 431 author = getAuthor(TRUNK_URL, revision) | 421 author = getAuthor(TRUNK_URL, revision) |
| 432 | 422 |
| 433 filename = str(revision)+".txt" | 423 filename = str(revision)+".txt" |
| 434 out = open(filename,"w") | 424 out = open(filename,"w") |
| 435 out.write(action +" " + str(revision) + " - ") | 425 out.write(action +" " + str(revision) + " - ") |
| 436 out.write(getRevisionLog(url, revision)) | 426 out.write(getRevisionLog(url, revision)) |
| 437 if (author): | 427 if (author): |
| 438 out.write("TBR=" + author) | 428 out.write("TBR=" + author) |
| 439 out.close() | 429 out.close() |
| 440 | 430 |
| 441 os.system('gcl change ' + str(revision) + " " + filename) | 431 os.system('gcl change ' + str(revision) + " " + filename) |
| 442 os.unlink(filename) | 432 os.unlink(filename) |
| 443 print author | 433 print author |
| 444 print revision | 434 print revision |
| 445 print ("gcl upload " + str(revision) + | 435 print ("gcl upload " + str(revision) + |
| 446 " --send_mail --no_try --no_presubmit --reviewers=" + author) | 436 " --send_mail --no_try --no_presubmit --reviewers=" + author) |
| 447 print "gcl commit " + str(revision) + " --no_presubmit --force" | 437 print "gcl commit " + str(revision) + " --no_presubmit --force" |
| 448 print "gcl delete " + str(revision) | 438 print "gcl delete " + str(revision) |
| 449 | 439 |
| 450 if prompt("Would you like to upload?"): | 440 if prompt("Would you like to upload?"): |
| 451 if PROMPT_FOR_AUTHOR: | 441 if PROMPT_FOR_AUTHOR: |
| 452 author = text_prompt("Enter a new author or press enter to accept default" , author) | 442 author = text_prompt("Enter a new author or press enter to accept default" , author) |
| 453 gclUpload(revision, author) | 443 gclUpload(revision, author) |
| 454 else: | 444 else: |
| 455 print "Deleting the changelist." | 445 print "Deleting the changelist." |
| 456 os.system("gcl delete " + str(revision)) | 446 os.system("gcl delete " + str(revision)) |
| 457 sys.exit(0) | 447 sys.exit(0) |
| 458 | 448 |
| 459 if prompt("Would you like to commit?"): | 449 if prompt("Would you like to commit?"): |
| 460 os.system("gcl commit " + str(revision) + " --no_presubmit --force") | 450 os.system("gcl commit " + str(revision) + " --no_presubmit --force") |
| 461 else: | 451 else: |
| 462 sys.exit(0) | 452 sys.exit(0) |
| 463 | 453 |
| 464 if __name__ == "__main__": | 454 if __name__ == "__main__": |
| 465 sys.exit(main()) | 455 sys.exit(main()) |
| OLD | NEW |