| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2014 the V8 project authors. All rights reserved. | 2 # Copyright 2014 the V8 project authors. All rights reserved. |
| 3 # Redistribution and use in source and binary forms, with or without | 3 # Redistribution and use in source and binary forms, with or without |
| 4 # modification, are permitted provided that the following conditions are | 4 # modification, are permitted provided that the following conditions are |
| 5 # met: | 5 # met: |
| 6 # | 6 # |
| 7 # * Redistributions of source code must retain the above copyright | 7 # * Redistributions of source code must retain the above copyright |
| 8 # notice, this list of conditions and the following disclaimer. | 8 # notice, this list of conditions and the following disclaimer. |
| 9 # * Redistributions in binary form must reproduce the above | 9 # * Redistributions in binary form must reproduce the above |
| 10 # copyright notice, this list of conditions and the following | 10 # copyright notice, this list of conditions and the following |
| (...skipping 14 matching lines...) Expand all Loading... |
| 25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 28 | 28 |
| 29 import argparse | 29 import argparse |
| 30 from collections import OrderedDict | 30 from collections import OrderedDict |
| 31 import sys | 31 import sys |
| 32 | 32 |
| 33 from common_includes import * | 33 from common_includes import * |
| 34 | 34 |
| 35 def IsSvnNumber(rev): |
| 36 return rev.isdigit() and len(rev) < 8 |
| 37 |
| 35 class Preparation(Step): | 38 class Preparation(Step): |
| 36 MESSAGE = "Preparation." | 39 MESSAGE = "Preparation." |
| 37 | 40 |
| 38 def RunStep(self): | 41 def RunStep(self): |
| 39 if os.path.exists(self.Config("ALREADY_MERGING_SENTINEL_FILE")): | 42 if os.path.exists(self.Config("ALREADY_MERGING_SENTINEL_FILE")): |
| 40 if self._options.force: | 43 if self._options.force: |
| 41 os.remove(self.Config("ALREADY_MERGING_SENTINEL_FILE")) | 44 os.remove(self.Config("ALREADY_MERGING_SENTINEL_FILE")) |
| 42 elif self._options.step == 0: # pragma: no cover | 45 elif self._options.step == 0: # pragma: no cover |
| 43 self.Die("A merge is already in progress") | 46 self.Die("A merge is already in progress") |
| 44 open(self.Config("ALREADY_MERGING_SENTINEL_FILE"), "a").close() | 47 open(self.Config("ALREADY_MERGING_SENTINEL_FILE"), "a").close() |
| (...skipping 20 matching lines...) Expand all Loading... |
| 65 | 68 |
| 66 | 69 |
| 67 class SearchArchitecturePorts(Step): | 70 class SearchArchitecturePorts(Step): |
| 68 MESSAGE = "Search for corresponding architecture ports." | 71 MESSAGE = "Search for corresponding architecture ports." |
| 69 | 72 |
| 70 def RunStep(self): | 73 def RunStep(self): |
| 71 self["full_revision_list"] = list(OrderedDict.fromkeys( | 74 self["full_revision_list"] = list(OrderedDict.fromkeys( |
| 72 self._options.revisions)) | 75 self._options.revisions)) |
| 73 port_revision_list = [] | 76 port_revision_list = [] |
| 74 for revision in self["full_revision_list"]: | 77 for revision in self["full_revision_list"]: |
| 75 # Search for commits which matches the "Port rXXX" pattern. | 78 # Search for commits which matches the "Port XXX" pattern. |
| 76 git_hashes = self.GitLog(reverse=True, format="%H", | 79 git_hashes = self.GitLog(reverse=True, format="%H", |
| 77 grep="Port r%d" % int(revision), | 80 grep="Port %s" % revision, |
| 78 branch=self.vc.RemoteMasterBranch()) | 81 branch=self.vc.RemoteMasterBranch()) |
| 79 for git_hash in git_hashes.splitlines(): | 82 for git_hash in git_hashes.splitlines(): |
| 80 svn_revision = self.vc.GitSvn(git_hash, self.vc.RemoteMasterBranch()) | |
| 81 if not svn_revision: # pragma: no cover | |
| 82 self.Die("Cannot determine svn revision for %s" % git_hash) | |
| 83 revision_title = self.GitLog(n=1, format="%s", git_hash=git_hash) | 83 revision_title = self.GitLog(n=1, format="%s", git_hash=git_hash) |
| 84 | 84 |
| 85 # Is this revision included in the original revision list? | 85 # Is this revision included in the original revision list? |
| 86 if svn_revision in self["full_revision_list"]: | 86 if git_hash in self["full_revision_list"]: |
| 87 print("Found port of r%s -> r%s (already included): %s" | 87 print("Found port of %s -> %s (already included): %s" |
| 88 % (revision, svn_revision, revision_title)) | 88 % (revision, git_hash, revision_title)) |
| 89 else: | 89 else: |
| 90 print("Found port of r%s -> r%s: %s" | 90 print("Found port of %s -> %s: %s" |
| 91 % (revision, svn_revision, revision_title)) | 91 % (revision, git_hash, revision_title)) |
| 92 port_revision_list.append(svn_revision) | 92 port_revision_list.append(git_hash) |
| 93 | 93 |
| 94 # Do we find any port? | 94 # Do we find any port? |
| 95 if len(port_revision_list) > 0: | 95 if len(port_revision_list) > 0: |
| 96 if self.Confirm("Automatically add corresponding ports (%s)?" | 96 if self.Confirm("Automatically add corresponding ports (%s)?" |
| 97 % ", ".join(port_revision_list)): | 97 % ", ".join(port_revision_list)): |
| 98 #: 'y': Add ports to revision list. | 98 #: 'y': Add ports to revision list. |
| 99 self["full_revision_list"].extend(port_revision_list) | 99 self["full_revision_list"].extend(port_revision_list) |
| 100 | 100 |
| 101 | 101 |
| 102 class FindGitRevisions(Step): | 102 class CreateCommitMessage(Step): |
| 103 MESSAGE = "Find the git revisions associated with the patches." | 103 MESSAGE = "Create commit message." |
| 104 | 104 |
| 105 def RunStep(self): | 105 def RunStep(self): |
| 106 self["patch_commit_hashes"] = [] | |
| 107 for revision in self["full_revision_list"]: | |
| 108 next_hash = self.vc.SvnGit(revision, self.vc.RemoteMasterBranch()) | |
| 109 if not next_hash: # pragma: no cover | |
| 110 self.Die("Cannot determine git hash for r%s" % revision) | |
| 111 self["patch_commit_hashes"].append(next_hash) | |
| 112 | 106 |
| 113 # Stringify: [123, 234] -> "r123, r234" | 107 # Stringify: [123, 234] -> "r123, r234" |
| 114 self["revision_list"] = ", ".join(map(lambda s: "r%s" % s, | 108 self["revision_list"] = ", ".join(map(lambda s: "r%s" % s, |
| 115 self["full_revision_list"])) | 109 self["full_revision_list"])) |
| 116 | 110 |
| 117 if not self["revision_list"]: # pragma: no cover | 111 if not self["revision_list"]: # pragma: no cover |
| 118 self.Die("Revision list is empty.") | 112 self.Die("Revision list is empty.") |
| 119 | 113 |
| 114 if self._options.revert and not self._options.revert_bleeding_edge: |
| 115 action_text = "Rollback of %s" |
| 116 else: |
| 117 action_text = "Merged %s" |
| 118 |
| 120 # The commit message title is added below after the version is specified. | 119 # The commit message title is added below after the version is specified. |
| 121 self["new_commit_msg"] = "" | 120 msg_pieces = [ |
| 121 "\n".join(action_text % s for s in self["full_revision_list"]), |
| 122 ] |
| 123 msg_pieces.append("\n\n") |
| 122 | 124 |
| 123 for commit_hash in self["patch_commit_hashes"]: | 125 for commit_hash in self["full_revision_list"]: |
| 124 patch_merge_desc = self.GitLog(n=1, format="%s", git_hash=commit_hash) | 126 patch_merge_desc = self.GitLog(n=1, format="%s", git_hash=commit_hash) |
| 125 self["new_commit_msg"] += "%s\n\n" % patch_merge_desc | 127 msg_pieces.append("%s\n\n" % patch_merge_desc) |
| 126 | 128 |
| 127 bugs = [] | 129 bugs = [] |
| 128 for commit_hash in self["patch_commit_hashes"]: | 130 for commit_hash in self["full_revision_list"]: |
| 129 msg = self.GitLog(n=1, git_hash=commit_hash) | 131 msg = self.GitLog(n=1, git_hash=commit_hash) |
| 130 for bug in re.findall(r"^[ \t]*BUG[ \t]*=[ \t]*(.*?)[ \t]*$", msg, | 132 for bug in re.findall(r"^[ \t]*BUG[ \t]*=[ \t]*(.*?)[ \t]*$", msg, re.M): |
| 131 re.M): | 133 bugs.extend(s.strip() for s in bug.split(",")) |
| 132 bugs.extend(map(lambda s: s.strip(), bug.split(","))) | |
| 133 bug_aggregate = ",".join(sorted(filter(lambda s: s and s != "none", bugs))) | 134 bug_aggregate = ",".join(sorted(filter(lambda s: s and s != "none", bugs))) |
| 134 if bug_aggregate: | 135 if bug_aggregate: |
| 135 self["new_commit_msg"] += "BUG=%s\nLOG=N\n" % bug_aggregate | 136 msg_pieces.append("BUG=%s\nLOG=N\n" % bug_aggregate) |
| 137 |
| 138 self["new_commit_msg"] = "".join(msg_pieces) |
| 136 | 139 |
| 137 | 140 |
| 138 class ApplyPatches(Step): | 141 class ApplyPatches(Step): |
| 139 MESSAGE = "Apply patches for selected revisions." | 142 MESSAGE = "Apply patches for selected revisions." |
| 140 | 143 |
| 141 def RunStep(self): | 144 def RunStep(self): |
| 142 for commit_hash in self["patch_commit_hashes"]: | 145 for commit_hash in self["full_revision_list"]: |
| 143 print("Applying patch for %s to %s..." | 146 print("Applying patch for %s to %s..." |
| 144 % (commit_hash, self["merge_to_branch"])) | 147 % (commit_hash, self["merge_to_branch"])) |
| 145 patch = self.GitGetPatch(commit_hash) | 148 patch = self.GitGetPatch(commit_hash) |
| 146 TextToFile(patch, self.Config("TEMPORARY_PATCH_FILE")) | 149 TextToFile(patch, self.Config("TEMPORARY_PATCH_FILE")) |
| 147 self.ApplyPatch(self.Config("TEMPORARY_PATCH_FILE"), self._options.revert) | 150 self.ApplyPatch(self.Config("TEMPORARY_PATCH_FILE"), self._options.revert) |
| 148 if self._options.patch: | 151 if self._options.patch: |
| 149 self.ApplyPatch(self._options.patch, self._options.revert) | 152 self.ApplyPatch(self._options.patch, self._options.revert) |
| 150 | 153 |
| 151 | 154 |
| 152 class PrepareVersion(Step): | 155 class PrepareVersion(Step): |
| (...skipping 29 matching lines...) Expand all Loading... |
| 182 self["new_minor"], | 185 self["new_minor"], |
| 183 self["new_build"], | 186 self["new_build"], |
| 184 self["new_patch"]) | 187 self["new_patch"]) |
| 185 | 188 |
| 186 | 189 |
| 187 class CommitLocal(Step): | 190 class CommitLocal(Step): |
| 188 MESSAGE = "Commit to local branch." | 191 MESSAGE = "Commit to local branch." |
| 189 | 192 |
| 190 def RunStep(self): | 193 def RunStep(self): |
| 191 # Add a commit message title. | 194 # Add a commit message title. |
| 192 if self._options.revert: | 195 if self._options.revert and self._options.revert_bleeding_edge: |
| 193 if not self._options.revert_bleeding_edge: | 196 # TODO(machenbach): Find a better convention if multiple patches are |
| 194 title = ("Version %s (rollback of %s)" | 197 # reverted in one CL. |
| 195 % (self["version"], self["revision_list"])) | 198 self["commit_title"] = "Revert on master" |
| 196 else: | |
| 197 title = "Revert %s." % self["revision_list"] | |
| 198 else: | 199 else: |
| 199 title = ("Version %s (merged %s)" | 200 self["commit_title"] = "Version %s (cherry-pick)" % self["version"] |
| 200 % (self["version"], self["revision_list"])) | 201 self["new_commit_msg"] = "%s\n\n%s" % (self["commit_title"], |
| 201 self["commit_title"] = title | 202 self["new_commit_msg"]) |
| 202 self["new_commit_msg"] = "%s\n\n%s" % (title, self["new_commit_msg"]) | |
| 203 TextToFile(self["new_commit_msg"], self.Config("COMMITMSG_FILE")) | 203 TextToFile(self["new_commit_msg"], self.Config("COMMITMSG_FILE")) |
| 204 self.GitCommit(file_name=self.Config("COMMITMSG_FILE")) | 204 self.GitCommit(file_name=self.Config("COMMITMSG_FILE")) |
| 205 | 205 |
| 206 | 206 |
| 207 class CommitRepository(Step): | 207 class CommitRepository(Step): |
| 208 MESSAGE = "Commit to the repository." | 208 MESSAGE = "Commit to the repository." |
| 209 | 209 |
| 210 def RunStep(self): | 210 def RunStep(self): |
| 211 self.GitCheckout(self.Config("BRANCHNAME")) | 211 self.GitCheckout(self.Config("BRANCHNAME")) |
| 212 self.WaitForLGTM() | 212 self.WaitForLGTM() |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 268 if len(options.revisions) < 1: | 268 if len(options.revisions) < 1: |
| 269 if not options.patch: | 269 if not options.patch: |
| 270 print "Either a patch file or revision numbers must be specified" | 270 print "Either a patch file or revision numbers must be specified" |
| 271 return False | 271 return False |
| 272 if not options.message: | 272 if not options.message: |
| 273 print "You must specify a merge comment if no patches are specified" | 273 print "You must specify a merge comment if no patches are specified" |
| 274 return False | 274 return False |
| 275 options.bypass_upload_hooks = True | 275 options.bypass_upload_hooks = True |
| 276 # CC ulan to make sure that fixes are merged to Google3. | 276 # CC ulan to make sure that fixes are merged to Google3. |
| 277 options.cc = "ulan@chromium.org" | 277 options.cc = "ulan@chromium.org" |
| 278 |
| 279 # Thd old git-svn workflow is deprecated for this script. |
| 280 assert options.vc_interface != "git_svn" |
| 281 |
| 282 # Make sure to use git hashes in the new workflows. |
| 283 for revision in options.revisions: |
| 284 if (IsSvnNumber(revision) or |
| 285 (revision[0:1] == "r" and IsSvnNumber(revision[1:]))): |
| 286 print "Please provide full git hashes of the patches to merge." |
| 287 print "Got: %s" % revision |
| 288 return False |
| 278 return True | 289 return True |
| 279 | 290 |
| 280 def _Config(self): | 291 def _Config(self): |
| 281 return { | 292 return { |
| 282 "BRANCHNAME": "prepare-merge", | 293 "BRANCHNAME": "prepare-merge", |
| 283 "PERSISTFILE_BASENAME": "/tmp/v8-merge-to-branch-tempfile", | 294 "PERSISTFILE_BASENAME": "/tmp/v8-merge-to-branch-tempfile", |
| 284 "ALREADY_MERGING_SENTINEL_FILE": | 295 "ALREADY_MERGING_SENTINEL_FILE": |
| 285 "/tmp/v8-merge-to-branch-tempfile-already-merging", | 296 "/tmp/v8-merge-to-branch-tempfile-already-merging", |
| 286 "TEMPORARY_PATCH_FILE": "/tmp/v8-prepare-merge-tempfile-temporary-patch", | 297 "TEMPORARY_PATCH_FILE": "/tmp/v8-prepare-merge-tempfile-temporary-patch", |
| 287 "COMMITMSG_FILE": "/tmp/v8-prepare-merge-tempfile-commitmsg", | 298 "COMMITMSG_FILE": "/tmp/v8-prepare-merge-tempfile-commitmsg", |
| 288 } | 299 } |
| 289 | 300 |
| 290 def _Steps(self): | 301 def _Steps(self): |
| 291 return [ | 302 return [ |
| 292 Preparation, | 303 Preparation, |
| 293 CreateBranch, | 304 CreateBranch, |
| 294 SearchArchitecturePorts, | 305 SearchArchitecturePorts, |
| 295 FindGitRevisions, | 306 CreateCommitMessage, |
| 296 ApplyPatches, | 307 ApplyPatches, |
| 297 PrepareVersion, | 308 PrepareVersion, |
| 298 IncrementVersion, | 309 IncrementVersion, |
| 299 CommitLocal, | 310 CommitLocal, |
| 300 UploadStep, | 311 UploadStep, |
| 301 CommitRepository, | 312 CommitRepository, |
| 302 TagRevision, | 313 TagRevision, |
| 303 CleanUp, | 314 CleanUp, |
| 304 ] | 315 ] |
| 305 | 316 |
| 306 | 317 |
| 307 if __name__ == "__main__": # pragma: no cover | 318 if __name__ == "__main__": # pragma: no cover |
| 308 sys.exit(MergeToBranch().Run()) | 319 sys.exit(MergeToBranch().Run()) |
| OLD | NEW |