See DotEmacsDistributions
#!/bin/sh
# Simplifies running Emacs from an alternative $HOME in order to
# experiment with alternative configurations without affecting your
# default (~/.emacs.d) config.
#
# Run "emacs-distribution --help" for usage details.
#
# n.b. This script is essentially just a wrapper for the command:
# env HOME=$HOME/emacs/distributions/NAME emacs
# Version 1.01
# Author: Phil S.
# URL: http://www.emacswiki.org/emacs/emacs-distribution
# Dependencies:
# Requires the enhanced (non-POSIX) GNU getopt utility.
# Commentary:
# For lack of a better term, I am referring to these independent
# configurations as Emacs "distributions".
#
# Distributions will live under ~/emacs/distributions/
# Each distribution will serve as an alternative $HOME directory, and
# therefore the actual Emacs configuration will typically live in a
# .emacs.d sub-directory of that distribution.
#
# Distributions shouldn't interact with each other, so you can run
# them together and have multiple side-by-side emacs instances, each
# using a different configuration.
# TO DO:
# Rename to emacs-sandbox and ~/.emacs.d.sandboxes/
# Allow cloning of a single init file (all supported formats).
# Post-processing of repositories to support other directory structures.
# Support archive formats?
# (or a generic "fetch a thing" command to cover files/archives/URLs/repos/etc?)
# Running a distribution:
# -----------------------
# To run an Emacs distribution (whether an existing distribution
# OR a new blank-slate distribution), simply use:
# $ emacs-distribution NAME
#
# If NAME is a new distribution, the necessary directories and init
# file will be created first before running Emacs. As a convenience,
# the new init.el file will also be visited the first time a new
# distribution is started, so that you can immediately make any desired
# changes. On subsequent starts, this file is not automatically visited.
#
# Once running, you may wish to install new packages using the package
# manager (M-x package-install), or follow the installation directions
# for any other libraries you are interested in -- remembering to
# substitute the path "~/emacs/distributions/NAME/" for "~/" if placing
# files under your home directory from outside of the running Emacs
# instance (inside that instance "~/" will be the distribution's home).
# Pre-packaged distributions, and --clone:
# ----------------------------------------
# Some configurations such as Emacs Prelude or Emacs Live come ready-
# made with an init.el file, and need only be installed to the
# appropriate .emacs.d directory. If the repository (or otherwise)
# represents the .emacs.d directory, then you can clone it directly:
#
# $ emacs-distribution prelude --clone="https://github.com/bbatsov/prelude.git"
# $ emacs-distribution emacs-live --clone="https://github.com/overtone/emacs-live.git"
#
# In other scenarios, you can manually copy the appropriate directory as
# ~/emacs/distributions/NAME/.emacs.d and then run the distribution with:
# $ emacs-distribution NAME
#
# Distributions can also be cloned from the filesystem.
# The uses of --clone are as follows:
#
# $ emacs-distribution --clone NAME
# Creates a copy of ~/.emacs.d as NAME.
#
# $ emacs-distribution --clone=DIST NAME
# Creates a copy of existing distribution DIST as NAME.
#
# $ emacs-distribution --clone=PATH/TO/DIR NAME
# Creates a copy of existing directory DIR as NAME.
# DIR will be copied as the .emacs.d directory, unless it
# contains a .emacs.d directory.
#
# $ emacs-distribution --clone=REPO NAME
# Clones version-control repository REPO as NAME.
# (Only git repositories are supported at this time.)
# REPO is assumed to represent the .emacs.d directory.
# If it does not, you will need to manually modify the
# distribution directory.
# Default command-line options and per-distribution Emacs versions:
# -----------------------------------------------------------------
# To use a specific version of Emacs for a distribution, either
# specify the emacs command to run using the --emacs="COMMAND" option,
# or place a file named "emacs-command" in the distribution directory
# containing the command to run. If this file exists, it will be used
# automatically (unless overridden by --emacs="COMMAND").
#
# e.g.:
# $ emacs-distribution --emacs=emacs-22.3 my-emacs-22-config
# $ echo "emacs-22.3" >~/emacs/distributions/my-emacs-22-config/emacs-command
# $ emacs-distribution my-emacs-22-config
#
# Note that you may specify emacs command-line options as part of the
# emacs command with either of these techniques. Therefore if you wish
# to use certain options by default for a given distribution, the
# simplest way is to specify them in an "emacs-command" file.
#
# To pass options to emacs from the command line without specifying an
# emacs command, simply use the -- argument first. All subsequent
# arguments will be passed to Emacs. e.g.:
# $ emacs-distribution foo -- --debug-init --title="Foo distribution"
# Upgrading Emacs:
# ----------------
# This script facilitates a fairly useful approach for upgrading to
# a new version of Emacs (provided you are able to install the new
# version alongside the old).
#
# Simply set up a copy of your existing config as a new distribution
# to use with the new Emacs, until you're convinced everything is
# working. This will enable you to debug and edit the new config
# without the risk of breaking your existing one.
#
# Or you could flip this approach around, and instead make the
# original config into a distribution, in case you need it as a
# back-up.
# Notes on relocating $HOME:
# --------------------------
# As already indicated, this script works simply by setting an
# alternative $HOME directory for Emacs to see, which ensures that
# the 'distribution' is separate from your default configuration.
#
# See C-h i g (emacs) Find Init RET
#
# The down-side to this technique is that Emacs won't see all your
# other dot files (because it will be looking in $HOME), and so
# running other processes from within Emacs won't necessarily work as
# normal
# experimenting, and you can always symlink or copy the bits you need.
#
# The benefit is that if anything in the distribution writes files to
# the home directory, they can't clobber your real files, or add any
# unwanted cruft.
distrodir=${HOME}/emacs/distributions
# Process command line options
opts=$(getopt -o hd:e: -l help,distro:,emacs:,clone::,source-type:,delete -n "$(basename $0)" -- "$@")
if [ $? -eq 0 ]
eval set -- ${opts}
while true
case "$1" in
( -h|--help ) help=1
( -d|--distro ) {
shift
fakehome=$(readlink -f "${path}")
distro=$(basename $1)
}
( -e|--emacs ) shift
( -c|--clone ) clone=1
( -t|--source-type ) shift
( --delete ) delete=1
( -- ) shift
esac
done
else
help=1 # getopt returned (and reported) an error.
fi
# Process remaining (non-option) arguments
if [ -z "${fakehome}" ]
if [ -n "$1" ]
distro=$1
fakehome="${distrodir}/${distro}"
shift
else
help=1
fi
fi
if [ -n "${help}" ]
echo
echo "Usage:"
echo "$(basename $0) [ --emacs=COMMAND ] [ --clone[=SOURCE] ]"
echo " [ --source-type=TYPE ] [ NAME | --distro=PATH/TO/DIR/NAME ]"
echo " [ -- [ EMACS-OPTIONS ... ] ]"
echo "$(basename $0) [ -e COMMAND ] [ -c [SOURCE] ] [ -t TYPE ]"
echo " [ NAME | -d PATH/TO/DIR/NAME ] [ -- [ EMACS-OPTIONS ... ] ]"
echo "$(basename $0) --delete NAME"
echo
echo "Uses ${distrodir}/NAME unless --distro is specified."
echo
echo "Arguments following -- will be passed to Emacs. e.g.:"
echo "$(basename $0) mydistro -- --title='mydistro' --debug-init"
echo
echo "--emacs=\"COMMAND\" specifies a non-default emacs command."
echo "Command-line options may also be included. To automate this for"
echo "a given distribution, place the command to run in the file:"
echo "${distrodir}/NAME/emacs-command"
echo
echo "The --clone option will initialise a new distribution from an"
echo "existing source (~/.emacs.d by default, or a named distribution,"
echo "or a named directory, or a version control repository. It is"
echo "assumed that the repository represents the .emacs.d directory."
echo
echo "The --source-type option indicates the version-control system"
echo "for the repository, if it cannot be ascertained automatically"
echo "from SOURCE. Valid options are: bzr, cvs, darcs, git, hg, svn."
echo
echo "The --delete option will remove (rm -rf) the directory of the"
echo "specified (NAME) distribution."
echo
exit
fi
# Deleting a distribution.
if [ -n "${delete}" ]
if [ ! -d "${fakehome}" ]
echo "No distribution at ${fakehome}"
exit 1
fi
read -p "rm -rf \"${fakehome}\" (Yes/[No]) ? " delete
if [ "${delete}" = "yes" ] || [ "${delete}" = "Yes" ]
rm -rf "${fakehome}"
if [ $? -eq 0 ]
echo "Deleted distribution ${distro}"
deleted=1
fi
fi
if [ -z "${deleted}" ]
echo "Exiting."
fi
exit # Exit here, regardless.
fi
# Create the distribution.
emacsd="${fakehome}/.emacs.d"
if [ ! -d "${emacsd}" ]
# Are we cloning an existing source?
if [ -n "${clone}" ]
# Determine source type.
if [ -z "${vcs}" ]
if [ -z "${source}" ]
source="${HOME}/.emacs.d"
elif [ -d "${distrodir}/${source}" ]
source="${distrodir}/${source}"
elif [ "${source%.git}" != "${source}" ]
vcs=git
elif [ "${source#git://}" != "${source}" ]
vcs=git
elif [ "${source#cvs://}" != "${source}" ]
vcs=cvs
elif [ "${source#svn*://}" != "${source}" ]
vcs=svn
fi
fi
# Copy from source.
if [ -n "${vcs}" ]
# Clone from VCS.
case "${vcs}" in
( bzr ) bzr checkout "${source}" "${emacsd}"
( cvs ) cvs checkout -d "${emacsd}" "${source}"
( darcs ) darcs get "${source}" "${emacsd}"
( git ) git clone "${source}" "${emacsd}"
( hg ) hg clone "${source}" "${emacsd}"
( svn ) svn checkout "${source}" "${emacsd}"
( * ) echo "Unsupported source type."
esac
if [ ${x} != 0 ]
echo "Error cloning repository."
exit
fi
elif [ -d "${source}" ]
# Copy directory.
if [ -d "${source}/.emacs.d" ]
cp -r "${source}" "${fakehome}"
else
mkdir -p ${fakehome}
cp -r "${source}" "${emacsd}"
fi
fi
else
# Not cloning. Create blank-slate distribution.
mkdir -p "${emacsd}"
init="${emacsd}/init.el"
# touch ${init}
echo " >${init}
set -- "$@" "${init}" # Visit the new init file.
fi
elif [ -n "${clone}" ]
echo "Cannot clone into existing distribution. Exiting."
exit
fi
# Run the distribution.
# Check for a non-standard emacs executable.
# --emacs takes precedence
if [ -z "${emacs}" ]
if [ -f ${fakehome}/emacs-command ]
emacs=$(cat ${fakehome}/emacs-command)
fi
fi
if [ -z "${emacs}" ]
emacs=emacs
fi
# Because we eval the final command, "$@" (which would normally be
# identical to the following substitutions) isn't sufficient to pass
# additional arguments if they are quoted. Instead we list them out
# individually as "${1}" "${2}" "${3}" etc... (in which format we are
# assured that the argument values themselves cannot cause further
# quoting issues).
args=$(seq -s' ' -f '"${%1.0f}"' 1 $#)
# This approach was gleaned from the following article and its comments:
# http://www.yosefk.com/blog/passing-shell-script-arguments-to-a-subprocess.html
# Finally, run the command.
# echo exec env HOME=${fakehome} ${emacs} $args
eval exec env HOME=${fakehome} ${emacs} ${args}
# Local Variables:
# indent-tabs-mode: t
# tab-width: 4
# End:
CategoryDotEmacs