diff options
Diffstat (limited to 'archive/lib-src/mr/stow')
-rw-r--r-- | archive/lib-src/mr/stow | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/archive/lib-src/mr/stow b/archive/lib-src/mr/stow new file mode 100644 index 00000000..254bd0c3 --- /dev/null +++ b/archive/lib-src/mr/stow @@ -0,0 +1,273 @@ +# Plug-in to use GNU Stow to manage symlinks whose targets lie in a +# repository managed with myrepos +# +# The standard use case is for managing dotfiles inside one's home +# directory. +# +# Original author (2011): +# Adam Spiers <mr@adamspiers.org> +# +# This version reworked (2016, 2017) & maintained (2017) by: +# Sean Whitton <spwhitton@spwhitton.name> + +# BASIC USAGE INSTRUCTIONS +# +# To make mr use this file, add a line like this inside the [DEFAULT] +# section of your ~/.mrconfig: +# +# include = cat /usr/share/mr/stow +# +# and then inside each [repo] section of your ~/.mrconfig for +# which you want the contents to be stowed, add this line: +# +# stowable = true +# +# You must have at least version 2.1.0 of stow available. [1] +# +# If stow is not in your $PATH, you can export STOW_COMMAND to tell +# this plug-in where it is. +# +# The default behaviour is to stow on checkout, and restow on update. +# The manual actions 'stow', 'restow', 'unstow' and 'adopt' are also +# available. +# +# By default, ~/.STOW is used as the stow directory, and ~ as the +# target directory. You can export STOW_DIR and STOW_TARGET to +# override these defaults. +# +# DEALING WITH APPLICATIONS THAT MISTREAT SYMLINKS +# +# Some programs will replace a symlink to a stowed file with a regular +# copy of the file, and a subset of these will do this even if they +# haven't edited the file. This will cause stow operations to fail. +# +# To deal with this, run 'mr adopt'. This will move the modified file +# into your repository, and restore the usual symlink. Then you can +# use your VCS tools ('git diff', 'hg diff') to decide whether you +# want to keep the changes. +# +# FOLDING +# +# By default, this library passes --no-folding to stow. This allows +# you to have more than one repository stowing files into a single +# subdirectory in your home directory. For example, you might have a +# private and a public repository both stowing into ~/.gnupg. If you +# don't want this behaviour, set MR_FOLD. For example, in a +# repository's myrepos config section or in [DEFAULT]: +# +# lib = MR_FOLD= +# +# FIXUPS THAT CREATE FILES TO BE STOWED +# +# Stowing is automatically performed via post_checkout, and restowing +# via post_update, as can be seen from below (search for 'Automatic +# actions'). Note that these run before fixups, which allows fixups +# to refer to stowed files, but isn't ideal if the fixups are +# responsible for creating the stow package's installation image, +# e.g. via a typical './configure && make install' sequence. Here's a +# suggested mrconfig chunk to handle this particular use case: +# +# stowable = true +# lib = +# STOW_PKG_TYPE=directory +# STOW_NO_AUTOMATIC_ACTIONS=yes +# mr_pre_unstow () { +# install-info --delete --info-dir=$HOME/share/info $STOW_PKG_PATH/share/info/*.info +# } +# mr_post_stow () { +# install-info --info-dir=$HOME/share/info $STOW_PKG_PATH/share/info/*.info +# } +# fixups = +# if ! [ -e configure ]; then +# bash ./autogen.sh +# fi +# set_stow_common_opts +# ./configure --prefix=$STOW_PKG_PATH +# make install prefix=$STOW_PKG_PATH +# rm $STOW_PKG_PATH/share/info/dir +# mr_restow_regardless +# +# [1] Older versions could create a frankenstein ~/.git/ directory +# containing symlinks to multiple .git/ sub-directories in different +# stow packages! 2.1.0 onwards does not have this problem - it +# supports local per-directory .stow-local-ignore and global +# ~/.stow-global-ignore files, and even without configuration of +# these, it chooses sensible default ignore lists which prevent +# stowing of a package's .git/ sub-directory. These ignore lists are +# also ideal if you only want to stow a subset of a stow package's +# contents. + +lib = + : ${STOW_DIR:=$HOME/.STOW} + : ${STOW_TARGET:=$HOME} + STOW_NAME=$(echo "$MR_REPO" | tr / _) + # + if ! [ -d "$STOW_TARGET" ]; then mkdir -p "$STOW_TARGET"; fi + if ! [ -d "$STOW_DIR" ]; then mkdir -p "$STOW_DIR" ; fi + if ! [ -f "$STOW_DIR/.stow" ]; then touch "$STOW_DIR/.stow"; fi + # + #MR_STOWABLE=no + is_stowable () { + [ -z "$MR_DISABLE_STOW" ] && + ( cd "$MR_REPO" && mr stowable >/dev/null 2>&1 ) + #[ "$MR_STOWABLE" = yes ] + } + stowable_then_continue () { + if is_stowable; then + return 0 + else + if [ -n "$1" ]; then + info "$STOW_NAME isn't stowable; skipping $MR_ACTION" + fi + return 1 + fi + } + # + set_stow_common_opts () { + : ${STOW_PKG_TYPE:=symlink} + STOW_PKG_PATH="$STOW_DIR/$STOW_NAME" + # canonicalise -t and -d params with readlink if available + # stow can fail if they aren't canonical + if which readlink >/dev/null 2>&1; then + stow_common_opts="-t $(readlink -f $STOW_TARGET) -d $(readlink -f $STOW_DIR)" + else + stow_common_opts="-t $STOW_TARGET -d $STOW_DIR" + fi + STOW="${STOW_COMMAND:-$HOME/src/dotfiles/lib-src/stow/stow}" + case "`$STOW --version`" in + 'version 1.*') + stow_common_opts="$stow_common_opts -p" + ;; + *) + ;; + esac + if [ -n "$MR_STOW_OPTIONS" ]; then + stow_common_opts="$stow_common_opts $MR_STOW_OPTIONS" + fi + if [ -n "$MR_STOW_OVER" ]; then + stow_common_opts="$stow_common_opts --override=$MR_STOW_OVER" + fi + if ! (: "${MR_FOLD?}") 2>/dev/null; then + stow_common_opts="$stow_common_opts --no-folding" + fi + } + # + mr_stow () { + stowable_then_continue || return 0 + set_stow_common_opts + ensure_package_exists + command "$STOW" $stow_common_opts "$@" "$STOW_NAME" + mr_post_stow + info "Stowed $STOW_NAME" + } + mr_restow_if_already_stowed () { + stowable_then_continue || return 0 + if ! [ -L "$STOW_PKG_PATH" ]; then + info "$MR_REPO wasn't stowed yet; won't restow." + return + fi + mr_restow_regardless "$@" + } + mr_restow_regardless () { + stowable_then_continue || return 0 + set_stow_common_opts + ensure_package_exists + mr_pre_unstow + command "$STOW" -R $stow_common_opts "$@" "$STOW_NAME" + mr_post_stow + info "Restowed $STOW_NAME" + } + mr_pre_unstow () { + : # This can be "overridden" by the lib section of a repo definition + #info "no mr_pre_unstow hook" + } + mr_post_stow () { + : # This can be "overridden" by the lib section of a repo definition + #info "no mr_post_stow hook" + } + mr_unstow () { + stowable_then_continue || return 0 + set_stow_common_opts + if ! [ -d "$STOW_PKG_PATH" ]; then + info "$MR_REPO wasn't stowed yet in $STOW_PKG_PATH; can't unstow." + return + fi + mr_pre_unstow + command "$STOW" -D $stow_common_opts "$@" "$STOW_NAME" + if [ "$STOW_PKG_TYPE" = 'symlink' ]; then + rm -f "$STOW_PKG_PATH" + fi + info "Unstowed $STOW_NAME" + } + # + ensure_symlink_exists () { + [ $# = 2 ] || error "CONFIG BUG: Usage: ensure_symlink_exists SYMLINK TARGET" + symlink="$1" + required_target="$2" + if [ -L "$symlink" ]; then + actual_target="`readlink $symlink`" + if [ "$actual_target" = "$required_target" ]; then + return + else + error "Symlink $symlink already points to $actual_target, cannot point to $required_target; aborting." + fi + fi + if [ -e "$symlink" ]; then + error "Cannot create symlink $symlink - already exists; aborting." + fi + ln -s "$required_target" "$symlink" + } + # + mr_adopt () { + stowable_then_continue || return 0 + set_stow_common_opts + ensure_package_exists + mr_pre_unstow + command "$STOW" --adopt $stow_common_opts "$@" "$STOW_NAME" + mr_post_stow + info "Stowed $STOW_NAME with adoption" + } + # + ensure_package_exists () { + case "$STOW_PKG_TYPE" in + symlink) + ensure_symlink_exists "$STOW_PKG_PATH" "$MR_REPO" + ;; + directory) + [ -e "$STOW_PKG_PATH" ] || mkdir "$STOW_PKG_PATH" + [ -d "$STOW_PKG_PATH" ] || error "Expected $STOW_PKG_PATH to be a directory; aborting." + if [ -L "$STOW_PKG_PATH" ]; then + error "Didn't expect $STOW_PKG_PATH to be a symlink; aborting." + fi + ;; + *) + error "Unrecognised value '$STOW_PKG_TYPE' for \$STOW_PKG_TYPE; aborting." + ;; + esac + } + +#stowable = is_stowable +stowable = false +showstowable = + if is_stowable; then + echo "$STOW_NAME is stowable" + else + echo "$STOW_NAME is not stowable" + fi + +# Automatic actions +post_checkout_append = [ -n "$STOW_NO_AUTOMATIC_ACTIONS" ] || mr_stow +#post_update_append = mr_restow_if_already_stowed +post_update_append = [ -n "$STOW_NO_AUTOMATIC_ACTIONS" ] || mr_restow_regardless + +# Manual actions +stow = mr_stow "$@" +stowover = MR_STOW_OVER=. mr_stow "$@" +unstow = mr_unstow "$@" +restow = mr_restow_regardless "$@" +restowover = MR_STOW_OVER=. mr_restow_regardless "$@" +adopt = mr_adopt "$@" + +# Local variables: +# mode: sh +# End: |