From 4a079ad19d4971fdbbe9f6b12691d1514b93024b Mon Sep 17 00:00:00 2001 From: binaryDiv Date: Sat, 1 Aug 2026 17:14:49 +0200 Subject: [PATCH] dotfiles script v0.5.3 Change indentation from tabs to spaces. --- bin/dotfiles | 590 +++++++++++++++++++++++++-------------------------- 1 file changed, 295 insertions(+), 295 deletions(-) diff --git a/bin/dotfiles b/bin/dotfiles index c175440..af55641 100755 --- a/bin/dotfiles +++ b/bin/dotfiles @@ -2,49 +2,49 @@ ## ## binaryDiv's dotfiles management script -## Version 0.5.2 +## Version 0.5.3 ## usage() { - cat <<- END_OF_HELPTEXT - Usage: $(basename $0) COMMAND [...] - Manage dotfiles via git. + cat << END_OF_HELPTEXT + Usage: $(basename $0) COMMAND [...] + Manage dotfiles via git. - General commands: - help print this help message + General commands: + help print this help message - Installation: - install run this on first start: install dotfiles script in ~/bin, configure git, ... - (this will NOT create links for all dotfiles, though) + Installation: + install run this on first start: install dotfiles script in ~/bin, configure git, ... + (this will NOT create links for all dotfiles, though) - Dotfile management: - ls [DIR] list contents of .dotfiles directory (or DIR relative to it) - (-l: list host-specific files, i.e. ~/.dotfiles/_local/HOSTNAME/DIR) - edit FILE open ~/.dotfiles/FILE in vim - (-l: open host-specific files, i.e. ~/.dotfiles/_local/HOSTNAME/DIR) - diff FILE use vimdiff to compare ~/FILE to ~/.dotfiles/FILE - (-l: compare host-specific files, i.e. ~/.dotfiles/_local/HOSTNAME/DIR) - link FILE... create a symlink from ~/FILE to ~/.dotfiles/FILE (multiple files possible) - (-q: be quiet, only print something if an error occurs) - (-l: link host-specific files, i.e. ~/FILE to ~/.dotfiles/_local/HOSTNAME/FILE) - linkstatus [DIR] list contents of .dotfiles with information about whether there are links to - them in ~/, whether they differ, etc... - (-l: show linkstatus only for host-specific files, i.e. ~/.dotfiles/_local/HOSTNAME) - createfrom FILE... copy ~/FILE to ~/.dotfiles/FILE and symlink the file - (-n: only copy, leave the original, do not create symlink) - (-l: create host-specific files, i.e. ~/FILE to ~/.dotfiles/_local/HOSTNAME/FILE) + Dotfile management: + ls [DIR] list contents of .dotfiles directory (or DIR relative to it) + (-l: list host-specific files, i.e. ~/.dotfiles/_local/HOSTNAME/DIR) + edit FILE open ~/.dotfiles/FILE in vim + (-l: open host-specific files, i.e. ~/.dotfiles/_local/HOSTNAME/DIR) + diff FILE use vimdiff to compare ~/FILE to ~/.dotfiles/FILE + (-l: compare host-specific files, i.e. ~/.dotfiles/_local/HOSTNAME/DIR) + link FILE... create a symlink from ~/FILE to ~/.dotfiles/FILE (multiple files possible) + (-q: be quiet, only print something if an error occurs) + (-l: link host-specific files, i.e. ~/FILE to ~/.dotfiles/_local/HOSTNAME/FILE) + linkstatus [DIR] list contents of .dotfiles with information about whether there are links to + them in ~/, whether they differ, etc... + (-l: show linkstatus only for host-specific files, i.e. ~/.dotfiles/_local/HOSTNAME) + createfrom FILE... copy ~/FILE to ~/.dotfiles/FILE and symlink the file + (-n: only copy, leave the original, do not create symlink) + (-l: create host-specific files, i.e. ~/FILE to ~/.dotfiles/_local/HOSTNAME/FILE) - Synchronization: - git ARGS... wrapper for git (all arguments are passed on to git) - pull shortcut for git pull - END_OF_HELPTEXT + Synchronization: + git ARGS... wrapper for git (all arguments are passed on to git) + pull shortcut for git pull +END_OF_HELPTEXT } # Define root dir for dotfiles management (defaults to $HOME, can be overridden # with environment variables for debugging) if [[ -z $DOTFILESROOT ]]; then - DOTFILESROOT="$HOME" + DOTFILESROOT="$HOME" fi # Set variables @@ -62,315 +62,315 @@ shift # Parse command case "$cmd" in - help|--help|-h) - usage - exit 1 - ;; + help|--help|-h) + usage + exit 1 + ;; - install) - bin_source="$dotfilesdir/bin/dotfiles" - bin_target="$homedir/bin/dotfiles" - bin_dir="$homedir/bin" + install) + bin_source="$dotfilesdir/bin/dotfiles" + bin_target="$homedir/bin/dotfiles" + bin_dir="$homedir/bin" - if [[ ! -e $bin_target ]]; then - echo "* Installing dotfiles script in $bin_dir/" - mkdir -pv "$bin_dir" - ln -sr "$bin_source" "$bin_target" || exit 1 - elif [[ ! "$(realpath $bin_target)" -ef "$bin_source" ]]; then - echo "! Warning: File $bin_target already exists but is NOT a link to $bin_source" - fi + if [[ ! -e $bin_target ]]; then + echo "* Installing dotfiles script in $bin_dir/" + mkdir -pv "$bin_dir" + ln -sr "$bin_source" "$bin_target" || exit 1 + elif [[ ! "$(realpath $bin_target)" -ef "$bin_source" ]]; then + echo "! Warning: File $bin_target already exists but is NOT a link to $bin_source" + fi - if [[ ":$PATH:" != *":$bin_dir:"* ]]; then - echo "! Warning: $bin_dir is not in your PATH." - fi + if [[ ":$PATH:" != *":$bin_dir:"* ]]; then + echo "! Warning: $bin_dir is not in your PATH." + fi - show_bash_restart_info= + show_bash_restart_info= - # Enable user-defined bash completion per directory - bashcompletion_filename=.bash_completion - if [[ ! -e "$homedir/$bashcompletion_filename" ]] || - [[ ! "$(realpath "$homedir/$bashcompletion_filename")" -ef "$dotfilesdir/$bashcompletion_filename" ]]; - then - echo "* Installing user-defined bash completion (~/.bash_completion.d/)" - "$0" link -q "$bashcompletion_filename" && - show_bash_restart_info=1 - fi - mkdir -p "$homedir/.bash_completion.d" + # Enable user-defined bash completion per directory + bashcompletion_filename=.bash_completion + if [[ ! -e "$homedir/$bashcompletion_filename" ]] || + [[ ! "$(realpath "$homedir/$bashcompletion_filename")" -ef "$dotfilesdir/$bashcompletion_filename" ]]; + then + echo "* Installing user-defined bash completion (~/.bash_completion.d/)" + "$0" link -q "$bashcompletion_filename" && + show_bash_restart_info=1 + fi + mkdir -p "$homedir/.bash_completion.d" - # Activate dotfiles bash completion - bashcompletion_dotfiles_filename=.bash_completion.d/10-dotfiles.sh - if [[ ! -e "$homedir/$bashcompletion_dotfiles_filename" ]] || - [[ ! "$(realpath "$homedir/$bashcompletion_dotfiles_filename")" -ef "$dotfilesdir/$bashcompletion_dotfiles_filename" ]]; - then - echo "* Installing bash completion for dotfiles script" - "$0" link -q "$bashcompletion_dotfiles_filename" && - show_bash_restart_info=1 - fi + # Activate dotfiles bash completion + bashcompletion_dotfiles_filename=.bash_completion.d/10-dotfiles.sh + if [[ ! -e "$homedir/$bashcompletion_dotfiles_filename" ]] || + [[ ! "$(realpath "$homedir/$bashcompletion_dotfiles_filename")" -ef "$dotfilesdir/$bashcompletion_dotfiles_filename" ]]; + then + echo "* Installing bash completion for dotfiles script" + "$0" link -q "$bashcompletion_dotfiles_filename" && + show_bash_restart_info=1 + fi - if [[ -n $show_bash_restart_info ]]; then - echo " Restart bash or run 'source $homedir/.bash_completion' to activate it now" - fi + if [[ -n $show_bash_restart_info ]]; then + echo " Restart bash or run 'source $homedir/.bash_completion' to activate it now" + fi - echo "* Configuring git settings" - git -C "$dotfilesdir" config merge.ff only - ;; + echo "* Configuring git settings" + git -C "$dotfilesdir" config merge.ff only + ;; - ls) - lsdir="$dotfilesdir" - [[ $1 = "-l" ]] && { lsdir="$lsdir/$dotfiles_local_prefix"; shift; } - [[ -n $1 ]] && lsdir="$lsdir/$1" + ls) + lsdir="$dotfilesdir" + [[ $1 = "-l" ]] && { lsdir="$lsdir/$dotfiles_local_prefix"; shift; } + [[ -n $1 ]] && lsdir="$lsdir/$1" - echo "$lsdir:" - ls -lAh --color=auto "$lsdir" - ;; + echo "$lsdir:" + ls -lAh --color=auto "$lsdir" + ;; - linkstatus) - cd "$dotfilesdir" + linkstatus) + cd "$dotfilesdir" - subdir="$1" - [[ $subdir = "-l" ]] && { subdir="$dotfiles_local_prefix/$1"; shift; } + subdir="$1" + [[ $subdir = "-l" ]] && { subdir="$dotfiles_local_prefix/$1"; shift; } - if [[ -n $subdir ]]; then - subdir="${subdir%/}" - [[ $subdir = $dotfiles_local_base ]] && subdir=$dotfiles_local_prefix + if [[ -n $subdir ]]; then + subdir="${subdir%/}" + [[ $subdir = $dotfiles_local_base ]] && subdir=$dotfiles_local_prefix - [[ -e $subdir ]] || { echo "$dotfilesdir/$subdir does not exist"; exit 1; } - [[ -d $subdir ]] && subdir="$subdir/" - fi + [[ -e $subdir ]] || { echo "$dotfilesdir/$subdir does not exist"; exit 1; } + [[ -d $subdir ]] && subdir="$subdir/" + fi - shopt -s nullglob dotglob - dotdirs=() + shopt -s nullglob dotglob + dotdirs=() - for file in $subdir*; do - [[ -e $file ]] || continue - [[ $file = ".git" ]] && continue + for file in $subdir*; do + [[ -e $file ]] || continue + [[ $file = ".git" ]] && continue - file_home="$homedir/$file" - file_dotfiles="$dotfilesdir/$file" + file_home="$homedir/$file" + file_dotfiles="$dotfilesdir/$file" - if [[ $subdir == $dotfiles_local_base/* ]]; then - file_home="$homedir/${file#$dotfiles_local_prefix/}" - fi + if [[ $subdir == $dotfiles_local_base/* ]]; then + file_home="$homedir/${file#$dotfiles_local_prefix/}" + fi - if [[ -e $file_home ]]; then - if [[ "$(realpath "$file_home")" -ef "$file_dotfiles" ]]; then - file_status=" LINKED " - elif [[ -d $file_home ]]; then - dotdirs+=($file) - continue - elif [[ -f $file_home ]] && cmp --silent "$file_home" "$file_dotfiles"; then - file_status="== EQUAL " - else - file_status="<> DIFFERS " - fi - elif [[ $file = $dotfiles_local_base ]]; then - dotdirs=("$dotfiles_local_prefix" "${dotdirs[@]}") - continue - else - if [[ -d $file_dotfiles ]]; then - file_status="++ NEW DIR " - else - file_status="++ NEW FILE" - fi - fi + if [[ -e $file_home ]]; then + if [[ "$(realpath "$file_home")" -ef "$file_dotfiles" ]]; then + file_status=" LINKED " + elif [[ -d $file_home ]]; then + dotdirs+=($file) + continue + elif [[ -f $file_home ]] && cmp --silent "$file_home" "$file_dotfiles"; then + file_status="== EQUAL " + else + file_status="<> DIFFERS " + fi + elif [[ $file = $dotfiles_local_base ]]; then + dotdirs=("$dotfiles_local_prefix" "${dotdirs[@]}") + continue + else + if [[ -d $file_dotfiles ]]; then + file_status="++ NEW DIR " + else + file_status="++ NEW FILE" + fi + fi - [[ -d $file_dotfiles ]] && file="$file/" - echo "$file_status $file" - done + [[ -d $file_dotfiles ]] && file="$file/" + echo "$file_status $file" + done - if [[ $dotdirs ]]; then - echo - for dir in ${dotdirs[@]}; do - echo "[$dir]" - dotfiles linkstatus "$dir" | grep '\(LINKED\|EQUAL\|DIFFERS\|NEW\)' - echo - done - fi - ;; + if [[ $dotdirs ]]; then + echo + for dir in ${dotdirs[@]}; do + echo "[$dir]" + dotfiles linkstatus "$dir" | grep '\(LINKED\|EQUAL\|DIFFERS\|NEW\)' + echo + done + fi + ;; - edit) - basedir=$dotfilesdir - [[ $1 = "-l" ]] && { basedir="$basedir/$dotfiles_local_prefix"; shift; } - [[ -n $1 ]] || { echo "$(basename $0) $cmd: Missing argument."; exit 1; } - vim "$basedir/$1" - ;; + edit) + basedir=$dotfilesdir + [[ $1 = "-l" ]] && { basedir="$basedir/$dotfiles_local_prefix"; shift; } + [[ -n $1 ]] || { echo "$(basename $0) $cmd: Missing argument."; exit 1; } + vim "$basedir/$1" + ;; - diff) - basedir=$dotfilesdir - [[ $1 = "-l" ]] && { basedir="$basedir/$dotfiles_local_prefix"; shift; } - [[ -n $1 ]] || { echo "$(basename $0) $cmd: Missing argument."; exit 1; } - file_A="$homedir/$1" - file_B="$basedir/$1" + diff) + basedir=$dotfilesdir + [[ $1 = "-l" ]] && { basedir="$basedir/$dotfiles_local_prefix"; shift; } + [[ -n $1 ]] || { echo "$(basename $0) $cmd: Missing argument."; exit 1; } + file_A="$homedir/$1" + file_B="$basedir/$1" - if [[ "$(realpath "$file_A")" -ef "$file_B" ]]; then - echo "$file_A is a link to $file_B" - else - vimdiff "$file_A" "$file_B" - fi - ;; + if [[ "$(realpath "$file_A")" -ef "$file_B" ]]; then + echo "$file_A is a link to $file_B" + else + vimdiff "$file_A" "$file_B" + fi + ;; - link) - while getopts ":ql" opt; do - case $opt in - q) QUIET=1 ;; - l) link_local=1 ;; - \?) - echo "Invalid option: -$OPTARG" - exit 1 - ;; - esac - done - shift $((OPTIND-1)) + link) + while getopts ":ql" opt; do + case $opt in + q) QUIET=1 ;; + l) link_local=1 ;; + \?) + echo "Invalid option: -$OPTARG" + exit 1 + ;; + esac + done + shift $((OPTIND-1)) - [[ -n $1 ]] || { echo "$(basename $0) $cmd: Missing argument."; exit 1; } + [[ -n $1 ]] || { echo "$(basename $0) $cmd: Missing argument."; exit 1; } - for filename in "$@"; do - file_home="$homedir/$filename" - file_dotfiles="$dotfilesdir/$filename" + for filename in "$@"; do + file_home="$homedir/$filename" + file_dotfiles="$dotfilesdir/$filename" - if [[ $link_local ]]; then - file_dotfiles="$dotfilesdir/$dotfiles_local_prefix/$filename" - fi + if [[ $link_local ]]; then + file_dotfiles="$dotfilesdir/$dotfiles_local_prefix/$filename" + fi - if [[ ! -e $file_dotfiles ]]; then - echo "! $file_dotfiles: target file does not exist" - exit 1 - fi + if [[ ! -e $file_dotfiles ]]; then + echo "! $file_dotfiles: target file does not exist" + exit 1 + fi - if [[ -e $file_home ]]; then - if [[ "$(realpath "$file_home")" -ef "$file_dotfiles" ]]; then - # Target is already a link to the correct file - continue - fi + if [[ -e $file_home ]]; then + if [[ "$(realpath "$file_home")" -ef "$file_dotfiles" ]]; then + # Target is already a link to the correct file + continue + fi - if cmp --silent "$file_home" "$file_dotfiles"; then - echo "! $file_home already exists and is equal to $file_dotfiles" - read -p " Delete $file_home and create link? [y/N] " -r - if [[ ! $REPLY =~ ^[Yy]$ ]]; then - # Don't delete file - continue - fi - rm "$file_home" || exit 1 - else - echo "! $file_home already exists but is NOT a link to $file_dotfiles, please fix manually" - exit 1 - fi - fi + if cmp --silent "$file_home" "$file_dotfiles"; then + echo "! $file_home already exists and is equal to $file_dotfiles" + read -p " Delete $file_home and create link? [y/N] " -r + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + # Don't delete file + continue + fi + rm "$file_home" || exit 1 + else + echo "! $file_home already exists but is NOT a link to $file_dotfiles, please fix manually" + exit 1 + fi + fi - [[ $QUIET ]] || echo "* Creating link $file_home -> $file_dotfiles" - mkdir -p "$(dirname "$file_home")" || exit 1 - ln -sr "$file_dotfiles" "$file_home" || exit 1 - done - ;; + [[ $QUIET ]] || echo "* Creating link $file_home -> $file_dotfiles" + mkdir -p "$(dirname "$file_home")" || exit 1 + ln -sr "$file_dotfiles" "$file_home" || exit 1 + done + ;; - createfrom) - while getopts ":nl" opt; do - case $opt in - n) NOLINK=1 ;; - l) create_local=1 ;; - \?) - echo "Invalid option: -$OPTARG" - exit 1 - ;; - esac - done - shift $((OPTIND-1)) + createfrom) + while getopts ":nl" opt; do + case $opt in + n) NOLINK=1 ;; + l) create_local=1 ;; + \?) + echo "Invalid option: -$OPTARG" + exit 1 + ;; + esac + done + shift $((OPTIND-1)) - [[ -n $1 ]] || { echo "$(basename $0) $cmd: Missing argument."; exit 1; } + [[ -n $1 ]] || { echo "$(basename $0) $cmd: Missing argument."; exit 1; } - for filename in "$@"; do - file_home="$homedir/$filename" - file_dotfiles="$dotfilesdir/$filename" + for filename in "$@"; do + file_home="$homedir/$filename" + file_dotfiles="$dotfilesdir/$filename" - if [[ $create_local ]]; then - file_dotfiles="$dotfilesdir/$dotfiles_local_prefix/$filename" - fi + if [[ $create_local ]]; then + file_dotfiles="$dotfilesdir/$dotfiles_local_prefix/$filename" + fi - if [[ ! -e $file_home ]]; then - echo "! $file_home: source file does not exist" - exit 1 - elif [[ -e $file_dotfiles ]]; then - echo "! $file_dotfiles already exists" - exit 1 - else - mkdir -p "$(dirname "$file_dotfiles")" - [[ $QUIET ]] || echo "* Copy $file_home -> $file_dotfiles" - cp -n "$file_home" "$file_dotfiles" || exit 1 - if [[ ! $NOLINK ]]; then - [[ $QUIET ]] || echo "* Remove original and link $file_home -> $file_dotfiles" - rm "$file_home" || exit 1 - ln -sr "$file_dotfiles" "$file_home" || exit 1 - fi - fi - done - ;; - - git) - git -C "$dotfilesdir" "$@" - ;; + if [[ ! -e $file_home ]]; then + echo "! $file_home: source file does not exist" + exit 1 + elif [[ -e $file_dotfiles ]]; then + echo "! $file_dotfiles already exists" + exit 1 + else + mkdir -p "$(dirname "$file_dotfiles")" + [[ $QUIET ]] || echo "* Copy $file_home -> $file_dotfiles" + cp -n "$file_home" "$file_dotfiles" || exit 1 + if [[ ! $NOLINK ]]; then + [[ $QUIET ]] || echo "* Remove original and link $file_home -> $file_dotfiles" + rm "$file_home" || exit 1 + ln -sr "$file_dotfiles" "$file_home" || exit 1 + fi + fi + done + ;; - pull) - git -C "$dotfilesdir" pull "$@" - ;; + git) + git -C "$dotfilesdir" "$@" + ;; - bash-completion) - cat <<- 'END_OF_BASHCOMPLETION' - _dotfiles_filenames() { - compopt -o filenames - files=($(compgen -f -X '*/@(.|..|.git)' .dotfiles/$cur)) - COMPREPLY=(${files[@]#.dotfiles/}) - } - _dotfiles_filenames_local() { - compopt -o filenames - dotfiles_prefix=.dotfiles/_local/$(hostname) - files=($(compgen -f -X '*/@(.|..|.git)' $dotfiles_prefix/$cur)) - COMPREPLY=(${files[@]#$dotfiles_prefix/}) - } - _dotfiles() { - COMPREPLY=() - local cur="${COMP_WORDS[COMP_CWORD]}" - local commands="help install ls edit diff link linkstatus createfrom git pull" - local gitcommands="add commit diff fetch log pull push rebase status" + pull) + git -C "$dotfilesdir" pull "$@" + ;; - if [[ $COMP_CWORD -eq 1 ]]; then - COMPREPLY=($(compgen -W "$commands" -- $cur)) - else - case "${COMP_WORDS[1]}" in - ls|edit|diff) - [[ $COMP_CWORD -eq 2 ]] && _dotfiles_filenames - ;; + bash-completion) + cat << 'END_OF_BASHCOMPLETION' + _dotfiles_filenames() { + compopt -o filenames + files=($(compgen -f -X '*/@(.|..|.git)' .dotfiles/$cur)) + COMPREPLY=(${files[@]#.dotfiles/}) + } + _dotfiles_filenames_local() { + compopt -o filenames + dotfiles_prefix=.dotfiles/_local/$(hostname) + files=($(compgen -f -X '*/@(.|..|.git)' $dotfiles_prefix/$cur)) + COMPREPLY=(${files[@]#$dotfiles_prefix/}) + } + _dotfiles() { + COMPREPLY=() + local cur="${COMP_WORDS[COMP_CWORD]}" + local commands="help install ls edit diff link linkstatus createfrom git pull" + local gitcommands="add commit diff fetch log pull push rebase status" - link|linkstatus) - if [[ $COMP_CWORD -gt 2 && ${COMP_WORDS[2]} = "-l" ]]; then - _dotfiles_filenames_local - else - _dotfiles_filenames - fi - ;; + if [[ $COMP_CWORD -eq 1 ]]; then + COMPREPLY=($(compgen -W "$commands" -- $cur)) + else + case "${COMP_WORDS[1]}" in + ls|edit|diff) + [[ $COMP_CWORD -eq 2 ]] && _dotfiles_filenames + ;; - createfrom) - compopt -o filenames - COMPREPLY=($(compgen -f -- ${cur})) - ;; + link|linkstatus) + if [[ $COMP_CWORD -gt 2 && ${COMP_WORDS[2]} = "-l" ]]; then + _dotfiles_filenames_local + else + _dotfiles_filenames + fi + ;; - git) - if [[ $COMP_CWORD -eq 2 ]]; then - COMPREPLY=($(compgen -W "$gitcommands" -- ${cur})) - else - _dotfiles_filenames - fi - ;; - esac - fi - } + createfrom) + compopt -o filenames + COMPREPLY=($(compgen -f -- ${cur})) + ;; - complete -F _dotfiles dotfiles - END_OF_BASHCOMPLETION - ;; + git) + if [[ $COMP_CWORD -eq 2 ]]; then + COMPREPLY=($(compgen -W "$gitcommands" -- ${cur})) + else + _dotfiles_filenames + fi + ;; + esac + fi + } - *) - echo "Unknown command '$cmd', use 'help' to show all commands." - exit 1 - ;; + complete -F _dotfiles dotfiles +END_OF_BASHCOMPLETION + ;; + + *) + echo "Unknown command '$cmd', use 'help' to show all commands." + exit 1 + ;; esac