Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1)

Side by Side Diff: tools/release/merge_to_branch.py

Issue 1398033003: [Release] Update merge script to leverage auto-tag bot (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Adressed comments Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « tools/release/git_recipes.py ('k') | tools/release/roll_merge.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 29 matching lines...) Expand all
40 40
41 def RunStep(self): 41 def RunStep(self):
42 if os.path.exists(self.Config("ALREADY_MERGING_SENTINEL_FILE")): 42 if os.path.exists(self.Config("ALREADY_MERGING_SENTINEL_FILE")):
43 if self._options.force: 43 if self._options.force:
44 os.remove(self.Config("ALREADY_MERGING_SENTINEL_FILE")) 44 os.remove(self.Config("ALREADY_MERGING_SENTINEL_FILE"))
45 elif self._options.step == 0: # pragma: no cover 45 elif self._options.step == 0: # pragma: no cover
46 self.Die("A merge is already in progress") 46 self.Die("A merge is already in progress")
47 open(self.Config("ALREADY_MERGING_SENTINEL_FILE"), "a").close() 47 open(self.Config("ALREADY_MERGING_SENTINEL_FILE"), "a").close()
48 48
49 self.InitialEnvironmentChecks(self.default_cwd) 49 self.InitialEnvironmentChecks(self.default_cwd)
50 if self._options.branch: 50
51 self["merge_to_branch"] = self._options.branch 51 self["merge_to_branch"] = self._options.branch
52 else: # pragma: no cover
53 self.Die("Please specify a branch to merge to")
54 52
55 self.CommonPrepare() 53 self.CommonPrepare()
56 self.PrepareBranch() 54 self.PrepareBranch()
57 55
58 56
59 class CreateBranch(Step): 57 class CreateBranch(Step):
60 MESSAGE = "Create a fresh branch for the patch." 58 MESSAGE = "Create a fresh branch for the patch."
61 59
62 def RunStep(self): 60 def RunStep(self):
63 self.GitCreateBranch(self.Config("BRANCHNAME"), 61 self.GitCreateBranch(self.Config("BRANCHNAME"),
64 self.vc.RemoteBranch(self["merge_to_branch"])) 62 self.vc.RemoteBranch(self["merge_to_branch"]))
65 63
66 64
67 class SearchArchitecturePorts(Step): 65 class SearchArchitecturePorts(Step):
68 MESSAGE = "Search for corresponding architecture ports." 66 MESSAGE = "Search for corresponding architecture ports."
69 67
70 def RunStep(self): 68 def RunStep(self):
71 self["full_revision_list"] = list(OrderedDict.fromkeys( 69 self["full_revision_list"] = list(OrderedDict.fromkeys(
72 self._options.revisions)) 70 self._options.revisions))
73 port_revision_list = [] 71 port_revision_list = []
74 for revision in self["full_revision_list"]: 72 for revision in self["full_revision_list"]:
75 # Search for commits which matches the "Port XXX" pattern. 73 # Search for commits which matches the "Port XXX" pattern.
76 git_hashes = self.GitLog(reverse=True, format="%H", 74 git_hashes = self.GitLog(reverse=True, format="%H",
77 grep="Port %s" % revision, 75 grep="^[Pp]ort %s" % revision,
78 branch=self.vc.RemoteMasterBranch()) 76 branch=self.vc.RemoteMasterBranch())
79 for git_hash in git_hashes.splitlines(): 77 for git_hash in git_hashes.splitlines():
80 revision_title = self.GitLog(n=1, format="%s", git_hash=git_hash) 78 revision_title = self.GitLog(n=1, format="%s", git_hash=git_hash)
81 79
82 # Is this revision included in the original revision list? 80 # Is this revision included in the original revision list?
83 if git_hash in self["full_revision_list"]: 81 if git_hash in self["full_revision_list"]:
84 print("Found port of %s -> %s (already included): %s" 82 print("Found port of %s -> %s (already included): %s"
85 % (revision, git_hash, revision_title)) 83 % (revision, git_hash, revision_title))
86 else: 84 else:
87 print("Found port of %s -> %s: %s" 85 print("Found port of %s -> %s: %s"
88 % (revision, git_hash, revision_title)) 86 % (revision, git_hash, revision_title))
89 port_revision_list.append(git_hash) 87 port_revision_list.append(git_hash)
90 88
91 # Do we find any port? 89 # Do we find any port?
92 if len(port_revision_list) > 0: 90 if len(port_revision_list) > 0:
93 if self.Confirm("Automatically add corresponding ports (%s)?" 91 if self.Confirm("Automatically add corresponding ports (%s)?"
94 % ", ".join(port_revision_list)): 92 % ", ".join(port_revision_list)):
95 #: 'y': Add ports to revision list. 93 #: 'y': Add ports to revision list.
96 self["full_revision_list"].extend(port_revision_list) 94 self["full_revision_list"].extend(port_revision_list)
97 95
98 96
99 class CreateCommitMessage(Step): 97 class CreateCommitMessage(Step):
100 MESSAGE = "Create commit message." 98 MESSAGE = "Create commit message."
101 99
100 def _create_commit_description(self, commit_hash):
101 patch_merge_desc = self.GitLog(n=1, format="%s", git_hash=commit_hash)
102 description = "Merged: " + patch_merge_desc + "\n"
103 description += "Revision: " + commit_hash + "\n\n"
Michael Achenbach 2016/07/22 06:45:56 Is this revision to be processed by scripts or inf
Michael Hablich 2016/07/22 08:55:59 Scripts
104 return description
105
102 def RunStep(self): 106 def RunStep(self):
103 107
104 # Stringify: ["abcde", "12345"] -> "abcde, 12345" 108 # Stringify: ["abcde", "12345"] -> "abcde, 12345"
105 self["revision_list"] = ", ".join(self["full_revision_list"]) 109 self["revision_list"] = ", ".join(self["full_revision_list"])
106 110
107 if not self["revision_list"]: # pragma: no cover 111 if not self["revision_list"]: # pragma: no cover
108 self.Die("Revision list is empty.") 112 self.Die("Revision list is empty.")
109 113
110 action_text = "Merged %s" 114 msg_pieces = []
111 115
112 # The commit message title is added below after the version is specified. 116 if len(self["full_revision_list"]) > 1:
113 msg_pieces = [ 117 self["commit_title"] = "Merged: Squashed multiple commits."
114 "\n".join(action_text % s for s in self["full_revision_list"]), 118 for commit_hash in self["full_revision_list"]:
115 ] 119 msg_pieces.append(self._create_commit_description(commit_hash))
116 msg_pieces.append("\n\n") 120 else:
117 121 commit_hash = self["full_revision_list"][0]
118 for commit_hash in self["full_revision_list"]: 122 full_description = self._create_commit_description(commit_hash).split("\n" )
119 patch_merge_desc = self.GitLog(n=1, format="%s", git_hash=commit_hash) 123 self["commit_title"] = full_description[0]
Michael Achenbach 2016/07/22 06:45:57 We might need to ensure the length of the title he
Michael Hablich 2016/07/22 08:55:59 The web interface gives me the ability to create a
Michael Achenbach 2016/07/22 09:12:24 Wondering if 80 is too aggressive. We have commit
120 msg_pieces.append("%s\n\n" % patch_merge_desc) 124 msg_pieces.append(full_description[1] + "\n\n")
121 125
122 bugs = [] 126 bugs = []
123 for commit_hash in self["full_revision_list"]: 127 for commit_hash in self["full_revision_list"]:
124 msg = self.GitLog(n=1, git_hash=commit_hash) 128 msg = self.GitLog(n=1, git_hash=commit_hash)
125 for bug in re.findall(r"^[ \t]*BUG[ \t]*=[ \t]*(.*?)[ \t]*$", msg, re.M): 129 for bug in re.findall(r"^[ \t]*BUG[ \t]*=[ \t]*(.*?)[ \t]*$", msg, re.M):
126 bugs.extend(s.strip() for s in bug.split(",")) 130 bugs.extend(s.strip() for s in bug.split(","))
127 bug_aggregate = ",".join(sorted(filter(lambda s: s and s != "none", bugs))) 131 bug_aggregate = ",".join(sorted(filter(lambda s: s and s != "none", bugs)))
128 if bug_aggregate: 132 if bug_aggregate:
129 msg_pieces.append("BUG=%s\nLOG=N\n" % bug_aggregate) 133 msg_pieces.append("BUG=%s\nLOG=N\n" % bug_aggregate)
130 134
135 msg_pieces.append("NOTRY=true\nNOPRESUBMIT=true\n")
136
131 self["new_commit_msg"] = "".join(msg_pieces) 137 self["new_commit_msg"] = "".join(msg_pieces)
132 138
133 139
134 class ApplyPatches(Step): 140 class ApplyPatches(Step):
135 MESSAGE = "Apply patches for selected revisions." 141 MESSAGE = "Apply patches for selected revisions."
136 142
137 def RunStep(self): 143 def RunStep(self):
138 for commit_hash in self["full_revision_list"]: 144 for commit_hash in self["full_revision_list"]:
139 print("Applying patch for %s to %s..." 145 print("Applying patch for %s to %s..."
140 % (commit_hash, self["merge_to_branch"])) 146 % (commit_hash, self["merge_to_branch"]))
141 patch = self.GitGetPatch(commit_hash) 147 patch = self.GitGetPatch(commit_hash)
142 TextToFile(patch, self.Config("TEMPORARY_PATCH_FILE")) 148 TextToFile(patch, self.Config("TEMPORARY_PATCH_FILE"))
143 self.ApplyPatch(self.Config("TEMPORARY_PATCH_FILE")) 149 self.ApplyPatch(self.Config("TEMPORARY_PATCH_FILE"))
144 if self._options.patch: 150 if self._options.patch:
145 self.ApplyPatch(self._options.patch) 151 self.ApplyPatch(self._options.patch)
146 152
147
148 class PrepareVersion(Step):
149 MESSAGE = "Prepare version file."
150
151 def RunStep(self):
152 # This is used to calculate the patch level increment.
153 self.ReadAndPersistVersion()
154
155
156 class IncrementVersion(Step):
157 MESSAGE = "Increment version number."
158
159 def RunStep(self):
160 new_patch = str(int(self["patch"]) + 1)
161 if self.Confirm("Automatically increment V8_PATCH_LEVEL? (Saying 'n' will "
162 "fire up your EDITOR on %s so you can make arbitrary "
163 "changes. When you're done, save the file and exit your "
164 "EDITOR.)" % VERSION_FILE):
165 text = FileToText(os.path.join(self.default_cwd, VERSION_FILE))
166 text = MSub(r"(?<=#define V8_PATCH_LEVEL)(?P<space>\s+)\d*$",
167 r"\g<space>%s" % new_patch,
168 text)
169 TextToFile(text, os.path.join(self.default_cwd, VERSION_FILE))
170 else:
171 self.Editor(os.path.join(self.default_cwd, VERSION_FILE))
172 self.ReadAndPersistVersion("new_")
173 self["version"] = "%s.%s.%s.%s" % (self["new_major"],
174 self["new_minor"],
175 self["new_build"],
176 self["new_patch"])
177
178
179 class CommitLocal(Step): 153 class CommitLocal(Step):
180 MESSAGE = "Commit to local branch." 154 MESSAGE = "Commit to local branch."
181 155
182 def RunStep(self): 156 def RunStep(self):
183 # Add a commit message title. 157 # Add a commit message title.
184 self["commit_title"] = "Version %s (cherry-pick)" % self["version"]
185 self["new_commit_msg"] = "%s\n\n%s" % (self["commit_title"], 158 self["new_commit_msg"] = "%s\n\n%s" % (self["commit_title"],
186 self["new_commit_msg"]) 159 self["new_commit_msg"])
187 TextToFile(self["new_commit_msg"], self.Config("COMMITMSG_FILE")) 160 TextToFile(self["new_commit_msg"], self.Config("COMMITMSG_FILE"))
188 self.GitCommit(file_name=self.Config("COMMITMSG_FILE")) 161 self.GitCommit(file_name=self.Config("COMMITMSG_FILE"))
189 162
163 class AddInformationalComment(Step):
164 MESSAGE = 'Show additional information.'
165
166 def RunStep(self):
167 message = ("IMPORTANT: This script will no longer automatically "
Michael Achenbach 2016/07/22 06:45:56 I like it. I'd substitute IMPORTANT with NOTE or s
Michael Hablich 2016/07/22 08:55:59 Done.
168 "update include/v8-version.h "
169 "and create a tag. This is done automatically by the autotag bot. "
170 "Please call the merge_to_branch.py with --help for more information.")
171
172 self.GitCLAddComment(message)
190 173
191 class CommitRepository(Step): 174 class CommitRepository(Step):
192 MESSAGE = "Commit to the repository." 175 MESSAGE = "Commit to the repository."
193 176
194 def RunStep(self): 177 def RunStep(self):
195 self.GitCheckout(self.Config("BRANCHNAME")) 178 self.GitCheckout(self.Config("BRANCHNAME"))
196 self.WaitForLGTM() 179 self.WaitForLGTM()
Michael Achenbach 2016/07/22 06:45:56 How about we change the default behavior to end th
Michael Hablich 2016/07/22 08:55:59 Good idea for the next iteration. The most importa
197 self.GitPresubmit() 180 self.GitPresubmit()
198 self.vc.CLLand() 181 self.vc.CLLand()
199 182
200
201 class TagRevision(Step):
202 MESSAGE = "Create the tag."
203
204 def RunStep(self):
205 print "Creating tag %s" % self["version"]
206 self.vc.Tag(self["version"],
207 self.vc.RemoteBranch(self["merge_to_branch"]),
208 self["commit_title"])
209
210
211 class CleanUp(Step): 183 class CleanUp(Step):
212 MESSAGE = "Cleanup." 184 MESSAGE = "Cleanup."
213 185
214 def RunStep(self): 186 def RunStep(self):
215 self.CommonCleanup() 187 self.CommonCleanup()
216 print "*** SUMMARY ***" 188 print "*** SUMMARY ***"
217 print "version: %s" % self["version"]
218 print "branch: %s" % self["merge_to_branch"] 189 print "branch: %s" % self["merge_to_branch"]
219 if self["revision_list"]: 190 if self["revision_list"]:
220 print "patches: %s" % self["revision_list"] 191 print "patches: %s" % self["revision_list"]
221 192
222 193
223 class MergeToBranch(ScriptsBase): 194 class MergeToBranch(ScriptsBase):
224 def _Description(self): 195 def _Description(self):
225 return ("Performs the necessary steps to merge revisions from " 196 return ("Performs the necessary steps to merge revisions from "
226 "master to other branches, including candidates.") 197 "master to release branches like 4.5. This script does not "
198 "version the commit. See http://goo.gl/9ke2Vw for more "
199 "information.")
227 200
228 def _PrepareOptions(self, parser): 201 def _PrepareOptions(self, parser):
229 group = parser.add_mutually_exclusive_group(required=True) 202 group = parser.add_mutually_exclusive_group(required=True)
230 group.add_argument("--branch", help="The branch to merge to.") 203 group.add_argument("--branch", help="The branch to merge to.")
231 parser.add_argument("revisions", nargs="*", 204 parser.add_argument("revisions", nargs="*",
232 help="The revisions to merge.") 205 help="The revisions to merge.")
233 parser.add_argument("-f", "--force", 206 parser.add_argument("-f", "--force",
234 help="Delete sentinel file.", 207 help="Delete sentinel file.",
235 default=False, action="store_true") 208 default=False, action="store_true")
236 parser.add_argument("-m", "--message", 209 parser.add_argument("-m", "--message",
237 help="A commit message for the patch.") 210 help="A commit message for the patch.")
238 parser.add_argument("-p", "--patch", 211 parser.add_argument("-p", "--patch",
239 help="A patch file to apply as part of the merge.") 212 help="A patch file to apply as part of the merge.")
240 213
241 def _ProcessOptions(self, options): 214 def _ProcessOptions(self, options):
242 if len(options.revisions) < 1: 215 if len(options.revisions) < 1:
243 if not options.patch: 216 if not options.patch:
244 print "Either a patch file or revision numbers must be specified" 217 print "Either a patch file or revision numbers must be specified"
245 return False 218 return False
246 if not options.message: 219 if not options.message:
247 print "You must specify a merge comment if no patches are specified" 220 print "You must specify a merge comment if no patches are specified"
248 return False 221 return False
249 options.bypass_upload_hooks = True 222 options.bypass_upload_hooks = True
250 # CC ulan to make sure that fixes are merged to Google3. 223 # CC ulan to make sure that fixes are merged to Google3.
251 options.cc = "ulan@chromium.org" 224 options.cc = "ulan@chromium.org"
252 225
226 if len(options.branch.split('.')) > 2:
227 print ("This script does not support merging to roll branches. "
228 "Please use tools/release/roll_merge.py for this use case.")
229 return False
230
253 # Make sure to use git hashes in the new workflows. 231 # Make sure to use git hashes in the new workflows.
254 for revision in options.revisions: 232 for revision in options.revisions:
255 if (IsSvnNumber(revision) or 233 if (IsSvnNumber(revision) or
256 (revision[0:1] == "r" and IsSvnNumber(revision[1:]))): 234 (revision[0:1] == "r" and IsSvnNumber(revision[1:]))):
257 print "Please provide full git hashes of the patches to merge." 235 print "Please provide full git hashes of the patches to merge."
258 print "Got: %s" % revision 236 print "Got: %s" % revision
259 return False 237 return False
260 return True 238 return True
261 239
262 def _Config(self): 240 def _Config(self):
263 return { 241 return {
264 "BRANCHNAME": "prepare-merge", 242 "BRANCHNAME": "prepare-merge",
265 "PERSISTFILE_BASENAME": "/tmp/v8-merge-to-branch-tempfile", 243 "PERSISTFILE_BASENAME": "/tmp/v8-merge-to-branch-tempfile",
266 "ALREADY_MERGING_SENTINEL_FILE": 244 "ALREADY_MERGING_SENTINEL_FILE":
267 "/tmp/v8-merge-to-branch-tempfile-already-merging", 245 "/tmp/v8-merge-to-branch-tempfile-already-merging",
268 "TEMPORARY_PATCH_FILE": "/tmp/v8-prepare-merge-tempfile-temporary-patch", 246 "TEMPORARY_PATCH_FILE": "/tmp/v8-prepare-merge-tempfile-temporary-patch",
269 "COMMITMSG_FILE": "/tmp/v8-prepare-merge-tempfile-commitmsg", 247 "COMMITMSG_FILE": "/tmp/v8-prepare-merge-tempfile-commitmsg",
270 } 248 }
271 249
272 def _Steps(self): 250 def _Steps(self):
273 return [ 251 return [
274 Preparation, 252 Preparation,
275 CreateBranch, 253 CreateBranch,
276 SearchArchitecturePorts, 254 SearchArchitecturePorts,
277 CreateCommitMessage, 255 CreateCommitMessage,
278 ApplyPatches, 256 ApplyPatches,
279 PrepareVersion,
280 IncrementVersion,
281 CommitLocal, 257 CommitLocal,
282 UploadStep, 258 UploadStep,
259 AddInformationalComment,
283 CommitRepository, 260 CommitRepository,
284 TagRevision,
285 CleanUp, 261 CleanUp,
286 ] 262 ]
287 263
288 264
289 if __name__ == "__main__": # pragma: no cover 265 if __name__ == "__main__": # pragma: no cover
290 sys.exit(MergeToBranch().Run()) 266 sys.exit(MergeToBranch().Run())
OLDNEW
« no previous file with comments | « tools/release/git_recipes.py ('k') | tools/release/roll_merge.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698