182c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)#!/usr/bin/python 233e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)# 333e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)# Copyright (C) 2012 The Android Open Source Project 433e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)# 533e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)# Licensed under the Apache License, Version 2.0 (the "License"); 633e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)# you may not use this file except in compliance with the License. 733e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)# You may obtain a copy of the License at 833e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)# 933e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)# http://www.apache.org/licenses/LICENSE-2.0 1033e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)# 1133e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)# Unless required by applicable law or agreed to in writing, software 1233e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)# distributed under the License is distributed on an "AS IS" BASIS, 1333e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1433e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)# See the License for the specific language governing permissions and 1533e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)# limitations under the License. 1633e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles) 1782c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)"""Merge master-chromium to master within the Android tree.""" 1833e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles) 195d65b76f6747f51bb623d00205aca85581ad4d91Torne (Richard Coles)import logging 2033e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)import optparse 2133e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)import os 2233e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)import re 2333e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)import shutil 2470e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucciimport subprocess 2533e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)import sys 2633e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles) 2733e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)import merge_common 2833e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles) 2933e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles) 3033e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)AUTOGEN_MESSAGE = 'This commit was generated by merge_to_master.py.' 3170e496d940c22bf07cd2c5634e1819276ddac416Primiano TucciWEBVIEW_PROJECT = 'frameworks/webview' 3233e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles) 3333e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles) 3470e496d940c22bf07cd2c5634e1819276ddac416Primiano Tuccidef _GetAbsPath(project): 3570e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci """Returns the full path to a given project (either Chromium or Android).""" 3670e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci if project in merge_common.ALL_PROJECTS: 3770e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci abs_path = os.path.join(merge_common.REPOSITORY_ROOT, project) 3870e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci else: 3970e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci abs_path = os.path.join(os.environ['ANDROID_BUILD_TOP'], project) 4070e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci if not os.path.exists(abs_path): 4170e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci raise merge_common.MergeError('Cannot find path ' + abs_path) 4270e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci return abs_path 4382c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles) 4470e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 4570e496d940c22bf07cd2c5634e1819276ddac416Primiano Tuccidef _CheckoutSingleProject(project, target_branch): 4670e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci """Checks out the tip of the target_branch into a local branch (merge-to-XXX). 4782c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles) 4833e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles) Args: 4970e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci project: a Chromium project (., third_party/foo) or frameworks/webview. 5070e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci target_branch: name of the target branch (in the goog remote). 5133e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles) """ 5270e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci dest_dir = _GetAbsPath(project) 5370e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci tracking_branch = 'goog/' + target_branch 5470e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci logging.debug('Check out %-45s at %-16s', project, tracking_branch) 5570e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci merge_common.GetCommandStdout(['git', 'remote', 'update', 'goog'], 5670e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci cwd=dest_dir) 5770e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci merge_common.GetCommandStdout(['git', 'checkout', 5870e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci '-b', 'merge-to-' + target_branch, 5970e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci '-t', tracking_branch], cwd=dest_dir) 6070e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 6170e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 6270e496d940c22bf07cd2c5634e1819276ddac416Primiano Tuccidef _FetchSingleProject(project, remote, remote_ref): 6370e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci """Fetches a remote ref for the given project and returns the fetched SHA. 6470e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 6570e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci Args: 6670e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci project: a Chromium project (., third_party/foo) or frameworks/webview. 6770e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci remote: Git remote name (goog for most projects, history for squashed ones). 6870e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci remote_ref: the remote ref to fetch (e.g., refs/archive/chromium-XXX). 6970e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 7070e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci Returns: 7170e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci The SHA1 of the FETCH_HEAD. 7270e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci """ 7370e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci dest_dir = _GetAbsPath(project) 7470e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci logging.debug('Fetch %-45s %s:%s', project, remote, remote_ref) 7570e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci merge_common.GetCommandStdout(['git', 'fetch', remote, remote_ref], 7670e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci cwd=dest_dir) 7770e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci return merge_common.GetCommandStdout(['git', 'rev-parse', 'FETCH_HEAD'], 7870e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci cwd=dest_dir).strip() 7970e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 8070e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 8170e496d940c22bf07cd2c5634e1819276ddac416Primiano Tuccidef _MergeSingleProject(project, merge_sha, revision, target_branch, flatten): 8270e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci """Merges a single project at a given SHA. 8370e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 8470e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci Args: 8570e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci project: a Chromium project (., third_party/foo) or frameworks/webview. 8670e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci merge_sha: the SHA to merge. 8770e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci revision: Abbrev. commitish in the main Chromium repository. 8870e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci target_branch: name of the target branch. 8970e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci flatten: True: squash history while merging; False: perform a normal merge. 9070e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci """ 9170e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci dest_dir = _GetAbsPath(project) 9270e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci if flatten: 9333e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles) # Make the previous merges into grafts so we can do a correct merge. 9470e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci old_sha = merge_common.GetCommandStdout(['git', 'rev-parse', 'HEAD'], 9570e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci cwd=dest_dir).strip() 9633e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles) merge_log = os.path.join(dest_dir, '.merged-revisions') 9733e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles) if os.path.exists(merge_log): 9833e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles) shutil.copyfile(merge_log, 9933e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles) os.path.join(dest_dir, '.git', 'info', 'grafts')) 10070e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 10170e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci # Early out if there is nothing to merge. 10270e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci if not merge_common.GetCommandStdout(['git', 'rev-list', '-1', 10370e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 'HEAD..' + merge_sha], cwd=dest_dir): 10470e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci logging.debug('No new commits to merge in project %s', project) 10570e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci return 10670e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 10770e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci logging.debug('Merging project %s (flatten: %s)...', project, flatten) 10870e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci merge_cmd = ['git', 'merge', '--no-commit'] 10970e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci merge_cmd += ['--squash'] if flatten else ['--no-ff'] 11070e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci merge_cmd += [merge_sha] 11170e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci # Merge conflicts cause 'git merge' to return 1, so ignore errors 11270e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci merge_common.GetCommandStdout(merge_cmd, cwd=dest_dir, ignore_errors=True) 11370e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 11470e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci if flatten: 11570e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci dirs_to_prune = merge_common.PRUNE_WHEN_FLATTENING.get(project, []) 11670e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci if dirs_to_prune: 11770e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci merge_common.GetCommandStdout(['git', 'rm', '--ignore-unmatch', '-rf'] + 11870e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci dirs_to_prune, cwd=dest_dir) 11970e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 12070e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci if project in merge_common.ALL_PROJECTS: 12170e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci commit_msg = 'Merge from Chromium at DEPS revision %s' % revision 12270e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci else: 12370e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci commit_msg = 'Merge master-chromium into %s at %s' % (target_branch, 12470e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci revision) 12570e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci commit_msg += '\n\n' + AUTOGEN_MESSAGE 12670e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci merge_common.CheckNoConflictsAndCommitMerge(commit_msg, cwd=dest_dir) 12770e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 12870e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci if flatten: 12970e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci # Generate the new grafts file and commit it on top of the merge. 13070e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci new_sha = merge_common.GetCommandStdout(['git', 'rev-parse', 'HEAD'], 13170e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci cwd=dest_dir).strip() 13270e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci with open(merge_log, 'a+') as f: 13370e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci f.write('%s %s %s\n' % (new_sha, old_sha, merge_sha)) 13470e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci merge_common.GetCommandStdout(['git', 'add', '.merged-revisions'], 135e1a149c76208a84c7595203a465ae1218ded9d34Torne (Richard Coles) cwd=dest_dir) 13670e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci merge_common.GetCommandStdout( 13770e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci ['git', 'commit', '-m', 13870e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 'Record Chromium merge at DEPS revision %s\n\n%s' % 13970e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci (revision, AUTOGEN_MESSAGE)], cwd=dest_dir) 14070e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 14170e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 14270e496d940c22bf07cd2c5634e1819276ddac416Primiano Tuccidef _IsAncestor(ref1, ref2, cwd): 14370e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci """Checks whether ref1 is a ancestor of ref2 in the given Git repo.""" 14470e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci cmd = ['git', 'merge-base', '--is-ancestor', ref1, ref2] 14570e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci ret = subprocess.call(cmd, cwd=cwd) 14670e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci if ret == 0: 14770e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci return True 14870e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci elif ret == 1: 14970e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci return False 15070e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci else: 15170e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci raise merge_common.CommandError(ret, ' '.join(cmd), cwd, 'N/A', 'N/A') 15270e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 15370e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 15470e496d940c22bf07cd2c5634e1819276ddac416Primiano Tuccidef _MergeChromiumProjects(revision, target_branch, repo_shas=None, 15570e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci force=False): 15670e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci """Merges the Chromium projects from master-chromium to target_branch. 15770e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 15870e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci The larger projects' histories are flattened in the process. 15970e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci When repo_shas != None, it checks that the SHAs of the projects in the 16070e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci archive match exactly the SHAs of the projects in repo.prop. 16170e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 16270e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci Args: 16370e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci revision: Abbrev. commitish in the main Chromium repository. 16470e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci target_branch: target branch name to merge and push to. 16570e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci repo_shas: optional dict. of expected revisions (only for --repo-prop). 16670e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci force: True: merge anyways using the SHAs from repo.prop; False: bail out if 16770e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci projects mismatch (archive vs repo.prop). 16870e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci """ 16970e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci # Sync and checkout ToT for all projects (creating the merge-to-XXX branch) 17070e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci # and fetch the archive snapshot. 17170e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci fetched_shas = {} 17270e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci remote_ref = 'refs/archive/chromium-%s' % revision 17370e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci for project in merge_common.PROJECTS_WITH_FLAT_HISTORY: 17470e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci _CheckoutSingleProject(project, target_branch) 17570e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci fetched_shas[project] = _FetchSingleProject(project, 'history', remote_ref) 17670e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci for project in merge_common.PROJECTS_WITH_FULL_HISTORY: 17770e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci _CheckoutSingleProject(project, target_branch) 17870e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci fetched_shas[project] = _FetchSingleProject(project, 'goog', remote_ref) 17970e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 18070e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci if repo_shas: 18170e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci project_shas_mismatch = False 18270e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci for project, merge_sha in fetched_shas.items(): # the dict can be modified. 18370e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci expected_sha = repo_shas.get(project) 18470e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci if expected_sha != merge_sha: 18570e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci logging.warn('The SHA for project %s specified in the repo.prop (%s) ' 18670e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 'and the one in the archive (%s) differ.', 18770e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci project, expected_sha, merge_sha) 18870e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci dest_dir = _GetAbsPath(project) 18970e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci if expected_sha is None: 19070e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci reason = 'cannot find a SHA in the repo.pro for %s' % project 19170e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci elif _IsAncestor(merge_sha, expected_sha, cwd=dest_dir): 19270e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci reason = 'the SHA in repo.prop is ahead of the SHA in the archive. ' 19370e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci log_cmd = ['git', 'log', '--oneline', '--graph', '--max-count=10', 19470e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci '%s..%s' % (merge_sha, expected_sha)] 19570e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci log_cmd_output = merge_common.GetCommandStdout(log_cmd, cwd=dest_dir) 19670e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci reason += 'showing partial log (%s): \n %s' % (' '.join(log_cmd), 19770e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci log_cmd_output) 19870e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci elif _IsAncestor(expected_sha, merge_sha, cwd=dest_dir): 19970e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci reason = 'The SHA is already merged in the archive' 20070e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci else: 20170e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci reason = 'The project history diverged. Consult your Git historian.' 20270e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 20370e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci project_shas_mismatch = True 20470e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci if force: 20570e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci logging.debug('Merging the SHA in repo.prop anyways (due to --force)') 20670e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci fetched_shas[project] = expected_sha 20770e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci else: 20870e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci logging.debug('Reason: %s', reason) 20970e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci if not force and project_shas_mismatch: 21070e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci raise merge_common.MergeError( 21170e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 'The revision of some projects in the archive is different from the ' 21270e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 'one provided in build.prop. See the log for more details. Re-run ' 21370e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 'with --force to continue.') 21470e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 21570e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci for project in merge_common.PROJECTS_WITH_FLAT_HISTORY: 21670e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci _MergeSingleProject(project, fetched_shas[project], revision, target_branch, 21770e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci flatten=True) 21870e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci for project in merge_common.PROJECTS_WITH_FULL_HISTORY: 21970e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci _MergeSingleProject(project, fetched_shas[project], revision, target_branch, 22070e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci flatten=False) 22170e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 22270e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 22370e496d940c22bf07cd2c5634e1819276ddac416Primiano Tuccidef _GetNearestUpstreamAbbrevSHA(reference='history/master-chromium'): 22470e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci """Returns the abbrev. upstream SHA which closest to the given reference.""" 22570e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci logging.debug('Getting upstream SHA for %s...', reference) 22670e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci merge_common.GetCommandStdout(['git', 'remote', 'update', 'history']) 22770e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci upstream_commit = merge_common.Abbrev(merge_common.GetCommandStdout([ 22870e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 'git', 'merge-base', 'history/upstream-master', reference])) 22970e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 23070e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci # Pedantic check: look for the existence of a merge commit which contains the 23170e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci # |upstream_commit| in its message and is its children. 23270e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci merge_parents = merge_common.GetCommandStdout([ 23370e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 'git', 'rev-list', reference, '--grep', upstream_commit, '--merges', 23470e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci '--parents', '-1']) 23570e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci if upstream_commit not in merge_parents: 23670e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci raise merge_common.MergeError( 23770e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 'Found upstream commit %s, but the merge child (%s) could not be found ' 23870e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 'or is not a parent of the upstream SHA') 23970e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci logging.debug('Found nearest Chromium revision %s', upstream_commit) 24070e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci return upstream_commit 24170e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 24270e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 24370e496d940c22bf07cd2c5634e1819276ddac416Primiano Tuccidef _MergeWithRepoProp(repo_prop_file, target_branch, force): 24470e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci """Performs a merge using a repo.prop file (from Android build waterfall). 24570e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 24670e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci This does NOT merge (unless forced with force=True) the pinned 24770e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci revisions in repo.prop, as a repo.prop can snapshot an intermediate state 24870e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci (between two automerger cycles). Instead, this looks up the archived snapshot 24970e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci (generated by the chromium->master-chromium auto-merger) which is closest to 25070e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci the given repo.prop (following the main Chromium project) and merges that one. 25170e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci If the projects revisions don't match, it fails with detailed error messages. 25270e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 25370e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci Args: 25470e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci repo_prop_file: Path to a downloaded repo.prop file. 25570e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci target_branch: name of the target branch to merget to. 25670e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci force: ignores the aforementioned check and merged anyways. 25770e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci """ 25858324375a4ba319e7b114f3afece23a98de3f373Torne (Richard Coles) chromium_sha = None 25958324375a4ba319e7b114f3afece23a98de3f373Torne (Richard Coles) webview_sha = None 26070e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci repo_shas = {} # 'project/path' -> 'sha' 26158324375a4ba319e7b114f3afece23a98de3f373Torne (Richard Coles) with open(repo_prop_file) as prop: 26258324375a4ba319e7b114f3afece23a98de3f373Torne (Richard Coles) for line in prop: 26370e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci repo, sha = line.split() 26470e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci # Translate the Android repo paths into the relative project paths used in 26570e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci # merge_common (e.g., platform/external/chromium_org/foo -> foo). 26670e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci m = ( 26770e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci re.match(r'^platform/(frameworks/.+)$', repo) or 26870e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci re.match(r'^platform/external/chromium_org/?(.*?)(-history)?$', repo)) 26970e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci if m: 27070e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci project = m.group(1) if m.group(1) else '.' # '.' = Main project. 27170e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci repo_shas[project] = sha 27270e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 27370e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci chromium_sha = repo_shas.get('.') 27470e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci webview_sha = repo_shas.get(WEBVIEW_PROJECT) 27558324375a4ba319e7b114f3afece23a98de3f373Torne (Richard Coles) if not chromium_sha or not webview_sha: 27670e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci raise merge_common.MergeError('SHAs for projects not found; ' 27770e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 'invalid build.prop?') 27858324375a4ba319e7b114f3afece23a98de3f373Torne (Richard Coles) 27970e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci # Check that the revisions in repo.prop and the on in the archive match. 28070e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci archived_chromium_revision = _GetNearestUpstreamAbbrevSHA(chromium_sha) 28170e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci logging.info('Merging Chromium at %s and WebView at %s', 28270e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci archived_chromium_revision, webview_sha) 28370e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci _MergeChromiumProjects(archived_chromium_revision, target_branch, repo_shas, 28470e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci force) 28570e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 28670e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci _CheckoutSingleProject(WEBVIEW_PROJECT, target_branch) 28770e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci _MergeSingleProject(WEBVIEW_PROJECT, webview_sha, 28870e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci archived_chromium_revision, target_branch, flatten=False) 28958324375a4ba319e7b114f3afece23a98de3f373Torne (Richard Coles) 29058324375a4ba319e7b114f3afece23a98de3f373Torne (Richard Coles) 29170e496d940c22bf07cd2c5634e1819276ddac416Primiano Tuccidef Push(target_branch): 29270e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci """Push the finished snapshot to the Android repository. 29370e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 29470e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci Creates first a CL for frameworks/webview (if the merge-to-XXX branch exists) 29570e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci then wait for user confirmation and pushes the Chromium merges. This is to 29670e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci give an opportunity to get a +2 for frameworks/webview and then push both 29770e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci frameworks/webview and the Chromium projects atomically(ish). 29870e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 29970e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci Args: 30070e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci target_branch: name of the target branch (in the goog remote). 30170e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci """ 30270e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci merge_branch = 'merge-to-%s' % target_branch 30370e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 30470e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci # Create a Gerrit CL for the frameworks/webview project (if needed). 30570e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci dest_dir = _GetAbsPath(WEBVIEW_PROJECT) 30670e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci did_upload_webview_cl = False 30770e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci if merge_common.GetCommandStdout(['git', 'branch', '--list', merge_branch], 30870e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci cwd=dest_dir): 30970e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci # Check that there was actually something to merge. 31070e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci merge_range = 'goog/%s..%s' % (target_branch, merge_branch) 31170e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci if merge_common.GetCommandStdout(['git', 'rev-list', '-1', merge_range], 31270e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci cwd=dest_dir): 31370e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci logging.info('Uploading a merge CL for %s...', WEBVIEW_PROJECT) 31470e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci refspec = '%s:refs/for/%s' % (merge_branch, target_branch) 31570e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci upload = merge_common.GetCommandStdout(['git', 'push', 'goog', refspec], 31670e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci cwd=dest_dir) 31770e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci logging.info(upload) 31870e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci did_upload_webview_cl = True 31970e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 32070e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci prompt_msg = 'About push the Chromium projects merge. ' 32170e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci if not did_upload_webview_cl: 32270e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci logging.info('No merge CL needed for %s.', WEBVIEW_PROJECT) 32370e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci else: 32470e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci prompt_msg += ('At this point you should have the CL +2-ed and merge it ' 32570e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 'together with this push.') 32670e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci prompt_msg += '\nPress "y" to continue: ' 32770e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci if raw_input(prompt_msg) != 'y': 32870e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci logging.warn('Push aborted by the user!') 32970e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci return 33070e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 33170e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci logging.debug('Pushing Chromium projects to %s ...', target_branch) 33270e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci refspec = '%s:%s' % (merge_branch, target_branch) 33309ead754abd2cfcffea54040b1c9ac98db8af834Torne (Richard Coles) for path in merge_common.ALL_PROJECTS: 33409ead754abd2cfcffea54040b1c9ac98db8af834Torne (Richard Coles) logging.debug('Pushing %s', path) 33570e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci dest_dir = _GetAbsPath(path) 336e1a149c76208a84c7595203a465ae1218ded9d34Torne (Richard Coles) # Delete the graft before pushing otherwise git will attempt to push all the 337e1a149c76208a84c7595203a465ae1218ded9d34Torne (Richard Coles) # grafted-in objects to the server as well as the ones we want. 338e1a149c76208a84c7595203a465ae1218ded9d34Torne (Richard Coles) graftfile = os.path.join(dest_dir, '.git', 'info', 'grafts') 339e1a149c76208a84c7595203a465ae1218ded9d34Torne (Richard Coles) if os.path.exists(graftfile): 340e1a149c76208a84c7595203a465ae1218ded9d34Torne (Richard Coles) os.remove(graftfile) 3416adc8a2e72d90b3a5c1cf7cf7ec7be2f6c974851Torne (Richard Coles) merge_common.GetCommandStdout(['git', 'push', 'goog', refspec], 3426adc8a2e72d90b3a5c1cf7cf7ec7be2f6c974851Torne (Richard Coles) cwd=dest_dir) 34309ead754abd2cfcffea54040b1c9ac98db8af834Torne (Richard Coles) 34409ead754abd2cfcffea54040b1c9ac98db8af834Torne (Richard Coles) 34533e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)def main(): 34633e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles) parser = optparse.OptionParser(usage='%prog [options]') 34733e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles) parser.epilog = ('Takes the current master-chromium branch of the Chromium ' 34833e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles) 'projects in Android and merges them into master to publish ' 34933e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles) 'them.') 35033e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles) parser.add_option( 35170e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci '', '--revision', 352e1a149c76208a84c7595203a465ae1218ded9d34Torne (Richard Coles) default=None, 35370e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci help=('Merge to the specified archived master-chromium revision (abbrev. ' 35470e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci 'SHA or release version) rather than using HEAD. e.g., ' 35570e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci '--revision=a1b2c3d4e5f6 or --revision=38.0.2125.24')) 356e1a149c76208a84c7595203a465ae1218ded9d34Torne (Richard Coles) parser.add_option( 35758324375a4ba319e7b114f3afece23a98de3f373Torne (Richard Coles) '', '--repo-prop', 35858324375a4ba319e7b114f3afece23a98de3f373Torne (Richard Coles) default=None, metavar='FILE', 35958324375a4ba319e7b114f3afece23a98de3f373Torne (Richard Coles) help=('Merge to the revisions specified in this repo.prop file.')) 36058324375a4ba319e7b114f3afece23a98de3f373Torne (Richard Coles) parser.add_option( 36170e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci '', '--force', 36270e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci default=False, action='store_true', 36370e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci help=('Skip history checks and merged anyways (only for --repo-prop).')) 36470e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci parser.add_option( 36514ac2c8d49fa1f5f12e6a80af93f1c50312d80a8Torne (Richard Coles) '', '--push', 36682c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles) default=False, action='store_true', 36714ac2c8d49fa1f5f12e6a80af93f1c50312d80a8Torne (Richard Coles) help=('Push the result of a previous merge to the server.')) 3686adc8a2e72d90b3a5c1cf7cf7ec7be2f6c974851Torne (Richard Coles) parser.add_option( 3696adc8a2e72d90b3a5c1cf7cf7ec7be2f6c974851Torne (Richard Coles) '', '--target', 3706adc8a2e72d90b3a5c1cf7cf7ec7be2f6c974851Torne (Richard Coles) default='master', metavar='BRANCH', 3716adc8a2e72d90b3a5c1cf7cf7ec7be2f6c974851Torne (Richard Coles) help=('Target branch to push to. Defaults to master.')) 37233e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles) (options, args) = parser.parse_args() 37333e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles) if args: 37433e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles) parser.print_help() 37533e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles) return 1 37633e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles) 3775d65b76f6747f51bb623d00205aca85581ad4d91Torne (Richard Coles) logging.basicConfig(format='%(message)s', level=logging.DEBUG, 3785d65b76f6747f51bb623d00205aca85581ad4d91Torne (Richard Coles) stream=sys.stdout) 3795d65b76f6747f51bb623d00205aca85581ad4d91Torne (Richard Coles) 38014ac2c8d49fa1f5f12e6a80af93f1c50312d80a8Torne (Richard Coles) if options.push: 3816adc8a2e72d90b3a5c1cf7cf7ec7be2f6c974851Torne (Richard Coles) Push(options.target) 38258324375a4ba319e7b114f3afece23a98de3f373Torne (Richard Coles) elif options.repo_prop: 38370e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci _MergeWithRepoProp(os.path.expanduser(options.repo_prop), 38470e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci options.target, options.force) 38570e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci elif options.revision: 38670e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci _MergeChromiumProjects(options.revision, options.target) 38714ac2c8d49fa1f5f12e6a80af93f1c50312d80a8Torne (Richard Coles) else: 38870e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci first_upstream_sha = _GetNearestUpstreamAbbrevSHA() 38970e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci _MergeChromiumProjects(first_upstream_sha, options.target) 39082c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles) 39133e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles) return 0 39233e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles) 39333e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)if __name__ == '__main__': 39433e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles) sys.exit(main()) 395