#!/usr/bin/env bash # Default behaviour: # Reverts all patches in patch dir, notes down the ones # which were previously applied. Applies all from the beginning # and reports about previously unapplied patches. If there's # an error, reverts the last one and stops. # # Usage: ./patcher -b -r -v # -b: is the target location relative to which patches will be applied # -p: is where to take the patches from (default is geth) # -r: reverts all and exit if this flag is present # -c: reverts all to see what's applied, applies all previously applied back again, # reports unapplied patches in this branch by comparing with "develop" including # uncommitted ones and exits (with 1 if there are any) # -v: verbose error reporting about failed patch # # If -b is not present, default path is as below ($basepath). dir=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) # Patches path is geth unless specified. patches=("$dir"/geth/*.patch) # Use this branch as a reference for comparing patches # in current branch (-c option). baseBranch="develop" # Base path is vendor/github.com/ethereum/go-ethereum # unless specified. basepath="vendor/github.com/ethereum/go-ethereum" gitApply() { f=$1 basepath=$2 verbose=$3 if [ $verbose -eq 1 ]; then if [ $basepath == "." ]; then git apply "$f" else git apply "$f" --directory="$basepath" fi else if [ $basepath == "." ]; then git apply "$f" > /dev/null 2>&1 else git apply "$f" --directory="$basepath" > /dev/null 2>&1 fi fi } gitApplyReverse() { f=$1 basepath=$2 if [ $basepath == "." ]; then git apply "$f" -R > /dev/null 2>&1 else git apply "$f" --directory="$basepath" -R > /dev/null 2>&1 fi } verbose=0 revert=0 compare=0 while getopts b:p:rcv opt; do case $opt in b) basepath=$OPTARG ;; p) patches=("$dir"/$OPTARG/*.patch) ;; r) revert=1 ;; c) compare=1 ;; v) verbose=1 ;; \?) echo "Invalid flag: -$OPTARG" >&2 exit ;; esac done if [ $revert -eq 1 ]; then # Reverts in reverse order and exits. for ((i=${#patches[@]}-1; i>=0; i--)); do gitApplyReverse "${patches[$i]}" "$basepath" 0 done echo "Reverted all." exit fi if [ $compare -eq 1 ]; then applied=() unapplied=() # Finds applied patches using reverse order and # notes them down. for ((i=${#patches[@]}-1; i>=0; i--)); do f=${patches[$i]} gitApplyReverse "$f" "$basepath" if [ $? -ne 0 ]; then unapplied+=("$f") else applied+=("$f") fi done # Applies reverted patches back again. for ((i=${#applied[@]}-1; i>=0; i--)); do f=${applied[$i]} gitApply "$f" "$basepath" 0 done # Sorts out new patches' paths by comparing with base branch. fromBaseBranch=($(git diff $baseBranch --stat | grep "\\.patch" | while IFS=" " read -r -a line; do path="$(pwd)/${line[0]}" echo "$path" done )) # Also does the same with uncommitted. uncommitted=($(git status -u --porcelain | grep "\\.patch" | while IFS=" " read -r -a line; do length=${#line[@]} path="$(pwd)/${line[$((length - 1))]}" echo "$path" done )) newPatches=( "${fromBaseBranch[@]}" "${uncommitted[@]}" ) # Checks new patches and exits with 1 if there are unapplied. hasUnapplied=0 for newPatch in "${newPatches[@]}"; do for unapp in "${unapplied[@]}"; do if [ "$unapp" == "$newPatch" ]; then echo "Recently added/changed but not applied: $unapp" hasUnapplied=1 break fi done done exit $hasUnapplied fi applied=() echo -en "\\n" echo "Previously applied:" echo "===================" # Reverts every patch in reverse order to see # which was previously applied. for ((i=${#patches[@]}-1; i>=0; i--)); do f=${patches[$i]} gitApplyReverse "$f" "$basepath" if [ $? -eq 0 ]; then applied+=("$f") echo "$f" fi done echo "===================" echo -en "\\n" # Applies every patch from the beginning. for ((i=0; i<${#patches[@]}; i++)); do f=${patches[$i]} # If not applied, report it new. has=0 for patch in "${applied[@]}"; do if [ "$patch" == "$f" ]; then has=1 break fi done if [ $has -eq 0 ]; then echo "Applying new: $f" echo -en "\\n" fi gitApply "$f" "$basepath" $verbose if [ $? -ne 0 ]; then echo "Failed and reverting: $f" gitApplyReverse "$f" "$basepath" echo -en "\\n" exit fi done echo -en "\\n" echo "Done."