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: Truncate title more meaningful 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"
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:
121 commit_hash = self["full_revision_list"][0]
122 full_description = self._create_commit_description(commit_hash).split("\n" )
117 123
118 for commit_hash in self["full_revision_list"]: 124 #Truncate title because of code review tool
Michael Achenbach 2016/07/22 09:43:36 nit: space after #
119 patch_merge_desc = self.GitLog(n=1, format="%s", git_hash=commit_hash) 125 title = full_description[0]
120 msg_pieces.append("%s\n\n" % patch_merge_desc) 126 if len(title) > 100:
127 title = title[:96] + " ..."
128
129 self["commit_title"] = title
130 msg_pieces.append(full_description[1] + "\n\n")
121 131
122 bugs = [] 132 bugs = []
123 for commit_hash in self["full_revision_list"]: 133 for commit_hash in self["full_revision_list"]:
124 msg = self.GitLog(n=1, git_hash=commit_hash) 134 msg = self.GitLog(n=1, git_hash=commit_hash)
125 for bug in re.findall(r"^[ \t]*BUG[ \t]*=[ \t]*(.*?)[ \t]*$", msg, re.M): 135 for bug in re.findall(r"^[ \t]*BUG[ \t]*=[ \t]*(.*?)[ \t]*$", msg, re.M):
126 bugs.extend(s.strip() for s in bug.split(",")) 136 bugs.extend(s.strip() for s in bug.split(","))
127 bug_aggregate = ",".join(sorted(filter(lambda s: s and s != "none", bugs))) 137 bug_aggregate = ",".join(sorted(filter(lambda s: s and s != "none", bugs)))
128 if bug_aggregate: 138 if bug_aggregate:
129 msg_pieces.append("BUG=%s\nLOG=N\n" % bug_aggregate) 139 msg_pieces.append("BUG=%s\nLOG=N\n" % bug_aggregate)
130 140
141 msg_pieces.append("NOTRY=true\nNOPRESUBMIT=true\nNOTREECHECKS=true\n")
142
131 self["new_commit_msg"] = "".join(msg_pieces) 143 self["new_commit_msg"] = "".join(msg_pieces)
132 144
133 145
134 class ApplyPatches(Step): 146 class ApplyPatches(Step):
135 MESSAGE = "Apply patches for selected revisions." 147 MESSAGE = "Apply patches for selected revisions."
136 148
137 def RunStep(self): 149 def RunStep(self):
138 for commit_hash in self["full_revision_list"]: 150 for commit_hash in self["full_revision_list"]:
139 print("Applying patch for %s to %s..." 151 print("Applying patch for %s to %s..."
140 % (commit_hash, self["merge_to_branch"])) 152 % (commit_hash, self["merge_to_branch"]))
141 patch = self.GitGetPatch(commit_hash) 153 patch = self.GitGetPatch(commit_hash)
142 TextToFile(patch, self.Config("TEMPORARY_PATCH_FILE")) 154 TextToFile(patch, self.Config("TEMPORARY_PATCH_FILE"))
143 self.ApplyPatch(self.Config("TEMPORARY_PATCH_FILE")) 155 self.ApplyPatch(self.Config("TEMPORARY_PATCH_FILE"))
144 if self._options.patch: 156 if self._options.patch:
145 self.ApplyPatch(self._options.patch) 157 self.ApplyPatch(self._options.patch)
146 158
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): 159 class CommitLocal(Step):
180 MESSAGE = "Commit to local branch." 160 MESSAGE = "Commit to local branch."
181 161
182 def RunStep(self): 162 def RunStep(self):
183 # Add a commit message title. 163 # 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"], 164 self["new_commit_msg"] = "%s\n\n%s" % (self["commit_title"],
186 self["new_commit_msg"]) 165 self["new_commit_msg"])
187 TextToFile(self["new_commit_msg"], self.Config("COMMITMSG_FILE")) 166 TextToFile(self["new_commit_msg"], self.Config("COMMITMSG_FILE"))
188 self.GitCommit(file_name=self.Config("COMMITMSG_FILE")) 167 self.GitCommit(file_name=self.Config("COMMITMSG_FILE"))
189 168
169 class AddInformationalComment(Step):
170 MESSAGE = 'Show additional information.'
171
172 def RunStep(self):
173 message = ("NOTE: This script will no longer automatically "
174 "update include/v8-version.h "
175 "and create a tag. This is done automatically by the autotag bot. "
176 "Please call the merge_to_branch.py with --help for more information.")
177
178 self.GitCLAddComment(message)
190 179
191 class CommitRepository(Step): 180 class CommitRepository(Step):
192 MESSAGE = "Commit to the repository." 181 MESSAGE = "Commit to the repository."
193 182
194 def RunStep(self): 183 def RunStep(self):
195 self.GitCheckout(self.Config("BRANCHNAME")) 184 self.GitCheckout(self.Config("BRANCHNAME"))
196 self.WaitForLGTM() 185 self.WaitForLGTM()
197 self.GitPresubmit() 186 self.GitPresubmit()
198 self.vc.CLLand() 187 self.vc.CLLand()
199 188
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): 189 class CleanUp(Step):
212 MESSAGE = "Cleanup." 190 MESSAGE = "Cleanup."
213 191
214 def RunStep(self): 192 def RunStep(self):
215 self.CommonCleanup() 193 self.CommonCleanup()
216 print "*** SUMMARY ***" 194 print "*** SUMMARY ***"
217 print "version: %s" % self["version"]
218 print "branch: %s" % self["merge_to_branch"] 195 print "branch: %s" % self["merge_to_branch"]
219 if self["revision_list"]: 196 if self["revision_list"]:
220 print "patches: %s" % self["revision_list"] 197 print "patches: %s" % self["revision_list"]
221 198
222 199
223 class MergeToBranch(ScriptsBase): 200 class MergeToBranch(ScriptsBase):
224 def _Description(self): 201 def _Description(self):
225 return ("Performs the necessary steps to merge revisions from " 202 return ("Performs the necessary steps to merge revisions from "
226 "master to other branches, including candidates.") 203 "master to release branches like 4.5. This script does not "
204 "version the commit. See http://goo.gl/9ke2Vw for more "
205 "information.")
227 206
228 def _PrepareOptions(self, parser): 207 def _PrepareOptions(self, parser):
229 group = parser.add_mutually_exclusive_group(required=True) 208 group = parser.add_mutually_exclusive_group(required=True)
230 group.add_argument("--branch", help="The branch to merge to.") 209 group.add_argument("--branch", help="The branch to merge to.")
231 parser.add_argument("revisions", nargs="*", 210 parser.add_argument("revisions", nargs="*",
232 help="The revisions to merge.") 211 help="The revisions to merge.")
233 parser.add_argument("-f", "--force", 212 parser.add_argument("-f", "--force",
234 help="Delete sentinel file.", 213 help="Delete sentinel file.",
235 default=False, action="store_true") 214 default=False, action="store_true")
236 parser.add_argument("-m", "--message", 215 parser.add_argument("-m", "--message",
237 help="A commit message for the patch.") 216 help="A commit message for the patch.")
238 parser.add_argument("-p", "--patch", 217 parser.add_argument("-p", "--patch",
239 help="A patch file to apply as part of the merge.") 218 help="A patch file to apply as part of the merge.")
240 219
241 def _ProcessOptions(self, options): 220 def _ProcessOptions(self, options):
242 if len(options.revisions) < 1: 221 if len(options.revisions) < 1:
243 if not options.patch: 222 if not options.patch:
244 print "Either a patch file or revision numbers must be specified" 223 print "Either a patch file or revision numbers must be specified"
245 return False 224 return False
246 if not options.message: 225 if not options.message:
247 print "You must specify a merge comment if no patches are specified" 226 print "You must specify a merge comment if no patches are specified"
248 return False 227 return False
249 options.bypass_upload_hooks = True 228 options.bypass_upload_hooks = True
250 # CC ulan to make sure that fixes are merged to Google3. 229 # CC ulan to make sure that fixes are merged to Google3.
251 options.cc = "ulan@chromium.org" 230 options.cc = "ulan@chromium.org"
252 231
232 if len(options.branch.split('.')) > 2:
233 print ("This script does not support merging to roll branches. "
234 "Please use tools/release/roll_merge.py for this use case.")
235 return False
236
253 # Make sure to use git hashes in the new workflows. 237 # Make sure to use git hashes in the new workflows.
254 for revision in options.revisions: 238 for revision in options.revisions:
255 if (IsSvnNumber(revision) or 239 if (IsSvnNumber(revision) or
256 (revision[0:1] == "r" and IsSvnNumber(revision[1:]))): 240 (revision[0:1] == "r" and IsSvnNumber(revision[1:]))):
257 print "Please provide full git hashes of the patches to merge." 241 print "Please provide full git hashes of the patches to merge."
258 print "Got: %s" % revision 242 print "Got: %s" % revision
259 return False 243 return False
260 return True 244 return True
261 245
262 def _Config(self): 246 def _Config(self):
263 return { 247 return {
264 "BRANCHNAME": "prepare-merge", 248 "BRANCHNAME": "prepare-merge",
265 "PERSISTFILE_BASENAME": "/tmp/v8-merge-to-branch-tempfile", 249 "PERSISTFILE_BASENAME": "/tmp/v8-merge-to-branch-tempfile",
266 "ALREADY_MERGING_SENTINEL_FILE": 250 "ALREADY_MERGING_SENTINEL_FILE":
267 "/tmp/v8-merge-to-branch-tempfile-already-merging", 251 "/tmp/v8-merge-to-branch-tempfile-already-merging",
268 "TEMPORARY_PATCH_FILE": "/tmp/v8-prepare-merge-tempfile-temporary-patch", 252 "TEMPORARY_PATCH_FILE": "/tmp/v8-prepare-merge-tempfile-temporary-patch",
269 "COMMITMSG_FILE": "/tmp/v8-prepare-merge-tempfile-commitmsg", 253 "COMMITMSG_FILE": "/tmp/v8-prepare-merge-tempfile-commitmsg",
270 } 254 }
271 255
272 def _Steps(self): 256 def _Steps(self):
273 return [ 257 return [
274 Preparation, 258 Preparation,
275 CreateBranch, 259 CreateBranch,
276 SearchArchitecturePorts, 260 SearchArchitecturePorts,
277 CreateCommitMessage, 261 CreateCommitMessage,
278 ApplyPatches, 262 ApplyPatches,
279 PrepareVersion,
280 IncrementVersion,
281 CommitLocal, 263 CommitLocal,
282 UploadStep, 264 UploadStep,
265 AddInformationalComment,
283 CommitRepository, 266 CommitRepository,
284 TagRevision,
285 CleanUp, 267 CleanUp,
286 ] 268 ]
287 269
288 270
289 if __name__ == "__main__": # pragma: no cover 271 if __name__ == "__main__": # pragma: no cover
290 sys.exit(MergeToBranch().Run()) 272 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