status-im/status-go

View on GitHub
_assets/patches/patcher

Summary

Maintainability
Test Coverage
#!/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 <base_path> -r -v
# -b: <base_path> is the target location relative to which patches will be applied
# -p: <patch_path> 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 1
    fi
done

echo -en "\\n"
echo "Done."