OLD | NEW |
---|---|
(Empty) | |
1 #!/bin/bash | |
2 # Copyright 2012 the V8 project authors. All rights reserved. | |
3 # Redistribution and use in source and binary forms, with or without | |
4 # modification, are permitted provided that the following conditions are | |
5 # met: | |
6 # | |
7 # * Redistributions of source code must retain the above copyright | |
8 # notice, this list of conditions and the following disclaimer. | |
9 # * Redistributions in binary form must reproduce the above | |
10 # copyright notice, this list of conditions and the following | |
11 # disclaimer in the documentation and/or other materials provided | |
12 # with the distribution. | |
13 # * Neither the name of Google Inc. nor the names of its | |
14 # contributors may be used to endorse or promote products derived | |
15 # from this software without specific prior written permission. | |
16 # | |
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
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. | |
28 | |
29 ########## Global variable definitions | |
30 | |
31 BRANCHNAME=prepare-merge | |
32 VERSION_FILE="src/version.cc" | |
33 PERSISTFILE_BASENAME=/tmp/v8-merge-to-branch-tempfile | |
34 ALREADY_MERGING_SENTINEL_FILE="$PERSISTFILE_BASENAME-already-merging" | |
35 CHANGELOG_ENTRY_FILE="$PERSISTFILE_BASENAME-changelog-entry" | |
36 PATCH_FILE="$PERSISTFILE_BASENAME-patch" | |
37 COMMITMSG_FILE="$PERSISTFILE_BASENAME-commitmsg" | |
38 COMMITMSG_FILE_COPY="$PERSISTFILE_BASENAME-commitmsg-copy" | |
39 TOUCHED_FILES_FILE="$PERSISTFILE_BASENAME-touched-files" | |
40 TRUNK_REVISION_FILE="$PERSISTFILE_BASENAME-trunkrevision" | |
41 START_STEP=0 | |
42 CURRENT_STEP=0 | |
43 | |
44 usage() { | |
45 cat << EOF | |
46 usage: $0 [OPTIONS]... [BRANCH] [REVISION]... | |
47 | |
48 Performs the necessary steps to merge revisions from bleeding_edge | |
49 to other branches, including trunk. | |
50 | |
51 OPTIONS: | |
52 -h Show this message | |
53 -s Specify the step where to start work. Default: 0. | |
54 EOF | |
55 } | |
56 | |
57 ########## Function definitions | |
58 | |
59 die() { | |
60 [[ -n "$1" ]] && echo "Error: $1" | |
61 echo "Exiting." | |
62 exit 1 | |
63 } | |
64 | |
65 confirm() { | |
66 echo -n "$1 [Y/n] " | |
67 read ANSWER | |
68 if [[ -z "$ANSWER" || "$ANSWER" == "Y" || "$ANSWER" == "y" ]] ; then | |
69 return 0 | |
70 else | |
71 return 1 | |
72 fi | |
73 } | |
74 | |
75 delete_branch() { | |
76 local MATCH=$(git branch | grep $1 | awk '{print $NF}' ) | |
77 if [ "$MATCH" == "$1" ] ; then | |
78 confirm "Branch $1 exists, do you want to delete it?" | |
79 if [ $? -eq 0 ] ; then | |
80 git branch -D $1 || die "Deleting branch '$1' failed." | |
81 echo "Branch $1 deleted." | |
82 else | |
83 die "Can't continue. Please delete branch $1 and try again." | |
84 fi | |
85 fi | |
86 } | |
87 | |
88 # Persist and restore variables to support canceling/resuming execution | |
89 # of this script. | |
90 persist() { | |
91 local VARNAME=$1 | |
92 local FILE="$PERSISTFILE_BASENAME-$VARNAME" | |
93 echo "${!VARNAME}" > $FILE | |
94 } | |
95 | |
96 restore() { | |
97 local VARNAME=$1 | |
98 local FILE="$PERSISTFILE_BASENAME-$VARNAME" | |
99 local VALUE="$(cat $FILE)" | |
100 eval "$VARNAME=\"$VALUE\"" | |
101 } | |
102 | |
103 restore_if_unset() { | |
104 local VARNAME=$1 | |
105 [[ -z "${!VARNAME}" ]] && restore "$VARNAME" | |
106 [[ -z "${!VARNAME}" ]] && die "Variable '$VARNAME' could not be restored." | |
107 } | |
108 | |
109 persist_patch_commit_hashes() { | |
110 local FILE="$PERSISTFILE_BASENAME-PATCH_COMMIT_HASHES" | |
111 echo "PATCH_COMMIT_HASHES=( ${PATCH_COMMIT_HASHES[@]} )" > $FILE | |
112 } | |
113 | |
114 restore_patch_commit_hashes() { | |
115 local FILE="$PERSISTFILE_BASENAME-PATCH_COMMIT_HASHES" | |
116 source $FILE | |
117 } | |
118 | |
119 restore_patch_commit_hashes_if_unset() { | |
120 [[ "${#PATCH_COMMIT_HASHES[@]}" == 0 ]] && restore_patch_commit_hashes | |
121 [[ "${#PATCH_COMMIT_HASHES[@]}" == 0 ]] && \ | |
122 die "Variable PATCH_COMMIT_HASHES could not be restored." | |
123 } | |
124 | |
125 ########## Option parsing | |
126 | |
127 while getopts ":hs:f" OPTION ; do | |
128 case $OPTION in | |
129 h) usage | |
130 exit 0 | |
131 ;; | |
132 f) rm "$ALREADY_MERGING_SENTINEL_FILE" | |
133 ;; | |
134 s) START_STEP=$OPTARG | |
135 ;; | |
136 ?) echo "Illegal option: -$OPTARG" | |
137 usage | |
138 exit 1 | |
139 ;; | |
140 esac | |
141 done | |
142 let OPTION_COUNT=$OPTIND-1 | |
143 shift $OPTION_COUNT | |
144 | |
145 ########## Regular workflow | |
146 | |
147 # If there is a merge in progress, abort. | |
148 [[ -e "$ALREADY_MERGING_SENTINEL_FILE" ]] && [[ -z "$START_STEP" ]] \ | |
149 && die "A merge is already in progress" | |
150 touch "$ALREADY_MERGING_SENTINEL_FILE" | |
151 | |
152 # Cancel if this is not a git checkout. | |
153 [[ -d .git ]] \ | |
154 || die "This is not a git checkout, this script won't work for you." | |
155 | |
156 # Cancel if EDITOR is unset or not executable. | |
157 [[ -n "$EDITOR" && -x "$(which $EDITOR)" ]] \ | |
158 || die "Please set your EDITOR environment variable, you'll need it." | |
159 | |
160 if [ $START_STEP -le $CURRENT_STEP ] ; then | |
161 MERGE_TO_BRANCH=$1 | |
162 [[ -n "$MERGE_TO_BRANCH" ]] \ | |
163 || die "Please specify a branch to merge to" | |
164 shift | |
165 persist "MERGE_TO_BRANCH" | |
166 | |
167 echo ">>> Step $CURRENT_STEP: Preparation" | |
168 # Check for a clean workdir. | |
169 [[ -z "$(git status -s -uno)" ]] \ | |
170 || die "Workspace is not clean. Please commit or undo your changes." | |
171 | |
172 # Persist current branch. | |
173 CURRENT_BRANCH=$(git status -s -b -uno | grep "^##" | awk '{print $2}') | |
174 persist "CURRENT_BRANCH" | |
175 delete_branch $BRANCHNAME | |
176 fi | |
177 | |
178 let CURRENT_STEP+=1 | |
179 if [ $START_STEP -le $CURRENT_STEP ] ; then | |
180 echo ">>> Step $CURRENT_STEP: Fetch unfetched revisions." | |
181 git svn fetch || die "'git svn fetch' failed." | |
182 fi | |
183 | |
184 let CURRENT_STEP+=1 | |
185 if [ $START_STEP -le $CURRENT_STEP ] ; then | |
186 restore_if_unset "MERGE_TO_BRANCH" | |
187 echo ">>> Step $CURRENT_STEP: Create a fresh branch for the patch." | |
188 git checkout -b $BRANCHNAME svn/$MERGE_TO_BRANCH \ | |
189 || die "Creating branch $BRANCHNAME failed." | |
190 fi | |
191 | |
192 let CURRENT_STEP+=1 | |
193 if [ $START_STEP -le $CURRENT_STEP ] ; then | |
194 echo ">>> Step $CURRENT_STEP: Find the git \ | |
195 revisions associated with the patches." | |
196 current=0 | |
197 for REVISION in "$@" ; do | |
198 NEXT_HASH=$(git svn find-rev "r$REVISION" svn/bleeding_edge) | |
199 [[ -n "$NEXT_HASH" ]] \ | |
200 || die "Cannot determine git hash for r$REVISION" | |
201 PATCH_COMMIT_HASHES[$current]="$NEXT_HASH" | |
202 [[ -n "$NEW_COMMIT_MSG" ]] && NEW_COMMIT_MSG="$NEW_COMMIT_MSG," | |
203 NEW_COMMIT_MSG="$NEW_COMMIT_MSG r$REVISION" | |
204 let current+=1 | |
205 done | |
206 NEW_COMMIT_MSG="Merged$NEW_COMMIT_MSG into \ | |
Jakob Kummerow
2012/01/31 11:11:10
nit: fits on one line
danno
2012/01/31 11:13:11
Done.
| |
207 $MERGE_TO_BRANCH branch." | |
208 | |
209 echo "$NEW_COMMIT_MSG" > $COMMITMSG_FILE | |
210 echo >> $COMMITMSG_FILE | |
211 for HASH in ${PATCH_COMMIT_HASHES[@]} ; do | |
Jakob Kummerow
2012/01/31 11:11:10
nit: double space before "do"
danno
2012/01/31 11:13:11
Done.
| |
212 PATCH_MERGE_DESCRIPTION=$(git log -1 --format=%s $HASH) | |
213 echo "$PATCH_MERGE_DESCRIPTION" >> $COMMITMSG_FILE | |
214 echo >> $COMMITMSG_FILE | |
215 done | |
216 for HASH in ${PATCH_COMMIT_HASHES[@]} ; do | |
217 BUG=$(git log -1 $HASH | grep "BUG=" | awk -F '=' '{print $NF}') | |
218 if [ $BUG ] ; then | |
219 if [ "$BUG_AGGREGATE" ] ; then | |
220 BUG_AGGREGATE="$BUG_AGGREGATE," | |
221 fi | |
222 BUG_AGGREGATE="$BUG_AGGREGATE$BUG" | |
223 fi | |
224 done | |
225 if [ "$BUG_AGGREGATE" ] ; then | |
226 echo "BUG=$BUG_AGGREGATE" >> $COMMITMSG_FILE | |
227 else | |
228 echo "BUG=none" >> $COMMITMSG_FILE | |
Jakob Kummerow
2012/01/31 11:11:10
I'd just remove this. "BUG=none" adds no useful in
danno
2012/01/31 11:13:11
Done.
| |
229 fi | |
230 echo -n "TEST=none" >> $COMMITMSG_FILE | |
Jakob Kummerow
2012/01/31 11:11:10
Same here.
danno
2012/01/31 11:13:11
Done.
| |
231 persist "NEW_COMMIT_MSG" | |
232 persist_patch_commit_hashes | |
233 fi | |
234 | |
235 let CURRENT_STEP+=1 | |
236 if [ $START_STEP -le $CURRENT_STEP ] ; then | |
237 restore_if_unset "MERGE_TO_BRANCH" | |
238 restore_patch_commit_hashes_if_unset "PATCH_COMMIT_HASHES" | |
239 echo "${PATCH_COMMIT_HASHES[@]}" | |
240 echo ">>> Step $CURRENT_STEP: Apply the revision patch and create commit messa ge." | |
241 for HASH in ${PATCH_COMMIT_HASHES[@]} ; do | |
242 git log -1 -p $HASH | patch -p1 \ | |
243 || die "Cannot apply the patch for $HASH to $MERGE_TO_BRANCH" | |
244 done | |
245 fi | |
246 | |
247 let CURRENT_STEP+=1 | |
248 if [ $START_STEP -le $CURRENT_STEP ] ; then | |
249 echo ">>> Step $CURRENT_STEP: Prepare version.cc" | |
250 # These version numbers are used again for creating the tag | |
251 MAJOR=$(grep "#define MAJOR_VERSION" "$VERSION_FILE" | awk '{print $NF}') | |
Jakob Kummerow
2012/01/31 11:11:10
AFAICS the tag is created using NEWMAJOR, NEWMINOR
danno
2012/01/31 11:13:11
Done.
| |
252 persist "MAJOR" | |
253 MINOR=$(grep "#define MINOR_VERSION" "$VERSION_FILE" | awk '{print $NF}') | |
254 persist "MINOR" | |
255 BUILD=$(grep "#define BUILD_NUMBER" "$VERSION_FILE" | awk '{print $NF}') | |
256 persist "BUILD" | |
257 PATCH=$(grep "#define PATCH_LEVEL" "$VERSION_FILE" | awk '{print $NF}') | |
258 persist "PATCH" | |
259 fi | |
260 | |
261 let CURRENT_STEP+=1 | |
262 if [ $START_STEP -le $CURRENT_STEP ] ; then | |
263 echo ">>> Step $CURRENT_STEP: Increment version number." | |
264 restore_if_unset "PATCH" | |
265 NEWPATCH=$(($PATCH + 1)) | |
266 confirm "Automatically increment PATCH_LEVEL? (Saying 'n' will fire up \ | |
267 your EDITOR on $VERSION_FILE so you can make arbitrary changes. When \ | |
268 you're done, save the file and exit your EDITOR.)" | |
269 if [ $? -eq 0 ] ; then | |
270 sed -e "/#define PATCH_LEVEL/s/[0-9]*$/$NEWPATCH/" \ | |
271 -i "$VERSION_FILE" | |
272 else | |
273 $EDITOR "$VERSION_FILE" | |
274 fi | |
275 NEWMAJOR=$(grep "#define MAJOR_VERSION" "$VERSION_FILE" | awk '{print $NF}') | |
276 persist "NEWMAJOR" | |
277 NEWMINOR=$(grep "#define MINOR_VERSION" "$VERSION_FILE" | awk '{print $NF}') | |
278 persist "NEWMINOR" | |
279 NEWBUILD=$(grep "#define BUILD_NUMBER" "$VERSION_FILE" | awk '{print $NF}') | |
280 persist "NEWBUILD" | |
281 NEWPATCH=$(grep "#define PATCH_LEVEL" "$VERSION_FILE" | awk '{print $NF}') | |
282 persist "NEWPATCH" | |
283 fi | |
284 | |
285 let CURRENT_STEP+=1 | |
286 if [ $START_STEP -le $CURRENT_STEP ] ; then | |
287 echo ">>> Step $CURRENT_STEP: Commit to local branch." | |
288 git commit -a -F "$COMMITMSG_FILE" \ | |
289 || die "'git commit -a' failed." | |
290 fi | |
291 | |
292 let CURRENT_STEP+=1 | |
293 if [ $START_STEP -le $CURRENT_STEP ] ; then | |
294 echo ">>> Step $CURRENT_STEP: Upload for code review." | |
295 echo -n "Please enter the email address of a V8 reviewer for your patch: " | |
296 read REVIEWER | |
297 git cl upload -r "$REVIEWER" --send-mail \ | |
298 || die "'git cl upload' failed, please try again." | |
299 fi | |
300 | |
301 let CURRENT_STEP+=1 | |
302 if [ $START_STEP -le $CURRENT_STEP ] ; then | |
303 restore_if_unset "MERGE_TO_BRANCH" | |
304 git checkout $BRANCHNAME \ | |
305 || die "cannot ensure that the current branch is $BRANCHNAME" | |
306 echo ">>> Step $CURRENT_STEP: Commit to the repository." | |
307 echo "Please wait for an LGTM, then type \"LGTM<Return>\" to commit your \ | |
308 change. (If you need to iterate on the patch or double check that it's \ | |
309 sane, do so in another shell, but remember to not change the headline of \ | |
310 the uploaded CL." | |
311 unset ANSWER | |
312 while [ "$ANSWER" != "LGTM" ] ; do | |
313 [[ -n "$ANSWER" ]] && echo "That was not 'LGTM'." | |
314 echo -n "> " | |
315 read ANSWER | |
316 done | |
317 git cl dcommit || die "failed to commit to $MERGE_TO_BRANCH" | |
318 fi | |
319 | |
320 let CURRENT_STEP+=1 | |
321 if [ $START_STEP -le $CURRENT_STEP ] ; then | |
322 restore_if_unset "NEW_COMMIT_MSG" | |
323 restore_if_unset "MERGE_TO_BRANCH" | |
324 echo ">>> Step $CURRENT_STEP: Determine svn commit revision" | |
325 git svn fetch || die "'git svn fetch' failed." | |
326 COMMIT_HASH=$(git log -1 --format=%H --grep="$NEW_COMMIT_MSG" \ | |
327 svn/$MERGE_TO_BRANCH) | |
328 [[ -z "$COMMIT_HASH" ]] && die "Unable to map git commit to svn revision" | |
329 SVN_REVISION=$(git svn find-rev $COMMIT_HASH) | |
330 echo "subversion revision number is r$SVN_REVISION" | |
331 persist "SVN_REVISION" | |
332 fi | |
333 | |
334 let CURRENT_STEP+=1 | |
335 if [ $START_STEP -le $CURRENT_STEP ] ; then | |
336 restore_if_unset "SVN_REVISION" | |
337 restore_if_unset "NEWMAJOR" | |
338 restore_if_unset "NEWMINOR" | |
339 restore_if_unset "NEWBUILD" | |
340 restore_if_unset "NEWPATCH" | |
341 echo ">>> Step $CURRENT_STEP: Create the tag." | |
342 echo "Creating tag svn/tags/$NEWMAJOR.$NEWMINOR.$NEWBUILD.$NEWPATCH" | |
343 svn copy -r $SVN_REVISION \ | |
344 https://v8.googlecode.com/svn/branches/$MERGE_TO_BRANCH \ | |
345 https://v8.googlecode.com/svn/tags/$NEWMAJOR.$NEWMINOR.$NEWBUILD.$NEWPATCH \ | |
346 -m "Tagging version $NEWMAJOR.$NEWMINOR.$NEWBUILD.$NEWPATCH" | |
347 fi | |
348 | |
349 let CURRENT_STEP+=1 | |
350 if [ $START_STEP -le $CURRENT_STEP ] ; then | |
351 echo ">>> Step $CURRENT_STEP: Cleanup." | |
352 restore_if_unset "CURRENT_BRANCH" | |
353 git checkout -f $CURRENT_BRANCH | |
354 [[ "$BRANCHNAME" != "$CURRENT_BRANCH" ]] && git branch -D $BRANCHNAME | |
355 rm -f "$ALREADY_MERGING_SENTINEL_FILE" | |
356 fi | |
OLD | NEW |