#! /bin/sh # # Copyright (c) 2014 Bryan Drewery # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # XXX: Need to support Relnotes: yes # XXX: Need to support PR: set -e : ${SVN:=svn} shown=0 digest=0 MFC_LIST= SVN_BRANCH=head SVN_REMOTE="^/${SVN_BRANCH}" DO_STATUS=1 DO_RFA=0 while getopts "b:m:M:rS" FLAG; do case "${FLAG}" in b) SVN_BRANCH="${OPTARG}" SVN_REMOTE="^/${SVN_BRANCH}" ;; m) MSG="${OPTARG}" ;; M) SVN_REMOTE="${OPTARG%/${SVN_BRANCH}}/${SVN_BRANCH}" ;; r) DO_RFA=1 ;; S) DO_STATUS=0 ;; *) echo "Unsupported option ${FLAG}" >&2 exit 1 ;; esac done shift $((OPTIND-1)) # Verify checkout is right and determine MFC root branch=$(svn info | awk '/^Relative URL:/ {print $3}') # Trim away ^/ branch=${branch#*/} stable=${branch%/*} if ! [ "${stable}" = "stable" -o "${stable}" = "releng" ]; then echo "Must be ran from a stable/* checkout root." >&2 exit 1 fi mfs=0 MFC=MFC if [ "${stable}" = "releng" ]; then mfs=1 MFC=MFS fi # Ensure clean tree if [ ${DO_STATUS} -eq 1 ]; then svn_status="$(svn status -q)" if [ -n "${svn_status}" ]; then echo "Your tree is unclean." >&2 echo "${svn_status}" >&2 exit 1 fi fi # Determine MFC root release="${branch#*/}" release="${release%%.*}" if [ ${release} -ge 10 ]; then mfc_method=root else mfc_method=nested fi svn_root=${PWD} REVS="$@" tmplog=$(mktemp -t mfc) #trap 'rm -f ${tmplog};exit' exit for rev in ${REVS}; do if [ -n "${MFC_LIST}" ]; then MFC_LIST="${MFC_LIST}," digest=1 fi MFC_LIST="${MFC_LIST}r${rev#r}" done echo "${MFC} ${MFC_LIST}:" > ${tmplog} echo >> ${tmplog} if [ -n "${MSG}" ]; then echo " ${MSG}" >> ${tmplog} echo >> ${tmplog} fi if [ ${digest} -eq 1 ]; then indent=" " fmt=75 else indent=" " fmt=77 fi patterns=$(cat << EOF COPYRIGHT LOCKS MAINTAINERS Makefile Makefile.inc1 Makefile.mips ObsoleteFiles.inc README UPDATING bin/@DIR@ cddl/contrib/@DIR@ contrib/@DIR@ crypto/@DIR@ etc games/@DIR@ gnu/lib/@DIR@ gnu/usr.bin/@DIR@ include kerberos5 lib/@DIR@ libexec/@DIR@ release rescue sbin/@DIR@ secure/lib/@DIR@ secure/libexec/@DIR@ secure/usr.bin/@DIR@ secure/usr.sbin/@DIR@ share/@DIR@ share/Makefile share/man/@DIR@ sys tools/build/@DIR@ usr.bin/@DIR@ usr.sbin/@DIR@ EOF ) for rev in ${REVS}; do [ ${shown} -eq 1 ] && echo [ ${digest} -eq 1 ] && echo " r${rev#r}:" >&3 # Grab all of the first lines until the first empty line. Also # indent enough. fulllog=$(${SVN} log -vr ${rev} ${SVN_REMOTE}) if [ ${mfs} -eq 0 ]; then log=$(echo "${fulllog}" | sed -e '/Changed paths:/,/^$/d' | sed -e '3,/^$/!d;/^[[:space:]]*$/d') else log=$(echo "${fulllog}" | sed -e '/Changed paths:/,/^$/d' \ -e '1,2d' -Ee '/^-+$/d') fi echo "${log}" | fmt -w ${fmt} | sed -e "s/^/${indent}/" >&3 # Show log on stdout and align msg echo "r${rev#r}: ${log}" | fmt -w 70 | sed -e '2,$s,^, ,' if [ "${mfc_method}" = "root" ]; then ${SVN} merge -c ${rev} ${SVN_REMOTE} || { echo "--- Failed to merge r${rev#r}. Fix and resume." \ >&2 kill -SIGSTOP $$ } else # Do nested merge # Determine list of directories touched changed_files=$(echo "${fulllog}" | sed -e '/Changed paths:/,/^$/!d;/Changed paths:/d;/^$/d' \ -e "s,/${SVN_BRANCH}/,," | awk '{print $2}'| sort -r) merge_path= for sfile in ${changed_files}; do path=${sfile} seen_merge_path=0 until [ -z "${path}" ]; do file=${path##*/} path=${path%/*} if [ "${path}" = "${file}" ]; then path="" fullpath="${file}" else fullpath="${path}/${file}" fi #echo "path: ${path} file: ${file} fullpath: ${fullpath} merge_path: ${merge_path}" >&2 for pattern in ${patterns}; do [ "${pattern%/@DIR@}" != "${pattern}" ] \ && pattern="${pattern%/@DIR@}/${file}" if [ "${pattern}" = "${fullpath}" ]; \ then # Is this path already merged? if [ "${merge_path}" = \ "${fullpath}" ]; then seen_merge_path=1 break 2 fi ${SVN} merge -c ${rev} ${SVN_REMOTE}/${fullpath} ${fullpath} merge_path="${fullpath}" seen_merge_path=1 break 2 fi done done #[ ${seen_merge_path} -eq 0 ] && merge_path= if [ ${seen_merge_path} -eq 0 ]; then echo "--- Error ---" >&2 echo "No merge path found for ${sfile}" >&2 echo "Unable to continue, not cleaning up." >&2 exit 1 fi done fi shown=1 done 3>> ${tmplog} echo "--- Commit log:" if [ ${DO_RFA} -eq 1 ]; then if ! tail -n -1 "${tmplog}" | grep -q ": "; then echo >> "${tmplog}" fi echo "Approved by: re (XXX)" >> "${tmplog}" fi ${EDITOR:-vim} ${tmplog} if [ ${DO_RFA} -eq 1 ]; then tmpmfcdir=$(mktemp -dt mfc-rfa) tmpcommit="${tmpmfcdir}/mfc-$(echo "${REVS}" | tr ' ' '-').diff" tmpbody="${tmpmfcdir}/mailbody.txt" { cat "${tmplog}" echo echo "# > svn status" svn status -q | sed -e 's,^,# ,' echo "#" echo "# > svn diff --show-copies-as-adds -x -up" svn diff --show-copies-as-adds -x -up } > "${tmpcommit}" echo "Please approve the attached patch for ${branch}" > "${tmpbody}" mutt \ ${EMAIL+-e "set use_envelope_from=yes"} \ ${NOP+-e 'source /usr/local/share/examples/mutt/gpg.rc; set crypt_autosign=yes; set pgp_use_gpg_agent=yes;'} \ ${EMAIL+-b "${EMAIL}"} \ -e 'set fast_reply=yes' \ -e 'set autoedit=yes; set edit_headers=yes' \ -s "RfA: ${MFC} ${REVS} to ${branch}" \ -i "${tmpbody}" \ -a "${tmpcommit}" -- \ re@FreeBSD.org rm -rf "${tmpmfcdir}" fi mv -f "${tmplog}" commit echo "# svn commit -F commit"