blob: e659b41eb0b69c00c574ee6b8a57e2a07dccb27c [file] [log] [blame]
Guenter Roeck9aa69572017-12-12 13:36:30 -08001#!/bin/bash
2#
3# Copyright 2018 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6#
7# Script to merge upstream tags into chromeos.
8# The command creates a new branch with merge results.
9# If necessary, it also pushes the tag into the remote
10# repository and creates a branch pointing to it.
11
12readonly notify_to="chromeos-kernel@google.com"
13readonly notify_cc="chromium-os-reviews@chromium.org"
14
15# Valid tag pattern.
16PATTERN="^v[2-9](\.[0-9]+)+$"
17PATTERN_RC="^v[2-9](\.[0-9]+)+-rc$"
18GLOB="v[2-9].*[0-9]"
19NUMBER="^[1-9][0-9]*$"
20
21if git help push | grep -q push-option; then
22 git_skip_validation="-o skip-validation"
23fi
24
25# Initial parameter values.
26changeid="" # Get Change-Id from CL or generate new Change-Id.
27bug="" # Get Bug-Id from CL or provide on on command line.
28tag="" # Tag to merge; must be provided on command line.
29force=0 # Do not override Change-Id / Bug-Id.
30prepare=0 # Do not prepare for upload.
31upload=0 # Do not upload into Gerrit.
32do_dryrun=0 # If 1, don't push anything upstream, don't send email.
33notify=0 # Do not send notification e-mail.
34deadline=3 # Feedback deadline (in days, default 3).
35changes=() # List of uncommitted CLs to be applied prior to merge.
36patches=() # List of patches to apply before committing merge.
37dependency="" # No dependency
38Subject="" # default subject
39
40# derived parameters
41skip_merge=0 # Skip actual merge and upload.
42 # Will be set if tag has already been merged and force is true.
43
44readonly tmpfile=$(mktemp)
45
46trap 'rm -f "${tmpfile}"' EXIT
47trap 'exit 2' SIGHUP SIGINT SIGQUIT SIGTERM
48
49error() {
50 printf '%b: error: %b\n' "${0##*/}" "$*" >&2
51}
52
53die() {
54 error "$@"
55 exit 1
56}
57
58usage() {
59 cat <<-EOF
60Usage: ${0##*/} [options] tag
61
62Parameters:
63 tag Tag, branch, or SHA to merge. Must be either a valid stable
64 branch release tag, a valid branch name, or a valid SHA.
65
66Options:
67 -b bug-id[,bug-id] ...
68 Bug-id or list of bug IDs. Must be valid buganizer or chromium
69 bug ID. Mandatory unless the merge branch already exists
70 locally or in Gerrit.
71 -c change-id Change-Id as used by Gerrit. Optional.
72 -d deadline Feedback deadline in days (default: ${deadline})
73 -f Force. Override existing Change-Id and bug number.
74 -h Display help text and exit.
75 -l change-id Apply patch extracted from CL:change-id prior to merge.
76 May be repeated multiple times.
77 -n Send notification e-mail to ${notify_to}.
Guenter Roeck33f263a2019-06-28 08:51:23 -070078 -q dependency Add dependency (Cq-Depend: <dependency>)
Guenter Roeck9aa69572017-12-12 13:36:30 -080079 -p Prepare for upload into Gerrit. Implied if -u is specified.
80 -r Name of branch to base merge on. Determined from stable
81 release tag or from target branch name if not provided.
82 Must be existing local branch. Will be pushed into gerrit
83 as part of the merge process if not already available in
84 gerrit, and has to follow gerrit commit rules.
85 -s Simulate, or dry-run. Don't actually push anything into
86 gerrit, and don't send e-mails.
87 -S subject Replace default subject line with provided string
88 -t Target branch name. The branch must exist in the Chrome OS
89 repository.
90 -u Upload merge into Gerrit.
91 -x patchfile Patch to apply before committing merge. Patch will be applied
92 with "patch -p 1 < patchfile". May be repeated several times.
93EOF
94
95 if [[ $# -gt 0 ]]; then
96 echo
97 die "$@"
98 fi
99 exit 0
100}
101
102# Find and report remote.
103find_remote() {
104 local url="$1"
105 local remote
106
107 for remote in $(git remote 2>/dev/null); do
108 rurl=$(git remote get-url "${remote}")
109 # ignore trailing '/' when comparing repositories
110 if [[ "${rurl%/}" == "${url%/}" ]]; then
111 echo "${remote}"
112 break
113 fi
114 done
115}
116
117# Find remote. If there is no remote pointing to the referenced
118# kernel repository, create one.
119find_create_remote() {
120 local url="$1"
121 local default="$2"
122 local result
123
124 result="$(find_remote "${url}")"
125 if [[ -z "${result}" ]]; then
126 git remote add "${default}" "${url}"
127 result="${default}"
128 fi
129
130 echo "${result}"
131}
132
133# Find and report CrOS remote.
134# This is useful if the command runs on a checked out
135# tree with several remotes.
136find_chromeos() {
137 local url="https://chromium.googlesource.com/chromiumos/third_party/kernel"
138
139 find_remote "${url}"
140}
141
142# Find stable remote. If there is no remote pointing to the stable
143# kernel repository, create one.
144find_stable() {
145 local url="git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git"
146
147 find_create_remote "${url}" "stable"
148}
149
150# Find stable remote. If there is no remote pointing to the stable
151# kernel repository, create one.
152find_stable_rc() {
153 local url="git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable-rc.git"
154
155 find_create_remote "${url}" "stable-rc"
156}
157
158do_getparams() {
159 local bugs="" # List of bugs
160 local nbug="" # Numerical part of bug #, for validation.
161 local _bug
162 local option
163 local vtag
164
165 while getopts "b:c:d:fhl:npq:r:st:uS:x:" option; do
166 case ${option} in
167 b) bugs="${OPTARG}" ;;
168 c) changeid="Change-Id: ${OPTARG}" ;;
169 d) deadline="${OPTARG}"
170 if ! [[ "${deadline}" =~ ${NUMBER} ]]; then
171 die "Deadline must be numeric value > 0 (${deadline})"
172 fi
173 ;;
174 f) force=1 ;;
175 l) changes+=("${OPTARG}") ;;
176 n) notify=1 ;;
177 p) prepare=1 ;;
178 q) dependency="${OPTARG}" ;;
179 r) rbranch="${OPTARG}" ;;
180 t) tbranch="${OPTARG}" ;;
181 s) do_dryrun=1 ;;
182 S) Subject="${OPTARG}" ;;
183 u) upload=1 prepare=1 ;;
184 x) patches+=("${OPTARG}") ;;
185 h|?|*) usage ;;
186 esac
187 done
188 shift $((OPTIND - 1))
189 tag=$1
190 if [[ -z "${tag}" ]]; then
191 usage "tag parameter is mandatory"
192 fi
193 vtag=$(echo "${tag}" | grep -E "${PATTERN}")
194 if [[ "${tag}" != "${vtag}" ]]; then
195 # Not a stable release tag, meaning we can not get it from a stable release.
196 # Maybe it is a stable release candidate.
197 vtag=$(echo "${tag}" | grep -E "${PATTERN_RC}")
198 if [[ "${tag}" != "${vtag}" ]]; then
199 # Make sure that the reference exists and bail out if not.
200 if ! git rev-parse --verify "${tag}" >/dev/null 2>&1; then
201 die "Unknown reference '${tag}'."
202 fi
203 else
204 die "${tag} references a stable release candidate. Not supported yet."
205 fi
206 fi
207 if [[ -n "${rbranch}" ]]; then
208 if ! git rev-parse --verify "${rbranch}" >/dev/null 2>&1; then
209 die "No such branch: ${rbranch}."
210 fi
211 fi
212 if [[ "${bugs}" =~ ${NUMBER} ]]; then # default to crbug if numeric
213 bugs="chromium:${bugs}"
214 fi
215 if [[ -n "${bugs}" ]]; then
216 for _bug in ${bugs//,/ }; do
217 if [[ "${_bug}" == b:* ]]; then # buganizer
218 nbug="${_bug##b:}"
219 elif [[ "${_bug}" == b/* ]]; then # buganizer, alternative
220 nbug="${_bug##b/}"
221 elif [[ "${_bug}" == chromium:* ]]; then # crbug
222 nbug="${_bug##chromium:}"
223 fi
224 if [[ ! "${nbug}" =~ ${NUMBER} ]]; then
225 die "Invalid bug ID '${_bug}'."
226 fi
227 done
228 bug="BUG=${bugs}"
229 fi
Guenter Roeck33f263a2019-06-28 08:51:23 -0700230 dependency="${dependency:+Cq-Depend: ${dependency}}"
Guenter Roeck9aa69572017-12-12 13:36:30 -0800231}
232
233# Validate environment and repository.
234# We need a couple of commands, the repository must be
235# a CrOS kernel repository, and it must be clean.
236do_validate() {
237 local gerrit
238 local chromeos
239 local jq
240
241 gerrit=$(which gerrit)
242 if [[ -z "${gerrit}" ]]; then
243 die "gerrit is required. Get from chromite or run from chroot."
244 fi
245 jq=$(which jq)
246 if [[ -z ${jq} ]]; then
247 die "jq is required. Install (apt-get install jq) or run from chroot."
248 fi
249 chromeos=$(find_chromeos)
250 if [[ -z ${chromeos} ]]; then
251 die "$(pwd) is not a Chromium OS kernel repository."
252 fi
253 if [[ -n "$(git status -s)" ]]; then
254 die "Requires clean repository."
255 fi
256 if [[ -n "${tbranch}" ]]; then
257 if ! git rev-parse --verify "${chromeos}/${tbranch}" >/dev/null 2>&1; then
258 die "No such branch: ${chromeos}/${tbranch}."
259 fi
260 fi
261}
262
263# Validate provided Change-IDs.
264do_validate_changeids() {
265 local cl
266 local ref
267
268 for cl in "${changes[@]}"; do
269 ref=$(gerrit --json search "change:${cl}" \
270 | jq ".[].currentPatchSet.ref")
271 if [[ -z "${ref}" ]]; then
272 die "No such Change-Id: ${cl}."
273 fi
274 done
275}
276
277# Initialize global variables, plus some more validation.
278do_setup() {
279 readonly stable=$(find_stable)
280 readonly stable_rc=$(find_stable_rc)
281 local vtag
282 local dvtag
283
284 # If a stable release tag is provided, we need to update stable
285 # at this point to get the tag if it is not already available.
286 vtag=$(echo "${tag}" | grep -E "${PATTERN}")
287 if [[ "${tag}" == "${vtag}" ]]; then
288 if ! git rev-parse --verify "${tag}" >/dev/null 2>&1; then
289 if ! git fetch "${stable}" > /dev/null 2>&1; then
290 die "Failed to update stable release."
291 fi
292 if ! git rev-parse --verify "${tag}" >/dev/null 2>&1; then
293 die "Reference ${tag} not available."
294 fi
295 fi
296 else
297 # This might be a stable release candidate.
298 vtag=$(echo "${tag}" | grep -E "${PATTERN_RC}")
299 if [[ "${tag}" == "${vtag}" ]]; then
300 git fetch "${stable_rc}" > /dev/null 2>&1
301 # The stable release tag is "vX.Y.Z-rc". Stable release candidate
302 # branches are named "remote/linux-X.Y.y".
303 # Extract 'X' and 'Y', create the remote branch name,
304 # clone/update the remote branch, and set a matching tag
305 # on top of it.
306
307 die "Stable release candidates are not yet supported."
308 fi
309 fi
310
311 readonly ctag=$(git describe --match "${GLOB}" --abbrev=0 "${tag}" \
312 2>/dev/null | cut -f1,2 -d. | sed -e 's/v//')
313 readonly dtag=$(git describe --tags "${tag}")
314
315 # While we accept any valid reference as <tag>, we want it to be based
316 # on an existing release tag.
317 dbtag=${dtag%%-*}
318 dvtag=$(git describe --tags --abbrev=0 "${dtag}")
319 if [[ "${dbtag}" != "${dvtag}" ]]; then
320 die "${tag} (${dtag}) is not based on an existing release tag."
321 fi
322
323 readonly chromeos=$(find_chromeos)
324 if [[ -z "${chromeos}" ]]; then
325 die "Chromium OS kernel repository not found."
326 fi
327
328 # cbranch: Chromeos branch name
329 # mcbranch: local copy (baseline)
330 # ocbranch: remote (target) branch
331 #
332 # Note: This assumes that the target repository is ${chromeos},
333 # even if a remote branch has been specified. It might make sense
334 # to make this configurable.
335 if [[ -n "${tbranch}" ]]; then
336 readonly cbranch="${tbranch}"
337 else
338 readonly cbranch="chromeos-${ctag}"
339 fi
340 if [[ -n "${rbranch}" ]]; then
341 readonly ocbranch="${rbranch}"
342 else
343 readonly ocbranch="${chromeos}/${cbranch}"
344 fi
345
346 readonly mcbranch="merge/${cbranch}"
347
348 # Topic to use.
349 readonly topic="merge-${dtag}"
350
351 if ! git rev-parse --verify "${ocbranch}" >/dev/null 2>&1; then
352 usage "Invalid tag '${tag}': No such branch: '${ocbranch}'"
353 fi
354
355 # mbranch: Local branch used to execute the merge.
356 readonly mbranch="${mcbranch}-${dtag}"
357
358 # obranch: chromeos branch used as reference.
359 # May include local reverts from merge if necessary.
360 # If necessary, a branch with this name will be created locally and
361 # in the chromeos repository. It is necessary to perform the merge.
362 readonly obranch="stable-merge/linux/${dtag}"
363
364 if [[ ${do_dryrun} -ne 0 ]]; then
365 readonly dryrun="--dry-run"
366 fi
367
368 Subject="CHROMIUM: ${Subject:-Merge '${dtag}' into ${cbranch}}"
369}
370
371have_version() {
372 local tag
373 local tot_tag
374 local index
375 local v1
376 local v2
377 local vtag
378
379 tag=$1
380 vtag=$(echo "${tag}" | grep -E "${PATTERN}")
381 if [[ "${tag}" != "${vtag}" ]]; then
382 # Not a release tag, can not evaluate.
383 return 0
384 fi
385
386 tot_tag=$(git describe --match "v[2-9].*[0-9]" --abbrev=0 "${ocbranch}")
387
388 index=1
389 while true; do
390 v1=$(echo "${tag}" | cut -f${index} -d. | sed -e 's/[^0-9]//g')
391 v2=$(echo "${tot_tag}" | cut -f${index} -d. | sed -e 's/[^0-9]//g')
392 # If both version numbers are empty, we reached the end of the
393 # version number string, and the versions are equal.
394 # Return true.
395 if [[ -z "${v1}" && -z "${v2}" ]]; then
396 return 1
397 fi
398 # Interpret empty minor version numbers as version 0.
399 if [[ -z "${v1}" ]]; then
400 v1=0
401 fi
402 if [[ -z "${v2}" ]]; then
403 v2=0
404 fi
405 # If ToT version is larger than tag, return true.
406 if [[ ${v2} -gt ${v1} ]]; then
407 return 1
408 fi
409 # If tag version is targer than ToT, return false.
410 if [[ ${v2} -lt ${v1} ]]; then
411 return 0
412 fi
413 index=$((index + 1))
414 done
415}
416
417# Remove double quotes from beginning and end of a string, and
418# remove the escape character from double quotes within the string.
419dequote() {
420 local tmp="${1#\"}" # beginning
421 tmp="${tmp%\"}" # end
422 echo "${tmp//\\\"/\"}" # remove embedded escape characters
423}
424
425# Try to find the merge CL.
426# Walk through all CLs tagged with the merge topic
427# and try to find one with the expected subject line.
428# If found, set merge_cl to the respective value for later use.
429find_merge_cl() {
430 local cls
431 local cl
432 local subject
433
434 cls=($(gerrit --json search "hashtag:${topic}" \
435 | jq ".[].number" | sed -e 's/"//g'))
436
437 for cl in "${cls[@]}"; do
438 subject=$(dequote "$(gerrit --json search "change:${cl}" \
439 | jq ".[].subject")")
440 if [[ "${subject}" == "${Subject}" ]]; then
441 merge_cl="${cl}"
442 break
443 fi
444 done
445}
446
447# Prepare for merge.
448# - Update remotes.
449# - Verify that tag exists.
450# - Search for merge in gerrit. If it exists, validate bug ID and Change-Id.
451# - Push tag and reference branch into CrOS repository if necessary.
452do_prepare() {
453 local vtag
454 local obug
455 local ochangeid
456 local odependency
457 local ref
458
459 find_merge_cl
460
461 printf "Updating ${chromeos}..."
462 git fetch "${chromeos}" > /dev/null
463 printf "\nUpdating ${mcbranch} ..."
464 if git rev-parse --verify "${mcbranch}" >/dev/null 2>&1; then
465 if ! git checkout "${mcbranch}" >/dev/null 2>&1; then
466 die "Failed to check out '${mcbranch}'."
467 fi
468 git pull >/dev/null
469 else
470 if ! git checkout -b "${mcbranch}" "${ocbranch}"; then
471 die "Failed to create '${mcbranch}' from '${ocbranch}'."
472 fi
473 fi
474 echo
475
476 # Abort if chromeos already includes the tag unless 'force' is set.
477 if ! have_version "${dtag}"; then
478 if [[ ${force} -eq 0 ]]; then
479 die "Tag or reference '${tag}' already in '${ocbranch}'."
480 fi
481 echo "Warning: Tag '${tag}' already in '${ocbranch}'."
482 echo "Will not merge/notify/prepare/upload."
483 skip_merge=1
484 prepare=0
485 notify=0
486 upload=0
487 fi
488
489 if [[ -n "${merge_cl}" ]]; then
490 ref=$(dequote "$(gerrit --json search "change:${merge_cl}" \
491 | jq ".[].currentPatchSet.ref")")
492 fi
493 if [[ -n "${ref}" ]]; then
494 if ! git fetch "${chromeos}" "${ref}" >/dev/null 2>&1; then
495 die "Failed to fetch '${ref}' from '${chromeos}'."
496 fi
497 git show -s --format=%B FETCH_HEAD > "${tmpfile}"
498 else
499 # We may have a local merge branch.
500 if git rev-parse --verify "${mbranch}" >/dev/null 2>&1; then
501 local subject
502
503 # Make sure the branch actually includes the merge we are looking for.
504 git show -s --format=%B "${mbranch}" > "${tmpfile}"
505 subject="$(head -n 1 "${tmpfile}")"
506 if [[ "${subject}" != "${Subject}" ]]; then
507 rm -f "${tmpfile}"
508 touch "${tmpfile}"
509 fi
510 else
511 rm -f "${tmpfile}"
512 touch "${tmpfile}"
513 fi
514 fi
515 obug=$(grep "^BUG=" "${tmpfile}")
516 if [[ -n "${bug}" && -n "${obug}" && "${bug}" != "${obug}" \
517 && ${force} -eq 0 ]]; then
518 die "Bug mismatch: '${bug}' <-> '${obug}'. Use -f to override."
519 fi
520 if [[ -z "${bug}" ]]; then
521 bug="${obug}"
522 fi
523 if [[ -z "${bug}" ]]; then
524 die "New merge: must specify bug ID."
525 fi
526 ochangeid=$(grep "^Change-Id:" "${tmpfile}")
527 if [[ -n "${changeid}" && -n "${ochangeid}" \
528 && "${changeid}" != "${ochangeid}" && ${force} -eq 0 ]]; then
529 die "Change-Id mismatch: '${changeid}' <-> '${ochangeid}'. Use -f to override."
530 fi
531 if [[ -z "${changeid}" ]]; then
532 changeid="${ochangeid}"
533 fi
534
Guenter Roeck33f263a2019-06-28 08:51:23 -0700535 odependency=$(grep "^Cq-Depend:" "${tmpfile}")
Guenter Roeck9aa69572017-12-12 13:36:30 -0800536 if [[ -n "${dependency}" && -n "${odependency}" && \
537 "${dependency}" != "${odependency}" && ${force} -eq 0 ]]; then
538 die "Dependency mismatch: '${dependency}' <-> '${odependency}'. Use -f to override."
539 fi
540 if [[ -z "${dependency}" ]]; then
541 dependency="${odependency}"
542 fi
543
544 # Check out local reference branch; create it if needed.
545 # It will be retained since it may be needed to apply reverts
546 # prior to executing the merge.
547 # It is the responsibility of the user to remove it after it is
548 # no longer needed.
549 # Note: git rev-parse returns success if ${obranch} includes an
550 # abbreviated SHA. It also returns success if a _remote_ branch
551 # with the same name exists. So let's use show-ref instead.
552 # if ! git rev-parse --verify --quiet "${obranch}"; then
553 if ! git show-ref --verify --quiet "refs/heads/${obranch}"; then
554 if ! git checkout -b "${obranch}" "${tag}"; then
555 die "Failed to create '${obranch}' from '${tag}'."
556 fi
557 else
558 if ! git checkout "${obranch}"; then
559 die "Failed to check out '${obranch}'."
560 fi
561 fi
562
563 if [[ ${prepare} -ne 0 ]]; then
564 # Push reference branch as well as the tag into the CrOS repository.
565 # Assume linear changes only; if the reference branch is reparented,
566 # the user has to explicitly update or remove the remote branch.
567 # Only push tag if it is a release tag; otherwise we neither want nor
568 # need it in the CrOS repository.
569 vtag=$(echo "${tag}" | grep -E "${PATTERN}")
570 if [[ -n "${vtag}" ]]; then
571 git push ${git_skip_validation} --no-verify ${dryrun} "${chromeos}" "refs/tags/${tag}"
572 else
573 echo "${tag} is not a release tag, not pushed"
574 fi
575 if ! git push ${git_skip_validation} --no-verify ${dryrun} "${chromeos}" "${obranch}"; then
576 die "Failed to upload '${obranch}' into '${chromeos}'."
577 fi
578 fi
579}
580
581gitismerge()
582{
583 local sha="$1"
584 local msha
585
586 msha=$(git rev-list -1 --merges "${sha}"~1.."${sha}")
587 [[ -n "$msha" ]]
588}
589
590# Apply patches from gerrit CLs into merge branch.
591do_apply_changes() {
592 local cl
593 local ref
594
595 for cl in "${changes[@]}"; do
596 echo "Applying CL:${cl}"
597 ref=$(dequote "$(gerrit --json search "change:${cl}" \
598 | jq ".[].currentPatchSet.ref" | head -n1)")
599 if [[ -z "${ref}" ]]; then
600 die "Patch set for CL:${cl} not found."
601 fi
602 if ! git fetch "${chromeos}" "${ref}" >/dev/null 2>&1; then
603 die "Failed to fetch CL:${cl}."
604 fi
605 if gitismerge FETCH_HEAD; then
606 # git cherry-pick -m <parent> does not work since it pulls in
607 # the merge as single commit. This messes up history and was
608 # seen to result in obscure and avoidable conflicts.
609 if ! git merge --no-edit FETCH_HEAD; then
610 die "Failed to merge CL:${cl} into merge branch."
611 fi
612 else
613 if ! git cherry-pick FETCH_HEAD; then
614 die "Failed to cherry-pick CL:${cl} into merge branch."
615 fi
616 fi
617 done
618}
619
620# Do the merge.
621# - Create merge branch.
622# - Merge.
623# - Handle conflicts [abort if there are unhandled conflicts].
624# - Create detailed merge commit log.
625do_merge() {
626 # xbranch: Name of branch to merge.
627 # ref: Baseline reference for request-pull.
628 local xbranch
629 local ref
630 local patch
631
632 git branch -D "${mbranch}" >/dev/null 2>&1
633 if ! git checkout -b "${mbranch}" "${ocbranch}"; then
634 die "Failed to create merge branch '${mbranch}'."
635 fi
636
637 if [[ ${prepare} -eq 0 ]]; then
638 xbranch="${obranch}"
639 else
640 xbranch="${chromeos}/${obranch}"
641 fi
642
643 do_apply_changes
644 ref=$(git rev-parse HEAD)
645
646 # Do the merge.
647 # Use --no-ff to ensure this is always handled as merge, even for linear
648 # merges. Otherwise linear merges would succeed and move the branch HEAD
649 # forward even though --no-commit is specified. This lets us add an
650 # explicit merge commit log.
651 conflicts=()
652 if ! git merge --no-commit --no-ff "${xbranch}" > "${tmpfile}"; then
653 files=$(git rerere status)
654 if [[ -n "${files}" ]]; then
655 error "Unresolved conflicts: ${files}."
656 die "Please fix, commit, and repeat merge. Make sure you have 'git rerere' enabled."
657 fi
658 echo "All conflicts resolved, continuing"
659 conflicts=($(grep CONFLICT "${tmpfile}" \
660 | sed -e 's/.*Merge conflict in //'))
661 fi
662
663 # Note: The following is no longer needed in recent versions of git.
664 # Keep it around since it does not hurt.
665 if [[ ${#conflicts[@]} -gt 0 ]]; then
666 git add ${conflicts[*]}
667 fi
668
669 for patch in "${patches[@]}"; do
670 if ! patch -p 1 < "${patch}" >"${tmpfile}"; then
671 die "Failed to apply patch ${patch}"
672 fi
673 if ! git add $(sed -e 's/.* //' "${tmpfile}"); then
674 die "Failed to add patched files to git commit list"
675 fi
676 done
677
678 if ! git commit -s --no-edit; then
679 die "Failed to commit merge."
680 fi
681
682 # Update commit message.
683
684 ( echo "${Subject}"
685 echo
686 echo "Merge of ${tag} into ${cbranch}"
687 echo
688 ) > "${tmpfile}"
689
690 # Add conflicts to description.
691 if [[ ${#conflicts[@]} -gt 0 ]]; then
692 (
693 echo "Conflicts:"
694 for conflict in "${conflicts[@]}"; do
695 echo " ${conflict}"
696 done
697 echo
698 ) >> "${tmpfile}"
699 fi
700
701 if [[ -n "$(git log --oneline "${tag}..${obranch}")" ]]; then
702 ( echo "Changes applied on top of '${tag}' prior to merge:"
703 git log --oneline --no-decorate "${tag}..${obranch}" | \
704 sed -e 's/^/ /'
705 echo
706 ) >> "${tmpfile}"
707 fi
708
709 ( echo "Changelog:"
710 git request-pull "${ref}" . | \
711 sed -n '/^--------------/,$p'
712
713 echo
714
Guenter Roeck33f263a2019-06-28 08:51:23 -0700715 echo "${bug}"
716 echo "TEST=Build and test on various affected systems"
717 echo
Guenter Roeck9aa69572017-12-12 13:36:30 -0800718 if [[ -n "${dependency}" ]]; then
719 echo "${dependency}"
720 fi
Guenter Roeck9aa69572017-12-12 13:36:30 -0800721 if [[ -n "${changeid}" ]]; then
Guenter Roeck9aa69572017-12-12 13:36:30 -0800722 echo "${changeid}"
723 fi
724 ) >> "${tmpfile}"
725
726 # Amend commit with the updated description.
727 if ! git commit -s --amend -F "${tmpfile}"; then
728 die "Failed to amend merge with commit log."
729 fi
730}
731
732do_notify() {
733 local cl
734 local email_cc
735 local cc_notify_cc
736 local subject
737 local message
738 local lbug
739 local tdeadline
740
741 if [[ -z "${merge_cl}" ]]; then
742 die "No merge CL, can not send notifications."
743 fi
744
745 gerrit --json search "change:${merge_cl}" > "${tmpfile}"
746
747 cl=$(dequote "$(jq ".[].number" "${tmpfile}")")
748 if [[ -z "${cl}" ]]; then
749 die "Missing CL for topic '${topic}' (upload into gerrit first)."
750 fi
751
752 subject=$(dequote "$(jq ".[].subject" "${tmpfile}")")
753 message=$(dequote "$(jq ".[].commitMessage" "${tmpfile}" \
754 | sed -e 's/\\n/\n/g')")
755 email_cc=$(dequote "$(jq ".[].owner.email" "${tmpfile}")")
756 if [[ -n "${email_cc}" ]]; then
757 email_cc="-cc=${email_cc}"
758 fi
759
760 if [[ -n "${notify_cc}" ]]; then
761 cc_notify_cc="--cc=${notify_cc}"
762 fi
763
764 if [[ "${bug##BUG=b:}" != "${bug}" ]]; then # buganizer
765 lbug="https://b.corp.google.com/${bug##BUG=b:}"
766 elif [[ "${bug##BUG=chromium:}" != "${bug}" ]]; then # crbug
767 lbug="https://crbug.com/${bug##BUG=chromium:}"
768 else # unknown/invalid
769 lbug="${bug##BUG=}"
770 fi
771
772 tdeadline=$(($(date +%s) + deadline * 86400))
773
774 cat <<-EOF > "${tmpfile}"
775Subject: Review request: "${subject}"
776
777This is the start of the review cycle for the merge of stable release
778${tag} into ${cbranch}. If anyone has issues or concerns
779with this stable release being applied, please let me know.
780
781Bug: ${lbug}
782Code review: https://chromium-review.googlesource.com/#/q/${cl}
783
784Responses should be made by $(date --date="@${tdeadline}").
785Anything received after that time might be too late.
786
787Commit message and changelog are as follows.
788
789${message}
790EOF
791
792 if ! git send-email ${dryrun} --to="${notify_to}" "${cc_notify_cc}" "${email_cc}" \
793 --8bit-encoding="UTF-8" \
794 --suppress-cc=all "${tmpfile}"; then
795 die "Failed to send notification e-mail to '${notify_to}'."
796 fi
797}
798
799do_upload() {
800 if [[ ${upload} -ne 0 ]]; then
801 if ! git push --no-verify ${dryrun} "${chromeos}" "${mbranch}:refs/for/${cbranch}%t=${topic}"; then
802 die "Failed to upload changes into '${chromeos}'."
803 fi
804 elif [[ ${prepare} -ne 0 ]]; then
805 echo "Push into ${chromeos} using the following command:"
806 echo " git push --no-verify ${chromeos} ${mbranch}:refs/for/${cbranch}%t=${topic}"
807 fi
808}
809
810main() {
811 do_getparams "$@"
812 do_validate
813 do_validate_changeids
814 do_setup
815 do_prepare
816 if [[ ${skip_merge} -eq 0 ]]; then
817 do_merge
818 do_upload
819 fi
820 if [[ ${notify} -ne 0 ]]; then
821 find_merge_cl
822 do_notify
823 fi
824
825 exit 0
826}
827
828main "$@"