11cb0ef41Sopenharmony_ci#!/usr/bin/env python3 21cb0ef41Sopenharmony_ci# Copyright 2014 the V8 project authors. All rights reserved. 31cb0ef41Sopenharmony_ci# Redistribution and use in source and binary forms, with or without 41cb0ef41Sopenharmony_ci# modification, are permitted provided that the following conditions are 51cb0ef41Sopenharmony_ci# met: 61cb0ef41Sopenharmony_ci# 71cb0ef41Sopenharmony_ci# * Redistributions of source code must retain the above copyright 81cb0ef41Sopenharmony_ci# notice, this list of conditions and the following disclaimer. 91cb0ef41Sopenharmony_ci# * Redistributions in binary form must reproduce the above 101cb0ef41Sopenharmony_ci# copyright notice, this list of conditions and the following 111cb0ef41Sopenharmony_ci# disclaimer in the documentation and/or other materials provided 121cb0ef41Sopenharmony_ci# with the distribution. 131cb0ef41Sopenharmony_ci# * Neither the name of Google Inc. nor the names of its 141cb0ef41Sopenharmony_ci# contributors may be used to endorse or promote products derived 151cb0ef41Sopenharmony_ci# from this software without specific prior written permission. 161cb0ef41Sopenharmony_ci# 171cb0ef41Sopenharmony_ci# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 181cb0ef41Sopenharmony_ci# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 191cb0ef41Sopenharmony_ci# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 201cb0ef41Sopenharmony_ci# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 211cb0ef41Sopenharmony_ci# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 221cb0ef41Sopenharmony_ci# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 231cb0ef41Sopenharmony_ci# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 241cb0ef41Sopenharmony_ci# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 251cb0ef41Sopenharmony_ci# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 261cb0ef41Sopenharmony_ci# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 271cb0ef41Sopenharmony_ci# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 281cb0ef41Sopenharmony_ci 291cb0ef41Sopenharmony_ciimport argparse 301cb0ef41Sopenharmony_cifrom collections import OrderedDict 311cb0ef41Sopenharmony_ciimport sys 321cb0ef41Sopenharmony_ci 331cb0ef41Sopenharmony_cifrom common_includes import * 341cb0ef41Sopenharmony_cifrom git_recipes import GetCommitMessageFooterMap 351cb0ef41Sopenharmony_ci 361cb0ef41Sopenharmony_cidef IsSvnNumber(rev): 371cb0ef41Sopenharmony_ci return rev.isdigit() and len(rev) < 8 381cb0ef41Sopenharmony_ci 391cb0ef41Sopenharmony_ciclass Preparation(Step): 401cb0ef41Sopenharmony_ci MESSAGE = "Preparation." 411cb0ef41Sopenharmony_ci 421cb0ef41Sopenharmony_ci def RunStep(self): 431cb0ef41Sopenharmony_ci if os.path.exists(self.Config("ALREADY_MERGING_SENTINEL_FILE")): 441cb0ef41Sopenharmony_ci if self._options.force: 451cb0ef41Sopenharmony_ci os.remove(self.Config("ALREADY_MERGING_SENTINEL_FILE")) 461cb0ef41Sopenharmony_ci elif self._options.step == 0: # pragma: no cover 471cb0ef41Sopenharmony_ci self.Die("A merge is already in progress. Use -f to continue") 481cb0ef41Sopenharmony_ci open(self.Config("ALREADY_MERGING_SENTINEL_FILE"), "a").close() 491cb0ef41Sopenharmony_ci 501cb0ef41Sopenharmony_ci self.InitialEnvironmentChecks(self.default_cwd) 511cb0ef41Sopenharmony_ci 521cb0ef41Sopenharmony_ci self["merge_to_branch"] = self._options.branch 531cb0ef41Sopenharmony_ci 541cb0ef41Sopenharmony_ci self.CommonPrepare() 551cb0ef41Sopenharmony_ci self.PrepareBranch() 561cb0ef41Sopenharmony_ci 571cb0ef41Sopenharmony_ci 581cb0ef41Sopenharmony_ciclass CreateBranch(Step): 591cb0ef41Sopenharmony_ci MESSAGE = "Create a fresh branch for the patch." 601cb0ef41Sopenharmony_ci 611cb0ef41Sopenharmony_ci def RunStep(self): 621cb0ef41Sopenharmony_ci self.GitCreateBranch(self.Config("BRANCHNAME"), 631cb0ef41Sopenharmony_ci self.vc.RemoteBranch(self["merge_to_branch"])) 641cb0ef41Sopenharmony_ci 651cb0ef41Sopenharmony_ci 661cb0ef41Sopenharmony_ciclass SearchArchitecturePorts(Step): 671cb0ef41Sopenharmony_ci MESSAGE = "Search for corresponding architecture ports." 681cb0ef41Sopenharmony_ci 691cb0ef41Sopenharmony_ci def RunStep(self): 701cb0ef41Sopenharmony_ci self["full_revision_list"] = list(OrderedDict.fromkeys( 711cb0ef41Sopenharmony_ci self._options.revisions)) 721cb0ef41Sopenharmony_ci port_revision_list = [] 731cb0ef41Sopenharmony_ci for revision in self["full_revision_list"]: 741cb0ef41Sopenharmony_ci # Search for commits which matches the "Port XXX" pattern. 751cb0ef41Sopenharmony_ci git_hashes = self.GitLog(reverse=True, format="%H", 761cb0ef41Sopenharmony_ci grep="^[Pp]ort %s" % revision, 771cb0ef41Sopenharmony_ci branch=self.vc.RemoteMainBranch()) 781cb0ef41Sopenharmony_ci for git_hash in git_hashes.splitlines(): 791cb0ef41Sopenharmony_ci revision_title = self.GitLog(n=1, format="%s", git_hash=git_hash) 801cb0ef41Sopenharmony_ci 811cb0ef41Sopenharmony_ci # Is this revision included in the original revision list? 821cb0ef41Sopenharmony_ci if git_hash in self["full_revision_list"]: 831cb0ef41Sopenharmony_ci print("Found port of %s -> %s (already included): %s" 841cb0ef41Sopenharmony_ci % (revision, git_hash, revision_title)) 851cb0ef41Sopenharmony_ci else: 861cb0ef41Sopenharmony_ci print("Found port of %s -> %s: %s" 871cb0ef41Sopenharmony_ci % (revision, git_hash, revision_title)) 881cb0ef41Sopenharmony_ci port_revision_list.append(git_hash) 891cb0ef41Sopenharmony_ci 901cb0ef41Sopenharmony_ci # Do we find any port? 911cb0ef41Sopenharmony_ci if len(port_revision_list) > 0: 921cb0ef41Sopenharmony_ci if self.Confirm("Automatically add corresponding ports (%s)?" 931cb0ef41Sopenharmony_ci % ", ".join(port_revision_list)): 941cb0ef41Sopenharmony_ci #: 'y': Add ports to revision list. 951cb0ef41Sopenharmony_ci self["full_revision_list"].extend(port_revision_list) 961cb0ef41Sopenharmony_ci 971cb0ef41Sopenharmony_ci 981cb0ef41Sopenharmony_ciclass CreateCommitMessage(Step): 991cb0ef41Sopenharmony_ci MESSAGE = "Create commit message." 1001cb0ef41Sopenharmony_ci 1011cb0ef41Sopenharmony_ci def _create_commit_description(self, commit_hash): 1021cb0ef41Sopenharmony_ci patch_merge_desc = self.GitLog(n=1, format="%s", git_hash=commit_hash) 1031cb0ef41Sopenharmony_ci description = "Merged: " + patch_merge_desc + "\n" 1041cb0ef41Sopenharmony_ci description += "Revision: " + commit_hash + "\n\n" 1051cb0ef41Sopenharmony_ci return description 1061cb0ef41Sopenharmony_ci 1071cb0ef41Sopenharmony_ci def RunStep(self): 1081cb0ef41Sopenharmony_ci 1091cb0ef41Sopenharmony_ci # Stringify: ["abcde", "12345"] -> "abcde, 12345" 1101cb0ef41Sopenharmony_ci self["revision_list"] = ", ".join(self["full_revision_list"]) 1111cb0ef41Sopenharmony_ci 1121cb0ef41Sopenharmony_ci if not self["revision_list"]: # pragma: no cover 1131cb0ef41Sopenharmony_ci self.Die("Revision list is empty.") 1141cb0ef41Sopenharmony_ci 1151cb0ef41Sopenharmony_ci msg_pieces = [] 1161cb0ef41Sopenharmony_ci 1171cb0ef41Sopenharmony_ci if len(self["full_revision_list"]) > 1: 1181cb0ef41Sopenharmony_ci self["commit_title"] = "Merged: Squashed multiple commits." 1191cb0ef41Sopenharmony_ci for commit_hash in self["full_revision_list"]: 1201cb0ef41Sopenharmony_ci msg_pieces.append(self._create_commit_description(commit_hash)) 1211cb0ef41Sopenharmony_ci else: 1221cb0ef41Sopenharmony_ci commit_hash = self["full_revision_list"][0] 1231cb0ef41Sopenharmony_ci full_description = self._create_commit_description(commit_hash).split("\n") 1241cb0ef41Sopenharmony_ci 1251cb0ef41Sopenharmony_ci #Truncate title because of code review tool 1261cb0ef41Sopenharmony_ci title = full_description[0] 1271cb0ef41Sopenharmony_ci if len(title) > 100: 1281cb0ef41Sopenharmony_ci title = title[:96] + " ..." 1291cb0ef41Sopenharmony_ci 1301cb0ef41Sopenharmony_ci self["commit_title"] = title 1311cb0ef41Sopenharmony_ci msg_pieces.append(full_description[1] + "\n\n") 1321cb0ef41Sopenharmony_ci 1331cb0ef41Sopenharmony_ci bugs = [] 1341cb0ef41Sopenharmony_ci for commit_hash in self["full_revision_list"]: 1351cb0ef41Sopenharmony_ci msg = self.GitLog(n=1, git_hash=commit_hash) 1361cb0ef41Sopenharmony_ci for bug in re.findall(r"^[ \t]*BUG[ \t]*=[ \t]*(.*?)[ \t]*$", msg, re.M): 1371cb0ef41Sopenharmony_ci bugs.extend(s.strip() for s in bug.split(",")) 1381cb0ef41Sopenharmony_ci gerrit_bug = GetCommitMessageFooterMap(msg).get('Bug', '') 1391cb0ef41Sopenharmony_ci bugs.extend(s.strip() for s in gerrit_bug.split(",")) 1401cb0ef41Sopenharmony_ci bug_aggregate = ",".join( 1411cb0ef41Sopenharmony_ci sorted(filter(lambda s: s and s != "none", set(bugs)))) 1421cb0ef41Sopenharmony_ci if bug_aggregate: 1431cb0ef41Sopenharmony_ci # TODO(machenbach): Use proper gerrit footer for bug after switch to 1441cb0ef41Sopenharmony_ci # gerrit. Keep BUG= for now for backwards-compatibility. 1451cb0ef41Sopenharmony_ci msg_pieces.append("BUG=%s\n" % bug_aggregate) 1461cb0ef41Sopenharmony_ci 1471cb0ef41Sopenharmony_ci msg_pieces.append("NOTRY=true\nNOPRESUBMIT=true\nNOTREECHECKS=true\n") 1481cb0ef41Sopenharmony_ci 1491cb0ef41Sopenharmony_ci self["new_commit_msg"] = "".join(msg_pieces) 1501cb0ef41Sopenharmony_ci 1511cb0ef41Sopenharmony_ci 1521cb0ef41Sopenharmony_ciclass ApplyPatches(Step): 1531cb0ef41Sopenharmony_ci MESSAGE = "Apply patches for selected revisions." 1541cb0ef41Sopenharmony_ci 1551cb0ef41Sopenharmony_ci def RunStep(self): 1561cb0ef41Sopenharmony_ci for commit_hash in self["full_revision_list"]: 1571cb0ef41Sopenharmony_ci print("Applying patch for %s to %s..." 1581cb0ef41Sopenharmony_ci % (commit_hash, self["merge_to_branch"])) 1591cb0ef41Sopenharmony_ci patch = self.GitGetPatch(commit_hash) 1601cb0ef41Sopenharmony_ci TextToFile(patch, self.Config("TEMPORARY_PATCH_FILE")) 1611cb0ef41Sopenharmony_ci self.ApplyPatch(self.Config("TEMPORARY_PATCH_FILE")) 1621cb0ef41Sopenharmony_ci if self._options.patch: 1631cb0ef41Sopenharmony_ci self.ApplyPatch(self._options.patch) 1641cb0ef41Sopenharmony_ci 1651cb0ef41Sopenharmony_ciclass CommitLocal(Step): 1661cb0ef41Sopenharmony_ci MESSAGE = "Commit to local branch." 1671cb0ef41Sopenharmony_ci 1681cb0ef41Sopenharmony_ci def RunStep(self): 1691cb0ef41Sopenharmony_ci # Add a commit message title. 1701cb0ef41Sopenharmony_ci self["new_commit_msg"] = "%s\n\n%s" % (self["commit_title"], 1711cb0ef41Sopenharmony_ci self["new_commit_msg"]) 1721cb0ef41Sopenharmony_ci TextToFile(self["new_commit_msg"], self.Config("COMMITMSG_FILE")) 1731cb0ef41Sopenharmony_ci self.GitCommit(file_name=self.Config("COMMITMSG_FILE")) 1741cb0ef41Sopenharmony_ci 1751cb0ef41Sopenharmony_ciclass CommitRepository(Step): 1761cb0ef41Sopenharmony_ci MESSAGE = "Commit to the repository." 1771cb0ef41Sopenharmony_ci 1781cb0ef41Sopenharmony_ci def RunStep(self): 1791cb0ef41Sopenharmony_ci self.GitCheckout(self.Config("BRANCHNAME")) 1801cb0ef41Sopenharmony_ci self.WaitForLGTM() 1811cb0ef41Sopenharmony_ci self.GitPresubmit() 1821cb0ef41Sopenharmony_ci self.vc.CLLand() 1831cb0ef41Sopenharmony_ci 1841cb0ef41Sopenharmony_ciclass CleanUp(Step): 1851cb0ef41Sopenharmony_ci MESSAGE = "Cleanup." 1861cb0ef41Sopenharmony_ci 1871cb0ef41Sopenharmony_ci def RunStep(self): 1881cb0ef41Sopenharmony_ci self.CommonCleanup() 1891cb0ef41Sopenharmony_ci print("*** SUMMARY ***") 1901cb0ef41Sopenharmony_ci print("branch: %s" % self["merge_to_branch"]) 1911cb0ef41Sopenharmony_ci if self["revision_list"]: 1921cb0ef41Sopenharmony_ci print("patches: %s" % self["revision_list"]) 1931cb0ef41Sopenharmony_ci 1941cb0ef41Sopenharmony_ci 1951cb0ef41Sopenharmony_ciclass MergeToBranch(ScriptsBase): 1961cb0ef41Sopenharmony_ci def _Description(self): 1971cb0ef41Sopenharmony_ci return ("Performs the necessary steps to merge revisions from " 1981cb0ef41Sopenharmony_ci "main to release branches like 4.5. This script does not " 1991cb0ef41Sopenharmony_ci "version the commit. See http://goo.gl/9ke2Vw for more " 2001cb0ef41Sopenharmony_ci "information.") 2011cb0ef41Sopenharmony_ci 2021cb0ef41Sopenharmony_ci def _PrepareOptions(self, parser): 2031cb0ef41Sopenharmony_ci group = parser.add_mutually_exclusive_group(required=True) 2041cb0ef41Sopenharmony_ci group.add_argument("--branch", help="The branch to merge to.") 2051cb0ef41Sopenharmony_ci parser.add_argument("revisions", nargs="*", 2061cb0ef41Sopenharmony_ci help="The revisions to merge.") 2071cb0ef41Sopenharmony_ci parser.add_argument("-f", "--force", 2081cb0ef41Sopenharmony_ci help="Delete sentinel file.", 2091cb0ef41Sopenharmony_ci default=False, action="store_true") 2101cb0ef41Sopenharmony_ci parser.add_argument("-m", "--message", 2111cb0ef41Sopenharmony_ci help="A commit message for the patch.") 2121cb0ef41Sopenharmony_ci parser.add_argument("-p", "--patch", 2131cb0ef41Sopenharmony_ci help="A patch file to apply as part of the merge.") 2141cb0ef41Sopenharmony_ci 2151cb0ef41Sopenharmony_ci def _ProcessOptions(self, options): 2161cb0ef41Sopenharmony_ci if len(options.revisions) < 1: 2171cb0ef41Sopenharmony_ci if not options.patch: 2181cb0ef41Sopenharmony_ci print("Either a patch file or revision numbers must be specified") 2191cb0ef41Sopenharmony_ci return False 2201cb0ef41Sopenharmony_ci if not options.message: 2211cb0ef41Sopenharmony_ci print("You must specify a merge comment if no patches are specified") 2221cb0ef41Sopenharmony_ci return False 2231cb0ef41Sopenharmony_ci options.bypass_upload_hooks = True 2241cb0ef41Sopenharmony_ci 2251cb0ef41Sopenharmony_ci if len(options.branch.split('.')) > 2: 2261cb0ef41Sopenharmony_ci print ("This script does not support merging to roll branches. " 2271cb0ef41Sopenharmony_ci "Please use tools/release/roll_merge.py for this use case.") 2281cb0ef41Sopenharmony_ci return False 2291cb0ef41Sopenharmony_ci 2301cb0ef41Sopenharmony_ci # Make sure to use git hashes in the new workflows. 2311cb0ef41Sopenharmony_ci for revision in options.revisions: 2321cb0ef41Sopenharmony_ci if (IsSvnNumber(revision) or 2331cb0ef41Sopenharmony_ci (revision[0:1] == "r" and IsSvnNumber(revision[1:]))): 2341cb0ef41Sopenharmony_ci print("Please provide full git hashes of the patches to merge.") 2351cb0ef41Sopenharmony_ci print("Got: %s" % revision) 2361cb0ef41Sopenharmony_ci return False 2371cb0ef41Sopenharmony_ci return True 2381cb0ef41Sopenharmony_ci 2391cb0ef41Sopenharmony_ci def _Config(self): 2401cb0ef41Sopenharmony_ci return { 2411cb0ef41Sopenharmony_ci "BRANCHNAME": "prepare-merge", 2421cb0ef41Sopenharmony_ci "PERSISTFILE_BASENAME": RELEASE_WORKDIR + "v8-merge-to-branch-tempfile", 2431cb0ef41Sopenharmony_ci "ALREADY_MERGING_SENTINEL_FILE": 2441cb0ef41Sopenharmony_ci RELEASE_WORKDIR + "v8-merge-to-branch-tempfile-already-merging", 2451cb0ef41Sopenharmony_ci "TEMPORARY_PATCH_FILE": 2461cb0ef41Sopenharmony_ci RELEASE_WORKDIR + "v8-prepare-merge-tempfile-temporary-patch", 2471cb0ef41Sopenharmony_ci "COMMITMSG_FILE": RELEASE_WORKDIR + "v8-prepare-merge-tempfile-commitmsg", 2481cb0ef41Sopenharmony_ci } 2491cb0ef41Sopenharmony_ci 2501cb0ef41Sopenharmony_ci def _Steps(self): 2511cb0ef41Sopenharmony_ci return [ 2521cb0ef41Sopenharmony_ci Preparation, 2531cb0ef41Sopenharmony_ci CreateBranch, 2541cb0ef41Sopenharmony_ci SearchArchitecturePorts, 2551cb0ef41Sopenharmony_ci CreateCommitMessage, 2561cb0ef41Sopenharmony_ci ApplyPatches, 2571cb0ef41Sopenharmony_ci CommitLocal, 2581cb0ef41Sopenharmony_ci UploadStep, 2591cb0ef41Sopenharmony_ci CommitRepository, 2601cb0ef41Sopenharmony_ci CleanUp, 2611cb0ef41Sopenharmony_ci ] 2621cb0ef41Sopenharmony_ci 2631cb0ef41Sopenharmony_ci 2641cb0ef41Sopenharmony_ciif __name__ == "__main__": # pragma: no cover 2651cb0ef41Sopenharmony_ci sys.exit(MergeToBranch().Run()) 266