Chromium Code Reviews| 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" | |
| 120 # The commit message title is added below after the version is specified. | 118 # The commit message title is added below after the version is specified. |
| 121 self["new_commit_msg"] = "" | 119 self["new_commit_msg"] = "\n".join(map(lambda s: action_text % s, |
| 120 self["full_revision_list"])) | |
|
tandrii(chromium)
2014/11/05 12:59:15
I think this is shorter and more readable:
join(ac
Michael Achenbach
2014/11/05 13:30:37
Done.
| |
| 121 self["new_commit_msg"] += "\n\n" | |
| 122 | 122 |
| 123 for commit_hash in self["patch_commit_hashes"]: | 123 for commit_hash in self["full_revision_list"]: |
| 124 patch_merge_desc = self.GitLog(n=1, format="%s", git_hash=commit_hash) | 124 patch_merge_desc = self.GitLog(n=1, format="%s", git_hash=commit_hash) |
| 125 self["new_commit_msg"] += "%s\n\n" % patch_merge_desc | 125 self["new_commit_msg"] += "%s\n\n" % patch_merge_desc |
| 126 | 126 |
| 127 bugs = [] | 127 bugs = [] |
| 128 for commit_hash in self["patch_commit_hashes"]: | 128 for commit_hash in self["full_revision_list"]: |
| 129 msg = self.GitLog(n=1, git_hash=commit_hash) | 129 msg = self.GitLog(n=1, git_hash=commit_hash) |
| 130 for bug in re.findall(r"^[ \t]*BUG[ \t]*=[ \t]*(.*?)[ \t]*$", msg, | 130 for bug in re.findall(r"^[ \t]*BUG[ \t]*=[ \t]*(.*?)[ \t]*$", msg, |
| 131 re.M): | 131 re.M): |
| 132 bugs.extend(map(lambda s: s.strip(), bug.split(","))) | 132 bugs.extend(map(lambda s: s.strip(), bug.split(","))) |
|
tandrii(chromium)
2014/11/05 12:59:15
consider shorter:
bugs.extend(s.strip() for s in b
Michael Achenbach
2014/11/05 13:30:37
Done.
| |
| 133 bug_aggregate = ",".join(sorted(filter(lambda s: s and s != "none", bugs))) | 133 bug_aggregate = ",".join(sorted(filter(lambda s: s and s != "none", bugs))) |
| 134 if bug_aggregate: | 134 if bug_aggregate: |
| 135 self["new_commit_msg"] += "BUG=%s\nLOG=N\n" % bug_aggregate | 135 self["new_commit_msg"] += "BUG=%s\nLOG=N\n" % bug_aggregate |
| 136 | 136 |
| 137 | 137 |
| 138 class ApplyPatches(Step): | 138 class ApplyPatches(Step): |
| 139 MESSAGE = "Apply patches for selected revisions." | 139 MESSAGE = "Apply patches for selected revisions." |
| 140 | 140 |
| 141 def RunStep(self): | 141 def RunStep(self): |
| 142 for commit_hash in self["patch_commit_hashes"]: | 142 for commit_hash in self["full_revision_list"]: |
| 143 print("Applying patch for %s to %s..." | 143 print("Applying patch for %s to %s..." |
| 144 % (commit_hash, self["merge_to_branch"])) | 144 % (commit_hash, self["merge_to_branch"])) |
| 145 patch = self.GitGetPatch(commit_hash) | 145 patch = self.GitGetPatch(commit_hash) |
| 146 TextToFile(patch, self.Config("TEMPORARY_PATCH_FILE")) | 146 TextToFile(patch, self.Config("TEMPORARY_PATCH_FILE")) |
| 147 self.ApplyPatch(self.Config("TEMPORARY_PATCH_FILE"), self._options.revert) | 147 self.ApplyPatch(self.Config("TEMPORARY_PATCH_FILE"), self._options.revert) |
| 148 if self._options.patch: | 148 if self._options.patch: |
| 149 self.ApplyPatch(self._options.patch, self._options.revert) | 149 self.ApplyPatch(self._options.patch, self._options.revert) |
| 150 | 150 |
| 151 | 151 |
| 152 class PrepareVersion(Step): | 152 class PrepareVersion(Step): |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 182 self["new_minor"], | 182 self["new_minor"], |
| 183 self["new_build"], | 183 self["new_build"], |
| 184 self["new_patch"]) | 184 self["new_patch"]) |
| 185 | 185 |
| 186 | 186 |
| 187 class CommitLocal(Step): | 187 class CommitLocal(Step): |
| 188 MESSAGE = "Commit to local branch." | 188 MESSAGE = "Commit to local branch." |
| 189 | 189 |
| 190 def RunStep(self): | 190 def RunStep(self): |
| 191 # Add a commit message title. | 191 # Add a commit message title. |
| 192 if self._options.revert: | 192 if self._options.revert and self._options.revert_bleeding_edge: |
| 193 if not self._options.revert_bleeding_edge: | 193 # TODO(machenbach): Find a better convention if multiple patches are |
| 194 title = ("Version %s (rollback of %s)" | 194 # reverted in one CL. |
| 195 % (self["version"], self["revision_list"])) | 195 self["commit_title"] = "Revert on master" |
| 196 else: | |
| 197 title = "Revert %s." % self["revision_list"] | |
| 198 else: | 196 else: |
| 199 title = ("Version %s (merged %s)" | 197 self["commit_title"] = "Version %s (cherry-pick)" % self["version"] |
| 200 % (self["version"], self["revision_list"])) | 198 self["new_commit_msg"] = "%s\n\n%s" % (self["commit_title"], |
| 201 self["commit_title"] = title | 199 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")) | 200 TextToFile(self["new_commit_msg"], self.Config("COMMITMSG_FILE")) |
| 204 self.GitCommit(file_name=self.Config("COMMITMSG_FILE")) | 201 self.GitCommit(file_name=self.Config("COMMITMSG_FILE")) |
| 205 | 202 |
| 206 | 203 |
| 207 class CommitRepository(Step): | 204 class CommitRepository(Step): |
| 208 MESSAGE = "Commit to the repository." | 205 MESSAGE = "Commit to the repository." |
| 209 | 206 |
| 210 def RunStep(self): | 207 def RunStep(self): |
| 211 self.GitCheckout(self.Config("BRANCHNAME")) | 208 self.GitCheckout(self.Config("BRANCHNAME")) |
| 212 self.WaitForLGTM() | 209 self.WaitForLGTM() |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 268 if len(options.revisions) < 1: | 265 if len(options.revisions) < 1: |
| 269 if not options.patch: | 266 if not options.patch: |
| 270 print "Either a patch file or revision numbers must be specified" | 267 print "Either a patch file or revision numbers must be specified" |
| 271 return False | 268 return False |
| 272 if not options.message: | 269 if not options.message: |
| 273 print "You must specify a merge comment if no patches are specified" | 270 print "You must specify a merge comment if no patches are specified" |
| 274 return False | 271 return False |
| 275 options.bypass_upload_hooks = True | 272 options.bypass_upload_hooks = True |
| 276 # CC ulan to make sure that fixes are merged to Google3. | 273 # CC ulan to make sure that fixes are merged to Google3. |
| 277 options.cc = "ulan@chromium.org" | 274 options.cc = "ulan@chromium.org" |
| 275 | |
| 276 # Thd old git-svn workflow is deprecated for this script. | |
| 277 assert options.vc_interface != "git_svn" | |
| 278 | |
| 279 # Make sure to use git hashes in the new workflows. | |
| 280 for revision in options.revisions: | |
| 281 if (IsSvnNumber(revision) or | |
| 282 (revision[0:1] == "r" and IsSvnNumber(revision[1:]))): | |
| 283 print "Please provide full git hashes of the patches to merge." | |
| 284 print "Got: %s" % revision | |
| 285 return False | |
| 278 return True | 286 return True |
| 279 | 287 |
| 280 def _Config(self): | 288 def _Config(self): |
| 281 return { | 289 return { |
| 282 "BRANCHNAME": "prepare-merge", | 290 "BRANCHNAME": "prepare-merge", |
| 283 "PERSISTFILE_BASENAME": "/tmp/v8-merge-to-branch-tempfile", | 291 "PERSISTFILE_BASENAME": "/tmp/v8-merge-to-branch-tempfile", |
| 284 "ALREADY_MERGING_SENTINEL_FILE": | 292 "ALREADY_MERGING_SENTINEL_FILE": |
| 285 "/tmp/v8-merge-to-branch-tempfile-already-merging", | 293 "/tmp/v8-merge-to-branch-tempfile-already-merging", |
| 286 "TEMPORARY_PATCH_FILE": "/tmp/v8-prepare-merge-tempfile-temporary-patch", | 294 "TEMPORARY_PATCH_FILE": "/tmp/v8-prepare-merge-tempfile-temporary-patch", |
| 287 "COMMITMSG_FILE": "/tmp/v8-prepare-merge-tempfile-commitmsg", | 295 "COMMITMSG_FILE": "/tmp/v8-prepare-merge-tempfile-commitmsg", |
| 288 } | 296 } |
| 289 | 297 |
| 290 def _Steps(self): | 298 def _Steps(self): |
| 291 return [ | 299 return [ |
| 292 Preparation, | 300 Preparation, |
| 293 CreateBranch, | 301 CreateBranch, |
| 294 SearchArchitecturePorts, | 302 SearchArchitecturePorts, |
| 295 FindGitRevisions, | 303 CreateCommitMessage, |
| 296 ApplyPatches, | 304 ApplyPatches, |
| 297 PrepareVersion, | 305 PrepareVersion, |
| 298 IncrementVersion, | 306 IncrementVersion, |
| 299 CommitLocal, | 307 CommitLocal, |
| 300 UploadStep, | 308 UploadStep, |
| 301 CommitRepository, | 309 CommitRepository, |
| 302 TagRevision, | 310 TagRevision, |
| 303 CleanUp, | 311 CleanUp, |
| 304 ] | 312 ] |
| 305 | 313 |
| 306 | 314 |
| 307 if __name__ == "__main__": # pragma: no cover | 315 if __name__ == "__main__": # pragma: no cover |
| 308 sys.exit(MergeToBranch().Run()) | 316 sys.exit(MergeToBranch().Run()) |
| OLD | NEW |