Index: head/Tools/scripts/mfh =================================================================== --- head/Tools/scripts/mfh (revision 537958) +++ head/Tools/scripts/mfh (revision 537959) @@ -1,196 +1,196 @@ #!/bin/sh # # mfh - Merge from head to a given branch # # Usage: mfh [] [] # is optional and defaults to latest branch # If supplying multiple revnumbers, put them in chronological order # (111111 111112 111113) # # Copyright 2013 Baptiste Daroussin # 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. # # MAINTAINER= portmgr@FreeBSD.org set -eu LF=$(printf '\nX') LF=${LF%X} IFS="${LF}" # defaults from here --------------------------- : ${svnserver:="svn+ssh://repo.FreeBSD.org"} : ${EDITOR:=vi} # implementation from here --------------------- err() { echo "$@" >&2 exit 1 } ask() { question=${1} answer=x while [ "${answer}" != "y" -a "${answer}" != "n" ] ; do printf "%s [y/n] " "${question}" read -r answer done [ "${answer}" = "y" ] && return 0 return 1 } # -- find svn and diff coloring command -- if [ -n "$(command -v svn 2>/dev/null)" ]; then svn=svn elif [ -n "$(command -v svnlite 2>/dev/null)" ]; then svn=svnlite else err "Neither svn(1) nor svnlite(1) found. Please install devel/subversion." fi latest_branch=$("${svn}" ls ${svnserver}/ports/branches/|sed -ne '/^2.*Q./s|/$||p'|tail -1) [ $# -lt 1 ] && err "$(basename "$0") requires at least 1 arguments: [] [...]" branch=$1 if [ -n "$(command -v ydiff)" ] ; then cdiffcmd=ydiff elif [ -n "$(command -v colordiff)" ] ; then cdiffcmd=colordiff else cdiffcmd=cat fi # I sure hope by 2030 we'll be doing something else. Yes, famous last words. if expr "${branch}" : '20[12][0-9]Q[1-4]' > /dev/null; then shift if [ "${latest_branch}" != "${branch}" ]; then ask "/!\\ The latest branch is ${latest_branch}, do you really want to commit to ${branch}?" || exit 1 fi else branch=${latest_branch} fi ## -- parse revisions -- for rev in "$@" do rev=${rev##r} # remove a leading "r" case ${rev} in ''|*[!-0-9]*) err "revision \"${rev}\" should be a number" ;; esac done ## -- set up tmpdir and auto-clean -- dir=$(mktemp -d /tmp/mfh.XXXXXX) trap "rc=\$? ; echo '+ rm -rf \"${dir}\"' ; rm -rf \"\${dir}\" ; trap - EXIT ; exit \$rc" EXIT INT QUIT TERM cd "${dir}" ## -- check out, merge, generate commit log -- printf "MFH:" > commit.txt for rev in "$@" do rev=${rev##r} printf " r%s" "${rev}" >> commit.txt done echo >> commit.txt # iterate over revisions to assemble dirlist (for checkout) # and accumulate log messages dirlist="" for rev in "$@" do rev=${rev##r} for f in $("${svn}" diff --summarize -c "r${rev}" "${svnserver}/ports/head"); do # ignore top-level filenames without slash case ${f} in */*) ;; *) continue ;; esac f=${f#*/ports/head/} # strip down f to the first two components (CATEGORY/PORTNAME), # so that if the MFH only addresses files/, we still get the # full port to review, and, for instance, bump PORTREVISION # separately if the patch was broken out from a larger lump. while :; do case ${f} in */*/*) f=${f%/*} ;; *) break ;; esac done dirlist="${dirlist}${f}${LF}" done "${svn}" log "-r${rev##-}" ${svnserver}/ports/head | sed '1,2d;$d;/^MFH:/d' \ | sed '$d' >> commit.txt done dirlist=$(printf '%s' "${dirlist}" | sort -u | sed "s}^}${branch}/}") "${svn}" co --quiet --depth=empty ${svnserver}/ports/branches/"${branch}" "${svn}" up --parents --set-depth=infinity $dirlist "${svn}" up --quiet "${branch}" for rev in "$@" do rev=${rev##r} "${svn}" merge -c "r${rev}" ^/head/ "${branch}" done "${svn}" up --quiet "${branch}" ## -- present final result to user -- svnstat=$("${svn}" status "${branch}") if [ -z "${svnstat}" ] ; then err "The MFH came up empty - already merged? Wrong revision given?" fi echo printf '%s\n' "${svnstat}" "${svn}" diff "${branch}" | $cdiffcmd printf '\nSee the status/diff above: all the merge work was done on %s.\n' "${dir}/${branch}" ask "Do you want to commit? (no = start a shell)" || ( echo "Dropping you to a shell so you can investigate. Exit the shell to resume this script." cd "${branch}" pwd su -m $(id -un) || : ask "Do you want to commit now? (no = clean up and abort)" || err "User-requested abort." ) echo >> commit.txt -echo "Approved by: " >> commit.txt +echo "Approved by: " >> commit.txt ## -- edit pre-assembled log message and commit -- ${EDITOR} commit.txt while ! "${svn}" ci -F commit.txt "${branch}"; do if ! ask "Commit failed. Re-edit message and try again?"; then save_log="$(mktemp -t mfh)" cp -f commit.txt "${save_log}" echo "Saving commit log to ${save_log}" break fi ${EDITOR} commit.txt done # the trap will clean up for us exit 0