Commit a4899042 authored by nsylvain@chromium.org's avatar nsylvain@chromium.org

Make drover look a little bit more like the rest of

the python code we have. 90% of the changes are removing
trailing spaces.

Review URL: http://codereview.chromium.org/282014

git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@29312 0039d316-1c4b-4281-b951-d872f2087c98
parent 5ab676cf
...@@ -2,129 +2,145 @@ ...@@ -2,129 +2,145 @@
# Use of this source code is governed by a BSD-style license that can be # Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file. # found in the LICENSE file.
import optparse
import os
import re
import subprocess import subprocess
import sys import sys
import re
import os
import webbrowser import webbrowser
USAGE = """
WARNING: Please use this tool in an empty directory
(or at least one that you don't mind clobbering.)
REQUIRES: SVN 1.5+
NOTE: NO NEED TO CHECKOUT ANYTHING IN ADVANCE OF USING THIS TOOL."
Valid parameters:
[Merge from trunk to branch]
<revision> --merge <branch_num>
Example: %(app)s 12345 --merge 187
[Merge from trunk to branch, ignoring revision history]
<revision> --mplus <branch_num>
Example: %(app)s 12345 --mplus 187
[Revert from trunk]
<revision> --revert
Example: %(app)s 12345 --revert
[Revert from branch]
<revision> --revert <branch_num>
Example: %(app)s 12345 --revert 187
"""
export_map_ = None export_map_ = None
files_info_ = None files_info_ = None
delete_map_ = None delete_map_ = None
file_pattern_ = r"[ ]+([MADUC])[ ]+/((?:trunk|branches/\d+)/src(.*)/(.*))" file_pattern_ = r"[ ]+([MADUC])[ ]+/((?:trunk|branches/\d+)/src(.*)/(.*))"
def deltree(root): def deltree(root):
""" """Removes a given directory"""
Removes a given directory
"""
if (not os.path.exists(root)): if (not os.path.exists(root)):
return return
if sys.platform == 'win32': if sys.platform == 'win32':
os.system('rmdir /S /Q ' + root.replace('/','\\')) os.system('rmdir /S /Q ' + root.replace('/','\\'))
else: else:
for name in os.listdir(root): for name in os.listdir(root):
path = os.path.join(root, name) path = os.path.join(root, name)
if os.path.isdir(path): if os.path.isdir(path):
deltree(path) deltree(path)
else: else:
os.unlink(path) os.unlink(path)
os.rmdir(root) os.rmdir(root)
def clobberDir(dir): def clobberDir(dir):
""" """Removes a given directory"""
Removes a given directory
"""
if (os.path.exists(dir)): if (os.path.exists(dir)):
print dir + " directory found, deleting" print dir + " directory found, deleting"
#The following line was removed due to access controls in Windows # The following line was removed due to access controls in Windows
#which make os.unlink(path) calls impossible. # which make os.unlink(path) calls impossible.
#deltree(dir) #TODO(laforge) : Is this correct?
os.system('rmdir /S /Q ' + dir.replace('/','\\')) deltree(dir)
def gclUpload(revision, author): def gclUpload(revision, author):
command = ("gcl upload " + str(revision) + command = ("gcl upload " + str(revision) +
" --send_mail --no_try --no_presubmit --reviewers=" + author) " --send_mail --no_try --no_presubmit --reviewers=" + author)
os.system(command) os.system(command)
def getSVNInfo(url, revision): def getSVNInfo(url, revision):
command = 'svn info ' + url + "@"+str(revision) command = 'svn info ' + url + "@"+str(revision)
svn_info = subprocess.Popen(command, svn_info = subprocess.Popen(command,
shell=True, shell=True,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE).stdout.readlines() stderr=subprocess.PIPE).stdout.readlines()
rtn = {} info = {}
for line in svn_info: for line in svn_info:
match = re.search(r"(.*?):(.*)", line) match = re.search(r"(.*?):(.*)", line)
if match: if match:
rtn[match.group(1).strip()]=match.group(2).strip() info[match.group(1).strip()]=match.group(2).strip()
return rtn return info
def getAuthor(url, revision): def getAuthor(url, revision):
info = getSVNInfo(url, revision) info = getSVNInfo(url, revision)
if (info.has_key("Last Changed Author")): if (info.has_key("Last Changed Author")):
return info["Last Changed Author"] return info["Last Changed Author"]
return None return None
def isSVNFile(url, revision): def isSVNFile(url, revision):
info = getSVNInfo(url, revision) info = getSVNInfo(url, revision)
if (info.has_key("Node Kind")): if (info.has_key("Node Kind")):
if (info["Node Kind"] == "file"): return True if (info["Node Kind"] == "file"):
return True
return False return False
def isSVNDirectory(url, revision): def isSVNDirectory(url, revision):
info = getSVNInfo(url, revision) info = getSVNInfo(url, revision)
if (info.has_key("Node Kind")): if (info.has_key("Node Kind")):
if (info["Node Kind"] == "directory"): return True if (info["Node Kind"] == "directory"):
return True
return False return False
def getRevisionLog(url, revision): def getRevisionLog(url, revision):
""" """Takes an svn url and gets the associated revision."""
Takes an svn url and gets the associated revision.
"""
command = 'svn log ' + url + " -r"+str(revision) command = 'svn log ' + url + " -r"+str(revision)
svn_info = subprocess.Popen(command, svn_log = subprocess.Popen(command,
shell=True, shell=True,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE).stdout.readlines() stderr=subprocess.PIPE).stdout.readlines()
rtn= "" log = ""
pos = 0 pos = 0
for line in svn_info: for line in svn_log:
if (pos > 2): if (pos > 2):
rtn += line.replace('-','').replace('\r','') log += line.replace('-','').replace('\r','')
else: else:
pos = pos + 1 pos = pos + 1
return log
return rtn
def checkoutRevision(url, revision, branch_url, revert=False): def checkoutRevision(url, revision, branch_url, revert=False):
files_info = getFileInfo(url, revision) files_info = getFileInfo(url, revision)
paths = getBestMergePaths2(files_info, revision) paths = getBestMergePaths2(files_info, revision)
export_map = getBestExportPathsMap2(files_info, revision) export_map = getBestExportPathsMap2(files_info, revision)
command = 'svn checkout -N ' + branch_url command = 'svn checkout -N ' + branch_url
print command print command
os.system(command) os.system(command)
match = re.search(r"svn://.*/(.*)", branch_url) match = re.search(r"svn://.*/(.*)", branch_url)
if match: if match:
os.chdir(match.group(1)) os.chdir(match.group(1))
#This line is extremely important due to the way svn behaves in the set-depths # This line is extremely important due to the way svn behaves in the
#action. If parents aren't handled before children, the child directories get # set-depths action. If parents aren't handled before children, the child
#clobbered and the merge step fails. # directories get clobbered and the merge step fails.
paths.sort() paths.sort()
#Checkout the directories that already exist # Checkout the directories that already exist
for path in paths: for path in paths:
if (export_map.has_key(path) and not revert): if (export_map.has_key(path) and not revert):
print "Exclude new directory " + path print "Exclude new directory " + path
...@@ -134,50 +150,49 @@ def checkoutRevision(url, revision, branch_url, revert=False): ...@@ -134,50 +150,49 @@ def checkoutRevision(url, revision, branch_url, revert=False):
base = '' base = ''
for subpath in subpaths: for subpath in subpaths:
base += '/' + subpath base += '/' + subpath
#This logic ensures that you don't empty out any directories # This logic ensures that you don't empty out any directories
if not os.path.exists("." + base): if not os.path.exists("." + base):
command = ('svn update --depth empty ' + "." + base) command = ('svn update --depth empty ' + "." + base)
print command print command
os.system(command) os.system(command)
if (revert): if (revert):
files = getAllFilesInRevision(files_info) files = getAllFilesInRevision(files_info)
else: else:
files = getExistingFilesInRevision(files_info) files = getExistingFilesInRevision(files_info)
for file in files: for file in files:
#Prevent the tool from clobbering the src directory # Prevent the tool from clobbering the src directory
if (file == ""): if (file == ""):
continue continue
command = ('svn up ".' + file + '"') command = ('svn up ".' + file + '"')
print command print command
os.system(command) os.system(command)
def mergeRevision(url, revision): def mergeRevision(url, revision):
paths = getBestMergePaths(url, revision) paths = getBestMergePaths(url, revision)
export_map = getBestExportPathsMap(url, revision) export_map = getBestExportPathsMap(url, revision)
for path in paths: for path in paths:
if export_map.has_key(path): if export_map.has_key(path):
continue continue
command = ('svn merge -N -r ' + str(revision-1) + ":" + str(revision) + " ") command = ('svn merge -N -r ' + str(revision-1) + ":" + str(revision) + " ")
command = command + url + path + "@" + str(revision) + " ." + path command = command + url + path + "@" + str(revision) + " ." + path
print command print command
os.system(command) os.system(command)
def exportRevision(url, revision): def exportRevision(url, revision):
paths = getBestExportPathsMap(url, revision).keys() paths = getBestExportPathsMap(url, revision).keys()
paths.sort() paths.sort()
for path in paths: for path in paths:
command = ('svn export -N ' + url + path + "@" + str(revision) + " ." command = ('svn export -N ' + url + path + "@" + str(revision) + " ." +
+ path) path)
print command print command
os.system(command) os.system(command)
command = ('svn add .' + path) command = 'svn add .' + path
print command print command
os.system(command) os.system(command)
...@@ -185,20 +200,19 @@ def deleteRevision(url, revision): ...@@ -185,20 +200,19 @@ def deleteRevision(url, revision):
paths = getBestDeletePathsMap(url, revision).keys() paths = getBestDeletePathsMap(url, revision).keys()
paths.sort() paths.sort()
paths.reverse() paths.reverse()
for path in paths: for path in paths:
command = ("svn delete ." + path) command = "svn delete ." + path
print command print command
os.system(command) os.system(command)
def revertExportRevision(url, revision): def revertExportRevision(url, revision):
paths = getBestExportPathsMap(url, revision).keys() paths = getBestExportPathsMap(url, revision).keys()
paths.sort() paths.sort()
paths.reverse() paths.reverse()
for path in paths: for path in paths:
command = ("svn delete ." + path) command = "svn delete ." + path
print command print command
os.system(command) os.system(command)
...@@ -206,47 +220,43 @@ def revertRevision(url, revision): ...@@ -206,47 +220,43 @@ def revertRevision(url, revision):
paths = getBestMergePaths(url, revision) paths = getBestMergePaths(url, revision)
for path in paths: for path in paths:
command = ('svn merge -N -r ' + str(revision) + ":" + str(revision-1) + command = ('svn merge -N -r ' + str(revision) + ":" + str(revision-1) +
" " + url + path + " ." + path) " " + url + path + " ." + path)
print command print command
os.system(command) os.system(command)
def getFileInfo(url, revision): def getFileInfo(url, revision):
global files_info_, file_pattern_ global files_info_, file_pattern_
if (files_info_ != None): if (files_info_ != None):
return files_info_ return files_info_
command = 'svn log ' + url + " -r " + str(revision) + " -v" command = 'svn log ' + url + " -r " + str(revision) + " -v"
svn_log = subprocess.Popen(command, svn_log = subprocess.Popen(command,
shell=True, shell=True,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE).stdout.readlines() stderr=subprocess.PIPE).stdout.readlines()
rtn = [] info = []
for line in svn_log: for line in svn_log:
#A workaround to dump the (from .*) stuff, regex not so friendly in the 2nd # A workaround to dump the (from .*) stuff, regex not so friendly in the 2nd
#pass... # pass...
match = re.search(r"(.*) \(from.*\)", line) match = re.search(r"(.*) \(from.*\)", line)
if match: if match:
line = match.group(1) line = match.group(1)
match = re.search(file_pattern_, line) match = re.search(file_pattern_, line)
if match: if match:
rtn.append([match.group(1).strip(), match.group(2).strip(), info.append([match.group(1).strip(), match.group(2).strip(),
match.group(3).strip(),match.group(4).strip()]) match.group(3).strip(),match.group(4).strip()])
files_info_ = rtn files_info_ = info
return rtn return rtn
def getBestMergePaths(url, revision): def getBestMergePaths(url, revision):
""" """Takes an svn url and gets the associated revision."""
Takes an svn url and gets the associated revision.
"""
return getBestMergePaths2(getFileInfo(url, revision), revision) return getBestMergePaths2(getFileInfo(url, revision), revision)
def getBestMergePaths2(files_info, revision): def getBestMergePaths2(files_info, revision):
""" """Takes an svn url and gets the associated revision."""
Takes an svn url and gets the associated revision.
"""
map = dict() map = dict()
for file_info in files_info: for file_info in files_info:
...@@ -256,13 +266,11 @@ def getBestMergePaths2(files_info, revision): ...@@ -256,13 +266,11 @@ def getBestMergePaths2(files_info, revision):
def getBestExportPathsMap(url, revision): def getBestExportPathsMap(url, revision):
return getBestExportPathsMap2(getFileInfo(url, revision), revision) return getBestExportPathsMap2(getFileInfo(url, revision), revision)
def getBestExportPathsMap2(files_info, revision): def getBestExportPathsMap2(files_info, revision):
""" """Takes an svn url and gets the associated revision."""
Takes an svn url and gets the associated revision.
"""
global export_map_ global export_map_
if export_map_: if export_map_:
return export_map_ return export_map_
...@@ -273,18 +281,15 @@ def getBestExportPathsMap2(files_info, revision): ...@@ -273,18 +281,15 @@ def getBestExportPathsMap2(files_info, revision):
map[file_info[2] + "/" + file_info[3]] = "" map[file_info[2] + "/" + file_info[3]] = ""
export_map_ = map export_map_ = map
return map return map
def getBestDeletePathsMap(url, revision): def getBestDeletePathsMap(url, revision):
return getBestDeletePathsMap2(getFileInfo(url, revision), revision) return getBestDeletePathsMap2(getFileInfo(url, revision), revision)
def getBestDeletePathsMap2(files_info, revision): def getBestDeletePathsMap2(files_info, revision):
""" """Takes an svn url and gets the associated revision."""
Takes an svn url and gets the associated revision.
"""
global delete_map_ global delete_map_
if delete_map_: if delete_map_:
return delete_map_ return delete_map_
...@@ -295,13 +300,13 @@ def getBestDeletePathsMap2(files_info, revision): ...@@ -295,13 +300,13 @@ def getBestDeletePathsMap2(files_info, revision):
map[file_info[2] + "/" + file_info[3]] = "" map[file_info[2] + "/" + file_info[3]] = ""
delete_map_ = map delete_map_ = map
return map return map
def getExistingFilesInRevision(files_info): def getExistingFilesInRevision(files_info):
""" """Checks for existing files in the revision.
Checks for existing files in the revision, anything that's A will require
special treatment (either a merge or an export + add) Anything that's A will require special treatment (either a merge or an
export + add)
""" """
map = [] map = []
for file_info in files_info: for file_info in files_info:
...@@ -311,36 +316,36 @@ def getExistingFilesInRevision(files_info): ...@@ -311,36 +316,36 @@ def getExistingFilesInRevision(files_info):
return map return map
def getAllFilesInRevision(files_info): def getAllFilesInRevision(files_info):
""" """Checks for existing files in the revision.
Checks for existing files in the revision, anything that's A will require
special treatment (either a merge or an export + add) Anything that's A will require special treatment (either a merge or an
export + add)
""" """
map = [] map = []
for file_info in files_info: for file_info in files_info:
map.append(file_info[2] + "/" + file_info[3]) map.append(file_info[2] + "/" + file_info[3])
return map return map
def prompt(question): def prompt(question):
p = None answer = None
while not p: while not p:
print question + " [y|n]:" print question + " [y|n]:"
p = sys.stdin.readline() answer = sys.stdin.readline()
if p.lower().startswith('n'): if answer.lower().startswith('n'):
return False return False
elif p.lower().startswith('y'): elif answer.lower().startswith('y'):
return True return True
else: else:
p = None answer = None
def text_prompt(question, default): def text_prompt(question, default):
print question + " [" + default + "]:" print question + " [" + default + "]:"
p = sys.stdin.readline() answer = sys.stdin.readline()
if p.strip() == "": if answer.strip() == "":
return default return default
return p return answer
def main(argv=None): def main(argv=None):
BASE_URL = "svn://chrome-svn/chrome" BASE_URL = "svn://chrome-svn/chrome"
TRUNK_URL = BASE_URL + "/trunk/src" TRUNK_URL = BASE_URL + "/trunk/src"
...@@ -348,7 +353,8 @@ def main(argv=None): ...@@ -348,7 +353,8 @@ def main(argv=None):
DEFAULT_WORKING = "working" DEFAULT_WORKING = "working"
SKIP_CHECK_WORKING = True SKIP_CHECK_WORKING = True
PROMPT_FOR_AUTHOR = False PROMPT_FOR_AUTHOR = False
# Override the default properties if there is a drover.properties file.
global file_pattern_ global file_pattern_
if os.path.exists("drover.properties"): if os.path.exists("drover.properties"):
file = open("drover.properties") file = open("drover.properties")
...@@ -358,23 +364,7 @@ def main(argv=None): ...@@ -358,23 +364,7 @@ def main(argv=None):
file_pattern_ = FILE_PATTERN file_pattern_ = FILE_PATTERN
if (len(sys.argv) == 1): if (len(sys.argv) == 1):
print "WARNING: Please use this tool in an empty directory (or at least one" print USAGE % {"app": sys.argv[0]}
print "that you don't mind clobbering."
print "REQUIRES: SVN 1.5+"
print "NOTE: NO NEED TO CHECKOUT ANYTHING IN ADVANCE OF USING THIS TOOL."
print "\nValid parameters:"
print "\n[Merge from trunk to branch]"
print "<revision> --merge <branch_num>"
print "Example " + sys.argv[0] + " 12345 --merge 187"
print "\n[Merge from trunk to branch, ignoring revision history]"
print "<revision> --mplus <branch_num>"
print "Example " + sys.argv[0] + " 12345 --mplus 187"
print "\n[Revert from trunk]"
print " <revision> --revert"
print "Example " + sys.argv[0] + " 12345 --revert"
print "\n[Revert from branch]"
print " <revision> --revert <branch_num>"
print "Example " + sys.argv[0] + " 12345 --revert 187"
sys.exit(0) sys.exit(0)
revision = int(sys.argv[1]) revision = int(sys.argv[1])
...@@ -383,39 +373,39 @@ def main(argv=None): ...@@ -383,39 +373,39 @@ def main(argv=None):
else: else:
url = TRUNK_URL url = TRUNK_URL
action = "Merge" action = "Merge"
working = DEFAULT_WORKING working = DEFAULT_WORKING
command = 'svn log ' + url + " -r "+str(revision) + " -v" command = 'svn log ' + url + " -r "+str(revision) + " -v"
os.system(command) os.system(command)
if not prompt("Is this the correct revision?"): if not prompt("Is this the correct revision?"):
sys.exit(0) sys.exit(0)
if (os.path.exists(working)): if (os.path.exists(working)):
if not (SKIP_CHECK_WORKING or prompt("Working directory: '" + working + "' already exists, clobber?")): if not (SKIP_CHECK_WORKING or prompt("Working directory: '" + working + "' already exists, clobber?")):
sys.exit(0) sys.exit(0)
deltree(working) deltree(working)
os.makedirs(working) os.makedirs(working)
os.chdir(working) os.chdir(working)
if (len(sys.argv) > 1): if (len(sys.argv) > 1):
if sys.argv[2] in ['--merge','-m']: if sys.argv[2] in ['--merge','-m']:
if (len(sys.argv) != 4): if (len(sys.argv) != 4):
print "Please specify the branch # you want (i.e. 182) after --merge" print "Please specify the branch # you want (i.e. 182) after --merge"
sys.exit(0) sys.exit(0)
branch_url = BRANCH_URL.replace("$branch", sys.argv[3]) branch_url = BRANCH_URL.replace("$branch", sys.argv[3])
#Checkout everything but stuff that got added into a new dir # Checkout everything but stuff that got added into a new dir
checkoutRevision(url, revision, branch_url) checkoutRevision(url, revision, branch_url)
#Merge everything that changed # Merge everything that changed
mergeRevision(url, revision) mergeRevision(url, revision)
#"Export" files that were added from the source and add them to branch # "Export" files that were added from the source and add them to branch
exportRevision(url, revision) exportRevision(url, revision)
#Delete directories that were deleted (file deletes are handled in the # Delete directories that were deleted (file deletes are handled in the
#merge). # merge).
deleteRevision(url, revision) deleteRevision(url, revision)
elif sys.argv[2] in ['--revert','-r']: elif sys.argv[2] in ['--revert','-r']:
if (len(sys.argv) == 4): if (len(sys.argv) == 4):
url = BRANCH_URL.replace("$branch", sys.argv[3]) url = BRANCH_URL.replace("$branch", sys.argv[3])
...@@ -426,10 +416,10 @@ def main(argv=None): ...@@ -426,10 +416,10 @@ def main(argv=None):
else: else:
print "Unknown parameter " + sys.argv[2] print "Unknown parameter " + sys.argv[2]
sys.exit(0) sys.exit(0)
#Check the base url so we actually find the author who made the change # Check the base url so we actually find the author who made the change
author = getAuthor(TRUNK_URL, revision) author = getAuthor(TRUNK_URL, revision)
filename = str(revision)+".txt" filename = str(revision)+".txt"
out = open(filename,"w") out = open(filename,"w")
out.write(action +" " + str(revision) + " - ") out.write(action +" " + str(revision) + " - ")
...@@ -437,12 +427,12 @@ def main(argv=None): ...@@ -437,12 +427,12 @@ def main(argv=None):
if (author): if (author):
out.write("TBR=" + author) out.write("TBR=" + author)
out.close() out.close()
os.system('gcl change ' + str(revision) + " " + filename) os.system('gcl change ' + str(revision) + " " + filename)
os.unlink(filename) os.unlink(filename)
print author print author
print revision print revision
print ("gcl upload " + str(revision) + print ("gcl upload " + str(revision) +
" --send_mail --no_try --no_presubmit --reviewers=" + author) " --send_mail --no_try --no_presubmit --reviewers=" + author)
print "gcl commit " + str(revision) + " --no_presubmit --force" print "gcl commit " + str(revision) + " --no_presubmit --force"
print "gcl delete " + str(revision) print "gcl delete " + str(revision)
...@@ -460,6 +450,6 @@ def main(argv=None): ...@@ -460,6 +450,6 @@ def main(argv=None):
os.system("gcl commit " + str(revision) + " --no_presubmit --force") os.system("gcl commit " + str(revision) + " --no_presubmit --force")
else: else:
sys.exit(0) sys.exit(0)
if __name__ == "__main__": if __name__ == "__main__":
sys.exit(main()) sys.exit(main())
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment