1#!/bin/sh 2# 3# Copyright (c) 2015 Ryan Stone. All rights reserved. 4# 5# Redistribution and use in source and binary forms, with or without 6# modification, are permitted provided that the following conditions 7# are met: 8# 1. Redistributions of source code must retain the above copyright 9# notice, this list of conditions and the following disclaimer. 10# 2. Redistributions in binary form must reproduce the above copyright 11# notice, this list of conditions and the following disclaimer in the 12# documentation and/or other materials provided with the distribution. 13# 14# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24# SUCH DAMAGE. 25# 26# $FreeBSD$ 27 28# This script is used to submit a series of git commits to Differential. Each 29# commit is submitted as a separate review. For each review, this script will 30# create a branch called review_DXXXX (e.g. review_D2185 for Differential 31# revision D2185). When you need to make a change to a review, checkout the 32# review_D2185 branch, commit your change with "git commit --fixup HEAD". To\ 33# upload the change to Differential, use the command: 34# $ arc diff --update D2185 review_D2185_base 35# 36# When your reviews are complete, merge all of the review_DXXXX branches 37# together, and then do a git rebase -ik to meld the code review fixes into the 38# commit that they fixed. Now you have a clean series of patches to push to 39# git. 40 41usage() 42{ 43 echo "Usage: arcgit <-c commit | -r commit1~..commit2> [-R reviewer] " >&2 44 echo " [-C subscriber] [-T testplan] [-n]" >&2 45} 46 47error() 48{ 49 echo "$@" >&2 50 usage 51 rm -f $phab_before $phab_after $arc_msg 52 exit 1 53} 54 55create_review() 56{ 57 local commit phab_id arc_dir 58 unset phab_before phab_after arc_msg 59 commit=$1 60 61 phab_before=`mktemp -t arcoutput` 62 phab_after=`mktemp -t arcoutput` 63 echo "Create review for '`git show $commit -s --oneline`'" 64 65 if [ -n "$dry_run" ] 66 then 67 return 68 fi 69 70 git checkout $commit > /dev/null || error "Could not checkout $commit" 71 72 arc_dir="$(git rev-parse --git-dir)/arc" 73 arc_msg="$arc_dir/create-message" 74 mkdir -p $arc_dir 75 git show -s --format='%B' HEAD > $arc_msg 76 echo >> $arc_msg 77 echo "Test Plan:" >> $arc_msg 78 cat $test_plan >> $arc_msg 79 echo >> $arc_msg 80 echo "Reviewers:" >> $arc_msg 81 echo "$reviewers" >> $arc_msg 82 echo >> $arc_msg 83 echo "Subscribers:" >> $arc_msg 84 echo "$cc_list" >> $arc_msg 85 echo >> $arc_msg 86 87 arc list > $phab_before 88 yes | env EDITOR=true arc diff --create --allow-untracked HEAD~ 89 arc list > $phab_after 90 91 headline="$(git show $commit -s --format=%s)" 92 phab_id=`comm -13 "$phab_before" "$phab_after" | fgrep "$headline" \ 93 | egrep -o 'D[0-9]+:' | tr -d ':'` 94 95 if [ -z "$phab_id" ] 96 then 97 error "Could not get review ID" 98 fi 99 100 git branch review_${phab_id}_base HEAD~ 101 102 git checkout -b review_$phab_id 103 cat - <<EOF | git commit --allow-empty -F - 104squash! $headline 105 106Differential Revision: https://reviews.freebsd.org/$phab_id 107Reviewed by: 108EOF 109} 110 111unset range test_plan reviewers cc_list dry_run 112 113while getopts ":c:C:nr:R:T:" o 114do 115 case "$o" in 116 c) 117 range="${OPTARG}~..${OPTARG}" 118 ;; 119 C) 120 if [ -z "$cc_list" ] 121 then 122 cc_list="$OPTARG" 123 else 124 cc_list="$cc_list, $OPTARG" 125 fi 126 ;; 127 n) 128 dry_run=1 129 ;; 130 r) 131 range=$OPTARG 132 ;; 133 R) 134 if [ -z "$reviewers" ] 135 then 136 reviewers="$OPTARG" 137 else 138 reviewers="$reviewers, $OPTARG" 139 fi 140 ;; 141 T) 142 test_plan=$OPTARG 143 ;; 144 *) 145 error "Unrecognized argument '-$OPTARG'" 146 esac 147done 148 149shift $((OPTIND - 1)) 150OPTIND=1 151 152if [ -n "$1" ] 153then 154 error "Unrecognized argument $1" 155fi 156 157if [ -z "$range" ] 158then 159 error "-c or -r argument is mandatory" 160fi 161 162if [ -n "$test_plan" -a ! -r "$test_plan" ] 163then 164 error "$test_plan is not readable" 165fi 166 167if ! type git > /dev/null 2> /dev/null 168then 169 error "Install devel/git first" 170fi 171 172if ! type arc > /dev/null 2> /dev/null 173then 174 error "Install devel/arcanist first" 175fi 176 177git update-index -q --refresh 178if ! git diff-index --quiet --cached HEAD 179then 180 error "index is unclean" 181fi 182 183if ! git diff-files --quiet 184then 185 error "Working directory is unclean" 186fi 187 188if git ls-files --other --error-unmatch . > /dev/null 2> /dev/null 189then 190 error "Working directory contains untracked files" 191fi 192 193# We have to do a git checkout in order to run arc, so save the original branch 194# so that we can check it out again once we're done. 195if ! orig_branch=$(git symbolic-ref --short -q HEAD) 196then 197 orig_branch=$(git show -s --pretty='%H' HEAD) 198fi 199 200git log --format=%H $range | tail -r | while read -r commit 201do 202 create_review $commit < /dev/null 203done 204 205# Note that due to the use of the pipeline above, the body of the while loop 206# above runs in a subshell. If it exits with an error, execution resumes 207# here rather than exiting the script, so we have to cache the right exit code 208# and return it when we're done cleaning up. 209code=$? 210 211git checkout $orig_branch 212 213exit $code 214 215