TOUCH=$(which touch)
WGET=$(which wget)
+#####
+# Compatibility checks
+
+BASH="${BASH_VERSION%.*}"
+BASH_MAJOR="${BASH%.*}"
+BASH_MINOR="${BASH#*.}"
+
+if [ "${BASH_MAJOR}" -eq 3 ] && [ "${BASH_MINOR}" -eq 0 ]; then
+ echo "ERROR: ${0} requires Bash version >= 3.1" >&2
+ echo "you're running ${BASH}, which doesn't support += array assignment" >&2
+ exit 1
+fi
+
#####
# Utility functions
function run_on_all_repos()
{
COMMAND="${1}"
+ shift
if [ -z "${REPO}" ]; then # run on all repositories
for REPO in *; do
if [ "${REPO}" = '*' ]; then
break # no known repositories
+ elif [ -f "${REPO}" ]; then
+ continue # repositories are directories
fi
- "${COMMAND}" "${REPO}" || return 1
+ "${COMMAND}" "${@}" "${REPO}" || return 1
done
return
fi
}
# Global variable to allow passing associative arrats between functions
-declare -A REPO_SOURCE_DATA
+
+if [ "${BASH_MAJOR}" -ge 4 ]; then
+ declare -A REPO_SOURCE_DATA
+fi
function set_repo_source()
{
+ if [ "${BASH_MAJOR}" -lt 4 ]; then
+ echo "ERROR: ${0}'s set_repo_source requires Bash version >= 4.0" >&2
+ echo "you're running ${BASH}, which doesn't support associative arrays" >&2
+ return 1
+ fi
REPO=$(nonempty_option 'set_repo_source' 'REPO' "${1}") || return 1
> "${REPO}/source_cache" || return 1
for KEY in "${!REPO_SOURCE_DATA[@]}"; do
# usage: get_repo_source REPO
function get_repo_source()
{
+ if [ "${BASH_MAJOR}" -lt 4 ]; then
+ echo "ERROR: ${0}'s get_repo_source() requires Bash version >= 4.0" >&2
+ echo "you're running ${BASH}, which doesn't support associative arrays" >&2
+ return 1
+ fi
REPO=$(nonempty_option 'get_repo_source' 'REPO' "${1}") || return 1
REPO_SOURCE_DATA=()
if [ -f "${REPO}/source_cache" ]; then
fi
}
+function git_fetch()
+{
+ REPO=$(nonempty_option 'git_fetch' 'REPO' "${1}") || return 1
+ REMOTES=$(cd "${REPO}" && "${GIT}" remote) || return 1
+ if [ -n "${REMOTES}" ]; then
+ (cd "${REPO}" && "${GIT}" pull) || return 1
+ else
+ echo "no remote repositories found for ${REPO}"
+ fi
+}
+
function wget_fetch()
{
REPO=$(nonempty_option 'wget_fetch' 'REPO' "${1}") || return 1
if [ -n "${SERVER_ETAG}" ]; then # store new ETag
REPO_SOURCE_DATA['etag']="${SERVER_ETAG}"
set_repo_source "${REPO}" || return 1
- else
- if [ -n "${ETAG}" ]; then # clear old ETag
- unset "${REPO_SOURCE_DATA['etag']}"
- set_repo_source "${REPO}" || return 1
- fi
+ elif [ -n "${ETAG}" ]; then # clear old ETag
+ unset "${REPO_SOURCE_DATA['etag']}"
+ set_repo_source "${REPO}" || return 1
fi
echo "extracting ${BUNDLE} to ${REPO}"
"${TAR}" -xf "${BUNDLE}" -C "${REPO}" --strip-components 1 --overwrite || return 1
fi
}
+
# usage: link_file REPO FILE
#
# Create the symbolic link to the version of FILE in the REPO
echo "ERROR: destination path (${REPO}) already exists." >&2
return 1
fi
- mkdir -p "${REPO}"
CACHE_SOURCE='yes'
FETCH='yes'
case "${TRANSFER}" in
"${GIT}" clone "${URL}" "${REPO}" || return 1
;;
'wget')
+ mkdir -p "${REPO}"
;;
*)
echo "PROGRAMMING ERROR: add ${TRANSFER} support to clone command" >&2
# multi-repo case handled in main() by run_on_all_repos()
REPO=$(nonempty_option 'fetch' 'REPO' "${1}") || return 1
maxargs 'fetch' 1 "${@}" || return 1
- get_repo_source "${REPO}" || return 1
- TRANSFER=$(nonempty_option 'fetch' 'TRANSFER' "${REPO_SOURCE_DATA['transfer']}") || return 1
+ if [ "${BASH_MAJOR}" -ge 4 ]; then
+ get_repo_source "${REPO}" || return 1
+ TRANSFER=$(nonempty_option 'fetch' 'TRANSFER' "${REPO_SOURCE_DATA['transfer']}") || return 1
+ else
+ echo "WARNING: Bash version < 4.0, assuming all repos use git transfer" >&2
+ TRANSFER='git'
+ fi
if [ "${TRANSFER}" = 'git' ]; then
- "${GIT}" --git-dir "${REPO}/.git" pull || return 1
+ git_fetch "${REPO}" || return 1
elif [ "${TRANSFER}" = 'wget' ]; then
wget_fetch "${REPO}" || return 1
else
{
MODE='standard'
while [ "${1::2}" = '--' ]; do
- case "${1}" in
- '--removed')
- MODE='removed'
- ;;
- '--local-patch')
- MODE='local-patch'
- ;;
- *)
- echo "ERROR: invalid option to diff (${1})" >&2
- return 1
- esac
+ case "${1}" in
+ '--removed')
+ MODE='removed'
+ ;;
+ '--local-patch')
+ MODE='local-patch'
+ ;;
+ *)
+ echo "ERROR: invalid option to diff (${1})" >&2
+ return 1
+ esac
shift
done
# multi-repo case handled in main() by run_on_all_repos()
exec 3<&1 # save stdout to file descriptor 3
echo "save local removed to ${REPO}/local-patch/000-local.remove"
exec 1>"${REPO}/local-patch/000-local.remove" # redirect stdout
- diff "${REPO}" --removed
+ diff --removed "${REPO}"
exec 1<&3 # restore old stdout
exec 3<&- # close temporary fd 3
return
if [ ! -e "${TARGET}/${FILE}" ]; then
echo "${FILE}"
fi
- else
- if [ -f "${TARGET}/${FILE}" ]; then
- (cd "${REPO}/src" && "${DIFF}" -u "${FILE}" "${TARGET}/${FILE}")
- fi
+ elif [ -f "${TARGET}/${FILE}" ]; then
+ (cd "${REPO}/src" && "${DIFF}" -u "${FILE}" "${TARGET}/${FILE}")
fi
done <<-EOF
$(list_files "${REPO}/src")
if [ -f "${FILE}" ]; then
echo "apply ${FILE}"
pushd "${REPO}/patched-src/" > /dev/null || return 1
- "${PATCH}" -p0 < "../../${FILE}" || return 1
+ "${PATCH}" -p1 < "../../${FILE}" || return 1
popd > /dev/null || return 1
fi
done
# remove any files marked for removal in local-patch
for REMOVE in "${REPO}/local-patch"/*.remove; do
if [ -f "${REMOVE}" ]; then
+ echo "apply ${FILE}"
while read LINE; do
if [ -z "${LINE}" ] || [ "${LINE:0:1}" = '#' ]; then
continue # ignore blank lines and comments
DRY_RUN='no' # If 'yes', disable any actions that change the filesystem
BACKUP='yes'
while [ "${1::2}" = '--' ]; do
- case "${1}" in
- '--force')
- FORCE='yes'
- ;;
- '--force-file')
- FORCE='file'
- ;;
- '--dry-run')
- DRY_RUN='yes'
- ;;
- '--no-backup')
- BACKUP='no'
- ;;
- *)
- echo "ERROR: invalid option to link (${1})" >&2
- return 1
- esac
+ case "${1}" in
+ '--force')
+ FORCE='yes'
+ ;;
+ '--force-file')
+ FORCE='file'
+ ;;
+ '--dry-run')
+ DRY_RUN='yes'
+ ;;
+ '--no-backup')
+ BACKUP='no'
+ ;;
+ *)
+ echo "ERROR: invalid option to link (${1})" >&2
+ return 1
+ esac
shift
done
# multi-repo case handled in main() by run_on_all_repos()
{
COMMAND=''
while [ "${1::2}" = '--' ]; do
- case "${1}" in
- '--help')
- main_help || return 1
- return
- ;;
- '--version')
- echo "${VERSION}"
- return
- ;;
- '--dotfiles-dir')
- DOTFILES_DIR="${2}"
- shift
- ;;
- '--target')
- TARGET="${2}"
- shift
- ;;
- *)
- echo "ERROR: invalid option to ${0} (${1})" >&2
- return 1
- esac
+ case "${1}" in
+ '--help')
+ main_help || return 1
+ return
+ ;;
+ '--version')
+ echo "${VERSION}"
+ return
+ ;;
+ '--dotfiles-dir')
+ DOTFILES_DIR="${2}"
+ shift
+ ;;
+ '--target')
+ TARGET="${2}"
+ shift
+ ;;
+ *)
+ echo "ERROR: invalid option to ${0} (${1})" >&2
+ return 1
+ esac
shift
done
COMMAND=$(get_selection "${1}" "${COMMANDS[@]}") || return 1
shift
done
if [ "${#}" -eq 0 ]; then
- run_on_all_repos "${COMMAND}" "$OPTIONS[@]" || return 1
+ run_on_all_repos "${COMMAND}" "${OPTIONS[@]}" || return 1
else
maxargs "${0}" 1 "${@}" || return 1
"${COMMAND}" "${OPTIONS[@]}" "${1}" || return 1