@ -15,9 +15,11 @@ import re
import trychange
import trychange
def Backquote(cmd):
def Backquote(cmd, cwd=None ):
"""Like running `cmd` in a shell script."""
"""Like running `cmd` in a shell script."""
return subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0].strip()
return subprocess.Popen(cmd,
cwd=cwd,
stdout=subprocess.PIPE).communicate()[0].strip()
def GetTryServerConfig():
def GetTryServerConfig():
@ -37,18 +39,18 @@ def GetTryServerConfig():
return locals
return locals
def GetBranchName():
def GetBranchName(working_dir=None ):
"""Return name of current git branch."""
"""Return name of current git branch."""
branch = Backquote(['git', 'symbolic-ref', 'HEAD'])
branch = Backquote(['git', 'symbolic-ref', 'HEAD'], working_dir )
if not branch.startswith('refs/heads/'):
if not branch.startswith('refs/heads/'):
raise "Couldn't figure out branch name"
raise "Couldn't figure out branch name"
branch = branch[len('refs/heads/'):]
branch = branch[len('refs/heads/'):]
return branch
return branch
def GetPatchName():
def GetPatchName(working_dir=None ):
"""Construct a name for this patch."""
"""Construct a name for this patch."""
short_sha = Backquote(['git', 'rev-parse', '--short=4', 'HEAD'])
short_sha = Backquote(['git', 'rev-parse', '--short=4', 'HEAD'], working_dir )
return GetBranchName() + '-' + short_sha
return GetBranchName() + '-' + short_sha
@ -61,26 +63,59 @@ def GetRietveldPatchsetNumber():
return Backquote(['git', 'config',
return Backquote(['git', 'config',
'branch.%s.rietveldpatchset' % GetBranchName()])
'branch.%s.rietveldpatchset' % GetBranchName()])
def GetSubRepWorkingDir(sub_rep_path):
"""Computes the path to the sub repository"""
if sub_rep_path:
root_dir = os.path.abspath(Backquote(['git', 'rev-parse', '--show-cdup']))
return os.path.join(root_dir, sub_rep_path)
return None
def GetMungedDiff(branch, prefix='src/'):
def GetMungedDiff(branch, prefix, sub_rep_path):
"""Get the diff we'll send to the try server. We munge paths to match svn."""
"""Get the diff we'll send to the try server. We munge paths to match svn.
We add the prefix that the try bot is expecting. If sub_rep_path is
provided, diff will be calculated in the sub repository."""
# Make the following changes:
# Make the following changes:
# - Prepend "src/" (or some other prefix) to paths as svn is expecting
# - Prepend "src/" (or some other prefix) to paths as svn is expecting
# - In the case of added files, replace /dev/null with the path to the file
# - In the case of added files, replace /dev/null with the path to the file
# being added.
# being added.
cwd = GetSubRepWorkingDir(sub_rep_path)
output = []
output = []
if not branch:
if not branch:
# Try to guess the upstream branch.
# Try to guess the upstream branch.
branch = Backquote(['git', 'cl', 'upstream'])
branch = Backquote(['git', 'cl', 'upstream'], cwd)
diff = subprocess.Popen(['git', 'diff-tree', '-p', '--no-prefix',
command = ['git', 'diff-tree', '-p']
branch, 'HEAD'],
stdout=subprocess.PIPE).stdout.readlines()
new_cwd = None
if not sub_rep_path:
command.extend(['--no-prefix'])
else:
# Append /
sub_rep_path = os.path.join(sub_rep_path, '')
# Add the right prefix
command.extend(['--src-prefix=' + sub_rep_path])
command.extend(['--dst-prefix=' + sub_rep_path])
command.extend([branch, 'HEAD'])
# Run diff tree
diff = subprocess.Popen(command,
stdout=subprocess.PIPE,
cwd=cwd).stdout.readlines()
# Replace --- /dev/null with --- <new file name>
for i in range(len(diff)):
for i in range(len(diff)):
line = diff[i]
line = diff[i]
if line.startswith('--- /dev/null'):
if line.startswith('--- /dev/null'):
line = '--- %s' % prefix + diff[i+1][4:]
line = '--- %s' % diff[i+1][4:]
elif line.startswith('--- ') or line.startswith('+++ '):
output.append(line)
line = line[0:4] + prefix + line[4:]
diff = output
# Add root prefix
output = []
for line in diff:
if line.startswith('--- ') or line.startswith('+++ '):
line = line[0:4] + os.path.join(prefix,line[4:])
output.append(line)
output.append(line)
munged_diff = ''.join(output)
munged_diff = ''.join(output)
@ -89,6 +124,20 @@ def GetMungedDiff(branch, prefix='src/'):
return munged_diff
return munged_diff
def OneRepositoryDiff(diff_file, patch_names, branch, prefix, sub_rep_path):
"""Computes a diff for one git repository at a given path against a given
branch. Writes the diff into diff_file and appends a name to the
patch_names list."""
diff = GetMungedDiff(branch, prefix, sub_rep_path)
# Write the diff out
diff_file.write(diff)
# Add patch name to list of patches
patch_name = GetPatchName(GetSubRepWorkingDir(sub_rep_path))
patch_names.extend([patch_name])
def ValidEmail(email):
def ValidEmail(email):
return re.match(r"^[a-zA-Z0-9._%-+]+@[a-zA-Z0-9._%-]+.[a-zA-Z]{2,6}$", email)
return re.match(r"^[a-zA-Z0-9._%-+]+@[a-zA-Z0-9._%-]+.[a-zA-Z]{2,6}$", email)
@ -119,26 +168,55 @@ if __name__ == '__main__':
help="Make the try run use be a clobber build")
help="Make the try run use be a clobber build")
parser.add_option("-r", "--revision",
parser.add_option("-r", "--revision",
help="Specify the SVN base revision to use")
help="Specify the SVN base revision to use")
parser.add_option("--root", default="src", metavar="PATH",
help="Specify the root prefix that is appended to paths "
"in the patch")
parser.add_option("--dry_run", action="store_true",
help="Print the diff but don't send it to the try bots")
parser.add_option("--sub_rep", nargs=2, action="append", default=[],
metavar="PATH BRANCH",
help="Specify a path to a git sub-repository and a branch "
"to diff with in order to simultanously try changes "
"in multiple git repositories. Option may be "
"specified multiple times.")
parser.add_option("--webkit", metavar="BRANCH",
help="Specify webkit branch. Syntactic sugar for "
"--sub_rep third_party/WebKit/ <branch>")
(options, args) = parser.parse_args(sys.argv)
(options, args) = parser.parse_args(sys.argv)
if options.webkit:
options.sub_rep.extend([('third_party/WebKit/', options.webkit)])
branch = None
branch = None
if len(args) > 1:
if len(args) > 1:
branch = args[1]
branch = args[1]
patch_names = []
patch_name = GetPatchName()
# Dump all diffs into one diff file.
diff = GetMungedDiff(branch)
# Write the diff out to a temporary file
diff_file = tempfile.NamedTemporaryFile()
diff_file = tempfile.NamedTemporaryFile()
diff_file.write(diff)
# Calculate diff for main git repository.
OneRepositoryDiff(diff_file, patch_names, branch, options.root, None)
# Calculate diff for each extra git repository.
for path_and_branch in options.sub_rep:
OneRepositoryDiff(diff_file,
patch_names,
path_and_branch[1],
options.root,
path_and_branch[0])
# Make diff file ready for reading.
diff_file.flush()
diff_file.flush()
# Concatenate patch names
# Prepare args for TryChange
email = GetEmail()
email = GetEmail()
user = email.partition('@')[0]
user = email.partition('@')[0]
args = [
args = [
'-u', user,
'-u', user,
'-e', email,
'-e', email,
'-n', patch_name,
'-n', '-'.join( patch_names) ,
'--diff', diff_file.name,
'--diff', diff_file.name,
]
]
@ -146,7 +224,7 @@ if __name__ == '__main__':
# upload via SVN.
# upload via SVN.
config = GetTryServerConfig()
config = GetTryServerConfig()
if config is not None:
if config is not None:
sendmsg = "Sending %s using HTTP..." % patch_name
sendmsg = "Sending %s using HTTP..." % '-'.join( patch_names)
args.extend(['--use_http'])
args.extend(['--use_http'])
if config['try_server_http_host'] is not None:
if config['try_server_http_host'] is not None:
args.extend(['--host', config['try_server_http_host']])
args.extend(['--host', config['try_server_http_host']])
@ -174,5 +252,9 @@ if __name__ == '__main__':
'--patchset', GetRietveldPatchsetNumber(),
'--patchset', GetRietveldPatchsetNumber(),
])
])
if options.dry_run:
print open(diff_file.name, 'r').read()
exit(0)
print sendmsg
print sendmsg
TryChange(args=args)
TryChange(args=args)