#!/usr/bin/env zsh # WHAT IS THIS { # Wrap the entire program in a braced block so it won't run at all if you do curl | zsh and the connection drops. global_ignore=( '\.git.*' 'README\.md' '\.editorconfig' '\.stow-no-folding' '\.mypy_cache' '\..*\.swp' ) supported_stow_versions=( 2.3.2-fixbug56727 ) call-git() { git $argv ${VERBOSE:+-v} } call-gnu-stow() { $STOW --dotfiles -d $DOTFILES -t ~ --ignore=${^global_ignore} ${VERBOSE:+-vvv} $argv } do-bootstrap() { echo 'Bootstrapping your dotfiles...' >&2 mkdir -p $DOTFILES touch $DOTFILES/.stow if (( $+commands[stow] )); then local stow_version=${"$(stow -V)"#stow*version } if (( 0 == $supported_stow_versions[(Ie)$stow_version] )); then echo "GNU Stow is installed, but version $stow_version is bugged." >&2 echo 'See https://github.com/aspiers/stow/issues/33 for info.' >&2 echo 'Will fetch patched version of Stow...' >&2 install-custom-gnu-stow || return $? else echo "GNU Stow version $stow_version is installed and is compatible with dots." >&2 fi else echo 'GNU Stow is not installed, fetching it...' >&2 install-custom-gnu-stow || return $? fi do-clone dots git vim zsh || return $? do-stow } install-custom-gnu-stow() { clone-one stow || return $? STOW=$DOTFILES/stow/dot-local/bin/stow } do-clone() { echo "Requested packages: $argv" >&2 for package in $argv; do clone-one $package || return $? done } clone-one() { local url=$1 local package=${${url##*/}%.git} # Simple package names are fetched from the configured source prefix. [[ $url != */* ]] && url=$DOTS_SOURCE_PREFIX/$package # user/repo packages are fetched from that user's GitHub repos. [[ $url != *:* ]] && url=https://github.com/$url if [[ -d $DOTFILES/$package ]]; then echo "Looks like you already have $package cloned." >&2 return 1 fi echo "Retrieving $package from $url now..." >&2 call-git clone $url $DOTFILES/$package } do-stow() { local -a packages packages=($argv) if (( $#packages == 0 )); then packages=( $DOTFILES/*(N:t) ) if (( $#packages == 0 )); then echo "No installed packages! Do you want to clone some first?" >&2 return 1 fi echo "Stowing all packages ($packages) into $HOME now..." >&2 else echo "Stowing $packages into $HOME now..." >&2 fi for nofold in $DOTFILES/${^packages}/.stow-no-folding(N); process-stow-no-folding $nofold call-gnu-stow -S $packages } do-unstow() { local -a packages packages=($argv) if (( $#packages == 0 )); then packages=( $DOTFILES/*(N:t) ) if (( $#packages == 0 )); then echo "No installed packages! There's nothing to unstow!" >&2 return 1 fi echo "Unstowing all packages ($packages) from $HOME now..." >&2 else echo "Unstowing $packages from $HOME now..." >&2 fi call-gnu-stow -D $packages } do-restow() { local -a packages packages=($argv) if (( $#packages == 0 )); then packages=( $DOTFILES/*(N:t) ) if (( $#packages == 0 )); then echo "No installed packages! Do you want to clone some first?" >&2 return 1 fi echo "Restowing all packages ($packages) into $HOME now..." >&2 else echo "Restowing $packages into $HOME now..." >&2 fi for nofold in $DOTFILES/${^packages}/.stow-no-folding(N); process-stow-no-folding $nofold call-gnu-stow -R $packages } process-stow-no-folding() { zmodload zsh/mapfile for file in ${(f)mapfile[$1]}; do file=~/$file [[ -e $file ]] && continue mkdir -p ${file:h} touch $file done } do-fetch() { local -a packages packages=($argv) (( $#packages == 0 )) && packages=( $DOTFILES/*(N:t) ) for p in $packages; do cd $DOTFILES/$p call-git fetch -p || return $? done } do-pull() { local -a packages packages=($argv) (( $#packages == 0 )) && packages=( $DOTFILES/*(N:t) ) for p in $packages; do cd $DOTFILES/$p call-git pull || return $? done } do-status() { local -a packages packages=($argv) (( $#packages == 0 )) && packages=( $DOTFILES/*(N:t) ) local length=${#${(O@)packages//?/X}[1]} git_status remote_status for p in $packages; do cd $DOTFILES/$p # Always show the repo's name at the beginning of the line. printf %${length}s' ' $p # Check if the package is actually a repo. It might not be if it's brand-new. git_status="$(call-git status --porcelain --ignore-submodules -unormal 2>/dev/null)" if (( $? != 0 )); then printf '%9s' '' # space across to the last column print -P %F{247}not a git repository%f continue fi # Indicate the working tree status. if [[ -z $git_status ]]; then print -Pn %F{green}clean%f' ' else print -Pn %F{red}unclean%f fi printf '%2s' '' # If the repository has an upstream remote configured, indicate the number # of commits out of sync we are from the remote. if git rev-parse --abbrev-ref @{u} &>/dev/null; then remote_status="$(git rev-list --left-right --count HEAD...@{u} 2>/dev/null)" remote_status=(${(ps:\t:)remote_status}) print -Pn %F{cyan} (( $remote_status[1] > 0 )) && print -n $remote_status[1]⇡ (( $remote_status[2] > 0 )) && print -n $remote_status[2]⇣ print -P %f else print -P %F{247}no remote%f fi done } main() { : ${DOTFILES:=~/dotfiles} ${STOW:=stow} ${DOTS_SOURCE_PREFIX:=https://git.00dani.me/dot} local opt_index=$argv[(I)(-v|--verbose)] if (( opt_index != 0 )); then VERBOSE=yes argv[opt_index]=() fi comm=$1 if (( $# == 0 )); then if [[ -d $DOTFILES ]]; then comm=status else comm=bootstrap fi fi case $comm in bootstrap) do-bootstrap ;; clone) do-clone ${argv[2,-1]} ;; fetch) do-fetch ${argv[2,-1]} ;; stow) do-stow ${argv[2,-1]} ;; unstow) do-unstow ${argv[2,-1]} ;; restow) do-restow ${argv[2,-1]} ;; pull) do-pull ${argv[2,-1]} ;; st|status) do-status ${argv[2,-1]} ;; *) echo "Unknown subcommand $comm" >&2; return 2 ;; esac } main "$@" }