Index: tools/push-to-trunk/merge_to_branch.py |
diff --git a/tools/push-to-trunk/merge_to_branch.py b/tools/push-to-trunk/merge_to_branch.py |
deleted file mode 100755 |
index 7aa9fb6ab68c124f63eddda067d0f91149c4c5b5..0000000000000000000000000000000000000000 |
--- a/tools/push-to-trunk/merge_to_branch.py |
+++ /dev/null |
@@ -1,316 +0,0 @@ |
-#!/usr/bin/env python |
-# Copyright 2014 the V8 project authors. All rights reserved. |
-# Redistribution and use in source and binary forms, with or without |
-# modification, are permitted provided that the following conditions are |
-# met: |
-# |
-# * Redistributions of source code must retain the above copyright |
-# notice, this list of conditions and the following disclaimer. |
-# * Redistributions in binary form must reproduce the above |
-# copyright notice, this list of conditions and the following |
-# disclaimer in the documentation and/or other materials provided |
-# with the distribution. |
-# * Neither the name of Google Inc. nor the names of its |
-# contributors may be used to endorse or promote products derived |
-# from this software without specific prior written permission. |
-# |
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
- |
-import argparse |
-from collections import OrderedDict |
-import sys |
- |
-from common_includes import * |
- |
-def IsSvnNumber(rev): |
- return rev.isdigit() and len(rev) < 8 |
- |
-class Preparation(Step): |
- MESSAGE = "Preparation." |
- |
- def RunStep(self): |
- if os.path.exists(self.Config("ALREADY_MERGING_SENTINEL_FILE")): |
- if self._options.force: |
- os.remove(self.Config("ALREADY_MERGING_SENTINEL_FILE")) |
- elif self._options.step == 0: # pragma: no cover |
- self.Die("A merge is already in progress") |
- open(self.Config("ALREADY_MERGING_SENTINEL_FILE"), "a").close() |
- |
- self.InitialEnvironmentChecks(self.default_cwd) |
- if self._options.revert_master: |
- # FIXME(machenbach): Make revert master obsolete? |
- self["merge_to_branch"] = "master" |
- elif self._options.branch: |
- self["merge_to_branch"] = self._options.branch |
- else: # pragma: no cover |
- self.Die("Please specify a branch to merge to") |
- |
- self.CommonPrepare() |
- self.PrepareBranch() |
- |
- |
-class CreateBranch(Step): |
- MESSAGE = "Create a fresh branch for the patch." |
- |
- def RunStep(self): |
- self.GitCreateBranch(self.Config("BRANCHNAME"), |
- self.vc.RemoteBranch(self["merge_to_branch"])) |
- |
- |
-class SearchArchitecturePorts(Step): |
- MESSAGE = "Search for corresponding architecture ports." |
- |
- def RunStep(self): |
- self["full_revision_list"] = list(OrderedDict.fromkeys( |
- self._options.revisions)) |
- port_revision_list = [] |
- for revision in self["full_revision_list"]: |
- # Search for commits which matches the "Port XXX" pattern. |
- git_hashes = self.GitLog(reverse=True, format="%H", |
- grep="Port %s" % revision, |
- branch=self.vc.RemoteMasterBranch()) |
- for git_hash in git_hashes.splitlines(): |
- revision_title = self.GitLog(n=1, format="%s", git_hash=git_hash) |
- |
- # Is this revision included in the original revision list? |
- if git_hash in self["full_revision_list"]: |
- print("Found port of %s -> %s (already included): %s" |
- % (revision, git_hash, revision_title)) |
- else: |
- print("Found port of %s -> %s: %s" |
- % (revision, git_hash, revision_title)) |
- port_revision_list.append(git_hash) |
- |
- # Do we find any port? |
- if len(port_revision_list) > 0: |
- if self.Confirm("Automatically add corresponding ports (%s)?" |
- % ", ".join(port_revision_list)): |
- #: 'y': Add ports to revision list. |
- self["full_revision_list"].extend(port_revision_list) |
- |
- |
-class CreateCommitMessage(Step): |
- MESSAGE = "Create commit message." |
- |
- def RunStep(self): |
- |
- # Stringify: [123, 234] -> "r123, r234" |
- self["revision_list"] = ", ".join(map(lambda s: "r%s" % s, |
- self["full_revision_list"])) |
- |
- if not self["revision_list"]: # pragma: no cover |
- self.Die("Revision list is empty.") |
- |
- if self._options.revert and not self._options.revert_master: |
- action_text = "Rollback of %s" |
- else: |
- action_text = "Merged %s" |
- |
- # The commit message title is added below after the version is specified. |
- msg_pieces = [ |
- "\n".join(action_text % s for s in self["full_revision_list"]), |
- ] |
- msg_pieces.append("\n\n") |
- |
- for commit_hash in self["full_revision_list"]: |
- patch_merge_desc = self.GitLog(n=1, format="%s", git_hash=commit_hash) |
- msg_pieces.append("%s\n\n" % patch_merge_desc) |
- |
- bugs = [] |
- for commit_hash in self["full_revision_list"]: |
- msg = self.GitLog(n=1, git_hash=commit_hash) |
- for bug in re.findall(r"^[ \t]*BUG[ \t]*=[ \t]*(.*?)[ \t]*$", msg, re.M): |
- bugs.extend(s.strip() for s in bug.split(",")) |
- bug_aggregate = ",".join(sorted(filter(lambda s: s and s != "none", bugs))) |
- if bug_aggregate: |
- msg_pieces.append("BUG=%s\nLOG=N\n" % bug_aggregate) |
- |
- self["new_commit_msg"] = "".join(msg_pieces) |
- |
- |
-class ApplyPatches(Step): |
- MESSAGE = "Apply patches for selected revisions." |
- |
- def RunStep(self): |
- for commit_hash in self["full_revision_list"]: |
- print("Applying patch for %s to %s..." |
- % (commit_hash, self["merge_to_branch"])) |
- patch = self.GitGetPatch(commit_hash) |
- TextToFile(patch, self.Config("TEMPORARY_PATCH_FILE")) |
- self.ApplyPatch(self.Config("TEMPORARY_PATCH_FILE"), self._options.revert) |
- if self._options.patch: |
- self.ApplyPatch(self._options.patch, self._options.revert) |
- |
- |
-class PrepareVersion(Step): |
- MESSAGE = "Prepare version file." |
- |
- def RunStep(self): |
- if self._options.revert_master: |
- return |
- # This is used to calculate the patch level increment. |
- self.ReadAndPersistVersion() |
- |
- |
-class IncrementVersion(Step): |
- MESSAGE = "Increment version number." |
- |
- def RunStep(self): |
- if self._options.revert_master: |
- return |
- new_patch = str(int(self["patch"]) + 1) |
- if self.Confirm("Automatically increment PATCH_LEVEL? (Saying 'n' will " |
- "fire up your EDITOR on %s so you can make arbitrary " |
- "changes. When you're done, save the file and exit your " |
- "EDITOR.)" % VERSION_FILE): |
- text = FileToText(os.path.join(self.default_cwd, VERSION_FILE)) |
- text = MSub(r"(?<=#define PATCH_LEVEL)(?P<space>\s+)\d*$", |
- r"\g<space>%s" % new_patch, |
- text) |
- TextToFile(text, os.path.join(self.default_cwd, VERSION_FILE)) |
- else: |
- self.Editor(os.path.join(self.default_cwd, VERSION_FILE)) |
- self.ReadAndPersistVersion("new_") |
- self["version"] = "%s.%s.%s.%s" % (self["new_major"], |
- self["new_minor"], |
- self["new_build"], |
- self["new_patch"]) |
- |
- |
-class CommitLocal(Step): |
- MESSAGE = "Commit to local branch." |
- |
- def RunStep(self): |
- # Add a commit message title. |
- if self._options.revert and self._options.revert_master: |
- # TODO(machenbach): Find a better convention if multiple patches are |
- # reverted in one CL. |
- self["commit_title"] = "Revert on master" |
- else: |
- self["commit_title"] = "Version %s (cherry-pick)" % self["version"] |
- self["new_commit_msg"] = "%s\n\n%s" % (self["commit_title"], |
- self["new_commit_msg"]) |
- TextToFile(self["new_commit_msg"], self.Config("COMMITMSG_FILE")) |
- self.GitCommit(file_name=self.Config("COMMITMSG_FILE")) |
- |
- |
-class CommitRepository(Step): |
- MESSAGE = "Commit to the repository." |
- |
- def RunStep(self): |
- self.GitCheckout(self.Config("BRANCHNAME")) |
- self.WaitForLGTM() |
- self.GitPresubmit() |
- self.vc.CLLand() |
- |
- |
-class TagRevision(Step): |
- MESSAGE = "Create the tag." |
- |
- def RunStep(self): |
- if self._options.revert_master: |
- return |
- print "Creating tag %s" % self["version"] |
- self.vc.Tag(self["version"], |
- self.vc.RemoteBranch(self["merge_to_branch"]), |
- self["commit_title"]) |
- |
- |
-class CleanUp(Step): |
- MESSAGE = "Cleanup." |
- |
- def RunStep(self): |
- self.CommonCleanup() |
- if not self._options.revert_master: |
- print "*** SUMMARY ***" |
- print "version: %s" % self["version"] |
- print "branch: %s" % self["merge_to_branch"] |
- if self["revision_list"]: |
- print "patches: %s" % self["revision_list"] |
- |
- |
-class MergeToBranch(ScriptsBase): |
- def _Description(self): |
- return ("Performs the necessary steps to merge revisions from " |
- "master to other branches, including candidates.") |
- |
- def _PrepareOptions(self, parser): |
- group = parser.add_mutually_exclusive_group(required=True) |
- group.add_argument("--branch", help="The branch to merge to.") |
- group.add_argument("-R", "--revert-master", |
- help="Revert specified patches from master.", |
- default=False, action="store_true") |
- parser.add_argument("revisions", nargs="*", |
- help="The revisions to merge.") |
- parser.add_argument("-f", "--force", |
- help="Delete sentinel file.", |
- default=False, action="store_true") |
- parser.add_argument("-m", "--message", |
- help="A commit message for the patch.") |
- parser.add_argument("--revert", |
- help="Revert specified patches.", |
- default=False, action="store_true") |
- parser.add_argument("-p", "--patch", |
- help="A patch file to apply as part of the merge.") |
- |
- def _ProcessOptions(self, options): |
- # TODO(machenbach): Add a test that covers revert from master |
- if len(options.revisions) < 1: |
- if not options.patch: |
- print "Either a patch file or revision numbers must be specified" |
- return False |
- if not options.message: |
- print "You must specify a merge comment if no patches are specified" |
- return False |
- options.bypass_upload_hooks = True |
- # CC ulan to make sure that fixes are merged to Google3. |
- options.cc = "ulan@chromium.org" |
- |
- # Make sure to use git hashes in the new workflows. |
- for revision in options.revisions: |
- if (IsSvnNumber(revision) or |
- (revision[0:1] == "r" and IsSvnNumber(revision[1:]))): |
- print "Please provide full git hashes of the patches to merge." |
- print "Got: %s" % revision |
- return False |
- return True |
- |
- def _Config(self): |
- return { |
- "BRANCHNAME": "prepare-merge", |
- "PERSISTFILE_BASENAME": "/tmp/v8-merge-to-branch-tempfile", |
- "ALREADY_MERGING_SENTINEL_FILE": |
- "/tmp/v8-merge-to-branch-tempfile-already-merging", |
- "TEMPORARY_PATCH_FILE": "/tmp/v8-prepare-merge-tempfile-temporary-patch", |
- "COMMITMSG_FILE": "/tmp/v8-prepare-merge-tempfile-commitmsg", |
- } |
- |
- def _Steps(self): |
- return [ |
- Preparation, |
- CreateBranch, |
- SearchArchitecturePorts, |
- CreateCommitMessage, |
- ApplyPatches, |
- PrepareVersion, |
- IncrementVersion, |
- CommitLocal, |
- UploadStep, |
- CommitRepository, |
- TagRevision, |
- CleanUp, |
- ] |
- |
- |
-if __name__ == "__main__": # pragma: no cover |
- sys.exit(MergeToBranch().Run()) |