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 27# This script is used to submit a series of git commits to Differential. Each 28# commit is submitted as a separate review. For each review, this script will 29# create a branch called review_DXXXX (e.g. review_D2185 for Differential 30# revision D2185). When you need to make a change to a review, checkout the 31# review_D2185 branch, commit your change with "git commit --fixup HEAD". To\ 32# upload the change to Differential, use the command: 33# $ arc diff --update D2185 review_D2185_base 34# 35# When your reviews are complete, merge all of the review_DXXXX branches 36# together, and then do a git rebase -ik to meld the code review fixes into the 37# commit that they fixed. Now you have a clean series of patches to push to 38# git. 39 40usage() 41{ 42 echo "Usage: arcgit <-c commit | -r commit1~..commit2> [-R reviewer] " >&2 43 echo " [-C subscriber] [-T testplan] [-n]" >&2 44} 45 46error() 47{ 48 echo "$@" >&2 49 usage 50 rm -f $phab_before $phab_after $arc_msg 51 exit 1 52} 53 54create_review() 55{ 56 local commit phab_id arc_dir 57 unset phab_before phab_after arc_msg 58 commit=$1 59 60 phab_before=`mktemp -t arcoutput` 61 phab_after=`mktemp -t arcoutput` 62 echo "Create review for '`git show $commit -s --oneline`'" 63 64 if [ -n "$dry_run" ] 65 then 66 return 67 fi 68 69 git checkout $commit > /dev/null || error "Could not checkout $commit" 70 71 arc_dir="$(git rev-parse --git-dir)/arc" 72 arc_msg="$arc_dir/create-message" 73 mkdir -p $arc_dir 74 git show -s --format='%B' HEAD > $arc_msg 75 echo >> $arc_msg 76 echo "Test Plan:" >> $arc_msg 77 cat $test_plan >> $arc_msg 78 echo >> $arc_msg 79 echo "Reviewers:" >> $arc_msg 80 echo "$reviewers" >> $arc_msg 81 echo >> $arc_msg 82 echo "Subscribers:" >> $arc_msg 83 echo "$cc_list" >> $arc_msg 84 echo >> $arc_msg 85 86 arc list > $phab_before 87 yes | env EDITOR=true arc diff --create --allow-untracked HEAD~ 88 arc list > $phab_after 89 90 headline="$(git show $commit -s --format=%s)" 91 phab_id=`comm -13 "$phab_before" "$phab_after" | fgrep "$headline" \ 92 | egrep -o 'D[0-9]+:' | tr -d ':'` 93 94 if [ -z "$phab_id" ] 95 then 96 error "Could not get review ID" 97 fi 98 99 git branch review_${phab_id}_base HEAD~ 100 101 git checkout -b review_$phab_id 102 cat - <<EOF | git commit --allow-empty -F - 103squash! $headline 104 105Differential Revision: https://reviews.freebsd.org/$phab_id 106Reviewed by: 107EOF 108} 109 110unset range test_plan reviewers cc_list dry_run 111 112while getopts ":c:C:nr:R:T:" o 113do 114 case "$o" in 115 c) 116 range="${OPTARG}~..${OPTARG}" 117 ;; 118 C) 119 if [ -z "$cc_list" ] 120 then 121 cc_list="$OPTARG" 122 else 123 cc_list="$cc_list, $OPTARG" 124 fi 125 ;; 126 n) 127 dry_run=1 128 ;; 129 r) 130 range=$OPTARG 131 ;; 132 R) 133 if [ -z "$reviewers" ] 134 then 135 reviewers="$OPTARG" 136 else 137 reviewers="$reviewers, $OPTARG" 138 fi 139 ;; 140 T) 141 test_plan=$OPTARG 142 ;; 143 *) 144 error "Unrecognized argument '-$OPTARG'" 145 esac 146done 147 148shift $((OPTIND - 1)) 149OPTIND=1 150 151if [ -n "$1" ] 152then 153 error "Unrecognized argument $1" 154fi 155 156if [ -z "$range" ] 157then 158 error "-c or -r argument is mandatory" 159fi 160 161if [ -n "$test_plan" -a ! -r "$test_plan" ] 162then 163 error "$test_plan is not readable" 164fi 165 166if ! type git > /dev/null 2> /dev/null 167then 168 error "Install devel/git first" 169fi 170 171if ! type arc > /dev/null 2> /dev/null 172then 173 error "Install devel/arcanist first" 174fi 175 176git update-index -q --refresh 177if ! git diff-index --quiet --cached HEAD 178then 179 error "index is unclean" 180fi 181 182if ! git diff-files --quiet 183then 184 error "Working directory is unclean" 185fi 186 187if git ls-files --other --error-unmatch . > /dev/null 2> /dev/null 188then 189 error "Working directory contains untracked files" 190fi 191 192# We have to do a git checkout in order to run arc, so save the original branch 193# so that we can check it out again once we're done. 194if ! orig_branch=$(git symbolic-ref --short -q HEAD) 195then 196 orig_branch=$(git show -s --pretty='%H' HEAD) 197fi 198 199git log --format=%H $range | tail -r | while read -r commit 200do 201 create_review $commit < /dev/null 202done 203 204# Note that due to the use of the pipeline above, the body of the while loop 205# above runs in a subshell. If it exits with an error, execution resumes 206# here rather than exiting the script, so we have to cache the right exit code 207# and return it when we're done cleaning up. 208code=$? 209 210git checkout $orig_branch 211 212exit $code 213 214