@ -15,9 +15,11 @@ import re
import trychange
def Backquote(cmd):
def Backquote(cmd, cwd=None ):
"""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():
@ -37,18 +39,18 @@ def GetTryServerConfig():
return locals
def GetBranchName():
def GetBranchName(working_dir=None ):
"""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/'):
raise "Couldn't figure out branch name"
branch = branch[len('refs/heads/'):]
return branch
def GetPatchName():
def GetPatchName(working_dir=None ):
"""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
@ -61,26 +63,59 @@ def GetRietveldPatchsetNumber():
return Backquote(['git', 'config',
'branch.%s.rietveldpatchset' % GetBranchName()])
def GetMungedDiff(branch, prefix='src/'):
"""Get the diff we'll send to the try server. We munge paths to match svn."""
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, sub_rep_path):
"""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:
# - 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
# being added.
cwd = GetSubRepWorkingDir(sub_rep_path)
output = []
if not branch:
# Try to guess the upstream branch.
branch = Backquote(['git', 'cl', 'upstream'])
diff = subprocess.Popen(['git', 'diff-tree', '-p', '--no-prefix',
branch, 'HEAD'],
stdout=subprocess.PIPE).stdout.readlines()
branch = Backquote(['git', 'cl', 'upstream'], cwd)
command = ['git', 'diff-tree', '-p']
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)):
line = diff[i]
if line.startswith('--- /dev/null'):
line = '--- %s' % prefix + diff[i+1][4:]
elif line.startswith('--- ') or line.startswith('+++ '):
line = line[0:4] + prefix + line[4:]
line = '--- %s' % diff[i+1][4:]
output.append(line)
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)
munged_diff = ''.join(output)
@ -89,6 +124,20 @@ def GetMungedDiff(branch, prefix='src/'):
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):
return re.match(r"^[a-zA-Z0-9._%-+]+@[a-zA-Z0-9._%-]+.[a-zA-Z]{2,6}$", email)
@ -119,34 +168,63 @@ if __name__ == '__main__':
help="Make the try run use be a clobber build")
parser.add_option("-r", "--revision",
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)
if options.webkit:
options.sub_rep.extend([('third_party/WebKit/', options.webkit)])
branch = None
if len(args) > 1:
branch = args[1]
patch_names = []
patch_name = GetPatchName()
diff = GetMungedDiff(branch)
# Write the diff out to a temporary file
# Dump all diffs into one diff file.
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()
# Concatenate patch names
# Prepare args for TryChange
email = GetEmail()
user = email.partition('@')[0]
args = [
'-u', user,
'-e', email,
'-n', patch_name,
'--diff', diff_file.name,
'-n', '-'.join( patch_names) ,
'--diff', diff_file.name,
]
# Send to try server via HTTP if we can parse the config, otherwise
# upload via SVN.
config = GetTryServerConfig()
if config is not None:
sendmsg = "Sending %s using HTTP..." % patch_name
sendmsg = "Sending %s using HTTP..." % '-'.join( patch_names)
args.extend(['--use_http'])
if config['try_server_http_host'] is not None:
args.extend(['--host', config['try_server_http_host']])
@ -174,5 +252,9 @@ if __name__ == '__main__':
'--patchset', GetRietveldPatchsetNumber(),
])
if options.dry_run:
print open(diff_file.name, 'r').read()
exit(0)
print sendmsg
TryChange(args=args)