#!/usr/bin/env bash set -e cd $(dirname $0) cd .. cd .. if [[ -z ${PACKAGE} ]] || [[ -z ${TO_COMMIT} ]] ; then echo "USAGE: PACKAGE= [TO_DIR=] TO_COMMIT= [TO_REMOTE=] ./scripts/rebase" exit 1 fi # Basic environment variables STAGING_BRANCH=staging-${RANDOM} FROM_REMOTE_NAME=from-remote-${RANDOM} FROM_BRANCH=from-${RANDOM} TO_REMOTE_NAME=to-remote-${RANDOM} TO_BRANCH=to-${RANDOM} GC_DIRECTORY=packages/${PACKAGE}/generated-changes EXCLUDE_DIRECTORY=${GC_DIRECTORY}/exclude CHARTS_ORIGINAL_DIRECTORY=packages/${PACKAGE}/charts-original CRD_CHART_DIRECTORY=packages/${PACKAGE}/charts-crd # TODO: get from package.yaml CRDS_DIRECTORY=crd-manifest # TODO: get from package.yaml PACKAGE_YAML_PATH=packages/${PACKAGE}/package.yaml # Grab environment variables from package.yaml FROM_REMOTE=$(yq e '.url' ${PACKAGE_YAML_PATH}) if [[ "${FROM_REMOTE}" != *.git ]]; then echo "./scripts/rebase can only be done on Git-based packages, found url: ${FROM_REMOTE}" exit 1 fi if [[ -z "${TO_REMOTE}" ]]; then TO_REMOTE=${FROM_REMOTE} fi FROM_COMMIT=$(yq e '.commit' ${PACKAGE_YAML_PATH}) if [[ -z ${FROM_COMMIT} ]]; then echo "Unable to find 'commit' in ${PACKAGE_YAML_PATH}" fi FROM_DIR=$(yq e '.subdirectory' ${PACKAGE_YAML_PATH}) if [[ -z ${FROM_DIR} ]]; then FROM_DIR="." fi if [[ -z "${TO_DIR}" ]]; then TO_DIR=${FROM_DIR} fi CHARTS_WORKING_DIRECTORY=$(yq e '.workingDir' ${PACKAGE_YAML_PATH}) if [ -z "${CHARTS_WORKING_DIRECTORY}" ] || [ "${CHARTS_WORKING_DIRECTORY}" == "null" ]; then DEST_DIRECTORY=packages/${PACKAGE}/charts else DEST_DIRECTORY=packages/${PACKAGE}/${CHARTS_WORKING_DIRECTORY} fi IGNORE_DEPS=$(yq e '.ignoreDependencies' ${PACKAGE_YAML_PATH} -o p | cut -d ' ' -f3) # Start rebase process if [ -z "${FROM_DIR}" ] && [ -z "${TO_DIR}" ]; then echo "Attempting to rebase ${PACKAGE} from commit ${FROM_COMMIT} to ${TO_DIR}" elif [ -z "${FROM_DIR}" ] && [ -n "${TO_DIR}" ]; then echo "Attempting to rebase ${PACKAGE} from commit ${FROM_COMMIT} to ${TO_DIR} at subdirectory ${TO_DIR}" elif [ -n "${FROM_DIR}" ] && [ -z "${TO_DIR}" ]; then echo "Attempting to rebase ${PACKAGE} from commit ${FROM_COMMIT} at subdirectory ${FROM_DIR} to ${TO_DIR}" else echo "Attempting to rebase ${PACKAGE} from commit ${FROM_COMMIT} at subdirectory ${FROM_DIR} to ${TO_DIR} at subdirectory ${TO_DIR}" fi echo "" # Pre-rebase checks echo "Running pre-flight checks..." echo "> Checking if Git is clean..." if [[ -n "$(git status --porcelain)" ]]; then echo "Cannot run rebase on unclean repository:" git status --porcelain exit 1 fi echo "> Checking if ${DEST_DIRECTORY} exists..." if [ -d "${DEST_DIRECTORY}" ]; then echo "Destination directory ${DEST_DIRECTORY} already exists" exit 1 fi remotes=$(git remote) echo "> Checking if ${FROM_REMOTE_NAME} exists..." if echo ${remotes} | grep -q ${FROM_REMOTE_NAME}; then echo "Remote ${FROM_REMOTE_NAME} already exists" exit 1 fi echo "> Checking if ${TO_REMOTE_NAME} exists..." if echo ${remotes} | grep -q ${TO_REMOTE_NAME}; then echo "Remote ${TO_REMOTE_NAME} already exists" exit 1 fi # Ensure branches doesn't already exist echo "> Checking if sandbox branches (${STAGING_BRANCH}, ${FROM_BRANCH}, ${TO_BRANCH}) exist..." branches=$(git show-ref --heads | cut -d/ -f3-) if echo ${branches} | grep -q ${STAGING_BRANCH}; then echo "Branch ${STAGING_BRANCH} already exists" exit 1 elif echo ${branches} | grep -q ${FROM_BRANCH}; then echo "Branch ${FROM_BRANCH} already exists" exit 1 elif echo ${branches} | grep -q ${TO_BRANCH}; then echo "Branch ${TO_BRANCH} already exists" exit 1 fi CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) # Cleanup logic trap 'cleanup' EXIT cleanup() { # Execute all cleanup even if there is failures echo "" echo "Cleaning up Git remotes and branches created by this script..." set +e git remote rm ${FROM_REMOTE_NAME} 2>/dev/null git remote rm ${TO_REMOTE_NAME} 2>/dev/null git clean -df 1>/dev/null 2>/dev/null git reset --hard 1>/dev/null 2>/dev/null git checkout ${CURRENT_BRANCH} 2>/dev/null git branch -D ${STAGING_BRANCH} 2>/dev/null git branch -D ${FROM_BRANCH} 2>/dev/null git branch -D ${TO_BRANCH} 2>/dev/null } echo "" echo "Preparing Git for rebase..." # Start rebase echo "> Checking out a new branch at ${STAGING_BRANCH} to sandbox changes..." git checkout -b ${STAGING_BRANCH} 1>/dev/null 2>/dev/null # Add provided remotes to git echo "> Adding ${FROM_REMOTE} as a remote ${FROM_REMOTE_NAME} on Git" git remote add ${FROM_REMOTE_NAME} ${FROM_REMOTE} 1>/dev/null 2>/dev/null echo "> Fetching ${FROM_COMMIT} from ${FROM_REMOTE_NAME} on Git" git fetch --depth=1 ${FROM_REMOTE_NAME} ${FROM_COMMIT} echo "> Adding ${TO_REMOTE} as a remote ${TO_REMOTE_NAME} on Git" git remote add ${TO_REMOTE_NAME} ${TO_REMOTE} 1>/dev/null 2>/dev/null echo "> Fetching ${TO_COMMIT} from ${TO_REMOTE_NAME} on Git" git fetch --depth=1 ${TO_REMOTE_NAME} ${TO_COMMIT} # Checkout the provided tag from that remote into the FROM_BRANCH echo "> Checking out a new branch at ${FROM_BRANCH} tracking ${FROM_REMOTE} at ${FROM_COMMIT}..." git checkout ${FROM_COMMIT} -b ${FROM_BRANCH} 1>/dev/null 2>/dev/null # Fail if subdirectory specified does not exist on remote echo "> Checking if ${FROM_REMOTE} at ${FROM_COMMIT} has subdirectory '${FROM_DIR}'..." if [ ! -d "${FROM_DIR}" ]; then echo "${FROM_REMOTE} at ${FROM_COMMIT} does not contain subdirectory ${FROM_DIR}" exit 1 fi # Copy subdirectory into a special folder mkdir -p .rebase/from cp -R ${FROM_DIR}/* .rebase/from git add .rebase/from 1>/dev/null 2>/dev/null git commit -m "Temporarily moving ${FROM_DIR} to .rebase/from" 1>/dev/null 2>/dev/null # Checkout the provided tag from that remote into the TO_BRANCH echo "> Checking out a new branch at ${TO_BRANCH} tracking ${TO_REMOTE} at ${TO_COMMIT}..." git checkout ${TO_COMMIT} -b ${TO_BRANCH} 1>/dev/null 2>/dev/null # Fail if subdirectory specified does not exist on remote echo "> Checking if ${TO_REMOTE} at ${TO_COMMIT} has subdirectory '${TO_DIR}'..." if [ ! -d "${TO_DIR}" ]; then echo "${TO_REMOTE} at ${TO_COMMIT} does not contain subdirectory ${TO_DIR}" exit 1 fi # Copy subdirectory into a special folder mkdir -p .rebase/to cp -R ${TO_DIR}/* .rebase/to git add .rebase/to 1>/dev/null 2>/dev/null git commit -m "Temporarily moving ${TO_DIR} to .rebase/to" 1>/dev/null 2>/dev/null # Pull charts into directory in order git checkout ${STAGING_BRANCH} 1>/dev/null 2>/dev/null PACKAGE=${PACKAGE} USE_CACHE=${USE_CACHE} make prepare 1>/dev/null 2>/dev/null chmod 644 $(find ${DEST_DIRECTORY} -type f | xargs) # Save changes added by developer if [ -d "${CRD_CHART_DIRECTORY}" ]; then git add ${DEST_DIRECTORY} ${CRD_CHART_DIRECTORY} else git add ${DEST_DIRECTORY} fi git commit -m "Add changes saved in generated-changes" 1>/dev/null 2>/dev/null # Keep track of commits that need to be dropped entirely GC_COMMIT=$(git rev-parse --short HEAD) if [ -d "${CRD_CHART_DIRECTORY}" ]; then # Move CRDs back into main chart so that you can rebase changes to CRDs as well mv ${CRD_CHART_DIRECTORY}/${CRDS_DIRECTORY} ${DEST_DIRECTORY}/crds git add ${CRD_CHART_DIRECTORY}/${CRDS_DIRECTORY} ${DEST_DIRECTORY}/crds git commit -m "Move CRDs back into main chart" 1>/dev/null 2>/dev/null fi if [ -d "${EXCLUDE_DIRECTORY}" ]; then # Calculate excluded files and directories EXCLUDE_DIRECTORIES=$(find ${EXCLUDE_DIRECTORY}/* -type d | sed "s/${EXCLUDE_DIRECTORY//\//\\/}\///") EXCLUDE_FILES=$(find ${EXCLUDE_DIRECTORY}/* -type f | sed "s/${EXCLUDE_DIRECTORY//\//\\/}\///") fi git checkout ${FROM_BRANCH} -- .rebase/from 1>/dev/null 2>/dev/null git checkout ${TO_BRANCH} -- .rebase/to 1>/dev/null 2>/dev/null ADDED_PATHS=$(diff -rq .rebase/to .rebase/from | grep 'Only in .rebase/to' | cut -d ' ' -f3- | sed 's/: /\//g' || true) REMOVED_PATHS=$(diff -rq .rebase/to .rebase/from | grep 'Only in .rebase/from' | cut -d ' ' -f3- | sed 's/: /\//g' || true) git reset HEAD 1>/dev/null 2>/dev/null git add .rebase/from 1>/dev/null 2>/dev/null git commit -m "Add .rebase/from" 1>/dev/null 2>/dev/null mv .rebase/from .rebase/from-copy mv .rebase/to .rebase/from if [[ -n ${IGNORE_DEPS} ]]; then IGNORE_DEPS_DIFF_ARGS=$(echo "${IGNORE_DEPS}" | tr ' ' '\n' | sed 's:\(.*\):\:!.rebase/from/charts/\1/*:g' | xargs) else unset IGNORE_DEPS_DIFF_ARGS fi if [[ -n ${EXCLUDE_FILES} ]]; then EXCLUDE_FILES_DIFF_ARGS=$(echo "${EXCLUDE_FILES}" | tr ' ' '\n' | sed 's:\(.*\):\:!.rebase/from/\1:g' | xargs) else unset EXCLUDE_FILES_DIFF_ARGS fi git diff -- ${IGNORE_DEPS_DIFF_ARGS} ${EXCLUDE_FILES_DIFF_ARGS} > diff.patch git reset HEAD~1 git checkout -- . sed -i.bak -e "s:diff \(.*\) a/.rebase/from/\(.*\) b/.rebase/from/\(.*\):diff \1 a/${DEST_DIRECTORY}/\2 b/${DEST_DIRECTORY}/\3 :g" diff.patch sed -i.bak "s:--- a/.rebase/from/\(.*\):--- a/${DEST_DIRECTORY}/\1:g" diff.patch sed -i.bak "s:+++ b/.rebase/from/\(.*\):+++ b/${DEST_DIRECTORY}/\1:g" diff.patch sed -i.bak "s:index \(.*\) \(.*\):index \1 100644:g" diff.patch rm diff.patch.bak echo "> Applying difference in three-way merge..." git apply --reject diff.patch 1>/dev/null 2>/dev/null || true for to_path in ${ADDED_PATHS}; do from_paths=$(echo ${to_path} | sed "s:.rebase/to:.rebase/from:g") [[ -d ${from_paths} ]] && from_paths=$(find ${from_paths} -type f) for from_path in ${from_paths}; do unset should_continue for ignored_dep in ${IGNORE_DEPS}; do if echo ${from_path} | grep ".rebase/from/charts/${ignored_dep}" 1>/dev/null 2>/dev/null; then should_continue=1; break fi done [[ -n ${should_continue} ]] && continue chart_path=$(echo ${from_path} | sed "s:.rebase/from:${DEST_DIRECTORY}:g") mkdir -p $(dirname ${chart_path}) cp ${from_path} ${chart_path} done done for to_path in ${REMOVED_PATHS}; do from_paths=$(echo ${to_path} | sed "s:.rebase/to:.rebase/from:g") [[ -d ${from_paths} ]] && from_paths=$(find ${from_paths} -type f) for from_path in ${from_paths}; do unset should_continue for ignored_dep in ${IGNORE_DEPS}; do if echo ${from_path} | grep ".rebase/from/charts/${ignored_dep}" 1>/dev/null 2>/dev/null; then should_continue=1; break fi done [[ -n ${should_continue} ]] && continue chart_path=$(echo ${from_path} | sed "s:.rebase/from/::g") rm ${DEST_DIRECTORY}/${chart_path} 1>/dev/null 2>/dev/null || true done done if [ -d "${EXCLUDE_DIRECTORY}" ]; then # Move modified excluded files into excludes directory for d in ${EXCLUDE_DIRECTORIES}; do mkdir -p ${EXCLUDE_DIRECTORY}/${d} done for f in ${EXCLUDE_FILES}; do # the file might be removed in upstream if [ -f .rebase/from-copy/${f} ]; then mv .rebase/from-copy/${f} ${EXCLUDE_DIRECTORY}/${f} fi done unset EXCLUDE_DIRECTORIES unset EXCLUDE_FILES fi if [ -d "${CRD_CHART_DIRECTORY}" ]; then # Move CRDs back back into CRD chart since changes would have been tracked there git reset HEAD~1 1>/dev/null 2>/dev/null mv ${DEST_DIRECTORY}/crds ${CRD_CHART_DIRECTORY}/${CRDS_DIRECTORY} fi rm diff.patch rm -rf .rebase # Clean up empty directories from the merge find ${DEST_DIRECTORY} -type d -empty -delete 1>/dev/null 2>/dev/null echo "" echo "INTERACTIVE REBASE SHELL" echo "===" echo "" echo "The changes have been loaded into your working directory." echo "" echo "Please look through each file that was changed and remove .rej files using an editor of your choice." echo "" echo "Once you have manually resolved the .rej files or added any additonal necessary changes, do the following:" if [ -d "${CRD_CHART_DIRECTORY}" ]; then echo "1) Add all changes to ${DEST_DIRECTORY}, ${EXCLUDE_DIRECTORY}, and ${CRD_CHART_DIRECTORY} to staging (e.g. git add)" else echo "1) Add all changes to ${DEST_DIRECTORY} and ${EXCLUDE_DIRECTORY} to staging (e.g. git add)" fi echo "2) Commit all other changes to save them (e.g. 'git add ; git commit -m \"message\"')" echo "Note: Any additional commits you make will show up in your branch at the end of the rebase, so committing changes from 'make patch' could be helpful" echo "" if [ -d "${CRD_CHART_DIRECTORY}" ]; then echo "Once you have added all changes to ${DEST_DIRECTORY}, ${EXCLUDE_DIRECTORY} or ${CRD_CHART_DIRECTORY}, exit out of the shell to move to the next commit." else echo "Once you have added all changes to ${DEST_DIRECTORY} or ${EXCLUDE_DIRECTORY}, exit out of the shell to move to the next commit." fi echo "" echo "To force abort the rebase and discard your changes at any time, run 'abort'" set +e CURR_DIR=$(pwd) bash --rcfile <(echo "PS1='(interactive-rebase-shell) '; alias abort='touch .abort_rebase; exit'") -i set -e cd ${CURR_DIR} 1>/dev/null 2>/dev/null # Ensure additional commits do not contain changes to DEST_DIRECTORY, EXCLUDE_DIRECTORY, or CRD_CHART_DIRECTORY BAD_COMMITS=$(git log --first-parent --oneline ${GC_COMMIT}..HEAD -- ${DEST_DIRECTORY} ${EXCLUDE_DIRECTORY} ${CRD_CHART_DIRECTORY}) # Ensure that changes are only added or modified to DEST_DIRECTORY, EXCLUDE_DIRECTORY, or CRD_CHART_DIRECTORY BAD_CHANGES=$( git status --porcelain \ | grep -v "A ${DEST_DIRECTORY}" \ | grep -v "M ${DEST_DIRECTORY}" \ | grep -v "R ${DEST_DIRECTORY}" \ | grep -v "D ${DEST_DIRECTORY}" \ | grep -v "A ${EXCLUDE_DIRECTORY}" \ | grep -v "M ${EXCLUDE_DIRECTORY}" \ | grep -v "R ${EXCLUDE_DIRECTORY}" \ | grep -v "D ${EXCLUDE_DIRECTORY}" \ | grep -v "A ${CRD_CHART_DIRECTORY}" \ | grep -v "M ${CRD_CHART_DIRECTORY}" \ | grep -v "R ${CRD_CHART_DIRECTORY}" \ | grep -v "D ${CRD_CHART_DIRECTORY}" \ | tee ) set +e GIT_REJ_CHECK=$(git diff --staged --name-only | grep ".rej") if [[ -n ${GIT_REJ_CHECK} ]]; then GIT_REJ_CHECK_DIFF_ARGS=$(echo "${GIT_REJ_CHECK}" | tr ' ' '\n' | sed "s:\(.*\):\:!\1:g" | xargs) else unset GIT_REJ_CHECK_DIFF_ARGS fi GIT_DIFF_CHECK=$(git diff --staged --check -- ${GIT_REJ_CHECK_DIFF_ARGS} ${EXCLUDE_FILES_DIFF_ARGS} || true) set -e # Loop back through shell until the developer resolves all conflicts while [ -n "${BAD_COMMITS}" ] || [ -n "${BAD_CHANGES}" ] || [[ -n "${GIT_DIFF_CHECK}" ]]; do echo "" if [ -f .abort_rebase ]; then rm .abort_rebase echo "Detected ABORT_REBASE has been set. Exiting..." exit 1 fi if [ -n "${BAD_COMMITS}" ]; then echo "ERROR: Detected the following commits that violate rebase guidelines:" echo "${BAD_COMMITS}" echo "" fi if [ -n "${BAD_CHANGES}" ]; then echo "ERROR: Detected the following changes that violate rebase guidelines:" echo "${BAD_CHANGES}" echo "" fi if [ -n "${GIT_DIFF_CHECK}" ]; then echo "ERROR: Detected unresolved issues in git diff --staged --check:" echo "${GIT_DIFF_CHECK}" echo "" fi if [ -n "${GIT_REJ_CHECK}" ]; then echo "ERROR: Detected unresolved .rej files in staging:" echo "${GIT_REJ_CHECK}" echo "" fi echo "Only changes to ${DEST_DIRECTORY}, ${EXCLUDE_DIRECTORY}, or ${CRD_CHART_DIRECTORY} should be in staging. All other changes should be committed." echo "" echo "Please modify the commits and try again." echo "" echo "To force abort the rebase and discard your changes at any time, run 'abort'" set +e bash --rcfile <(echo "PS1='(interactive-rebase-shell) '; alias abort='touch .abort_rebase; exit'") -i set -e # Ensure additional commits do not contain changes to DEST_DIRECTORY, EXCLUDE_DIRECTORY, or CRD_CHART_DIRECTORY BAD_COMMITS=$(git log --first-parent --oneline ${GC_COMMIT}..HEAD -- ${DEST_DIRECTORY} ${EXCLUDE_DIRECTORY} ${CRD_CHART_DIRECTORY}) # Ensure that changes are only added or modified to DEST_DIRECTORY, EXCLUDE_DIRECTORY, or CRD_CHART_DIRECTORY BAD_CHANGES=$( git status --porcelain \ | grep -v "A ${DEST_DIRECTORY}" \ | grep -v "M ${DEST_DIRECTORY}" \ | grep -v "R ${DEST_DIRECTORY}" \ | grep -v "D ${DEST_DIRECTORY}" \ | grep -v "A ${EXCLUDE_DIRECTORY}" \ | grep -v "M ${EXCLUDE_DIRECTORY}" \ | grep -v "R ${EXCLUDE_DIRECTORY}" \ | grep -v "D ${EXCLUDE_DIRECTORY}" \ | grep -v "A ${CRD_CHART_DIRECTORY}" \ | grep -v "M ${CRD_CHART_DIRECTORY}" \ | grep -v "R ${CRD_CHART_DIRECTORY}" \ | grep -v "D ${CRD_CHART_DIRECTORY}" \ | tee ) set +e GIT_REJ_CHECK=$(git diff --staged --name-only | grep ".rej") if [[ -n ${GIT_REJ_CHECK} ]]; then GIT_REJ_CHECK_DIFF_ARGS=$(echo "${GIT_REJ_CHECK}" | tr ' ' '\n' | sed "s:\(.*\):\:!\1:g" | xargs) else unset GIT_REJ_CHECK_DIFF_ARGS fi GIT_DIFF_CHECK=$(git diff --staged --check -- ${GIT_REJ_CHECK_DIFF_ARGS} ${EXCLUDE_FILES_DIFF_ARGS} || true) set -e done # Merge current staged changes into the GC_COMMIT directly, ignoring any other commits added by the user # Then set the GC_COMMIT once more since the commit hash has changed NUM_USER_COMMITS=$(git rev-list --count ${GC_COMMIT}..HEAD) git commit --fixup "${GC_COMMIT}" 1>/dev/null 2>/dev/null && GIT_SEQUENCE_EDITOR=true git rebase --interactive --autosquash "${GC_COMMIT}^" 1>/dev/null 2>/dev/null GC_COMMIT=$(git rev-parse --short HEAD~${NUM_USER_COMMITS}) # Run make patch and save all changes that were added to the working directory throughout the rebase rm ${CHARTS_ORIGINAL_DIRECTORY} 1>/dev/null 2>/dev/null || true PACKAGE=${PACKAGE} USE_CACHE=${USE_CACHE} make patch 1>/dev/null 2>/dev/null git add ${GC_DIRECTORY} git commit --allow-empty -m "Rebase ${PACKAGE} to ${TO_COMMIT}" 1>/dev/null 2>/dev/null # Ensure Git is clean if [ -n "$(git status --porcelain)" ]; then echo "Found unexpected changes after generating final patch post-rebase" git status --porcelain exit 1 fi # Cherry pick all changes to the current branch COMMITS=$(git log --first-parent --oneline ${GC_COMMIT}..HEAD | cut -d' ' -f1 | tail -r) echo "" echo "Applying the following commits to your current branch:" echo "" echo "$(git log --first-parent --oneline ${GC_COMMIT}..HEAD)" git checkout ${CURRENT_BRANCH} 1>/dev/null 2>/dev/null for commit in ${COMMITS}; do git cherry-pick --allow-empty -m 1 ${commit} 1>/dev/null 2>/dev/null || git commit --allow-empty 1>/dev/null 2>/dev/null done echo "" echo ">> Modifying the package.yaml and finishing up rebase..." PACKAGE=${PACKAGE} USE_CACHE=${USE_CACHE} make prepare 1>/dev/null 2>/dev/null yq e -i ".url = \"${TO_REMOTE}\"" ${PACKAGE_YAML_PATH} 1>/dev/null 2>/dev/null yq e -i ".commit = \"${TO_COMMIT}\"" ${PACKAGE_YAML_PATH} 1>/dev/null 2>/dev/null if [[ ${TO_DIR} != "." ]]; then yq e -i ".subdirectory = \"${TO_DIR}\"" ${PACKAGE_YAML_PATH} 1>/dev/null 2>/dev/null fi PACKAGE=${PACKAGE} USE_CACHE=${USE_CACHE} make patch 1>/dev/null 2>/dev/null PACKAGE=${PACKAGE} USE_CACHE=${USE_CACHE} make clean 1>/dev/null 2>/dev/null git add ${GC_DIRECTORY} ${PACKAGE_YAML_PATH} git commit --allow-empty -m "Update ${PACKAGE} to new base ${TO_COMMIT}" 1>/dev/null 2>/dev/null echo "" echo "Hooray! The rebase is complete."