# Dotfiles management script. For details, run
# $ dotfiles.sh --help
#
-# Copyright (C) 2011-2012 W. Trevor King <wking@tremily.us>
+# Copyright (C) 2011-2015 W. Trevor King <wking@tremily.us>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-VERSION='0.2'
+VERSION='0.4'
DOTFILES_DIR="${PWD}"
TARGET=~
CHECK_WGET_TYPE_AND_ENCODING='no'
#####
# External utilities
-DIFF=$(which diff)
-GIT=$(which git)
-LN=$(which ln)
-MV=$(which mv)
-PATCH=$(which patch)
-SED=$(which sed)
-RM=$(which rm)
-RSYNC=$(which rsync)
-TAR=$(which tar)
-TOUCH=$(which touch)
-WGET=$(which wget)
+DIFF=${DOTFILES_DIFF:-$(command -v diff)}
+GIT=${DOTFILES_GIT:-$(command -v git)}
+LN=${DOTFILES_LN:-$(command -v ln)}
+MV=${DOTFILES_MV:-$(command -v mv)}
+PATCH=${DOTFILES_PATCH:-$(command -v patch)}
+SED=${DOTFILES_SED:-$(command -v sed)}
+RM=${DOTFILES_RM:-$(command -v rm)}
+RSYNC=${DOTFILES_RSYNC:-$(command -v rsync)}
+TAR=${DOTFILES_TAR:-$(command -v tar)}
+TOUCH=${DOTFILES_TOUCH:-$(command -v touch)}
+WGET=${DOTFILES_WGET:-$(command -v wget)}
#####
# Compatibility checks
COMMAND="${1}"
shift
if [ -z "${REPO}" ]; then # run on all repositories
- for REPO in *; do
+ for REPO in *; do
if [ "${REPO}" = '*' ]; then
break # no known repositories
elif [ -f "${REPO}" ]; then
done < <(cd "${DIR}" && find .)
}
-# Global variable to allow passing associative arrats between functions
+# Global variable to allow passing associative arrays between functions
if [ "${BASH_MAJOR}" -ge 4 ]; then
declare -A REPO_SOURCE_DATA
REPO_SOURCE_DATA['repo']="${REPO}"
if [ -d "${REPO}/.git" ]; then
REPO_SOURCE_DATA['transfer']='git'
+ REPO_SOURCE_DATA['url']=$(
+ git --git-dir "${REPO}/.git/" config remote.origin.url)
else
echo "ERROR: no source location found for ${REPO}" >&2
return 1
fi
}
+###
+# list command
+
+COMMANDS+=('list')
+
+function list_help()
+{
+ echo 'List current dotfiles repositories.'
+ if [ "${1}" = '--one-line' ]; then return; fi
+
+ cat <<-EOF
+
+ usage: $0 ${COMMAND} [REPO]
+
+ List information for 'REPO' in a form simular to the 'clone'
+ command's arguments. If 'REPO' is not give, all repositories will
+ be listed. Examples:
+
+ $0 list public
+ public wget http://example.com/public-dotfiles.tar.gz
+ $0 list
+ public wget http://example.com/public-dotfiles.tar.gz
+ private git ssh://example.com/~/private-dotfiles.git
+ EOF
+}
+
+function list()
+{
+ # multi-repo case handled in main() by run_on_all_repos()
+ REPO=$(nonempty_option 'list' 'REPO' "${1}") || return 1
+ maxargs 'list' 1 "${@}" || return 1
+ if [ "${BASH_MAJOR}" -ge 4 ]; then
+ get_repo_source "${REPO}" || return 1
+ TRANSFER=$(nonempty_option 'list' 'TRANSFER' "${REPO_SOURCE_DATA['transfer']}") || return 1
+ URL=$(nonempty_option 'list' 'URL' "${REPO_SOURCE_DATA['url']}") || return 1
+ else
+ echo "WARNING: Bash version < 4.0, cannot use assuming all repos use git transfer" >&2
+ TRANSFER='git'
+ URL=$(git --git-dir "${REPO}/.git/" config remote.origin.url)
+ fi
+ echo "${REPO} ${TRANSFER} ${URL}"
+}
+
###
# fetch command
}
###
-# fetch command
+# diff command
COMMANDS+=('diff')
'--removed')
MODE='removed'
;;
- '--local-patch')
+ '--local-patch')
MODE='local-patch'
;;
*)
if [ "${FORCE_LINK}" = 'no' ]; then
# don't prompt about --force-link, because this will happen a lot
continue # already simlinked
+ elif [ ! -h "${TARGET}/${FILE}" ]; then
+ # target file/dir underneath an already symlinked dir
+ continue
else
# don't backup links that already point to the right place
BACKUP='no'
function disconnect()
{
# multi-repo case handled in main() by run_on_all_repos()
- REPO=$(nonempty_option 'link' 'REPO' "${1}") || return 1
+ REPO=$(nonempty_option 'disconnect' 'REPO' "${1}") || return 1
maxargs 'disconnect' 1 "${@}" || return 1
DOTFILES_SRC="${DOTFILES_DIR}/${REPO}/patched-src"
cat <<-EOF
- usage: $0 ${COMMAND} [REPO]
+ usage: $0 ${COMMAND} [options] [REPO]
Where 'REPO' is the name the dotfiles repository to update.
If it is not given, all repositories will be updateed.
to bring them in sync with the central repositories. Keeps track
of the last update time to avoid multiple fetches in the same
week.
+
+ ${COMMAND} passes any options it receives through to the link
+ command.
EOF
}
function update()
{
+ LINK_OPTS=''
+ while [ "${1::2}" = '--' ]; do
+ LINK_OPTS="${LINK_FN_OPTS} ${1}"
+ shift
+ done
# multi-repo case handled in main() by run_on_all_repos()
- REPO=$(nonempty_option 'link' 'REPO' "${1}") || return 1
+ REPO=$(nonempty_option 'update' 'REPO' "${1}") || return 1
maxargs 'disconnect' 1 "${@}" || return 1
# Update once a week from our remote repository. Mark updates by
"${TOUCH}" "${UPDATE_FILE}" || return 1
fetch "${REPO}" || return 1
patch "${REPO}" || return 1
- link "${REPO}" || return 1
+ link ${LINK_OPTS} "${REPO}" || return 1
echo "${REPO} dotfiles updated"
fi
}
echo "${VERSION}"
return
;;
- '--dotfiles-dir')
- DOTFILES_DIR="${2}"
+ '--dotfiles-dir')
+ DOTFILES_DIR="${2}"
shift
;;
'--target')
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
+ "${COMMAND}" "${OPTIONS[@]}" "${1}" || return 1
fi
fi
}