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 |